<?php
namespace XF\Admin\Controller;
use XF\Entity\OptionGroup;
use XF\Mvc\FormAction;
use XF\Mvc\ParameterBag;
use XF\Util\Arr;
use function count, is_array;
class Option extends AbstractController
{
protected function preDispatchController($action, ParameterBag $params)
{
$this->assertAdminPermission('option');
}
public function actionIndex()
{
$optionRepo = $this->getOptionRepo();
$viewParams = [
'groups' => $optionRepo->findOptionGroupList()->fetch(),
'canAdd' => $optionRepo->canAddOption()
];
return $this->view('XF:Option\GroupList', 'option_group_list', $viewParams);
}
public function actionMenu()
{
$optionRepo = $this->getOptionRepo();
$viewParams = [
'groups' => $optionRepo->findOptionGroupList()->fetch()
];
return $this->view('XF:Option\GroupMenu', 'option_group_menu', $viewParams);
}
public function actionGroup(ParameterBag $params)
{
$group = $this->assertGroupExists($params['group_id']);
if ($group->AddOn && !$group->AddOn->active)
{
return $this->error(\XF::phrase('option_group_belongs_to_disabled_addon', [
'addon' => $group->AddOn->title,
'link' => $this->buildLink('add-ons')
]));
}
$optionRepo = $this->getOptionRepo();
$viewParams = [
'group' => $group,
'groups' => $optionRepo->findOptionGroupList()->fetch(),
'canAdd' => $optionRepo->canAddOption()
];
return $this->view('XF:Option\Listing', 'option_list', $viewParams);
}
public function actionUpdate()
{
$this->assertPostOnly();
$input = $this->filter([
'options_listed' => 'array-str',
'options' => 'array'
]);
$options = [];
foreach ($input['options_listed'] AS $optionId)
{
if (!isset($input['options'][$optionId]))
{
$options[$optionId] = false;
}
else
{
$options[$optionId] = $input['options'][$optionId];
}
}
$this->getOptionRepo()->updateOptions($options);
return $this->redirect($this->getDynamicRedirect());
}
protected function groupAddEdit(OptionGroup $group)
{
$viewParams = [
'group' => $group
];
return $this->view('XF:Option\GroupEdit', 'option_group_edit', $viewParams);
}
public function actionGroupAdd()
{
if (!$this->getOptionRepo()->canAddOption())
{
return $this->noPermission();
}
$group = $this->em()->create('XF:OptionGroup');
return $this->groupAddEdit($group);
}
public function actionGroupEdit(ParameterBag $params)
{
$group = $this->assertGroupExists($params['group_id'], ['MasterTitle', 'MasterDescription']);
if (!$group->canEdit())
{
return $this->noPermission();
}
return $this->groupAddEdit($group);
}
protected function groupSaveProcess(OptionGroup $group)
{
$entityInput = $this->filter([
'group_id' => 'str',
'icon' => 'str',
'display_order' => 'uint',
'debug_only' => 'bool',
'advanced' => 'bool',
'addon_id' => 'str'
]);
$form = $this->formAction();
$form->basicEntitySave($group, $entityInput);
$phraseInput = $this->filter([
'title' => 'str',
'description' => 'str'
]);
$form->validate(function(FormAction $form) use ($phraseInput)
{
if ($phraseInput['title'] === '')
{
$form->logError(\XF::phrase('please_enter_valid_title'), 'title');
}
});
$form->apply(function() use ($phraseInput, $group)
{
$title = $group->getMasterPhrase(true);
$title->phrase_text = $phraseInput['title'];
$title->save();
$description = $group->getMasterPhrase(false);
$description->phrase_text = $phraseInput['description'];
$description->save();
});
return $form;
}
public function actionGroupSave(ParameterBag $params)
{
$this->assertPostOnly();
if ($params['group_id'])
{
$group = $this->assertGroupExists($params['group_id']);
if (!$group->canEdit())
{
return $this->noPermission();
}
}
else
{
if (!$this->getOptionRepo()->canAddOption())
{
return $this->noPermission();
}
$group = $this->em()->create('XF:OptionGroup');
}
$form = $this->groupSaveProcess($group);
$form->run();
return $this->redirect($this->buildLink('options/groups', $group) . $this->buildLinkHash($group->group_id));
}
public function actionGroupDelete(ParameterBag $params)
{
$group = $this->assertGroupExists($params['group_id']);
if (!$group->canEdit())
{
return $this->noPermission();
}
/** @var \XF\ControllerPlugin\Delete $plugin */
$plugin = $this->plugin('XF:Delete');
return $plugin->actionDelete(
$group,
$this->buildLink('options/groups/delete', $group),
$this->buildLink('options/groups/edit', $group),
$this->buildLink('options'),
$group->title
);
}
protected function optionAddEdit(\XF\Entity\Option $option, $baseRelations = [])
{
$relations = $baseRelations;
$group = null;
if ($option->exists() && $option->Relations)
{
foreach ($option->Relations AS $relation)
{
$relations[$relation->group_id] = $relation->display_order;
}
$groupId = $this->filter('group_id', 'str');
if (isset($option->Relations[$groupId]))
{
$group = $option->Relations[$groupId]->OptionGroup;
}
else
{
$group = $option->Relations->first()->OptionGroup;
}
}
$optionRepo = $this->getOptionRepo();
$viewParams = [
'option' => $option,
'group' => $group,
'groups' => $optionRepo->findAllGroups()->fetch(),
'relations' => $relations,
'redirect' => $this->app->getDynamicRedirect()
];
return $this->view('XF:Option\Edit', 'option_edit', $viewParams);
}
public function actionAdd()
{
if (!$this->getOptionRepo()->canAddOption())
{
return $this->noPermission();
}
$option = $this->em()->create('XF:Option');
$baseRelations = [];
$groupId = $this->filter('group_id', 'str');
if ($groupId)
{
$baseRelations[$groupId] = 1;
}
return $this->optionAddEdit($option, $baseRelations);
}
public function actionEdit(ParameterBag $params)
{
$option = $this->assertOptionExists($params['option_id']);
if (!$option->canEdit())
{
return $this->noPermission();
}
return $this->optionAddEdit($option);
}
protected function optionSaveProcess(\XF\Entity\Option $option)
{
$entityInput = $this->filter([
'option_id' => 'str',
'default_value' => 'str',
'edit_format' => 'str',
'edit_format_params' => 'str',
'data_type' => 'str',
'validation_class' => 'str',
'validation_method' => 'str',
'advanced' => 'bool',
'addon_id' => 'str'
]);
$subOptions = Arr::stringToArray($this->filter('sub_options', 'str'),'/\r?\n/');
$form = $this->formAction();
$form->basicEntitySave($option, $entityInput)
->setup(function() use ($option, $subOptions)
{
$option->sub_options = $subOptions;
});
$phraseInput = $this->filter([
'title' => 'str',
'explain' => 'str'
]);
$form->validate(function(FormAction $form) use ($phraseInput)
{
if ($phraseInput['title'] === '')
{
$form->logError(\XF::phrase('please_enter_valid_title'), 'title');
}
});
$form->apply(function() use ($phraseInput, $option)
{
$title = $option->getMasterPhrase(true);
$title->phrase_text = $phraseInput['title'];
$title->save();
$explain = $option->getMasterPhrase(false);
$explain->phrase_text = $phraseInput['explain'];
$explain->save();
});
$groups = $this->getOptionRepo()->findAllGroups()->fetch();
$relationMap = [];
foreach ($this->filter('relations', 'array') AS $groupId => $relation)
{
if (is_array($relation)
&& !empty($relation['selected'])
&& isset($relation['display_order'])
&& isset($groups[$groupId])
)
{
$relationMap[$groupId] = $this->app->inputFilterer()->filter($relation['display_order'], 'uint');
}
}
$form->validate(function(FormAction $form) use ($option, $groups, $relationMap)
{
if (!count($relationMap))
{
$form->logError(\XF::phrase('this_option_must_belong_to_at_least_one_group'), 'relations');
}
});
$form->apply(function() use ($option, $relationMap)
{
$option->updateRelations($relationMap);
});
return $form;
}
public function actionSave(ParameterBag $params)
{
$this->assertPostOnly();
if ($params['option_id'])
{
$option = $this->assertOptionExists($params['option_id']);
if (!$option->canEdit())
{
return $this->noPermission();
}
}
else
{
if (!$this->getOptionRepo()->canAddOption())
{
return $this->noPermission();
}
$option = $this->em()->create('XF:Option');
}
$this->optionSaveProcess($option)->run();
return $this->redirect(
$this->getDynamicRedirect($this->buildLink('options'), false)
);
}
public function actionDelete(ParameterBag $params)
{
$option = $this->assertOptionExists($params['option_id']);
if (!$option->canEdit())
{
return $this->noPermission();
}
$returnUrl = $this->getDynamicRedirect(
$this->buildLink('options'), false
);
$confirmUrl = $this->buildLink('options/delete', $option, [
'_xfRedirect' => $returnUrl
]);
/** @var \XF\ControllerPlugin\Delete $plugin */
$plugin = $this->plugin('XF:Delete');
return $plugin->actionDelete(
$option,
$confirmUrl,
$this->buildLink('options/edit', $option),
$returnUrl,
$option->title
);
}
public function actionView(ParameterBag $params)
{
$option = $this->assertOptionExists($params['option_id']);
$relation = $option->Relations->first();
$group = $relation ? $relation->OptionGroup : null;
return $this->redirect(
$group ? $this->buildLink('options/groups', $group) . '#' . $option->option_id : $this->buildLink('options')
);
}
public function actionEmailHandlerSetup(ParameterBag $params)
{
$option = $this->assertEmailHandlerOption($params->option_id);
if ($this->isPost())
{
$newType = $this->filter('new_type', 'str');
switch ($newType)
{
case 'pop3':
case 'imap':
$transports = stream_get_transports();
$viewParams = [
'option' => $option,
'type' => $newType,
'transports' => $transports
];
return $this->view('XF:Option\EmailHandlerServer', 'option_email_handler_server', $viewParams);
case 'google':
$viewParams = [
'option' => $option,
'redirectUri' => $this->buildLink('canonical:misc/email-oauth-setup')
];
return $this->view('XF:Option\EmailHandlerGoogle', 'option_email_handler_google', $viewParams);
case 'disabled':
$optionValue = ['enabled' => false];
$this->getOptionRepo()->updateOption($option->option_id, $optionValue);
// break missing intentionally
case 'unchanged':
default:
return $this->redirect($this->getDynamicRedirect());
}
}
$viewParams = [
'option' => $option
];
return $this->view('XF:Option\EmailHandlerSetup', 'option_email_handler_setup', $viewParams);
}
public function actionEmailHandlerServer(ParameterBag $params)
{
$this->assertPostOnly();
$option = $this->assertEmailHandlerOption($params->option_id);
$optionValue = $this->filter([
'type' => 'str',
'host' => 'str',
'port' => 'uint',
'username' => 'str',
'password' => 'str',
'encryption' => 'str'
]);
$optionValue['enabled'] = true;
$this->getOptionRepo()->updateOption($option->option_id, $optionValue);
return $this->redirect($this->getDynamicRedirect());
}
public function actionEmailHandlerOAuth(ParameterBag $params)
{
$option = $this->assertEmailHandlerOption($params->option_id);
/** @var \XF\Admin\ControllerPlugin\EmailOAuth $oAuthPlugin */
$oAuthPlugin = $this->plugin('XF:Admin:EmailOAuth');
if ($this->isPost())
{
$input = $this->filter([
'username' => 'str',
'client_id' => 'str',
'client_secret' => 'str',
'type' => 'str'
]);
$oAuthEmailSetup = $oAuthPlugin->getGoogleOAuthEmailSetupConfig(
$input['client_id'],
$input['client_secret'],
$this->buildLink('canonical:options/email-handler-oauth', $option),
[
'username' => $input['username'],
'type' => $input['type']
]
);
return $oAuthPlugin->actionTriggerOAuthRequest($oAuthEmailSetup);
}
$oAuthEmailSetup = $oAuthPlugin->assertOAuthEmailSetupData(true);
$optionValue = array_merge($oAuthEmailSetup['input'], [
'enabled' => true,
'password' => $oAuthEmailSetup['tokenData']['token'],
'oauth' => $oAuthPlugin->getOAuthEmailOptionData($oAuthEmailSetup)
]);
if (empty($optionValue['host']))
{
$defaultConnection = $oAuthPlugin->getDefaultProviderConnectionData(
$oAuthEmailSetup['provider'],
$optionValue['type']
);
if ($defaultConnection)
{
$optionValue['host'] = $defaultConnection['host'];
$optionValue['port'] = $defaultConnection['port'];
$optionValue['encryption'] = $defaultConnection['encryption'];
}
}
if (empty($optionValue['username']) && !empty($oAuthEmailSetup['loginUserName']))
{
$optionValue['username'] = $oAuthEmailSetup['loginUserName'];
}
$this->getOptionRepo()->updateOption($option->option_id, $optionValue);
return $this->redirect($this->buildLink('options/view', $option));
}
public function actionEmailTransportSetup(ParameterBag $params)
{
$option = $this->assertEmailTransportOption($params->option_id);
if ($this->isPost())
{
$newType = $this->filter('new_type', 'str');
switch ($newType)
{
case 'smtp':
$transports = stream_get_transports();
$viewParams = [
'option' => $option,
'type' => $newType,
'transports' => $transports
];
return $this->view('XF:Option\EmailTransportServer', 'option_email_transport_server', $viewParams);
case 'google':
$viewParams = [
'option' => $option,
'redirectUri' => $this->buildLink('canonical:misc/email-oauth-setup')
];
return $this->view('XF:Option\EmailTransportGoogle', 'option_email_transport_google', $viewParams);
case 'sendmail':
$optionValue = ['emailTransport' => 'sendmail'];
$this->getOptionRepo()->updateOption($option->option_id, $optionValue);
// break missing intentionally
case 'unchanged':
default:
return $this->redirect($this->getDynamicRedirect());
}
}
$viewParams = [
'option' => $option
];
return $this->view('XF:Option\EmailTransportSetup', 'option_email_transport_setup', $viewParams);
}
public function actionEmailTransportServer(ParameterBag $params)
{
$this->assertPostOnly();
$option = $this->assertEmailTransportOption($params->option_id);
$optionValue = $this->filter([
'emailTransport' => 'str',
'smtpHost' => 'str',
'smtpPort' => 'uint',
'smtpAuth' => 'str',
'smtpLoginUsername' => 'str',
'smtpLoginPassword' => 'str',
'smtpEncrypt' => 'str'
]);
$this->getOptionRepo()->updateOption($option->option_id, $optionValue);
return $this->redirect($this->getDynamicRedirect());
}
public function actionEmailTransportOAuth(ParameterBag $params)
{
$option = $this->assertEmailTransportOption($params->option_id);
/** @var \XF\Admin\ControllerPlugin\EmailOAuth $oAuthPlugin */
$oAuthPlugin = $this->plugin('XF:Admin:EmailOAuth');
if ($this->isPost())
{
$input = $this->filter([
'smtpLoginUsername' => 'str',
'client_id' => 'str',
'client_secret' => 'str'
]);
$oAuthEmailSetup = $oAuthPlugin->getGoogleOAuthEmailSetupConfig(
$input['client_id'],
$input['client_secret'],
$this->buildLink('canonical:options/email-transport-oauth', $option),
[
'smtpLoginUsername' => $input['smtpLoginUsername']
]
);
return $oAuthPlugin->actionTriggerOAuthRequest($oAuthEmailSetup);
}
$oAuthEmailSetup = $oAuthPlugin->assertOAuthEmailSetupData(true);
$optionValue = array_merge($oAuthEmailSetup['input'], [
'emailTransport' => 'smtp',
'smtpAuth' => 'login',
'smtpLoginPassword' => $oAuthEmailSetup['tokenData']['token'],
'oauth' => $oAuthPlugin->getOAuthEmailOptionData($oAuthEmailSetup)
]);
if (empty($optionValue['smtpHost']))
{
$defaultConnection = $oAuthPlugin->getDefaultProviderConnectionData(
$oAuthEmailSetup['provider'],
$optionValue['emailTransport']
);
if ($defaultConnection)
{
$optionValue['smtpHost'] = $defaultConnection['host'];
$optionValue['smtpPort'] = $defaultConnection['port'];
$optionValue['smtpEncrypt'] = $defaultConnection['encryption'];
}
}
if (empty($optionValue['smtpLoginUsername']) && !empty($oAuthEmailSetup['loginUserName']))
{
$optionValue['smtpLoginUsername'] = $oAuthEmailSetup['loginUserName'];
}
$this->getOptionRepo()->updateOption($option->option_id, $optionValue);
return $this->redirect($this->buildLink('options/view', $option));
}
protected function assertEmailHandlerOption($optionId)
{
$option = $this->assertOptionExists($optionId);
if (
$option->edit_format !== 'template'
|| $option->edit_format_params !== 'option_template_advancedEmailHandler'
)
{
throw $this->exception($this->noPermission());
}
return $option;
}
protected function assertEmailTransportOption($optionId)
{
$option = $this->assertOptionExists($optionId);
if (
$option->edit_format !== 'template'
|| $option->edit_format_params !== 'option_template_advancedEmailTransport'
)
{
throw $this->exception($this->noPermission());
}
return $option;
}
/**
* @param string $groupId
* @param array|string|null $with
* @param null|string $phraseKey
*
* @return OptionGroup
*/
protected function assertGroupExists($groupId, $with = null, $phraseKey = null)
{
return $this->assertRecordExists('XF:OptionGroup', $groupId, $with, $phraseKey);
}
/**
* @param string $optionId
* @param array|string|null $with
* @param null|string $phraseKey
*
* @return \XF\Entity\Option
*/
protected function assertOptionExists($optionId, $with = null, $phraseKey = null)
{
return $this->assertRecordExists('XF:Option', $optionId, $with, $phraseKey);
}
/**
* @return \XF\Repository\Option
*/
protected function getOptionRepo()
{
return $this->repository('XF:Option');
}
}