xref: /webtrees/app/Module/UserJournalModule.php (revision 5229eade2a8cdd1381e19f96f67fc1c8b92ca95d)
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 UserJournalModule
37 */
38class UserJournalModule 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 “Journal” module */
63        return I18N::translate('A private area to record notes or keep a journal.');
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('user_id', '=', Auth::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/user_blog/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('Journal');
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 true;
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 false;
148    }
149
150    /**
151     * @param ServerRequestInterface $request
152     *
153     * @return ResponseInterface
154     */
155    public function getEditJournalAction(ServerRequestInterface $request): ResponseInterface
156    {
157        if (!Auth::check()) {
158            throw new AccessDeniedHttpException();
159        }
160
161        $news_id = $request->getQueryParams()['news_id'] ?? '';
162
163        if ($news_id !== '') {
164            $row = DB::table('news')
165                ->where('news_id', '=', $news_id)
166                ->where('user_id', '=', Auth::id())
167                ->first();
168        } else {
169            $row = (object) [
170                'body'    => '',
171                'subject' => '',
172            ];
173        }
174
175        $title = I18N::translate('Add/edit a journal/news entry');
176
177        return $this->viewResponse('modules/user_blog/edit', [
178            'body'    => $row->body,
179            'news_id' => $news_id,
180            'subject' => $row->subject,
181            'title'   => $title,
182        ]);
183    }
184
185    /**
186     * @param ServerRequestInterface $request
187     *
188     * @return ResponseInterface
189     */
190    public function postEditJournalAction(ServerRequestInterface $request): ResponseInterface
191    {
192        $tree = $request->getAttribute('tree');
193        assert($tree instanceof Tree, new InvalidArgumentException());
194
195        if (!Auth::check()) {
196            throw new AccessDeniedHttpException();
197        }
198
199        $news_id = $request->getQueryParams()['news_id'] ?? '';
200        $subject = $request->getParsedBody()['subject'];
201        $body    = $request->getParsedBody()['body'];
202
203        $subject = $this->html_service->sanitize($subject);
204        $body    = $this->html_service->sanitize($body);
205
206        if ($news_id !== '') {
207            DB::table('news')
208                ->where('news_id', '=', $news_id)
209                ->where('user_id', '=', Auth::id())
210                ->update([
211                    'body'    => $body,
212                    'subject' => $subject,
213                ]);
214        } else {
215            DB::table('news')->insert([
216                'body'    => $body,
217                'subject' => $subject,
218                'user_id' => Auth::id(),
219            ]);
220        }
221
222        $url = route('user-page', ['tree' => $tree->name()]);
223
224        return redirect($url);
225    }
226
227    /**
228     * @param ServerRequestInterface $request
229     *
230     * @return ResponseInterface
231     */
232    public function postDeleteJournalAction(ServerRequestInterface $request): ResponseInterface
233    {
234        $tree    = $request->getAttribute('tree');
235        $news_id = $request->getQueryParams()['news_id'];
236
237        DB::table('news')
238            ->where('news_id', '=', $news_id)
239            ->where('user_id', '=', Auth::id())
240            ->delete();
241
242        $url = route('user-page', ['tree' => $tree->name()]);
243
244        return redirect($url);
245    }
246}
247