727 lines
20 KiB
PHP
Raw Normal View History

2014-05-14 23:24:20 +10:00
<?php namespace System\Controllers;
use Str;
use Lang;
use File;
2014-05-14 23:24:20 +10:00
use Flash;
use Config;
2014-05-14 23:24:20 +10:00
use Backend;
use Redirect;
use Response;
2014-05-14 23:24:20 +10:00
use BackendMenu;
use Cms\Classes\ThemeManager;
2014-05-14 23:24:20 +10:00
use Backend\Classes\Controller;
use System\Models\Parameters;
use System\Models\PluginVersion;
2014-05-14 23:24:20 +10:00
use System\Classes\UpdateManager;
use System\Classes\PluginManager;
2014-07-27 15:07:22 +11:00
use System\Classes\SettingsManager;
2015-01-28 18:03:35 +11:00
use ApplicationException;
2014-05-14 23:24:20 +10:00
use Exception;
/**
* Updates controller
*
* @package october\system
* @author Alexey Bobkov, Samuel Georges
*
*/
class Updates extends Controller
{
public $implement = ['Backend.Behaviors.ListController'];
public $requiredPermissions = ['system.manage_updates'];
public $listConfig = ['list' => 'config_list.yaml', 'manage' => 'config_manage_list.yaml'];
2014-05-14 23:24:20 +10:00
public function __construct()
{
parent::__construct();
$this->addJs('/modules/system/assets/js/updates/updates.js', 'core');
$this->addCss('/modules/system/assets/css/updates/updates.css', 'core');
2014-05-14 23:24:20 +10:00
BackendMenu::setContext('October.System', 'system', 'updates');
2014-07-27 15:07:22 +11:00
SettingsManager::setContext('October.System', 'updates');
2014-05-14 23:24:20 +10:00
}
/**
* Index controller
2014-05-14 23:24:20 +10:00
*/
public function index()
{
$this->vars['coreBuild'] = Parameters::get('system::core.build');
$this->vars['projectId'] = Parameters::get('system::project.id');
$this->vars['projectName'] = Parameters::get('system::project.name');
$this->vars['projectOwner'] = Parameters::get('system::project.owner');
2015-02-11 06:22:58 +10:00
$this->vars['pluginsActiveCount'] = PluginVersion::isEnabled()->count();
2015-01-10 10:41:02 +11:00
$this->vars['pluginsCount'] = PluginVersion::count();
return $this->asExtension('ListController')->index();
2014-05-14 23:24:20 +10:00
}
/**
* Plugin manage controller
*/
public function manage()
{
$this->pageTitle = 'system::lang.plugins.manage';
PluginManager::instance()->clearDisabledCache();
return $this->asExtension('ListController')->index();
}
/**
* Install new plugins / themes
*/
public function install($tab = null)
{
if (get('search')) {
return Response::make($this->onSearchProducts());
}
try {
$this->bodyClass = 'compact-container breadcrumb-flush';
2015-05-04 13:29:48 +02:00
$this->pageTitle = 'system::lang.plugins.install_products';
$this->addJs('/modules/system/assets/js/updates/install.js', 'core');
$this->addCss('/modules/system/assets/css/updates/install.css', 'core');
$this->vars['activeTab'] = $tab ?: 'plugins';
$this->vars['installedPlugins'] = $this->getInstalledPlugins();
$this->vars['installedThemes'] = $this->getInstalledThemes();
}
catch (Exception $ex) {
$this->handleError($ex);
}
}
/**
* {@inheritDoc}
*/
public function listInjectRowClass($record, $definition = null)
{
2014-10-18 11:58:50 +02:00
if ($record->disabledByConfig) {
return 'hidden';
2014-10-18 11:58:50 +02:00
}
2014-10-18 11:58:50 +02:00
if ($record->orphaned || $record->is_disabled) {
return 'safe disabled';
2014-10-18 11:58:50 +02:00
}
2014-10-18 11:58:50 +02:00
if ($definition != 'manage') {
return;
2014-10-18 11:58:50 +02:00
}
2014-10-18 11:58:50 +02:00
if ($record->disabledBySystem) {
return 'negative';
2014-10-18 11:58:50 +02:00
}
return 'positive';
}
2014-05-14 23:24:20 +10:00
/**
* Runs a specific update step.
*/
public function onExecuteStep()
{
/*
* Address timeout limits
*/
@set_time_limit(3600);
2014-05-14 23:24:20 +10:00
$manager = UpdateManager::instance();
$stepCode = post('code');
switch ($stepCode) {
case 'downloadCore':
$manager->downloadCore(post('hash'));
break;
case 'extractCore':
$manager->extractCore(post('hash'), post('build'));
break;
case 'downloadPlugin':
$manager->downloadPlugin(post('name'), post('hash'));
break;
2014-07-24 20:07:52 +10:00
case 'downloadTheme':
$manager->downloadTheme(post('name'), post('hash'));
break;
2014-05-14 23:24:20 +10:00
case 'extractPlugin':
$manager->extractPlugin(post('name'), post('hash'));
break;
2014-07-24 20:07:52 +10:00
case 'extractTheme':
$manager->extractTheme(post('name'), post('hash'));
break;
2014-05-14 23:24:20 +10:00
case 'completeUpdate':
$manager->update();
Flash::success(Lang::get('system::lang.updates.update_success'));
return Redirect::refresh();
2014-05-14 23:24:20 +10:00
case 'completeInstall':
$manager->update();
Flash::success(Lang::get('system::lang.install.install_success'));
return Redirect::refresh();
2014-05-14 23:24:20 +10:00
}
}
//
// Updates
//
/**
* Spawns the update checker popup.
*/
public function onLoadUpdates()
{
return $this->makePartial('update_form');
}
/**
* Contacts the update server for a list of necessary updates.
*/
public function onCheckForUpdates()
{
try {
$manager = UpdateManager::instance();
$result = $manager->requestUpdateList();
$this->vars['core'] = array_get($result, 'core', false);
$this->vars['hasUpdates'] = array_get($result, 'hasUpdates', false);
$this->vars['pluginList'] = array_get($result, 'plugins', []);
$this->vars['themeList'] = array_get($result, 'themes', []);
}
catch (Exception $ex) {
2014-05-14 23:24:20 +10:00
$this->handleError($ex);
}
return ['#updateContainer' => $this->makePartial('update_list')];
}
/**
* Contacts the update server for a list of necessary updates.
*/
public function onForceUpdate()
{
try {
$manager = UpdateManager::instance();
$result = $manager->requestUpdateList(true);
$coreHash = array_get($result, 'core.hash', false);
$coreBuild = array_get($result, 'core.build', false);
$core = [$coreHash, $coreBuild];
$plugins = [];
$pluginList = array_get($result, 'plugins', []);
foreach ($pluginList as $code => $plugin) {
$plugins[$code] = array_get($plugin, 'hash', null);
}
2014-07-24 21:13:04 +10:00
$themes = [];
$themeList = array_get($result, 'themes', []);
foreach ($themeList as $code => $theme) {
$themes[$code] = array_get($theme, 'hash', null);
}
2014-05-14 23:24:20 +10:00
/*
* Update steps
*/
2014-07-24 21:13:04 +10:00
$updateSteps = $this->buildUpdateSteps($core, $plugins, $themes);
2014-05-14 23:24:20 +10:00
/*
* Finish up
*/
$updateSteps[] = [
'code' => 'completeUpdate',
'label' => Lang::get('system::lang.updates.update_completing'),
];
$this->vars['updateSteps'] = $updateSteps;
}
catch (Exception $ex) {
2014-05-14 23:24:20 +10:00
$this->handleError($ex);
}
return $this->makePartial('execute');
}
/**
* Converts the update data to an actionable array of steps.
*/
public function onApplyUpdates()
{
try {
$coreHash = post('hash');
$coreBuild = post('build');
$core = [$coreHash, $coreBuild];
2014-07-24 20:07:52 +10:00
$plugins = post('plugins', []);
2014-10-18 11:58:50 +02:00
if (!is_array($plugins)) {
$plugins = [];
}
2014-07-24 20:07:52 +10:00
$themes = post('themes', []);
2014-10-18 11:58:50 +02:00
if (!is_array($themes)) {
$themes = [];
}
2014-07-24 20:07:52 +10:00
2014-05-14 23:24:20 +10:00
/*
* Update steps
*/
2014-07-24 20:07:52 +10:00
$updateSteps = $this->buildUpdateSteps($core, $plugins, $themes);
2014-05-14 23:24:20 +10:00
/*
* Finish up
*/
$updateSteps[] = [
'code' => 'completeUpdate',
'label' => Lang::get('system::lang.updates.update_completing'),
];
$this->vars['updateSteps'] = $updateSteps;
}
catch (Exception $ex) {
2014-05-14 23:24:20 +10:00
$this->handleError($ex);
}
return $this->makePartial('execute');
}
2014-08-01 18:18:09 +10:00
protected function buildUpdateSteps($core, $plugins, $themes)
2014-05-14 23:24:20 +10:00
{
2014-10-18 11:58:50 +02:00
if (!is_array($core)) {
2014-07-24 20:07:52 +10:00
$core = [null, null];
2014-10-18 11:58:50 +02:00
}
2014-07-24 20:07:52 +10:00
2014-10-18 11:58:50 +02:00
if (!is_array($themes)) {
2014-07-24 20:07:52 +10:00
$themes = [];
2014-10-18 11:58:50 +02:00
}
2014-05-14 23:24:20 +10:00
2015-04-25 09:50:02 +10:00
if (!is_array($plugins)) {
$plugins = [];
}
2014-05-14 23:24:20 +10:00
$updateSteps = [];
list($coreHash, $coreBuild) = $core;
/*
* Download
*/
if ($coreHash) {
$updateSteps[] = [
'code' => 'downloadCore',
'label' => Lang::get('system::lang.updates.core_downloading'),
'hash' => $coreHash
];
}
2015-04-25 09:50:02 +10:00
foreach ($themes as $name => $hash) {
2014-05-14 23:24:20 +10:00
$updateSteps[] = [
2015-04-25 09:50:02 +10:00
'code' => 'downloadTheme',
'label' => Lang::get('system::lang.updates.theme_downloading', compact('name')),
2014-05-14 23:24:20 +10:00
'name' => $name,
'hash' => $hash
];
}
2015-04-25 09:50:02 +10:00
foreach ($plugins as $name => $hash) {
2014-07-24 20:07:52 +10:00
$updateSteps[] = [
2015-04-25 09:50:02 +10:00
'code' => 'downloadPlugin',
'label' => Lang::get('system::lang.updates.plugin_downloading', compact('name')),
2014-07-24 20:07:52 +10:00
'name' => $name,
'hash' => $hash
];
}
2014-05-14 23:24:20 +10:00
/*
* Extract
*/
if ($coreHash) {
$updateSteps[] = [
'code' => 'extractCore',
'label' => Lang::get('system::lang.updates.core_extracting'),
'hash' => $coreHash,
'build' => $coreBuild
];
}
2015-04-25 09:50:02 +10:00
foreach ($themes as $name => $hash) {
2014-05-14 23:24:20 +10:00
$updateSteps[] = [
2015-04-25 09:50:02 +10:00
'code' => 'extractTheme',
'label' => Lang::get('system::lang.updates.theme_extracting', compact('name')),
2014-05-14 23:24:20 +10:00
'name' => $name,
'hash' => $hash
];
}
2015-04-25 09:50:02 +10:00
foreach ($plugins as $name => $hash) {
2014-07-24 20:07:52 +10:00
$updateSteps[] = [
2015-04-25 09:50:02 +10:00
'code' => 'extractPlugin',
'label' => Lang::get('system::lang.updates.plugin_extracting', compact('name')),
2014-07-24 20:07:52 +10:00
'name' => $name,
'hash' => $hash
];
}
2014-05-14 23:24:20 +10:00
return $updateSteps;
}
//
// Bind to Project
//
/**
* Displays the form for entering a Project ID
*/
public function onLoadProjectForm()
{
return $this->makePartial('project_form');
}
/**
* Validate the project ID and execute the project installation
*/
public function onAttachProject()
{
try {
2015-03-14 11:31:49 +11:00
if (!$projectId = trim(post('project_id'))) {
2014-05-14 23:24:20 +10:00
throw new ApplicationException(Lang::get('system::lang.project.id.missing'));
2014-10-18 11:58:50 +02:00
}
2014-05-14 23:24:20 +10:00
$manager = UpdateManager::instance();
$result = $manager->requestProjectDetails($projectId);
Parameters::set([
'system::project.id' => $projectId,
'system::project.name' => $result['name'],
'system::project.owner' => $result['owner'],
]);
return $this->onForceUpdate();
}
catch (Exception $ex) {
2014-05-14 23:24:20 +10:00
$this->handleError($ex);
return $this->makePartial('project_form');
}
}
public function onDetachProject()
{
Parameters::set([
'system::project.id' => null,
'system::project.name' => null,
'system::project.owner' => null,
]);
Flash::success(Lang::get('system::lang.project.unbind_success'));
return Backend::redirect('system/updates');
2014-05-14 23:24:20 +10:00
}
//
// Plugin management
2014-05-14 23:24:20 +10:00
//
/**
* Validate the plugin code and execute the plugin installation
*/
public function onInstallPlugin()
{
try {
2015-03-14 11:31:49 +11:00
if (!$code = trim(post('code'))) {
2014-05-14 23:24:20 +10:00
throw new ApplicationException(Lang::get('system::lang.install.missing_plugin_name'));
2014-10-18 11:58:50 +02:00
}
2014-05-14 23:24:20 +10:00
$manager = UpdateManager::instance();
$result = $manager->requestPluginDetails($code);
2014-10-18 11:58:50 +02:00
if (!isset($result['code']) || !isset($result['hash'])) {
2014-05-14 23:24:20 +10:00
throw new ApplicationException(Lang::get('system::lang.server.response_invalid'));
2014-10-18 11:58:50 +02:00
}
2014-05-14 23:24:20 +10:00
$name = $result['code'];
$hash = $result['hash'];
$plugins = [$name => $hash];
/*
* Update steps
*/
$updateSteps = $this->buildUpdateSteps(null, $plugins, []);
2014-05-14 23:24:20 +10:00
/*
* Finish up
*/
$updateSteps[] = [
'code' => 'completeInstall',
'label' => Lang::get('system::lang.install.install_completing'),
];
$this->vars['updateSteps'] = $updateSteps;
return $this->makePartial('execute');
}
catch (Exception $ex) {
2014-05-14 23:24:20 +10:00
$this->handleError($ex);
return $this->makePartial('plugin_form');
}
}
/**
* Rollback and remove plugins from the system.
* @return void
*/
public function onRemovePlugins()
{
if (($checkedIds = post('checked')) && is_array($checkedIds) && count($checkedIds)) {
foreach ($checkedIds as $objectId) {
2014-10-18 11:58:50 +02:00
if (!$object = PluginVersion::find($objectId)) {
continue;
2014-10-18 11:58:50 +02:00
}
PluginManager::instance()->deletePlugin($object->code);
}
2014-06-05 22:05:56 +10:00
Flash::success(Lang::get('system::lang.plugins.remove_success'));
}
return $this->listRefresh('manage');
}
/**
* Rollback and remove a single plugin from the system.
* @return void
*/
public function onRemovePlugin()
{
if ($pluginCode = post('code')) {
PluginManager::instance()->deletePlugin($pluginCode);
Flash::success(Lang::get('system::lang.plugins.remove_success'));
}
return Redirect::refresh();
}
/**
* Rebuilds plugin database migrations.
* @return void
*/
public function onRefreshPlugins()
{
if (($checkedIds = post('checked')) && is_array($checkedIds) && count($checkedIds)) {
foreach ($checkedIds as $objectId) {
2014-10-18 11:58:50 +02:00
if (!$object = PluginVersion::find($objectId)) {
continue;
2014-10-18 11:58:50 +02:00
}
PluginManager::instance()->refreshPlugin($object->code);
}
2014-06-05 22:05:56 +10:00
Flash::success(Lang::get('system::lang.plugins.refresh_success'));
}
return $this->listRefresh('manage');
}
public function onLoadDisableForm()
{
try {
$this->vars['checked'] = post('checked');
}
catch (Exception $ex) {
$this->handleError($ex);
}
return $this->makePartial('disable_form');
}
public function onDisablePlugins()
{
$disable = post('disable', false);
if (($checkedIds = post('checked')) && is_array($checkedIds) && count($checkedIds)) {
$manager = PluginManager::instance();
foreach ($checkedIds as $objectId) {
2014-10-18 11:58:50 +02:00
if (!$object = PluginVersion::find($objectId)) {
continue;
2014-10-18 11:58:50 +02:00
}
2014-10-18 11:58:50 +02:00
if ($disable) {
$manager->disablePlugin($object->code, true);
}
else {
$manager->enablePlugin($object->code, true);
2014-10-18 11:58:50 +02:00
}
$object->is_disabled = $disable;
$object->save();
}
}
2014-10-18 11:58:50 +02:00
if ($disable) {
2014-06-05 22:05:56 +10:00
Flash::success(Lang::get('system::lang.plugins.disable_success'));
}
else {
2014-06-05 22:05:56 +10:00
Flash::success(Lang::get('system::lang.plugins.enable_success'));
2014-10-18 11:58:50 +02:00
}
return Backend::redirect('system/updates/manage');
}
//
// Theme management
//
/**
* Validate the theme code and execute the theme installation
*/
public function onInstallTheme()
{
try {
if (!$code = trim(post('code'))) {
throw new ApplicationException(Lang::get('system::lang.install.missing_theme_name'));
}
$manager = UpdateManager::instance();
$result = $manager->requestThemeDetails($code);
if (!isset($result['code']) || !isset($result['hash'])) {
throw new ApplicationException(Lang::get('system::lang.server.response_invalid'));
}
$name = $result['code'];
$hash = $result['hash'];
$themes = [$name => $hash];
$plugins = [];
2015-04-25 09:50:02 +10:00
foreach ((array) array_get($result, 'require') as $plugin) {
if (
($name = array_get($plugin, 'code')) &&
($hash = array_get($plugin, 'hash'))
) {
$plugins[$name] = $hash;
}
}
/*
* Update steps
*/
$updateSteps = $this->buildUpdateSteps(null, $plugins, $themes);
/*
* Finish up
*/
$updateSteps[] = [
'code' => 'completeInstall',
'label' => Lang::get('system::lang.install.install_completing'),
];
$this->vars['updateSteps'] = $updateSteps;
return $this->makePartial('execute');
}
catch (Exception $ex) {
$this->handleError($ex);
return $this->makePartial('theme_form');
}
}
/**
* Deletes a single theme from the system.
* @return void
*/
public function onRemoveTheme()
{
if ($themeCode = post('code')) {
ThemeManager::instance()->deleteTheme($themeCode);
Flash::success(trans('cms::lang.theme.delete_theme_success'));
}
return Redirect::refresh();
}
//
// Product install
//
2015-04-04 17:31:39 +11:00
public function onSearchProducts()
{
$searchType = get('search', 'plugins');
$serverUri = $searchType == 'plugins' ? 'plugin/search' : 'theme/search';
$manager = UpdateManager::instance();
return $manager->requestServerData($serverUri, ['query' => get('query')]);
}
2015-04-04 17:31:39 +11:00
public function onGetPopularPlugins()
{
$installed = $this->getInstalledPlugins();
$popular = UpdateManager::instance()->requestPopularProducts('plugin');
$popular = $this->filterPopularProducts($popular, $installed);
return ['result' => $popular];
}
2015-04-04 17:31:39 +11:00
public function onGetPopularThemes()
{
$installed = $this->getInstalledThemes();
$popular = UpdateManager::instance()->requestPopularProducts('theme');
$popular = $this->filterPopularProducts($popular, $installed);
return ['result' => $popular];
}
protected function getInstalledPlugins()
{
$installed = PluginVersion::lists('code');
$manager = UpdateManager::instance();
return $manager->requestProductDetails($installed, 'plugin');
}
protected function getInstalledThemes()
{
$history = Parameters::get('system::theme.history', []);
$manager = UpdateManager::instance();
$installed = $manager->requestProductDetails(array_keys($history), 'theme');
/*
* Splice in the directory names
*/
foreach ($installed as $key => $data) {
$code = array_get($data, 'code');
$installed[$key]['dirName'] = array_get($history, $code, $code);
}
return $installed;
}
/*
* Remove installed products from the collection
*/
protected function filterPopularProducts($popular, $installed)
{
$installedArray = [];
foreach ($installed as $product) {
$installedArray[] = array_get($product, 'code', -1);
}
foreach ($popular as $key => $product) {
$code = array_get($product, 'code');
if (in_array($code, $installedArray)) {
unset($popular[$key]);
}
}
return array_values($popular);
}
2014-10-18 11:58:50 +02:00
}