mirror of
https://github.com/joomla-extensions/patchtester.git
synced 2024-12-22 10:58:58 +00:00
Clean up controllers and models
Signed-off-by: Roland Dalmulder <contact@rolandd.com>
This commit is contained in:
parent
63510fe4f9
commit
96b8480c51
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<form addfieldprefix="PatchTester\Field">
|
||||
<form addfieldprefix="Joomla\Component\Patchtester\Administrator\Field">
|
||||
<fields name="filter">
|
||||
<field
|
||||
name="search"
|
||||
@ -87,10 +87,10 @@
|
||||
type="list"
|
||||
onchange="this.form.submit();"
|
||||
>
|
||||
<option value="a.title ASC">JGLOBAL_TITLE_ASC</option>
|
||||
<option value="a.title DESC">JGLOBAL_TITLE_DESC</option>
|
||||
<option value="a.pull_id ASC">COM_PATCHTESTER_PULL_ID_ASC</option>
|
||||
<option value="a.pull_id DESC">COM_PATCHTESTER_PULL_ID_DESC</option>
|
||||
<option value="pulls.title ASC">JGLOBAL_TITLE_ASC</option>
|
||||
<option value="pulls.title DESC">JGLOBAL_TITLE_DESC</option>
|
||||
<option value="pulls.pull_id ASC">COM_PATCHTESTER_PULL_ID_ASC</option>
|
||||
<option value="pulls.pull_id DESC">COM_PATCHTESTER_PULL_ID_DESC</option>
|
||||
</field>
|
||||
|
||||
<field
|
||||
|
@ -1,104 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Patch testing component for the Joomla! CMS
|
||||
*
|
||||
* @copyright Copyright (C) 2011 - 2012 Ian MacLennan, Copyright (C) 2013 - 2018 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Patchtester\Administrator\Controller;
|
||||
|
||||
use Joomla\CMS\Application\CMSApplication;
|
||||
use Joomla\CMS\Component\ComponentHelper;
|
||||
use Joomla\CMS\MVC\Controller\BaseController;
|
||||
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
|
||||
use Joomla\Component\Patchtester\Administrator\Model\AbstractModel;
|
||||
use Joomla\Input\Input;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
/**
|
||||
* Base controller for the patch testing component
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
abstract class AbstractController extends BaseController
|
||||
{
|
||||
/**
|
||||
* The active application
|
||||
*
|
||||
* @var CMSApplication
|
||||
* @since 4.0.0
|
||||
*/
|
||||
protected $app;
|
||||
/**
|
||||
* The object context
|
||||
*
|
||||
* @var string
|
||||
* @since 2.0
|
||||
*/
|
||||
protected $context;
|
||||
/**
|
||||
* The default view to display
|
||||
*
|
||||
* @var string
|
||||
* @since 2.0
|
||||
*/
|
||||
protected $defaultView = 'pulls';
|
||||
/**
|
||||
* Instantiate the controller
|
||||
*
|
||||
* @param CMSApplication $app The application object.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public function __construct($config = array(), MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null)
|
||||
{
|
||||
$this->app = $app;
|
||||
// Set the context for the controller
|
||||
$this->context = 'com_patchtester.' . $this->getInput()->getCmd('view', $this->defaultView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the application object.
|
||||
*
|
||||
* @return CMSApplication
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function getApplication()
|
||||
{
|
||||
return $this->app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the input object.
|
||||
*
|
||||
* @return Input
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function getInput()
|
||||
{
|
||||
return $this->app->input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state for the model object
|
||||
*
|
||||
* @param AbstractModel $model Model object
|
||||
*
|
||||
* @return Registry
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
protected function initializeState($model)
|
||||
{
|
||||
$state = new Registry();
|
||||
// Load the parameters.
|
||||
$params = ComponentHelper::getParams('com_patchtester');
|
||||
$state->set('github_user', $params->get('org', 'joomla'));
|
||||
$state->set('github_repo', $params->get('repo', 'joomla-cms'));
|
||||
return $state;
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ namespace Joomla\Component\Patchtester\Administrator\Controller;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Controller\BaseController;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\Component\Patchtester\Administrator\Model\PullModel;
|
||||
|
||||
@ -24,7 +25,7 @@ use Joomla\Component\Patchtester\Administrator\Model\PullModel;
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class ApplyController extends AbstractController
|
||||
class ApplyController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Execute the controller.
|
||||
@ -33,16 +34,15 @@ class ApplyController extends AbstractController
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public function execute()
|
||||
public function execute($task): void
|
||||
{
|
||||
try {
|
||||
$model = new PullModel(null, Factory::getDbo());
|
||||
// Initialize the state for the model
|
||||
$model->setState($this->initializeState($model));
|
||||
if ($model->apply($this->getInput()->getUint('pull_id'))) {
|
||||
/** @var PullModel $model */
|
||||
$model = Factory::getApplication()->bootComponent('com_patchtester')->getMVCFactory()->createModel('Pull', 'Administrator', ['ignore_request' => true]);
|
||||
$msg = Text::_('COM_PATCHTESTER_NO_FILES_TO_PATCH');
|
||||
|
||||
if ($model->apply($this->input->getUint('pull_id'))) {
|
||||
$msg = Text::_('COM_PATCHTESTER_APPLY_OK');
|
||||
} else {
|
||||
$msg = Text::_('COM_PATCHTESTER_NO_FILES_TO_PATCH');
|
||||
}
|
||||
|
||||
$type = 'message';
|
||||
@ -51,7 +51,7 @@ class ApplyController extends AbstractController
|
||||
$type = 'error';
|
||||
}
|
||||
|
||||
$this->getApplication()->enqueueMessage($msg, $type);
|
||||
$this->getApplication()->redirect(Route::_('index.php?option=com_patchtester', false));
|
||||
$this->app->enqueueMessage($msg, $type);
|
||||
$this->app->redirect(Route::_('index.php?option=com_patchtester', false));
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,7 @@
|
||||
|
||||
namespace Joomla\Component\Patchtester\Administrator\Controller;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Controller\BaseController;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
@ -33,111 +30,4 @@ class DisplayController extends BaseController
|
||||
* @since 1.6
|
||||
*/
|
||||
protected $default_view = 'pulls';
|
||||
|
||||
/**
|
||||
* Default ordering value
|
||||
*
|
||||
* @var string
|
||||
* @since 4.0.0
|
||||
*/
|
||||
protected $defaultFullOrdering = 'a.pull_id DESC';
|
||||
/**
|
||||
* Execute the controller.
|
||||
*
|
||||
* @return boolean True on success
|
||||
*
|
||||
* @since 2.0
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function executeOld()
|
||||
{
|
||||
// Set up variables to build our classes
|
||||
$view = $this->getInput()->getCmd('view', $this->defaultView);
|
||||
$format = $this->getInput()->getCmd('format', 'html');
|
||||
// Register the layout paths for the view
|
||||
$paths = new \SplPriorityQueue();
|
||||
// Add the path for template overrides
|
||||
$paths->insert(JPATH_THEMES . '/' . $this->getApplication()->getTemplate() . '/html/com_patchtester/' . $view, 2);
|
||||
// Add the path for the default layouts
|
||||
$paths->insert(dirname(__DIR__) . '/View/' . ucfirst($view) . '/tmpl', 1);
|
||||
// Build the class names for the model and view
|
||||
$viewClass = '\\PatchTester\\View\\' . ucfirst($view) . '\\' . ucfirst($view) . ucfirst($format) . 'View';
|
||||
$modelClass = '\\PatchTester\\Model\\' . ucfirst($view) . 'Model';
|
||||
// Sanity check - Ensure our classes exist
|
||||
if (!class_exists($viewClass)) {
|
||||
// Try to use a default view
|
||||
$viewClass = '\\PatchTester\\View\\Default' . ucfirst($format) . 'View';
|
||||
if (!class_exists($viewClass)) {
|
||||
throw new \RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_VIEW_NOT_FOUND', $view, $format), 500);
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists($modelClass)) {
|
||||
throw new \RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_MODEL_NOT_FOUND', $modelClass), 500);
|
||||
}
|
||||
|
||||
// Initialize the model class now; need to do it before setting the state to get required data from it
|
||||
$model = new $modelClass($this->context, null, Factory::getDbo());
|
||||
// Initialize the state for the model
|
||||
$state = $this->initializeState($model);
|
||||
foreach ($state as $key => $value) {
|
||||
$model->setState($key, $value);
|
||||
}
|
||||
|
||||
// Initialize the view class now
|
||||
$view = new $viewClass($model, $paths);
|
||||
// Echo the rendered view for the application
|
||||
echo $view->render();
|
||||
// Finished!
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state for the model object
|
||||
*
|
||||
* @param AbstractModel $model Model object
|
||||
*
|
||||
* @return Registry
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
protected function initializeState($model)
|
||||
{
|
||||
$state = parent::initializeState($model);
|
||||
$app = $this->getApplication();
|
||||
// Load the filter state.
|
||||
$state->set('filter.search', $app->getUserStateFromRequest($this->context . '.filter.search', 'filter_search', ''));
|
||||
$state->set('filter.applied', $app->getUserStateFromRequest($this->context . '.filter.applied', 'filter_applied', ''));
|
||||
$state->set('filter.branch', $app->getUserStateFromRequest($this->context . '.filter.branch', 'filter_branch', ''));
|
||||
$state->set('filter.rtc', $app->getUserStateFromRequest($this->context . '.filter.rtc', 'filter_rtc', ''));
|
||||
$state->set('filter.npm', $app->getUserStateFromRequest($this->context . '.filter.npm', 'filter_npm', ''));
|
||||
$state->set('filter.label', $app->getUserStateFromRequest($this->context . '.filter.label', 'filter_label', ''));
|
||||
// Pre-fill the limits.
|
||||
$limit = $app->getUserStateFromRequest('global.list.limit', 'limit', $app->input->get('list_limit', 20), 'uint');
|
||||
$state->set('list.limit', $limit);
|
||||
$fullOrdering = $app->getUserStateFromRequest($this->context . '.fullorder', 'list_fullordering', $this->defaultFullOrdering);
|
||||
$orderingParts = explode(' ', $fullOrdering);
|
||||
if (count($orderingParts) !== 2) {
|
||||
$fullOrdering = $this->defaultFullOrdering;
|
||||
$orderingParts = explode(' ', $fullOrdering);
|
||||
}
|
||||
|
||||
$state->set('list.fullordering', $fullOrdering);
|
||||
// The 2nd part will be considered the direction
|
||||
$direction = $orderingParts[array_key_last($orderingParts)];
|
||||
if (in_array(strtoupper($direction), ['ASC', 'DESC', ''])) {
|
||||
$state->set('list.direction', $direction);
|
||||
}
|
||||
|
||||
// The 1st part will be the ordering
|
||||
$ordering = $orderingParts[array_key_first($orderingParts)];
|
||||
if (in_array($ordering, $model->getSortFields())) {
|
||||
$state->set('list.ordering', $ordering);
|
||||
}
|
||||
|
||||
$value = $app->getUserStateFromRequest($this->context . '.limitstart', 'limitstart', 0);
|
||||
$limitstart = ($limit != 0 ? (floor($value / $limit) * $limit) : 0);
|
||||
$state->set('list.start', $limitstart);
|
||||
return $state;
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,10 @@
|
||||
|
||||
namespace Joomla\Component\Patchtester\Administrator\Controller;
|
||||
|
||||
use Exception;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Controller\BaseController;
|
||||
use Joomla\CMS\Response\JsonResponse;
|
||||
use Joomla\Component\Patchtester\Administrator\Model\PullsModel;
|
||||
|
||||
@ -23,48 +25,42 @@ use Joomla\Component\Patchtester\Administrator\Model\PullsModel;
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class FetchController extends AbstractController
|
||||
class FetchController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Execute the controller.
|
||||
*
|
||||
* @return void Redirects the application
|
||||
*
|
||||
* @throws Exception
|
||||
* @since 2.0
|
||||
*/
|
||||
public function execute()
|
||||
public function execute($task)
|
||||
{
|
||||
// We don't want this request to be cached.
|
||||
$this->getApplication()->setHeader('Expires', 'Mon, 1 Jan 2001 00:00:00 GMT', true);
|
||||
$this->getApplication()->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true);
|
||||
$this->getApplication()->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', false);
|
||||
$this->getApplication()->setHeader('Pragma', 'no-cache');
|
||||
$this->getApplication()->setHeader('Content-Type', $this->getApplication()->mimeType . '; charset=' . $this->getApplication()->charSet);
|
||||
$session = Factory::getSession();
|
||||
$this->app->setHeader('Expires', 'Mon, 1 Jan 2001 00:00:00 GMT', true);
|
||||
$this->app->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true);
|
||||
$this->app->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', false);
|
||||
$this->app->setHeader('Pragma', 'no-cache');
|
||||
$this->app->setHeader('Content-Type', $this->app->mimeType . '; charset=' . $this->app->charSet);
|
||||
$session = Factory::getApplication()->getSession();
|
||||
|
||||
try {
|
||||
// Fetch our page from the session
|
||||
$page = $session->get('com_patchtester_fetcher_page', 1);
|
||||
$model = new PullsModel();
|
||||
// Initialize the state for the model
|
||||
$state = $this->initializeState($model);
|
||||
foreach ($state as $key => $value) {
|
||||
$model->setState($key, $value);
|
||||
}
|
||||
/** @var PullsModel $model */
|
||||
$model = $this->app->bootComponent('com_patchtester')->getMVCFactory()->createModel('Pulls', 'Administrator', ['ignore_request' => true]);
|
||||
|
||||
$status = $model->requestFromGithub($page);
|
||||
} catch (\Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
$response = new JsonResponse($e);
|
||||
$this->getApplication()->sendHeaders();
|
||||
$this->app->sendHeaders();
|
||||
echo json_encode($response);
|
||||
$this->getApplication()->close(1);
|
||||
$this->app->close(1);
|
||||
}
|
||||
|
||||
// Store the last page to the session if given one
|
||||
if (isset($status['lastPage']) && $status['lastPage'] !== false) {
|
||||
$session->set('com_patchtester_fetcher_last_page', $status['lastPage']);
|
||||
}
|
||||
|
||||
// Update the UI and session now
|
||||
if ($status['complete'] || $page === $session->get('com_patchtester_fetcher_last_page', false)) {
|
||||
$status['complete'] = true;
|
||||
$status['header'] = Text::_('COM_PATCHTESTER_FETCH_SUCCESSFUL', true);
|
||||
@ -83,8 +79,8 @@ class FetchController extends AbstractController
|
||||
}
|
||||
|
||||
$response = new JsonResponse($status, $message, false, true);
|
||||
$this->getApplication()->sendHeaders();
|
||||
$this->app->sendHeaders();
|
||||
echo json_encode($response);
|
||||
$this->getApplication()->close();
|
||||
$this->app->close();
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ use Joomla\CMS\Component\ComponentHelper;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Controller\BaseController;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\Component\Patchtester\Administrator\Model\PullModel;
|
||||
use Joomla\Component\Patchtester\Administrator\Model\PullsModel;
|
||||
@ -28,7 +29,7 @@ use Joomla\Filesystem\File;
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class ResetController extends AbstractController
|
||||
class ResetController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Execute the controller.
|
||||
@ -37,20 +38,25 @@ class ResetController extends AbstractController
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public function execute(): void
|
||||
public function execute($task): void
|
||||
{
|
||||
try {
|
||||
$hasErrors = false;
|
||||
$revertErrored = false;
|
||||
$pullModel = new PullModel(null, Factory::getDbo());
|
||||
$pullsModel = new PullsModel($this->context, null, Factory::getDbo());
|
||||
$testsModel = new TestsModel(null, Factory::getDbo());
|
||||
// Check the applied patches in the database first
|
||||
$mvcFactory = Factory::getApplication()->bootComponent('com_patchtester')->getMVCFactory();
|
||||
/** @var PullModel $pullModel */
|
||||
$pullModel = $mvcFactory->createModel('Pull', 'Administrator', ['ignore_request' => true]);
|
||||
/** @var PullsModel $pullModel */
|
||||
$pullsModel = $mvcFactory->createModel('Pulls', 'Administrator', ['ignore_request' => true]);
|
||||
/** @var TestsModel $pullModel */
|
||||
$testsModel = $mvcFactory->createModel('Tests', 'Administrator', ['ignore_request' => true]);
|
||||
|
||||
// Check the applied patches in the database first
|
||||
$appliedPatches = $testsModel->getAppliedPatches();
|
||||
$params = ComponentHelper::getParams('com_patchtester');
|
||||
// Decide based on repository settings whether patch will be applied through Github or CIServer
|
||||
// Decide based on repository settings whether patch will be applied through Github or CIServer
|
||||
if ((bool) $params->get('ci_switch', 1)) {
|
||||
// Let's try to cleanly revert all applied patches with ci
|
||||
// Let's try to cleanly revert all applied patches with ci
|
||||
foreach ($appliedPatches as $patch) {
|
||||
try {
|
||||
$pullModel->revertWithCIServer($patch->id);
|
||||
@ -76,7 +82,7 @@ class ResetController extends AbstractController
|
||||
} catch (\RuntimeException $e) {
|
||||
$hasErrors = true;
|
||||
|
||||
$this->getApplication()->enqueueMessage(
|
||||
$this->app->enqueueMessage(
|
||||
Text::sprintf('COM_PATCHTESTER_ERROR_TRUNCATING_PULLS_TABLE', $e->getMessage()),
|
||||
'error'
|
||||
);
|
||||
@ -89,7 +95,7 @@ class ResetController extends AbstractController
|
||||
} catch (\RuntimeException $e) {
|
||||
$hasErrors = true;
|
||||
|
||||
$this->getApplication()->enqueueMessage(
|
||||
$this->app->enqueueMessage(
|
||||
Text::sprintf('COM_PATCHTESTER_ERROR_TRUNCATING_TESTS_TABLE', $e->getMessage()),
|
||||
'error'
|
||||
);
|
||||
@ -101,7 +107,7 @@ class ResetController extends AbstractController
|
||||
if (count($backups)) {
|
||||
foreach ($backups as $file) {
|
||||
if (!File::delete(JPATH_COMPONENT . '/backups/' . $file)) {
|
||||
$this->getApplication()->enqueueMessage(
|
||||
$this->app->enqueueMessage(
|
||||
Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE', JPATH_COMPONENT . '/backups/' . $file),
|
||||
'error'
|
||||
);
|
||||
@ -115,7 +121,7 @@ class ResetController extends AbstractController
|
||||
$msg = Text::sprintf(
|
||||
'COM_PATCHTESTER_RESET_HAS_ERRORS',
|
||||
JPATH_COMPONENT . '/backups',
|
||||
Factory::getDbo()->replacePrefix('#__patchtester_tests')
|
||||
Factory::getApplication()->get('DatabaseDriver')->replacePrefix('#__patchtester_tests')
|
||||
);
|
||||
$type = 'warning';
|
||||
} else {
|
||||
@ -127,7 +133,7 @@ class ResetController extends AbstractController
|
||||
$type = 'error';
|
||||
}
|
||||
|
||||
$this->getApplication()->enqueueMessage($msg, $type);
|
||||
$this->getApplication()->redirect(Route::_('index.php?option=com_patchtester', false));
|
||||
$this->app->enqueueMessage($msg, $type);
|
||||
$this->app->redirect(Route::_('index.php?option=com_patchtester', false));
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
namespace Joomla\Component\Patchtester\Administrator\Controller;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Controller\BaseController;
|
||||
use Joomla\CMS\Router\Route;
|
||||
use Joomla\Component\Patchtester\Administrator\Model\PullModel;
|
||||
|
||||
@ -23,7 +23,7 @@ use Joomla\Component\Patchtester\Administrator\Model\PullModel;
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class RevertController extends AbstractController
|
||||
class RevertController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Execute the controller.
|
||||
@ -32,13 +32,12 @@ class RevertController extends AbstractController
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public function execute()
|
||||
public function execute($task)
|
||||
{
|
||||
try {
|
||||
$model = new PullModel(null, Factory::getDbo());
|
||||
// Initialize the state for the model
|
||||
$model->setState($this->initializeState($model));
|
||||
$model->revert($this->getInput()->getUint('pull_id'));
|
||||
/** @var PullModel $model */
|
||||
$model = $this->app->bootComponent('com_patchtester')->getMVCFactory()->createModel('Pull', 'Administrator', ['ignore_request' => true]);
|
||||
$model->revert($this->input->getUint('pull_id'));
|
||||
$msg = Text::_('COM_PATCHTESTER_REVERT_OK');
|
||||
$type = 'message';
|
||||
} catch (\Exception $e) {
|
||||
@ -46,7 +45,7 @@ class RevertController extends AbstractController
|
||||
$type = 'error';
|
||||
}
|
||||
|
||||
$this->getApplication()->enqueueMessage($msg, $type);
|
||||
$this->getApplication()->redirect(Route::_('index.php?option=com_patchtester', false));
|
||||
$this->app->enqueueMessage($msg, $type);
|
||||
$this->app->redirect(Route::_('index.php?option=com_patchtester', false));
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,10 @@ namespace Joomla\Component\Patchtester\Administrator\Controller;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Controller\BaseController;
|
||||
use Joomla\CMS\Response\JsonResponse;
|
||||
use Joomla\CMS\Session\Session;
|
||||
use Joomla\Component\Patchtester\Administrator\Helper\Helper;
|
||||
use Joomla\Component\Patchtester\Administrator\Model\TestsModel;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
@ -25,7 +25,7 @@ use Joomla\Component\Patchtester\Administrator\Model\TestsModel;
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class StartfetchController extends AbstractController
|
||||
class StartfetchController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Execute the controller.
|
||||
@ -34,20 +34,19 @@ class StartfetchController extends AbstractController
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public function execute()
|
||||
public function execute($task): void
|
||||
{
|
||||
// We don't want this request to be cached.
|
||||
$this->getApplication()->setHeader('Expires', 'Mon, 1 Jan 2001 00:00:00 GMT', true);
|
||||
$this->getApplication()->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true);
|
||||
$this->getApplication()->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', false);
|
||||
$this->getApplication()->setHeader('Pragma', 'no-cache');
|
||||
$this->getApplication()->setHeader('Content-Type', $this->getApplication()->mimeType . '; charset=' . $this->getApplication()->charSet);
|
||||
// Check for a valid token. If invalid, send a 403 with the error message.
|
||||
$this->app->setHeader('Expires', 'Mon, 1 Jan 2001 00:00:00 GMT', true);
|
||||
$this->app->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true);
|
||||
$this->app->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', false);
|
||||
$this->app->setHeader('Pragma', 'no-cache');
|
||||
$this->app->setHeader('Content-Type', $this->app->mimeType . '; charset=' . $this->app->charSet);
|
||||
|
||||
if (!Session::checkToken('request')) {
|
||||
$response = new JsonResponse(new \Exception(Text::_('JINVALID_TOKEN'), 403));
|
||||
$this->getApplication()->sendHeaders();
|
||||
$this->app->sendHeaders();
|
||||
echo json_encode($response);
|
||||
$this->getApplication()->close(1);
|
||||
$this->app->close(1);
|
||||
}
|
||||
|
||||
// Make sure we can fetch the data from GitHub - throw an error on < 10 available requests
|
||||
@ -56,40 +55,48 @@ class StartfetchController extends AbstractController
|
||||
$rate = json_decode($rateResponse->body);
|
||||
} catch (\Exception $e) {
|
||||
$response = new JsonResponse(new \Exception(Text::sprintf('COM_PATCHTESTER_COULD_NOT_CONNECT_TO_GITHUB', $e->getMessage()), $e->getCode(), $e));
|
||||
$this->getApplication()->sendHeaders();
|
||||
$this->app->sendHeaders();
|
||||
echo json_encode($response);
|
||||
$this->getApplication()->close(1);
|
||||
$this->app->close(1);
|
||||
}
|
||||
|
||||
// If over the API limit, we can't build this list
|
||||
if ($rate->resources->core->remaining < 10) {
|
||||
$response = new JsonResponse(new \Exception(Text::sprintf('COM_PATCHTESTER_API_LIMIT_LIST', Factory::getDate($rate->resources->core->reset)), 429));
|
||||
$this->getApplication()->sendHeaders();
|
||||
$this->app->sendHeaders();
|
||||
echo json_encode($response);
|
||||
$this->getApplication()->close(1);
|
||||
$this->app->close(1);
|
||||
}
|
||||
|
||||
$testsModel = new TestsModel(null, Factory::getDbo());
|
||||
$testsModel = Factory::getApplication()->bootComponent('com_patchtester')->getMVCFactory()->createModel('Tests', 'Administrator', ['ignore_request' => true]);
|
||||
try {
|
||||
// Sanity check, ensure there aren't any applied patches
|
||||
if (count($testsModel->getAppliedPatches()) >= 1) {
|
||||
$response = new JsonResponse(new \Exception(Text::_('COM_PATCHTESTER_ERROR_APPLIED_PATCHES'), 500));
|
||||
$this->getApplication()->sendHeaders();
|
||||
$this->app->sendHeaders();
|
||||
echo json_encode($response);
|
||||
$this->getApplication()->close(1);
|
||||
$this->app->close(1);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$response = new JsonResponse($e);
|
||||
$this->getApplication()->sendHeaders();
|
||||
$this->app->sendHeaders();
|
||||
echo json_encode($response);
|
||||
$this->getApplication()->close(1);
|
||||
$this->app->close(1);
|
||||
}
|
||||
|
||||
// We're able to successfully pull data, prepare our environment
|
||||
Factory::getSession()->set('com_patchtester_fetcher_page', 1);
|
||||
$response = new JsonResponse(array('complete' => false, 'header' => Text::_('COM_PATCHTESTER_FETCH_PROCESSING', true)), Text::sprintf('COM_PATCHTESTER_FETCH_PAGE_NUMBER', 1), false, true);
|
||||
$this->getApplication()->sendHeaders();
|
||||
Factory::getApplication()->getSession()->set('com_patchtester_fetcher_page', 1);
|
||||
$response = new JsonResponse(
|
||||
[
|
||||
'complete' => false,
|
||||
'header' => Text::_('COM_PATCHTESTER_FETCH_PROCESSING', true)
|
||||
],
|
||||
Text::sprintf('COM_PATCHTESTER_FETCH_PAGE_NUMBER', 1),
|
||||
false,
|
||||
true
|
||||
);
|
||||
$this->app->sendHeaders();
|
||||
echo json_encode($response);
|
||||
$this->getApplication()->close();
|
||||
$this->app->close();
|
||||
}
|
||||
}
|
||||
|
@ -7,16 +7,18 @@
|
||||
* @license GNU General Public License version 2 or later
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Patchtester\Administrator\Github\Exception;
|
||||
namespace Joomla\Component\Patchtester\Administrator\Exception;
|
||||
|
||||
use Joomla\CMS\Http\Response;
|
||||
use DomainException;
|
||||
use Exception;
|
||||
use Joomla\Http\Response;
|
||||
|
||||
/**
|
||||
* Exception representing an unexpected response
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class UnexpectedResponse extends \DomainException
|
||||
class UnexpectedResponse extends DomainException
|
||||
{
|
||||
/**
|
||||
* The Response object.
|
||||
@ -25,18 +27,23 @@ class UnexpectedResponse extends \DomainException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private $response;
|
||||
/**
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Response $response The Response object.
|
||||
* @param string $message The Exception message to throw.
|
||||
* @param integer $code The Exception code.
|
||||
* @param \Exception $previous The previous exception used for the exception chaining.
|
||||
* @param Response $response The Response object.
|
||||
* @param string $message The Exception message to throw.
|
||||
* @param int $code The Exception code.
|
||||
* @param Exception|null $previous The previous exception used for the exception chaining.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function __construct(Response $response, $message = '', $code = 0, \Exception $previous = null)
|
||||
{
|
||||
public function __construct(
|
||||
Response $response,
|
||||
$message = '',
|
||||
$code = 0,
|
||||
Exception $previous = null
|
||||
) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->response = $response;
|
||||
}
|
||||
@ -48,7 +55,7 @@ class UnexpectedResponse extends \DomainException
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function getResponse()
|
||||
public function getResponse(): Response
|
||||
{
|
||||
return $this->response;
|
||||
}
|
@ -31,7 +31,8 @@ class BranchField extends ListField
|
||||
* @since 4.1.0
|
||||
*/
|
||||
protected $type = 'Branch';
|
||||
/**
|
||||
|
||||
/**
|
||||
* Build a list of available branches.
|
||||
*
|
||||
* @return array List of options
|
||||
@ -40,14 +41,19 @@ class BranchField extends ListField
|
||||
*/
|
||||
public function getOptions(): array
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
$query = $db->getQuery(true);
|
||||
$query->select('DISTINCT(' . $db->quoteName('branch') . ') AS ' . $db->quoteName('text'))
|
||||
$query->select(
|
||||
'DISTINCT(' . $db->quoteName('branch') . ') AS ' . $db->quoteName(
|
||||
'text'
|
||||
)
|
||||
)
|
||||
->select($db->quoteName('branch', 'value'))
|
||||
->from('#__patchtester_pulls')
|
||||
->where($db->quoteName('branch') . ' != ' . $db->quote(''))
|
||||
->order($db->quoteName('branch') . ' ASC');
|
||||
$options = $db->setQuery($query)->loadAssocList();
|
||||
|
||||
return array_merge(parent::getOptions(), $options);
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,8 @@ class LabelField extends ListField
|
||||
* @since 4.1.0
|
||||
*/
|
||||
protected $type = 'Label';
|
||||
/**
|
||||
|
||||
/**
|
||||
* Build a list of available fields.
|
||||
*
|
||||
* @return array List of options
|
||||
@ -40,13 +41,18 @@ class LabelField extends ListField
|
||||
*/
|
||||
public function getOptions(): array
|
||||
{
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
$db = Factory::getContainer()->get('DatabaseDriver');
|
||||
$query = $db->getQuery(true);
|
||||
$query->select('DISTINCT(' . $db->quoteName('name') . ') AS ' . $db->quoteName('text'))
|
||||
$query->select(
|
||||
'DISTINCT(' . $db->quoteName('name') . ') AS ' . $db->quoteName(
|
||||
'text'
|
||||
)
|
||||
)
|
||||
->select($db->quoteName('name', 'value'))
|
||||
->from($db->quoteName('#__patchtester_pulls_labels'))
|
||||
->order($db->quoteName('name') . ' ASC');
|
||||
$options = $db->setQuery($query)->loadAssocList();
|
||||
|
||||
return array_merge(parent::getOptions(), $options);
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,9 @@ namespace Joomla\Component\Patchtester\Administrator\GitHub;
|
||||
|
||||
use Joomla\CMS\Http\Http;
|
||||
use Joomla\CMS\Http\HttpFactory;
|
||||
use Joomla\CMS\Http\Response;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
use Joomla\Component\Patchtester\Administrator\Github\Exception\UnexpectedResponse;
|
||||
use Joomla\Component\Patchtester\Administrator\Exception\UnexpectedResponse;
|
||||
use Joomla\Http\Response;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
/**
|
||||
@ -30,18 +30,19 @@ class GitHub
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected $options;
|
||||
/**
|
||||
/**
|
||||
* The HTTP client object to use in sending HTTP requests.
|
||||
*
|
||||
* @var Http
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected $client;
|
||||
/**
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Registry $options Connector options.
|
||||
* @param Http $client The HTTP client object.
|
||||
* @param Registry|null $options Connector options.
|
||||
* @param Http|null $client The HTTP client object.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@ -58,7 +59,7 @@ class GitHub
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function getClient()
|
||||
public function getClient(): Http
|
||||
{
|
||||
return $this->client;
|
||||
}
|
||||
@ -66,22 +67,27 @@ class GitHub
|
||||
/**
|
||||
* Get the diff for a pull request.
|
||||
*
|
||||
* @param string $user The name of the owner of the GitHub repository.
|
||||
* @param string $repo The name of the GitHub repository.
|
||||
* @param integer $pullId The pull request number.
|
||||
* @param string $user The name of the owner of the GitHub repository.
|
||||
* @param string $repo The name of the GitHub repository.
|
||||
* @param int $pullId The pull request number.
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function getDiffForPullRequest($user, $repo, $pullId)
|
||||
{
|
||||
// Build the request path.
|
||||
$path = "/repos/$user/$repo/pulls/" . (int) $pullId;
|
||||
// Build the request headers.
|
||||
$headers = array('Accept' => 'application/vnd.github.diff');
|
||||
public function getDiffForPullRequest(
|
||||
string $user,
|
||||
string $repo,
|
||||
int $pullId
|
||||
): Response {
|
||||
$path = "/repos/$user/$repo/pulls/" . $pullId;
|
||||
|
||||
$headers = ['Accept' => 'application/vnd.github.diff'];
|
||||
$prepared = $this->prepareRequest($path, 0, 0, $headers);
|
||||
return $this->processResponse($this->client->get($prepared['url'], $prepared['headers']));
|
||||
|
||||
return $this->processResponse(
|
||||
$this->client->get($prepared['url'], $prepared['headers'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,27 +95,28 @@ class GitHub
|
||||
*
|
||||
* This method will add appropriate pagination details if necessary and also prepend the API url to have a complete URL for the request.
|
||||
*
|
||||
* @param string $path Path to process
|
||||
* @param integer $page Page to request
|
||||
* @param integer $limit Number of results to return per page
|
||||
* @param array $headers The headers to send with the request
|
||||
* @param string $path Path to process
|
||||
* @param int $page Page to request
|
||||
* @param int $limit Number of results to return per page
|
||||
* @param array $headers The headers to send with the request
|
||||
*
|
||||
* @return array Associative array containing the prepared URL and request headers
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function prepareRequest(
|
||||
$path,
|
||||
$page = 0,
|
||||
$limit = 0,
|
||||
array $headers = array()
|
||||
) {
|
||||
string $path,
|
||||
int $page = 0,
|
||||
int $limit = 0,
|
||||
array $headers = []
|
||||
): array {
|
||||
$url = $this->fetchUrl($path, $page, $limit);
|
||||
|
||||
if ($token = $this->options->get('gh.token', false)) {
|
||||
$headers['Authorization'] = "token $token";
|
||||
}
|
||||
|
||||
return array('url' => $url, 'headers' => $headers);
|
||||
return ['url' => $url, 'headers' => $headers];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,9 +125,9 @@ class GitHub
|
||||
* This method will add appropriate pagination details and basic authentication credentials if necessary
|
||||
* and also prepend the API url to have a complete URL for the request.
|
||||
*
|
||||
* @param string $path URL to inflect
|
||||
* @param integer $page Page to request
|
||||
* @param integer $limit Number of results to return per page
|
||||
* @param string $path URL to inflect
|
||||
* @param int $page Page to request
|
||||
* @param int $limit Number of results to return per page
|
||||
*
|
||||
* @return string The request URL.
|
||||
*
|
||||
@ -130,38 +137,39 @@ class GitHub
|
||||
{
|
||||
// Get a new Uri object using the API URL and given path.
|
||||
$uri = new Uri($this->options->get('api.url') . $path);
|
||||
// If we have a defined page number add it to the JUri object.
|
||||
// If we have a defined page number add it to the JUri object.
|
||||
if ($page > 0) {
|
||||
$uri->setVar('page', (int) $page);
|
||||
$uri->setVar('page', (int)$page);
|
||||
}
|
||||
|
||||
// If we have a defined items per page add it to the JUri object.
|
||||
if ($limit > 0) {
|
||||
$uri->setVar('per_page', (int) $limit);
|
||||
$uri->setVar('per_page', (int)$limit);
|
||||
}
|
||||
|
||||
return (string) $uri;
|
||||
return (string)$uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the response and return it.
|
||||
*
|
||||
* @param Response $response The response.
|
||||
* @param integer $expectedCode The expected response code.
|
||||
* @param int $expectedCode The expected response code.
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws UnexpectedResponse
|
||||
*@since 3.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function processResponse(Response $response, $expectedCode = 200)
|
||||
{
|
||||
protected function processResponse(
|
||||
Response $response,
|
||||
int $expectedCode = 200
|
||||
): Response {
|
||||
// Validate the response code.
|
||||
if ($response->code != $expectedCode) {
|
||||
// Decode the error response and throw an exception.
|
||||
// Decode the error response and throw an exception.
|
||||
$body = json_decode($response->body);
|
||||
$error = isset($body->error) ? $body->error
|
||||
: (isset($body->message) ? $body->message : 'Unknown Error');
|
||||
$error = $body->error ?? ($body->message ?? 'Unknown Error');
|
||||
|
||||
throw new UnexpectedResponse(
|
||||
$response,
|
||||
@ -187,15 +195,17 @@ class GitHub
|
||||
*/
|
||||
public function getFileContents($user, $repo, $path, $ref = null)
|
||||
{
|
||||
$path = "/repos/$user/$repo/contents/$path";
|
||||
$path = "/repos/$user/$repo/contents/$path";
|
||||
$prepared = $this->prepareRequest($path);
|
||||
if ($ref) {
|
||||
$url = new Uri($prepared['url']);
|
||||
$url->setVar('ref', $ref);
|
||||
$prepared['url'] = (string) $url;
|
||||
$prepared['url'] = (string)$url;
|
||||
}
|
||||
|
||||
return $this->processResponse($this->client->get($prepared['url'], $prepared['headers']));
|
||||
return $this->processResponse(
|
||||
$this->client->get($prepared['url'], $prepared['headers'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,18 +222,22 @@ class GitHub
|
||||
public function getFilesForPullRequest($user, $repo, $pullId, $page = 1)
|
||||
{
|
||||
// Build the request path.
|
||||
$path = "/repos/$user/$repo/pulls/" . (int) $pullId . '/files?page=' . $page;
|
||||
$path = "/repos/$user/$repo/pulls/" . (int)$pullId . '/files?page='
|
||||
. $page;
|
||||
$prepared = $this->prepareRequest($path);
|
||||
return $this->processResponse($this->client->get($prepared['url'], $prepared['headers']));
|
||||
|
||||
return $this->processResponse(
|
||||
$this->client->get($prepared['url'], $prepared['headers'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the open issues for a repository.
|
||||
*
|
||||
* @param string $user The name of the owner of the GitHub repository.
|
||||
* @param string $repo The name of the GitHub repository.
|
||||
* @param integer $page The page number from which to get items.
|
||||
* @param integer $limit The number of items on a page.
|
||||
* @param string $user The name of the owner of the GitHub repository.
|
||||
* @param string $repo The name of the GitHub repository.
|
||||
* @param int $page The page number from which to get items.
|
||||
* @param int $limit The number of items on a page.
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
@ -236,16 +250,19 @@ class GitHub
|
||||
$page,
|
||||
$limit
|
||||
);
|
||||
return $this->processResponse($this->client->get($prepared['url'], $prepared['headers']));
|
||||
|
||||
return $this->processResponse(
|
||||
$this->client->get($prepared['url'], $prepared['headers'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the open pull requests for a repository.
|
||||
*
|
||||
* @param string $user The name of the owner of the GitHub repository.
|
||||
* @param string $repo The name of the GitHub repository.
|
||||
* @param integer $page The page number from which to get items.
|
||||
* @param integer $limit The number of items on a page.
|
||||
* @param string $user The name of the owner of the GitHub repository.
|
||||
* @param string $repo The name of the GitHub repository.
|
||||
* @param int $page The page number from which to get items.
|
||||
* @param int $limit The number of items on a page.
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
@ -258,7 +275,10 @@ class GitHub
|
||||
$page,
|
||||
$limit
|
||||
);
|
||||
return $this->processResponse($this->client->get($prepared['url'], $prepared['headers']));
|
||||
|
||||
return $this->processResponse(
|
||||
$this->client->get($prepared['url'], $prepared['headers'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -279,9 +299,9 @@ class GitHub
|
||||
/**
|
||||
* Get a single pull request.
|
||||
*
|
||||
* @param string $user The name of the owner of the GitHub repository.
|
||||
* @param string $repo The name of the GitHub repository.
|
||||
* @param integer $pullId The pull request number.
|
||||
* @param string $user The name of the owner of the GitHub repository.
|
||||
* @param string $repo The name of the GitHub repository.
|
||||
* @param int $pullId The pull request number.
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
@ -290,9 +310,12 @@ class GitHub
|
||||
public function getPullRequest($user, $repo, $pullId)
|
||||
{
|
||||
// Build the request path.
|
||||
$path = "/repos/$user/$repo/pulls/" . (int) $pullId;
|
||||
$path = "/repos/$user/$repo/pulls/" . (int)$pullId;
|
||||
$prepared = $this->prepareRequest($path);
|
||||
return $this->processResponse($this->client->get($prepared['url'], $prepared['headers']));
|
||||
|
||||
return $this->processResponse(
|
||||
$this->client->get($prepared['url'], $prepared['headers'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -305,7 +328,10 @@ class GitHub
|
||||
public function getRateLimit()
|
||||
{
|
||||
$prepared = $this->prepareRequest('/rate_limit');
|
||||
return $this->processResponse($this->client->get($prepared['url'], $prepared['headers']));
|
||||
|
||||
return $this->processResponse(
|
||||
$this->client->get($prepared['url'], $prepared['headers'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -321,6 +347,7 @@ class GitHub
|
||||
public function setOption($key, $value)
|
||||
{
|
||||
$this->options->set($key, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Joomla\Component\Patchtester\Administrator;
|
||||
|
||||
use Joomla\CMS\Component\ComponentHelper;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
/**
|
||||
* @package ${NAMESPACE}
|
||||
* @subpackage
|
||||
*
|
||||
* @copyright A copyright
|
||||
* @license A "Slug" license name e.g. GPL2
|
||||
*/
|
||||
trait GithubCredentialsTrait
|
||||
{
|
||||
protected function getCredentials() {
|
||||
$state = new Registry();
|
||||
$params = ComponentHelper::getParams('com_patchtester');
|
||||
$state->set('github_user', $params->get('org', 'joomla'));
|
||||
$state->set('github_repo', $params->get('repo', 'joomla-cms'));
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
@ -12,8 +12,8 @@ namespace Joomla\Component\Patchtester\Administrator\Helper;
|
||||
use Joomla\CMS\Component\ComponentHelper;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\Component\Patchtester\Administrator\GitHub\GitHub;
|
||||
use Joomla\Registry\Registry;
|
||||
use src\GitHub\GitHub;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
|
@ -10,7 +10,6 @@
|
||||
namespace Joomla\Component\Patchtester\Administrator\Model;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
|
@ -15,15 +15,17 @@ use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Filesystem\Path;
|
||||
use Joomla\CMS\Http\HttpFactory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
|
||||
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
|
||||
use Joomla\CMS\Version;
|
||||
use Joomla\Component\Patchtester\Administrator\Github\Exception\UnexpectedResponse;
|
||||
use Joomla\Component\Patchtester\Administrator\GitHub\GitHub;
|
||||
use Joomla\Component\Patchtester\Administrator\GithubCredentialsTrait;
|
||||
use Joomla\Component\Patchtester\Administrator\Helper\Helper;
|
||||
use Joomla\Database\DatabaseDriver;
|
||||
use Joomla\Filesystem\File;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use Joomla\Registry\Registry;
|
||||
use RuntimeException;
|
||||
use src\GitHub\Exception\UnexpectedResponse;
|
||||
use src\GitHub\GitHub;
|
||||
use stdClass;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
@ -35,8 +37,9 @@ use stdClass;
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class PullModel extends AbstractModel
|
||||
class PullModel extends BaseDatabaseModel
|
||||
{
|
||||
use GithubCredentialsTrait;
|
||||
/**
|
||||
* Array containing top level non-production folders
|
||||
*
|
||||
@ -44,21 +47,21 @@ class PullModel extends AbstractModel
|
||||
* @since 2.0
|
||||
*/
|
||||
protected $nonProductionFolders
|
||||
= array(
|
||||
= [
|
||||
'build',
|
||||
'docs',
|
||||
'installation',
|
||||
'tests',
|
||||
'.github',
|
||||
);
|
||||
/**
|
||||
];
|
||||
/**
|
||||
* Array containing non-production files
|
||||
*
|
||||
* @var array
|
||||
* @since 2.0
|
||||
*/
|
||||
protected $nonProductionFiles
|
||||
= array(
|
||||
= [
|
||||
'.drone.yml',
|
||||
'.gitignore',
|
||||
'.php_cs',
|
||||
@ -77,25 +80,27 @@ class PullModel extends AbstractModel
|
||||
'jorobo.dist.ini',
|
||||
'manifest.xml',
|
||||
'crowdin.yaml',
|
||||
);
|
||||
/**
|
||||
];
|
||||
|
||||
/**
|
||||
* The namespace mapper
|
||||
*
|
||||
* @var \JNamespacePsr4Map
|
||||
* @since 4.0.0
|
||||
*/
|
||||
protected $namespaceMapper;
|
||||
/**
|
||||
* Instantiate the model.
|
||||
*
|
||||
* @param Registry $state The model state.
|
||||
* @param DatabaseDriver $db The database adpater.
|
||||
*
|
||||
* @since 4.0.0
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function __construct(Registry $state = null, DatabaseDriver $db = null)
|
||||
{
|
||||
parent::__construct($state, $db);
|
||||
public function __construct(
|
||||
$config = [],
|
||||
MVCFactoryInterface $factory = null
|
||||
) {
|
||||
parent::__construct($config, $factory);
|
||||
|
||||
$this->state = $this->getCredentials();
|
||||
|
||||
$this->namespaceMapper = new \JNamespacePsr4Map();
|
||||
}
|
||||
|
||||
@ -103,19 +108,19 @@ class PullModel extends AbstractModel
|
||||
* Patches the code with the supplied pull request
|
||||
* However uses different methods for different repositories.
|
||||
*
|
||||
* @param integer $id ID of the pull request to apply
|
||||
* @param int $id ID of the pull request to apply
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.0
|
||||
* @return bool
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public function apply(int $id): bool
|
||||
{
|
||||
$params = ComponentHelper::getParams('com_patchtester');
|
||||
// Decide based on repository settings whether patch will be applied through Github or CIServer
|
||||
if ((bool) $params->get('ci_switch', 0)) {
|
||||
if ((bool)$params->get('ci_switch', 0)) {
|
||||
return $this->applyWithCIServer($id);
|
||||
}
|
||||
|
||||
@ -129,9 +134,9 @@ class PullModel extends AbstractModel
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @since 3.0
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function applyWithCIServer(int $id): bool
|
||||
{
|
||||
@ -151,10 +156,10 @@ class PullModel extends AbstractModel
|
||||
Folder::create($ciSettings->get('folder.temp'));
|
||||
}
|
||||
|
||||
$tempPath = $ciSettings->get('folder.temp') . '/' . $id;
|
||||
$backupsPath = $ciSettings->get('folder.backups') . '/' . $id;
|
||||
$delLogPath = $tempPath . '/' . $ciSettings->get('zip.log.name');
|
||||
$zipPath = $tempPath . '/' . $ciSettings->get('zip.name');
|
||||
$tempPath = $ciSettings->get('folder.temp') . '/' . $id;
|
||||
$backupsPath = $ciSettings->get('folder.backups') . '/' . $id;
|
||||
$delLogPath = $tempPath . '/' . $ciSettings->get('zip.log.name');
|
||||
$zipPath = $tempPath . '/' . $ciSettings->get('zip.name');
|
||||
$serverZipPath = sprintf($ciSettings->get('zip.url'), $id);
|
||||
// Patch has already been applied
|
||||
if (file_exists($backupsPath)) {
|
||||
@ -163,7 +168,10 @@ class PullModel extends AbstractModel
|
||||
|
||||
$version = new Version();
|
||||
$httpOption = new Registry();
|
||||
$httpOption->set('userAgent', $version->getUserAgent('Joomla', true, false));
|
||||
$httpOption->set(
|
||||
'userAgent',
|
||||
$version->getUserAgent('Joomla', true, false)
|
||||
);
|
||||
// Try to download the zip file
|
||||
try {
|
||||
$http = HttpFactory::getHttp($httpOption);
|
||||
@ -172,24 +180,33 @@ class PullModel extends AbstractModel
|
||||
$result = null;
|
||||
}
|
||||
|
||||
if ($result === null || ($result->getStatusCode() !== 200 && $result->getStatusCode() !== 310)) {
|
||||
throw new RuntimeException(Text::_('COM_PATCHTESTER_SERVER_RESPONDED_NOT_200'));
|
||||
if ($result === null
|
||||
|| ($result->getStatusCode() !== 200
|
||||
&& $result->getStatusCode() !== 310)
|
||||
) {
|
||||
throw new RuntimeException(
|
||||
Text::_('COM_PATCHTESTER_SERVER_RESPONDED_NOT_200')
|
||||
);
|
||||
}
|
||||
|
||||
// Assign to variable to avlod PHP notice "Indirect modification of overloaded property"
|
||||
$content = (string) $result->getBody();
|
||||
$content = (string)$result->getBody();
|
||||
// Write the file to disk
|
||||
File::write($zipPath, $content);
|
||||
// Check if zip folder could have been downloaded
|
||||
if (!file_exists($zipPath)) {
|
||||
throw new RuntimeException(Text::_('COM_PATCHTESTER_ZIP_DOES_NOT_EXIST'));
|
||||
throw new RuntimeException(
|
||||
Text::_('COM_PATCHTESTER_ZIP_DOES_NOT_EXIST')
|
||||
);
|
||||
}
|
||||
|
||||
Folder::create($tempPath);
|
||||
$zip = new Zip();
|
||||
if (!$zip->extract($zipPath, $tempPath)) {
|
||||
Folder::delete($tempPath);
|
||||
throw new RuntimeException(Text::_('COM_PATCHTESTER_ZIP_EXTRACT_FAILED'));
|
||||
throw new RuntimeException(
|
||||
Text::_('COM_PATCHTESTER_ZIP_EXTRACT_FAILED')
|
||||
);
|
||||
}
|
||||
|
||||
// Remove zip to avoid get listing afterwards
|
||||
@ -198,14 +215,16 @@ class PullModel extends AbstractModel
|
||||
if ($this->verifyAutoloader($tempPath) === false) {
|
||||
// There is something broken in the autoloader, clean up and go back
|
||||
Folder::delete($tempPath);
|
||||
throw new RuntimeException(Text::_('COM_PATCHTESTER_PATCH_BREAKS_SITE'));
|
||||
throw new RuntimeException(
|
||||
Text::_('COM_PATCHTESTER_PATCH_BREAKS_SITE')
|
||||
);
|
||||
}
|
||||
|
||||
// Get files from deleted_logs
|
||||
$deletedFiles = (file_exists($delLogPath) ? file($delLogPath) : []);
|
||||
$deletedFiles = array_map('trim', $deletedFiles);
|
||||
if (file_exists($delLogPath)) {
|
||||
// Remove deleted_logs to avoid get listing afterwards
|
||||
// Remove deleted_logs to avoid get listing afterwards
|
||||
File::delete($delLogPath);
|
||||
}
|
||||
|
||||
@ -220,7 +239,7 @@ class PullModel extends AbstractModel
|
||||
$filePath = explode(DIRECTORY_SEPARATOR, Path::clean($file));
|
||||
array_pop($filePath);
|
||||
$filePath = implode(DIRECTORY_SEPARATOR, $filePath);
|
||||
// Deleted_logs returns files as well as folder, if value is folder, unset and skip
|
||||
// Deleted_logs returns files as well as folder, if value is folder, unset and skip
|
||||
if (is_dir(JPATH_ROOT . '/' . $file)) {
|
||||
unset($files[$key]);
|
||||
continue;
|
||||
@ -232,21 +251,37 @@ class PullModel extends AbstractModel
|
||||
Folder::create($backupsPath . '/' . $filePath);
|
||||
}
|
||||
|
||||
File::move(JPATH_ROOT . '/' . $file, $backupsPath . '/' . $file);
|
||||
File::move(
|
||||
JPATH_ROOT . '/' . $file,
|
||||
$backupsPath . '/' . $file
|
||||
);
|
||||
}
|
||||
|
||||
if (file_exists($tempPath . '/' . $file)) {
|
||||
// Create directories if they don't exist until file
|
||||
if (!file_exists(JPATH_ROOT . '/' . $filePath) || !is_dir(JPATH_ROOT . '/' . $filePath)) {
|
||||
if (!file_exists(JPATH_ROOT . '/' . $filePath)
|
||||
|| !is_dir(
|
||||
JPATH_ROOT . '/' . $filePath
|
||||
)
|
||||
) {
|
||||
Folder::create(JPATH_ROOT . '/' . $filePath);
|
||||
}
|
||||
|
||||
File::copy($tempPath . '/' . $file, JPATH_ROOT . '/' . $file);
|
||||
File::copy(
|
||||
$tempPath . '/' . $file,
|
||||
JPATH_ROOT . '/' . $file
|
||||
);
|
||||
}
|
||||
} catch (RuntimeException $exception) {
|
||||
Folder::delete($tempPath);
|
||||
Folder::move($backupsPath, $backupsPath . '_failed');
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_FAILED_APPLYING_PATCH', $file, $exception->getMessage()));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_FAILED_APPLYING_PATCH',
|
||||
$file,
|
||||
$exception->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,6 +293,7 @@ class PullModel extends AbstractModel
|
||||
// Change the media version
|
||||
$version = new Version();
|
||||
$version->refreshMediaVersion();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -269,9 +305,9 @@ class PullModel extends AbstractModel
|
||||
*
|
||||
* @return stdClass The pull request data
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @since 2.0
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function retrieveGitHubData(GitHub $github, int $id): stdClass
|
||||
{
|
||||
@ -279,19 +315,38 @@ class PullModel extends AbstractModel
|
||||
$rateResponse = $github->getRateLimit();
|
||||
$rate = json_decode($rateResponse->body, false);
|
||||
} catch (UnexpectedResponse $exception) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_COULD_NOT_CONNECT_TO_GITHUB', $exception->getMessage()), $exception->getCode(), $exception);
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_COULD_NOT_CONNECT_TO_GITHUB',
|
||||
$exception->getMessage()
|
||||
), $exception->getCode(), $exception
|
||||
);
|
||||
}
|
||||
|
||||
// If over the API limit, we can't build this list
|
||||
if ((int) $rate->resources->core->remaining === 0) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_API_LIMIT_LIST', Factory::getDate($rate->resources->core->reset)));
|
||||
if ((int)$rate->resources->core->remaining === 0) {
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_API_LIMIT_LIST',
|
||||
Factory::getDate($rate->resources->core->reset)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$pullResponse = $github->getPullRequest($this->getState()->get('github_user'), $this->getState()->get('github_repo'), $id);
|
||||
$pullResponse = $github->getPullRequest(
|
||||
$this->getState()->get('github_user'),
|
||||
$this->getState()->get('github_repo'),
|
||||
$id
|
||||
);
|
||||
$pull = json_decode($pullResponse->body, false);
|
||||
} catch (UnexpectedResponse $exception) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_COULD_NOT_CONNECT_TO_GITHUB', $exception->getMessage()), $exception->getCode(), $exception);
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_COULD_NOT_CONNECT_TO_GITHUB',
|
||||
$exception->getMessage()
|
||||
), $exception->getCode(), $exception
|
||||
);
|
||||
}
|
||||
|
||||
return $pull;
|
||||
@ -309,7 +364,7 @@ class PullModel extends AbstractModel
|
||||
private function verifyAutoloader(string $path): bool
|
||||
{
|
||||
$result = false;
|
||||
// Check if we have an autoload file
|
||||
// Check if we have an autoload file
|
||||
if (!file_exists($path . '/libraries/vendor/autoload.php')) {
|
||||
return $result;
|
||||
}
|
||||
@ -322,35 +377,50 @@ class PullModel extends AbstractModel
|
||||
'autoload_psr4.php',
|
||||
];
|
||||
$filesToCheck = [];
|
||||
array_walk($composerFiles, static function ($composerFile) use (&$filesToCheck, $path) {
|
||||
|
||||
if (file_exists($path . '/libraries/vendor/composer/' . $composerFile) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($composerFile === 'autoload_static.php') {
|
||||
// Get the generated token
|
||||
$autoload = file_get_contents($path . '/libraries/vendor/autoload.php');
|
||||
$resultMatch = preg_match('/ComposerAutoloaderInit(.*)::/', $autoload, $match);
|
||||
if (!$resultMatch) {
|
||||
array_walk(
|
||||
$composerFiles,
|
||||
static function ($composerFile) use (&$filesToCheck, $path) {
|
||||
if (file_exists(
|
||||
$path . '/libraries/vendor/composer/' . $composerFile
|
||||
) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the class is already defined
|
||||
$autoloadClass = '\Composer\Autoload\ComposerStaticInit' . $match[1];
|
||||
if (class_exists($autoloadClass)) {
|
||||
return;
|
||||
}
|
||||
if ($composerFile === 'autoload_static.php') {
|
||||
// Get the generated token
|
||||
$autoload = file_get_contents(
|
||||
$path . '/libraries/vendor/autoload.php'
|
||||
);
|
||||
$resultMatch = preg_match(
|
||||
'/ComposerAutoloaderInit(.*)::/',
|
||||
$autoload,
|
||||
$match
|
||||
);
|
||||
if (!$resultMatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once $path . '/libraries/vendor/composer/autoload_static.php';
|
||||
// Get all the files
|
||||
$files = $autoloadClass::$files;
|
||||
$filesToCheck = array_merge($filesToCheck, $files);
|
||||
} else {
|
||||
$files = require $path . '/libraries/vendor/composer/' . $composerFile;
|
||||
$filesToCheck = array_merge($filesToCheck, $files);
|
||||
// Check if the class is already defined
|
||||
$autoloadClass = '\Composer\Autoload\ComposerStaticInit'
|
||||
. $match[1];
|
||||
if (class_exists($autoloadClass)) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once $path
|
||||
. '/libraries/vendor/composer/autoload_static.php';
|
||||
// Get all the files
|
||||
$files = $autoloadClass::$files;
|
||||
$filesToCheck = array_merge($filesToCheck, $files);
|
||||
} else {
|
||||
$files = require $path
|
||||
. '/libraries/vendor/composer/' . $composerFile;
|
||||
$filesToCheck = array_merge($filesToCheck, $files);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
return $this->checkFilesExist($filesToCheck, $path);
|
||||
}
|
||||
|
||||
@ -393,24 +463,29 @@ class PullModel extends AbstractModel
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
private function saveAppliedPatch(int $id, array $fileList, string $sha = null): int
|
||||
{
|
||||
$record = (object) array(
|
||||
private function saveAppliedPatch(
|
||||
int $id,
|
||||
array $fileList,
|
||||
string $sha = null
|
||||
): int {
|
||||
$record = (object)[
|
||||
'pull_id' => $id,
|
||||
'data' => json_encode($fileList),
|
||||
'patched_by' => Factory::getUser()->id,
|
||||
'applied' => 1,
|
||||
'applied_version' => JVERSION,
|
||||
);
|
||||
$db = $this->getDb();
|
||||
];
|
||||
$db = $this->getDatabase();
|
||||
$db->insertObject('#__patchtester_tests', $record);
|
||||
$insertId = $db->insertid();
|
||||
if ($sha !== null) {
|
||||
// Insert the retrieved commit SHA into the pulls table for this item
|
||||
$db->setQuery($db->getQuery(true)
|
||||
// Insert the retrieved commit SHA into the pulls table for this item
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->update('#__patchtester_pulls')
|
||||
->set('sha = ' . $db->quote($sha))
|
||||
->where($db->quoteName('pull_id') . ' = ' . $id))->execute();
|
||||
->where($db->quoteName('pull_id') . ' = ' . $id)
|
||||
)->execute();
|
||||
}
|
||||
|
||||
return $insertId;
|
||||
@ -423,14 +498,14 @@ class PullModel extends AbstractModel
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @since 2.0
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function applyWithGitHub(int $id): bool
|
||||
{
|
||||
$github = Helper::initializeGithub();
|
||||
$pull = $this->retrieveGitHubData($github, $id);
|
||||
$pull = $this->retrieveGitHubData($github, $id);
|
||||
if ($pull->head->repo === null) {
|
||||
throw new RuntimeException(Text::_('COM_PATCHTESTER_REPO_IS_GONE'));
|
||||
}
|
||||
@ -438,7 +513,12 @@ class PullModel extends AbstractModel
|
||||
try {
|
||||
$files = $this->getFiles($id, 1);
|
||||
} catch (UnexpectedResponse $exception) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_COULD_NOT_CONNECT_TO_GITHUB', $exception->getMessage()), $exception->getCode(), $exception);
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_COULD_NOT_CONNECT_TO_GITHUB',
|
||||
$exception->getMessage()
|
||||
), $exception->getCode(), $exception
|
||||
);
|
||||
}
|
||||
|
||||
if (!count($files)) {
|
||||
@ -454,7 +534,12 @@ class PullModel extends AbstractModel
|
||||
switch ($file->action) {
|
||||
case 'deleted':
|
||||
if (!file_exists(JPATH_ROOT . '/' . $file->filename)) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_FILE_DELETED_DOES_NOT_EXIST_S', $file->filename));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_FILE_DELETED_DOES_NOT_EXIST_S',
|
||||
$file->filename
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -463,17 +548,43 @@ class PullModel extends AbstractModel
|
||||
case 'modified':
|
||||
case 'renamed':
|
||||
// If the backup file already exists, we can't apply the patch
|
||||
if (file_exists(JPATH_COMPONENT . '/backups/' . md5($file->filename) . '.txt')) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_CONFLICT_S', $file->filename));
|
||||
if (file_exists(
|
||||
JPATH_COMPONENT . '/backups/' . md5($file->filename)
|
||||
. '.txt'
|
||||
)
|
||||
) {
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_CONFLICT_S',
|
||||
$file->filename
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($file->action === 'modified' && !file_exists(JPATH_ROOT . '/' . $file->filename)) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_FILE_MODIFIED_DOES_NOT_EXIST_S', $file->filename));
|
||||
if ($file->action === 'modified'
|
||||
&& !file_exists(
|
||||
JPATH_ROOT . '/' . $file->filename
|
||||
)
|
||||
) {
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_FILE_MODIFIED_DOES_NOT_EXIST_S',
|
||||
$file->filename
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$contentsResponse = $github->getFileContents($pull->head->user->login, $pull->head->repo->name, $file->repofilename, urlencode($pull->head->ref));
|
||||
$contents = json_decode($contentsResponse->body, false);
|
||||
$contentsResponse = $github->getFileContents(
|
||||
$pull->head->user->login,
|
||||
$pull->head->repo->name,
|
||||
$file->repofilename,
|
||||
urlencode($pull->head->ref)
|
||||
);
|
||||
$contents = json_decode(
|
||||
$contentsResponse->body,
|
||||
false
|
||||
);
|
||||
// In case encoding type ever changes
|
||||
switch ($contents->encoding) {
|
||||
case 'base64':
|
||||
@ -481,10 +592,19 @@ class PullModel extends AbstractModel
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException(Text::_('COM_PATCHTESTER_ERROR_UNSUPPORTED_ENCODING'));
|
||||
throw new RuntimeException(
|
||||
Text::_(
|
||||
'COM_PATCHTESTER_ERROR_UNSUPPORTED_ENCODING'
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (UnexpectedResponse $exception) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_COULD_NOT_CONNECT_TO_GITHUB', $exception->getMessage()), $exception->getCode(), $exception);
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_COULD_NOT_CONNECT_TO_GITHUB',
|
||||
$exception->getMessage()
|
||||
), $exception->getCode(), $exception
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -496,40 +616,84 @@ class PullModel extends AbstractModel
|
||||
// We only create a backup if the file already exists
|
||||
if (
|
||||
$file->action === 'deleted'
|
||||
|| (file_exists(JPATH_ROOT . '/' . $file->filename) && $file->action === 'modified')
|
||||
|| (file_exists(JPATH_ROOT . '/' . $file->originalFile) && $file->action === 'renamed')
|
||||
|| (file_exists(JPATH_ROOT . '/' . $file->filename)
|
||||
&& $file->action === 'modified')
|
||||
|| (file_exists(JPATH_ROOT . '/' . $file->originalFile)
|
||||
&& $file->action === 'renamed')
|
||||
) {
|
||||
$filename = $file->action === 'renamed' ? $file->originalFile : $file->filename;
|
||||
$filename = $file->action === 'renamed' ? $file->originalFile
|
||||
: $file->filename;
|
||||
$src = JPATH_ROOT . '/' . $filename;
|
||||
$dest = JPATH_COMPONENT . '/backups/' . md5($filename) . '.txt';
|
||||
$dest = JPATH_COMPONENT . '/backups/' . md5($filename)
|
||||
. '.txt';
|
||||
if (!File::copy(Path::clean($src), $dest)) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_COPY_FILE', $src, $dest));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_CANNOT_COPY_FILE',
|
||||
$src,
|
||||
$dest
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
switch ($file->action) {
|
||||
case 'modified':
|
||||
case 'added':
|
||||
if (!File::write(Path::clean(JPATH_ROOT . '/' . $file->filename), $file->body)) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_WRITE_FILE', JPATH_ROOT . '/' . $file->filename));
|
||||
if (!File::write(
|
||||
Path::clean(JPATH_ROOT . '/' . $file->filename),
|
||||
$file->body
|
||||
)
|
||||
) {
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_CANNOT_WRITE_FILE',
|
||||
JPATH_ROOT . '/' . $file->filename
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
case 'deleted':
|
||||
if (!File::delete(Path::clean(JPATH_ROOT . '/' . $file->filename))) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE', JPATH_ROOT . '/' . $file->filename));
|
||||
if (!File::delete(
|
||||
Path::clean(JPATH_ROOT . '/' . $file->filename)
|
||||
)
|
||||
) {
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE',
|
||||
JPATH_ROOT . '/' . $file->filename
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
case 'renamed':
|
||||
if (!File::delete(Path::clean(JPATH_ROOT . '/' . $file->originalFile))) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE', JPATH_ROOT . '/' . $file->originalFile));
|
||||
if (!File::delete(
|
||||
Path::clean(JPATH_ROOT . '/' . $file->originalFile)
|
||||
)
|
||||
) {
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE',
|
||||
JPATH_ROOT . '/' . $file->originalFile
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!File::write(Path::clean(JPATH_ROOT . '/' . $file->filename), $file->body)) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_WRITE_FILE', JPATH_ROOT . '/' . $file->filename));
|
||||
if (!File::write(
|
||||
Path::clean(JPATH_ROOT . '/' . $file->filename),
|
||||
$file->body
|
||||
)
|
||||
) {
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_CANNOT_WRITE_FILE',
|
||||
JPATH_ROOT . '/' . $file->filename
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -545,6 +709,7 @@ class PullModel extends AbstractModel
|
||||
// Change the media version
|
||||
$version = new Version();
|
||||
$version->refreshMediaVersion();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -561,11 +726,19 @@ class PullModel extends AbstractModel
|
||||
*/
|
||||
private function getFiles(int $id, int $page, array $files = []): array
|
||||
{
|
||||
$github = Helper::initializeGithub();
|
||||
$filesResponse = $github->getFilesForPullRequest($this->getState()->get('github_user'), $this->getState()->get('github_repo'), $id, $page);
|
||||
$files = array_merge($files, json_decode($filesResponse->getBody(), false));
|
||||
$lastPage = 1;
|
||||
$headers = $filesResponse->getHeaders();
|
||||
$github = Helper::initializeGithub();
|
||||
$filesResponse = $github->getFilesForPullRequest(
|
||||
$this->getState()->get('github_user'),
|
||||
$this->getState()->get('github_repo'),
|
||||
$id,
|
||||
$page
|
||||
);
|
||||
$files = array_merge(
|
||||
$files,
|
||||
json_decode($filesResponse->getBody(), false)
|
||||
);
|
||||
$lastPage = 1;
|
||||
$headers = $filesResponse->getHeaders();
|
||||
|
||||
if (!isset($headers['link'])) {
|
||||
return $files;
|
||||
@ -578,7 +751,7 @@ class PullModel extends AbstractModel
|
||||
);
|
||||
if ($matches && isset($matches[0])) {
|
||||
preg_match('/\d+/', $matches[0], $pages);
|
||||
$lastPage = (int) $pages[0];
|
||||
$lastPage = (int)$pages[0];
|
||||
}
|
||||
|
||||
if ($page <= $lastPage) {
|
||||
@ -600,10 +773,10 @@ class PullModel extends AbstractModel
|
||||
private function parseFileList(array $files): array
|
||||
{
|
||||
$parsedFiles = array();
|
||||
/*
|
||||
* Check if the patch tester is running in a development environment
|
||||
* If we are not in development, we'll need to check the exclusion lists
|
||||
*/
|
||||
/*
|
||||
* Check if the patch tester is running in a development environment
|
||||
* If we are not in development, we'll need to check the exclusion lists
|
||||
*/
|
||||
$isDev = file_exists(JPATH_INSTALLATION . '/index.php');
|
||||
foreach ($files as $file) {
|
||||
if (!$isDev) {
|
||||
@ -621,20 +794,24 @@ class PullModel extends AbstractModel
|
||||
$prodFileName = $file->filename;
|
||||
$prodRenamedFileName = $file->previous_filename ?? false;
|
||||
$filePath = explode('/', $prodFileName);
|
||||
// Remove the `src` here to match the CMS paths if needed
|
||||
// Remove the `src` here to match the CMS paths if needed
|
||||
if ($filePath[0] === 'src') {
|
||||
$prodFileName = str_replace('src/', '', $prodFileName);
|
||||
}
|
||||
|
||||
if ($prodRenamedFileName) {
|
||||
$filePath = explode('/', $prodRenamedFileName);
|
||||
// Remove the `src` here to match the CMS paths if needed
|
||||
// Remove the `src` here to match the CMS paths if needed
|
||||
if ($filePath[0] === 'src') {
|
||||
$prodRenamedFileName = str_replace('src/', '', $prodRenamedFileName);
|
||||
$prodRenamedFileName = str_replace(
|
||||
'src/',
|
||||
'',
|
||||
$prodRenamedFileName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$parsedFiles[] = (object) array(
|
||||
$parsedFiles[] = (object)array(
|
||||
'action' => $file->status,
|
||||
'filename' => $prodFileName,
|
||||
'repofilename' => $file->filename,
|
||||
@ -654,14 +831,14 @@ class PullModel extends AbstractModel
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.0
|
||||
* @throws RuntimeException
|
||||
* @since 3.0
|
||||
*/
|
||||
public function revert(int $id): bool
|
||||
{
|
||||
$params = ComponentHelper::getParams('com_patchtester');
|
||||
// Decide based on repository settings whether patch will be applied through Github or CIServer
|
||||
if ((bool) $params->get('ci_switch', 0)) {
|
||||
if ((bool)$params->get('ci_switch', 0)) {
|
||||
return $this->revertWithCIServer($id);
|
||||
}
|
||||
|
||||
@ -675,8 +852,8 @@ class PullModel extends AbstractModel
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 3.0
|
||||
* @throws RuntimeException
|
||||
* @since 3.0
|
||||
*/
|
||||
public function revertWithCIServer(int $id): bool
|
||||
{
|
||||
@ -690,10 +867,17 @@ class PullModel extends AbstractModel
|
||||
|
||||
$files = json_decode($testRecord->data, false);
|
||||
if (!$files) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_READING_DATABASE_TABLE', __METHOD__, htmlentities($testRecord->data)));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_READING_DATABASE_TABLE',
|
||||
__METHOD__,
|
||||
htmlentities($testRecord->data)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$backupsPath = $ciSettings->get('folder.backups') . "/$testRecord->pull_id";
|
||||
$backupsPath = $ciSettings->get('folder.backups')
|
||||
. "/$testRecord->pull_id";
|
||||
foreach ($files as $file) {
|
||||
try {
|
||||
$filePath = explode("\\", $file);
|
||||
@ -704,11 +888,16 @@ class PullModel extends AbstractModel
|
||||
File::delete(JPATH_ROOT . "/$file");
|
||||
// Move from backup, if it exists there
|
||||
if (file_exists($backupsPath . '/' . $file)) {
|
||||
File::move($backupsPath . '/' . $file, JPATH_ROOT . '/' . $file);
|
||||
File::move(
|
||||
$backupsPath . '/' . $file,
|
||||
JPATH_ROOT . '/' . $file
|
||||
);
|
||||
}
|
||||
|
||||
// If folder is empty, remove it as well
|
||||
if (count(glob(JPATH_ROOT . '/' . $filePath . '/*')) === 0) {
|
||||
if (count(glob(JPATH_ROOT . '/' . $filePath . '/*'))
|
||||
=== 0
|
||||
) {
|
||||
Folder::delete(JPATH_ROOT . '/' . $filePath);
|
||||
}
|
||||
} elseif (file_exists($backupsPath . '/' . $file)) {
|
||||
@ -717,10 +906,19 @@ class PullModel extends AbstractModel
|
||||
Folder::create(JPATH_ROOT . '/' . $filePath);
|
||||
}
|
||||
|
||||
File::move($backupsPath . '/' . $file, JPATH_ROOT . '/' . $file);
|
||||
File::move(
|
||||
$backupsPath . '/' . $file,
|
||||
JPATH_ROOT . '/' . $file
|
||||
);
|
||||
}
|
||||
} catch (RuntimeException $exception) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_FAILED_REVERT_PATCH', $file, $exception->getMessage()));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_FAILED_REVERT_PATCH',
|
||||
$file,
|
||||
$exception->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -730,6 +928,7 @@ class PullModel extends AbstractModel
|
||||
// Change the media version
|
||||
$version = new Version();
|
||||
$version->refreshMediaVersion();
|
||||
|
||||
return $this->removeTest($testRecord);
|
||||
}
|
||||
|
||||
@ -744,11 +943,14 @@ class PullModel extends AbstractModel
|
||||
*/
|
||||
private function getTestRecord(int $id): stdClass
|
||||
{
|
||||
$db = $this->getDb();
|
||||
return $db->setQuery($db->getQuery(true)
|
||||
$db = $this->getDatabase();
|
||||
|
||||
return $db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->select('*')
|
||||
->from('#__patchtester_tests')
|
||||
->where('id = ' . (int) $id))->loadObject();
|
||||
->where('id = ' . (int)$id)
|
||||
)->loadObject();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -762,16 +964,21 @@ class PullModel extends AbstractModel
|
||||
*/
|
||||
private function removeTest(stdClass $testRecord): bool
|
||||
{
|
||||
$db = $this->getDb();
|
||||
// Remove the retrieved commit SHA from the pulls table for this item
|
||||
$db->setQuery($db->getQuery(true)
|
||||
$db = $this->getDatabase();
|
||||
// Remove the retrieved commit SHA from the pulls table for this item
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->update('#__patchtester_pulls')
|
||||
->set('sha = ' . $db->quote(''))
|
||||
->where($db->quoteName('id') . ' = ' . (int) $testRecord->id))->execute();
|
||||
// And delete the record from the tests table
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->where($db->quoteName('id') . ' = ' . (int)$testRecord->id)
|
||||
)->execute();
|
||||
// And delete the record from the tests table
|
||||
$db->setQuery(
|
||||
$db->getQuery(true)
|
||||
->delete('#__patchtester_tests')
|
||||
->where('id = ' . (int) $testRecord->id))->execute();
|
||||
->where('id = ' . (int)$testRecord->id)
|
||||
)->execute();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -782,8 +989,8 @@ class PullModel extends AbstractModel
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 2.0
|
||||
* @throws RuntimeException
|
||||
* @since 2.0
|
||||
*/
|
||||
public function revertWithGitHub(int $id): bool
|
||||
{
|
||||
@ -795,22 +1002,40 @@ class PullModel extends AbstractModel
|
||||
|
||||
$files = json_decode($testRecord->data, false);
|
||||
if (!$files) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_READING_DATABASE_TABLE', __METHOD__, htmlentities($testRecord->data)));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_READING_DATABASE_TABLE',
|
||||
__METHOD__,
|
||||
htmlentities($testRecord->data)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($files as $file) {
|
||||
switch ($file->action) {
|
||||
case 'deleted':
|
||||
case 'modified':
|
||||
$src = JPATH_COMPONENT . '/backups/' . md5($file->filename) . '.txt';
|
||||
$src = JPATH_COMPONENT . '/backups/' . md5($file->filename)
|
||||
. '.txt';
|
||||
$dest = JPATH_ROOT . '/' . $file->filename;
|
||||
if (!File::copy($src, $dest)) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_COPY_FILE', $src, $dest));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_CANNOT_COPY_FILE',
|
||||
$src,
|
||||
$dest
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (file_exists($src)) {
|
||||
if (!File::delete($src)) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE', $src));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE',
|
||||
$src
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -820,28 +1045,51 @@ class PullModel extends AbstractModel
|
||||
$src = JPATH_ROOT . '/' . $file->filename;
|
||||
if (file_exists($src)) {
|
||||
if (!File::delete($src)) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE', $src));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE',
|
||||
$src
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 'renamed':
|
||||
$originalSrc = JPATH_COMPONENT . '/backups/' . md5($file->originalFile) . '.txt';
|
||||
$originalSrc = JPATH_COMPONENT . '/backups/' . md5(
|
||||
$file->originalFile
|
||||
) . '.txt';
|
||||
$newSrc = JPATH_ROOT . '/' . $file->filename;
|
||||
$dest = JPATH_ROOT . '/' . $file->originalFile;
|
||||
if (!File::copy($originalSrc, $dest)) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_COPY_FILE', $originalSrc, $dest));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_CANNOT_COPY_FILE',
|
||||
$originalSrc,
|
||||
$dest
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (file_exists($originalSrc)) {
|
||||
if (!File::delete($originalSrc)) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE', $originalSrc));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE',
|
||||
$originalSrc
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($newSrc)) {
|
||||
if (!File::delete($newSrc)) {
|
||||
throw new RuntimeException(Text::sprintf('COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE', $newSrc));
|
||||
throw new RuntimeException(
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_CANNOT_DELETE_FILE',
|
||||
$newSrc
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -855,6 +1103,7 @@ class PullModel extends AbstractModel
|
||||
// Change the media version
|
||||
$version = new Version();
|
||||
$version->refreshMediaVersion();
|
||||
|
||||
return $this->removeTest($testRecord);
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\MVC\Model\ListModel;
|
||||
use Joomla\Component\Patchtester\Administrator\Github\Exception\UnexpectedResponse;
|
||||
use Joomla\Component\Patchtester\Administrator\GithubCredentialsTrait;
|
||||
use Joomla\Component\Patchtester\Administrator\Helper\Helper;
|
||||
use Joomla\Database\DatabaseQuery;
|
||||
use RuntimeException;
|
||||
@ -29,6 +30,8 @@ use RuntimeException;
|
||||
*/
|
||||
class PullsModel extends ListModel
|
||||
{
|
||||
use GithubCredentialsTrait;
|
||||
|
||||
/**
|
||||
* The object context
|
||||
*
|
||||
@ -43,18 +46,18 @@ class PullsModel extends ListModel
|
||||
* @since 2.0
|
||||
*/
|
||||
protected $sortFields = ['pulls.pull_id', 'pulls.title'];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $config An optional associative array of configuration settings.
|
||||
*
|
||||
* @since 4.0.0
|
||||
* @throws Exception
|
||||
*
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public function __construct($config = [])
|
||||
{
|
||||
$config = [];
|
||||
if (empty($config['filter_fields'])) {
|
||||
$config['filter_fields'] = [
|
||||
'applied',
|
||||
@ -63,12 +66,27 @@ class PullsModel extends ListModel
|
||||
'draft',
|
||||
'label',
|
||||
'branch',
|
||||
|
||||
'pulls.title',
|
||||
'pulls.pull_id',
|
||||
];
|
||||
}
|
||||
|
||||
parent::__construct($config);
|
||||
}
|
||||
|
||||
protected function populateState($ordering = 'pulls.pull_id', $direction = 'DESC')
|
||||
{
|
||||
parent::populateState(
|
||||
$ordering,
|
||||
$direction
|
||||
);
|
||||
|
||||
foreach ($this->getCredentials() as $name => $value) {
|
||||
$this->state->set($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get an array of data items.
|
||||
*
|
||||
@ -93,13 +111,13 @@ class PullsModel extends ListModel
|
||||
->select($db->quoteName(['name', 'color']))
|
||||
->from($db->quoteName('#__patchtester_pulls_labels'));
|
||||
array_walk($items, static function ($item) use ($db, $query) {
|
||||
|
||||
$query->clear('where');
|
||||
$query->clear('where');
|
||||
$query->where($db->quoteName('pull_id') . ' = ' . $item->pull_id);
|
||||
$db->setQuery($query);
|
||||
$item->labels = $db->loadObjectList();
|
||||
});
|
||||
$this->cache[$store] = $items;
|
||||
|
||||
return $this->cache[$store];
|
||||
}
|
||||
|
||||
@ -118,11 +136,11 @@ class PullsModel extends ListModel
|
||||
*/
|
||||
protected function getStoreId($id = '')
|
||||
{
|
||||
// Add the list state to the store id.
|
||||
$id .= ':' . $this->getState()->get('list.start');
|
||||
$id .= ':' . $this->getState()->get('list.limit');
|
||||
$id .= ':' . $this->getState()->get('list.ordering');
|
||||
$id .= ':' . $this->getState()->get('list.direction');
|
||||
|
||||
return md5($this->context . ':' . $id);
|
||||
}
|
||||
|
||||
@ -135,11 +153,14 @@ class PullsModel extends ListModel
|
||||
*
|
||||
* @return array An array of results.
|
||||
*
|
||||
* @since 2.0
|
||||
* @throws RuntimeException
|
||||
* @since 2.0
|
||||
*/
|
||||
protected function getList($query, int $limitstart = 0, int $limit = 0): array
|
||||
{
|
||||
protected function getList(
|
||||
$query,
|
||||
int $limitstart = 0,
|
||||
int $limit = 0
|
||||
): array {
|
||||
return $this->getDatabase()->setQuery($query, $limitstart, $limit)
|
||||
->loadObjectList();
|
||||
}
|
||||
@ -183,53 +204,68 @@ class PullsModel extends ListModel
|
||||
$query->select('pulls.*')
|
||||
->select($db->quoteName('tests.id', 'applied'))
|
||||
->from($db->quoteName('#__patchtester_pulls', 'pulls'))
|
||||
->leftJoin($db->quoteName('#__patchtester_tests', 'tests')
|
||||
->leftJoin(
|
||||
$db->quoteName('#__patchtester_tests', 'tests')
|
||||
. ' ON ' . $db->quoteName('tests.pull_id') . ' = '
|
||||
. $db->quoteName('pulls.pull_id'));
|
||||
. $db->quoteName('pulls.pull_id')
|
||||
);
|
||||
$search = $this->getState()->get('filter.search');
|
||||
|
||||
if (!empty($search)) {
|
||||
if (stripos($search, 'id:') === 0) {
|
||||
$query->where($db->quoteName('pulls.pull_id') . ' = ' . (int) substr(
|
||||
$search,
|
||||
3
|
||||
));
|
||||
$query->where(
|
||||
$db->quoteName('pulls.pull_id') . ' = ' . (int)substr(
|
||||
$search,
|
||||
3
|
||||
)
|
||||
);
|
||||
} elseif (is_numeric($search)) {
|
||||
$query->where($db->quoteName('pulls.pull_id') . ' = ' . (int) $search);
|
||||
$query->where(
|
||||
$db->quoteName('pulls.pull_id') . ' = ' . (int)$search
|
||||
);
|
||||
} else {
|
||||
$query->where('(' . $db->quoteName('pulls.title') . ' LIKE ' . $db->quote('%' . $db->escape($search, true) . '%') . ')');
|
||||
$query->where(
|
||||
'(' . $db->quoteName('pulls.title') . ' LIKE ' . $db->quote(
|
||||
'%' . $db->escape($search, true) . '%'
|
||||
) . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$applied = $this->getState()->get('filter.applied');
|
||||
if (!empty($applied)) {
|
||||
// Not applied patches have a NULL value, so build our value part of the query based on this
|
||||
// Not applied patches have a NULL value, so build our value part of the query based on this
|
||||
$value = $applied === 'no' ? ' IS NULL' : ' = 1';
|
||||
$query->where($db->quoteName('applied') . $value);
|
||||
}
|
||||
|
||||
$branch = $this->getState()->get('filter.branch');
|
||||
if (!empty($branch)) {
|
||||
$query->where($db->quoteName('pulls.branch') . ' IN (' . implode(',', $db->quote($branch)) . ')');
|
||||
$query->where(
|
||||
$db->quoteName('pulls.branch') . ' IN (' . implode(
|
||||
',',
|
||||
$db->quote($branch)
|
||||
) . ')'
|
||||
);
|
||||
}
|
||||
|
||||
$applied = $this->getState()->get('filter.rtc');
|
||||
if (!empty($applied)) {
|
||||
// Not applied patches have a NULL value, so build our value part of the query based on this
|
||||
// Not applied patches have a NULL value, so build our value part of the query based on this
|
||||
$value = $applied === 'no' ? '0' : '1';
|
||||
$query->where($db->quoteName('pulls.is_rtc') . ' = ' . $value);
|
||||
}
|
||||
|
||||
$npm = $this->getState()->get('filter.npm');
|
||||
if (!empty($npm)) {
|
||||
// Not applied patches have a NULL value, so build our value part of the query based on this
|
||||
// Not applied patches have a NULL value, so build our value part of the query based on this
|
||||
$value = $npm === 'no' ? '0' : '1';
|
||||
$query->where($db->quoteName('pulls.is_npm') . ' = ' . $value);
|
||||
}
|
||||
|
||||
$draft = $this->getState()->get('filter.draft');
|
||||
if (!empty($draft)) {
|
||||
// Not applied patches have a NULL value, so build our value part of the query based on this
|
||||
// Not applied patches have a NULL value, so build our value part of the query based on this
|
||||
$value = $draft === 'no' ? '0' : '1';
|
||||
$query->where($db->quoteName('pulls.is_draft') . ' = ' . $value);
|
||||
}
|
||||
@ -239,27 +275,43 @@ class PullsModel extends ListModel
|
||||
if (!empty($labels) && $labels[0] !== '') {
|
||||
$labelQuery
|
||||
->select($db->quoteName('pulls_labels.pull_id'))
|
||||
->select('COUNT(' . $db->quoteName('pulls_labels.name') . ') AS '
|
||||
. $db->quoteName('labelCount'))
|
||||
->from($db->quoteName(
|
||||
'#__patchtester_pulls_labels',
|
||||
'pulls_labels'
|
||||
))
|
||||
->where($db->quoteName('pulls_labels.name') . ' IN (' . implode(
|
||||
',',
|
||||
$db->quote($labels)
|
||||
) . ')')
|
||||
->select(
|
||||
'COUNT(' . $db->quoteName('pulls_labels.name') . ') AS '
|
||||
. $db->quoteName('labelCount')
|
||||
)
|
||||
->from(
|
||||
$db->quoteName(
|
||||
'#__patchtester_pulls_labels',
|
||||
'pulls_labels'
|
||||
)
|
||||
)
|
||||
->where(
|
||||
$db->quoteName('pulls_labels.name') . ' IN (' . implode(
|
||||
',',
|
||||
$db->quote($labels)
|
||||
) . ')'
|
||||
)
|
||||
->group($db->quoteName('pulls_labels.pull_id'));
|
||||
$query->leftJoin('(' . $labelQuery->__toString() . ') AS ' . $db->quoteName('pulls_labels')
|
||||
$query->leftJoin(
|
||||
'(' . $labelQuery->__toString() . ') AS ' . $db->quoteName(
|
||||
'pulls_labels'
|
||||
)
|
||||
. ' ON ' . $db->quoteName('pulls_labels.pull_id') . ' = '
|
||||
. $db->quoteName('pulls.pull_id'))
|
||||
->where($db->quoteName('pulls_labels.labelCount') . ' = ' . count($labels));
|
||||
. $db->quoteName('pulls.pull_id')
|
||||
)
|
||||
->where(
|
||||
$db->quoteName('pulls_labels.labelCount') . ' = ' . count(
|
||||
$labels
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$ordering = $this->getState()->get('list.ordering', 'pulls.pull_id');
|
||||
$direction = $this->getState()->get('list.direction', 'DESC');
|
||||
if (!empty($ordering)) {
|
||||
$query->order($db->escape($ordering) . ' ' . $db->escape($direction));
|
||||
$query->order(
|
||||
$db->escape($ordering) . ' ' . $db->escape($direction)
|
||||
);
|
||||
}
|
||||
|
||||
return $query;
|
||||
@ -296,12 +348,20 @@ class PullsModel extends ListModel
|
||||
|
||||
try {
|
||||
// TODO - Option to configure the batch size
|
||||
$batchSize = 100;
|
||||
$pullsResponse = Helper::initializeGithub()->getOpenPulls($this->getState()->get('github_user'), $this->getState()->get('github_repo'), $page, $batchSize);
|
||||
$pulls = json_decode($pullsResponse->body);
|
||||
$batchSize = 100;
|
||||
$pullsResponse = Helper::initializeGithub()->getOpenPulls(
|
||||
$this->getState()->get('github_user'),
|
||||
$this->getState()->get('github_repo'),
|
||||
$page,
|
||||
$batchSize
|
||||
);
|
||||
$pulls = json_decode($pullsResponse->body);
|
||||
} catch (UnexpectedResponse $exception) {
|
||||
throw new RuntimeException(
|
||||
Text::sprintf('COM_PATCHTESTER_ERROR_GITHUB_FETCH', $exception->getMessage()),
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_GITHUB_FETCH',
|
||||
$exception->getMessage()
|
||||
),
|
||||
$exception->getCode(),
|
||||
$exception
|
||||
);
|
||||
@ -332,7 +392,7 @@ class PullsModel extends ListModel
|
||||
$matches[0]
|
||||
);
|
||||
preg_match('/\d+/', $pageSegment, $pages);
|
||||
$lastPage = (int) $pages[0];
|
||||
$lastPage = (int)$pages[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -353,30 +413,38 @@ class PullsModel extends ListModel
|
||||
if (strtolower($label->name) === 'rtc') {
|
||||
$isRTC = true;
|
||||
} elseif (
|
||||
in_array(strtolower($label->name), ['npm resource changed', 'composer dependency changed'], true)
|
||||
in_array(
|
||||
strtolower($label->name),
|
||||
['npm resource changed', 'composer dependency changed'],
|
||||
true
|
||||
)
|
||||
) {
|
||||
$isNPM = true;
|
||||
}
|
||||
|
||||
$labels[] = implode(',', [
|
||||
(int) $pull->number,
|
||||
$this->getDatabase()->quote($label->name),
|
||||
$this->getDatabase()->quote($label->color),
|
||||
]);
|
||||
(int)$pull->number,
|
||||
$this->getDatabase()->quote($label->name),
|
||||
$this->getDatabase()->quote($label->color),
|
||||
]);
|
||||
}
|
||||
|
||||
// Build the data object to store in the database
|
||||
$pullData = [
|
||||
(int) $pull->number,
|
||||
$this->getDatabase()->quote(HTMLHelper::_('string.truncate', ($pull->title ?? ''), 150)),
|
||||
$this->getDatabase()->quote(HTMLHelper::_('string.truncate', ($pull->body ?? ''), 100)),
|
||||
(int)$pull->number,
|
||||
$this->getDatabase()->quote(
|
||||
HTMLHelper::_('string.truncate', ($pull->title ?? ''), 150)
|
||||
),
|
||||
$this->getDatabase()->quote(
|
||||
HTMLHelper::_('string.truncate', ($pull->body ?? ''), 100)
|
||||
),
|
||||
$this->getDatabase()->quote($pull->html_url),
|
||||
(int) $isRTC,
|
||||
(int) $isNPM,
|
||||
(int)$isRTC,
|
||||
(int)$isNPM,
|
||||
$this->getDatabase()->quote($branch),
|
||||
($pull->draft ? 1 : 0)
|
||||
];
|
||||
$data[] = implode(',', $pullData);
|
||||
$data[] = implode(',', $pullData);
|
||||
}
|
||||
|
||||
// If there are no pulls to insert then bail, assume we're finished
|
||||
@ -385,15 +453,28 @@ class PullsModel extends ListModel
|
||||
}
|
||||
|
||||
try {
|
||||
$this->getDatabase()->setQuery($this->getDatabase()->getQuery(true)
|
||||
$this->getDatabase()->setQuery(
|
||||
$this->getDatabase()->getQuery(true)
|
||||
->insert('#__patchtester_pulls')
|
||||
->columns(['pull_id', 'title', 'description', 'pull_url',
|
||||
'is_rtc', 'is_npm', 'branch', 'is_draft'])
|
||||
->values($data));
|
||||
->columns([
|
||||
'pull_id',
|
||||
'title',
|
||||
'description',
|
||||
'pull_url',
|
||||
'is_rtc',
|
||||
'is_npm',
|
||||
'branch',
|
||||
'is_draft'
|
||||
])
|
||||
->values($data)
|
||||
);
|
||||
$this->getDatabase()->execute();
|
||||
} catch (RuntimeException $exception) {
|
||||
throw new RuntimeException(
|
||||
Text::sprintf('COM_PATCHTESTER_ERROR_INSERT_DATABASE', $exception->getMessage()),
|
||||
Text::sprintf(
|
||||
'COM_PATCHTESTER_ERROR_INSERT_DATABASE',
|
||||
$exception->getMessage()
|
||||
),
|
||||
$exception->getCode(),
|
||||
$exception
|
||||
);
|
||||
@ -401,10 +482,12 @@ class PullsModel extends ListModel
|
||||
|
||||
if ($labels) {
|
||||
try {
|
||||
$this->getDatabase()->setQuery($this->getDatabase()->getQuery(true)
|
||||
$this->getDatabase()->setQuery(
|
||||
$this->getDatabase()->getQuery(true)
|
||||
->insert('#__patchtester_pulls_labels')
|
||||
->columns(['pull_id', 'name', 'color'])
|
||||
->values($labels));
|
||||
->values($labels)
|
||||
);
|
||||
$this->getDatabase()->execute();
|
||||
} catch (RuntimeException $exception) {
|
||||
throw new RuntimeException(
|
||||
|
@ -11,6 +11,8 @@ namespace Joomla\Component\Patchtester\Administrator\Model;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
|
||||
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
|
||||
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
@ -19,7 +21,7 @@ namespace Joomla\Component\Patchtester\Administrator\Model;
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class TestsModel extends AbstractModel
|
||||
class TestsModel extends BaseDatabaseModel
|
||||
{
|
||||
/**
|
||||
* Retrieves a list of applied patches
|
||||
@ -30,7 +32,7 @@ class TestsModel extends AbstractModel
|
||||
*/
|
||||
public function getAppliedPatches(): array
|
||||
{
|
||||
$db = $this->getDb();
|
||||
$db = $this->getDatabase();
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->select('*')
|
||||
->from($db->quoteName('#__patchtester_tests'))
|
||||
@ -47,6 +49,6 @@ class TestsModel extends AbstractModel
|
||||
*/
|
||||
public function truncateTable(): void
|
||||
{
|
||||
$this->getDb()->truncateTable('#__patchtester_tests');
|
||||
$this->getDatabase()->truncateTable('#__patchtester_tests');
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Patch testing component for the Joomla! CMS
|
||||
*
|
||||
* @copyright Copyright (C) 2011 - 2012 Ian MacLennan, Copyright (C) 2013 - 2018 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later
|
||||
*/
|
||||
|
||||
namespace Joomla\Component\Patchtester\Administrator\View\Fetch;
|
||||
|
||||
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* View class for a list of pull requests.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class HtmlView extends BaseHtmlView
|
||||
{
|
||||
}
|
@ -132,9 +132,8 @@ class HtmlView extends BaseHtmlView
|
||||
ToolbarHelper::title(Text::_('COM_PATCHTESTER'), 'patchtester fas fa-save');
|
||||
if (!count($this->envErrors)) {
|
||||
$toolbar = Toolbar::getInstance('toolbar');
|
||||
$toolbar->appendButton('Popup', 'sync', 'COM_PATCHTESTER_TOOLBAR_FETCH_DATA', 'index.php?option=com_patchtester&view=fetch&tmpl=component', 500, 210, 0, 0, 'window.parent.location.reload()', Text::_('COM_PATCHTESTER_HEADING_FETCH_DATA'));
|
||||
// Add a reset button.
|
||||
$toolbar->appendButton('Standard', 'expired', 'COM_PATCHTESTER_TOOLBAR_RESET', 'reset', false);
|
||||
$toolbar->appendButton('Popup', 'sync', 'COM_PATCHTESTER_TOOLBAR_FETCH_DATA', 'index.php?option=com_patchtester&view=fetch&task=fetch&tmpl=component', 500, 210, 0, 0, 'window.parent.location.reload()', Text::_('COM_PATCHTESTER_HEADING_FETCH_DATA'));
|
||||
$toolbar->appendButton('Standard', 'expired', 'COM_PATCHTESTER_TOOLBAR_RESET', 'reset.reset', false);
|
||||
}
|
||||
|
||||
ToolbarHelper::preferences('com_patchtester');
|
||||
|
@ -15,8 +15,6 @@ use Joomla\CMS\Language\Text;
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/** @var \Joomla\Component\Patchtester\Administrator\View\DefaultHtmlView $this */
|
||||
|
||||
HTMLHelper::_('jquery.framework');
|
||||
HTMLHelper::_('behavior.core');
|
||||
HTMLHelper::_('script', 'com_patchtester/fetcher.js', ['version' => 'auto', 'relative' => true]);
|
||||
@ -30,5 +28,5 @@ Text::script('COM_PATCHTESTER_FETCH_AN_ERROR_HAS_OCCURRED');
|
||||
<div id="progress" class="progress">
|
||||
<div id="progress-bar" class="progress-bar progress-bar-striped progress-bar-animated bg-success" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" role="progressbar"></div>
|
||||
</div>
|
||||
<input id="patchtester-token" type="hidden" name="<?php echo Factory::getSession()->getFormToken(); ?>" value="1" />
|
||||
<input id="patchtester-token" type="hidden" name="<?php echo Factory::getApplication()->getSession()->getFormToken(); ?>" value="1" />
|
||||
</div>
|
@ -40,7 +40,7 @@ if (typeof Joomla === 'undefined') {
|
||||
jQuery.ajax({
|
||||
type: 'GET',
|
||||
url: path,
|
||||
data: 'task=' + task,
|
||||
data: `task=${task}.${task}`,
|
||||
dataType: 'json',
|
||||
success: function (response, textStatus, xhr) {
|
||||
try {
|
||||
|
@ -10,7 +10,7 @@ if (typeof Joomla === 'undefined') {
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function (event) {
|
||||
var submitPatch = document.querySelectorAll(".submitPatch");
|
||||
const submitPatch = document.querySelectorAll(".submitPatch");
|
||||
|
||||
/**
|
||||
* EventListener which listens on submitPatch Button,
|
||||
@ -21,9 +21,9 @@ document.addEventListener("DOMContentLoaded", function (event) {
|
||||
*/
|
||||
submitPatch.forEach(function (element) {
|
||||
element.addEventListener("click", function (event) {
|
||||
var currentTarget = event.currentTarget;
|
||||
var task = currentTarget.dataset.task
|
||||
var id = currentTarget.dataset.id
|
||||
const currentTarget = event.currentTarget;
|
||||
const task = `${currentTarget.dataset.task}.${currentTarget.dataset.task}`
|
||||
const id = parseInt(currentTarget.dataset.id)
|
||||
|
||||
PatchTester.submitpatch(task, id);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user