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 @@