]> git.mxchange.org Git - friendica.git/blob - src/Module/Moderation/Report/Create.php
Changes:
[friendica.git] / src / Module / Moderation / Report / Create.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2024, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Module\Moderation\Report;
23
24 use Friendica\App;
25 use Friendica\BaseModule;
26 use Friendica\Content\Conversation as ConversationContent;
27 use Friendica\Content\Pager;
28 use Friendica\Content\Text\BBCode;
29 use Friendica\Core\L10n;
30 use Friendica\Core\Protocol;
31 use Friendica\Core\Renderer;
32 use Friendica\Core\Session\Model\UserSession;
33 use Friendica\Core\System;
34 use Friendica\Database\DBA;
35 use Friendica\DI;
36 use Friendica\Model\Contact;
37 use Friendica\Model\Item;
38 use Friendica\Model\Post;
39 use Friendica\Moderation\Entity\Report;
40 use Friendica\Module\Response;
41 use Friendica\Navigation\SystemMessages;
42 use Friendica\Network\HTTPException\ForbiddenException;
43 use Friendica\Util\Network;
44 use Friendica\Util\Profiler;
45 use Psr\Log\LoggerInterface;
46
47 class Create extends BaseModule
48 {
49         const CONTACT_ACTION_NONE     = 0;
50         const CONTACT_ACTION_COLLAPSE = 1;
51         const CONTACT_ACTION_IGNORE   = 2;
52         const CONTACT_ACTION_BLOCK    = 3;
53
54         /** @var SystemMessages */
55         private $systemMessages;
56         /** @var App\Page */
57         private $page;
58         /** @var UserSession */
59         private $session;
60         /** @var \Friendica\Moderation\Factory\Report */
61         private $factory;
62         /** @var \Friendica\Moderation\Repository\Report */
63         private $repository;
64
65         public function __construct(\Friendica\Moderation\Repository\Report $repository, \Friendica\Moderation\Factory\Report $factory, UserSession $session, App\Page $page, SystemMessages $systemMessages, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
66         {
67                 parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
68
69                 $this->systemMessages = $systemMessages;
70                 $this->page           = $page;
71                 $this->session        = $session;
72                 $this->factory        = $factory;
73                 $this->repository     = $repository;
74         }
75
76         protected function post(array $request = [])
77         {
78                 if (!$this->session->getLocalUserId()) {
79                         throw new ForbiddenException();
80                 }
81
82                 $report = [];
83                 foreach (['cid', 'category', 'rule-ids', 'uri-ids'] as $key) {
84                         if (isset($request[$key])) {
85                                 $report[$key] = $request[$key];
86                         }
87                 }
88
89                 if (isset($request['url'])) {
90                         $cid = Contact::getIdForURL($request['url']);
91                         if ($cid) {
92                                 $report['cid'] = $cid;
93                         } else {
94                                 $report['url'] = $request['url'];
95                                 $this->systemMessages->addNotice($this->t('Contact not found or their server is already blocked on this node.'));
96                         }
97                 }
98
99                 if (isset($request['comment'])) {
100                         $this->session->set('report_comment', $request['comment']);
101                         unset($request['comment']);
102                 }
103
104                 if (isset($request['report_create'])) {
105                         $report = $this->factory->createFromForm(
106                                 System::getRules(true),
107                                 $request['cid'],
108                                 $this->session->getLocalUserId(),
109                                 $request['category'],
110                                 !empty($request['rule-ids']) ? explode(',', $request['rule-ids']) : [],
111                                 $this->session->get('report_comment') ?? '',
112                                 !empty($request['uri-ids']) ? explode(',', $request['uri-ids']) : [],
113                                 (bool)($request['forward'] ?? false),
114                         );
115                         $this->repository->save($report);
116
117                         switch ($request['contact_action'] ?? 0) {
118                                 case self::CONTACT_ACTION_COLLAPSE:
119                                         Contact\User::setCollapsed($request['cid'], $this->session->getLocalUserId(), true);
120                                         break;
121                                 case self::CONTACT_ACTION_IGNORE:
122                                         Contact\User::setIgnored($request['cid'], $this->session->getLocalUserId(), true);
123                                         break;
124                                 case self::CONTACT_ACTION_BLOCK:
125                                         Contact\User::setBlocked($request['cid'], $this->session->getLocalUserId(), true);
126                                         break;
127                         }
128                 }
129
130                 $this->baseUrl->redirect($this->args->getCommand() . '?' . http_build_query($report));
131         }
132
133         protected function content(array $request = []): string
134         {
135                 if (!$this->session->getLocalUserId()) {
136                         throw new ForbiddenException($this->t('Please login to access this page.'));
137                 }
138
139                 $this->page['aside'] = $this->getAside($request);
140
141                 if (empty($request['cid'])) {
142                         return $this->pickContact($request);
143                 }
144
145                 if (empty($request['category'])) {
146                         return $this->pickCategory($request);
147                 }
148
149                 if ($request['category'] == Report::CATEGORY_VIOLATION && !isset($request['rule-ids'])) {
150                         return $this->pickRules($request);
151                 }
152
153                 if (!isset($request['uri-ids'])) {
154                         return $this->pickPosts($request);
155                 }
156
157                 return $this->summary($request);
158         }
159
160         private function pickContact(array $request): string
161         {
162                 $tpl = Renderer::getMarkupTemplate('moderation/report/create/pick_contact.tpl');
163                 return Renderer::replaceMacros($tpl, [
164                         '$l10n' => [
165                                 'title'       => $this->t('Create Moderation Report'),
166                                 'page'        => $this->t('Pick Contact'),
167                                 'description' => $this->t('Please enter below the contact address or profile URL you would like to create a moderation report about.'),
168                                 'submit'      => $this->t('Submit'),
169                         ],
170
171                         '$url' => ['url', $this->t('Contact address/URL'), $request['url'] ?? ''],
172                 ]);
173         }
174
175         private function pickCategory(array $request): string
176         {
177                 $tpl = Renderer::getMarkupTemplate('moderation/report/create/pick_category.tpl');
178                 return Renderer::replaceMacros($tpl, [
179                         '$l10n' => [
180                                 'title'       => $this->t('Create Moderation Report'),
181                                 'page'        => $this->t('Pick Category'),
182                                 'description' => $this->t('Please pick below the category of your report.'),
183                                 'submit'      => $this->t('Submit'),
184                         ],
185
186                         '$category_spam'      => ['category', $this->t('Spam')                     , Report::CATEGORY_SPAM     , $this->t('This contact is publishing many repeated/overly long posts/replies or advertising their product/websites in otherwise irrelevant conversations.'), $request['category'] == Report::CATEGORY_SPAM],
187                         '$category_illegal'   => ['category', $this->t('Illegal Content')          , Report::CATEGORY_ILLEGAL  , $this->t("This contact is publishing content that is considered illegal in this node's hosting juridiction."), $request['category'] == Report::CATEGORY_ILLEGAL],
188                         '$category_safety'    => ['category', $this->t('Community Safety')         , Report::CATEGORY_SAFETY   , $this->t("This contact aggravated you or other people, by being provocative or insensitive, intentionally or not. This includes disclosing people's private information (doxxing), posting threats or offensive pictures in posts or replies."), $request['category'] == Report::CATEGORY_SAFETY],
189                         '$category_unwanted'  => ['category', $this->t('Unwanted Content/Behavior'), Report::CATEGORY_UNWANTED , $this->t("This contact has repeatedly published content irrelevant to the node's theme or is openly criticizing the node's administration/moderation without directly engaging with the relevant people for example or repeatedly nitpicking on a sensitive topic."), $request['category'] == Report::CATEGORY_UNWANTED],
190                         '$category_violation' => ['category', $this->t('Rules Violation')          , Report::CATEGORY_VIOLATION, $this->t('This contact violated one or more rules of this node. You will be able to pick which one(s) in the next step.'), $request['category'] == Report::CATEGORY_VIOLATION],
191                         '$category_other'     => ['category', $this->t('Other')                    , Report::CATEGORY_OTHER    , $this->t('Please elaborate below why you submitted this report. The more details you provide, the better your report can be handled.'), $request['category'] == Report::CATEGORY_OTHER],
192
193                         '$comment' => ['comment', $this->t('Additional Information'), $this->session->get('report_comment') ?? '', $this->t('Please provide any additional information relevant to this particular report. You will be able to attach posts by this contact in the next step, but any context is welcome.')],
194                 ]);
195         }
196
197         private function pickRules(array $request): string
198         {
199                 $rules = [];
200
201                 foreach (System::getRules(true) as $rule_line => $rule_text) {
202                         $rules[] = ['rule-ids[]', $rule_line, $rule_text, in_array($rule_line, $request['rule_ids'] ?? [])];
203                 }
204
205                 $tpl = Renderer::getMarkupTemplate('moderation/report/create/pick_rules.tpl');
206                 return Renderer::replaceMacros($tpl, [
207                         '$l10n' => [
208                                 'title'       => $this->t('Create Moderation Report'),
209                                 'page'        => $this->t('Pick Rules'),
210                                 'description' => $this->t('Please pick below the node rules you believe this contact violated.'),
211                                 'submit'      => $this->t('Submit'),
212                         ],
213
214                         '$rules' => $rules,
215                 ]);
216         }
217
218         private function pickPosts(array $request): string
219         {
220                 $threads = [];
221
222                 $contact = DBA::selectFirst('contact', ['contact-type', 'network'], ['id' => $request['cid']]);
223                 if (DBA::isResult($contact)) {
224                         $contact_field = $contact['contact-type'] == Contact::TYPE_COMMUNITY || $contact['network'] == Protocol::MAIL ? 'owner-id' : 'author-id';
225
226                         $condition = [
227                                 $contact_field => $request['cid'],
228                                 'gravity'      => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT],
229                         ];
230
231                         if (empty($contact['network']) || in_array($contact['network'], Protocol::FEDERATED)) {
232                                 $condition = DBA::mergeConditions($condition, ['(`uid` = 0 OR (`uid` = ? AND NOT `global`))', DI::userSession()->getLocalUserId()]);
233                         } else {
234                                 $condition['uid'] = DI::userSession()->getLocalUserId();
235                         }
236
237                         if (DI::mode()->isMobile()) {
238                                 $itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_mobile_network',
239                                         DI::config()->get('system', 'itemspage_network_mobile'));
240                         } else {
241                                 $itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_network',
242                                         DI::config()->get('system', 'itemspage_network'));
243                         }
244
245                         $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage);
246
247                         $params = ['order' => ['received' => true], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
248
249                         $fields = array_merge(Item::DISPLAY_FIELDLIST, ['featured']);
250                         $items  = Post::toArray(Post::selectForUser(DI::userSession()->getLocalUserId(), $fields, $condition, $params));
251
252                         $formSecurityToken = BaseModule::getFormSecurityToken('contact_action');
253
254                         $threads = DI::conversation()->getContextLessThreadList($items, ConversationContent::MODE_CONTACT_POSTS, false, false, $formSecurityToken);
255                 }
256
257                 $tpl = Renderer::getMarkupTemplate('moderation/report/create/pick_posts.tpl');
258                 return Renderer::replaceMacros($tpl, [
259                         '$l10n' => [
260                                 'title'       => $this->t('Create Moderation Report'),
261                                 'page'        => $this->t('Pick Posts'),
262                                 'description' => $this->t('Please optionally pick posts to attach to your report.'),
263                                 'submit'      => $this->t('Submit'),
264                         ],
265
266                         '$threads' => $threads,
267                 ]);
268         }
269
270         private function summary(array $request): string
271         {
272                 $this->page['aside'] = '';
273
274                 $contact = Contact::getById($request['cid'], ['url']);
275
276                 $tpl = Renderer::getMarkupTemplate('moderation/report/create/summary.tpl');
277                 return Renderer::replaceMacros($tpl, [
278                         '$l10n' => [
279                                 'title'                => $this->t('Create Moderation Report'),
280                                 'page'                 => $this->t('Summary'),
281                                 'submit'               => $this->t('Submit Report'),
282                                 'contact_action_title' => $this->t('Further Action'),
283                                 'contact_action_desc'  => $this->t('You can also perform one of the following action on the contact you reported:'),
284                         ],
285
286                         '$cid'      => $request['cid'],
287                         '$category' => $request['category'],
288                         '$ruleIds'  => implode(',', $request['rule-ids'] ?? []),
289                         '$uriIds'   => implode(',', $request['uri-ids'] ?? []),
290
291                         '$nothing'  => ['contact_action', $this->t('Nothing'), self::CONTACT_ACTION_NONE, '', true],
292                         '$collapse' => ['contact_action', $this->t('Collapse contact'), self::CONTACT_ACTION_COLLAPSE, $this->t('Their posts and replies will keep appearing in your Network page but their content will be collapsed by default.')],
293                         '$ignore'   => ['contact_action', $this->t('Ignore contact'), self::CONTACT_ACTION_IGNORE, $this->t("Their posts won't appear in your Network page anymore, but their replies can appear in forum threads. They still can follow you.")],
294                         '$block'    => ['contact_action', $this->t('Block contact'), self::CONTACT_ACTION_BLOCK, $this->t("Their posts won't appear in your Network page anymore, but their replies can appear in forum threads, with their content collapsed by default. They cannot follow you but still can have access to your public posts by other means.")],
295
296                         '$display_forward' => !Network::isLocalLink($contact['url']),
297                         '$forward'         => ['report_forward', $this->t('Forward report'), self::CONTACT_ACTION_BLOCK, $this->t('Would you ike to forward this report to the remote server?')],
298
299                         '$summary' => $this->getAside($request),
300                 ]);
301         }
302
303         private function getAside(array $request): string
304         {
305                 $contact = null;
306                 if (!empty($request['cid'])) {
307                         $contact = Contact::getById($request['cid']);
308                 }
309
310                 switch ($request['category'] ?? 0) {
311                         case Report::CATEGORY_SPAM:      $category = $this->t('Spam'); break;
312                         case Report::CATEGORY_ILLEGAL:   $category = $this->t('Illegal Content'); break;
313                         case Report::CATEGORY_SAFETY:    $category = $this->t('Community Safety'); break;
314                         case Report::CATEGORY_UNWANTED:  $category = $this->t('Unwanted Content/Behavior'); break;
315                         case Report::CATEGORY_VIOLATION: $category = $this->t('Rules Violation'); break;
316                         case Report::CATEGORY_OTHER:     $category = $this->t('Other'); break;
317
318                         default: $category = '';
319                 }
320
321                 if (!empty($request['rule-ids'])) {
322                         $rules = array_filter(System::getRules(true), function ($rule_id) use ($request) {
323                                 return in_array($rule_id, $request['rule-ids']);
324                         }, ARRAY_FILTER_USE_KEY);
325                 }
326
327                 $tpl = Renderer::getMarkupTemplate('moderation/report/create/aside.tpl');
328                 return Renderer::replaceMacros($tpl, [
329                         '$l10n' => [
330                                 'contact_title'  => $this->t('1. Pick a contact'),
331                                 'category_title' => $this->t('2. Pick a category'),
332                                 'rules_title'    => $this->t('2a. Pick rules'),
333                                 'comment_title'  => $this->t('2b. Add comment'),
334                                 'posts_title'    => $this->t('3. Pick posts'),
335                         ],
336
337                         '$contact'  => $contact,
338                         '$category' => $category,
339                         '$rules'    => $rules ?? [],
340                         '$comment'  => BBCode::convertForUriId($contact['uri-id'] ?? 0, $this->session->get('report_comment') ?? '', BBCode::EXTERNAL),
341                         '$posts'    => count($request['uri-ids'] ?? []),
342                 ]);
343         }
344 }