diff --git a/installation/CHANGELOG b/installation/CHANGELOG index 1463e12096dca..2135de2f9451a 100644 --- a/installation/CHANGELOG +++ b/installation/CHANGELOG @@ -28,6 +28,7 @@ $ -> Language fix or change 05-Feb-2012 Mark Dexter # [#27907] Sitename not escaped + # [#27802] The package installer insert two database entries on templates 04-Feb-2012 Christophe Demko # [#27960] Unable to create an article (Sudhi Seshachala) diff --git a/libraries/joomla/installer/adapters/language.php b/libraries/joomla/installer/adapters/language.php index b9de7c60f0cd5..3653e97333fb8 100644 --- a/libraries/joomla/installer/adapters/language.php +++ b/libraries/joomla/installer/adapters/language.php @@ -1,669 +1,669 @@ -parent->getPath('source'); - if (!$source) - { - $this->parent - ->setPath( - 'source', - ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/language/' . $this->parent->extension->element - ); - } - $this->manifest = $this->parent->getManifest(); - $root = $this->manifest->document; - - // Get the client application target - if ((string) $this->manifest->attributes()->client == 'both') - { - JError::raiseWarning(42, JText::_('JLIB_INSTALLER_ERROR_DEPRECATED_FORMAT')); - $element = $this->manifest->site->files; - if (!$this->_install('site', JPATH_SITE, 0, $element)) - { - return false; - } - - $element = $this->manifest->administration->files; - if (!$this->_install('administrator', JPATH_ADMINISTRATOR, 1, $element)) - { - return false; - } - // This causes an issue because we have two eid's, *sigh* nasty hacks! - return true; - } - elseif ($cname = (string) $this->manifest->attributes()->client) - { - // Attempt to map the client to a base path - $client = JApplicationHelper::getClientInfo($cname, true); - if ($client === null) - { - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_UNKNOWN_CLIENT_TYPE', $cname))); - return false; - } - $basePath = $client->path; - $clientId = $client->id; - $element = $this->manifest->files; - - return $this->_install($cname, $basePath, $clientId, $element); - } - else - { - // No client attribute was found so we assume the site as the client - $cname = 'site'; - $basePath = JPATH_SITE; - $clientId = 0; - $element = $this->manifest->files; - - return $this->_install($cname, $basePath, $clientId, $element); - } - } - - /** - * Install function that is designed to handle individual clients - * - * @param string $cname Cname @todo: not used - * @param string $basePath The base name. - * @param integer $clientId The client id. - * @param object &$element The XML element. - * - * @return boolean - * - * @since 11.1 - */ - protected function _install($cname, $basePath, $clientId, &$element) - { - $this->manifest = $this->parent->getManifest(); - - // Get the language name - // Set the extensions name - $name = JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'); - $this->set('name', $name); - - // Get the Language tag [ISO tag, eg. en-GB] - $tag = (string) $this->manifest->tag; - - // Check if we found the tag - if we didn't, we may be trying to install from an older language package - if (!$tag) - { - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::_('JLIB_INSTALLER_ERROR_NO_LANGUAGE_TAG'))); - return false; - } - - $this->set('tag', $tag); - - // Set the language installation path - $this->parent->setPath('extension_site', $basePath . '/language/' . $tag); - - // Do we have a meta file in the file list? In other words... is this a core language pack? - if ($element && count($element->children())) - { - $files = $element->children(); - foreach ($files as $file) - { - if ((string) $file->attributes()->file == 'meta') - { - $this->_core = true; - break; - } - } - } - - // Either we are installing a core pack or a core pack must exist for the language we are installing. - if (!$this->_core) - { - if (!JFile::exists($this->parent->getPath('extension_site') . '/' . $this->get('tag') . '.xml')) - { - $this->parent - ->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_NO_CORE_LANGUAGE', $this->get('tag')))); - return false; - } - } - - // If the language directory does not exist, let's create it - $created = false; - if (!file_exists($this->parent->getPath('extension_site'))) - { - if (!$created = JFolder::create($this->parent->getPath('extension_site'))) - { - $this->parent - ->abort( - JText::sprintf( - 'JLIB_INSTALLER_ABORT', - JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_FOLDER_FAILED', $this->parent->getPath('extension_site')) - ) - ); - return false; - } - } - else - { - // Look for an update function or update tag - $updateElement = $this->manifest->update; - // Upgrade manually set or - // Update function available or - // Update tag detected - if ($this->parent->getUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update')) - || is_a($updateElement, 'JXMLElement')) - { - return $this->update(); // transfer control to the update function - } - elseif (!$this->parent->getOverwrite()) - { - // Overwrite is set - // We didn't have overwrite set, find an update function or find an update tag so lets call it safe - if (file_exists($this->parent->getPath('extension_site'))) - { - // If the site exists say so. - JError::raiseWarning( - 1, - JText::sprintf( - 'JLIB_INSTALLER_ABORT', - JText::sprintf('JLIB_INSTALLER_ERROR_FOLDER_IN_USE', $this->parent->getPath('extension_site')) - ) - ); - } - else - { - // If the admin exists say so. - JError::raiseWarning( - 1, - JText::sprintf( - 'JLIB_INSTALLER_ABORT', - JText::sprintf('JLIB_INSTALLER_ERROR_FOLDER_IN_USE', $this->parent->getPath('extension_administrator')) - ) - ); - } - return false; - } - } - - /* - * If we created the language directory we will want to remove it if we - * have to roll back the installation, so let's add it to the installation - * step stack - */ - if ($created) - { - $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_site'))); - } - - // Copy all the necessary files - if ($this->parent->parseFiles($element) === false) - { - // Install failed, rollback changes - $this->parent->abort(); - return false; - } - - // Parse optional tags - $this->parent->parseMedia($this->manifest->media); - - // Copy all the necessary font files to the common pdf_fonts directory - $this->parent->setPath('extension_site', $basePath . '/language/pdf_fonts'); - $overwrite = $this->parent->setOverwrite(true); - if ($this->parent->parseFiles($this->manifest->fonts) === false) - { - // Install failed, rollback changes - $this->parent->abort(); - return false; - } - $this->parent->setOverwrite($overwrite); - - // Get the language description - $description = (string) $this->manifest->description; - if ($description) - { - $this->parent->set('message', JText::_($description)); - } - else - { - $this->parent->set('message', ''); - } - - // Add an entry to the extension table with a whole heap of defaults - $row = JTable::getInstance('extension'); - $row->set('name', $this->get('name')); - $row->set('type', 'language'); - $row->set('element', $this->get('tag')); - // There is no folder for languages - $row->set('folder', ''); - $row->set('enabled', 1); - $row->set('protected', 0); - $row->set('access', 0); - $row->set('client_id', $clientId); - $row->set('params', $this->parent->getParams()); - $row->set('manifest_cache', $this->parent->generateManifestCache()); - - if (!$row->store()) - { - // Install failed, roll back changes - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', $row->getError())); - return false; - } - - // Clobber any possible pending updates - $update = JTable::getInstance('update'); - $uid = $update->find(array('element' => $this->get('tag'), 'type' => 'language', 'client_id' => '', 'folder' => '')); - if ($uid) - { - $update->delete($uid); - } - - return $row->get('extension_id'); - } - - /** - * Custom update method - * - * @return boolean True on success, false on failure - * - * @since 11.1 - */ - public function update() - { - $xml = $this->parent->getManifest(); - - $this->manifest = $xml; - - $cname = $xml->attributes()->client; - - // Attempt to map the client to a base path - $client = JApplicationHelper::getClientInfo($cname, true); - if ($client === null || (empty($cname) && $cname !== 0)) - { - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_UNKNOWN_CLIENT_TYPE', $cname))); - return false; - } - $basePath = $client->path; - $clientId = $client->id; - - // Get the language name - // Set the extensions name - $name = (string) $this->manifest->name; - $name = JFilterInput::getInstance()->clean($name, 'cmd'); - $this->set('name', $name); - - // Get the Language tag [ISO tag, eg. en-GB] - $tag = (string) $xml->tag; - - // Check if we found the tag - if we didn't, we may be trying to install from an older language package - if (!$tag) - { - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::_('JLIB_INSTALLER_ERROR_NO_LANGUAGE_TAG'))); - return false; - } - - $this->set('tag', $tag); - $folder = $tag; - - // Set the language installation path - $this->parent->setPath('extension_site', $basePath . '/language/' . $this->get('tag')); - - // Do we have a meta file in the file list? In other words... is this a core language pack? - if (count($xml->files->children())) - { - foreach ($xml->files->children() as $file) - { - if ((string) $file->attributes()->file == 'meta') - { - $this->_core = true; - break; - } - } - } - - // Either we are installing a core pack or a core pack must exist for the language we are installing. - if (!$this->_core) - { - if (!JFile::exists($this->parent->getPath('extension_site') . '/' . $this->get('tag') . '.xml')) - { - $this->parent - ->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_NO_CORE_LANGUAGE', $this->get('tag')))); - return false; - } - } - - // Copy all the necessary files - if ($this->parent->parseFiles($xml->files) === false) - { - // Install failed, rollback changes - $this->parent->abort(); - return false; - } - - // Parse optional tags - $this->parent->parseMedia($xml->media); - - // Copy all the necessary font files to the common pdf_fonts directory - $this->parent->setPath('extension_site', $basePath . '/language/pdf_fonts'); - $overwrite = $this->parent->setOverwrite(true); - if ($this->parent->parseFiles($xml->fonts) === false) - { - // Install failed, rollback changes - $this->parent->abort(); - return false; - } - $this->parent->setOverwrite($overwrite); - - // Get the language description and set it as message - $this->parent->set('message', (string) $xml->description); - - // Finalization and Cleanup Section - - // Clobber any possible pending updates - $update = JTable::getInstance('update'); - $uid = $update->find(array('element' => $this->get('tag'), 'type' => 'language', 'client_id' => $clientId)); - if ($uid) - { - $update->delete($uid); - } - - // Update an entry to the extension table - $row = JTable::getInstance('extension'); - $eid = $row->find(array('element' => strtolower($this->get('tag')), 'type' => 'language', 'client_id' => $clientId)); - if ($eid) - { - $row->load($eid); - } - else - { - // set the defaults - $row->set('folder', ''); // There is no folder for language - $row->set('enabled', 1); - $row->set('protected', 0); - $row->set('access', 0); - $row->set('client_id', $clientId); - $row->set('params', $this->parent->getParams()); - } - $row->set('name', $this->get('name')); - $row->set('type', 'language'); - $row->set('element', $this->get('tag')); - $row->set('manifest_cache', $this->parent->generateManifestCache()); - - if (!$row->store()) - { - // Install failed, roll back changes - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', $row->getError())); - return false; - } - - // And now we run the postflight - ob_start(); - ob_implicit_flush(false); - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight')) - { - $this->parent->manifestClass->postflight('update', $this); - } - $msg .= ob_get_contents(); // append messages - ob_end_clean(); - if ($msg != '') - { - $this->parent->set('extension_message', $msg); - } - - return $row->get('extension_id'); - } - - /** - * Custom uninstall method - * - * @param string $eid The tag of the language to uninstall - * - * @return mixed Return value for uninstall method in component uninstall file - * - * @since 11.1 - */ - public function uninstall($eid) - { - // Load up the extension details - $extension = JTable::getInstance('extension'); - $extension->load($eid); - // Grab a copy of the client details - $client = JApplicationHelper::getClientInfo($extension->get('client_id')); - - // Check the element isn't blank to prevent nuking the languages directory...just in case - $element = $extension->get('element'); - if (empty($element)) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_ELEMENT_EMPTY')); - return false; - } - - // Check that the language is not protected, Normally en-GB. - $protected = $extension->get('protected'); - if ($protected == 1) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_PROTECTED')); - return false; - } - - // Verify that it's not the default language for that client - $params = JComponentHelper::getParams('com_languages'); - if ($params->get($client->name) == $element) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_DEFAULT')); - return false; - } - - // Construct the path from the client, the language and the extension element name - $path = $client->path . '/language/' . $element; - - // Get the package manifest object and remove media - $this->parent->setPath('source', $path); - // We do findManifest to avoid problem when uninstalling a list of extension: getManifest cache its manifest file - $this->parent->findManifest(); - $this->manifest = $this->parent->getManifest(); - $this->parent->removeFiles($this->manifest->media); - - // Check it exists - if (!JFolder::exists($path)) - { - // If the folder doesn't exist lets just nuke the row as well and presume the user killed it for us - $extension->delete(); - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_PATH_EMPTY')); - return false; - } - - if (!JFolder::delete($path)) - { - // If deleting failed we'll leave the extension entry in tact just in case - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_DIRECTORY')); - return false; - } - - // Remove the extension table entry - $extension->delete(); - - // Setting the language of users which have this language as the default language - $db = JFactory::getDbo(); - $query = $db->getQuery(true); - $query->from('#__users'); - $query->select('*'); - $db->setQuery($query); - $users = $db->loadObjectList(); - if ($client->name == 'administrator') - { - $param_name = 'admin_language'; - } - else - { - $param_name = 'language'; - } - - $count = 0; - foreach ($users as $user) - { - $registry = new JRegistry; - $registry->loadString($user->params); - if ($registry->get($param_name) == $element) - { - $registry->set($param_name, ''); - $query = $db->getQuery(true); - $query->update('#__users'); - $query->set('params=' . $db->quote($registry)); - $query->where('id=' . (int) $user->id); - $db->setQuery($query); - $db->query(); - $count = $count + 1; - } - } - if (!empty($count)) - { - JError::raiseNotice(500, JText::plural('JLIB_INSTALLER_NOTICE_LANG_RESET_USERS', $count)); - } - - // All done! - return true; - } - - /** - * Custom discover method - * Finds language files - * - * @return boolean True on success - * - * @since 11.1 - */ - public function discover() - { - $results = array(); - $site_languages = JFolder::folders(JPATH_SITE . '/language'); - $admin_languages = JFolder::folders(JPATH_ADMINISTRATOR . '/language'); - foreach ($site_languages as $language) - { - if (file_exists(JPATH_SITE . '/language/' . $language . '/' . $language . '.xml')) - { - $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_SITE . '/language/' . $language . '/' . $language . '.xml'); - $extension = JTable::getInstance('extension'); - $extension->set('type', 'language'); - $extension->set('client_id', 0); - $extension->set('element', $language); - $extension->set('name', $language); - $extension->set('state', -1); - $extension->set('manifest_cache', json_encode($manifest_details)); - $results[] = $extension; - } - } - foreach ($admin_languages as $language) - { - if (file_exists(JPATH_ADMINISTRATOR . '/language/' . $language . '/' . $language . '.xml')) - { - $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_ADMINISTRATOR . '/language/' . $language . '/' . $language . '.xml'); - $extension = JTable::getInstance('extension'); - $extension->set('type', 'language'); - $extension->set('client_id', 1); - $extension->set('element', $language); - $extension->set('name', $language); - $extension->set('state', -1); - $extension->set('manifest_cache', json_encode($manifest_details)); - $results[] = $extension; - } - } - return $results; - } - - /** - * Custom discover install method - * Basically updates the manifest cache and leaves everything alone - * - * @return integer The extension id - * - * @since 11.1 - */ - public function discover_install() - { - // Need to find to find where the XML file is since we don't store this normally - $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); - $short_element = $this->parent->extension->element; - $manifestPath = $client->path . '/language/' . $short_element . '/' . $short_element . '.xml'; - $this->parent->manifest = $this->parent->isManifest($manifestPath); - $this->parent->setPath('manifest', $manifestPath); - $this->parent->setPath('source', $client->path . '/language/' . $short_element); - $this->parent->setPath('extension_root', $this->parent->getPath('source')); - $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); - $this->parent->extension->manifest_cache = json_encode($manifest_details); - $this->parent->extension->state = 0; - $this->parent->extension->name = $manifest_details['name']; - $this->parent->extension->enabled = 1; - //$this->parent->extension->params = $this->parent->getParams(); - try - { - $this->parent->extension->store(); - } - catch (JException $e) - { - JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_LANG_DISCOVER_STORE_DETAILS')); - return false; - } - return $this->parent->extension->get('extension_id'); - } - - /** - * Refreshes the extension table cache - * - * @return boolean result of operation, true if updated, false on failure - * - * @since 11.1 - */ - public function refreshManifestCache() - { - $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); - $manifestPath = $client->path . '/language/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml'; - $this->parent->manifest = $this->parent->isManifest($manifestPath); - $this->parent->setPath('manifest', $manifestPath); - $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); - $this->parent->extension->manifest_cache = json_encode($manifest_details); - $this->parent->extension->name = $manifest_details['name']; - - if ($this->parent->extension->store()) - { - return true; - } - else - { - JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_MOD_REFRESH_MANIFEST_CACHE')); - - return false; - } - } -} +parent->getPath('source'); + if (!$source) + { + $this->parent + ->setPath( + 'source', + ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/language/' . $this->parent->extension->element + ); + } + $this->manifest = $this->parent->getManifest(); + $root = $this->manifest->document; + + // Get the client application target + if ((string) $this->manifest->attributes()->client == 'both') + { + JError::raiseWarning(42, JText::_('JLIB_INSTALLER_ERROR_DEPRECATED_FORMAT')); + $element = $this->manifest->site->files; + if (!$this->_install('site', JPATH_SITE, 0, $element)) + { + return false; + } + + $element = $this->manifest->administration->files; + if (!$this->_install('administrator', JPATH_ADMINISTRATOR, 1, $element)) + { + return false; + } + // This causes an issue because we have two eid's, *sigh* nasty hacks! + return true; + } + elseif ($cname = (string) $this->manifest->attributes()->client) + { + // Attempt to map the client to a base path + $client = JApplicationHelper::getClientInfo($cname, true); + if ($client === null) + { + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_UNKNOWN_CLIENT_TYPE', $cname))); + return false; + } + $basePath = $client->path; + $clientId = $client->id; + $element = $this->manifest->files; + + return $this->_install($cname, $basePath, $clientId, $element); + } + else + { + // No client attribute was found so we assume the site as the client + $cname = 'site'; + $basePath = JPATH_SITE; + $clientId = 0; + $element = $this->manifest->files; + + return $this->_install($cname, $basePath, $clientId, $element); + } + } + + /** + * Install function that is designed to handle individual clients + * + * @param string $cname Cname @todo: not used + * @param string $basePath The base name. + * @param integer $clientId The client id. + * @param object &$element The XML element. + * + * @return boolean + * + * @since 11.1 + */ + protected function _install($cname, $basePath, $clientId, &$element) + { + $this->manifest = $this->parent->getManifest(); + + // Get the language name + // Set the extensions name + $name = JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'); + $this->set('name', $name); + + // Get the Language tag [ISO tag, eg. en-GB] + $tag = (string) $this->manifest->tag; + + // Check if we found the tag - if we didn't, we may be trying to install from an older language package + if (!$tag) + { + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::_('JLIB_INSTALLER_ERROR_NO_LANGUAGE_TAG'))); + return false; + } + + $this->set('tag', $tag); + + // Set the language installation path + $this->parent->setPath('extension_site', $basePath . '/language/' . $tag); + + // Do we have a meta file in the file list? In other words... is this a core language pack? + if ($element && count($element->children())) + { + $files = $element->children(); + foreach ($files as $file) + { + if ((string) $file->attributes()->file == 'meta') + { + $this->_core = true; + break; + } + } + } + + // Either we are installing a core pack or a core pack must exist for the language we are installing. + if (!$this->_core) + { + if (!JFile::exists($this->parent->getPath('extension_site') . '/' . $this->get('tag') . '.xml')) + { + $this->parent + ->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_NO_CORE_LANGUAGE', $this->get('tag')))); + return false; + } + } + + // If the language directory does not exist, let's create it + $created = false; + if (!file_exists($this->parent->getPath('extension_site'))) + { + if (!$created = JFolder::create($this->parent->getPath('extension_site'))) + { + $this->parent + ->abort( + JText::sprintf( + 'JLIB_INSTALLER_ABORT', + JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_FOLDER_FAILED', $this->parent->getPath('extension_site')) + ) + ); + return false; + } + } + else + { + // Look for an update function or update tag + $updateElement = $this->manifest->update; + // Upgrade manually set or + // Update function available or + // Update tag detected + if ($this->parent->getUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update')) + || $updateElement) + { + return $this->update(); // transfer control to the update function + } + elseif (!$this->parent->getOverwrite()) + { + // Overwrite is set + // We didn't have overwrite set, find an update function or find an update tag so lets call it safe + if (file_exists($this->parent->getPath('extension_site'))) + { + // If the site exists say so. + JError::raiseWarning( + 1, + JText::sprintf( + 'JLIB_INSTALLER_ABORT', + JText::sprintf('JLIB_INSTALLER_ERROR_FOLDER_IN_USE', $this->parent->getPath('extension_site')) + ) + ); + } + else + { + // If the admin exists say so. + JError::raiseWarning( + 1, + JText::sprintf( + 'JLIB_INSTALLER_ABORT', + JText::sprintf('JLIB_INSTALLER_ERROR_FOLDER_IN_USE', $this->parent->getPath('extension_administrator')) + ) + ); + } + return false; + } + } + + /* + * If we created the language directory we will want to remove it if we + * have to roll back the installation, so let's add it to the installation + * step stack + */ + if ($created) + { + $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_site'))); + } + + // Copy all the necessary files + if ($this->parent->parseFiles($element) === false) + { + // Install failed, rollback changes + $this->parent->abort(); + return false; + } + + // Parse optional tags + $this->parent->parseMedia($this->manifest->media); + + // Copy all the necessary font files to the common pdf_fonts directory + $this->parent->setPath('extension_site', $basePath . '/language/pdf_fonts'); + $overwrite = $this->parent->setOverwrite(true); + if ($this->parent->parseFiles($this->manifest->fonts) === false) + { + // Install failed, rollback changes + $this->parent->abort(); + return false; + } + $this->parent->setOverwrite($overwrite); + + // Get the language description + $description = (string) $this->manifest->description; + if ($description) + { + $this->parent->set('message', JText::_($description)); + } + else + { + $this->parent->set('message', ''); + } + + // Add an entry to the extension table with a whole heap of defaults + $row = JTable::getInstance('extension'); + $row->set('name', $this->get('name')); + $row->set('type', 'language'); + $row->set('element', $this->get('tag')); + // There is no folder for languages + $row->set('folder', ''); + $row->set('enabled', 1); + $row->set('protected', 0); + $row->set('access', 0); + $row->set('client_id', $clientId); + $row->set('params', $this->parent->getParams()); + $row->set('manifest_cache', $this->parent->generateManifestCache()); + + if (!$row->store()) + { + // Install failed, roll back changes + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', $row->getError())); + return false; + } + + // Clobber any possible pending updates + $update = JTable::getInstance('update'); + $uid = $update->find(array('element' => $this->get('tag'), 'type' => 'language', 'client_id' => '', 'folder' => '')); + if ($uid) + { + $update->delete($uid); + } + + return $row->get('extension_id'); + } + + /** + * Custom update method + * + * @return boolean True on success, false on failure + * + * @since 11.1 + */ + public function update() + { + $xml = $this->parent->getManifest(); + + $this->manifest = $xml; + + $cname = $xml->attributes()->client; + + // Attempt to map the client to a base path + $client = JApplicationHelper::getClientInfo($cname, true); + if ($client === null || (empty($cname) && $cname !== 0)) + { + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_UNKNOWN_CLIENT_TYPE', $cname))); + return false; + } + $basePath = $client->path; + $clientId = $client->id; + + // Get the language name + // Set the extensions name + $name = (string) $this->manifest->name; + $name = JFilterInput::getInstance()->clean($name, 'cmd'); + $this->set('name', $name); + + // Get the Language tag [ISO tag, eg. en-GB] + $tag = (string) $xml->tag; + + // Check if we found the tag - if we didn't, we may be trying to install from an older language package + if (!$tag) + { + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::_('JLIB_INSTALLER_ERROR_NO_LANGUAGE_TAG'))); + return false; + } + + $this->set('tag', $tag); + $folder = $tag; + + // Set the language installation path + $this->parent->setPath('extension_site', $basePath . '/language/' . $this->get('tag')); + + // Do we have a meta file in the file list? In other words... is this a core language pack? + if (count($xml->files->children())) + { + foreach ($xml->files->children() as $file) + { + if ((string) $file->attributes()->file == 'meta') + { + $this->_core = true; + break; + } + } + } + + // Either we are installing a core pack or a core pack must exist for the language we are installing. + if (!$this->_core) + { + if (!JFile::exists($this->parent->getPath('extension_site') . '/' . $this->get('tag') . '.xml')) + { + $this->parent + ->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_NO_CORE_LANGUAGE', $this->get('tag')))); + return false; + } + } + + // Copy all the necessary files + if ($this->parent->parseFiles($xml->files) === false) + { + // Install failed, rollback changes + $this->parent->abort(); + return false; + } + + // Parse optional tags + $this->parent->parseMedia($xml->media); + + // Copy all the necessary font files to the common pdf_fonts directory + $this->parent->setPath('extension_site', $basePath . '/language/pdf_fonts'); + $overwrite = $this->parent->setOverwrite(true); + if ($this->parent->parseFiles($xml->fonts) === false) + { + // Install failed, rollback changes + $this->parent->abort(); + return false; + } + $this->parent->setOverwrite($overwrite); + + // Get the language description and set it as message + $this->parent->set('message', (string) $xml->description); + + // Finalization and Cleanup Section + + // Clobber any possible pending updates + $update = JTable::getInstance('update'); + $uid = $update->find(array('element' => $this->get('tag'), 'type' => 'language', 'client_id' => $clientId)); + if ($uid) + { + $update->delete($uid); + } + + // Update an entry to the extension table + $row = JTable::getInstance('extension'); + $eid = $row->find(array('element' => strtolower($this->get('tag')), 'type' => 'language', 'client_id' => $clientId)); + if ($eid) + { + $row->load($eid); + } + else + { + // set the defaults + $row->set('folder', ''); // There is no folder for language + $row->set('enabled', 1); + $row->set('protected', 0); + $row->set('access', 0); + $row->set('client_id', $clientId); + $row->set('params', $this->parent->getParams()); + } + $row->set('name', $this->get('name')); + $row->set('type', 'language'); + $row->set('element', $this->get('tag')); + $row->set('manifest_cache', $this->parent->generateManifestCache()); + + if (!$row->store()) + { + // Install failed, roll back changes + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', $row->getError())); + return false; + } + + // And now we run the postflight + ob_start(); + ob_implicit_flush(false); + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight')) + { + $this->parent->manifestClass->postflight('update', $this); + } + $msg .= ob_get_contents(); // append messages + ob_end_clean(); + if ($msg != '') + { + $this->parent->set('extension_message', $msg); + } + + return $row->get('extension_id'); + } + + /** + * Custom uninstall method + * + * @param string $eid The tag of the language to uninstall + * + * @return mixed Return value for uninstall method in component uninstall file + * + * @since 11.1 + */ + public function uninstall($eid) + { + // Load up the extension details + $extension = JTable::getInstance('extension'); + $extension->load($eid); + // Grab a copy of the client details + $client = JApplicationHelper::getClientInfo($extension->get('client_id')); + + // Check the element isn't blank to prevent nuking the languages directory...just in case + $element = $extension->get('element'); + if (empty($element)) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_ELEMENT_EMPTY')); + return false; + } + + // Check that the language is not protected, Normally en-GB. + $protected = $extension->get('protected'); + if ($protected == 1) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_PROTECTED')); + return false; + } + + // Verify that it's not the default language for that client + $params = JComponentHelper::getParams('com_languages'); + if ($params->get($client->name) == $element) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_DEFAULT')); + return false; + } + + // Construct the path from the client, the language and the extension element name + $path = $client->path . '/language/' . $element; + + // Get the package manifest object and remove media + $this->parent->setPath('source', $path); + // We do findManifest to avoid problem when uninstalling a list of extension: getManifest cache its manifest file + $this->parent->findManifest(); + $this->manifest = $this->parent->getManifest(); + $this->parent->removeFiles($this->manifest->media); + + // Check it exists + if (!JFolder::exists($path)) + { + // If the folder doesn't exist lets just nuke the row as well and presume the user killed it for us + $extension->delete(); + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_PATH_EMPTY')); + return false; + } + + if (!JFolder::delete($path)) + { + // If deleting failed we'll leave the extension entry in tact just in case + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_DIRECTORY')); + return false; + } + + // Remove the extension table entry + $extension->delete(); + + // Setting the language of users which have this language as the default language + $db = JFactory::getDbo(); + $query = $db->getQuery(true); + $query->from('#__users'); + $query->select('*'); + $db->setQuery($query); + $users = $db->loadObjectList(); + if ($client->name == 'administrator') + { + $param_name = 'admin_language'; + } + else + { + $param_name = 'language'; + } + + $count = 0; + foreach ($users as $user) + { + $registry = new JRegistry; + $registry->loadString($user->params); + if ($registry->get($param_name) == $element) + { + $registry->set($param_name, ''); + $query = $db->getQuery(true); + $query->update('#__users'); + $query->set('params=' . $db->quote($registry)); + $query->where('id=' . (int) $user->id); + $db->setQuery($query); + $db->query(); + $count = $count + 1; + } + } + if (!empty($count)) + { + JError::raiseNotice(500, JText::plural('JLIB_INSTALLER_NOTICE_LANG_RESET_USERS', $count)); + } + + // All done! + return true; + } + + /** + * Custom discover method + * Finds language files + * + * @return boolean True on success + * + * @since 11.1 + */ + public function discover() + { + $results = array(); + $site_languages = JFolder::folders(JPATH_SITE . '/language'); + $admin_languages = JFolder::folders(JPATH_ADMINISTRATOR . '/language'); + foreach ($site_languages as $language) + { + if (file_exists(JPATH_SITE . '/language/' . $language . '/' . $language . '.xml')) + { + $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_SITE . '/language/' . $language . '/' . $language . '.xml'); + $extension = JTable::getInstance('extension'); + $extension->set('type', 'language'); + $extension->set('client_id', 0); + $extension->set('element', $language); + $extension->set('name', $language); + $extension->set('state', -1); + $extension->set('manifest_cache', json_encode($manifest_details)); + $results[] = $extension; + } + } + foreach ($admin_languages as $language) + { + if (file_exists(JPATH_ADMINISTRATOR . '/language/' . $language . '/' . $language . '.xml')) + { + $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_ADMINISTRATOR . '/language/' . $language . '/' . $language . '.xml'); + $extension = JTable::getInstance('extension'); + $extension->set('type', 'language'); + $extension->set('client_id', 1); + $extension->set('element', $language); + $extension->set('name', $language); + $extension->set('state', -1); + $extension->set('manifest_cache', json_encode($manifest_details)); + $results[] = $extension; + } + } + return $results; + } + + /** + * Custom discover install method + * Basically updates the manifest cache and leaves everything alone + * + * @return integer The extension id + * + * @since 11.1 + */ + public function discover_install() + { + // Need to find to find where the XML file is since we don't store this normally + $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); + $short_element = $this->parent->extension->element; + $manifestPath = $client->path . '/language/' . $short_element . '/' . $short_element . '.xml'; + $this->parent->manifest = $this->parent->isManifest($manifestPath); + $this->parent->setPath('manifest', $manifestPath); + $this->parent->setPath('source', $client->path . '/language/' . $short_element); + $this->parent->setPath('extension_root', $this->parent->getPath('source')); + $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); + $this->parent->extension->manifest_cache = json_encode($manifest_details); + $this->parent->extension->state = 0; + $this->parent->extension->name = $manifest_details['name']; + $this->parent->extension->enabled = 1; + //$this->parent->extension->params = $this->parent->getParams(); + try + { + $this->parent->extension->store(); + } + catch (JException $e) + { + JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_LANG_DISCOVER_STORE_DETAILS')); + return false; + } + return $this->parent->extension->get('extension_id'); + } + + /** + * Refreshes the extension table cache + * + * @return boolean result of operation, true if updated, false on failure + * + * @since 11.1 + */ + public function refreshManifestCache() + { + $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); + $manifestPath = $client->path . '/language/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml'; + $this->parent->manifest = $this->parent->isManifest($manifestPath); + $this->parent->setPath('manifest', $manifestPath); + $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); + $this->parent->extension->manifest_cache = json_encode($manifest_details); + $this->parent->extension->name = $manifest_details['name']; + + if ($this->parent->extension->store()) + { + return true; + } + else + { + JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_MOD_REFRESH_MANIFEST_CACHE')); + + return false; + } + } +} diff --git a/libraries/joomla/installer/adapters/module.php b/libraries/joomla/installer/adapters/module.php index 53aa0884a591e..6a2ccd2a53149 100644 --- a/libraries/joomla/installer/adapters/module.php +++ b/libraries/joomla/installer/adapters/module.php @@ -1,945 +1,945 @@ -parent->getPath('source'); - - if (!$source) - { - $this->parent - ->setPath( - 'source', - ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $this->parent->extension->element - ); - } - - $this->manifest = $this->parent->getManifest(); - - if ($this->manifest->files) - { - $element = $this->manifest->files; - $extension = ''; - - if (count($element->children())) - { - foreach ($element->children() as $file) - { - if ((string) $file->attributes()->module) - { - $extension = strtolower((string) $file->attributes()->module); - break; - } - } - } - - if ($extension) - { - $lang = JFactory::getLanguage(); - $source = $path ? $path : ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $extension; - $folder = (string) $element->attributes()->folder; - - if ($folder && file_exists("$path/$folder")) - { - $source = "$path/$folder"; - } - - $client = (string) $this->manifest->attributes()->client; - $lang->load($extension . '.sys', $source, null, false, false) - || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), null, false, false) - || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false) - || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), $lang->getDefault(), false, false); - } - } - } - - /** - * Custom install method - * - * @return boolean True on success - * - * @since 11.1 - */ - public function install() - { - // Get a database connector object - $db = $this->parent->getDbo(); - - // Get the extension manifest object - $this->manifest = $this->parent->getManifest(); - - // Manifest Document Setup Section - - // Set the extensions name - $name = (string) $this->manifest->name; - $name = JFilterInput::getInstance()->clean($name, 'string'); - $this->set('name', $name); - - // Get the component description - $description = (string) $this->manifest->description; - if ($description) - { - $this->parent->set('message', JText::_($description)); - } - else - { - $this->parent->set('message', ''); - } - - // Target Application Section - // Get the target application - if ($cname = (string) $this->manifest->attributes()->client) - { - // Attempt to map the client to a base path - $client = JApplicationHelper::getClientInfo($cname, true); - - if ($client === false) - { - $this->parent - ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_UNKNOWN_CLIENT', JText::_('JLIB_INSTALLER_' . $this->route), $client->name)); - return false; - } - - $basePath = $client->path; - $clientId = $client->id; - } - else - { - // No client attribute was found so we assume the site as the client - $cname = 'site'; - $basePath = JPATH_SITE; - $clientId = 0; - } - - // Set the installation path - $element = ''; - if (count($this->manifest->files->children())) - { - foreach ($this->manifest->files->children() as $file) - { - if ((string) $file->attributes()->module) - { - $element = (string) $file->attributes()->module; - $this->set('element', $element); - - break; - } - } - } - if (!empty($element)) - { - $this->parent->setPath('extension_root', $basePath . '/modules/' . $element); - } - else - { - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_INSTALL_NOFILE', JText::_('JLIB_INSTALLER_' . $this->route))); - - return false; - } - - // Check to see if a module by the same name is already installed - // If it is, then update the table because if the files aren't there - // we can assume that it was (badly) uninstalled - // If it isn't, add an entry to extensions - $query = $db->getQuery(true); - $query->select($query->qn('extension_id'))->from($query->qn('#__extensions')); - $query->where($query->qn('element') . ' = ' . $query->q($element))->where($query->qn('client_id') . ' = ' . (int) $clientId); - $db->setQuery($query); - - try - { - $db->Query(); - } - catch (JException $e) - { - // Install failed, roll back changes - $this->parent - ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))); - - return false; - } - - $id = $db->loadResult(); - - // If the module directory already exists, then we will assume that the - // module is already installed or another module is using that - // directory. - // Check that this is either an issue where its not overwriting or it is - // set to upgrade anyway - - if (file_exists($this->parent->getPath('extension_root')) && (!$this->parent->getOverwrite() || $this->parent->getUpgrade())) - { - // Look for an update function or update tag - $updateElement = $this->manifest->update; - // Upgrade manually set or - // Update function available or - // Update tag detected - if ($this->parent->getUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update')) - || is_a($updateElement, 'JXMLElement')) - { - // Force this one - $this->parent->setOverwrite(true); - $this->parent->setUpgrade(true); - - if ($id) - { - // If there is a matching extension mark this as an update; semantics really - $this->route = 'Update'; - } - } - elseif (!$this->parent->getOverwrite()) - { - // Overwrite is set - // We didn't have overwrite set, find an update function or find an update tag so lets call it safe - $this->parent - ->abort( - JText::sprintf( - 'JLIB_INSTALLER_ABORT_MOD_INSTALL_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route), - $this->parent->getPath('extension_root') - ) - ); - - return false; - } - } - - // Installer Trigger Loading - - // If there is an manifest class file, let's load it; we'll copy it later (don't have destination yet) - $this->scriptElement = $this->manifest->scriptfile; - $manifestScript = (string) $this->manifest->scriptfile; - - if ($manifestScript) - { - $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript; - - if (is_file($manifestScriptFile)) - { - // Load the file - include_once $manifestScriptFile; - } - - // Set the class name - $classname = $element . 'InstallerScript'; - - if (class_exists($classname)) - { - // Create a new instance. - $this->parent->manifestClass = new $classname($this); - // And set this so we can copy it later. - $this->set('manifest_script', $manifestScript); - - // Note: if we don't find the class, don't bother to copy the file. - } - } - - // Run preflight if possible (since we know we're not an update) - ob_start(); - ob_implicit_flush(false); - - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight')) - { - if ($this->parent->manifestClass->preflight($this->route, $this) === false) - { - // Install failed, rollback changes - $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_CUSTOM_INSTALL_FAILURE')); - - return false; - } - } - - // Create msg object; first use here - $msg = ob_get_contents(); - ob_end_clean(); - - // Filesystem Processing Section - - // If the module directory does not exist, lets create it - $created = false; - - if (!file_exists($this->parent->getPath('extension_root'))) - { - if (!$created = JFolder::create($this->parent->getPath('extension_root'))) - { - $this->parent - ->abort( - JText::sprintf( - 'JLIB_INSTALLER_ABORT_MOD_INSTALL_CREATE_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route), - $this->parent->getPath('extension_root') - ) - ); - - return false; - } - } - - // Since we created the module directory and will want to remove it if - // we have to roll back the installation, let's add it to the - // installation step stack - - if ($created) - { - $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_root'))); - } - - // Copy all necessary files - if ($this->parent->parseFiles($this->manifest->files, -1) === false) - { - // Install failed, roll back changes - $this->parent->abort(); - - return false; - } - - // If there is a manifest script, let's copy it. - if ($this->get('manifest_script')) - { - $path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script'); - $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->get('manifest_script'); - - if (!file_exists($path['dest']) || $this->parent->getOverwrite()) - { - if (!$this->parent->copyFiles(array($path))) - { - // Install failed, rollback changes - $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_MANIFEST')); - - return false; - } - } - } - - // Parse optional tags - $this->parent->parseMedia($this->manifest->media, $clientId); - $this->parent->parseLanguages($this->manifest->languages, $clientId); - - // Parse deprecated tags - $this->parent->parseFiles($this->manifest->images, -1); - - // Database Processing Section - - $row = JTable::getInstance('extension'); - - // Was there a module already installed with the same name? - if ($id) - { - // Load the entry and update the manifest_cache - $row->load($id); - $row->name = $this->get('name'); // update name - $row->manifest_cache = $this->parent->generateManifestCache(); // update manifest - - if (!$row->store()) - { - // Install failed, roll back changes - $this->parent - ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))); - - return false; - } - } - else - { - $row->set('name', $this->get('name')); - $row->set('type', 'module'); - $row->set('element', $this->get('element')); - $row->set('folder', ''); // There is no folder for modules - $row->set('enabled', 1); - $row->set('protected', 0); - $row->set('access', $clientId == 1 ? 2 : 0); - $row->set('client_id', $clientId); - $row->set('params', $this->parent->getParams()); - $row->set('custom_data', ''); // custom data - $row->set('manifest_cache', $this->parent->generateManifestCache()); - - if (!$row->store()) - { - // Install failed, roll back changes - $this->parent - ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))); - return false; - } - - // Set the insert id - $row->extension_id = $db->insertid(); - - // Since we have created a module item, we add it to the installation step stack - // so that if we have to rollback the changes we can undo it. - $this->parent->pushStep(array('type' => 'extension', 'extension_id' => $row->extension_id)); - - // Create unpublished module in jos_modules - $name = preg_replace('#[\*?]#', '', JText::_($this->get('name'))); - $module = JTable::getInstance('module'); - $module->set('title', $name); - $module->set('module', $this->get('element')); - $module->set('access', '1'); - $module->set('showtitle', '1'); - $module->set('client_id', $clientId); - $module->set('language', '*'); - - $module->store(); - } - - // Let's run the queries for the module - // If Joomla 1.5 compatible, with discrete sql files, execute appropriate - // file for utf-8 support or non-utf-8 support - - // Try for Joomla 1.5 type queries - // Second argument is the utf compatible version attribute - if (strtolower($this->route) == 'install') - { - $utfresult = $this->parent->parseSQLFiles($this->manifest->install->sql); - - if ($utfresult === false) - { - // Install failed, rollback changes - $this->parent - ->abort( - JText::sprintf('JLIB_INSTALLER_ABORT_MOD_INSTALL_SQL_ERROR', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true)) - ); - - return false; - } - - // Set the schema version to be the latest update version - if ($this->manifest->update) - { - $this->parent->setSchemaVersion($this->manifest->update->schemas, $row->extension_id); - } - } - elseif (strtolower($this->route) == 'update') - { - if ($this->manifest->update) - { - $result = $this->parent->parseSchemaUpdates($this->manifest->update->schemas, $row->extension_id); - if ($result === false) - { - // Install failed, rollback changes - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_UPDATE_SQL_ERROR', $db->stderr(true))); - return false; - } - } - } - - // Start Joomla! 1.6 - ob_start(); - ob_implicit_flush(false); - - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, $this->route)) - { - if ($this->parent->manifestClass->{$this->route}($this) === false) - { - // Install failed, rollback changes - $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_CUSTOM_INSTALL_FAILURE')); - - return false; - } - } - - // Append messages - $msg .= ob_get_contents(); - ob_end_clean(); - - // Finalization and Cleanup Section - - // Lastly, we will copy the manifest file to its appropriate place. - if (!$this->parent->copyManifest(-1)) - { - // Install failed, rollback changes - $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_COPY_SETUP')); - - return false; - } - - // And now we run the postflight - ob_start(); - ob_implicit_flush(false); - - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight')) - { - $this->parent->manifestClass->postflight($this->route, $this); - } - - // Append messages - $msg .= ob_get_contents(); - ob_end_clean(); - - if ($msg != '') - { - $this->parent->set('extension_message', $msg); - } - - return $row->get('extension_id'); - } - - /** - * Custom update method - * - * This is really a shell for the install system - * - * @return boolean True on success. - * - * @since 11.1 - */ - public function update() - { - // Set the overwrite setting - $this->parent->setOverwrite(true); - $this->parent->setUpgrade(true); - // Set the route for the install - $this->route = 'Update'; - - // Go to install which handles updates properly - return $this->install(); - } - - /** - * Custom discover method - * - * @return array JExtension list of extensions available - * - * @since 11.1 - */ - public function discover() - { - $results = array(); - $site_list = JFolder::folders(JPATH_SITE . '/modules'); - $admin_list = JFolder::folders(JPATH_ADMINISTRATOR . '/modules'); - $site_info = JApplicationHelper::getClientInfo('site', true); - $admin_info = JApplicationHelper::getClientInfo('administrator', true); - - foreach ($site_list as $module) - { - $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_SITE . "/modules/$module/$module.xml"); - $extension = JTable::getInstance('extension'); - $extension->set('type', 'module'); - $extension->set('client_id', $site_info->id); - $extension->set('element', $module); - $extension->set('name', $module); - $extension->set('state', -1); - $extension->set('manifest_cache', json_encode($manifest_details)); - $results[] = clone $extension; - } - - foreach ($admin_list as $module) - { - $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_ADMINISTRATOR . "/modules/$module/$module.xml"); - $extension = JTable::getInstance('extension'); - $extension->set('type', 'module'); - $extension->set('client_id', $admin_info->id); - $extension->set('element', $module); - $extension->set('name', $module); - $extension->set('state', -1); - $extension->set('manifest_cache', json_encode($manifest_details)); - $results[] = clone $extension; - } - - return $results; - } - - /** - * Custom discover_install method - * - * @return mixed Extension ID on success, boolean false on failure - * - * @since 11.1 - */ - public function discover_install() - { - // Modules are like templates, and are one of the easiest - // If its not in the extensions table we just add it - $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); - $manifestPath = $client->path . '/modules/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml'; - $this->parent->manifest = $this->parent->isManifest($manifestPath); - $description = (string) $this->parent->manifest->description; - - if ($description) - { - $this->parent->set('message', JText::_($description)); - } - else - { - $this->parent->set('message', ''); - } - - $this->parent->setPath('manifest', $manifestPath); - $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); - // TODO: Re-evaluate this; should we run installation triggers? postflight perhaps? - $this->parent->extension->manifest_cache = json_encode($manifest_details); - $this->parent->extension->state = 0; - $this->parent->extension->name = $manifest_details['name']; - $this->parent->extension->enabled = 1; - $this->parent->extension->params = $this->parent->getParams(); - if ($this->parent->extension->store()) - { - return $this->parent->extension->get('extension_id'); - } - else - { - JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_MOD_DISCOVER_STORE_DETAILS')); - - return false; - } - } - - /** - * Refreshes the extension table cache - * - * @return boolean Result of operation, true if updated, false on failure. - * - * @since 11.1 - */ - public function refreshManifestCache() - { - $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); - $manifestPath = $client->path . '/modules/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml'; - $this->parent->manifest = $this->parent->isManifest($manifestPath); - $this->parent->setPath('manifest', $manifestPath); - $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); - $this->parent->extension->manifest_cache = json_encode($manifest_details); - $this->parent->extension->name = $manifest_details['name']; - - if ($this->parent->extension->store()) - { - return true; - } - else - { - JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_MOD_REFRESH_MANIFEST_CACHE')); - - return false; - } - } - - /** - * Custom uninstall method - * - * @param integer $id The id of the module to uninstall - * - * @return boolean True on success - * - * @since 11.1 - */ - public function uninstall($id) - { - // Initialise variables. - $row = null; - $retval = true; - $db = $this->parent->getDbo(); - - // First order of business will be to load the module object table from the database. - // This should give us the necessary information to proceed. - $row = JTable::getInstance('extension'); - - if (!$row->load((int) $id) || !strlen($row->element)) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_ERRORUNKOWNEXTENSION')); - return false; - } - - // Is the module we are trying to uninstall a core one? - // Because that is not a good idea... - if ($row->protected) - { - JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_WARNCOREMODULE', $row->name)); - return false; - } - - // Get the extension root path - $element = $row->element; - $client = JApplicationHelper::getClientInfo($row->client_id); - - if ($client === false) - { - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_UNKNOWN_CLIENT', $row->client_id)); - return false; - } - $this->parent->setPath('extension_root', $client->path . '/modules/' . $element); - - $this->parent->setPath('source', $this->parent->getPath('extension_root')); - - // Get the package manifest objecct - // We do findManifest to avoid problem when uninstalling a list of extensions: getManifest cache its manifest file. - $this->parent->findManifest(); - $this->manifest = $this->parent->getManifest(); - - // Attempt to load the language file; might have uninstall strings - $this->loadLanguage(($row->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $element); - - // If there is an manifest class file, let's load it - $this->scriptElement = $this->manifest->scriptfile; - $manifestScript = (string) $this->manifest->scriptfile; - - if ($manifestScript) - { - $manifestScriptFile = $this->parent->getPath('extension_root') . '/' . $manifestScript; - - if (is_file($manifestScriptFile)) - { - // Load the file - include_once $manifestScriptFile; - } - - // Set the class name - $classname = $element . 'InstallerScript'; - - if (class_exists($classname)) - { - // Create a new instance - $this->parent->manifestClass = new $classname($this); - // And set this so we can copy it later - $this->set('manifest_script', $manifestScript); - - // Note: if we don't find the class, don't bother to copy the file - } - } - - ob_start(); - ob_implicit_flush(false); - - // Run uninstall if possible - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'uninstall')) - { - $this->parent->manifestClass->uninstall($this); - } - - $msg = ob_get_contents(); - ob_end_clean(); - - if (!($this->manifest instanceof JXMLElement)) - { - // Make sure we delete the folders - JFolder::delete($this->parent->getPath('extension_root')); - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_INVALID_NOTFOUND_MANIFEST')); - - return false; - } - - /* - * Let's run the uninstall queries for the component - * If Joomla 1.5 compatible, with discreet sql files - execute appropriate - * file for utf-8 support or non-utf support - */ - // Try for Joomla 1.5 type queries - // Second argument is the utf compatible version attribute - $utfresult = $this->parent->parseSQLFiles($this->manifest->uninstall->sql); - - if ($utfresult === false) - { - // Install failed, rollback changes - JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_SQL_ERROR', $db->stderr(true))); - $retval = false; - } - - // Remove the schema version - $query = $db->getQuery(true); - $query->delete()->from('#__schemas')->where('extension_id = ' . $row->extension_id); - $db->setQuery($query); - $db->Query(); - - // Remove other files - $this->parent->removeFiles($this->manifest->media); - $this->parent->removeFiles($this->manifest->languages, $row->client_id); - - // Let's delete all the module copies for the type we are uninstalling - $query = $db->getQuery(true); - $query->select($query->qn('id'))->from($query->qn('#__modules')); - $query->where($query->qn('module') . ' = ' . $query->q($row->element)); - $query->where($query->qn('client_id') . ' = ' . (int) $row->client_id); - $db->setQuery($query); - - try - { - $modules = $db->loadColumn(); - } - catch (JException $e) - { - $modules = array(); - } - - // Do we have any module copies? - if (count($modules)) - { - // Ensure the list is sane - JArrayHelper::toInteger($modules); - $modID = implode(',', $modules); - - // Wipe out any items assigned to menus - $query = 'DELETE' . ' FROM #__modules_menu' . ' WHERE moduleid IN (' . $modID . ')'; - $db->setQuery($query); - try - { - $db->query(); - } - catch (JException $e) - { - JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_EXCEPTION', $db->stderr(true))); - $retval = false; - } - - // Wipe out any instances in the modules table - $query = 'DELETE' . ' FROM #__modules' . ' WHERE id IN (' . $modID . ')'; - $db->setQuery($query); - - try - { - $db->query(); - } - catch (JException $e) - { - JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_EXCEPTION', $db->stderr(true))); - $retval = false; - } - } - - // Now we will no longer need the module object, so let's delete it and free up memory - $row->delete($row->extension_id); - $query = 'DELETE FROM #__modules WHERE module = ' . $db->Quote($row->element) . ' AND client_id = ' . $row->client_id; - $db->setQuery($query); - - try - { - // Clean up any other ones that might exist as well - $db->Query(); - } - catch (JException $e) - { - // Ignore the error... - } - - unset($row); - - // Remove the installation folder - if (!JFolder::delete($this->parent->getPath('extension_root'))) - { - // JFolder should raise an error - $retval = false; - } - - return $retval; - } - - /** - * Custom rollback method - * - Roll back the menu item - * - * @param array $arg Installation step to rollback - * - * @return boolean True on success - * - * @since 11.1 - */ - protected function _rollback_menu($arg) - { - // Get database connector object - $db = $this->parent->getDbo(); - - // Remove the entry from the #__modules_menu table - $query = 'DELETE' . ' FROM #__modules_menu' . ' WHERE moduleid=' . (int) $arg['id']; - $db->setQuery($query); - - try - { - return $db->query(); - } - catch (JException $e) - { - return false; - } - } - - /** - * Custom rollback method - * - Roll back the module item - * - * @param array $arg Installation step to rollback - * - * @return boolean True on success - * - * @since 11.1 - */ - protected function _rollback_module($arg) - { - // Get database connector object - $db = $this->parent->getDbo(); - - // Remove the entry from the #__modules table - $query = 'DELETE' . ' FROM #__modules' . ' WHERE id=' . (int) $arg['id']; - $db->setQuery($query); - try - { - return $db->query(); - } - catch (JException $e) - { - return false; - } - } -} +parent->getPath('source'); + + if (!$source) + { + $this->parent + ->setPath( + 'source', + ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $this->parent->extension->element + ); + } + + $this->manifest = $this->parent->getManifest(); + + if ($this->manifest->files) + { + $element = $this->manifest->files; + $extension = ''; + + if (count($element->children())) + { + foreach ($element->children() as $file) + { + if ((string) $file->attributes()->module) + { + $extension = strtolower((string) $file->attributes()->module); + break; + } + } + } + + if ($extension) + { + $lang = JFactory::getLanguage(); + $source = $path ? $path : ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $extension; + $folder = (string) $element->attributes()->folder; + + if ($folder && file_exists("$path/$folder")) + { + $source = "$path/$folder"; + } + + $client = (string) $this->manifest->attributes()->client; + $lang->load($extension . '.sys', $source, null, false, false) + || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), null, false, false) + || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false) + || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), $lang->getDefault(), false, false); + } + } + } + + /** + * Custom install method + * + * @return boolean True on success + * + * @since 11.1 + */ + public function install() + { + // Get a database connector object + $db = $this->parent->getDbo(); + + // Get the extension manifest object + $this->manifest = $this->parent->getManifest(); + + // Manifest Document Setup Section + + // Set the extensions name + $name = (string) $this->manifest->name; + $name = JFilterInput::getInstance()->clean($name, 'string'); + $this->set('name', $name); + + // Get the component description + $description = (string) $this->manifest->description; + if ($description) + { + $this->parent->set('message', JText::_($description)); + } + else + { + $this->parent->set('message', ''); + } + + // Target Application Section + // Get the target application + if ($cname = (string) $this->manifest->attributes()->client) + { + // Attempt to map the client to a base path + $client = JApplicationHelper::getClientInfo($cname, true); + + if ($client === false) + { + $this->parent + ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_UNKNOWN_CLIENT', JText::_('JLIB_INSTALLER_' . $this->route), $client->name)); + return false; + } + + $basePath = $client->path; + $clientId = $client->id; + } + else + { + // No client attribute was found so we assume the site as the client + $cname = 'site'; + $basePath = JPATH_SITE; + $clientId = 0; + } + + // Set the installation path + $element = ''; + if (count($this->manifest->files->children())) + { + foreach ($this->manifest->files->children() as $file) + { + if ((string) $file->attributes()->module) + { + $element = (string) $file->attributes()->module; + $this->set('element', $element); + + break; + } + } + } + if (!empty($element)) + { + $this->parent->setPath('extension_root', $basePath . '/modules/' . $element); + } + else + { + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_INSTALL_NOFILE', JText::_('JLIB_INSTALLER_' . $this->route))); + + return false; + } + + // Check to see if a module by the same name is already installed + // If it is, then update the table because if the files aren't there + // we can assume that it was (badly) uninstalled + // If it isn't, add an entry to extensions + $query = $db->getQuery(true); + $query->select($query->qn('extension_id'))->from($query->qn('#__extensions')); + $query->where($query->qn('element') . ' = ' . $query->q($element))->where($query->qn('client_id') . ' = ' . (int) $clientId); + $db->setQuery($query); + + try + { + $db->Query(); + } + catch (JException $e) + { + // Install failed, roll back changes + $this->parent + ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))); + + return false; + } + + $id = $db->loadResult(); + + // If the module directory already exists, then we will assume that the + // module is already installed or another module is using that + // directory. + // Check that this is either an issue where its not overwriting or it is + // set to upgrade anyway + + if (file_exists($this->parent->getPath('extension_root')) && (!$this->parent->getOverwrite() || $this->parent->getUpgrade())) + { + // Look for an update function or update tag + $updateElement = $this->manifest->update; + // Upgrade manually set or + // Update function available or + // Update tag detected + if ($this->parent->getUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update')) + || $updateElement) + { + // Force this one + $this->parent->setOverwrite(true); + $this->parent->setUpgrade(true); + + if ($id) + { + // If there is a matching extension mark this as an update; semantics really + $this->route = 'Update'; + } + } + elseif (!$this->parent->getOverwrite()) + { + // Overwrite is set + // We didn't have overwrite set, find an update function or find an update tag so lets call it safe + $this->parent + ->abort( + JText::sprintf( + 'JLIB_INSTALLER_ABORT_MOD_INSTALL_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route), + $this->parent->getPath('extension_root') + ) + ); + + return false; + } + } + + // Installer Trigger Loading + + // If there is an manifest class file, let's load it; we'll copy it later (don't have destination yet) + $this->scriptElement = $this->manifest->scriptfile; + $manifestScript = (string) $this->manifest->scriptfile; + + if ($manifestScript) + { + $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript; + + if (is_file($manifestScriptFile)) + { + // Load the file + include_once $manifestScriptFile; + } + + // Set the class name + $classname = $element . 'InstallerScript'; + + if (class_exists($classname)) + { + // Create a new instance. + $this->parent->manifestClass = new $classname($this); + // And set this so we can copy it later. + $this->set('manifest_script', $manifestScript); + + // Note: if we don't find the class, don't bother to copy the file. + } + } + + // Run preflight if possible (since we know we're not an update) + ob_start(); + ob_implicit_flush(false); + + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight')) + { + if ($this->parent->manifestClass->preflight($this->route, $this) === false) + { + // Install failed, rollback changes + $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_CUSTOM_INSTALL_FAILURE')); + + return false; + } + } + + // Create msg object; first use here + $msg = ob_get_contents(); + ob_end_clean(); + + // Filesystem Processing Section + + // If the module directory does not exist, lets create it + $created = false; + + if (!file_exists($this->parent->getPath('extension_root'))) + { + if (!$created = JFolder::create($this->parent->getPath('extension_root'))) + { + $this->parent + ->abort( + JText::sprintf( + 'JLIB_INSTALLER_ABORT_MOD_INSTALL_CREATE_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route), + $this->parent->getPath('extension_root') + ) + ); + + return false; + } + } + + // Since we created the module directory and will want to remove it if + // we have to roll back the installation, let's add it to the + // installation step stack + + if ($created) + { + $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_root'))); + } + + // Copy all necessary files + if ($this->parent->parseFiles($this->manifest->files, -1) === false) + { + // Install failed, roll back changes + $this->parent->abort(); + + return false; + } + + // If there is a manifest script, let's copy it. + if ($this->get('manifest_script')) + { + $path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script'); + $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->get('manifest_script'); + + if (!file_exists($path['dest']) || $this->parent->getOverwrite()) + { + if (!$this->parent->copyFiles(array($path))) + { + // Install failed, rollback changes + $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_MANIFEST')); + + return false; + } + } + } + + // Parse optional tags + $this->parent->parseMedia($this->manifest->media, $clientId); + $this->parent->parseLanguages($this->manifest->languages, $clientId); + + // Parse deprecated tags + $this->parent->parseFiles($this->manifest->images, -1); + + // Database Processing Section + + $row = JTable::getInstance('extension'); + + // Was there a module already installed with the same name? + if ($id) + { + // Load the entry and update the manifest_cache + $row->load($id); + $row->name = $this->get('name'); // update name + $row->manifest_cache = $this->parent->generateManifestCache(); // update manifest + + if (!$row->store()) + { + // Install failed, roll back changes + $this->parent + ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))); + + return false; + } + } + else + { + $row->set('name', $this->get('name')); + $row->set('type', 'module'); + $row->set('element', $this->get('element')); + $row->set('folder', ''); // There is no folder for modules + $row->set('enabled', 1); + $row->set('protected', 0); + $row->set('access', $clientId == 1 ? 2 : 0); + $row->set('client_id', $clientId); + $row->set('params', $this->parent->getParams()); + $row->set('custom_data', ''); // custom data + $row->set('manifest_cache', $this->parent->generateManifestCache()); + + if (!$row->store()) + { + // Install failed, roll back changes + $this->parent + ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))); + return false; + } + + // Set the insert id + $row->extension_id = $db->insertid(); + + // Since we have created a module item, we add it to the installation step stack + // so that if we have to rollback the changes we can undo it. + $this->parent->pushStep(array('type' => 'extension', 'extension_id' => $row->extension_id)); + + // Create unpublished module in jos_modules + $name = preg_replace('#[\*?]#', '', JText::_($this->get('name'))); + $module = JTable::getInstance('module'); + $module->set('title', $name); + $module->set('module', $this->get('element')); + $module->set('access', '1'); + $module->set('showtitle', '1'); + $module->set('client_id', $clientId); + $module->set('language', '*'); + + $module->store(); + } + + // Let's run the queries for the module + // If Joomla 1.5 compatible, with discrete sql files, execute appropriate + // file for utf-8 support or non-utf-8 support + + // Try for Joomla 1.5 type queries + // Second argument is the utf compatible version attribute + if (strtolower($this->route) == 'install') + { + $utfresult = $this->parent->parseSQLFiles($this->manifest->install->sql); + + if ($utfresult === false) + { + // Install failed, rollback changes + $this->parent + ->abort( + JText::sprintf('JLIB_INSTALLER_ABORT_MOD_INSTALL_SQL_ERROR', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true)) + ); + + return false; + } + + // Set the schema version to be the latest update version + if ($this->manifest->update) + { + $this->parent->setSchemaVersion($this->manifest->update->schemas, $row->extension_id); + } + } + elseif (strtolower($this->route) == 'update') + { + if ($this->manifest->update) + { + $result = $this->parent->parseSchemaUpdates($this->manifest->update->schemas, $row->extension_id); + if ($result === false) + { + // Install failed, rollback changes + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_UPDATE_SQL_ERROR', $db->stderr(true))); + return false; + } + } + } + + // Start Joomla! 1.6 + ob_start(); + ob_implicit_flush(false); + + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, $this->route)) + { + if ($this->parent->manifestClass->{$this->route}($this) === false) + { + // Install failed, rollback changes + $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_CUSTOM_INSTALL_FAILURE')); + + return false; + } + } + + // Append messages + $msg .= ob_get_contents(); + ob_end_clean(); + + // Finalization and Cleanup Section + + // Lastly, we will copy the manifest file to its appropriate place. + if (!$this->parent->copyManifest(-1)) + { + // Install failed, rollback changes + $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_COPY_SETUP')); + + return false; + } + + // And now we run the postflight + ob_start(); + ob_implicit_flush(false); + + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight')) + { + $this->parent->manifestClass->postflight($this->route, $this); + } + + // Append messages + $msg .= ob_get_contents(); + ob_end_clean(); + + if ($msg != '') + { + $this->parent->set('extension_message', $msg); + } + + return $row->get('extension_id'); + } + + /** + * Custom update method + * + * This is really a shell for the install system + * + * @return boolean True on success. + * + * @since 11.1 + */ + public function update() + { + // Set the overwrite setting + $this->parent->setOverwrite(true); + $this->parent->setUpgrade(true); + // Set the route for the install + $this->route = 'Update'; + + // Go to install which handles updates properly + return $this->install(); + } + + /** + * Custom discover method + * + * @return array JExtension list of extensions available + * + * @since 11.1 + */ + public function discover() + { + $results = array(); + $site_list = JFolder::folders(JPATH_SITE . '/modules'); + $admin_list = JFolder::folders(JPATH_ADMINISTRATOR . '/modules'); + $site_info = JApplicationHelper::getClientInfo('site', true); + $admin_info = JApplicationHelper::getClientInfo('administrator', true); + + foreach ($site_list as $module) + { + $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_SITE . "/modules/$module/$module.xml"); + $extension = JTable::getInstance('extension'); + $extension->set('type', 'module'); + $extension->set('client_id', $site_info->id); + $extension->set('element', $module); + $extension->set('name', $module); + $extension->set('state', -1); + $extension->set('manifest_cache', json_encode($manifest_details)); + $results[] = clone $extension; + } + + foreach ($admin_list as $module) + { + $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_ADMINISTRATOR . "/modules/$module/$module.xml"); + $extension = JTable::getInstance('extension'); + $extension->set('type', 'module'); + $extension->set('client_id', $admin_info->id); + $extension->set('element', $module); + $extension->set('name', $module); + $extension->set('state', -1); + $extension->set('manifest_cache', json_encode($manifest_details)); + $results[] = clone $extension; + } + + return $results; + } + + /** + * Custom discover_install method + * + * @return mixed Extension ID on success, boolean false on failure + * + * @since 11.1 + */ + public function discover_install() + { + // Modules are like templates, and are one of the easiest + // If its not in the extensions table we just add it + $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); + $manifestPath = $client->path . '/modules/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml'; + $this->parent->manifest = $this->parent->isManifest($manifestPath); + $description = (string) $this->parent->manifest->description; + + if ($description) + { + $this->parent->set('message', JText::_($description)); + } + else + { + $this->parent->set('message', ''); + } + + $this->parent->setPath('manifest', $manifestPath); + $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); + // TODO: Re-evaluate this; should we run installation triggers? postflight perhaps? + $this->parent->extension->manifest_cache = json_encode($manifest_details); + $this->parent->extension->state = 0; + $this->parent->extension->name = $manifest_details['name']; + $this->parent->extension->enabled = 1; + $this->parent->extension->params = $this->parent->getParams(); + if ($this->parent->extension->store()) + { + return $this->parent->extension->get('extension_id'); + } + else + { + JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_MOD_DISCOVER_STORE_DETAILS')); + + return false; + } + } + + /** + * Refreshes the extension table cache + * + * @return boolean Result of operation, true if updated, false on failure. + * + * @since 11.1 + */ + public function refreshManifestCache() + { + $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); + $manifestPath = $client->path . '/modules/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml'; + $this->parent->manifest = $this->parent->isManifest($manifestPath); + $this->parent->setPath('manifest', $manifestPath); + $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); + $this->parent->extension->manifest_cache = json_encode($manifest_details); + $this->parent->extension->name = $manifest_details['name']; + + if ($this->parent->extension->store()) + { + return true; + } + else + { + JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_MOD_REFRESH_MANIFEST_CACHE')); + + return false; + } + } + + /** + * Custom uninstall method + * + * @param integer $id The id of the module to uninstall + * + * @return boolean True on success + * + * @since 11.1 + */ + public function uninstall($id) + { + // Initialise variables. + $row = null; + $retval = true; + $db = $this->parent->getDbo(); + + // First order of business will be to load the module object table from the database. + // This should give us the necessary information to proceed. + $row = JTable::getInstance('extension'); + + if (!$row->load((int) $id) || !strlen($row->element)) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_ERRORUNKOWNEXTENSION')); + return false; + } + + // Is the module we are trying to uninstall a core one? + // Because that is not a good idea... + if ($row->protected) + { + JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_WARNCOREMODULE', $row->name)); + return false; + } + + // Get the extension root path + $element = $row->element; + $client = JApplicationHelper::getClientInfo($row->client_id); + + if ($client === false) + { + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_UNKNOWN_CLIENT', $row->client_id)); + return false; + } + $this->parent->setPath('extension_root', $client->path . '/modules/' . $element); + + $this->parent->setPath('source', $this->parent->getPath('extension_root')); + + // Get the package manifest objecct + // We do findManifest to avoid problem when uninstalling a list of extensions: getManifest cache its manifest file. + $this->parent->findManifest(); + $this->manifest = $this->parent->getManifest(); + + // Attempt to load the language file; might have uninstall strings + $this->loadLanguage(($row->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $element); + + // If there is an manifest class file, let's load it + $this->scriptElement = $this->manifest->scriptfile; + $manifestScript = (string) $this->manifest->scriptfile; + + if ($manifestScript) + { + $manifestScriptFile = $this->parent->getPath('extension_root') . '/' . $manifestScript; + + if (is_file($manifestScriptFile)) + { + // Load the file + include_once $manifestScriptFile; + } + + // Set the class name + $classname = $element . 'InstallerScript'; + + if (class_exists($classname)) + { + // Create a new instance + $this->parent->manifestClass = new $classname($this); + // And set this so we can copy it later + $this->set('manifest_script', $manifestScript); + + // Note: if we don't find the class, don't bother to copy the file + } + } + + ob_start(); + ob_implicit_flush(false); + + // Run uninstall if possible + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'uninstall')) + { + $this->parent->manifestClass->uninstall($this); + } + + $msg = ob_get_contents(); + ob_end_clean(); + + if (!($this->manifest instanceof JXMLElement)) + { + // Make sure we delete the folders + JFolder::delete($this->parent->getPath('extension_root')); + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_INVALID_NOTFOUND_MANIFEST')); + + return false; + } + + /* + * Let's run the uninstall queries for the component + * If Joomla 1.5 compatible, with discreet sql files - execute appropriate + * file for utf-8 support or non-utf support + */ + // Try for Joomla 1.5 type queries + // Second argument is the utf compatible version attribute + $utfresult = $this->parent->parseSQLFiles($this->manifest->uninstall->sql); + + if ($utfresult === false) + { + // Install failed, rollback changes + JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_SQL_ERROR', $db->stderr(true))); + $retval = false; + } + + // Remove the schema version + $query = $db->getQuery(true); + $query->delete()->from('#__schemas')->where('extension_id = ' . $row->extension_id); + $db->setQuery($query); + $db->Query(); + + // Remove other files + $this->parent->removeFiles($this->manifest->media); + $this->parent->removeFiles($this->manifest->languages, $row->client_id); + + // Let's delete all the module copies for the type we are uninstalling + $query = $db->getQuery(true); + $query->select($query->qn('id'))->from($query->qn('#__modules')); + $query->where($query->qn('module') . ' = ' . $query->q($row->element)); + $query->where($query->qn('client_id') . ' = ' . (int) $row->client_id); + $db->setQuery($query); + + try + { + $modules = $db->loadColumn(); + } + catch (JException $e) + { + $modules = array(); + } + + // Do we have any module copies? + if (count($modules)) + { + // Ensure the list is sane + JArrayHelper::toInteger($modules); + $modID = implode(',', $modules); + + // Wipe out any items assigned to menus + $query = 'DELETE' . ' FROM #__modules_menu' . ' WHERE moduleid IN (' . $modID . ')'; + $db->setQuery($query); + try + { + $db->query(); + } + catch (JException $e) + { + JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_EXCEPTION', $db->stderr(true))); + $retval = false; + } + + // Wipe out any instances in the modules table + $query = 'DELETE' . ' FROM #__modules' . ' WHERE id IN (' . $modID . ')'; + $db->setQuery($query); + + try + { + $db->query(); + } + catch (JException $e) + { + JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_EXCEPTION', $db->stderr(true))); + $retval = false; + } + } + + // Now we will no longer need the module object, so let's delete it and free up memory + $row->delete($row->extension_id); + $query = 'DELETE FROM #__modules WHERE module = ' . $db->Quote($row->element) . ' AND client_id = ' . $row->client_id; + $db->setQuery($query); + + try + { + // Clean up any other ones that might exist as well + $db->Query(); + } + catch (JException $e) + { + // Ignore the error... + } + + unset($row); + + // Remove the installation folder + if (!JFolder::delete($this->parent->getPath('extension_root'))) + { + // JFolder should raise an error + $retval = false; + } + + return $retval; + } + + /** + * Custom rollback method + * - Roll back the menu item + * + * @param array $arg Installation step to rollback + * + * @return boolean True on success + * + * @since 11.1 + */ + protected function _rollback_menu($arg) + { + // Get database connector object + $db = $this->parent->getDbo(); + + // Remove the entry from the #__modules_menu table + $query = 'DELETE' . ' FROM #__modules_menu' . ' WHERE moduleid=' . (int) $arg['id']; + $db->setQuery($query); + + try + { + return $db->query(); + } + catch (JException $e) + { + return false; + } + } + + /** + * Custom rollback method + * - Roll back the module item + * + * @param array $arg Installation step to rollback + * + * @return boolean True on success + * + * @since 11.1 + */ + protected function _rollback_module($arg) + { + // Get database connector object + $db = $this->parent->getDbo(); + + // Remove the entry from the #__modules table + $query = 'DELETE' . ' FROM #__modules' . ' WHERE id=' . (int) $arg['id']; + $db->setQuery($query); + try + { + return $db->query(); + } + catch (JException $e) + { + return false; + } + } +} diff --git a/libraries/joomla/installer/adapters/package.php b/libraries/joomla/installer/adapters/package.php index 1137593014375..1fd1e54bdd43d 100644 --- a/libraries/joomla/installer/adapters/package.php +++ b/libraries/joomla/installer/adapters/package.php @@ -1,563 +1,564 @@ -manifest = $this->parent->getManifest(); - $extension = 'pkg_' . strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->packagename, 'cmd')); - $lang = JFactory::getLanguage(); - $source = $path; - $lang->load($extension . '.sys', $source, null, false, false) - || $lang->load($extension . '.sys', JPATH_SITE, null, false, false) - || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false) - || $lang->load($extension . '.sys', JPATH_SITE, $lang->getDefault(), false, false); - } - - /** - * Custom install method - * - * @return int The extension id - * - * @since 11.1 - */ - public function install() - { - // Get the extension manifest object - $this->manifest = $this->parent->getManifest(); - - // Manifest Document Setup Section - - // Set the extensions name - $filter = JFilterInput::getInstance(); - $name = (string) $this->manifest->packagename; - $name = $filter->clean($name, 'cmd'); - $this->set('name', $name); - - $element = 'pkg_' . $filter->clean($this->manifest->packagename, 'cmd'); - $this->set('element', $element); - - // Get the component description - $description = (string) $this->manifest->description; - if ($description) - { - $this->parent->set('message', JText::_($description)); - } - else - { - $this->parent->set('message', ''); - } - - // Set the installation path - $files = $this->manifest->files; - $group = (string) $this->manifest->packagename; - if (!empty($group)) - { - $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/packages/' . implode(DS, explode('/', $group))); - } - else - { - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_NO_PACK', JText::_('JLIB_INSTALLER_' . strtoupper($this->route)))); - return false; - } - - /** - * --------------------------------------------------------------------------------------------- - * Installer Trigger Loading - * --------------------------------------------------------------------------------------------- - */ - // If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet) - $this->scriptElement = $this->manifest->scriptfile; - $manifestScript = (string) $this->manifest->scriptfile; - - if ($manifestScript) - { - $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript; - - if (is_file($manifestScriptFile)) - { - // load the file - include_once $manifestScriptFile; - } - - // Set the class name - $classname = $element . 'InstallerScript'; - - if (class_exists($classname)) - { - // create a new instance - $this->parent->manifestClass = new $classname($this); - // and set this so we can copy it later - $this->set('manifest_script', $manifestScript); - // Note: if we don't find the class, don't bother to copy the file - } - } - - // run preflight if possible (since we know we're not an update) - ob_start(); - ob_implicit_flush(false); - - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight')) - { - if ($this->parent->manifestClass->preflight($this->route, $this) === false) - { - // Install failed, rollback changes - $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PACKAGE_INSTALL_CUSTOM_INSTALL_FAILURE')); - - return false; - } - } - - $msg = ob_get_contents(); // create msg object; first use here - ob_end_clean(); - - // Filesystem Processing Section - - if ($folder = $files->attributes()->folder) - { - $source = $this->parent->getPath('source') . '/' . $folder; - } - else - { - $source = $this->parent->getPath('source'); - } - - // Install all necessary files - if (count($this->manifest->files->children())) - { - $i = 0; - foreach ($this->manifest->files->children() as $child) - { - $file = $source . '/' . $child; - if (is_dir($file)) - { - // If it's actually a directory then fill it up - $package = array(); - $package['dir'] = $file; - $package['type'] = JInstallerHelper::detectType($file); - } - else - { - // If it's an archive - $package = JInstallerHelper::unpack($file); - } - $tmpInstaller = new JInstaller; - if (!$tmpInstaller->install($package['dir'])) - { - $this->parent->abort( - JText::sprintf( - 'JLIB_INSTALLER_ABORT_PACK_INSTALL_ERROR_EXTENSION', JText::_('JLIB_INSTALLER_' . strtoupper($this->route)), - basename($file) - ) - ); - return false; - } - else - { - $results[$i] = array( - 'name' => $tmpInstaller->manifest->name, - 'result' => $tmpInstaller->install($package['dir']) - ); - } - $i++; - } - } - else - { - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_NO_FILES', JText::_('JLIB_INSTALLER_' . strtoupper($this->route)))); - return false; - } - - // Parse optional tags - $this->parent->parseLanguages($this->manifest->languages); - - // Extension Registration - - $row = JTable::getInstance('extension'); - $eid = $row->find(array('element' => strtolower($this->get('element')), 'type' => 'package')); - if ($eid) - { - $row->load($eid); - } - else - { - $row->name = $this->get('name'); - $row->type = 'package'; - $row->element = $this->get('element'); - // There is no folder for modules - $row->folder = ''; - $row->enabled = 1; - $row->protected = 0; - $row->access = 1; - $row->client_id = 0; - // custom data - $row->custom_data = ''; - $row->params = $this->parent->getParams(); - } - // Update the manifest cache for the entry - $row->manifest_cache = $this->parent->generateManifestCache(); - - if (!$row->store()) - { - // Install failed, roll back changes - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_ROLLBACK', $row->getError())); - return false; - } - - // Finalization and Cleanup Section - - // Start Joomla! 1.6 - ob_start(); - ob_implicit_flush(false); - - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, $this->route)) - { - if ($this->parent->manifestClass->{$this->route}($this) === false) - { - // Install failed, rollback changes - $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_FILE_INSTALL_CUSTOM_INSTALL_FAILURE')); - - return false; - } - } - - $msg .= ob_get_contents(); // append messages - ob_end_clean(); - - // Lastly, we will copy the manifest file to its appropriate place. - $manifest = array(); - $manifest['src'] = $this->parent->getPath('manifest'); - $manifest['dest'] = JPATH_MANIFESTS . '/packages/' . basename($this->parent->getPath('manifest')); - - if (!$this->parent->copyFiles(array($manifest), true)) - { - // Install failed, rollback changes - $this->parent->abort( - JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_COPY_SETUP', JText::_('JLIB_INSTALLER_ABORT_PACK_INSTALL_NO_FILES')) - ); - return false; - } - - // If there is a manifest script, let's copy it. - if ($this->get('manifest_script')) - { - // First, we have to create a folder for the script if one isn't present - $created = false; - - if (!file_exists($this->parent->getPath('extension_root'))) - { - JFolder::create($this->parent->getPath('extension_root')); - } - - $path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script'); - $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->get('manifest_script'); - - if (!file_exists($path['dest']) || $this->parent->getOverwrite()) - { - if (!$this->parent->copyFiles(array($path))) - { - // Install failed, rollback changes - $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PACKAGE_INSTALL_MANIFEST')); - - return false; - } - } - } - - // And now we run the postflight - ob_start(); - ob_implicit_flush(false); - - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight')) - { - $this->parent->manifestClass->postflight($this->route, $this, $results); - } - - $msg .= ob_get_contents(); // append messages - ob_end_clean(); - - if ($msg != '') - { - $this->parent->set('extension_message', $msg); - } - return $row->extension_id; - } - - /** - * Updates a package - * - * The only difference between an update and a full install - * is how we handle the database - * - * @return void - * - * @since 11.1 - */ - public function update() - { - $this->route = 'update'; - $this->install(); - } - - /** - * Custom uninstall method - * - * @param integer $id The id of the package to uninstall. - * - * @return boolean True on success - * - * @since 11.1 - */ - public function uninstall($id) - { - // Initialise variables. - $row = null; - $retval = true; - - $row = JTable::getInstance('extension'); - $row->load($id); - - if ($row->protected) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_WARNCOREPACK')); - return false; - } - - $manifestFile = JPATH_MANIFESTS . '/packages/' . $row->get('element') . '.xml'; - $manifest = new JPackageManifest($manifestFile); - - // Set the package root path - $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/packages/' . $manifest->packagename); - - // Because packages may not have their own folders we cannot use the standard method of finding an installation manifest - if (!file_exists($manifestFile)) - { - // TODO: Fail? - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_MISSINGMANIFEST')); - return false; - - } - - $xml = JFactory::getXML($manifestFile); - - // If we cannot load the XML file return false - if (!$xml) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_LOAD_MANIFEST')); - return false; - } - - /* - * Check for a valid XML root tag. - * @todo: Remove backwards compatibility in a future version - * Should be 'extension', but for backward compatibility we will accept 'install'. - */ - if ($xml->getName() != 'install' && $xml->getName() != 'extension') - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_INVALID_MANIFEST')); - return false; - } - - // If there is an manifest class file, let's load it - $this->scriptElement = $manifest->scriptfile; - $manifestScript = (string) $manifest->scriptfile; - - if ($manifestScript) - { - $manifestScriptFile = $this->parent->getPath('extension_root') . '/' . $manifestScript; - - if (is_file($manifestScriptFile)) - { - // Load the file - include_once $manifestScriptFile; - } - - // Set the class name - $classname = $row->element . 'InstallerScript'; - - if (class_exists($classname)) - { - // Create a new instance - $this->parent->manifestClass = new $classname($this); - // And set this so we can copy it later - $this->set('manifest_script', $manifestScript); - - // Note: if we don't find the class, don't bother to copy the file - } - } - - ob_start(); - ob_implicit_flush(false); - - // Run uninstall if possible - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'uninstall')) - { - $this->parent->manifestClass->uninstall($this); - } - - $msg = ob_get_contents(); - ob_end_clean(); - - $error = false; - foreach ($manifest->filelist as $extension) - { - $tmpInstaller = new JInstaller; - $id = $this->_getExtensionID($extension->type, $extension->id, $extension->client, $extension->group); - $client = JApplicationHelper::getClientInfo($extension->client, true); - if ($id) - { - if (!$tmpInstaller->uninstall($extension->type, $id, $client->id)) - { - $error = true; - JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_NOT_PROPER', basename($extension->filename))); - } - } - else - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_UNKNOWN_EXTENSION')); - } - } - - // Remove any language files - $this->parent->removeFiles($xml->languages); - - // clean up manifest file after we're done if there were no errors - if (!$error) - { - JFile::delete($manifestFile); - $folder = $this->parent->getPath('extension_root'); - if (JFolder::exists($folder)) - { - JFolder::delete($folder); - } - $row->delete(); - } - else - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_MANIFEST_NOT_REMOVED')); - } - - // Return the result up the line - return $retval; - } - - /** - * Gets the extension id. - * - * @param string $type The extension type. - * @param string $id The name of the extension (the element field). - * @param integer $client The application id (0: Joomla CMS site; 1: Joomla CMS administrator). - * @param string $group The extension group (mainly for plugins). - * - * @return integer - * - * @since 11.1 - */ - protected function _getExtensionID($type, $id, $client, $group) - { - $db = $this->parent->getDbo(); - $result = $id; - - $query = $db->getQuery(true); - $query->select('extension_id'); - $query->from('#__extensions'); - $query->where('type = ' . $db->Quote($type)); - $query->where('element = ' . $db->Quote($id)); - - switch ($type) - { - case 'plugin': - // Plugins have a folder but not a client - $query->where('folder = ' . $db->Quote($group)); - break; - - case 'library': - case 'package': - case 'component': - // Components, packages and libraries don't have a folder or client. - // Included for completeness. - break; - - case 'language': - case 'module': - case 'template': - // Languages, modules and templates have a client but not a folder - $client = JApplicationHelper::getClientInfo($client, true); - $query->where('client_id = ' . (int) $client->id); - break; - } - - $db->setQuery($query); - $result = $db->loadResult(); - - // Note: For templates, libraries and packages their unique name is their key. - // This means they come out the same way they came in. - return $result; - } - - /** - * Refreshes the extension table cache - * - * @return boolean Result of operation, true if updated, false on failure - * - * @since 11.1 - */ - public function refreshManifestCache() - { - // Need to find to find where the XML file is since we don't store this normally - $manifestPath = JPATH_MANIFESTS . '/packages/' . $this->parent->extension->element . '.xml'; - $this->parent->manifest = $this->parent->isManifest($manifestPath); - $this->parent->setPath('manifest', $manifestPath); - - $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); - $this->parent->extension->manifest_cache = json_encode($manifest_details); - $this->parent->extension->name = $manifest_details['name']; - - try - { - return $this->parent->extension->store(); - } - catch (JException $e) - { - JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_PACK_REFRESH_MANIFEST_CACHE')); - return false; - } - } -} +manifest = $this->parent->getManifest(); + $extension = 'pkg_' . strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->packagename, 'cmd')); + $lang = JFactory::getLanguage(); + $source = $path; + $lang->load($extension . '.sys', $source, null, false, false) + || $lang->load($extension . '.sys', JPATH_SITE, null, false, false) + || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false) + || $lang->load($extension . '.sys', JPATH_SITE, $lang->getDefault(), false, false); + } + + /** + * Custom install method + * + * @return int The extension id + * + * @since 11.1 + */ + public function install() + { + // Get the extension manifest object + $this->manifest = $this->parent->getManifest(); + + // Manifest Document Setup Section + + // Set the extensions name + $filter = JFilterInput::getInstance(); + $name = (string) $this->manifest->packagename; + $name = $filter->clean($name, 'cmd'); + $this->set('name', $name); + + $element = 'pkg_' . $filter->clean($this->manifest->packagename, 'cmd'); + $this->set('element', $element); + + // Get the component description + $description = (string) $this->manifest->description; + if ($description) + { + $this->parent->set('message', JText::_($description)); + } + else + { + $this->parent->set('message', ''); + } + + // Set the installation path + $files = $this->manifest->files; + $group = (string) $this->manifest->packagename; + if (!empty($group)) + { + $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/packages/' . implode(DS, explode('/', $group))); + } + else + { + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_NO_PACK', JText::_('JLIB_INSTALLER_' . strtoupper($this->route)))); + return false; + } + + /** + * --------------------------------------------------------------------------------------------- + * Installer Trigger Loading + * --------------------------------------------------------------------------------------------- + */ + // If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet) + $this->scriptElement = $this->manifest->scriptfile; + $manifestScript = (string) $this->manifest->scriptfile; + + if ($manifestScript) + { + $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript; + + if (is_file($manifestScriptFile)) + { + // load the file + include_once $manifestScriptFile; + } + + // Set the class name + $classname = $element . 'InstallerScript'; + + if (class_exists($classname)) + { + // create a new instance + $this->parent->manifestClass = new $classname($this); + // and set this so we can copy it later + $this->set('manifest_script', $manifestScript); + // Note: if we don't find the class, don't bother to copy the file + } + } + + // run preflight if possible (since we know we're not an update) + ob_start(); + ob_implicit_flush(false); + + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight')) + { + if ($this->parent->manifestClass->preflight($this->route, $this) === false) + { + // Install failed, rollback changes + $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PACKAGE_INSTALL_CUSTOM_INSTALL_FAILURE')); + + return false; + } + } + + $msg = ob_get_contents(); // create msg object; first use here + ob_end_clean(); + + // Filesystem Processing Section + + if ($folder = $files->attributes()->folder) + { + $source = $this->parent->getPath('source') . '/' . $folder; + } + else + { + $source = $this->parent->getPath('source'); + } + + // Install all necessary files + if (count($this->manifest->files->children())) + { + $i = 0; + foreach ($this->manifest->files->children() as $child) + { + $file = $source . '/' . $child; + if (is_dir($file)) + { + // If it's actually a directory then fill it up + $package = array(); + $package['dir'] = $file; + $package['type'] = JInstallerHelper::detectType($file); + } + else + { + // If it's an archive + $package = JInstallerHelper::unpack($file); + } + $tmpInstaller = new JInstaller; + $installResult = $tmpInstaller->install($package['dir']); + if (!$installResult) + { + $this->parent->abort( + JText::sprintf( + 'JLIB_INSTALLER_ABORT_PACK_INSTALL_ERROR_EXTENSION', JText::_('JLIB_INSTALLER_' . strtoupper($this->route)), + basename($file) + ) + ); + return false; + } + else + { + $results[$i] = array( + 'name' => $tmpInstaller->manifest->name, + 'result' => $installResult + ); + } + $i++; + } + } + else + { + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_NO_FILES', JText::_('JLIB_INSTALLER_' . strtoupper($this->route)))); + return false; + } + + // Parse optional tags + $this->parent->parseLanguages($this->manifest->languages); + + // Extension Registration + + $row = JTable::getInstance('extension'); + $eid = $row->find(array('element' => strtolower($this->get('element')), 'type' => 'package')); + if ($eid) + { + $row->load($eid); + } + else + { + $row->name = $this->get('name'); + $row->type = 'package'; + $row->element = $this->get('element'); + // There is no folder for modules + $row->folder = ''; + $row->enabled = 1; + $row->protected = 0; + $row->access = 1; + $row->client_id = 0; + // custom data + $row->custom_data = ''; + $row->params = $this->parent->getParams(); + } + // Update the manifest cache for the entry + $row->manifest_cache = $this->parent->generateManifestCache(); + + if (!$row->store()) + { + // Install failed, roll back changes + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_ROLLBACK', $row->getError())); + return false; + } + + // Finalization and Cleanup Section + + // Start Joomla! 1.6 + ob_start(); + ob_implicit_flush(false); + + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, $this->route)) + { + if ($this->parent->manifestClass->{$this->route}($this) === false) + { + // Install failed, rollback changes + $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_FILE_INSTALL_CUSTOM_INSTALL_FAILURE')); + + return false; + } + } + + $msg .= ob_get_contents(); // append messages + ob_end_clean(); + + // Lastly, we will copy the manifest file to its appropriate place. + $manifest = array(); + $manifest['src'] = $this->parent->getPath('manifest'); + $manifest['dest'] = JPATH_MANIFESTS . '/packages/' . basename($this->parent->getPath('manifest')); + + if (!$this->parent->copyFiles(array($manifest), true)) + { + // Install failed, rollback changes + $this->parent->abort( + JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_COPY_SETUP', JText::_('JLIB_INSTALLER_ABORT_PACK_INSTALL_NO_FILES')) + ); + return false; + } + + // If there is a manifest script, let's copy it. + if ($this->get('manifest_script')) + { + // First, we have to create a folder for the script if one isn't present + $created = false; + + if (!file_exists($this->parent->getPath('extension_root'))) + { + JFolder::create($this->parent->getPath('extension_root')); + } + + $path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script'); + $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->get('manifest_script'); + + if (!file_exists($path['dest']) || $this->parent->getOverwrite()) + { + if (!$this->parent->copyFiles(array($path))) + { + // Install failed, rollback changes + $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PACKAGE_INSTALL_MANIFEST')); + + return false; + } + } + } + + // And now we run the postflight + ob_start(); + ob_implicit_flush(false); + + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight')) + { + $this->parent->manifestClass->postflight($this->route, $this, $results); + } + + $msg .= ob_get_contents(); // append messages + ob_end_clean(); + + if ($msg != '') + { + $this->parent->set('extension_message', $msg); + } + return $row->extension_id; + } + + /** + * Updates a package + * + * The only difference between an update and a full install + * is how we handle the database + * + * @return void + * + * @since 11.1 + */ + public function update() + { + $this->route = 'update'; + $this->install(); + } + + /** + * Custom uninstall method + * + * @param integer $id The id of the package to uninstall. + * + * @return boolean True on success + * + * @since 11.1 + */ + public function uninstall($id) + { + // Initialise variables. + $row = null; + $retval = true; + + $row = JTable::getInstance('extension'); + $row->load($id); + + if ($row->protected) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_WARNCOREPACK')); + return false; + } + + $manifestFile = JPATH_MANIFESTS . '/packages/' . $row->get('element') . '.xml'; + $manifest = new JPackageManifest($manifestFile); + + // Set the package root path + $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/packages/' . $manifest->packagename); + + // Because packages may not have their own folders we cannot use the standard method of finding an installation manifest + if (!file_exists($manifestFile)) + { + // TODO: Fail? + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_MISSINGMANIFEST')); + return false; + + } + + $xml = JFactory::getXML($manifestFile); + + // If we cannot load the XML file return false + if (!$xml) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_LOAD_MANIFEST')); + return false; + } + + /* + * Check for a valid XML root tag. + * @todo: Remove backwards compatibility in a future version + * Should be 'extension', but for backward compatibility we will accept 'install'. + */ + if ($xml->getName() != 'install' && $xml->getName() != 'extension') + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_INVALID_MANIFEST')); + return false; + } + + // If there is an manifest class file, let's load it + $this->scriptElement = $manifest->scriptfile; + $manifestScript = (string) $manifest->scriptfile; + + if ($manifestScript) + { + $manifestScriptFile = $this->parent->getPath('extension_root') . '/' . $manifestScript; + + if (is_file($manifestScriptFile)) + { + // Load the file + include_once $manifestScriptFile; + } + + // Set the class name + $classname = $row->element . 'InstallerScript'; + + if (class_exists($classname)) + { + // Create a new instance + $this->parent->manifestClass = new $classname($this); + // And set this so we can copy it later + $this->set('manifest_script', $manifestScript); + + // Note: if we don't find the class, don't bother to copy the file + } + } + + ob_start(); + ob_implicit_flush(false); + + // Run uninstall if possible + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'uninstall')) + { + $this->parent->manifestClass->uninstall($this); + } + + $msg = ob_get_contents(); + ob_end_clean(); + + $error = false; + foreach ($manifest->filelist as $extension) + { + $tmpInstaller = new JInstaller; + $id = $this->_getExtensionID($extension->type, $extension->id, $extension->client, $extension->group); + $client = JApplicationHelper::getClientInfo($extension->client, true); + if ($id) + { + if (!$tmpInstaller->uninstall($extension->type, $id, $client->id)) + { + $error = true; + JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_NOT_PROPER', basename($extension->filename))); + } + } + else + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_UNKNOWN_EXTENSION')); + } + } + + // Remove any language files + $this->parent->removeFiles($xml->languages); + + // clean up manifest file after we're done if there were no errors + if (!$error) + { + JFile::delete($manifestFile); + $folder = $this->parent->getPath('extension_root'); + if (JFolder::exists($folder)) + { + JFolder::delete($folder); + } + $row->delete(); + } + else + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_MANIFEST_NOT_REMOVED')); + } + + // Return the result up the line + return $retval; + } + + /** + * Gets the extension id. + * + * @param string $type The extension type. + * @param string $id The name of the extension (the element field). + * @param integer $client The application id (0: Joomla CMS site; 1: Joomla CMS administrator). + * @param string $group The extension group (mainly for plugins). + * + * @return integer + * + * @since 11.1 + */ + protected function _getExtensionID($type, $id, $client, $group) + { + $db = $this->parent->getDbo(); + $result = $id; + + $query = $db->getQuery(true); + $query->select('extension_id'); + $query->from('#__extensions'); + $query->where('type = ' . $db->Quote($type)); + $query->where('element = ' . $db->Quote($id)); + + switch ($type) + { + case 'plugin': + // Plugins have a folder but not a client + $query->where('folder = ' . $db->Quote($group)); + break; + + case 'library': + case 'package': + case 'component': + // Components, packages and libraries don't have a folder or client. + // Included for completeness. + break; + + case 'language': + case 'module': + case 'template': + // Languages, modules and templates have a client but not a folder + $client = JApplicationHelper::getClientInfo($client, true); + $query->where('client_id = ' . (int) $client->id); + break; + } + + $db->setQuery($query); + $result = $db->loadResult(); + + // Note: For templates, libraries and packages their unique name is their key. + // This means they come out the same way they came in. + return $result; + } + + /** + * Refreshes the extension table cache + * + * @return boolean Result of operation, true if updated, false on failure + * + * @since 11.1 + */ + public function refreshManifestCache() + { + // Need to find to find where the XML file is since we don't store this normally + $manifestPath = JPATH_MANIFESTS . '/packages/' . $this->parent->extension->element . '.xml'; + $this->parent->manifest = $this->parent->isManifest($manifestPath); + $this->parent->setPath('manifest', $manifestPath); + + $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); + $this->parent->extension->manifest_cache = json_encode($manifest_details); + $this->parent->extension->name = $manifest_details['name']; + + try + { + return $this->parent->extension->store(); + } + catch (JException $e) + { + JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_PACK_REFRESH_MANIFEST_CACHE')); + return false; + } + } +} diff --git a/libraries/joomla/installer/adapters/plugin.php b/libraries/joomla/installer/adapters/plugin.php index f18a8212a4765..065c24333a81a 100644 --- a/libraries/joomla/installer/adapters/plugin.php +++ b/libraries/joomla/installer/adapters/plugin.php @@ -1,857 +1,857 @@ -parent->getPath('source'); - if (!$source) - { - $this->parent->setPath('source', JPATH_PLUGINS . '/' . $this->parent->extension->folder . '/' . $this->parent->extension->element); - } - $this->manifest = $this->parent->getManifest(); - $element = $this->manifest->files; - if ($element) - { - $group = strtolower((string) $this->manifest->attributes()->group); - $name = ''; - if (count($element->children())) - { - foreach ($element->children() as $file) - { - if ((string) $file->attributes()->plugin) - { - $name = strtolower((string) $file->attributes()->plugin); - break; - } - } - } - if ($name) - { - $extension = "plg_${group}_${name}"; - $lang = JFactory::getLanguage(); - $source = $path ? $path : JPATH_PLUGINS . "/$group/$name"; - $folder = (string) $element->attributes()->folder; - if ($folder && file_exists("$path/$folder")) - { - $source = "$path/$folder"; - } - $lang->load($extension . '.sys', $source, null, false, false) - || $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, null, false, false) - || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false) - || $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, $lang->getDefault(), false, false); - } - } - } - - /** - * Custom install method - * - * @return boolean True on success - * - * @since 11.1 - */ - public function install() - { - // Get a database connector object - $db = $this->parent->getDbo(); - - // Get the extension manifest object - $this->manifest = $this->parent->getManifest(); - - $xml = $this->manifest; - - // Manifest Document Setup Section - - // Set the extension name - $name = (string) $xml->name; - $name = JFilterInput::getInstance()->clean($name, 'string'); - $this->set('name', $name); - - // Get the component description - $description = (string) $xml->description; - if ($description) - { - $this->parent->set('message', JText::_($description)); - } - else - { - $this->parent->set('message', ''); - } - - /* - * Backward Compatibility - * @todo Deprecate in future version - */ - $type = (string) $xml->attributes()->type; - - // Set the installation path - if (count($xml->files->children())) - { - foreach ($xml->files->children() as $file) - { - if ((string) $file->attributes()->$type) - { - $element = (string) $file->attributes()->$type; - break; - } - } - } - $group = (string) $xml->attributes()->group; - if (!empty($element) && !empty($group)) - { - $this->parent->setPath('extension_root', JPATH_PLUGINS . '/' . $group . '/' . $element); - } - else - { - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_NO_FILE', JText::_('JLIB_INSTALLER_' . $this->route))); - return false; - } - - // Check if we should enable overwrite settings - - // Check to see if a plugin by the same name is already installed. - $query = $db->getQuery(true); - $query->select($query->qn('extension_id'))->from($query->qn('#__extensions')); - $query->where($query->qn('folder') . ' = ' . $query->q($group)); - $query->where($query->qn('element') . ' = ' . $query->q($element)); - $db->setQuery($query); - try - { - $db->Query(); - } - catch (JException $e) - { - // Install failed, roll back changes - $this->parent - ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))); - return false; - } - $id = $db->loadResult(); - - // If it's on the fs... - if (file_exists($this->parent->getPath('extension_root')) && (!$this->parent->getOverwrite() || $this->parent->getUpgrade())) - { - $updateElement = $xml->update; - // Upgrade manually set or - // Update function available or - // Update tag detected - if ($this->parent->getUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update')) - || is_a($updateElement, 'JXMLElement')) - { - // Force this one - $this->parent->setOverwrite(true); - $this->parent->setUpgrade(true); - if ($id) - { - // If there is a matching extension mark this as an update; semantics really - $this->route = 'update'; - } - } - elseif (!$this->parent->getOverwrite()) - { - // Overwrite is set - // We didn't have overwrite set, find an update function or find an update tag so lets call it safe - $this->parent - ->abort( - JText::sprintf( - 'JLIB_INSTALLER_ABORT_PLG_INSTALL_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route), - $this->parent->getPath('extension_root') - ) - ); - return false; - } - } - - // Installer Trigger Loading - - // If there is an manifest class file, let's load it; we'll copy it later (don't have destination yet). - - if ((string) $xml->scriptfile) - { - $manifestScript = (string) $xml->scriptfile; - $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript; - if (is_file($manifestScriptFile)) - { - // Load the file - include_once $manifestScriptFile; - } - // If a dash is present in the group name, remove it - $groupClass = str_replace('-', '', $group); - // Set the class name - $classname = 'plg' . $groupClass . $element . 'InstallerScript'; - if (class_exists($classname)) - { - // Create a new instance - $this->parent->manifestClass = new $classname($this); - // And set this so we can copy it later - $this->set('manifest_script', $manifestScript); - - // Note: if we don't find the class, don't bother to copy the file - } - } - - // Run preflight if possible (since we know we're not an update) - ob_start(); - ob_implicit_flush(false); - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight')) - { - if ($this->parent->manifestClass->preflight($this->route, $this) === false) - { - // Install failed, rollback changes - $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PLG_INSTALL_CUSTOM_INSTALL_FAILURE')); - return false; - } - } - $msg = ob_get_contents(); // create msg object; first use here - ob_end_clean(); - - // Filesystem Processing Section - - // If the plugin directory does not exist, lets create it - $created = false; - if (!file_exists($this->parent->getPath('extension_root'))) - { - if (!$created = JFolder::create($this->parent->getPath('extension_root'))) - { - $this->parent - ->abort( - JText::sprintf( - 'JLIB_INSTALLER_ABORT_PLG_INSTALL_CREATE_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route), - $this->parent->getPath('extension_root') - ) - ); - return false; - } - } - - // If we're updating at this point when there is always going to be an extension_root find the old XML files - if ($this->route == 'update') - { - // Hunt for the original XML file - $old_manifest = null; - $tmpInstaller = new JInstaller; // create a new installer because findManifest sets stuff; side effects! - // Look in the extension root - $tmpInstaller->setPath('source', $this->parent->getPath('extension_root')); - if ($tmpInstaller->findManifest()) - { - $old_manifest = $tmpInstaller->getManifest(); - $this->oldFiles = $old_manifest->files; - } - } - - // If we created the plugin directory and will want to remove it if we - // have to roll back the installation, let's add it to the installation - // step stack - - if ($created) - { - $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_root'))); - } - - // Copy all necessary files - if ($this->parent->parseFiles($xml->files, -1, $this->oldFiles) === false) - { - // Install failed, roll back changes - $this->parent->abort(); - return false; - } - - // Parse optional tags -- media and language files for plugins go in admin app - $this->parent->parseMedia($xml->media, 1); - $this->parent->parseLanguages($xml->languages, 1); - - // If there is a manifest script, lets copy it. - if ($this->get('manifest_script')) - { - $path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script'); - $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->get('manifest_script'); - - if (!file_exists($path['dest'])) - { - if (!$this->parent->copyFiles(array($path))) - { - // Install failed, rollback changes - $this->parent - ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_MANIFEST', JText::_('JLIB_INSTALLER_' . $this->route))); - return false; - } - } - } - - // Database Processing Section - - $row = JTable::getInstance('extension'); - // Was there a plugin with the same name already installed? - if ($id) - { - if (!$this->parent->getOverwrite()) - { - // Install failed, roll back changes - $this->parent - ->abort( - JText::sprintf( - 'JLIB_INSTALLER_ABORT_PLG_INSTALL_ALLREADY_EXISTS', JText::_('JLIB_INSTALLER_' . $this->route), - $this->get('name') - ) - ); - return false; - } - $row->load($id); - $row->name = $this->get('name'); - $row->manifest_cache = $this->parent->generateManifestCache(); - $row->store(); // update the manifest cache and name - } - else - { - // Store in the extensions table (1.6) - $row->name = $this->get('name'); - $row->type = 'plugin'; - $row->ordering = 0; - $row->element = $element; - $row->folder = $group; - $row->enabled = 0; - $row->protected = 0; - $row->access = 1; - $row->client_id = 0; - $row->params = $this->parent->getParams(); - // Custom data - $row->custom_data = ''; - // System data - $row->system_data = ''; - $row->manifest_cache = $this->parent->generateManifestCache(); - - // Editor plugins are published by default - if ($group == 'editors') - { - $row->enabled = 1; - } - - if (!$row->store()) - { - // Install failed, roll back changes - $this->parent - ->abort( - JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true)) - ); - return false; - } - - // Since we have created a plugin item, we add it to the installation step stack - // so that if we have to rollback the changes we can undo it. - $this->parent->pushStep(array('type' => 'extension', 'id' => $row->extension_id)); - $id = $row->extension_id; - } - - // Let's run the queries for the module - // If Joomla 1.5 compatible, with discreet sql files - execute appropriate - // file for utf-8 support or non-utf-8 support - - // Try for Joomla 1.5 type queries - // Second argument is the utf compatible version attribute - if (strtolower($this->route) == 'install') - { - $utfresult = $this->parent->parseSQLFiles($this->manifest->install->sql); - if ($utfresult === false) - { - // Install failed, rollback changes - $this->parent - ->abort( - JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_SQL_ERROR', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true)) - ); - return false; - } - - // Set the schema version to be the latest update version - if ($this->manifest->update) - { - $this->parent->setSchemaVersion($this->manifest->update->schemas, $row->extension_id); - } - } - elseif (strtolower($this->route) == 'update') - { - if ($this->manifest->update) - { - $result = $this->parent->parseSchemaUpdates($this->manifest->update->schemas, $row->extension_id); - if ($result === false) - { - // Install failed, rollback changes - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_UPDATE_SQL_ERROR', $db->stderr(true))); - return false; - } - } - } - - // Start Joomla! 1.6 - ob_start(); - ob_implicit_flush(false); - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, $this->route)) - { - if ($this->parent->manifestClass->{$this->route}($this) === false) - { - // Install failed, rollback changes - $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PLG_INSTALL_CUSTOM_INSTALL_FAILURE')); - return false; - } - } - // Append messages - $msg .= ob_get_contents(); - ob_end_clean(); - - // Finalization and Cleanup Section - - // Lastly, we will copy the manifest file to its appropriate place. - if (!$this->parent->copyManifest(-1)) - { - // Install failed, rollback changes - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_COPY_SETUP', JText::_('JLIB_INSTALLER_' . $this->route))); - return false; - } - // And now we run the postflight - ob_start(); - ob_implicit_flush(false); - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight')) - { - $this->parent->manifestClass->postflight($this->route, $this); - } - // Append messages - $msg .= ob_get_contents(); - ob_end_clean(); - if ($msg != '') - { - $this->parent->set('extension_message', $msg); - } - return $id; - } - - /** - * Custom update method - * - * @return boolean True on success - * - * @since 11.1 - */ - public function update() - { - // Set the overwrite setting - $this->parent->setOverwrite(true); - $this->parent->setUpgrade(true); - // Set the route for the install - $this->route = 'update'; - // Go to install which handles updates properly - return $this->install(); - } - - /** - * Custom uninstall method - * - * @param integer $id The id of the plugin to uninstall - * - * @return boolean True on success - * - * @since 11.1 - */ - public function uninstall($id) - { - $this->route = 'uninstall'; - - // Initialise variables. - $row = null; - $retval = true; - $db = $this->parent->getDbo(); - - // First order of business will be to load the plugin object table from the database. - // This should give us the necessary information to proceed. - $row = JTable::getInstance('extension'); - if (!$row->load((int) $id)) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_ERRORUNKOWNEXTENSION')); - return false; - } - - // Is the plugin we are trying to uninstall a core one? - // Because that is not a good idea... - if ($row->protected) - { - JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_WARNCOREPLUGIN', $row->name)); - return false; - } - - // Get the plugin folder so we can properly build the plugin path - if (trim($row->folder) == '') - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_FOLDER_FIELD_EMPTY')); - return false; - } - - // Set the plugin root path - if (is_dir(JPATH_PLUGINS . '/' . $row->folder . '/' . $row->element)) - { - // Use 1.6 plugins - $this->parent->setPath('extension_root', JPATH_PLUGINS . '/' . $row->folder . '/' . $row->element); - } - else - { - // Use Legacy 1.5 plugins - $this->parent->setPath('extension_root', JPATH_PLUGINS . '/' . $row->folder); - } - - // Because 1.5 plugins don't have their own folders we cannot use the standard method of finding an installation manifest - // Since 1.6 they do, however until we move to 1.7 and remove 1.6 legacy we still need to use this method. - // When we get there it'll be something like "$this->parent->findManifest();$manifest = $this->parent->getManifest();" - $manifestFile = $this->parent->getPath('extension_root') . '/' . $row->element . '.xml'; - - if (!file_exists($manifestFile)) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_INVALID_NOTFOUND_MANIFEST')); - return false; - } - - $xml = JFactory::getXML($manifestFile); - - $this->manifest = $xml; - - // If we cannot load the XML file return null - if (!$xml) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_LOAD_MANIFEST')); - return false; - } - - /* - * Check for a valid XML root tag. - * @todo: Remove backwards compatibility in a future version - * Should be 'extension', but for backward compatibility we will accept 'install'. - */ - if ($xml->getName() != 'install' && $xml->getName() != 'extension') - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_INVALID_MANIFEST')); - return false; - } - - // Attempt to load the language file; might have uninstall strings - $this->parent->setPath('source', JPATH_PLUGINS . '/' . $row->folder . '/' . $row->element); - $this->loadLanguage(JPATH_PLUGINS . '/' . $row->folder . '/' . $row->element); - - // Installer Trigger Loading - - // If there is an manifest class file, let's load it; we'll copy it later (don't have dest yet) - $manifestScript = (string) $xml->scriptfile; - if ($manifestScript) - { - $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript; - if (is_file($manifestScriptFile)) - { - // Load the file - include_once $manifestScriptFile; - } - // If a dash is present in the folder, remove it - $folderClass = str_replace('-', '', $row->folder); - // Set the class name - $classname = 'plg' . $folderClass . $row->element . 'InstallerScript'; - if (class_exists($classname)) - { - // Create a new instance - $this->parent->manifestClass = new $classname($this); - // And set this so we can copy it later - $this->set('manifest_script', $manifestScript); - - // Note: if we don't find the class, don't bother to copy the file - } - } - - // Run preflight if possible (since we know we're not an update) - ob_start(); - ob_implicit_flush(false); - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight')) - { - if ($this->parent->manifestClass->preflight($this->route, $this) === false) - { - // Install failed, rollback changes - $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PLG_INSTALL_CUSTOM_INSTALL_FAILURE')); - return false; - } - } - // Create msg object; first use here - $msg = ob_get_contents(); - ob_end_clean(); - - // Let's run the queries for the module - // If Joomla 1.5 compatible, with discreet sql files - execute appropriate - // file for utf-8 support or non-utf-8 support - - // Try for Joomla 1.5 type queries - // Second argument is the utf compatible version attribute - $utfresult = $this->parent->parseSQLFiles($xml->{strtolower($this->route)}->sql); - if ($utfresult === false) - { - // Install failed, rollback changes - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_UNINSTALL_SQL_ERROR', $db->stderr(true))); - return false; - } - - // Start Joomla! 1.6 - ob_start(); - ob_implicit_flush(false); - if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'uninstall')) - { - $this->parent->manifestClass->uninstall($this); - } - // Append messages - $msg = ob_get_contents(); - ob_end_clean(); - - // Remove the plugin files - $this->parent->removeFiles($xml->images, -1); - $this->parent->removeFiles($xml->files, -1); - JFile::delete($manifestFile); - - // Remove all media and languages as well - $this->parent->removeFiles($xml->media); - $this->parent->removeFiles($xml->languages, 1); - - // Remove the schema version - $query = $db->getQuery(true); - $query->delete()->from('#__schemas')->where('extension_id = ' . $row->extension_id); - $db->setQuery($query); - $db->Query(); - - // Now we will no longer need the plugin object, so let's delete it - $row->delete($row->extension_id); - unset($row); - - // If the folder is empty, let's delete it - $files = JFolder::files($this->parent->getPath('extension_root')); - - JFolder::delete($this->parent->getPath('extension_root')); - - if ($msg) - { - $this->parent->set('extension_message', $msg); - } - - return $retval; - } - - /** - * Custom discover method - * - * @return array JExtension) list of extensions available - * - * @since 11.1 - */ - public function discover() - { - $results = array(); - $folder_list = JFolder::folders(JPATH_SITE . '/plugins'); - - foreach ($folder_list as $folder) - { - $file_list = JFolder::files(JPATH_SITE . '/plugins/' . $folder, '\.xml$'); - foreach ($file_list as $file) - { - $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_SITE . '/plugins/' . $folder . '/' . $file); - $file = JFile::stripExt($file); - // Ignore example plugins - if ($file == 'example') - { - continue; - } - - $extension = JTable::getInstance('extension'); - $extension->set('type', 'plugin'); - $extension->set('client_id', 0); - $extension->set('element', $file); - $extension->set('folder', $folder); - $extension->set('name', $file); - $extension->set('state', -1); - $extension->set('manifest_cache', json_encode($manifest_details)); - $results[] = $extension; - } - $folder_list = JFolder::folders(JPATH_SITE . '/plugins/' . $folder); - foreach ($folder_list as $plugin_folder) - { - $file_list = JFolder::files(JPATH_SITE . '/plugins/' . $folder . '/' . $plugin_folder, '\.xml$'); - foreach ($file_list as $file) - { - $manifest_details = JApplicationHelper::parseXMLInstallFile( - JPATH_SITE . '/plugins/' . $folder . '/' . $plugin_folder . '/' . $file - ); - $file = JFile::stripExt($file); - - if ($file == 'example') - { - continue; - } - - // ignore example plugins - $extension = JTable::getInstance('extension'); - $extension->set('type', 'plugin'); - $extension->set('client_id', 0); - $extension->set('element', $file); - $extension->set('folder', $folder); - $extension->set('name', $file); - $extension->set('state', -1); - $extension->set('manifest_cache', json_encode($manifest_details)); - $results[] = $extension; - } - } - } - return $results; - } - - /** - * Custom discover_install method. - * - * @return mixed - * - * @since 11.1 - */ - public function discover_install() - { - // Plugins use the extensions table as their primary store - // Similar to modules and templates, rather easy - // If it's not in the extensions table we just add it - $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); - if (is_dir($client->path . '/plugins/' . $this->parent->extension->folder . '/' . $this->parent->extension->element)) - { - $manifestPath = $client->path . '/plugins/' . $this->parent->extension->folder . '/' . $this->parent->extension->element . '/' - . $this->parent->extension->element . '.xml'; - } - else - { - $manifestPath = $client->path . '/plugins/' . $this->parent->extension->folder . '/' . $this->parent->extension->element . '.xml'; - } - $this->parent->manifest = $this->parent->isManifest($manifestPath); - $description = (string) $this->parent->manifest->description; - if ($description) - { - $this->parent->set('message', JText::_($description)); - } - else - { - $this->parent->set('message', ''); - } - $this->parent->setPath('manifest', $manifestPath); - $manifest_details = JApplicationHelper::parseXMLInstallFile($manifestPath); - $this->parent->extension->manifest_cache = json_encode($manifest_details); - $this->parent->extension->state = 0; - $this->parent->extension->name = $manifest_details['name']; - $this->parent->extension->enabled = ('editors' == $this->parent->extension->folder) ? 1 : 0; - $this->parent->extension->params = $this->parent->getParams(); - if ($this->parent->extension->store()) - { - return $this->parent->extension->get('extension_id'); - } - else - { - JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_PLG_DISCOVER_STORE_DETAILS')); - return false; - } - } - - /** - * Refreshes the extension table cache. - * - * @return boolean Result of operation, true if updated, false on failure. - * - * @since 11.1 - */ - public function refreshManifestCache() - { - // Plugins use the extensions table as their primary store - // Similar to modules and templates, rather easy - // If it's not in the extensions table we just add it - $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); - $manifestPath = $client->path . '/plugins/' . $this->parent->extension->folder . '/' . $this->parent->extension->element . '/' - . $this->parent->extension->element . '.xml'; - $this->parent->manifest = $this->parent->isManifest($manifestPath); - $this->parent->setPath('manifest', $manifestPath); - $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); - $this->parent->extension->manifest_cache = json_encode($manifest_details); - - $this->parent->extension->name = $manifest_details['name']; - if ($this->parent->extension->store()) - { - return true; - } - else - { - JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_PLG_REFRESH_MANIFEST_CACHE')); - return false; - } - } -} +parent->getPath('source'); + if (!$source) + { + $this->parent->setPath('source', JPATH_PLUGINS . '/' . $this->parent->extension->folder . '/' . $this->parent->extension->element); + } + $this->manifest = $this->parent->getManifest(); + $element = $this->manifest->files; + if ($element) + { + $group = strtolower((string) $this->manifest->attributes()->group); + $name = ''; + if (count($element->children())) + { + foreach ($element->children() as $file) + { + if ((string) $file->attributes()->plugin) + { + $name = strtolower((string) $file->attributes()->plugin); + break; + } + } + } + if ($name) + { + $extension = "plg_${group}_${name}"; + $lang = JFactory::getLanguage(); + $source = $path ? $path : JPATH_PLUGINS . "/$group/$name"; + $folder = (string) $element->attributes()->folder; + if ($folder && file_exists("$path/$folder")) + { + $source = "$path/$folder"; + } + $lang->load($extension . '.sys', $source, null, false, false) + || $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, null, false, false) + || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false) + || $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, $lang->getDefault(), false, false); + } + } + } + + /** + * Custom install method + * + * @return boolean True on success + * + * @since 11.1 + */ + public function install() + { + // Get a database connector object + $db = $this->parent->getDbo(); + + // Get the extension manifest object + $this->manifest = $this->parent->getManifest(); + + $xml = $this->manifest; + + // Manifest Document Setup Section + + // Set the extension name + $name = (string) $xml->name; + $name = JFilterInput::getInstance()->clean($name, 'string'); + $this->set('name', $name); + + // Get the component description + $description = (string) $xml->description; + if ($description) + { + $this->parent->set('message', JText::_($description)); + } + else + { + $this->parent->set('message', ''); + } + + /* + * Backward Compatibility + * @todo Deprecate in future version + */ + $type = (string) $xml->attributes()->type; + + // Set the installation path + if (count($xml->files->children())) + { + foreach ($xml->files->children() as $file) + { + if ((string) $file->attributes()->$type) + { + $element = (string) $file->attributes()->$type; + break; + } + } + } + $group = (string) $xml->attributes()->group; + if (!empty($element) && !empty($group)) + { + $this->parent->setPath('extension_root', JPATH_PLUGINS . '/' . $group . '/' . $element); + } + else + { + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_NO_FILE', JText::_('JLIB_INSTALLER_' . $this->route))); + return false; + } + + // Check if we should enable overwrite settings + + // Check to see if a plugin by the same name is already installed. + $query = $db->getQuery(true); + $query->select($query->qn('extension_id'))->from($query->qn('#__extensions')); + $query->where($query->qn('folder') . ' = ' . $query->q($group)); + $query->where($query->qn('element') . ' = ' . $query->q($element)); + $db->setQuery($query); + try + { + $db->Query(); + } + catch (JException $e) + { + // Install failed, roll back changes + $this->parent + ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))); + return false; + } + $id = $db->loadResult(); + + // If it's on the fs... + if (file_exists($this->parent->getPath('extension_root')) && (!$this->parent->getOverwrite() || $this->parent->getUpgrade())) + { + $updateElement = $xml->update; + // Upgrade manually set or + // Update function available or + // Update tag detected + if ($this->parent->getUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update')) + || $updateElement) + { + // Force this one + $this->parent->setOverwrite(true); + $this->parent->setUpgrade(true); + if ($id) + { + // If there is a matching extension mark this as an update; semantics really + $this->route = 'update'; + } + } + elseif (!$this->parent->getOverwrite()) + { + // Overwrite is set + // We didn't have overwrite set, find an update function or find an update tag so lets call it safe + $this->parent + ->abort( + JText::sprintf( + 'JLIB_INSTALLER_ABORT_PLG_INSTALL_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route), + $this->parent->getPath('extension_root') + ) + ); + return false; + } + } + + // Installer Trigger Loading + + // If there is an manifest class file, let's load it; we'll copy it later (don't have destination yet). + + if ((string) $xml->scriptfile) + { + $manifestScript = (string) $xml->scriptfile; + $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript; + if (is_file($manifestScriptFile)) + { + // Load the file + include_once $manifestScriptFile; + } + // If a dash is present in the group name, remove it + $groupClass = str_replace('-', '', $group); + // Set the class name + $classname = 'plg' . $groupClass . $element . 'InstallerScript'; + if (class_exists($classname)) + { + // Create a new instance + $this->parent->manifestClass = new $classname($this); + // And set this so we can copy it later + $this->set('manifest_script', $manifestScript); + + // Note: if we don't find the class, don't bother to copy the file + } + } + + // Run preflight if possible (since we know we're not an update) + ob_start(); + ob_implicit_flush(false); + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight')) + { + if ($this->parent->manifestClass->preflight($this->route, $this) === false) + { + // Install failed, rollback changes + $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PLG_INSTALL_CUSTOM_INSTALL_FAILURE')); + return false; + } + } + $msg = ob_get_contents(); // create msg object; first use here + ob_end_clean(); + + // Filesystem Processing Section + + // If the plugin directory does not exist, lets create it + $created = false; + if (!file_exists($this->parent->getPath('extension_root'))) + { + if (!$created = JFolder::create($this->parent->getPath('extension_root'))) + { + $this->parent + ->abort( + JText::sprintf( + 'JLIB_INSTALLER_ABORT_PLG_INSTALL_CREATE_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route), + $this->parent->getPath('extension_root') + ) + ); + return false; + } + } + + // If we're updating at this point when there is always going to be an extension_root find the old XML files + if ($this->route == 'update') + { + // Hunt for the original XML file + $old_manifest = null; + $tmpInstaller = new JInstaller; // create a new installer because findManifest sets stuff; side effects! + // Look in the extension root + $tmpInstaller->setPath('source', $this->parent->getPath('extension_root')); + if ($tmpInstaller->findManifest()) + { + $old_manifest = $tmpInstaller->getManifest(); + $this->oldFiles = $old_manifest->files; + } + } + + // If we created the plugin directory and will want to remove it if we + // have to roll back the installation, let's add it to the installation + // step stack + + if ($created) + { + $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_root'))); + } + + // Copy all necessary files + if ($this->parent->parseFiles($xml->files, -1, $this->oldFiles) === false) + { + // Install failed, roll back changes + $this->parent->abort(); + return false; + } + + // Parse optional tags -- media and language files for plugins go in admin app + $this->parent->parseMedia($xml->media, 1); + $this->parent->parseLanguages($xml->languages, 1); + + // If there is a manifest script, lets copy it. + if ($this->get('manifest_script')) + { + $path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script'); + $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->get('manifest_script'); + + if (!file_exists($path['dest'])) + { + if (!$this->parent->copyFiles(array($path))) + { + // Install failed, rollback changes + $this->parent + ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_MANIFEST', JText::_('JLIB_INSTALLER_' . $this->route))); + return false; + } + } + } + + // Database Processing Section + + $row = JTable::getInstance('extension'); + // Was there a plugin with the same name already installed? + if ($id) + { + if (!$this->parent->getOverwrite()) + { + // Install failed, roll back changes + $this->parent + ->abort( + JText::sprintf( + 'JLIB_INSTALLER_ABORT_PLG_INSTALL_ALLREADY_EXISTS', JText::_('JLIB_INSTALLER_' . $this->route), + $this->get('name') + ) + ); + return false; + } + $row->load($id); + $row->name = $this->get('name'); + $row->manifest_cache = $this->parent->generateManifestCache(); + $row->store(); // update the manifest cache and name + } + else + { + // Store in the extensions table (1.6) + $row->name = $this->get('name'); + $row->type = 'plugin'; + $row->ordering = 0; + $row->element = $element; + $row->folder = $group; + $row->enabled = 0; + $row->protected = 0; + $row->access = 1; + $row->client_id = 0; + $row->params = $this->parent->getParams(); + // Custom data + $row->custom_data = ''; + // System data + $row->system_data = ''; + $row->manifest_cache = $this->parent->generateManifestCache(); + + // Editor plugins are published by default + if ($group == 'editors') + { + $row->enabled = 1; + } + + if (!$row->store()) + { + // Install failed, roll back changes + $this->parent + ->abort( + JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true)) + ); + return false; + } + + // Since we have created a plugin item, we add it to the installation step stack + // so that if we have to rollback the changes we can undo it. + $this->parent->pushStep(array('type' => 'extension', 'id' => $row->extension_id)); + $id = $row->extension_id; + } + + // Let's run the queries for the module + // If Joomla 1.5 compatible, with discreet sql files - execute appropriate + // file for utf-8 support or non-utf-8 support + + // Try for Joomla 1.5 type queries + // Second argument is the utf compatible version attribute + if (strtolower($this->route) == 'install') + { + $utfresult = $this->parent->parseSQLFiles($this->manifest->install->sql); + if ($utfresult === false) + { + // Install failed, rollback changes + $this->parent + ->abort( + JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_SQL_ERROR', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true)) + ); + return false; + } + + // Set the schema version to be the latest update version + if ($this->manifest->update) + { + $this->parent->setSchemaVersion($this->manifest->update->schemas, $row->extension_id); + } + } + elseif (strtolower($this->route) == 'update') + { + if ($this->manifest->update) + { + $result = $this->parent->parseSchemaUpdates($this->manifest->update->schemas, $row->extension_id); + if ($result === false) + { + // Install failed, rollback changes + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_UPDATE_SQL_ERROR', $db->stderr(true))); + return false; + } + } + } + + // Start Joomla! 1.6 + ob_start(); + ob_implicit_flush(false); + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, $this->route)) + { + if ($this->parent->manifestClass->{$this->route}($this) === false) + { + // Install failed, rollback changes + $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PLG_INSTALL_CUSTOM_INSTALL_FAILURE')); + return false; + } + } + // Append messages + $msg .= ob_get_contents(); + ob_end_clean(); + + // Finalization and Cleanup Section + + // Lastly, we will copy the manifest file to its appropriate place. + if (!$this->parent->copyManifest(-1)) + { + // Install failed, rollback changes + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_INSTALL_COPY_SETUP', JText::_('JLIB_INSTALLER_' . $this->route))); + return false; + } + // And now we run the postflight + ob_start(); + ob_implicit_flush(false); + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight')) + { + $this->parent->manifestClass->postflight($this->route, $this); + } + // Append messages + $msg .= ob_get_contents(); + ob_end_clean(); + if ($msg != '') + { + $this->parent->set('extension_message', $msg); + } + return $id; + } + + /** + * Custom update method + * + * @return boolean True on success + * + * @since 11.1 + */ + public function update() + { + // Set the overwrite setting + $this->parent->setOverwrite(true); + $this->parent->setUpgrade(true); + // Set the route for the install + $this->route = 'update'; + // Go to install which handles updates properly + return $this->install(); + } + + /** + * Custom uninstall method + * + * @param integer $id The id of the plugin to uninstall + * + * @return boolean True on success + * + * @since 11.1 + */ + public function uninstall($id) + { + $this->route = 'uninstall'; + + // Initialise variables. + $row = null; + $retval = true; + $db = $this->parent->getDbo(); + + // First order of business will be to load the plugin object table from the database. + // This should give us the necessary information to proceed. + $row = JTable::getInstance('extension'); + if (!$row->load((int) $id)) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_ERRORUNKOWNEXTENSION')); + return false; + } + + // Is the plugin we are trying to uninstall a core one? + // Because that is not a good idea... + if ($row->protected) + { + JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_WARNCOREPLUGIN', $row->name)); + return false; + } + + // Get the plugin folder so we can properly build the plugin path + if (trim($row->folder) == '') + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_FOLDER_FIELD_EMPTY')); + return false; + } + + // Set the plugin root path + if (is_dir(JPATH_PLUGINS . '/' . $row->folder . '/' . $row->element)) + { + // Use 1.6 plugins + $this->parent->setPath('extension_root', JPATH_PLUGINS . '/' . $row->folder . '/' . $row->element); + } + else + { + // Use Legacy 1.5 plugins + $this->parent->setPath('extension_root', JPATH_PLUGINS . '/' . $row->folder); + } + + // Because 1.5 plugins don't have their own folders we cannot use the standard method of finding an installation manifest + // Since 1.6 they do, however until we move to 1.7 and remove 1.6 legacy we still need to use this method. + // When we get there it'll be something like "$this->parent->findManifest();$manifest = $this->parent->getManifest();" + $manifestFile = $this->parent->getPath('extension_root') . '/' . $row->element . '.xml'; + + if (!file_exists($manifestFile)) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_INVALID_NOTFOUND_MANIFEST')); + return false; + } + + $xml = JFactory::getXML($manifestFile); + + $this->manifest = $xml; + + // If we cannot load the XML file return null + if (!$xml) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_LOAD_MANIFEST')); + return false; + } + + /* + * Check for a valid XML root tag. + * @todo: Remove backwards compatibility in a future version + * Should be 'extension', but for backward compatibility we will accept 'install'. + */ + if ($xml->getName() != 'install' && $xml->getName() != 'extension') + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_INVALID_MANIFEST')); + return false; + } + + // Attempt to load the language file; might have uninstall strings + $this->parent->setPath('source', JPATH_PLUGINS . '/' . $row->folder . '/' . $row->element); + $this->loadLanguage(JPATH_PLUGINS . '/' . $row->folder . '/' . $row->element); + + // Installer Trigger Loading + + // If there is an manifest class file, let's load it; we'll copy it later (don't have dest yet) + $manifestScript = (string) $xml->scriptfile; + if ($manifestScript) + { + $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript; + if (is_file($manifestScriptFile)) + { + // Load the file + include_once $manifestScriptFile; + } + // If a dash is present in the folder, remove it + $folderClass = str_replace('-', '', $row->folder); + // Set the class name + $classname = 'plg' . $folderClass . $row->element . 'InstallerScript'; + if (class_exists($classname)) + { + // Create a new instance + $this->parent->manifestClass = new $classname($this); + // And set this so we can copy it later + $this->set('manifest_script', $manifestScript); + + // Note: if we don't find the class, don't bother to copy the file + } + } + + // Run preflight if possible (since we know we're not an update) + ob_start(); + ob_implicit_flush(false); + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight')) + { + if ($this->parent->manifestClass->preflight($this->route, $this) === false) + { + // Install failed, rollback changes + $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PLG_INSTALL_CUSTOM_INSTALL_FAILURE')); + return false; + } + } + // Create msg object; first use here + $msg = ob_get_contents(); + ob_end_clean(); + + // Let's run the queries for the module + // If Joomla 1.5 compatible, with discreet sql files - execute appropriate + // file for utf-8 support or non-utf-8 support + + // Try for Joomla 1.5 type queries + // Second argument is the utf compatible version attribute + $utfresult = $this->parent->parseSQLFiles($xml->{strtolower($this->route)}->sql); + if ($utfresult === false) + { + // Install failed, rollback changes + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PLG_UNINSTALL_SQL_ERROR', $db->stderr(true))); + return false; + } + + // Start Joomla! 1.6 + ob_start(); + ob_implicit_flush(false); + if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'uninstall')) + { + $this->parent->manifestClass->uninstall($this); + } + // Append messages + $msg = ob_get_contents(); + ob_end_clean(); + + // Remove the plugin files + $this->parent->removeFiles($xml->images, -1); + $this->parent->removeFiles($xml->files, -1); + JFile::delete($manifestFile); + + // Remove all media and languages as well + $this->parent->removeFiles($xml->media); + $this->parent->removeFiles($xml->languages, 1); + + // Remove the schema version + $query = $db->getQuery(true); + $query->delete()->from('#__schemas')->where('extension_id = ' . $row->extension_id); + $db->setQuery($query); + $db->Query(); + + // Now we will no longer need the plugin object, so let's delete it + $row->delete($row->extension_id); + unset($row); + + // If the folder is empty, let's delete it + $files = JFolder::files($this->parent->getPath('extension_root')); + + JFolder::delete($this->parent->getPath('extension_root')); + + if ($msg) + { + $this->parent->set('extension_message', $msg); + } + + return $retval; + } + + /** + * Custom discover method + * + * @return array JExtension) list of extensions available + * + * @since 11.1 + */ + public function discover() + { + $results = array(); + $folder_list = JFolder::folders(JPATH_SITE . '/plugins'); + + foreach ($folder_list as $folder) + { + $file_list = JFolder::files(JPATH_SITE . '/plugins/' . $folder, '\.xml$'); + foreach ($file_list as $file) + { + $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_SITE . '/plugins/' . $folder . '/' . $file); + $file = JFile::stripExt($file); + // Ignore example plugins + if ($file == 'example') + { + continue; + } + + $extension = JTable::getInstance('extension'); + $extension->set('type', 'plugin'); + $extension->set('client_id', 0); + $extension->set('element', $file); + $extension->set('folder', $folder); + $extension->set('name', $file); + $extension->set('state', -1); + $extension->set('manifest_cache', json_encode($manifest_details)); + $results[] = $extension; + } + $folder_list = JFolder::folders(JPATH_SITE . '/plugins/' . $folder); + foreach ($folder_list as $plugin_folder) + { + $file_list = JFolder::files(JPATH_SITE . '/plugins/' . $folder . '/' . $plugin_folder, '\.xml$'); + foreach ($file_list as $file) + { + $manifest_details = JApplicationHelper::parseXMLInstallFile( + JPATH_SITE . '/plugins/' . $folder . '/' . $plugin_folder . '/' . $file + ); + $file = JFile::stripExt($file); + + if ($file == 'example') + { + continue; + } + + // ignore example plugins + $extension = JTable::getInstance('extension'); + $extension->set('type', 'plugin'); + $extension->set('client_id', 0); + $extension->set('element', $file); + $extension->set('folder', $folder); + $extension->set('name', $file); + $extension->set('state', -1); + $extension->set('manifest_cache', json_encode($manifest_details)); + $results[] = $extension; + } + } + } + return $results; + } + + /** + * Custom discover_install method. + * + * @return mixed + * + * @since 11.1 + */ + public function discover_install() + { + // Plugins use the extensions table as their primary store + // Similar to modules and templates, rather easy + // If it's not in the extensions table we just add it + $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); + if (is_dir($client->path . '/plugins/' . $this->parent->extension->folder . '/' . $this->parent->extension->element)) + { + $manifestPath = $client->path . '/plugins/' . $this->parent->extension->folder . '/' . $this->parent->extension->element . '/' + . $this->parent->extension->element . '.xml'; + } + else + { + $manifestPath = $client->path . '/plugins/' . $this->parent->extension->folder . '/' . $this->parent->extension->element . '.xml'; + } + $this->parent->manifest = $this->parent->isManifest($manifestPath); + $description = (string) $this->parent->manifest->description; + if ($description) + { + $this->parent->set('message', JText::_($description)); + } + else + { + $this->parent->set('message', ''); + } + $this->parent->setPath('manifest', $manifestPath); + $manifest_details = JApplicationHelper::parseXMLInstallFile($manifestPath); + $this->parent->extension->manifest_cache = json_encode($manifest_details); + $this->parent->extension->state = 0; + $this->parent->extension->name = $manifest_details['name']; + $this->parent->extension->enabled = ('editors' == $this->parent->extension->folder) ? 1 : 0; + $this->parent->extension->params = $this->parent->getParams(); + if ($this->parent->extension->store()) + { + return $this->parent->extension->get('extension_id'); + } + else + { + JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_PLG_DISCOVER_STORE_DETAILS')); + return false; + } + } + + /** + * Refreshes the extension table cache. + * + * @return boolean Result of operation, true if updated, false on failure. + * + * @since 11.1 + */ + public function refreshManifestCache() + { + // Plugins use the extensions table as their primary store + // Similar to modules and templates, rather easy + // If it's not in the extensions table we just add it + $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); + $manifestPath = $client->path . '/plugins/' . $this->parent->extension->folder . '/' . $this->parent->extension->element . '/' + . $this->parent->extension->element . '.xml'; + $this->parent->manifest = $this->parent->isManifest($manifestPath); + $this->parent->setPath('manifest', $manifestPath); + $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); + $this->parent->extension->manifest_cache = json_encode($manifest_details); + + $this->parent->extension->name = $manifest_details['name']; + if ($this->parent->extension->store()) + { + return true; + } + else + { + JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_PLG_REFRESH_MANIFEST_CACHE')); + return false; + } + } +} diff --git a/libraries/joomla/installer/adapters/template.php b/libraries/joomla/installer/adapters/template.php index 6bd5da8050a73..33ff1fd94195c 100644 --- a/libraries/joomla/installer/adapters/template.php +++ b/libraries/joomla/installer/adapters/template.php @@ -1,559 +1,581 @@ -parent->getPath('source'); - - if (!$source) - { - $this->parent - ->setPath( - 'source', - ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/templates/' . $this->parent->extension->element - ); - } - - $clientId = isset($this->parent->extension) ? $this->parent->extension->client_id : 0; - $this->manifest = $this->parent->getManifest(); - $name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd')); - $client = (string) $this->manifest->attributes()->client; - - // Load administrator language if not set. - if (!$client) - { - $client = 'ADMINISTRATOR'; - } - - $extension = "tpl_$name"; - $lang = JFactory::getLanguage(); - $source = $path ? $path : ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/templates/' . $name; - $lang->load($extension . '.sys', $source, null, false, false) - || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), null, false, false) - || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false) - || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), $lang->getDefault(), false, false); - } - - /** - * Custom install method - * - * @return boolean True on success - * - * @since 11.1 - */ - public function install() - { - $lang = JFactory::getLanguage(); - $xml = $this->parent->getManifest(); - - // Get the client application target - if ($cname = (string) $xml->attributes()->client) - { - // Attempt to map the client to a base path - $client = JApplicationHelper::getClientInfo($cname, true); - if ($client === false) - { - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_UNKNOWN_CLIENT', $cname)); - return false; - } - $basePath = $client->path; - $clientId = $client->id; - } - else - { - // No client attribute was found so we assume the site as the client - $cname = 'site'; - $basePath = JPATH_SITE; - $clientId = 0; - } - - // Set the extension's name - $name = JFilterInput::getInstance()->clean((string) $xml->name, 'cmd'); - - $element = strtolower(str_replace(" ", "_", $name)); - $this->set('name', $name); - $this->set('element', $element); - - $db = $this->parent->getDbo(); - $query = $db->getQuery(true); - $query->select($db->quoteName('extension_id')); - $query->from($db->quoteName('#__extensions')); - $query->where($db->quoteName('type') . ' = ' . $db->quote('template')); - $query->where($db->quoteName('element') . ' = ' . $element); - $id = $db->loadResult(); - - // Set the template root path - $this->parent->setPath('extension_root', $basePath . '/templates/' . $element); - - // if it's on the fs... - if (file_exists($this->parent->getPath('extension_root')) && (!$this->parent->getOverwrite() || $this->parent->getUpgrade())) - { - $updateElement = $xml->update; - // Upgrade manually set or - // Update function available or - // Update tag detected - if ($this->parent->getUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update')) - || is_a($updateElement, 'JXMLElement')) - { - // Force this one - $this->parent->setOverwrite(true); - $this->parent->setUpgrade(true); - if ($id) - { - // if there is a matching extension mark this as an update; semantics really - $this->route = 'update'; - } - } - elseif (!$this->parent->getOverwrite()) - { - // Overwrite is not set - // If we didn't have overwrite set, find an update function or find an update tag so let's call it safe - $this->parent - ->abort( - JText::sprintf( - 'JLIB_INSTALLER_ABORT_PLG_INSTALL_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route), - $this->parent->getPath('extension_root') - ) - ); - return false; - } - } - - /* - * If the template directory already exists, then we will assume that the template is already - * installed or another template is using that directory. - */ - if (file_exists($this->parent->getPath('extension_root')) && !$this->parent->getOverwrite()) - { - JError::raiseWarning( - 100, - JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_ANOTHER_TEMPLATE_USING_DIRECTORY', $this->parent->getPath('extension_root')) - ); - return false; - } - - // If the template directory does not exist, let's create it - $created = false; - if (!file_exists($this->parent->getPath('extension_root'))) - { - if (!$created = JFolder::create($this->parent->getPath('extension_root'))) - { - $this->parent - ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_FAILED_CREATE_DIRECTORY', $this->parent->getPath('extension_root'))); - - return false; - } - } - - // If we created the template directory and will want to remove it if we have to roll back - // the installation, let's add it to the installation step stack - if ($created) - { - $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_root'))); - } - - // Copy all the necessary files - if ($this->parent->parseFiles($xml->files, -1) === false) - { - // Install failed, rollback changes - $this->parent->abort(); - - return false; - } - - if ($this->parent->parseFiles($xml->images, -1) === false) - { - // Install failed, rollback changes - $this->parent->abort(); - - return false; - } - - if ($this->parent->parseFiles($xml->css, -1) === false) - { - // Install failed, rollback changes - $this->parent->abort(); - - return false; - } - - // Parse optional tags - $this->parent->parseMedia($xml->media); - $this->parent->parseLanguages($xml->languages, $clientId); - - // Get the template description - $this->parent->set('message', JText::_((string) $xml->description)); - - // Lastly, we will copy the manifest file to its appropriate place. - if (!$this->parent->copyManifest(-1)) - { - // Install failed, rollback changes - $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_TPL_INSTALL_COPY_SETUP')); - - return false; - } - - // Extension Registration - - $row = JTable::getInstance('extension'); - - if ($this->route == 'update' && $id) - { - $row->load($id); - } - else - { - $row->type = 'template'; - $row->element = $this->get('element'); - // There is no folder for templates - $row->folder = ''; - $row->enabled = 1; - $row->protected = 0; - $row->access = 1; - $row->client_id = $clientId; - $row->params = $this->parent->getParams(); - $row->custom_data = ''; // custom data - } - $row->name = $this->get('name'); // name might change in an update - $row->manifest_cache = $this->parent->generateManifestCache(); - - if (!$row->store()) - { - // Install failed, roll back changes - $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_ROLLBACK', $db->stderr(true))); - - return false; - } - - if ($this->route == 'install') - { - //insert record in #__template_styles - $query = $db->getQuery(true); - $query->insert('#__template_styles'); - $query->set('template=' . $db->Quote($row->element)); - $query->set('client_id=' . $db->Quote($clientId)); - $query->set('home=0'); - $debug = $lang->setDebug(false); - $query->set('title=' . $db->Quote(JText::sprintf('JLIB_INSTALLER_DEFAULT_STYLE', JText::_($this->get('name'))))); - $lang->setDebug($debug); - $query->set('params=' . $db->Quote($row->params)); - $db->setQuery($query); - // There is a chance this could fail but we don't care... - $db->query(); - } - - return $row->get('extension_id'); - } - - /** - * Custom update method for components - * - * @return boolean True on success - * - * @since 11.1 - */ - public function update() - { - return $this->install(); - } - - /** - * Custom uninstall method - * - * @param integer $id The extension ID - * - * @return boolean True on success - * - * @since 11.1 - */ - public function uninstall($id) - { - // Initialise variables. - $retval = true; - - // First order of business will be to load the template object table from the database. - // This should give us the necessary information to proceed. - $row = JTable::getInstance('extension'); - - if (!$row->load((int) $id) || !strlen($row->element)) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_ERRORUNKOWNEXTENSION')); - return false; - } - - // Is the template we are trying to uninstall a core one? - // Because that is not a good idea... - if ($row->protected) - { - JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_WARNCORETEMPLATE', $row->name)); - return false; - } - - $name = $row->element; - $clientId = $row->client_id; - - // For a template the id will be the template name which represents the subfolder of the templates folder that the template resides in. - if (!$name) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_ID_EMPTY')); - - return false; - } - - // Deny remove default template - $db = $this->parent->getDbo(); - $query = 'SELECT COUNT(*) FROM #__template_styles' . ' WHERE home = 1 AND template = ' . $db->Quote($name); - $db->setQuery($query); - - if ($db->loadResult() != 0) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_DEFAULT')); - - return false; - } - - // Get the template root path - $client = JApplicationHelper::getClientInfo($clientId); - - if (!$client) - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_INVALID_CLIENT')); - return false; - } - - $this->parent->setPath('extension_root', $client->path . '/templates/' . strtolower($name)); - $this->parent->setPath('source', $this->parent->getPath('extension_root')); - - // We do findManifest to avoid problem when uninstalling a list of extensions: getManifest cache its manifest file - $this->parent->findManifest(); - $manifest = $this->parent->getManifest(); - if (!($manifest instanceof JXMLElement)) - { - // Kill the extension entry - $row->delete($row->extension_id); - unset($row); - // Make sure we delete the folders - JFolder::delete($this->parent->getPath('extension_root')); - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_INVALID_NOTFOUND_MANIFEST')); - - return false; - } - - // Remove files - $this->parent->removeFiles($manifest->media); - $this->parent->removeFiles($manifest->languages, $clientId); - - // Delete the template directory - if (JFolder::exists($this->parent->getPath('extension_root'))) - { - $retval = JFolder::delete($this->parent->getPath('extension_root')); - } - else - { - JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_DIRECTORY')); - $retval = false; - } - - // Set menu that assigned to the template back to default template - $query = 'UPDATE #__menu INNER JOIN #__template_styles' . ' ON #__template_styles.id = #__menu.template_style_id' - . ' SET #__menu.template_style_id = 0' . ' WHERE #__template_styles.template = ' . $db->Quote(strtolower($name)) - . ' AND #__template_styles.client_id = ' . $db->Quote($clientId); - $db->setQuery($query); - $db->Query(); - - $query = 'DELETE FROM #__template_styles' . ' WHERE template = ' . $db->Quote($name) . ' AND client_id = ' . $db->Quote($clientId); - $db->setQuery($query); - $db->Query(); - - $row->delete($row->extension_id); - unset($row); - - return $retval; - } - - /** - * Discover existing but uninstalled templates - * - * @return array JExtensionTable list - */ - public function discover() - { - $results = array(); - $site_list = JFolder::folders(JPATH_SITE . '/templates'); - $admin_list = JFolder::folders(JPATH_ADMINISTRATOR . '/templates'); - $site_info = JApplicationHelper::getClientInfo('site', true); - $admin_info = JApplicationHelper::getClientInfo('administrator', true); - - foreach ($site_list as $template) - { - if ($template == 'system') - { - continue; - - // Ignore special system template - } - $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_SITE . "/templates/$template/templateDetails.xml"); - $extension = JTable::getInstance('extension'); - $extension->set('type', 'template'); - $extension->set('client_id', $site_info->id); - $extension->set('element', $template); - $extension->set('name', $template); - $extension->set('state', -1); - $extension->set('manifest_cache', json_encode($manifest_details)); - $results[] = $extension; - } - - foreach ($admin_list as $template) - { - if ($template == 'system') - { - continue; - - // Ignore special system template - } - - $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_ADMINISTRATOR . "/templates/$template/templateDetails.xml"); - $extension = JTable::getInstance('extension'); - $extension->set('type', 'template'); - $extension->set('client_id', $admin_info->id); - $extension->set('element', $template); - $extension->set('name', $template); - $extension->set('state', -1); - $extension->set('manifest_cache', json_encode($manifest_details)); - $results[] = $extension; - } - - return $results; - } - - /** - * Discover_install - * Perform an install for a discovered extension - * - * @return boolean - * - * @since 11.1 - */ - public function discover_install() - { - // Templates are one of the easiest - // If its not in the extensions table we just add it - $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); - $manifestPath = $client->path . '/templates/' . $this->parent->extension->element . '/templateDetails.xml'; - $this->parent->manifest = $this->parent->isManifest($manifestPath); - $description = (string) $this->parent->manifest->description; - - if ($description) - { - $this->parent->set('message', JText::_($description)); - } - else - { - $this->parent->set('message', ''); - } - - $this->parent->setPath('manifest', $manifestPath); - $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); - $this->parent->extension->manifest_cache = json_encode($manifest_details); - $this->parent->extension->state = 0; - $this->parent->extension->name = $manifest_details['name']; - $this->parent->extension->enabled = 1; - - $data = new JObject; - - foreach ($manifest_details as $key => $value) - { - $data->set($key, $value); - } - - $this->parent->extension->params = $this->parent->getParams(); - - if ($this->parent->extension->store()) - { - //insert record in #__template_styles - $db = $this->parent->getDbo(); - $query = $db->getQuery(true); - $query->insert('#__template_styles'); - $query->set('template=' . $db->Quote($this->parent->extension->element)); - $query->set('client_id=' . $db->Quote($this->parent->extension->client_id)); - $query->set('home=0'); - $query->set('title=' . $db->Quote(JText::sprintf('JLIB_INSTALLER_DEFAULT_STYLE', $this->parent->extension->name))); - $query->set('params=' . $db->Quote($this->parent->extension->params)); - $db->setQuery($query); - $db->query(); - - return $this->parent->extension->get('extension_id'); - } - else - { - JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_TPL_DISCOVER_STORE_DETAILS')); - - return false; - } - } - - /** - * Refreshes the extension table cache - * - * @return boolean Result of operation, true if updated, false on failure - * - * @since 11.1 - */ - public function refreshManifestCache() - { - // Need to find to find where the XML file is since we don't store this normally. - $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); - $manifestPath = $client->path . '/templates/' . $this->parent->extension->element . '/templateDetails.xml'; - $this->parent->manifest = $this->parent->isManifest($manifestPath); - $this->parent->setPath('manifest', $manifestPath); - - $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); - $this->parent->extension->manifest_cache = json_encode($manifest_details); - $this->parent->extension->name = $manifest_details['name']; - - try - { - return $this->parent->extension->store(); - } - catch (JException $e) - { - JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_TPL_REFRESH_MANIFEST_CACHE')); - return false; - } - } -} +parent->getPath('source'); + + if (!$source) + { + $this->parent + ->setPath( + 'source', + ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/templates/' . $this->parent->extension->element + ); + } + + $clientId = isset($this->parent->extension) ? $this->parent->extension->client_id : 0; + $this->manifest = $this->parent->getManifest(); + $name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd')); + $client = (string) $this->manifest->attributes()->client; + + // Load administrator language if not set. + if (!$client) + { + $client = 'ADMINISTRATOR'; + } + + $extension = "tpl_$name"; + $lang = JFactory::getLanguage(); + $source = $path ? $path : ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/templates/' . $name; + $lang->load($extension . '.sys', $source, null, false, false) + || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), null, false, false) + || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false) + || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), $lang->getDefault(), false, false); + } + + /** + * Custom install method + * + * @return boolean True on success + * + * @since 11.1 + */ + public function install() + { + // Get a database connector object + $db = $this->parent->getDbo(); + + $lang = JFactory::getLanguage(); + $xml = $this->parent->getManifest(); + + // Get the client application target + if ($cname = (string) $xml->attributes()->client) + { + // Attempt to map the client to a base path + $client = JApplicationHelper::getClientInfo($cname, true); + if ($client === false) + { + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_UNKNOWN_CLIENT', $cname)); + return false; + } + $basePath = $client->path; + $clientId = $client->id; + } + else + { + // No client attribute was found so we assume the site as the client + $cname = 'site'; + $basePath = JPATH_SITE; + $clientId = 0; + } + + // Set the extension's name + $name = JFilterInput::getInstance()->clean((string) $xml->name, 'cmd'); + + $element = strtolower(str_replace(" ", "_", $name)); + $this->set('name', $name); + $this->set('element', $element); + + // Check to see if a template by the same name is already installed. + $query = $db->getQuery(true); + $query->select($query->qn('extension_id'))->from($query->qn('#__extensions')); + $query->where($query->qn('type') . ' = ' . $query->q('template')); + $query->where($query->qn('element') . ' = ' . $query->q($element)); + $db->setQuery($query); + + try + { + $id = $db->loadResult(); + } + catch (JDatabaseException $e) + { + // Install failed, roll back changes + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_ROLLBACK'), $e->getMessage()); + return false; + } + + // Legacy error handling switch based on the JError::$legacy switch. + // @deprecated 12.1 + if (JError::$legacy && $db->getErrorNum()) + { + // Install failed, roll back changes + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_ROLLBACK', $db->stderr(true))); + return false; + } + + // Set the template root path + $this->parent->setPath('extension_root', $basePath . '/templates/' . $element); + + // if it's on the fs... + if (file_exists($this->parent->getPath('extension_root')) && (!$this->parent->getOverwrite() || $this->parent->getUpgrade())) + { + $updateElement = $xml->update; + // Upgrade manually set or + // Update function available or + // Update tag detected + if ($this->parent->getUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update')) + || $updateElement) + { + // Force this one + $this->parent->setOverwrite(true); + $this->parent->setUpgrade(true); + if ($id) + { + // if there is a matching extension mark this as an update; semantics really + $this->route = 'update'; + } + } + elseif (!$this->parent->getOverwrite()) + { + // Overwrite is not set + // If we didn't have overwrite set, find an update function or find an update tag so let's call it safe + $this->parent + ->abort( + JText::sprintf( + 'JLIB_INSTALLER_ABORT_TPL_INSTALL_ANOTHER_TEMPLATE_USING_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route), + $this->parent->getPath('extension_root') + ) + ); + return false; + } + } + + /* + * If the template directory already exists, then we will assume that the template is already + * installed or another template is using that directory. + */ + if (file_exists($this->parent->getPath('extension_root')) && !$this->parent->getOverwrite()) + { + JError::raiseWarning( + 100, + JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_ANOTHER_TEMPLATE_USING_DIRECTORY', $this->parent->getPath('extension_root')) + ); + return false; + } + + // If the template directory does not exist, let's create it + $created = false; + if (!file_exists($this->parent->getPath('extension_root'))) + { + if (!$created = JFolder::create($this->parent->getPath('extension_root'))) + { + $this->parent + ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_FAILED_CREATE_DIRECTORY', $this->parent->getPath('extension_root'))); + + return false; + } + } + + // If we created the template directory and will want to remove it if we have to roll back + // the installation, let's add it to the installation step stack + if ($created) + { + $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_root'))); + } + + // Copy all the necessary files + if ($this->parent->parseFiles($xml->files, -1) === false) + { + // Install failed, rollback changes + $this->parent->abort(); + + return false; + } + + if ($this->parent->parseFiles($xml->images, -1) === false) + { + // Install failed, rollback changes + $this->parent->abort(); + + return false; + } + + if ($this->parent->parseFiles($xml->css, -1) === false) + { + // Install failed, rollback changes + $this->parent->abort(); + + return false; + } + + // Parse optional tags + $this->parent->parseMedia($xml->media); + $this->parent->parseLanguages($xml->languages, $clientId); + + // Get the template description + $this->parent->set('message', JText::_((string) $xml->description)); + + // Lastly, we will copy the manifest file to its appropriate place. + if (!$this->parent->copyManifest(-1)) + { + // Install failed, rollback changes + $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_TPL_INSTALL_COPY_SETUP')); + + return false; + } + + // Extension Registration + + $row = JTable::getInstance('extension'); + + if ($this->route == 'update' && $id) + { + $row->load($id); + } + else + { + $row->type = 'template'; + $row->element = $this->get('element'); + // There is no folder for templates + $row->folder = ''; + $row->enabled = 1; + $row->protected = 0; + $row->access = 1; + $row->client_id = $clientId; + $row->params = $this->parent->getParams(); + $row->custom_data = ''; // custom data + } + $row->name = $this->get('name'); // name might change in an update + $row->manifest_cache = $this->parent->generateManifestCache(); + + if (!$row->store()) + { + // Install failed, roll back changes + $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_ROLLBACK', $db->stderr(true))); + + return false; + } + + if ($this->route == 'install') + { + //insert record in #__template_styles + $query = $db->getQuery(true); + $query->insert('#__template_styles'); + $query->set('template=' . $db->Quote($row->element)); + $query->set('client_id=' . $db->Quote($clientId)); + $query->set('home=0'); + $debug = $lang->setDebug(false); + $query->set('title=' . $db->Quote(JText::sprintf('JLIB_INSTALLER_DEFAULT_STYLE', JText::_($this->get('name'))))); + $lang->setDebug($debug); + $query->set('params=' . $db->Quote($row->params)); + $db->setQuery($query); + // There is a chance this could fail but we don't care... + $db->query(); + } + + return $row->get('extension_id'); + } + + /** + * Custom update method for components + * + * @return boolean True on success + * + * @since 11.1 + */ + public function update() + { + return $this->install(); + } + + /** + * Custom uninstall method + * + * @param integer $id The extension ID + * + * @return boolean True on success + * + * @since 11.1 + */ + public function uninstall($id) + { + // Initialise variables. + $retval = true; + + // First order of business will be to load the template object table from the database. + // This should give us the necessary information to proceed. + $row = JTable::getInstance('extension'); + + if (!$row->load((int) $id) || !strlen($row->element)) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_ERRORUNKOWNEXTENSION')); + return false; + } + + // Is the template we are trying to uninstall a core one? + // Because that is not a good idea... + if ($row->protected) + { + JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_WARNCORETEMPLATE', $row->name)); + return false; + } + + $name = $row->element; + $clientId = $row->client_id; + + // For a template the id will be the template name which represents the subfolder of the templates folder that the template resides in. + if (!$name) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_ID_EMPTY')); + + return false; + } + + // Deny remove default template + $db = $this->parent->getDbo(); + $query = 'SELECT COUNT(*) FROM #__template_styles' . ' WHERE home = 1 AND template = ' . $db->Quote($name); + $db->setQuery($query); + + if ($db->loadResult() != 0) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_DEFAULT')); + + return false; + } + + // Get the template root path + $client = JApplicationHelper::getClientInfo($clientId); + + if (!$client) + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_INVALID_CLIENT')); + return false; + } + + $this->parent->setPath('extension_root', $client->path . '/templates/' . strtolower($name)); + $this->parent->setPath('source', $this->parent->getPath('extension_root')); + + // We do findManifest to avoid problem when uninstalling a list of extensions: getManifest cache its manifest file + $this->parent->findManifest(); + $manifest = $this->parent->getManifest(); + if (!($manifest instanceof JXMLElement)) + { + // Kill the extension entry + $row->delete($row->extension_id); + unset($row); + // Make sure we delete the folders + JFolder::delete($this->parent->getPath('extension_root')); + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_INVALID_NOTFOUND_MANIFEST')); + + return false; + } + + // Remove files + $this->parent->removeFiles($manifest->media); + $this->parent->removeFiles($manifest->languages, $clientId); + + // Delete the template directory + if (JFolder::exists($this->parent->getPath('extension_root'))) + { + $retval = JFolder::delete($this->parent->getPath('extension_root')); + } + else + { + JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_DIRECTORY')); + $retval = false; + } + + // Set menu that assigned to the template back to default template + $query = 'UPDATE #__menu INNER JOIN #__template_styles' . ' ON #__template_styles.id = #__menu.template_style_id' + . ' SET #__menu.template_style_id = 0' . ' WHERE #__template_styles.template = ' . $db->Quote(strtolower($name)) + . ' AND #__template_styles.client_id = ' . $db->Quote($clientId); + $db->setQuery($query); + $db->Query(); + + $query = 'DELETE FROM #__template_styles' . ' WHERE template = ' . $db->Quote($name) . ' AND client_id = ' . $db->Quote($clientId); + $db->setQuery($query); + $db->Query(); + + $row->delete($row->extension_id); + unset($row); + + return $retval; + } + + /** + * Discover existing but uninstalled templates + * + * @return array JExtensionTable list + */ + public function discover() + { + $results = array(); + $site_list = JFolder::folders(JPATH_SITE . '/templates'); + $admin_list = JFolder::folders(JPATH_ADMINISTRATOR . '/templates'); + $site_info = JApplicationHelper::getClientInfo('site', true); + $admin_info = JApplicationHelper::getClientInfo('administrator', true); + + foreach ($site_list as $template) + { + if ($template == 'system') + { + continue; + + // Ignore special system template + } + $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_SITE . "/templates/$template/templateDetails.xml"); + $extension = JTable::getInstance('extension'); + $extension->set('type', 'template'); + $extension->set('client_id', $site_info->id); + $extension->set('element', $template); + $extension->set('name', $template); + $extension->set('state', -1); + $extension->set('manifest_cache', json_encode($manifest_details)); + $results[] = $extension; + } + + foreach ($admin_list as $template) + { + if ($template == 'system') + { + continue; + + // Ignore special system template + } + + $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_ADMINISTRATOR . "/templates/$template/templateDetails.xml"); + $extension = JTable::getInstance('extension'); + $extension->set('type', 'template'); + $extension->set('client_id', $admin_info->id); + $extension->set('element', $template); + $extension->set('name', $template); + $extension->set('state', -1); + $extension->set('manifest_cache', json_encode($manifest_details)); + $results[] = $extension; + } + + return $results; + } + + /** + * Discover_install + * Perform an install for a discovered extension + * + * @return boolean + * + * @since 11.1 + */ + public function discover_install() + { + // Templates are one of the easiest + // If its not in the extensions table we just add it + $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); + $manifestPath = $client->path . '/templates/' . $this->parent->extension->element . '/templateDetails.xml'; + $this->parent->manifest = $this->parent->isManifest($manifestPath); + $description = (string) $this->parent->manifest->description; + + if ($description) + { + $this->parent->set('message', JText::_($description)); + } + else + { + $this->parent->set('message', ''); + } + + $this->parent->setPath('manifest', $manifestPath); + $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); + $this->parent->extension->manifest_cache = json_encode($manifest_details); + $this->parent->extension->state = 0; + $this->parent->extension->name = $manifest_details['name']; + $this->parent->extension->enabled = 1; + + $data = new JObject; + + foreach ($manifest_details as $key => $value) + { + $data->set($key, $value); + } + + $this->parent->extension->params = $this->parent->getParams(); + + if ($this->parent->extension->store()) + { + //insert record in #__template_styles + $db = $this->parent->getDbo(); + $query = $db->getQuery(true); + $query->insert('#__template_styles'); + $query->set('template=' . $db->Quote($this->parent->extension->element)); + $query->set('client_id=' . $db->Quote($this->parent->extension->client_id)); + $query->set('home=0'); + $query->set('title=' . $db->Quote(JText::sprintf('JLIB_INSTALLER_DEFAULT_STYLE', $this->parent->extension->name))); + $query->set('params=' . $db->Quote($this->parent->extension->params)); + $db->setQuery($query); + $db->query(); + + return $this->parent->extension->get('extension_id'); + } + else + { + JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_TPL_DISCOVER_STORE_DETAILS')); + + return false; + } + } + + /** + * Refreshes the extension table cache + * + * @return boolean Result of operation, true if updated, false on failure + * + * @since 11.1 + */ + public function refreshManifestCache() + { + // Need to find to find where the XML file is since we don't store this normally. + $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id); + $manifestPath = $client->path . '/templates/' . $this->parent->extension->element . '/templateDetails.xml'; + $this->parent->manifest = $this->parent->isManifest($manifestPath); + $this->parent->setPath('manifest', $manifestPath); + + $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest')); + $this->parent->extension->manifest_cache = json_encode($manifest_details); + $this->parent->extension->name = $manifest_details['name']; + + try + { + return $this->parent->extension->store(); + } + catch (JException $e) + { + JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_TPL_REFRESH_MANIFEST_CACHE')); + return false; + } + } +}