1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2019 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 <http://www.gnu.org/licenses/>. 16 */ 17declare(strict_types=1); 18 19namespace Fisharebest\Webtrees\Module; 20 21use Fisharebest\Webtrees\Auth; 22use Fisharebest\Webtrees\Carbon; 23use Fisharebest\Webtrees\I18N; 24use Fisharebest\Webtrees\Services\HtmlService; 25use Fisharebest\Webtrees\Tree; 26use Illuminate\Database\Capsule\Manager as DB; 27use Illuminate\Support\Str; 28use InvalidArgumentException; 29use Psr\Http\Message\ResponseInterface; 30use Psr\Http\Message\ServerRequestInterface; 31use stdClass; 32use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; 33use function assert; 34 35/** 36 * Class FamilyTreeNewsModule 37 */ 38class FamilyTreeNewsModule extends AbstractModule implements ModuleBlockInterface 39{ 40 use ModuleBlockTrait; 41 42 /** @var HtmlService */ 43 private $html_service; 44 45 /** 46 * HtmlBlockModule bootstrap. 47 * 48 * @param HtmlService $html_service 49 */ 50 public function boot(HtmlService $html_service) 51 { 52 $this->html_service = $html_service; 53 } 54 55 /** 56 * A sentence describing what this module does. 57 * 58 * @return string 59 */ 60 public function description(): string 61 { 62 /* I18N: Description of the “News” module */ 63 return I18N::translate('Family news and site announcements.'); 64 } 65 66 /** 67 * Generate the HTML content of this block. 68 * 69 * @param Tree $tree 70 * @param int $block_id 71 * @param string $context 72 * @param string[] $config 73 * 74 * @return string 75 */ 76 public function getBlock(Tree $tree, int $block_id, string $context, array $config = []): string 77 { 78 $articles = DB::table('news') 79 ->where('gedcom_id', '=', $tree->id()) 80 ->orderByDesc('updated') 81 ->get() 82 ->map(static function (stdClass $row): stdClass { 83 $row->updated = Carbon::make($row->updated); 84 85 return $row; 86 }); 87 88 $content = view('modules/gedcom_news/list', [ 89 'articles' => $articles, 90 'block_id' => $block_id, 91 'limit' => 5, 92 ]); 93 94 if ($context !== self::CONTEXT_EMBED) { 95 return view('modules/block-template', [ 96 'block' => Str::kebab($this->name()), 97 'id' => $block_id, 98 'config_url' => '', 99 'title' => $this->title(), 100 'content' => $content, 101 ]); 102 } 103 104 return $content; 105 } 106 107 /** 108 * How should this module be identified in the control panel, etc.? 109 * 110 * @return string 111 */ 112 public function title(): string 113 { 114 /* I18N: Name of a module */ 115 return I18N::translate('News'); 116 } 117 118 /** 119 * Should this block load asynchronously using AJAX? 120 * 121 * Simple blocks are faster in-line, more complex ones can be loaded later. 122 * 123 * @return bool 124 */ 125 public function loadAjax(): bool 126 { 127 return false; 128 } 129 130 /** 131 * Can this block be shown on the user’s home page? 132 * 133 * @return bool 134 */ 135 public function isUserBlock(): bool 136 { 137 return false; 138 } 139 140 /** 141 * Can this block be shown on the tree’s home page? 142 * 143 * @return bool 144 */ 145 public function isTreeBlock(): bool 146 { 147 return true; 148 } 149 150 /** 151 * @param ServerRequestInterface $request 152 * 153 * @return ResponseInterface 154 */ 155 public function getEditNewsAction(ServerRequestInterface $request): ResponseInterface 156 { 157 $tree = $request->getAttribute('tree'); 158 assert($tree instanceof Tree, new InvalidArgumentException()); 159 160 if (!Auth::isManager($tree)) { 161 throw new AccessDeniedHttpException(); 162 } 163 164 $news_id = $request->getQueryParams()['news_id'] ?? ''; 165 166 if ($news_id !== '') { 167 $row = DB::table('news') 168 ->where('news_id', '=', $news_id) 169 ->where('gedcom_id', '=', $tree->id()) 170 ->first(); 171 } else { 172 $row = (object) [ 173 'body' => '', 174 'subject' => '', 175 ]; 176 } 177 178 $title = I18N::translate('Add/edit a journal/news entry'); 179 180 return $this->viewResponse('modules/gedcom_news/edit', [ 181 'body' => $row->body, 182 'news_id' => $news_id, 183 'subject' => $row->subject, 184 'title' => $title, 185 ]); 186 } 187 188 /** 189 * @param ServerRequestInterface $request 190 * 191 * @return ResponseInterface 192 */ 193 public function postEditNewsAction(ServerRequestInterface $request): ResponseInterface 194 { 195 $tree = $request->getAttribute('tree'); 196 assert($tree instanceof Tree, new InvalidArgumentException()); 197 198 if (!Auth::isManager($tree)) { 199 throw new AccessDeniedHttpException(); 200 } 201 202 $news_id = $request->getQueryParams()['news_id'] ?? ''; 203 $subject = $request->getParsedBody()['subject']; 204 $body = $request->getParsedBody()['body']; 205 206 $subject = $this->html_service->sanitize($subject); 207 $body = $this->html_service->sanitize($body); 208 209 if ($news_id > 0) { 210 DB::table('news') 211 ->where('news_id', '=', $news_id) 212 ->where('gedcom_id', '=', $tree->id()) 213 ->update([ 214 'body' => $body, 215 'subject' => $subject, 216 ]); 217 } else { 218 DB::table('news')->insert([ 219 'body' => $body, 220 'subject' => $subject, 221 'gedcom_id' => $tree->id(), 222 ]); 223 } 224 225 $url = route('tree-page', ['tree' => $tree->name()]); 226 227 return redirect($url); 228 } 229 230 /** 231 * @param ServerRequestInterface $request 232 * 233 * @return ResponseInterface 234 */ 235 public function postDeleteNewsAction(ServerRequestInterface $request): ResponseInterface 236 { 237 $tree = $request->getAttribute('tree'); 238 $news_id = $request->getQueryParams()['news_id']; 239 240 if (!Auth::isManager($tree)) { 241 throw new AccessDeniedHttpException(); 242 } 243 244 DB::table('news') 245 ->where('news_id', '=', $news_id) 246 ->where('gedcom_id', '=', $tree->id()) 247 ->delete(); 248 249 $url = route('tree-page', ['tree' => $tree->name()]); 250 251 return redirect($url); 252 } 253} 254