File: /home/xedaptot/be.naniguide.com/app/Http/Controllers/Refactor/WebsiteController.php
<?php
namespace App\Http\Controllers\Refactor;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Model\Website;
use Gate;
class WebsiteController extends Controller
{
public function index(Request $request)
{
if (Gate::denies('list', Website::class)) {
return $this->notAuthorized();
}
$customer = $request->user()->customer;
$total = $customer->local()->websites()->count();
return view('refactor.pages.websites.index', [
'customer' => $customer,
'total' => $total,
]);
}
public function listing(Request $request)
{
if (Gate::denies('list', Website::class)) {
return $this->notAuthorized();
}
$websites = $request->user()->customer->local()->websites()
->search($request->keyword)
->orderBy($request->sort_order ?? 'created_at', $request->sort_direction ?? 'desc')
->paginate($request->per_page ?? 25);
return view('refactor.pages.websites._list', [
'websites' => $websites,
]);
}
public function create(Request $request)
{
if (Gate::denies('create', Website::class)) {
return $this->notAuthorized();
}
$website = Website::newDefault($request->user()->customer);
return view('refactor.pages.websites.form', [
'website' => $website,
'mode' => 'create',
]);
}
public function store(Request $request)
{
if (Gate::denies('create', Website::class)) {
return response()->json(['status' => 'error', 'message' => trans('messages.not_authorized')], 403);
}
$website = Website::newDefault($request->user()->customer);
$validator = $website->createFromArray($request->all());
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'errors' => $validator->errors(),
], 422);
}
return response()->json([
'status' => 'success',
'message' => trans('refactor/websites.flash.created'),
'redirect' => route('refactor.websites.edit', $website->uid),
]);
}
public function edit(Request $request, $uid)
{
$website = Website::findByUid($uid);
if (Gate::denies('read', $website)) {
return $this->notAuthorized();
}
return view('refactor.pages.websites.form', [
'website' => $website,
'mode' => 'edit',
]);
}
public function update(Request $request, $uid)
{
$website = Website::findByUid($uid);
if (Gate::denies('read', $website)) {
return response()->json(['status' => 'error', 'message' => trans('messages.not_authorized')], 403);
}
$validator = \Validator::make($request->all(), [
'url' => 'required|url|active_url',
], [
'url.url' => 'The URL format is invalid (e.g. https://www.example.com).',
'url.active_url' => 'The URL could not be reached. Please check that the domain exists and is publicly accessible.',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'errors' => $validator->errors(),
], 422);
}
// Update title from URL
try {
$website->title = Website::getTitleFromUrl($request->url);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'errors' => ['url' => [$e->getMessage()]],
], 422);
}
$website->url = $request->url;
$website->save();
return response()->json([
'status' => 'success',
'message' => trans('refactor/websites.flash.updated'),
]);
}
public function delete(Request $request)
{
$websites = Website::whereIn(
'uid',
is_array($request->uids) ? $request->uids : explode(',', $request->uids)
);
$total = $websites->count();
$deleted = 0;
foreach ($websites->get() as $website) {
if ($request->user()->customer->can('delete', $website)) {
$website->delete();
$deleted += 1;
}
}
return response()->json([
'status' => 'success',
'message' => trans('refactor/websites.flash.deleted'),
]);
}
public function connect(Request $request, $uid)
{
$website = Website::findByUid($uid);
// Ownership / RBAC first — non-owner must get 403 regardless of state.
// Use the `read` policy because `connect` additionally requires
// status=inactive, which we want to handle separately as a no-op
// (see idempotent short-circuit below).
if (Gate::denies('read', $website)) {
return response()->json(['status' => 'error', 'message' => trans('messages.not_authorized')], 403);
}
// Idempotent: the embed script's first GET to /check-js auto-flips
// the website to "connected" before the user clicks the manual
// Collega button on a stale tab. Returning 403 here would render as
// "NOT AUTHORIZED" — confusing because it actually IS connected.
// Past incident 2026-04-29 (Andrea/newsletter24).
if ($website->status === Website::STATUS_CONNECTED) {
return response()->json([
'status' => 'success',
'message' => trans('refactor/websites.flash.already_connected'),
]);
}
if (Gate::denies('connect', $website)) {
return response()->json(['status' => 'error', 'message' => trans('messages.not_authorized')], 403);
}
try {
$website->connect();
return response()->json([
'status' => 'success',
'message' => trans('refactor/websites.flash.connected'),
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => $e->getMessage(),
], 400);
}
}
public function disconnect(Request $request, $uid)
{
$website = Website::findByUid($uid);
if (Gate::denies('disconnect', $website)) {
return response()->json(['status' => 'error', 'message' => trans('messages.not_authorized')], 403);
}
$website->disconnect();
return response()->json([
'status' => 'success',
'message' => trans('refactor/websites.flash.disconnected'),
]);
}
/**
* Customer-facing embed script — served when a third-party site loads
* `<script src="/rui/websites/{uid}/connect-js">`. Branches by website
* activation state:
* • inactive → checkJs view (pings back to mark the website
* connected; reload will return the active branch).
* • active → connectJs view (loads AFormPopup helper + iterates
* every connected published form, instantiating a popup for each).
*
* Both branches use REFACTOR views (`refactor/websites/*` +
* `refactor/forms/frontend/*`). No legacy controller delegation, no
* legacy view chain — keeps the whole embed surface inside the
* refactor namespace per the layering rule.
*/
public function connectJs(Request $request, $uid)
{
$website = \App\Model\Website::findByUid($uid);
if (is_null($website)) {
return response('not available', 404);
}
if (!$website->isActive()) {
$content = view('refactor.websites.checkJs', [
'website' => $website,
]);
return response($content)
->header('Access-Control-Allow-Origin', '*')
->header('Content-Type', 'application/javascript');
}
$content = view('refactor.websites.connectJs', [
'website' => $website,
]);
return response($content)
->header('Access-Control-Allow-Origin', '*')
->header('Content-Type', 'application/javascript');
}
/**
* Inactive-website ping back endpoint. The checkJs view (returned by
* connectJs when the website is still inactive) GET-fetches this
* endpoint, which flips the website to connected. Mirrors the legacy
* `WebsiteController::checkJs` behaviour but lives in the refactor
* namespace so the embed chain has no legacy dependencies.
*/
public function checkJs(Request $request, $uid)
{
$website = \App\Model\Website::findByUid($uid);
if (is_null($website)) {
return response('not available', 404);
}
$website->setConnected();
return response('ok')
->header('Access-Control-Allow-Origin', '*')
->header('Content-Type', 'text/plain');
}
}