417 lines
15 KiB
PHP
417 lines
15 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Classes\Module;
|
|
use App\Models\AddOn;
|
|
use App\Models\Plan;
|
|
use App\Models\UserActiveModule;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Inertia\Inertia;
|
|
use ZipArchive;
|
|
|
|
class ModuleController extends Controller
|
|
{
|
|
public function index()
|
|
{
|
|
if (Auth::user()->can('manage-add-on')) {
|
|
$modules = $this->getAllModules();
|
|
|
|
return Inertia::render('modules/index', [
|
|
'modules' => $modules,
|
|
]);
|
|
} else {
|
|
return back()->with('error', __('Permission denied'));
|
|
}
|
|
}
|
|
|
|
public function upload()
|
|
{
|
|
if (Auth::user()->can('manage-add-on')) {
|
|
return Inertia::render('modules/upload');
|
|
} else {
|
|
return back()->with('error', __('Permission denied'));
|
|
}
|
|
}
|
|
|
|
public function enable(Request $request)
|
|
{
|
|
$module = (new Module())->find($request->name);
|
|
if (!empty($module)) {
|
|
|
|
\App::setLocale('en');
|
|
|
|
if ($module->isEnabled()) {
|
|
$check_child_module = $this->Check_Child_Module($module);
|
|
if ($check_child_module == true) {
|
|
$module = (new Module())->find($request->name);
|
|
$module->disable();
|
|
return redirect()->back()->with('success', __('Module Disable Successfully!'));
|
|
} else {
|
|
return redirect()->back()->with('error', __($check_child_module['msg']));
|
|
}
|
|
} else {
|
|
$addon = AddOn::where('module', $request->name)->first();
|
|
if (empty($addon)) {
|
|
Artisan::call('migrate --path=/packages/workdo/' . $request->name . '/src/Database/Migrations --force');
|
|
Artisan::call('package:seed ' . $request->name);
|
|
|
|
$filePath = base_path('packages/workdo/' . $request->name . '/module.json');
|
|
$jsonContent = file_get_contents($filePath);
|
|
$data = json_decode($jsonContent, true);
|
|
|
|
|
|
$addon = new AddOn;
|
|
$addon->module = $data['name'];
|
|
$addon->name = $data['alias'];
|
|
$addon->monthly_price = $data['monthly_price'] ?? 0;
|
|
$addon->yearly_price = $data['yearly_price'] ?? 0;
|
|
$addon->package_name = $data['package_name'];
|
|
$addon->for_admin = $data['for_admin'] ?? false;
|
|
$addon->priority = $data['priority'] ?? 0;
|
|
$addon->save();
|
|
}
|
|
(new Module())->moduleCacheForget($request->name);
|
|
$module = (new Module())->find($request->name);
|
|
$check_parent_module = $this->Check_Parent_Module($module);
|
|
if ($check_parent_module['status'] == true) {
|
|
Artisan::call('migrate --path=/packages/workdo/' . $request->name . '/src/Database/Migrations --force');
|
|
Artisan::call('package:seed ' . $request->name);
|
|
$module = (new Module())->find($request->name);
|
|
$module->enable();
|
|
return redirect()->back()->with('success', __('Module Enable Successfully!'));
|
|
} else {
|
|
return redirect()->back()->with('error', __($check_parent_module['msg']));
|
|
}
|
|
}
|
|
} else {
|
|
return redirect()->back()->with('error', __('oops something wren wrong!'));
|
|
}
|
|
}
|
|
|
|
public function Check_Parent_Module($module)
|
|
{
|
|
$path = $module->getPath() . '/module.json';
|
|
$json = json_decode(file_get_contents($path), true);
|
|
$data['status'] = true;
|
|
$data['msg'] = '';
|
|
|
|
if (isset($json['parent_module']) && !empty($json['parent_module'])) {
|
|
foreach ($json['parent_module'] as $key => $value) {
|
|
$modules = implode(',', $json['parent_module']);
|
|
$parent_module = module_is_active($value);
|
|
if ($parent_module == true) {
|
|
$module = (new Module())->find($value);
|
|
if ($module) {
|
|
$this->Check_Parent_Module($module);
|
|
}
|
|
} else {
|
|
$data['status'] = false;
|
|
$data['msg'] = 'please activate this module ' . $modules;
|
|
return $data;
|
|
}
|
|
}
|
|
return $data;
|
|
} else {
|
|
return $data;
|
|
}
|
|
}
|
|
public function Check_Child_Module($module)
|
|
{
|
|
$path = $module->getPath() . '/module.json';
|
|
$json = json_decode(file_get_contents($path), true);
|
|
if (isset($json['child_module']) && !empty($json['child_module'])) {
|
|
foreach ($json['child_module'] as $key => $value) {
|
|
$child_module = module_is_active($value);
|
|
if ($child_module == true) {
|
|
$module = (new Module())->find($value);
|
|
$module->disable();
|
|
if ($module) {
|
|
$this->Check_Child_Module($module);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private function getAllModules()
|
|
{
|
|
$modules = [];
|
|
$packagesPath = base_path('packages/workdo');
|
|
|
|
if (!File::exists($packagesPath)) {
|
|
return $modules;
|
|
}
|
|
|
|
$directories = File::directories($packagesPath);
|
|
|
|
foreach ($directories as $directory) {
|
|
$moduleName = basename($directory);
|
|
$moduleJsonPath = "{$directory}/module.json";
|
|
|
|
if (File::exists($moduleJsonPath)) {
|
|
$moduleData = json_decode(File::get($moduleJsonPath), true);
|
|
|
|
if ($moduleData) {
|
|
$addon = AddOn::where('module', $moduleData['name'])->first();
|
|
$modules[] = [
|
|
'name' => $moduleData['name'] ?? $moduleName,
|
|
'alias' => $addon ? $addon->name :$moduleData['alias'],
|
|
'description' => $moduleData['description'] ?? '',
|
|
'version' => $moduleData['version'] ?? '1.0.0',
|
|
'image' => url('/packages/workdo/' . $moduleName . '/favicon.png'),
|
|
'is_enabled' => $addon ? $addon->is_enable : false,
|
|
'package_name' => $moduleData['package_name'] ?? null,
|
|
'display' => $moduleData['display'] ?? true,
|
|
'priority' => $moduleData['priority'] ?? 10,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
usort($modules, function ($a, $b) {
|
|
return $a['priority'] - $b['priority'];
|
|
});
|
|
|
|
return $modules;
|
|
}
|
|
|
|
public function install(Request $request)
|
|
{
|
|
if (!Auth::user()->can('manage-add-on')) {
|
|
return back()->with('error', __('Permission denied'));
|
|
}
|
|
|
|
$request->validate([
|
|
'files' => 'required|array',
|
|
'files.*' => 'required|file|mimes:zip|max:51200'
|
|
], [
|
|
'files.required' => __('Please select at least one file to upload.'),
|
|
'files.array' => __('Files must be provided as an array.'),
|
|
'files.*.required' => __('Each file is required.'),
|
|
'files.*.file' => __('Each item must be a valid file.'),
|
|
'files.*.mimes' => __('Only ZIP files are allowed.'),
|
|
'files.*.max' => __('File size cannot exceed 50MB.'),
|
|
]);
|
|
|
|
$results = [];
|
|
$successCount = 0;
|
|
$errorCount = 0;
|
|
|
|
foreach ($request->file('files') as $file) {
|
|
try {
|
|
$result = $this->installSinglePackage($file);
|
|
$results[] = $result;
|
|
if ($result['success']) {
|
|
$successCount++;
|
|
} else {
|
|
$errorCount++;
|
|
}
|
|
} catch (\Exception $e) {
|
|
$results[] = [
|
|
'success' => false,
|
|
'filename' => $file->getClientOriginalName(),
|
|
'message' => $e->getMessage()
|
|
];
|
|
$errorCount++;
|
|
}
|
|
}
|
|
|
|
$message = "Installed {$successCount} packages successfully";
|
|
if ($errorCount > 0) {
|
|
$message .= ", {$errorCount} failed";
|
|
}
|
|
|
|
return back()->with($errorCount > 0 ? 'warning' : 'success', $message);
|
|
}
|
|
|
|
private function installSinglePackage($file)
|
|
{
|
|
$zip = new ZipArchive;
|
|
$fileName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
|
|
|
|
$res = $zip->open($file->getPathname());
|
|
if ($res !== TRUE) {
|
|
throw new \Exception("Unable to open ZIP file: {$file->getClientOriginalName()}");
|
|
}
|
|
|
|
$tempPath = base_path('packages/workdo/tmp_' . uniqid());
|
|
$zip->extractTo($tempPath);
|
|
$zip->close();
|
|
|
|
$rootFolder = array_diff(scandir($tempPath), ['.', '..']);
|
|
if (empty($rootFolder)) {
|
|
$this->deleteDirectory($tempPath);
|
|
throw new \Exception("Invalid ZIP structure: {$file->getClientOriginalName()}");
|
|
}
|
|
|
|
$rootFolderName = array_values($rootFolder)[0];
|
|
$moduleJsonPath = $tempPath . '/' . $rootFolderName . '/module.json';
|
|
|
|
if (!file_exists($moduleJsonPath)) {
|
|
$this->deleteDirectory($tempPath);
|
|
throw new \Exception("module.json not found: {$file->getClientOriginalName()}");
|
|
}
|
|
|
|
$moduleData = json_decode(file_get_contents($moduleJsonPath), true);
|
|
if (!$moduleData) {
|
|
$this->deleteDirectory($tempPath);
|
|
throw new \Exception("Invalid module.json: {$file->getClientOriginalName()}");
|
|
}
|
|
|
|
$extractPath = base_path('packages/workdo/' . $moduleData['name']);
|
|
$this->createDirectory($extractPath);
|
|
$this->moveExtractedFiles($tempPath . '/' . $rootFolderName, $extractPath);
|
|
$this->deleteDirectory($tempPath);
|
|
$this->setPermissions($extractPath);
|
|
|
|
$addon = AddOn::where('module', $moduleData['name'])->first();
|
|
if (!$addon) {
|
|
$addon = new AddOn;
|
|
$addon->module = $moduleData['name'];
|
|
$addon->name = $moduleData['alias'];
|
|
$addon->monthly_price = $moduleData['monthly_price'] ?? 0;
|
|
$addon->yearly_price = $moduleData['yearly_price'] ?? 0;
|
|
$addon->is_enable = false;
|
|
$addon->package_name = $moduleData['package_name'] ?? null;
|
|
$addon->for_admin = $moduleData['for_admin'] ?? null;
|
|
$addon->priority = $moduleData['priority'] ?? 0;
|
|
$addon->save();
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'filename' => $file->getClientOriginalName(),
|
|
'message' => "Installed successfully"
|
|
];
|
|
}
|
|
|
|
private function createDirectory($path)
|
|
{
|
|
if (!is_dir($path)) {
|
|
mkdir($path, 0777, true);
|
|
$this->setPermissions($path);
|
|
} else {
|
|
$this->setPermissions($path);
|
|
}
|
|
}
|
|
|
|
// Set directory permissions
|
|
private function setPermissions($path)
|
|
{
|
|
if (function_exists('chmod')) {
|
|
@chmod($path, 0777); // Set permissions if possible
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move files from one directory to another.
|
|
*
|
|
* @param string $source
|
|
* @param string $destination
|
|
*/
|
|
private function moveExtractedFiles($source, $destination, $filename = null)
|
|
{
|
|
// Adjust the source directory if a root folder (e.g., $filename) exists in the zip
|
|
if ($filename) {
|
|
$source = $source . DIRECTORY_SEPARATOR . $filename;
|
|
}
|
|
|
|
$files = array_diff(scandir($source), ['.', '..']);
|
|
foreach ($files as $file) {
|
|
$srcPath = $source . DIRECTORY_SEPARATOR . $file;
|
|
$destPath = $destination . DIRECTORY_SEPARATOR . $file;
|
|
|
|
if (is_dir($srcPath)) {
|
|
// Recursively move subdirectories
|
|
if (!is_dir($destPath)) {
|
|
mkdir($destPath, 0777, true);
|
|
}
|
|
// Check if chmod exists
|
|
if (function_exists('chmod')) {
|
|
@chmod($destPath, 0777); // Set permissions if possible
|
|
}
|
|
$this->moveExtractedFiles($srcPath, $destPath);
|
|
} else {
|
|
// Move file
|
|
rename($srcPath, $destPath);
|
|
// Check if chmod exists
|
|
if (function_exists('chmod')) {
|
|
@chmod($destPath, 0777); // Set permissions if possible
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a directory and its contents.
|
|
*
|
|
* @param string $dirPath
|
|
* @return bool
|
|
*/
|
|
private function deleteDirectory($dirPath)
|
|
{
|
|
if (!is_dir($dirPath)) {
|
|
return false;
|
|
}
|
|
|
|
$items = array_diff(scandir($dirPath), ['.', '..']);
|
|
foreach ($items as $item) {
|
|
$path = $dirPath . DIRECTORY_SEPARATOR . $item;
|
|
is_dir($path) ? $this->deleteDirectory($path) : unlink($path);
|
|
}
|
|
|
|
return rmdir($dirPath);
|
|
}
|
|
|
|
public function getUserActiveModules()
|
|
{
|
|
$user = Auth::user();
|
|
|
|
$oldplan = $user && $user->active_plan ? Plan::where('id', $user->active_plan)->first() : null;
|
|
$plan = Plan::find($oldplan->id);
|
|
if ($plan->custom_plan == 0) {
|
|
$modules = [];
|
|
} else {
|
|
$userActiveModules = UserActiveModule::where('user_id', Auth::id())->get();
|
|
|
|
$modules = [];
|
|
foreach ($userActiveModules as $userModule) {
|
|
$addon = AddOn::where('module', $userModule->module)->first();
|
|
if ($addon) {
|
|
$modules[] = [
|
|
'module' => $userModule->module,
|
|
'alias' => $addon->name,
|
|
'image' => url('/packages/workdo/' . $userModule->module . '/favicon.png'),
|
|
'monthly_price' => $addon->monthly_price,
|
|
'yearly_price' => $addon->yearly_price
|
|
];
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'modules' => $modules
|
|
]);
|
|
}
|
|
}
|
|
|
|
public function removeUserActiveModule($moduleId)
|
|
{
|
|
$deleted = UserActiveModule::where('user_id', Auth::id())
|
|
->where('module', $moduleId)
|
|
->delete();
|
|
|
|
return response()->json([
|
|
'success' => $deleted > 0,
|
|
'message' => $deleted > 0 ? 'Module removed successfully' : 'Module not found'
|
|
]);
|
|
}
|
|
}
|