HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/be.naniguide.com/app/Http/Controllers/Refactor/Admin/PluginController.php
<?php

namespace App\Http\Controllers\Refactor\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Model\Plugin;

class PluginController extends Controller
{
    public function index(Request $request)
    {
        if (!$request->user()->admin->can('read', new Plugin())) {
            return $this->notAuthorized();
        }

        return view('refactor.pages.admin.plugins.index');
    }

    public function listing(Request $request)
    {
        if (!$request->user()->admin->can('read', new Plugin())) {
            return $this->notAuthorized();
        }

        if (!$request->user()->admin->can('readAll', new Plugin())) {
            $request->merge(['admin_id' => $request->user()->admin->id]);
        }

        $request->merge(['no_customer' => true]);

        // Two possible statuses for a plugin from the user's point of view:
        //   - active   = running fine (DB status='active' AND not errored at runtime)
        //   - inactive = not running  (DB status='inactive' OR errored at runtime)
        // Errors (missing folder, broken composer.json, recorded in master file) live
        // outside the DB, so we resolve them once here and pass into the query.
        $erroredNames = Plugin::getErroredPluginNames();
        $request->merge(['_errored_names' => $erroredNames]);

        $query = Plugin::search($request);

        if ($request->status === 'active') {
            $query->where('plugins.status', 'active');
            if (!empty($erroredNames)) {
                $query->whereNotIn('plugins.name', $erroredNames);
            }
        } elseif ($request->status === 'inactive') {
            $query->where(function ($q) use ($erroredNames) {
                $q->where('plugins.status', 'inactive');
                if (!empty($erroredNames)) {
                    $q->orWhereIn('plugins.name', $erroredNames);
                }
            });
        }

        // Per-page rule: grid view = 16 (4×4 desktop, full rows at all breakpoints).
        $defaultPerPage = $request->view === 'grid' ? 16 : 15;
        $plugins = $query->paginate($request->per_page ?? $defaultPerPage);

        // Pre-check plugins for errors and setting URLs
        $settingUrls = [];
        $blacklist = [];
        foreach ($plugins as $plugin) {
            $error = $plugin->getPluginInfo('error');
            if (!is_null($error)) {
                $blacklist[$plugin->name] = 'Cannot load plugin: ' . $error;
                continue;
            }

            try {
                $composerJson = $plugin->getComposerJson();
                if (array_key_exists('extra', $composerJson) && array_key_exists('setting-route', $composerJson['extra'])) {
                    // Settings link is always shown when the plugin declares a
                    // setting-route — some plugins (e.g., acelle/awswhitelabel)
                    // require configuration BEFORE activation can succeed, so
                    // hiding Settings on inactive plugins would brick the flow.
                    // Plugins that want Settings to be reachable only after
                    // activation gate their own routes with a "<plugin>.active"
                    // middleware (e.g., console.active).
                    $settingUrls[$plugin->name] = action($composerJson['extra']['setting-route']);
                }
            } catch (\Exception $ex) {
                $blacklist[$plugin->name] = 'Plugin error: ' . $ex->getMessage();
            }
        }

        $view = $request->view === 'grid' ? '_grid' : '_list';

        return view('refactor.pages.admin.plugins.' . $view, compact('plugins', 'settingUrls', 'blacklist'));
    }

    public function install(Request $request)
    {
        if (!$request->user()->admin->can('install', Plugin::class)) {
            return $this->notAuthorized();
        }

        if ($request->isMethod('post')) {
            // Catch at the controller boundary so the real reason
            // surfaces in the install dialog (the JS reads `data.message`).
            // Without this, an SQL/file/composer failure bubbles up as
            // Laravel's generic "Server Error" and the admin has nothing
            // to grep for. Past incident 2026-05-15 (AppK / snapvalid):
            // `plugins.description` was varchar(191), composer description
            // ran 280 chars → SQLSTATE 22001 swallowed → UI showed
            // "Server Error" with zero hint.
            try {
                $pluginName = Plugin::upload($request);
                Plugin::register($pluginName);
            } catch (\Throwable $ex) {
                \Log::error('Plugin install failed: ' . $ex->getMessage(), ['exception' => $ex]);

                return response()->json([
                    'status' => 'error',
                    'message' => $ex->getMessage(),
                ], 500);
            }

            return response()->json([
                'status' => 'success',
                'message' => trans('refactor/admin_plugins.flash.installed'),
            ]);
        }

        return view('refactor.pages.admin.plugins.install');
    }

    public function delete(Request $request)
    {
        $plugin = Plugin::findByUid($request->uid);

        if (!$plugin || !$request->user()->admin->can('delete', $plugin)) {
            return response()->json(['status' => 'error', 'message' => 'Not authorized'], 403);
        }

        try {
            $plugin->deleteAndCleanup();
        } catch (\Exception $ex) {
            return response()->json(['status' => 'error', 'message' => $ex->getMessage()], 422);
        }

        return response()->json([
            'status' => 'success',
            'message' => trans('refactor/admin_plugins.flash.deleted'),
        ]);
    }

    public function enable(Request $request)
    {
        $plugins = Plugin::whereIn('uid', is_array($request->uids) ? $request->uids : explode(',', $request->uids));

        try {
            foreach ($plugins->get() as $plugin) {
                if ($request->user()->admin->can('enable', $plugin)) {
                    $plugin->activate();
                }
            }
        } catch (\Exception $ex) {
            return response()->json(['status' => 'error', 'message' => $ex->getMessage()], 422);
        }

        return response()->json(['status' => 'success', 'message' => trans('refactor/admin_plugins.flash.enabled')]);
    }

    public function disable(Request $request)
    {
        $plugins = Plugin::whereIn('uid', is_array($request->uids) ? $request->uids : explode(',', $request->uids));

        try {
            foreach ($plugins->get() as $plugin) {
                if ($request->user()->admin->can('disable', $plugin)) {
                    $plugin->disable();
                }
            }
        } catch (\Exception $ex) {
            return response()->json(['status' => 'error', 'message' => $ex->getMessage()], 422);
        }

        return response()->json(['status' => 'success', 'message' => trans('refactor/admin_plugins.flash.disabled')]);
    }

    public function refresh(Request $request)
    {
        $plugin = Plugin::findByUid($request->uid);

        if (!$plugin || !$request->user()->admin->can('update', $plugin)) {
            return response()->json(['status' => 'error', 'message' => 'Not authorized'], 403);
        }

        try {
            $plugin->refreshFromComposer();
        } catch (\Exception $ex) {
            return response()->json(['status' => 'error', 'message' => $ex->getMessage()], 422);
        }

        return response()->json([
            'status' => 'success',
            'message' => trans('refactor/admin_plugins.flash.refreshed'),
        ]);
    }
}