File: /home/xedaptot/be.naniguide.com/app/Http/Controllers/Pub/MailListController.php
<?php
namespace App\Http\Controllers\Pub;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Model\MailList;
use App\Model\IpLocation;
use App\Model\Subscriber;
use App\Library\StringHelper;
use App\Library\SubscriberUrlTagResolver;
use App\Services\SubscriberService;
use App\Services\TagTranslationService;
use RuntimeException;
class MailListController extends Controller
{
/**
* Sign up form page.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function signUpForm(Request $request)
{
$subscriberService = app(SubscriberService::class);
$customerUid = $request->customer_uid;
$customer = \App\Model\Customer::findByUid($customerUid);
if (is_null($customer)) {
throw new \Exception('Cannot find customer');
} else {
$customer->setUserDbConnection();
}
$list = \App\Model\MailList::findByUid($request->list_uid);
$mailListTemplate = $list->getTemplate('sign_up_form');
$logger = applog("optin-for-customer-{$customerUid}-list-{$list->uid}");
// Language
if ($list->customer->language) {
\App::setLocale($list->customer->language->code);
\Carbon\Carbon::setLocale($list->customer->language->code);
}
// Get old post values
$values = [];
if (null !== $request->old()) {
foreach ($request->old() as $key => $value) {
if (is_array($value)) {
$values[str_replace('[]', '', $key)] = implode(',', $value);
} else {
$values[$key] = $value;
}
}
}
// Create subscriber
if ($request->isMethod('post')) {
// Captcha check — fail-open: any vendor rejection lets the
// signup through but dispatches an admin notification. See
// App\Library\Tool::verifyCaptchaOrAlert.
if (\App\Model\Setting::isListSignupCaptchaEnabled()) {
\App\Library\Tool::verifyCaptchaOrAlert($request, 'list-signup');
}
try {
$logger->info('Opting in through default opt-in page '.$request->EMAIL);
[$validator, $subscriber] = $subscriberService->optInFromWeb($list, $request, $logger);
} catch (\Exception $ex) {
$logger->info('Failed to opt in: '.$request->EMAIL);
return view('somethingWentWrong', ['message' => $ex->getMessage()]);
}
if ($validator->fails()) {
return back()->withErrors($validator)->withInput();
}
$logger->info('Done opting in: '.$subscriber->email);
// Timeline record
if ($request->redirect_url) {
$logger->info("Redirecting {$subscriber->email} to {$request->redirect_url}");
return redirect()->away($request->redirect_url);
} elseif ($list->subscribe_confirmation && !$subscriber->isSubscribed()) {
// tell subscriber to check email for confirmation
$logger->info("Redirect {$subscriber->email} to ThankYou page, asking user to check email to confirm (double opt-in)");
return redirect()->action('Pub\MailListController@signUpThankyouPage', ['list_uid' => $list->uid, 'subscriber_id' => $subscriber->id, 'customer_uid' => $list->customer->uid]);
} else {
// All done, confirmed
$logger->info("All done, redirect {$subscriber->email} to Pub\MailListController@thankYou");
return redirect()->away($subscriberService->generateSignedConfirmationUrl($subscriber));
}
}
$html = $mailListTemplate->renderContent($values);
// wrapping html with form tag. html is <html>...<html>. wrap body only
$dom = new \DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
libxml_clear_errors();
// DOMDocument::loadHTML auto-creates <html>/<body> for any non-empty flow
// content. The only inputs that produce a null body are: empty/whitespace,
// <head>-only, comments-only, or broken-encoding templates — all of which
// mean the template is unusable, so fail loud instead of rendering broken.
$body = $dom->getElementsByTagName('body')->item(0);
if ($body === null) {
throw new \RuntimeException(sprintf(
'MailList template produced no body content. list_uid=%s template_id=%s',
$list->uid,
$mailListTemplate->id ?? 'null'
));
}
$form = $dom->createElement('form');
$form->setAttribute('method', 'post');
$form->setAttribute('action', action('Pub\MailListController@signUpForm', ['list_uid' => $list->uid, 'customer_uid' => $list->customer->uid]));
$form->setAttribute('autocomplete', 'off');
// Move all body content into the form
while ($body->hasChildNodes()) {
$form->appendChild($body->removeChild($body->firstChild));
}
// Add form to body
$body->appendChild($form);
return $dom->saveHTML();
}
/**
* Sign up thank you page.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function signUpThankyouPage(Request $request)
{
$customerUid = $request->customer_uid;
$customer = \App\Model\Customer::findByUid($customerUid);
if (is_null($customer)) {
throw new \Exception('Cannot find customer');
} else {
$customer->setUserDbConnection();
}
$list = \App\Model\MailList::findByUid($request->list_uid);
$mailListTemplate = $list->getTemplate('sign_up_confirmation_page');
$subscriber = \App\Model\Subscriber::find($request->subscriber_id);
// Language
if ($list->customer && $list->customer->language) {
\App::setLocale($list->customer->language->code);
\Carbon\Carbon::setLocale($list->customer->language->code);
}
// redirect if use outside url
if ($list->sign_up_confirmation_page_use_outside_url) {
return redirect($list->sign_up_confirmation_page_outside_url);
}
return $mailListTemplate->renderContent(null, $subscriber);
}
/**
* Sign up confirmation thank you page.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse|string
*/
public function thankYou(Request $request)
{
$subscriberService = app(SubscriberService::class);
$customerUid = $request->customer_uid;
$customer = \App\Model\Customer::findByUid($customerUid);
if (is_null($customer)) {
throw new \Exception('Cannot find customer');
} else {
$customer->setUserDbConnection();
}
$list = \App\Model\MailList::findByUid($request->list_uid);
$mailListTemplate = $list->getTemplate('sign_up_thankyou_page');
$subscriber = \App\Model\Subscriber::find($request->id);
// Language
if ($list->customer && $list->customer->language) {
\App::setLocale($list->customer->language->code);
\Carbon\Carbon::setLocale($list->customer->language->code);
}
if (is_null($subscriber)) {
return "Subscriber no longer exists";
}
if ((int) $list->customer_id !== (int) $customer->id || (int) $subscriber->mail_list_id !== (int) $list->id) {
abort(404);
}
try {
$subscriberService->confirmOptIn($subscriber);
} catch (RuntimeException $ex) {
abort(409, $ex->getMessage());
}
// redirect if use outside url
if ($list->sign_up_thankyou_page_use_outside_url) {
return redirect($this->transformOutsideUrl($list->sign_up_thankyou_page_outside_url, $subscriber));
}
return $mailListTemplate->renderContent(null, $subscriber);
}
/**
* Update profile form.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function profileUpdateForm(Request $request)
{
$customerUid = $request->customer_uid;
$customer = \App\Model\Customer::findByUid($customerUid);
if (is_null($customer)) {
throw new \Exception("Cannot find customer: '{$customerUid}'");
} else {
$customer->setUserDbConnection();
}
$list = \App\Model\MailList::findByUid($request->list_uid);
$subscriber = \App\Model\Subscriber::find($request->id);
$mailListTemplate = $list->getTemplate('profile_update_form');
if (is_null($subscriber) || is_null($list)) {
abort(404);
}
// Language
if ($list->customer && $list->customer->language) {
\App::setLocale($list->customer->language->code);
\Carbon\Carbon::setLocale($list->customer->language->code);
}
$values = [];
// Fetch subscriber fields to values
foreach ($list->fields as $key => $field) {
$value = $subscriber->getValueByField($field);
if (is_array($value)) {
$values[str_replace('[]', '', $key)] = implode(',', $value);
} else {
$values[$field->tag] = $value;
}
}
// Get old post values
if (null !== $request->old()) {
foreach ($request->old() as $key => $value) {
if (is_array($value)) {
$values[str_replace('[]', '', $key)] = implode(',', $value);
} else {
$values[$key] = $value;
}
}
}
if ($request->isMethod('post')) {
$rules = $subscriber->getRules();
$rules['EMAIL'] .= '|in:'.$subscriber->email;
// Validation
$this->validate($request, $rules);
// Update field
$subscriber->updateFields($request->all());
return redirect()->action('Pub\MailListController@profileUpdateSuccessPage', ['list_uid' => $list->uid, 'id' => $subscriber->id, 'customer_uid' => $list->customer->uid]);
}
$html = $mailListTemplate->renderContent($values, $subscriber);
// wrapping html with form tag. html is <html>...<html>. wrap body only
$dom = new \DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
libxml_clear_errors();
// DOMDocument::loadHTML auto-creates <html>/<body> for any non-empty flow
// content. The only inputs that produce a null body are: empty/whitespace,
// <head>-only, comments-only, or broken-encoding templates — all of which
// mean the template is unusable, so fail loud instead of rendering broken.
$body = $dom->getElementsByTagName('body')->item(0);
if ($body === null) {
throw new \RuntimeException(sprintf(
'MailList template produced no body content. list_uid=%s template_id=%s',
$list->uid,
$mailListTemplate->id ?? 'null'
));
}
$form = $dom->createElement('form');
$form->setAttribute('method', 'post');
$form->setAttribute('action', action('Pub\MailListController@profileUpdateForm', ['list_uid' => $list->uid, 'id' => $subscriber->id, 'customer_uid' => $list->customer->uid]));
$form->setAttribute('autocomplete', 'off');
// Move all body content into the form
while ($body->hasChildNodes()) {
$form->appendChild($body->removeChild($body->firstChild));
}
// Add form to body
$body->appendChild($form);
return $dom->saveHTML();
}
/**
* Update profile success.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse|string
*/
public function profileUpdateSuccessPage(Request $request)
{
$customerUid = $request->customer_uid;
$customer = \App\Model\Customer::findByUid($customerUid);
if (is_null($customer)) {
throw new \Exception('Cannot find customer');
} else {
$customer->setUserDbConnection();
}
$list = \App\Model\MailList::findByUid($request->list_uid);
$subscriber = \App\Model\Subscriber::find($request->id);
$mailListTemplate = $list->getTemplate('profile_update_success_page');
// Language
if ($list->customer && $list->customer->language) {
\App::setLocale($list->customer->language->code);
\Carbon\Carbon::setLocale($list->customer->language->code);
}
// redirect if use outside url
if ($mailListTemplate->profile_update_success_page_use_outside_url) {
return redirect($this->transformOutsideUrl($mailListTemplate->profile_update_success_page_outside_url, $subscriber));
}
return $mailListTemplate->renderContent(null, $subscriber);
}
/**
* Send update profile request.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse|\Illuminate\View\View|string
*/
public function profileUpdateEmailSent(Request $request)
{
$customerUid = $request->customer_uid;
$customer = \App\Model\Customer::findByUid($customerUid);
if (is_null($customer)) {
throw new \Exception('Cannot find customer');
} else {
$customer->setUserDbConnection();
}
$list = \App\Model\MailList::findByUid($request->list_uid);
$subscriber = \App\Model\Subscriber::find($request->id);
$mailListTemplate = $list->getTemplate('profile_update_email_sent');
// Language
if ($list->customer && $list->customer->language) {
\App::setLocale($list->customer->language->code);
\Carbon\Carbon::setLocale($list->customer->language->code);
}
// SEND EMAIL
try {
$list->sendProfileUpdateEmail($subscriber);
} catch (\Exception $ex) {
return view('somethingWentWrong', ['message' => $ex->getMessage()]);
}
// redirect if use outside url
if ($mailListTemplate->profile_update_email_sent_use_outside_url) {
return redirect($this->transformOutsideUrl($mailListTemplate->profile_update_email_sent_outside_url, $subscriber));
}
return $mailListTemplate->renderContent(null, $subscriber);
}
/**
* Render a customer-configured outside-redirect URL with subscriber tags
* substituted ({{ SUBSCRIBER_<FIELD> }}, {{ EMAIL }}, {{ FIRST_NAME }}, …)
* and append `subscriber_id` so the destination can identify the visitor.
*/
private function transformOutsideUrl(string $url, Subscriber $subscriber): string
{
$url = app(TagTranslationService::class)
->translate($url, new SubscriberUrlTagResolver($subscriber));
$separator = parse_url($url, PHP_URL_QUERY) ? '&' : '?';
return $url . $separator . 'subscriber_id=' . $subscriber->id;
}
}