freescout/freescout-dist/app/Http/Controllers/ModulesController.php

571 lines
26 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Misc\WpApi;
use Illuminate\Http\Request;
//use Nwidart\Modules\Traits\CanClearModulesCache;
use Symfony\Component\Console\Output\BufferedOutput;
class ModulesController extends Controller
{
//use CanClearModulesCache;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Modules.
*/
public function modules(Request $request)
{
$installed_modules = [];
$modules_directory = [];
$third_party_modules = [];
$all_modules = [];
$flashes = [];
$updates_available = false;
$flash = \Cache::get('modules_flash');
if ($flash) {
if (is_array($flash) && !isset($flash['text'])) {
$flashes = $flash;
} else {
$flashes[] = $flash;
}
\Cache::forget('modules_flash');
}
// Get available modules and cache them
if (\Cache::has('modules_directory')) {
$modules_directory = \Cache::get('modules_directory');
}
if (!$modules_directory) {
$modules_directory = WpApi::getModules();
if ($modules_directory && is_array($modules_directory) && count($modules_directory)) {
\Cache::put('modules_directory', $modules_directory, now()->addMinutes(15));
}
}
// Get installed modules
\Module::clearCache();
$modules = \Module::all();
foreach ($modules as $module) {
$module_data = [
'alias' => $module->getAlias(),
'name' => $module->getName(),
'description' => $module->getDescription(),
'version' => $module->get('version'),
'detailsUrl' => $module->get('detailsUrl'),
'author' => $module->get('author'),
'authorUrl' => $module->get('authorUrl'),
'requiredAppVersion' => $module->get('requiredAppVersion'),
'requiredPhpExtensions' => $module->get('requiredPhpExtensions'),
'requiredPhpExtensionsMissing' => \App\Module::getMissingExtensions($module->get('requiredPhpExtensions')),
'requiredModulesMissing' => \App\Module::getMissingModules($module->get('requiredModules'), $modules),
'img' => $module->get('img'),
'active' => $module->active(), //\App\Module::isActive($module->getAlias()),
'installed' => true,
'activated' => \App\Module::isLicenseActivated($module->getAlias(), $module->get('authorUrl')),
'license' => \App\Module::getLicense($module->getAlias()),
// Determined later
'new_version' => '',
];
$module_data = \App\Module::formatModuleData($module_data);
$installed_modules[] = $module_data;
}
// No need, as we update modules list on each page load
// Clear modules cache if any module has been added or removed
// if (count($modules) != count(Module::getCached())) {
// $this->clearCache();
// }
// Prepare directory modules
if (is_array($modules_directory)) {
foreach ($modules_directory as $i_dir => $dir_module) {
$modules_directory[$i_dir] = \App\Module::formatModuleData($dir_module);
// Remove modules without aliases
if (empty($dir_module['alias'])) {
unset($modules_directory[$i_dir]);
}
$all_modules[$dir_module['alias']] = $dir_module['name'];
foreach ($installed_modules as $i_installed => $module) {
if ($dir_module['alias'] == $module['alias']) {
// Set image from director
$installed_modules[$i_installed]['img'] = $dir_module['img'];
// Remove installed modules from modules directory.
unset($modules_directory[$i_dir]);
// Detect if new version is available
if (!empty($dir_module['version']) && version_compare($dir_module['version'], $module['version'], '>')) {
$installed_modules[$i_installed]['new_version'] = $dir_module['version'];
$updates_available = true;
}
continue 2;
}
}
if (empty($dir_module['authorUrl']) || !\App\Module::isOfficial($dir_module['authorUrl'])) {
unset($modules_directory[$i_dir]);
continue;
}
if (!empty($dir_module['requiredPhpExtensions'])) {
$modules_directory[$i_dir]['requiredPhpExtensionsMissing'] = \App\Module::getMissingExtensions($dir_module['requiredPhpExtensions']);
}
$modules_directory[$i_dir]['active'] = \App\Module::isActive($dir_module['alias']);
$modules_directory[$i_dir]['activated'] = false;
// Do not show third-party modules in Modules Derectory.
if (\App\Module::isThirdParty($dir_module)) {
$third_party_modules[] = $modules_directory[$i_dir];
unset($modules_directory[$i_dir]);
}
}
} else {
$modules_directory = [];
}
// Check modules symlinks. Somestimes instead of symlinks folders with files appear.
$invalid_symlinks = \App\Module::checkSymlinks(
collect($installed_modules)->where('active', true)->pluck('alias')->toArray()
);
return view('modules/modules', [
'installed_modules' => $installed_modules,
'modules_directory' => $modules_directory,
'third_party_modules' => $third_party_modules,
'flashes' => $flashes,
'updates_available' => $updates_available,
'all_modules' => $all_modules,
'invalid_symlinks' => $invalid_symlinks,
]);
}
/**
* Ajax.
*/
public function ajax(Request $request)
{
$response = [
'status' => 'error',
'msg' => '', // this is error message
];
switch ($request->action) {
case 'install':
case 'activate_license':
$license = $request->license;
$alias = $request->alias;
if (!$license) {
$response['msg'] = __('Empty license key');
}
if (!$response['msg']) {
$params = [
'license' => $license,
'module_alias' => $alias,
'url' => \App\Module::getAppUrl(),
];
$result = WpApi::activateLicense($params);
if (WpApi::$lastError) {
$response['msg'] = WpApi::$lastError['message'];
} elseif (!empty($result['code']) && !empty($result['message'])) {
$response['msg'] = $result['message'];
} else {
if (!empty($result['status']) && $result['status'] == 'valid') {
if ($request->action == 'install') {
// Download and install module
$license_details = WpApi::getVersion($params);
if (WpApi::$lastError) {
$response['msg'] = WpApi::$lastError['message'];
} elseif (!empty($license_details['code']) && !empty($license_details['message'])) {
$response['msg'] = $license_details['message'];
} elseif (!empty($license_details['download_link'])) {
// Download module
$module_archive = \Module::getPath().DIRECTORY_SEPARATOR.$alias.'.zip';
try {
\Helper::downloadRemoteFile($license_details['download_link'], $module_archive);
} catch (\Exception $e) {
$response['msg'] = $e->getMessage();
}
$download_error = false;
if (!file_exists($module_archive)) {
$download_error = true;
} else {
// Extract
try {
\Helper::unzip($module_archive, \Module::getPath());
} catch (\Exception $e) {
$response['msg'] = $e->getMessage();
}
// Check if extracted module exists
\Module::clearCache();
$module = \Module::findByAlias($alias);
if (!$module) {
$download_error = true;
}
}
// Remove archive
if (file_exists($module_archive)) {
\File::delete($module_archive);
}
if (!$response['msg'] && !$download_error) {
// Activate license
\App\Module::activateLicense($alias, $license);
\Session::flash('flash_success_floating', __('Module successfully installed!'));
$response['status'] = 'success';
} elseif ($download_error) {
$response['reload'] = true;
if ($response['msg']) {
\Session::flash('flash_error_floating', $response['msg']);
}
\Session::flash('flash_error_unescaped', __('Error occurred downloading the module. Please :%a_being%download:%a_end% module manually and extract into :folder', ['%a_being%' => '<a href="'.$license_details['download_link'].'" target="_blank">', '%a_end%' => '</a>', 'folder' => '<strong>'.\Module::getPath().'</strong>']));
}
} else {
$response['msg'] = __('Error occurred. Please try again later.');
}
} else {
// Just activate license
\App\Module::activateLicense($alias, $license);
\Session::flash('flash_success_floating', __('License successfully activated!'));
$response['status'] = 'success';
}
} elseif (!empty($result['error'])) {
$response['msg'] = \App\Module::getErrorMessage($result['error'], $result);
} else {
$response['msg'] = __('Error occurred. Please try again later.');
}
}
}
break;
case 'activate':
$alias = $request->alias;
$module = \Module::findByAlias($alias);
if (!$module) {
$response['msg'] = __('Module not found').': '.$alias;
}
// Check license
if (!$response['msg']) {
if (!empty($module->get('authorUrl')) && $module->isOfficial()) {
$params = [
'license' => $module->getLicense(),
'module_alias' => $alias,
'url' => \App\Module::getAppUrl(),
];
$license_result = WpApi::checkLicense($params);
if (!empty($license_result['code']) && !empty($license_result['message'])) {
// Remove remembered license key and deactivate license in DB
\App\Module::deactivateLicense($alias, '');
$response['msg'] = $license_result['message'];
} elseif (!empty($license_result['status']) && $license_result['status'] != 'valid' && $license_result['status'] != 'inactive') {
// Remove remembered license key and deactivate license in DB
\App\Module::deactivateLicense($alias, '');
switch ($license_result['status']) {
case 'expired':
$response['msg'] = __('License key has expired');
break;
case 'disabled':
$response['msg'] = __('License key has been revoked');
break;
case 'inactive':
$response['msg'] = __('License key has not been activated yet');
case 'site_inactive':
$response['msg'] = __('No activations left for this license key').' ('.__("Use 'Deactivate License' link above to transfer license key from another domain").')';
break;
}
} elseif (!empty($license_result['status']) && $license_result['status'] == 'inactive') {
// Activate the license.
$result = WpApi::activateLicense($params);
if (WpApi::$lastError) {
$response['msg'] = WpApi::$lastError['message'];
} elseif (!empty($result['code']) && !empty($result['message'])) {
$response['msg'] = $result['message'];
} else {
if (!empty($result['status']) && $result['status'] == 'valid') {
// Success.
} elseif (!empty($result['error'])) {
$response['msg'] = \App\Module::getErrorMessage($result['error'], $result);
} else {
// Some unknown error. Do nothing.
}
}
}
}
}
if (!$response['msg']) {
\App\Module::setActive($alias, true);
$outputLog = new BufferedOutput();
\Artisan::call('freescout:module-install', ['module_alias' => $alias], $outputLog);
$output = $outputLog->fetch();
// Get module name
$name = '?';
if ($module) {
$name = $module->getName();
}
$type = 'danger';
$msg = __('Error occurred activating ":name" module', ['name' => $name]);
if (session('flashes_floating') && is_array(session('flashes_floating'))) {
// If there was any error, module has been deactivated via modules.register_error filter
$msg = '';
foreach (session('flashes_floating') as $flash) {
$msg .= $flash['text'].' ';
}
} elseif (strstr($output, 'Configuration cached successfully')) {
$type = 'success';
$msg = __('":name" module successfully activated!', ['name' => $name]);
} else {
// Deactivate the module.
\App\Module::setActive($alias, false);
\Artisan::call('freescout:clear-cache');
}
// Check public folder.
if ($module && file_exists($module->getPath().DIRECTORY_SEPARATOR.'Public')) {
$symlink_path = public_path().\Module::getPublicPath($alias);
if (!file_exists($symlink_path)) {
$type = 'danger';
$msg = 'Error occurred creating a module symlink ('.$symlink_path.'). Please check folder permissions.';
\App\Module::setActive($alias, false);
\Artisan::call('freescout:clear-cache');
}
}
if ($type == 'success') {
// Migrate again, in case migration did not work in the moment the module was activated.
\Artisan::call('migrate', ['--force' => true]);
}
// \Session::flash does not work after BufferedOutput
$flash = [
'text' => '<strong>'.$msg.'</strong><pre class="margin-top">'.$output.'</pre>',
'unescaped' => true,
'type' => $type,
];
\Cache::forever('modules_flash', $flash);
$response['status'] = 'success';
}
break;
case 'deactivate':
$alias = $request->alias;
\App\Module::setActive($alias, false);
$outputLog = new BufferedOutput();
\Artisan::call('freescout:clear-cache', [], $outputLog);
$output = $outputLog->fetch();
// Get module name
$module = \Module::findByAlias($alias);
$name = '?';
if ($module) {
$name = $module->getName();
}
$type = 'danger';
$msg = __('Error occurred deactivating :name module', ['name' => $name]);
if (strstr($output, 'Configuration cached successfully')) {
$type = 'success';
$msg = __('":name" module successfully Deactivated!', ['name' => $name]);
}
// \Session::flash does not work after BufferedOutput
$flash = [
'text' => '<strong>'.$msg.'</strong><pre class="margin-top">'.$output.'</pre>',
'unescaped' => true,
'type' => $type,
];
\Cache::forever('modules_flash', $flash);
$response['status'] = 'success';
break;
case 'deactivate_license':
$license = $request->license;
$alias = $request->alias;
if (!$license) {
$response['msg'] = __('Empty license key');
}
if (!$response['msg']) {
$params = [
'license' => $license,
'module_alias' => $alias,
'url' => (!empty($request->any_url) ? '*' : \App\Module::getAppUrl()),
];
$result = WpApi::deactivateLicense($params);
if (WpApi::$lastError) {
$response['msg'] = WpApi::$lastError['message'];
} elseif (!empty($result['code']) && !empty($result['message'])) {
$response['msg'] = $result['message'];
} else {
if (!empty($result['status']) && $result['status'] == 'success') {
$db_module = \App\Module::getByAlias($alias);
if ($db_module && trim($db_module->license ?? '') == trim($license ?? '')) {
// Remove remembered license key and deactivate license in DB
\App\Module::deactivateLicense($alias, '');
// Deactivate module
\App\Module::setActive($alias, false);
\Artisan::call('freescout:clear-cache', []);
}
// Flash does not work here.
$flash = [
'text' => '<strong>'.__('License successfully Deactivated!').'</strong>',
'unescaped' => true,
'type' => 'success',
];
\Cache::forever('modules_flash', $flash);
$response['status'] = 'success';
} elseif (!empty($result['error'])) {
$response['msg'] = \App\Module::getErrorMessage($result['error'], $result);
} else {
$response['msg'] = __('Error occurred. Please try again later.');
}
}
}
break;
case 'delete':
$alias = $request->alias;
$module = \Module::findByAlias($alias);
if ($module) {
//\App\Module::deactivateLicense($alias, $license);
$module->delete();
\Session::flash('flash_success_floating', __('Module deleted'));
} else {
$response['msg'] = __('Module not found').': '.$alias;
}
$response['status'] = 'success';
break;
case 'update':
$update_result = \App\Module::updateModule($request->alias);
if ($update_result['download_error']) {
$response['reload'] = true;
if ($update_result['msg']) {
\Session::flash('flash_error_floating', $update_result['msg']);
}
if ($update_result['download_msg']) {
\Session::flash('flash_error_unescaped', $update_result['download_msg']);
}
}
// Install updated module.
if ($update_result['output'] || $update_result['status']) {
$type = 'danger';
$msg = $update_result['msg'];
if ($update_result['status'] == 'success') {
$type = 'success';
$msg = $update_result['msg_success'];
}
// \Session::flash does not work after BufferedOutput
$flash = [
'text' => '<strong>'.$msg.'</strong><pre class="margin-top">'.$update_result['output'].'</pre>',
'unescaped' => true,
'type' => $type,
];
\Cache::forever('modules_flash', $flash);
$response['status'] = 'success';
}
break;
case 'update_all':
$update_all_flashes = [];
foreach ($request->aliases as $alias) {
$update_result = \App\Module::updateModule($alias);
$type = 'danger';
$msg = $update_result['msg'];
if ($update_result['status'] == 'success') {
$type = 'success';
$msg = $update_result['msg_success'];
} elseif ($update_result['download_msg']) {
$msg .= '<br/>'.$update_result['download_msg'];
}
$text = '<strong>'.$update_result['module_name'].':</strong> '.$msg;
if (trim($update_result['output'])) {
$text .= '<pre class="margin-top">'.$update_result['output'].'</pre>';
}
// \Session::flash does not work after BufferedOutput
$update_all_flashes[] = [
'text' => $text,
'unescaped' => true,
'type' => $type,
];
}
if ($update_all_flashes) {
\Cache::forever('modules_flash', $update_all_flashes);
}
$response['status'] = 'success';
break;
default:
$response['msg'] = 'Unknown action';
break;
}
if ($response['status'] == 'error' && empty($response['msg'])) {
$response['msg'] = 'Unknown error occurred';
}
return \Response::json($response);
}
}