diff --git a/administrator/components/com_admin/sql/updates/mysql/4.0.0-2017-06-12.sql b/administrator/components/com_admin/sql/updates/mysql/4.0.0-2017-06-12.sql new file mode 100644 index 0000000000000..463ceb7e5dfe7 --- /dev/null +++ b/administrator/components/com_admin/sql/updates/mysql/4.0.0-2017-06-12.sql @@ -0,0 +1,2 @@ +ALTER TABLE `#__update_sites` ADD COLUMN `checked_out` int(10) unsigned NOT NULL DEFAULT 0; +ALTER TABLE `#__update_sites` ADD COLUMN `checked_out_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00'; diff --git a/administrator/components/com_admin/sql/updates/postgresql/4.0.0-2017-06-12.sql b/administrator/components/com_admin/sql/updates/postgresql/4.0.0-2017-06-12.sql new file mode 100644 index 0000000000000..c0730052badb4 --- /dev/null +++ b/administrator/components/com_admin/sql/updates/postgresql/4.0.0-2017-06-12.sql @@ -0,0 +1,2 @@ +ALTER TABLE `#__update_sites` ADD COLUMN `checked_out` int(10) unsigned NOT NULL DEFAULT 0; +ALTER TABLE `#__update_sites` ADD COLUMN `checked_out_time` timestamp without time zone DEFAULT '1970-01-01 00:00:00' NOT NULL; diff --git a/administrator/components/com_installer/Controller/Controller.php b/administrator/components/com_installer/Controller/Controller.php index 88f6cc9be9e46..d224d75ff4443 100644 --- a/administrator/components/com_installer/Controller/Controller.php +++ b/administrator/components/com_installer/Controller/Controller.php @@ -39,6 +39,17 @@ public function display($cachable = false, $urlparams = false) $vName = $this->input->get('view', 'install'); $vFormat = $document->getType(); $lName = $this->input->get('layout', 'default', 'string'); + $id = $this->input->getInt('update_site_id'); + + // Check for edit form. + if ($vName == 'updatesite' && $lName == 'edit' && !$this->checkEditId('com_installer.edit.updatesite', $id)) + { + // Somehow the person just went to the form - we don't allow that. + $this->setMessage(\JText::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $id), 'error'); + $this->setRedirect(\JRoute::_('index.php?option=com_installer&view=updatesites', false)); + + return false; + } // Get and render the view. if ($view = $this->getView($vName, $vFormat)) diff --git a/administrator/components/com_installer/Controller/Updatesite.php b/administrator/components/com_installer/Controller/Updatesite.php new file mode 100644 index 0000000000000..8932ed90f7b23 --- /dev/null +++ b/administrator/components/com_installer/Controller/Updatesite.php @@ -0,0 +1,69 @@ +getModel('updatesites'); + + // Get the first update_site_id checked in the table + $recordId = $this->input->post->get('cid', array(), 'array')[0]; + + // If no checkbox was selected then the user clicked on the update site name + $recordId = $recordId ? $recordId : $this->input->getInt('update_site_id'); + + // Get the list of the Joomla Core UpdateSites + $joomlaUpdateSitesIds = $model->getJoomlaUpdateSitesIds(0); + + if (in_array($recordId, $joomlaUpdateSitesIds)) + { + $this->setMessage( + \JText::sprintf( + 'COM_INSTALLER_MSG_UPDATESITES_DELETE_CANNOT_EDIT', + array_shift( + $model->getJoomlaUpdateSitesNames( + array($recordId) + ) + )->name + ), + 'error' + ); + + $this->setRedirect( + \JRoute::_( + 'index.php?option=' . $this->option . '&view=' . $this->view_list + . $this->getRedirectToListAppend(), false + ) + ); + + return false; + } + + parent::edit(); + } +} diff --git a/administrator/components/com_installer/Controller/Updatesites.php b/administrator/components/com_installer/Controller/Updatesites.php index 7637860ba364d..2e17872c1a5b6 100644 --- a/administrator/components/com_installer/Controller/Updatesites.php +++ b/administrator/components/com_installer/Controller/Updatesites.php @@ -6,13 +6,13 @@ * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ + namespace Joomla\Component\Installer\Administrator\Controller; defined('_JEXEC') or die; -use Joomla\CMS\Controller\Controller; +use Joomla\CMS\Controller\Admin; use Joomla\CMS\Mvc\Factory\MvcFactoryInterface; -use Joomla\Utilities\ArrayHelper; /** * Installer Update Sites Controller @@ -21,7 +21,7 @@ * @subpackage com_installer * @since 3.4 */ -class Updatesites extends Controller +class Updatesites extends Admin { /** * Constructor. @@ -38,95 +38,40 @@ public function __construct($config = array(), MvcFactoryInterface $factory = nu { parent::__construct($config, $factory, $app, $input); - $this->registerTask('unpublish', 'publish'); - $this->registerTask('publish', 'publish'); - $this->registerTask('delete', 'delete'); $this->registerTask('rebuild', 'rebuild'); } /** - * Enable/Disable an extension (if supported). - * - * @return void - * - * @since 3.4 - * - * @throws \Exception on error - */ - public function publish() - { - // Check for request forgeries. - \JSession::checkToken() or jexit(\JText::_('JINVALID_TOKEN')); - - $ids = $this->input->get('cid', array(), 'array'); - $values = array('publish' => 1, 'unpublish' => 0); - $task = $this->getTask(); - $value = ArrayHelper::getValue($values, $task, 0, 'int'); - - if (empty($ids)) - { - throw new \Exception(\JText::_('COM_INSTALLER_ERROR_NO_UPDATESITES_SELECTED'), 500); - } - - // Get the model. - /* @var \Joomla\Component\Installer\Administrator\Model\Updatesites $model */ - $model = $this->getModel('Updatesites'); - - // Change the state of the records. - if (!$model->publish($ids, $value)) - { - throw new \Exception(implode('', $model->getErrors()), 500); - } - - $ntext = ($value == 0) ? 'COM_INSTALLER_N_UPDATESITES_UNPUBLISHED' : 'COM_INSTALLER_N_UPDATESITES_PUBLISHED'; - - $this->setMessage(\JText::plural($ntext, count($ids))); - - $this->setRedirect(\JRoute::_('index.php?option=com_installer&view=updatesites', false)); - } - - /** - * Deletes an update site (if supported). + * Rebuild update sites tables. * * @return void * * @since 3.6 - * - * @throws \Exception on error */ - public function delete() + public function rebuild() { // Check for request forgeries. \JSession::checkToken() or jexit(\JText::_('JINVALID_TOKEN')); - $ids = $this->input->get('cid', array(), 'array'); - - if (empty($ids)) - { - throw new \Exception(\JText::_('COM_INSTALLER_ERROR_NO_UPDATESITES_SELECTED'), 500); - } - - // Delete the records. - $this->getModel('Updatesites')->delete($ids); + // Rebuild the update sites. + $this->getModel('updatesites')->rebuild(); $this->setRedirect(\JRoute::_('index.php?option=com_installer&view=updatesites', false)); } /** - * Rebuild update sites tables. + * Method to get a model object, loading it if required. * - * @return void + * @param string $name The model name. Optional. + * @param string $prefix The class prefix. Optional. + * @param array $config Configuration array for model. Optional. * - * @since 3.6 + * @return \Joomla\CMS\Model\Model The model. + * + * @since __DEPLOY_VERSION__ */ - public function rebuild() + public function getModel($name = 'Updatesite', $prefix = 'Administrator', $config = array('ignore_request' => true)) { - // Check for request forgeries. - \JSession::checkToken() or jexit(\JText::_('JINVALID_TOKEN')); - - // Rebuild the update sites. - $this->getModel('Updatesites')->rebuild(); - - $this->setRedirect(\JRoute::_('index.php?option=com_installer&view=updatesites', false)); + return parent::getModel($name, $prefix, $config); } } diff --git a/administrator/components/com_installer/Model/Updatesite.php b/administrator/components/com_installer/Model/Updatesite.php new file mode 100644 index 0000000000000..532c1900b3389 --- /dev/null +++ b/administrator/components/com_installer/Model/Updatesite.php @@ -0,0 +1,70 @@ +loadForm('com_installer.updatesite', 'updatesite', array('control' => 'jform', 'load_data' => $loadData)); + + if (empty($form)) + { + return false; + } + + return $form; + } + + /** + * Method to get the data that should be injected in the form. + * + * @return mixed The data for the form. + * + * @since __DEPLOY_VERSION__ + */ + protected function loadFormData() + { + $data = $this->getItem(); + + $this->preprocessData('com_installer.updatesite', $data); + + return $data; + } +} diff --git a/administrator/components/com_installer/Model/Updatesites.php b/administrator/components/com_installer/Model/Updatesites.php index 9df65a10f2651..635bd6229efcb 100644 --- a/administrator/components/com_installer/Model/Updatesites.php +++ b/administrator/components/com_installer/Model/Updatesites.php @@ -148,13 +148,8 @@ public function delete($ids = array()) $count = 0; - // Gets the update site names. - $query = $db->getQuery(true) - ->select($db->qn(array('update_site_id', 'name'))) - ->from($db->qn('#__update_sites')) - ->where($db->qn('update_site_id') . ' IN (' . implode(', ', $ids) . ')'); - $db->setQuery($query); - $updateSitesNames = $db->loadObjectList('update_site_id'); + // Gets Joomla core update sites names. + $updateSitesNames = $this->getJoomlaUpdateSitesNames($ids); // Gets Joomla core update sites Ids. $joomlaUpdateSitesIds = $this->getJoomlaUpdateSitesIds(0); @@ -377,7 +372,7 @@ public function rebuild() * * @since 3.6.0 */ - protected function getJoomlaUpdateSitesIds($column = 0) + public function getJoomlaUpdateSitesIds($column = 0) { $db = $this->getDbo(); @@ -398,6 +393,30 @@ protected function getJoomlaUpdateSitesIds($column = 0) return $db->loadColumn($column); } + /** + * Fetch the Joomla update sites names. + * + * @param array $ids Extension ids to delete. + * + * @return array Array with joomla core update site names. + * + * @since __DEPLOY_VERSION__ + */ + public function getJoomlaUpdateSitesNames($ids = array()) + { + $db = $this->getDbo(); + + // Gets the update site names. + $query = $db->getQuery(true) + ->select($db->quoteName(array('update_site_id', 'name'))) + ->from($db->quoteName('#__update_sites')) + ->where($db->quoteName('update_site_id') . ' IN (' . implode(', ', $ids) . ')'); + $db->setQuery($query); + $updateSitesNames = $db->loadObjectList('update_site_id'); + + return $updateSitesNames; + } + /** * Method to get the database query * @@ -410,9 +429,11 @@ protected function getListQuery() $query = $this->getDbo()->getQuery(true) ->select( array( - 's.update_site_id', + 's.checked_out', + 's.checked_out_time', 's.name AS update_site_name', 's.type AS update_site_type', + 's.update_site_id', 's.location', 's.enabled', 'e.extension_id', diff --git a/administrator/components/com_installer/Table/Updatesite.php b/administrator/components/com_installer/Table/Updatesite.php new file mode 100644 index 0000000000000..a1829bce1e653 --- /dev/null +++ b/administrator/components/com_installer/Table/Updatesite.php @@ -0,0 +1,68 @@ +typeAlias = 'com_installer.updatesite'; + + parent::__construct('#__update_sites', 'update_site_id', $db); + } + + /** + * Overloaded check function + * + * @return boolean True on success, false on failure + * + * @see \JTable::check + * @since __DEPLOY_VERSION__ + */ + public function check() + { + try + { + parent::check(); + } + catch (\Exception $e) + { + $this->setError($e->getMessage()); + + return false; + } + + // Check for valid name + if (trim($this->location) == '') + { + $this->setError(\JText::_('COM_INSTALLER_UPDATESITE_EDIT_VALID_NAME')); + + return false; + } + + return true; + } +} diff --git a/administrator/components/com_installer/View/Updatesite/Html.php b/administrator/components/com_installer/View/Updatesite/Html.php new file mode 100644 index 0000000000000..4f5fb5ea58b30 --- /dev/null +++ b/administrator/components/com_installer/View/Updatesite/Html.php @@ -0,0 +1,106 @@ +form = $this->get('Form'); + $this->item = $this->get('Item'); + + // Check for errors. + if (count($errors = $this->get('Errors'))) + { + throw new \JViewGenericdataexception(implode("\n", $errors), 500); + } + + $this->addToolbar(); + + return parent::display($tpl); + } + + /** + * Add the page title and toolbar. + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + protected function addToolbar() + { + \JFactory::getApplication()->input->set('hidemainmenu', true); + + $user = \JFactory::getUser(); + $userId = $user->id; + $checkedOut = !($this->item->checked_out == 0 || $this->item->checked_out == $userId); + + // Since we don't track these assets at the item level, use the category id. + $canDo = ContentHelper::getActions('com_installer', 'updatesite'); + + \JToolbarHelper::title(\JText::_('COM_INSTALLER_UPDATESITE_EDIT_TITLE'), 'address contact'); + + // Since it's an existing record, check the edit permission, or fall back to edit own if the owner. + $itemEditable = $canDo->get('core.edit'); + + $toolbarButtons = []; + + // Can't save the record if it's checked out and editable + if (!$checkedOut && $itemEditable) + { + $toolbarButtons[] = ['apply', 'updatesite.apply']; + $toolbarButtons[] = ['save', 'updatesite.save']; + } + + \JToolbarHelper::saveGroup( + $toolbarButtons, + 'btn-success' + ); + + \JToolbarHelper::cancel('updatesite.cancel', 'JTOOLBAR_CLOSE'); + } +} diff --git a/administrator/components/com_installer/View/Updatesites/Html.php b/administrator/components/com_installer/View/Updatesites/Html.php index 413e3892cb96f..eb7e4c8d83f6d 100644 --- a/administrator/components/com_installer/View/Updatesites/Html.php +++ b/administrator/components/com_installer/View/Updatesites/Html.php @@ -72,6 +72,11 @@ protected function addToolbar() { $canDo = ContentHelper::getActions('com_installer'); + if ($canDo->get('core.admin')) + { + ToolbarHelper::editList('updatesite.edit'); + } + if ($canDo->get('core.edit.state')) { ToolbarHelper::publish('updatesites.publish', 'JTOOLBAR_ENABLE', true); @@ -85,6 +90,11 @@ protected function addToolbar() ToolbarHelper::divider(); } + if ($canDo->get('core.edit.state')) + { + \JToolbarHelper::checkin('updatesites.checkin'); + } + if ($canDo->get('core.admin') || $canDo->get('core.options')) { ToolbarHelper::custom('updatesites.rebuild', 'refresh.png', 'refresh_f2.png', 'JTOOLBAR_REBUILD', false); diff --git a/administrator/components/com_installer/access.xml b/administrator/components/com_installer/access.xml index 1c90bfc488ca8..3065cd0a159c7 100644 --- a/administrator/components/com_installer/access.xml +++ b/administrator/components/com_installer/access.xml @@ -1,6 +1,7 @@ + diff --git a/administrator/components/com_installer/forms/updatesite.xml b/administrator/components/com_installer/forms/updatesite.xml new file mode 100644 index 0000000000000..124b74aed81fc --- /dev/null +++ b/administrator/components/com_installer/forms/updatesite.xml @@ -0,0 +1,31 @@ + + + + + + + + + diff --git a/administrator/components/com_installer/tmpl/updatesite/edit.php b/administrator/components/com_installer/tmpl/updatesite/edit.php new file mode 100644 index 0000000000000..7c2c03260e441 --- /dev/null +++ b/administrator/components/com_installer/tmpl/updatesite/edit.php @@ -0,0 +1,33 @@ + + + + + + + + form->getFieldset() as $field): ?> + + label; ?> + input; ?> + + + + + + + + + diff --git a/administrator/components/com_installer/tmpl/updatesites/default.php b/administrator/components/com_installer/tmpl/updatesites/default.php index 3eaf90f13a342..9c86e68ccfc3b 100644 --- a/administrator/components/com_installer/tmpl/updatesites/default.php +++ b/administrator/components/com_installer/tmpl/updatesites/default.php @@ -10,7 +10,10 @@ defined('_JEXEC') or die; JHtml::_('behavior.multiselect'); +JHtml::_('behavior.tabstate'); +$user = JFactory::getUser(); +$userId = $user->get('id'); $listOrder = $this->escape($this->state->get('list.ordering')); $listDirn = $this->escape($this->state->get('list.direction')); ?> @@ -65,8 +68,11 @@ - items as $i => $item) : ?> - + items as $i => $item) : + $canCheckin = $user->authorise('core.manage', 'com_checkin') || $item->checked_out == $userId || $item->checked_out == 0; + $canEdit = $user->authorise('core.edit', 'com_installer'); + ?> + update_site_id); ?> @@ -79,7 +85,16 @@ - update_site_name); ?> + checked_out) : ?> + editor, $item->checked_out_time, 'updatesites.', $canCheckin); ?> + + + checked_out ? '' : ''; ?> + + escape($item->name); ?> + + escape($item->name); ?> + escape($item->location); ?> diff --git a/administrator/language/en-GB/en-GB.com_installer.ini b/administrator/language/en-GB/en-GB.com_installer.ini index 707bb2cc6ff0e..7ac3d17a96319 100644 --- a/administrator/language/en-GB/en-GB.com_installer.ini +++ b/administrator/language/en-GB/en-GB.com_installer.ini @@ -26,6 +26,8 @@ COM_INSTALLER_EXTENSION_PROTECTED="Protected extension" COM_INSTALLER_EXTENSION_PUBLISHED="Extension enabled." COM_INSTALLER_EXTENSION_UNPUBLISHED="Extension disabled." COM_INSTALLER_FAILED_TO_ENABLE_UPDATES=", failed to enable updates" +COM_INSTALLER_FIELD_NAME_LABEL="Name" +COM_INSTALLER_FIELD_TYPE_LABEL="Type" COM_INSTALLER_FILTER_LABEL="Search by Extension Name" COM_INSTALLER_HEADER_DATABASE="Extensions: Database" COM_INSTALLER_HEADER_DISCOVER="Extensions: Discover" @@ -142,6 +144,7 @@ COM_INSTALLER_MSG_UPDATE_SUCCESS="Updating %s was successful." COM_INSTALLER_MSG_UPDATE_UPDATE="Update" COM_INSTALLER_MSG_UPDATESITES_DELETE_ERROR="An error has occurred while trying to delete "_QQ_"%s"_QQ_" update site: %s." COM_INSTALLER_MSG_UPDATESITES_DELETE_CANNOT_DELETE="%s update site cannot be deleted." +COM_INSTALLER_MSG_UPDATESITES_DELETE_CANNOT_EDIT="%s update site cannot be editted." COM_INSTALLER_MSG_UPDATESITES_N_DELETE_UPDATESITES_DELETED="%s update sites have been deleted." COM_INSTALLER_MSG_UPDATESITES_N_DELETE_UPDATESITES_DELETED_1="1 update site has been deleted." COM_INSTALLER_MSG_UPDATESITES_REBUILD_EXTENSION_PLUGIN_NOT_ENABLED="The Joomla Extension Plugin is disabled. This plugin must be enabled to rebuild the update sites." @@ -180,10 +183,13 @@ COM_INSTALLER_N_EXTENSIONS_PUBLISHED="%d extensions enabled." COM_INSTALLER_N_EXTENSIONS_PUBLISHED_1="%d extension enabled." COM_INSTALLER_N_EXTENSIONS_UNPUBLISHED="%d extensions disabled." COM_INSTALLER_N_EXTENSIONS_UNPUBLISHED_1="%d extension disabled." -COM_INSTALLER_N_UPDATESITES_PUBLISHED="%d update sites enabled." -COM_INSTALLER_N_UPDATESITES_PUBLISHED_1="%d update site enabled." -COM_INSTALLER_N_UPDATESITES_UNPUBLISHED="%d update sites disabled." -COM_INSTALLER_N_UPDATESITES_UNPUBLISHED_1="%d update site disabled." +COM_INSTALLER_N_ITEMS_CHECKED_IN_0="No update site checked in." +COM_INSTALLER_N_ITEMS_CHECKED_IN_1="%d update site checked in." +COM_INSTALLER_N_ITEMS_CHECKED_IN_MORE="%d update sites checked in." +COM_INSTALLER_N_ITEMS_PUBLISHED="%d update sites enabled." +COM_INSTALLER_N_ITEMS_PUBLISHED_1="%d update site enabled." +COM_INSTALLER_N_ITEMS_UNPUBLISHED="%d update sites disabled." +COM_INSTALLER_N_ITEMS_UNPUBLISHED_1="%d update site disabled." COM_INSTALLER_NEW_INSTALL="New Install" COM_INSTALLER_NEW_VERSION="Available" COM_INSTALLER_NO_INSTALL_TYPE_FOUND="No Install Type Found" @@ -248,6 +254,11 @@ COM_INSTALLER_UPDATESITE_DISABLE="Disable update site" COM_INSTALLER_UPDATESITE_DISABLED="Disabled update site" COM_INSTALLER_UPDATESITE_ENABLE="Enable update site" COM_INSTALLER_UPDATESITE_ENABLED="Enabled update site" +COM_INSTALLER_UPDATESITE_EDIT_DETAILS="Edit Update Site Location" +COM_INSTALLER_UPDATESITE_EDIT_TITLE="Edit Update Site" +COM_INSTALLER_UPDATESITE_EDIT_UPDATESITE_URL="Location URL" +COM_INSTALLER_UPDATESITE_EDIT_UPDATESITE_URL_DESC="Add your update site." +COM_INSTALLER_UPDATESITE_EDIT_VALID_NAME="Insert a valid URL" COM_INSTALLER_UPDATESITES_FILTER_SEARCH_DESC="Search in extension name. Prefix with ID: to search for an update site ID." COM_INSTALLER_UPDATESITES_FILTER_SEARCH_LABEL="Search Update Sites" COM_INSTALLER_UPLOAD_AND_INSTALL="Upload & Install" diff --git a/installation/sql/mysql/joomla.sql b/installation/sql/mysql/joomla.sql index e3d4baff92b60..8e2af455ba319 100644 --- a/installation/sql/mysql/joomla.sql +++ b/installation/sql/mysql/joomla.sql @@ -1899,6 +1899,8 @@ CREATE TABLE IF NOT EXISTS `#__update_sites` ( `enabled` int(11) DEFAULT 0, `last_check_timestamp` bigint(20) DEFAULT 0, `extra_query` varchar(1000) DEFAULT '', + `checked_out` int(10) unsigned NOT NULL DEFAULT 0, + `checked_out_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY (`update_site_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci COMMENT='Update Sites'; diff --git a/installation/sql/postgresql/joomla.sql b/installation/sql/postgresql/joomla.sql index 83c8690adeba3..a435f18666430 100644 --- a/installation/sql/postgresql/joomla.sql +++ b/installation/sql/postgresql/joomla.sql @@ -2045,6 +2045,8 @@ CREATE TABLE IF NOT EXISTS "#__update_sites" ( "enabled" bigint DEFAULT 0, "last_check_timestamp" bigint DEFAULT 0, "extra_query" varchar(1000) DEFAULT '', + "checked_out" int(10) unsigned NOT NULL DEFAULT 0, + "checked_out_time" timestamp without time zone DEFAULT '1970-01-01 00:00:00' NOT NULL, PRIMARY KEY ("update_site_id") );