<?php
namespace XF\Pub\Controller;
use function count, strlen;
class Misc extends AbstractController
{
protected function setupContactService()
{
/** @var \XF\Service\Contact $contactService */
$contactService = $this->service('XF:Contact');
$visitor = \XF::visitor();
$input = $this->filter([
'username' => 'str',
'email' => 'str',
'subject' => 'str',
'message' => 'str'
]);
if ($visitor->user_id)
{
$contactService->setFromUser($visitor);
if (!$visitor->email)
{
if (!$contactService->setEmail($input['email'], $error))
{
throw $this->exception($this->error($error));
}
}
}
else
{
$contactService->setFromGuest($input['username'], $input['email']);
}
$contactService
->setMessageDetails($input['subject'], $input['message'])
->setFromIp($this->request->getIp());
return $contactService;
}
public function actionContact()
{
$options = $this->options();
if ($options->contactUrl['type'] == 'custom')
{
return $this->redirect($options->contactUrl['custom'], '');
}
else if (!$options->contactUrl['type'])
{
return $this->redirect($this->buildLink('index'));
}
if (!\XF::visitor()->canUseContactForm())
{
return $this->noPermission();
}
$this->assertCanonicalUrl($this->buildLink('misc/contact'));
$redirect = $this->getDynamicRedirect(null, false);
$forceCaptcha = \XF::visitor()->user_state != 'valid';
if ($this->isPost())
{
$contactService = $this->setupContactService();
if (!$this->captchaIsValid($forceCaptcha))
{
return $this->error(\XF::phrase('did_not_complete_the_captcha_verification_properly'));
}
$contactService->checkForSpam();
if (!$contactService->validate($errors))
{
return $this->error($errors);
}
$this->assertNotFlooding('contact');
$contactService->send();
return $this->redirect($redirect, \XF::phrase('your_message_has_been_sent'));
}
else
{
$viewParams = [
'redirect' => $redirect,
'forceCaptcha' => $forceCaptcha
];
return $this->view('XF:Misc\Contact', 'contact_form', $viewParams);
}
}
public function actionLanguage()
{
$visitor = \XF::visitor();
if (!$visitor->canChangeLanguage($error))
{
return $this->noPermission($error);
}
$redirect = $this->getDynamicRedirect(null, true);
if ($this->request->exists('language_id'))
{
$this->assertValidCsrfToken($this->filter('t', 'str'));
$language = $this->app->language($this->filter('language_id', 'uint'));
if ($language->isUsable($visitor))
{
if ($visitor->user_id)
{
$visitor->language_id = $language->getId();
$visitor->save();
$this->app->response()->setCookie('language_id', false);
}
else
{
$this->app->response()->setCookie('language_id', $language->getId());
}
}
return $this->redirect($redirect);
}
else
{
$viewParams = [
'redirect' => $redirect,
'languages' => $this->repository('XF:Language')->getUserSelectableLanguages()
];
return $this->view('XF:Misc\Language', 'language_chooser', $viewParams);
}
}
public function actionStyle()
{
$visitor = \XF::visitor();
if (!$visitor->canChangeStyle($error))
{
return $this->noPermission($error);
}
$redirect = $this->getDynamicRedirect(null, true);
$csrfValid = true;
if ($visitor->user_id)
{
$csrfValid = $this->validateCsrfToken($this->filter('t', 'str'));
}
if ($this->request->exists('style_id') && $csrfValid)
{
$styleId = $this->filter('style_id', 'uint');
$style = $this->app->style($styleId);
if ($style['user_selectable'] || $visitor->is_admin)
{
if ($visitor->user_id)
{
$visitor->style_id = $styleId;
$visitor->save();
$this->app->response()->setCookie('style_id', false);
}
else
{
$this->app->response()->setCookie('style_id', $style->getId());
}
}
return $this->redirect($redirect);
}
else
{
$styles = $this->repository('XF:Style')->getUserSelectableStyles();
$styleId = $this->filter('style_id', 'uint');
if ($styleId && !empty($styles[$styleId]['user_selectable']))
{
$style = $styles[$styleId];
}
else
{
$style = false;
}
$viewParams = [
'redirect' => $redirect,
'style' => $style,
'styles' => $styles
];
return $this->view('XF:Misc\Style', 'style_chooser', $viewParams);
}
}
public function actionCaptcha()
{
$withRow = $this->filter('with_row', 'bool');
$rowType = preg_replace('#[^a-z0-9_ -]#i', '', $this->filter('row_type', 'str'));
return $this->view('XF:Misc\Captcha', 'captcha', [
'withRow' => $withRow,
'rowType' => $rowType
]);
}
public function actionIpInfo()
{
if (!\XF::visitor()->canViewIps())
{
return $this->noPermission();
}
$ip = $this->filter('ip', 'str');
$url = $this->options()->ipInfoUrl;
if (strpos($url, '{ip}') === false)
{
$url = 'https://whatismyipaddress.com/ip/{ip}';
}
return $this->redirectPermanently(str_replace('{ip}', urlencode($ip), $url));
}
public function actionLocationInfo()
{
$location = $this->filter('location', 'str');
$url = $this->options()->geoLocationUrl;
if (strpos($url, '{location}') === false)
{
$url = 'https://maps.google.com/maps?q={location}';
}
return $this->redirectPermanently(str_replace('{location}', urlencode($location), $url));
}
public function actionTagAutoComplete()
{
if (!$this->options()->enableTagging)
{
return $this->noPermission();
}
$tagRepo = $this->repository('XF:Tag');
$q = $this->filter('q', 'str');
$q = $tagRepo->normalizeTag($q);
if (strlen($q) >= 2)
{
$tags = $this->repository('XF:Tag')->getTagAutoCompleteResults($q);
$results = [];
foreach ($tags AS $tag)
{
$results[] = [
'id' => $tag->tag,
'text' => $tag->tag,
'q' => $q
];
}
}
else
{
$results = [];
}
$view = $this->view();
$view->setJsonParam('results', $results);
return $view;
}
public function actionCodeEditorModeLoader()
{
$language = $this->filter('language', 'str');
/** @var \XF\ControllerPlugin\CodeEditor $plugin */
$plugin = $this->plugin('XF:CodeEditor');
return $plugin->actionModeLoader($language);
}
public function actionAcceptPrivacyPolicy()
{
$visitor = \XF::visitor();
$lastUpdate = $this->options()->privacyPolicyLastUpdate;
if (!$visitor->user_id || !$lastUpdate)
{
return $this->noPermission();
}
if ($visitor->privacy_policy_accepted > $lastUpdate)
{
return $this->redirect(
$this->getDynamicRedirectIfNot(
$this->buildLink('misc/accept-privacy-policy')
)
);
}
if ($this->isPost())
{
if (!$this->filter('accept', 'bool'))
{
return $this->error(\XF::phrase('please_read_and_accept_our_privacy_policy_before_continuing'));
}
$visitor->privacy_policy_accepted = time();
$visitor->save();
return $this->redirect($this->getDynamicRedirect());
}
else
{
$privacyPolicyOption = $this->options()->privacyPolicyUrl;
$viewParams = [
'privacyPolicyOption' => $privacyPolicyOption
];
if ($privacyPolicyOption['type'] == 'default')
{
/** @var \XF\Entity\HelpPage $page */
$page = $this->finder('XF:HelpPage')
->where('page_name', 'privacy-policy')
->fetchOne();
if (!$page)
{
return $this->notFound();
}
$viewParams['page'] = $page;
$viewParams['templateName'] = 'public:_help_page_' . $page->page_id;
}
return $this->view('XF:Misc\AcceptPrivacyPolicy', 'accept_privacy_policy', $viewParams);
}
}
public function actionAcceptTerms()
{
$visitor = \XF::visitor();
$lastUpdate = $this->options()->termsLastUpdate;
if (!$visitor->user_id || !$lastUpdate)
{
return $this->noPermission();
}
if ($visitor->terms_accepted > $lastUpdate)
{
return $this->redirect(
$this->getDynamicRedirectIfNot(
$this->buildLink('misc/accept-terms')
)
);
}
if ($this->isPost())
{
if (!$this->filter('accept', 'bool'))
{
return $this->error(\XF::phrase('please_read_and_accept_our_terms_and_rules_before_continuing'));
}
$visitor->terms_accepted = time();
$visitor->save();
return $this->redirect($this->getDynamicRedirect());
}
else
{
$termsOption = $this->options()->tosUrl;
$viewParams = [
'termsOption' => $termsOption
];
if ($termsOption['type'] == 'default')
{
/** @var \XF\Entity\HelpPage $page */
$page = $this->finder('XF:HelpPage')
->where('page_name', 'terms')
->fetchOne();
if (!$page)
{
return $this->notFound();
}
$viewParams['page'] = $page;
$viewParams['templateName'] = 'public:_help_page_' . $page->page_id;
}
return $this->view('XF:Misc\AcceptTerms', 'accept_terms', $viewParams);
}
}
public function actionUpdatePushSubscription()
{
$this->assertPostOnly();
$visitor = \XF::visitor();
$subscription = $this->filter([
'endpoint' => 'str',
'unsubscribed' => 'bool',
'key' => 'str',
'token' => 'str',
'encoding' => 'str'
]);
/** @var \XF\Repository\UserPush $userPushRepo */
$userPushRepo = $this->repository('XF:UserPush');
if (!$userPushRepo->validateSubscriptionDetails($subscription, $validationError))
{
return $this->error($validationError ?: \XF::phrase('invalid_subscription_endpoint_provided'));
}
if ($subscription['unsubscribed'])
{
if ($visitor->user_id)
{
$userPushRepo->deleteUserPushSubscription($visitor, $subscription);
}
else
{
$userPushRepo->deletePushSubscription($subscription);
}
}
else if ($visitor->user_id && $this->options()->enablePush)
{
$userPushRepo->insertUserPushSubscription($visitor, $subscription);
$userPushRepo->limitUserPushSubscriptionCount($visitor, 20);
}
return $this->message(\XF::phrase('push_subscription_updated_successfully'));
}
public function actionFindEmoji()
{
$q = ltrim($this->filter('q', 'str', ['no-trim']));
if ($q !== '' && utf8_strlen($q) >= 2)
{
/** @var \XF\Repository\Emoji $emojiRepo */
$emojiRepo = $this->repository('XF:Emoji');
$results = $emojiRepo->getMatchingEmojiByString($q);
}
else
{
$results = [];
$q = '';
}
$viewParams = [
'q' => $q,
'results' => $results
];
return $this->view('XF:Misc\FindEmoji', '', $viewParams);
}
public function actionValidateUsername()
{
$this->assertPostOnly();
$username = $this->filter('content', 'str');
$errors = [];
$usernameValidator = $this->app->validator('Username');
$visitor = \XF::visitor();
if ($visitor->user_id)
{
$usernameValidator->setOption('self_user_id', $visitor->user_id);
}
if (!$usernameValidator->isValid($username, $errorKey))
{
$errors[] = $usernameValidator->getPrintableErrorValue($errorKey);
}
$view = $this->view('XF:Misc\ValidateUsername');
$view->setJsonParams([
'inputValid' => !count($errors),
'inputErrors' => $errors,
'validatedValue' => $username
]);
return $view;
}
public static function getActivityDetails(array $activities)
{
$output = [];
foreach ($activities AS $key => $activity)
{
if (strtolower($activity->controller_action) == 'contact')
{
$output[$key] = \XF::phrase('contacting_staff');
}
else
{
$output[$key] = false;
}
}
return $output;
}
public function assertNotRejected($action)
{
if (strtolower($action) == 'contact')
{
// bypass rejection for the default contact form
}
else
{
parent::assertNotRejected($action);
}
}
public function assertNotDisabled($action)
{
if (strtolower($action) == 'contact')
{
// bypass disabled notice for the default contact form
}
else
{
parent::assertNotRejected($action);
}
}
public function assertViewingPermissions($action)
{
switch (strtolower($action))
{
case 'captcha':
case 'contact':
case 'validateusername':
break;
default:
parent::assertViewingPermissions($action);
}
}
public function assertPolicyAcceptance($action) {}
}