. */ declare(strict_types=1); namespace Fisharebest\Webtrees\Module; use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Menu; use Fisharebest\Webtrees\Tree; use Illuminate\Database\Capsule\Manager as DB; use Illuminate\Database\Query\Builder; use Illuminate\Support\Collection; use stdClass; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; /** * Class FrequentlyAskedQuestionsModule */ class FrequentlyAskedQuestionsModule extends AbstractModule implements ModuleMenuInterface, ModuleConfigInterface { /** {@inheritdoc} */ public function getTitle(): string { /* I18N: Name of a module. Abbreviation for “Frequently Asked Questions” */ return I18N::translate('FAQ'); } /** {@inheritdoc} */ public function getDescription(): string { /* I18N: Description of the “FAQ” module */ return I18N::translate('A list of frequently asked questions and answers.'); } /** * The URL to a page where the user can modify the configuration of this module. * * @return string */ public function getConfigLink(): string { return route('module', [ 'module' => $this->getName(), 'action' => 'Admin', ]); } /** * The user can re-order menus. Until they do, they are shown in this order. * * @return int */ public function defaultMenuOrder(): int { return 40; } /** * A menu, to be added to the main application menu. * * @param Tree $tree * * @return Menu|null */ public function getMenu(Tree $tree) { if ($this->faqsExist($tree, WT_LOCALE)) { return new Menu($this->getTitle(), route('module', [ 'module' => 'faq', 'action' => 'Show', 'ged' => $tree->name(), ]), 'menu-help'); } return null; } /** * @param Tree $tree * * @return Response */ public function getAdminAction(Tree $tree): Response { $this->layout = 'layouts/administration'; $faqs = $this->faqsForTree($tree); $min_block_order = DB::table('block') ->where('module_name', '=', $this->getName()) ->where(function (Builder $query) use ($tree): void { $query ->whereNull('gedcom_id') ->orWhere('gedcom_id', '=', $tree->id()); }) ->min('block_order'); $max_block_order = DB::table('block') ->where('module_name', '=', $this->getName()) ->where(function (Builder $query) use ($tree): void { $query ->whereNull('gedcom_id') ->orWhere('gedcom_id', '=', $tree->id()); }) ->max('block_order'); $title = I18N::translate('Frequently asked questions') . ' — ' . $tree->title(); return $this->viewResponse('modules/faq/config', [ 'faqs' => $faqs, 'max_block_order' => $max_block_order, 'min_block_order' => $min_block_order, 'title' => $title, 'tree' => $tree, 'tree_names' => Tree::getNameList(), ]); } /** * @param Request $request * @param Tree $tree * * @return RedirectResponse */ public function postAdminDeleteAction(Request $request, Tree $tree): RedirectResponse { $block_id = (int) $request->get('block_id'); DB::table('block_setting')->where('block_id', '=', $block_id)->delete(); DB::table('block')->where('block_id', '=', $block_id)->delete(); $url = route('module', [ 'module' => 'faq', 'action' => 'Admin', 'ged' => $tree->name(), ]); return new RedirectResponse($url); } /** * @param Request $request * @param Tree $tree * * @return RedirectResponse */ public function postAdminMoveDownAction(Request $request, Tree $tree): RedirectResponse { $block_id = (int) $request->get('block_id'); $block_order = DB::table('block') ->where('block_id', '=', $block_id) ->value('block_order'); $swap_block = DB::table('block') ->where('module_name', '=', $this->getName()) ->where('block_order', '=', function (Builder $query) use ($block_order): void { $query ->from('block') ->where('module_name', '=', $this->getName()) ->where('block_order', '>', $block_order) ->select(DB::raw('MIN(block_order)')); }) ->select(['block_order', 'block_id']) ->first(); if ($swap_block !== null) { DB::table('block') ->where('block_id', '=', $block_id) ->update([ 'block_order' => $swap_block->block_order, ]); DB::table('block') ->where('block_id', '=', $swap_block->block_id) ->update([ 'block_order' => $block_order, ]); } $url = route('module', [ 'module' => 'faq', 'action' => 'Admin', 'ged' => $tree->name(), ]); return new RedirectResponse($url); } /** * @param Request $request * @param Tree $tree * * @return RedirectResponse */ public function postAdminMoveUpAction(Request $request, Tree $tree): RedirectResponse { $block_id = (int) $request->get('block_id'); $block_order = DB::table('block') ->where('block_id', '=', $block_id) ->value('block_order'); $swap_block = DB::table('block') ->where('module_name', '=', $this->getName()) ->where('block_order', '=', function (Builder $query) use ($block_order): void { $query ->from('block') ->where('module_name', '=', $this->getName()) ->where('block_order', '<', $block_order) ->select(DB::raw('MAX(block_order)')); }) ->select(['block_order', 'block_id']) ->first(); if ($swap_block !== null) { DB::table('block') ->where('block_id', '=', $block_id) ->update([ 'block_order' => $swap_block->block_order, ]); DB::table('block') ->where('block_id', '=', $swap_block->block_id) ->update([ 'block_order' => $block_order, ]); } $url = route('module', [ 'module' => 'faq', 'action' => 'Admin', 'ged' => $tree->name(), ]); return new RedirectResponse($url); } /** * @param Request $request * @param Tree $tree * * @return Response */ public function getAdminEditAction(Request $request, Tree $tree): Response { $this->layout = 'layouts/administration'; $block_id = (int) $request->get('block_id'); if ($block_id === 0) { // Creating a new faq $header = ''; $faqbody = ''; $block_order = 1 + (int) DB::table('block') ->where('module_name', '=', $this->getName()) ->max('block_order'); $languages = []; $title = I18N::translate('Add an FAQ'); } else { // Editing an existing faq $header = $this->getBlockSetting($block_id, 'header'); $faqbody = $this->getBlockSetting($block_id, 'faqbody'); $block_order = DB::table('block') ->where('block_id', '=', $block_id) ->value('block_order'); $languages = explode(',', $this->getBlockSetting($block_id, 'languages')); $title = I18N::translate('Edit the FAQ'); } return $this->viewResponse('modules/faq/edit', [ 'block_id' => $block_id, 'block_order' => $block_order, 'header' => $header, 'faqbody' => $faqbody, 'languages' => $languages, 'title' => $title, 'tree' => $tree, 'tree_names' => Tree::getIdList(), ]); } /** * @param Request $request * @param Tree $tree * * @return RedirectResponse */ public function postAdminEditAction(Request $request, Tree $tree): RedirectResponse { $block_id = (int) $request->get('block_id'); $faqbody = $request->get('faqbody', ''); $header = $request->get('header', ''); $languages = $request->get('languages', []); $gedcom_id = (int) $request->get('gedcom_id') ?: null; $block_order = (int) $request->get('block_order'); if ($block_id !== 0) { DB::table('block') ->where('block_id', '=', $block_id) ->update([ 'gedcom_id' => $gedcom_id, 'block_order' => $block_order, ]); } else { DB::table('block')->insert([ 'gedcom_id' => $gedcom_id, 'module_name' => $this->getName(), 'block_order' => $block_order, ]); $block_id = (int) DB::connection()->getPdo()->lastInsertId(); } $this->setBlockSetting($block_id, 'faqbody', $faqbody); $this->setBlockSetting($block_id, 'header', $header); $this->setBlockSetting($block_id, 'languages', implode(',', $languages)); $url = route('module', [ 'module' => 'faq', 'action' => 'Admin', 'ged' => $tree->name(), ]); return new RedirectResponse($url); } /** * @param Tree $tree * * @return Response */ public function getShowAction(Tree $tree): Response { // Filter foreign languages. $faqs = $this->faqsForTree($tree) ->filter(function (stdClass $faq): bool { return $faq->languages === '' || in_array(WT_LOCALE, explode(',', $faq->languages)); }); return $this->viewResponse('modules/faq/show', [ 'faqs' => $faqs, 'title' => I18N::translate('Frequently asked questions'), 'tree' => $tree, ]); } /** * @param Tree $tree * * @return Collection */ private function faqsForTree(Tree $tree): Collection { return DB::table('block') ->join('block_setting AS bs1', 'bs1.block_id', '=', 'block.block_id') ->join('block_setting AS bs2', 'bs2.block_id', '=', 'block.block_id') ->join('block_setting AS bs3', 'bs3.block_id', '=', 'block.block_id') ->where('module_name', '=', $this->getName()) ->where('bs1.setting_name', '=', 'header') ->where('bs2.setting_name', '=', 'faqbody') ->where('bs3.setting_name', '=', 'languages') ->where(function (Builder $query) use ($tree): void { $query ->whereNull('gedcom_id') ->orWhere('gedcom_id', '=', $tree->id()); }) ->orderBy('block_order') ->select(['block.block_id', 'block_order', 'gedcom_id', 'bs1.setting_value AS header', 'bs2.setting_value AS faqbody', 'bs3.setting_value AS languages']) ->get(); } /** * @param Tree $tree * @param string $language * * @return bool */ private function faqsExist(Tree $tree, string $language): bool { return DB::table('block') ->join('block_setting', 'block_setting.block_id', '=', 'block.block_id') ->where('module_name', '=', $this->getName()) ->where('setting_name', '=', 'languages') ->where(function (Builder $query) use ($tree): void { $query ->whereNull('gedcom_id') ->orWhere('gedcom_id', '=', $tree->id()); }) ->select(['setting_value AS languages']) ->get() ->filter(function (stdClass $faq) use ($language): bool { return $faq->languages === '' || in_array($language, explode(',', $faq->languages)); }) ->isNotEmpty(); } }