1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2023 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Module; 21 22use Fisharebest\Webtrees\Auth; 23use Fisharebest\Webtrees\DB; 24use Fisharebest\Webtrees\GedcomRecord; 25use Fisharebest\Webtrees\Http\RequestHandlers\TreePage; 26use Fisharebest\Webtrees\I18N; 27use Fisharebest\Webtrees\Registry; 28use Fisharebest\Webtrees\Tree; 29use Fisharebest\Webtrees\Validator; 30use Illuminate\Support\Str; 31use Psr\Http\Message\ResponseInterface; 32use Psr\Http\Message\ServerRequestInterface; 33 34/** 35 * Class FamilyTreeFavoritesModule 36 */ 37class FamilyTreeFavoritesModule extends AbstractModule implements ModuleBlockInterface 38{ 39 use ModuleBlockTrait; 40 41 /** 42 * How should this module be identified in the control panel, etc.? 43 * 44 * @return string 45 */ 46 public function title(): string 47 { 48 /* I18N: Name of a module */ 49 return I18N::translate('Favorites'); 50 } 51 52 public function description(): string 53 { 54 /* I18N: Description of the “Favorites” module */ 55 return I18N::translate('Display and manage a family tree’s favorite pages.'); 56 } 57 58 /** 59 * Generate the HTML content of this block. 60 * 61 * @param Tree $tree 62 * @param int $block_id 63 * @param string $context 64 * @param array<string,string> $config 65 * 66 * @return string 67 */ 68 public function getBlock(Tree $tree, int $block_id, string $context, array $config = []): string 69 { 70 $content = view('modules/favorites/favorites', [ 71 'block_id' => $block_id, 72 'can_edit' => Auth::isManager($tree), 73 'favorites' => $this->getFavorites($tree), 74 'module_name' => $this->name(), 75 'tree' => $tree, 76 ]); 77 78 if ($context !== self::CONTEXT_EMBED) { 79 return view('modules/block-template', [ 80 'block' => Str::kebab($this->name()), 81 'id' => $block_id, 82 'config_url' => '', 83 'title' => $this->title(), 84 'content' => $content, 85 ]); 86 } 87 88 return $content; 89 } 90 91 /** 92 * Should this block load asynchronously using AJAX? 93 * Simple blocks are faster in-line, more complex ones can be loaded later. 94 * 95 * @return bool 96 */ 97 public function loadAjax(): bool 98 { 99 return false; 100 } 101 102 /** 103 * Can this block be shown on the user’s home page? 104 * 105 * @return bool 106 */ 107 public function isUserBlock(): bool 108 { 109 return false; 110 } 111 112 /** 113 * Can this block be shown on the tree’s home page? 114 * 115 * @return bool 116 */ 117 public function isTreeBlock(): bool 118 { 119 return true; 120 } 121 122 /** 123 * Get the favorites for a family tree 124 * 125 * @param Tree $tree 126 * 127 * @return array<object> 128 */ 129 public function getFavorites(Tree $tree): array 130 { 131 return DB::table('favorite') 132 ->where('gedcom_id', '=', $tree->id()) 133 ->whereNull('user_id') 134 ->get() 135 ->map(static function (object $row) use ($tree): object { 136 if ($row->xref !== null) { 137 $row->record = Registry::gedcomRecordFactory()->make($row->xref, $tree); 138 139 if ($row->record instanceof GedcomRecord && !$row->record->canShowName()) { 140 $row->record = null; 141 $row->note = null; 142 } 143 } else { 144 $row->record = null; 145 } 146 147 return $row; 148 }) 149 ->all(); 150 } 151 152 /** 153 * @param ServerRequestInterface $request 154 * 155 * @return ResponseInterface 156 */ 157 public function postAddFavoriteAction(ServerRequestInterface $request): ResponseInterface 158 { 159 $tree = Validator::attributes($request)->tree(); 160 $user = Validator::attributes($request)->user(); 161 $note = Validator::parsedBody($request)->string('note'); 162 $title = Validator::parsedBody($request)->string('title'); 163 $url = Validator::parsedBody($request)->string('url'); 164 $type = Validator::parsedBody($request)->string('type'); 165 $xref = Validator::parsedBody($request)->string($type . '-xref', ''); 166 $record = $this->getRecordForType($type, $xref, $tree); 167 168 if (Auth::isManager($tree, $user)) { 169 if ($type === 'url' && $url !== '') { 170 $this->addUrlFavorite($tree, $url, $title ?: $url, $note); 171 } 172 173 if ($record instanceof GedcomRecord && $record->canShow()) { 174 $this->addRecordFavorite($tree, $record, $note); 175 } 176 } 177 178 $url = route(TreePage::class, ['tree' => $tree->name()]); 179 180 return redirect($url); 181 } 182 183 /** 184 * @param ServerRequestInterface $request 185 * 186 * @return ResponseInterface 187 */ 188 public function postDeleteFavoriteAction(ServerRequestInterface $request): ResponseInterface 189 { 190 $tree = Validator::attributes($request)->tree(); 191 $user = Validator::attributes($request)->user(); 192 $favorite_id = Validator::queryParams($request)->integer('favorite_id'); 193 194 if (Auth::isManager($tree, $user)) { 195 DB::table('favorite') 196 ->where('favorite_id', '=', $favorite_id) 197 ->whereNull('user_id') 198 ->delete(); 199 } 200 201 $url = route(TreePage::class, ['tree' => $tree->name()]); 202 203 return redirect($url); 204 } 205 206 /** 207 * @param Tree $tree 208 * @param string $url 209 * @param string $title 210 * @param string $note 211 * 212 * @return void 213 */ 214 private function addUrlFavorite(Tree $tree, string $url, string $title, string $note): void 215 { 216 DB::table('favorite')->updateOrInsert([ 217 'gedcom_id' => $tree->id(), 218 'user_id' => null, 219 'url' => $url, 220 ], [ 221 'favorite_type' => 'URL', 222 'note' => $note, 223 'title' => $title, 224 ]); 225 } 226 227 /** 228 * @param Tree $tree 229 * @param GedcomRecord $record 230 * @param string $note 231 * 232 * @return void 233 */ 234 private function addRecordFavorite(Tree $tree, GedcomRecord $record, string $note): void 235 { 236 DB::table('favorite')->updateOrInsert([ 237 'gedcom_id' => $tree->id(), 238 'user_id' => null, 239 'xref' => $record->xref(), 240 ], [ 241 'favorite_type' => $record->tag(), 242 'note' => $note, 243 ]); 244 } 245 246 private function getRecordForType(string $type, string $xref, Tree $tree): GedcomRecord|null 247 { 248 switch ($type) { 249 case 'indi': 250 return Registry::individualFactory()->make($xref, $tree); 251 252 case 'fam': 253 return Registry::familyFactory()->make($xref, $tree); 254 255 case 'sour': 256 return Registry::sourceFactory()->make($xref, $tree); 257 258 case 'repo': 259 return Registry::repositoryFactory()->make($xref, $tree); 260 261 case 'obje': 262 return Registry::mediaFactory()->make($xref, $tree); 263 264 default: 265 return null; 266 } 267 } 268} 269