1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2019 webtrees development team 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16declare(strict_types=1); 17 18namespace Fisharebest\Webtrees\Module; 19 20use Fisharebest\Webtrees\Auth; 21use Fisharebest\Webtrees\I18N; 22use Fisharebest\Webtrees\Individual; 23use Fisharebest\Webtrees\Menu; 24use Fisharebest\Webtrees\Tree; 25use Illuminate\Database\Capsule\Manager as DB; 26use stdClass; 27use Symfony\Component\HttpFoundation\RedirectResponse; 28use Symfony\Component\HttpFoundation\Request; 29use Symfony\Component\HttpFoundation\Response; 30 31/** 32 * Class StoriesModule 33 */ 34class StoriesModule extends AbstractModule implements ModuleInterface, ModuleConfigInterface, ModuleMenuInterface, ModuleTabInterface 35{ 36 use ModuleTabTrait; 37 use ModuleConfigTrait; 38 use ModuleMenuTrait; 39 40 /** @var int The default access level for this module. It can be changed in the control panel. */ 41 protected $access_level = Auth::PRIV_HIDE; 42 43 /** {@inheritdoc} */ 44 public function title(): string 45 { 46 /* I18N: Name of a module */ 47 return I18N::translate('Stories'); 48 } 49 50 /** {@inheritdoc} */ 51 public function description(): string 52 { 53 /* I18N: Description of the “Stories” module */ 54 return I18N::translate('Add narrative stories to individuals in the family tree.'); 55 } 56 57 /** 58 * The default position for this menu. It can be changed in the control panel. 59 * 60 * @return int 61 */ 62 public function defaultMenuOrder(): int 63 { 64 return 30; 65 } 66 67 /** 68 * The default position for this tab. It can be changed in the control panel. 69 * 70 * @return int 71 */ 72 function defaultTabOrder(): int { 73 return 70; 74 } 75 76 /** {@inheritdoc} */ 77 public function getTabContent(Individual $individual): string 78 { 79 return view('modules/stories/tab', [ 80 'is_admin' => Auth::isAdmin(), 81 'individual' => $individual, 82 'stories' => $this->getStoriesForIndividual($individual), 83 ]); 84 } 85 86 /** {@inheritdoc} */ 87 public function hasTabContent(Individual $individual): bool 88 { 89 return Auth::isManager($individual->tree()) || !empty($this->getStoriesForIndividual($individual)); 90 } 91 92 /** {@inheritdoc} */ 93 public function isGrayedOut(Individual $individual): bool 94 { 95 return !empty($this->getStoriesForIndividual($individual)); 96 } 97 98 /** {@inheritdoc} */ 99 public function canLoadAjax(): bool 100 { 101 return false; 102 } 103 104 /** 105 * @param Individual $individual 106 * 107 * @return stdClass[] 108 */ 109 private function getStoriesForIndividual(Individual $individual): array 110 { 111 $block_ids = DB::table('block') 112 ->where('module_name', '=', $this->getName()) 113 ->where('xref', '=', $individual->xref()) 114 ->where('gedcom_id', '=', $individual->tree()->id()) 115 ->pluck('block_id'); 116 117 $stories = []; 118 foreach ($block_ids as $block_id) { 119 $block_id = (int) $block_id; 120 121 // Only show this block for certain languages 122 $languages = $this->getBlockSetting($block_id, 'languages', ''); 123 if ($languages === '' || in_array(WT_LOCALE, explode(',', $languages))) { 124 $stories[] = (object) [ 125 'block_id' => $block_id, 126 'title' => $this->getBlockSetting($block_id, 'title'), 127 'story_body' => $this->getBlockSetting($block_id, 'story_body'), 128 ]; 129 } 130 } 131 132 return $stories; 133 } 134 135 /** 136 * A menu, to be added to the main application menu. 137 * 138 * @param Tree $tree 139 * 140 * @return Menu|null 141 */ 142 public function getMenu(Tree $tree) 143 { 144 $menu = new Menu($this->title(), route('module', [ 145 'module' => $this->getName(), 146 'action' => 'ShowList', 147 'ged' => $tree->name(), 148 ]), 'menu-story'); 149 150 return $menu; 151 } 152 153 /** 154 * @param Tree $tree 155 * 156 * @return Response 157 */ 158 public function getAdminAction(Tree $tree): Response 159 { 160 $this->layout = 'layouts/administration'; 161 162 $stories = DB::table('block') 163 ->where('module_name', '=', $this->getName()) 164 ->where('gedcom_id', '=', $tree->id()) 165 ->orderBy('xref') 166 ->get(); 167 168 foreach ($stories as $story) { 169 $block_id = (int) $story->block_id; 170 171 $story->individual = Individual::getInstance($story->xref, $tree); 172 $story->title = $this->getBlockSetting($block_id, 'title'); 173 $story->languages = $this->getBlockSetting($block_id, 'languages'); 174 } 175 176 return $this->viewResponse('modules/stories/config', [ 177 'stories' => $stories, 178 'title' => $this->title() . ' — ' . $tree->title(), 179 'tree' => $tree, 180 'tree_names' => Tree::getNameList(), 181 ]); 182 } 183 184 /** 185 * @param Request $request 186 * @param Tree $tree 187 * 188 * @return Response 189 */ 190 public function getAdminEditAction(Request $request, Tree $tree): Response 191 { 192 $this->layout = 'layouts/administration'; 193 194 $block_id = (int) $request->get('block_id'); 195 196 if ($block_id === 0) { 197 // Creating a new story 198 $individual = Individual::getInstance($request->get('xref', ''), $tree); 199 $story_title = ''; 200 $story_body = ''; 201 $languages = []; 202 203 $title = I18N::translate('Add a story') . ' — ' . e($tree->title()); 204 } else { 205 // Editing an existing story 206 $xref = (string) DB::table('block') 207 ->where('block_id', '=', $block_id) 208 ->value('xref'); 209 210 $individual = Individual::getInstance($xref, $tree); 211 $story_title = $this->getBlockSetting($block_id, 'title', ''); 212 $story_body = $this->getBlockSetting($block_id, 'story_body', ''); 213 $languages = explode(',', $this->getBlockSetting($block_id, 'languages')); 214 215 $title = I18N::translate('Edit the story') . ' — ' . e($tree->title()); 216 } 217 218 return $this->viewResponse('modules/stories/edit', [ 219 'block_id' => $block_id, 220 'languages' => $languages, 221 'story_body' => $story_body, 222 'story_title' => $story_title, 223 'title' => $title, 224 'tree' => $tree, 225 'individual' => $individual, 226 ]); 227 } 228 229 /** 230 * @param Request $request 231 * @param Tree $tree 232 * 233 * @return RedirectResponse 234 */ 235 public function postAdminEditAction(Request $request, Tree $tree): RedirectResponse 236 { 237 $block_id = (int) $request->get('block_id'); 238 $xref = $request->get('xref', ''); 239 $story_body = $request->get('story_body', ''); 240 $story_title = $request->get('story_title', ''); 241 $languages = $request->get('languages', []); 242 243 if ($block_id !== 0) { 244 DB::table('block') 245 ->where('block_id', '=', $block_id) 246 ->update([ 247 'gedcom_id' => $tree->id(), 248 'xref' => $xref, 249 ]); 250 } else { 251 DB::table('block')->insert([ 252 'gedcom_id' => $tree->id(), 253 'xref' => $xref, 254 'module_name' => $this->getName(), 255 'block_order' => 0, 256 ]); 257 258 $block_id = (int) DB::connection()->getPdo()->lastInsertId(); 259 } 260 261 $this->setBlockSetting($block_id, 'story_body', $story_body); 262 $this->setBlockSetting($block_id, 'title', $story_title); 263 $this->setBlockSetting($block_id, 'languages', implode(',', $languages)); 264 265 $url = route('module', [ 266 'module' => $this->getName(), 267 'action' => 'Admin', 268 'ged' => $tree->name(), 269 ]); 270 271 return new RedirectResponse($url); 272 } 273 274 /** 275 * @param Request $request 276 * @param Tree $tree 277 * 278 * @return Response 279 */ 280 public function postAdminDeleteAction(Request $request, Tree $tree): Response 281 { 282 $block_id = (int) $request->get('block_id'); 283 284 DB::table('block_setting') 285 ->where('block_id', '=', $block_id) 286 ->delete(); 287 288 DB::table('block') 289 ->where('block_id', '=', $block_id) 290 ->delete(); 291 292 $url = route('module', [ 293 'module' => $this->getName(), 294 'action' => 'Admin', 295 'ged' => $tree->name(), 296 ]); 297 298 return new RedirectResponse($url); 299 } 300 301 /** 302 * @param Tree $tree 303 * 304 * @return Response 305 */ 306 public function getShowListAction(Tree $tree): Response 307 { 308 $stories = DB::table('block') 309 ->where('module_name', '=', $this->getName()) 310 ->where('gedcom_id', '=', $tree->id()) 311 ->get() 312 ->map(function (stdClass $story) use ($tree): stdClass { 313 $block_id = (int) $story->block_id; 314 315 $story->individual = Individual::getInstance($story->xref, $tree); 316 $story->title = $this->getBlockSetting($block_id, 'title'); 317 $story->languages = $this->getBlockSetting($block_id, 'languages'); 318 319 return $story; 320 })->filter(function (stdClass $story): bool { 321 // Filter non-existant and private individuals. 322 return $story->individual instanceof Individual && $story->individual->canShow(); 323 })->filter(function (stdClass $story): bool { 324 // Filter foreign languages. 325 return $story->languages === '' || in_array(WT_LOCALE, explode(',', $story->languages)); 326 }); 327 328 return $this->viewResponse('modules/stories/list', [ 329 'stories' => $stories, 330 'title' => $this->title(), 331 ]); 332 } 333} 334