<?php
namespace App\Http\Controllers;
use App\Misc\WpApi;
use Illuminate\Http\Request;
use Symfony\Component\Console\Output\BufferedOutput;
class ModulesController extends Controller
{
* 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');
}
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));
}
}
\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(),
'installed' => true,
'activated' => \App\Module::isLicenseActivated($module->getAlias(), $module->get('authorUrl')),
'license' => \App\Module::getLicense($module->getAlias()),
'latestVersionNumberUrl' => $module->get('latestVersionUrl'),
'latestVersionZipUrl' => $module->get('latestVersionZipUrl'),
'new_version' => '',
];
$module_data = \App\Module::formatModuleData($module_data);
$installed_modules[] = $module_data;
}
if (is_array($modules_directory)) {
foreach ($modules_directory as $i_dir => $dir_module) {
$dir_module = \App\Module::formatModuleData($dir_module);
$modules_directory[$i_dir] = $dir_module;
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']) {
$installed_modules[$i_installed]['img'] = $dir_module['img'];
$installed_modules[$i_installed] = \App\Module::formatModuleData($installed_modules[$i_installed]);
unset($modules_directory[$i_dir]);
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['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;
if (\App\Module::isThirdParty($dir_module)) {
$third_party_modules[] = $modules_directory[$i_dir];
unset($modules_directory[$i_dir]);
} elseif (empty($dir_module['authorUrl']) || !\App\Module::isOfficial($dir_module['authorUrl'])) {
unset($modules_directory[$i_dir]);
continue;
}
}
} else {
$modules_directory = [];
}
foreach ($installed_modules as $i_installed => $module) {
if (\App\Module::isOfficial($module['authorUrl'])) {
continue;
}
$latest_version_number_url = $module['latestVersionNumberUrl'] ?? null;
if (! $latest_version_number_url) {
continue;
}
$client = new \GuzzleHttp\Client();
try {
$response = $client->request('GET', $latest_version_number_url, \Helper::setGuzzleDefaultOptions());
$latest_version = trim((string) $response->getBody());
if (empty($latest_version)) {
continue;
}
preg_match('#"version":[^"]*"([\d\.]+)"#', $latest_version, $m);
if (!empty($m[1])) {
$latest_version = $m[1];
}
$current_version = $module['version'];
} catch (\Exception $e) {
continue;
}
if (version_compare($latest_version, $current_version, '>')) {
$installed_modules[ $i_installed ]['new_version'] = $latest_version;
$updates_available = true;
}
}
$invalid_symlinks = \App\Module::checkSymlinks(
collect($installed_modules)->where('active', true)->pluck('alias')->toArray()
);
asort($all_modules);
usort($third_party_modules, function ($a, $b) {
return strcasecmp($a['name'], $b['name']);
});
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' => '',
];
switch ($request->action) {
case 'install':
case 'activate_license':
$license = $request->license;
$alias = preg_replace("#[^a-zA-Z0-9_\-]#", '', $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') {
$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'])) {
$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 {
try {
\Helper::unzip($module_archive, \Module::getPath());
} catch (\Exception $e) {
$response['msg'] = $e->getMessage();
}
\Module::clearCache();
$module = \Module::findByAlias($alias);
if (!$module) {
$download_error = true;
}
}
if (file_exists($module_archive)) {
\File::delete($module_archive);
}
if (!$response['msg'] && !$download_error) {
\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 {
\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.');
}
}
}
if (!empty($response['msg'])) {
break;
}
case 'activate':
$alias = $request->alias;
$module = \Module::findByAlias($alias);
if (!$module) {
$response['msg'] = __('Module not found').': '.$alias;
}
if (!$response['msg']) {
if (!empty($module->get('authorUrl')) && $module->isOfficial()) {
$params = [
'license' => $request->license ?: $module->getLicense(),
'module_alias' => $alias,
'url' => \App\Module::getAppUrl(),
];
$license_result = WpApi::checkLicense($params);
if (!empty($license_result['code']) && !empty($license_result['message'])) {
\App\Module::deactivateLicense($alias, '');
$response['msg'] = $license_result['message'];
} elseif (!empty($license_result['status']) && $license_result['status'] != 'valid' && $license_result['status'] != 'inactive') {
\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');
break;
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') {
$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') {
} elseif (!empty($result['error'])) {
$response['msg'] = \App\Module::getErrorMessage($result['error'], $result);
} else {
}
}
}
} else {
try {
$old_path = '';
$correct_path = '';
foreach (\Module::getScanPaths() as $key => $path) {
$manifests = \Module::getFiles()->glob("{$path}/module.json");
is_array($manifests) || $manifests = [];
foreach ($manifests as $manifest) {
$manifest_json = \Nwidart\Modules\Json::make($manifest);
if ($manifest_json->get('alias') != $alias) {
continue;
}
$providers = $manifest_json->get('providers');
$correct_folder_name = $providers[0] ?? '';
$correct_folder_name = preg_replace('#^Modules\\\\([^\\\\]+)\\\\.*#', '$1', $correct_folder_name);
if (!$correct_folder_name) {
break;
}
$old_path = str_replace('/module.json', '', $manifest);
$correct_path = \Module::getPath().'/'.$correct_folder_name;
if (\File::exists($old_path) && !\File::exists($correct_path)) {
\File::move($old_path, $correct_path);
\Module::scan();
}
break;
}
}
} catch (\Exception $e) {
if ($old_path && $correct_path) {
$response['msg'] = __('Rename ":old_path" into ":new_path"', [
'old_path' => str_replace(\Module::getPath(), '/Modules', $old_path),
'new_path' => str_replace(\Module::getPath(), '/Modules', $correct_path),
]);
} else {
\Helper::logException($e, '[Modules] Error occured checking module folder name on activation');
}
}
}
}
if (!$response['msg']) {
\App\Module::setActive($alias, true);
$user_locale = app()->getLocale();
$outputLog = new BufferedOutput();
\Artisan::call('freescout:module-install', ['module_alias' => $alias], $outputLog);
$output = $outputLog->fetch();
$name = '?';
if ($module) {
$name = $module->getName();
}
\Helper::setUserLocale($user_locale);
$type = 'danger';
$msg = __('Error occurred activating ":name" module', ['name' => $name]);
if (session('flashes_floating') && is_array(session('flashes_floating'))) {
$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 {
\App\Module::setActive($alias, false);
\Artisan::call('freescout:clear-cache');
}
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') {
\Artisan::call('migrate', ['--force' => true]);
}
$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);
$user_locale = app()->getLocale();
$outputLog = new BufferedOutput();
\Artisan::call('freescout:clear-cache', [], $outputLog);
$output = $outputLog->fetch();
$module = \Module::findByAlias($alias);
$name = '?';
if ($module) {
$name = $module->getName();
}
\Helper::setUserLocale($user_locale);
$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]);
}
$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 ?? '')) {
\App\Module::deactivateLicense($alias, '');
\App\Module::setActive($alias, false);
\Artisan::call('freescout:clear-cache', []);
}
$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);
} elseif (!empty($result['status']) && $result['status'] == 'error') {
$response['msg'] = __('License key does not exist');
} else {
$response['msg'] = __('Error occurred. Please try again later.');
}
}
}
break;
case 'delete':
$alias = $request->alias;
$module = \Module::findByAlias($alias);
if ($module) {
$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']);
}
}
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'];
}
$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>';
}
$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);
}
}