File: /home/xedaptot/be.naniguide.com/app/Http/Controllers/Refactor/FormController.php
<?php
namespace App\Http\Controllers\Refactor;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Model\Form;
use App\Model\FormTemplate;
use Gate;
class FormController extends Controller
{
public function index(Request $request)
{
if (Gate::denies('list', Form::class)) {
return $this->notAuthorized();
}
$customer = $request->user()->customer;
$totalForms = $customer->local()->forms()->count();
$published = $customer->local()->forms()->where('status', 'published')->count();
$draft = $customer->local()->forms()->where('status', 'draft')->count();
$websites = $customer->local()->websites()->connected()->orderBy('title')->get();
return view('refactor.pages.forms.index', [
'customer' => $customer,
'totalForms' => $totalForms,
'published' => $published,
'draft' => $draft,
'websites' => $websites,
]);
}
public function listing(Request $request)
{
if (Gate::denies('list', Form::class)) {
return $this->notAuthorized();
}
$query = $request->user()->customer->local()->forms()
->search($request->keyword)
->filter($request->all());
// Status filter
if (!empty($request->status)) {
$query->where('status', $request->status);
}
$forms = $query
->orderBy($request->sort_order ?? 'created_at', $request->sort_direction ?? 'desc')
->paginate($request->per_page ?? 25);
return view('refactor.pages.forms._list', [
'forms' => $forms,
]);
}
public function create(Request $request)
{
$form = Form::newDefault($request->user()->customer);
if (Gate::denies('create', Form::class)) {
return $this->notAuthorized();
}
if ($request->isMethod('post')) {
$validator = $form->createFromArray($request->all());
if ($validator->fails()) {
return response()->json([
'errors' => $validator->errors(),
], 422);
}
return response()->json([
'message' => trans('refactor/forms.flash.created'),
'redirect' => route('refactor.forms.builder', $form->uid),
]);
}
return view('refactor.pages.forms.create', [
'form' => $form,
]);
}
public function delete(Request $request)
{
$forms = Form::whereIn(
'uid',
is_array($request->uids) ? $request->uids : explode(',', $request->uids)
);
$total = $forms->count();
$deleted = 0;
foreach ($forms->get() as $form) {
if ($request->user()->customer->can('delete', $form)) {
$form->delete();
$deleted++;
}
}
return response()->json([
'message' => $deleted . ' ' . trans('refactor/forms.flash.deleted'),
]);
}
public function publish(Request $request)
{
$forms = Form::whereIn(
'uid',
is_array($request->uids) ? $request->uids : explode(',', $request->uids)
);
foreach ($forms->get() as $form) {
if ($request->user()->customer->can('update', $form)) {
$form->publish();
}
}
return response()->json(['message' => trans('refactor/forms.flash.published')]);
}
public function unpublish(Request $request)
{
$forms = Form::whereIn(
'uid',
is_array($request->uids) ? $request->uids : explode(',', $request->uids)
);
foreach ($forms->get() as $form) {
if ($request->user()->customer->can('update', $form)) {
$form->unpublish();
}
}
return response()->json(['message' => trans('refactor/forms.flash.unpublished')]);
}
public function builder(Request $request, $uid)
{
$form = \App\Model\Form::findByUid($uid);
if (!$form || \Gate::denies('update', $form)) {
return $this->notAuthorized();
}
$template = $form->template;
// If the form has no template assigned, redirect to forms index with an
// error rather than crashing on $template->getThemeMetadata() in the view.
if (!$template) {
return redirect()->route('refactor.forms.index')
->with('alert-danger', 'This form has no template assigned. Please recreate it.');
}
// Form fields for Builder widgets
$formFields = $form->mailList->getFields->map(function ($field) {
return [
'type' => $field->tag == 'EMAIL' ? 'email' : $field->type,
'label' => $field->label,
'name' => $field->tag,
'visible' => $field->visible,
'required' => $field->required,
'default' => $field->default_value,
'options' => $field->fieldOptions->map(function ($option) {
return ['value' => $option->value, 'text' => $option->label];
})->toArray(),
];
})->toArray();
// List context for builder sidebar info card
$mailList = $form->mailList;
$listInfo = [
'name' => $mailList->name,
'from_name' => $mailList->from_name,
'from_email' => $mailList->from_email,
'subscriber_count' => $mailList->subscribersCount(true) ?? 0,
'field_count' => count($formFields),
];
// Form templates for layout gallery
$formTemplates = FormTemplate::get();
$templates = $formTemplates->map(function ($ft) {
return $ft->template;
})->filter();
// Tags for the builder
$tags = $form->getBuilderTags();
return view('refactor.pages.forms.builder', [
'form' => $form,
'template' => $template,
'formFields' => $formFields,
'listInfo' => $listInfo,
'templates' => $templates,
'tags' => $tags,
'customer' => $form->customer,
]);
}
public function preview(Request $request, $uid)
{
$form = Form::findByUid($uid);
if (!$form || Gate::denies('read', $form)) {
return $this->notAuthorized();
}
// Wrap the raw saved HTML in a minimal document that links the
// public popup stylesheet PLUS inlines the critical Terms-widget
// layout rules directly inside <style>. Without this, the preview
// iframe served raw body markup with no stylesheet — class-based
// defensive rules (e.g. `.acm-terms--inline { display: flex }` for
// legacy saved forms that lack the inline-style version of the
// template) never applied, and any widget shape relying on class
// fallbacks rendered as a stacked default-block layout.
//
// The inline `<style>` block is the SAME ruleset as
// `public/core/css/form_popup_content.css` (Terms-widget section).
// Inlining guarantees the rules apply even if the iframe sandbox /
// network conditions block the external link request — robust path,
// no race with link-load timing.
$cssUrl = url('core/css/form_popup_content.css') . '?v=' . time();
$body = $form->renderedContent();
$inlineStyles = <<<'CSS'
/* Universal jQuery validate error placement — every form field inside the
preview iframe gets the same inline error styling as production (kept in
sync with public/core/css/form_popup_content.css). The Terms-widget
`.acm-terms label.error` rules below override this with !important. */
form label.error,
[data-form-container-wrapper] label.error {
display: block !important;
margin: 4px 0 0 0;
font-size: 12px;
line-height: 1.4;
color: #dc3545;
}
.acm-terms__text a { color: inherit; text-decoration: underline; }
.acm-terms__agree {
display: flex; flex-wrap: wrap; align-items: flex-start;
gap: 8px; cursor: pointer; user-select: none; margin: 0;
}
.acm-terms__agree > .acm-terms__input { flex: 0 0 auto; margin-top: 3px; }
.acm-terms__agree > .acm-terms__agree-label { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; word-break: break-word; line-height: 1.45; }
.acm-terms--inline {
display: flex !important; flex-wrap: wrap !important; align-items: flex-start !important; gap: 8px !important;
}
.acm-terms--inline > .acm-terms__input { flex: 0 0 auto !important; margin-top: 3px; }
.acm-terms--inline > .acm-terms__text { flex: 1 1 0 !important; min-width: 0 !important; overflow-wrap: anywhere; word-break: break-word; }
.acm-terms label.error,
.acm-terms__agree label.error {
order: 99 !important; flex: 0 0 100% !important; width: 100% !important;
margin: 6px 0 0 0 !important; padding: 0 !important;
font-size: 12px !important; line-height: 1.4 !important;
font-weight: 400 !important; color: #dc3545 !important;
display: block !important;
}
CSS;
$html = '<!doctype html><html><head><meta charset="utf-8">'
. '<meta name="viewport" content="width=device-width,initial-scale=1">'
. '<title>' . e($form->name) . '</title>'
. '<link rel="stylesheet" href="' . e($cssUrl) . '">'
. '<style>' . $inlineStyles . '</style>'
. '</head><body>' . $body . '</body></html>';
return response($html)
->header('Content-Type', 'text/html; charset=UTF-8');
}
public function connect(Request $request, $uid)
{
$form = Form::findByUid($uid);
if (!$form || Gate::denies('update', $form)) {
return $this->notAuthorized();
}
return view('refactor.pages.forms.connect', [
'form' => $form,
]);
}
public function connectSave(Request $request, $uid)
{
$form = Form::findByUid($uid);
if (!$form || Gate::denies('update', $form)) {
return $this->notAuthorized();
}
$validator = \Validator::make($request->all(), [
'website_uid' => 'required',
]);
if ($validator->fails()) {
return response()->view('refactor.pages.forms.connect', [
'form' => $form,
'errors' => $validator->errors(),
], 400);
}
$site = \App\Model\Website::findByUid($request->website_uid);
$form->connect($site);
return response()->json([
'message' => trans('refactor/forms_builder.connected', ['site' => $site->title]),
]);
}
public function disconnect(Request $request, $uid)
{
$form = Form::findByUid($uid);
if (!$form || Gate::denies('update', $form)) {
return $this->notAuthorized();
}
$form->disconnect();
return response()->json([
'message' => trans('refactor/forms_builder.disconnected'),
]);
}
public function settings(Request $request, $uid)
{
$form = Form::findByUid($uid);
if (!$form || Gate::denies('update', $form)) {
return $this->notAuthorized();
}
$form->saveSettingsFromArray($request->all());
return response()->json([
'message' => trans('refactor/forms_builder.settings_saved'),
'name' => $form->name,
]);
}
public function builderSave(\App\Http\Requests\Refactor\SaveFormBuilderRequest $request, $uid)
{
// Authorization + JSON structural rules already enforced by SaveFormBuilderRequest.
// On failure Laravel returns 422 with { message, errors: { key: [messages] } }.
$form = Form::findByUid($uid);
$form->setTemplateContent($request->input('content'), $request->input('json'));
return response()->json([
'status' => 'success',
]);
}
public function changeTemplate(Request $request, $uid)
{
$form = Form::findByUid($uid);
if (!$form || Gate::denies('update', $form)) {
return $this->notAuthorized();
}
$template = \App\Model\Template::findByUid($request->template_uid);
$form->changeTemplate($template);
return response()->json([
'status' => 'success',
]);
}
public function previewSave(Request $request, $uid)
{
$form = Form::findByUid($uid);
if (!$form || Gate::denies('read', $form)) {
return $this->notAuthorized();
}
$request->session()->put('form-preview-content-' . $form->uid, $request->content);
return response()->json(['status' => 'ok']);
}
}