diff --git a/database.sql b/database.sql index 7da771e0ea..6a7cb50e2c 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2023.09-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1531 +-- DB_UPDATE_VERSION 1532 -- ------------------------------------------ @@ -1316,7 +1316,7 @@ CREATE TABLE IF NOT EXISTS `post-engagement` ( INDEX `owner-id` (`owner-id`), INDEX `created` (`created`), FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, - FOREIGN KEY (`owner-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT + FOREIGN KEY (`owner-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Engagement data per post'; -- @@ -1741,7 +1741,7 @@ CREATE TABLE IF NOT EXISTS `report` ( `assigned-uid` mediumint unsigned COMMENT 'Assigned moderator user', `status` tinyint unsigned NOT NULL COMMENT 'Status of the report, one of Entity\Report::STATUS_*', `resolution` tinyint unsigned COMMENT 'Resolution of the report, one of Entity\Report::RESOLUTION_*', - `created` datetime(6) NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', + `created` datetime(6) NOT NULL DEFAULT '0001-01-01 00:00:00.000000' COMMENT '', `edited` datetime(6) COMMENT 'Last time the report has been edited', PRIMARY KEY(`id`), INDEX `uid` (`uid`), diff --git a/doc/database/db_report.md b/doc/database/db_report.md index c2890e25a9..cae4a2f33b 100644 --- a/doc/database/db_report.md +++ b/doc/database/db_report.md @@ -6,24 +6,24 @@ Table report Fields ------ -| Field | Description | Type | Null | Key | Default | Extra | -| --------------- | ------------------------------------------------------------ | ------------------ | ---- | --- | ------------------- | -------------- | -| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment | -| uid | Reporting user | mediumint unsigned | YES | | NULL | | -| reporter-id | Reporting contact | int unsigned | YES | | NULL | | -| cid | Reported contact | int unsigned | NO | | NULL | | -| gsid | Reported contact server | int unsigned | YES | | NULL | | -| comment | Report | text | YES | | NULL | | -| category-id | Report category, one of Entity\Report::CATEGORY_* | int unsigned | NO | | 1 | | -| forward | Forward the report to the remote server | boolean | YES | | NULL | | -| public-remarks | Remarks shared with the reporter | text | YES | | NULL | | -| private-remarks | Remarks shared with the moderation team | text | YES | | NULL | | -| last-editor-uid | Last editor user | mediumint unsigned | YES | | NULL | | -| assigned-uid | Assigned moderator user | mediumint unsigned | YES | | NULL | | -| status | Status of the report, one of Entity\Report::STATUS_* | tinyint unsigned | NO | | NULL | | -| resolution | Resolution of the report, one of Entity\Report::RESOLUTION_* | tinyint unsigned | YES | | NULL | | -| created | | datetime(6) | NO | | 0001-01-01 00:00:00 | | -| edited | Last time the report has been edited | datetime(6) | YES | | NULL | | +| Field | Description | Type | Null | Key | Default | Extra | +| --------------- | ------------------------------------------------------------ | ------------------ | ---- | --- | -------------------------- | -------------- | +| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment | +| uid | Reporting user | mediumint unsigned | YES | | NULL | | +| reporter-id | Reporting contact | int unsigned | YES | | NULL | | +| cid | Reported contact | int unsigned | NO | | NULL | | +| gsid | Reported contact server | int unsigned | YES | | NULL | | +| comment | Report | text | YES | | NULL | | +| category-id | Report category, one of Entity\Report::CATEGORY_* | int unsigned | NO | | 1 | | +| forward | Forward the report to the remote server | boolean | YES | | NULL | | +| public-remarks | Remarks shared with the reporter | text | YES | | NULL | | +| private-remarks | Remarks shared with the moderation team | text | YES | | NULL | | +| last-editor-uid | Last editor user | mediumint unsigned | YES | | NULL | | +| assigned-uid | Assigned moderator user | mediumint unsigned | YES | | NULL | | +| status | Status of the report, one of Entity\Report::STATUS_* | tinyint unsigned | NO | | NULL | | +| resolution | Resolution of the report, one of Entity\Report::RESOLUTION_* | tinyint unsigned | YES | | NULL | | +| created | | datetime(6) | NO | | 0001-01-01 00:00:00.000000 | | +| edited | Last time the report has been edited | datetime(6) | YES | | NULL | | Indexes ------------ diff --git a/src/Database/DBA.php b/src/Database/DBA.php index 930e60472f..0c0fd0f80d 100644 --- a/src/Database/DBA.php +++ b/src/Database/DBA.php @@ -42,6 +42,11 @@ class DBA */ const NULL_DATETIME = '0001-01-01 00:00:00'; + /** + * Lowest possible datetime(6) value + */ + const NULL_DATETIME6 = '0001-01-01 00:00:00.000000'; + public static function connect(): bool { return DI::dba()->connect(); diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index 6291d0ffc4..6ac52ea9c3 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -284,7 +284,7 @@ class DBStructure echo $sql; } if ($action) { - $r = DBA::e($sql); + $r = DBA::e(str_replace('\\', '\\\\', $sql)); if (!DBA::isResult($r)) { $errors .= self::printUpdateError($name); } @@ -493,7 +493,7 @@ class DBStructure DI::config()->set('system', 'maintenance_reason', DI::l10n()->t('%s: updating %s table.', DateTimeFormat::utcNow() . ' ' . date('e'), $name)); } - $r = DBA::e($sql3); + $r = DBA::e(str_replace('\\', '\\\\', $sql3)); if (!DBA::isResult($r)) { $errors .= self::printUpdateError($sql3); } diff --git a/src/Module/BaseModeration.php b/src/Module/BaseModeration.php index a575e51f79..8fa9733eb6 100644 --- a/src/Module/BaseModeration.php +++ b/src/Module/BaseModeration.php @@ -107,6 +107,7 @@ abstract class BaseModeration extends BaseModule $aside_sub = [ 'information' => [$this->t('Information'), [ 'overview' => ['moderation', $this->t('Overview'), 'overview'], + 'reports' => ['moderation/reports', $this->t('Reports'), 'overview'], ]], 'configuration' => [$this->t('Configuration'), [ 'users' => ['moderation/users', $this->t('Users'), 'users'], diff --git a/src/Module/Moderation/Reports.php b/src/Module/Moderation/Reports.php new file mode 100644 index 0000000000..4792efe628 --- /dev/null +++ b/src/Module/Moderation/Reports.php @@ -0,0 +1,107 @@ +. + * + */ + +namespace Friendica\Module\Moderation; + +use Friendica\App; +use Friendica\Content\Pager; +use Friendica\Content\Text\BBCode; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Database\Database; +use Friendica\Database\DBA; +use Friendica\Module\BaseModeration; +use Friendica\Module\Response; +use Friendica\Navigation\SystemMessages; +use Friendica\Util\DateTimeFormat; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; + +class Reports extends BaseModeration +{ + /** @var Database */ + private $database; + + public function __construct(Database $database, App\Page $page, App $app, SystemMessages $systemMessages, IHandleUserSessions $session, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = []) + { + parent::__construct($page, $app, $systemMessages, $session, $l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); + + $this->database = $database; + } + + protected function content(array $request = []): string + { + parent::content(); + + $total = $this->database->count('report'); + + $pager = new Pager($this->l10n, $this->args->getQueryString(), 10); + + $query = $this->database->p("SELECT `report`.`id`, `report`.`cid`, `report`.`comment`, `report`.`forward`, `report`.`created`, `report`.`reporter-id`, + `report`.`category`, `report`.`rules`, `contact`.`micro`, `contact`.`name`, `contact`.`nick`, `contact`.`url`, `contact`.`addr` FROM report + INNER JOIN `contact` ON `contact`.`id` = `report`.`cid` ORDER BY `report`.`created` DESC LIMIT ?, ?", $pager->getStart(), $pager->getItemsPerPage()); + + $reports = []; + while ($report = $this->database->fetch($query)) { + $report['posts'] = []; + $report['created'] = DateTimeFormat::local($report['created'], DateTimeFormat::MYSQL); + + $reports[$report['id']] = $report; + } + $this->database->close($query); + + $condition = ["SELECT `post-view`.`created`, `post-view`.`guid`, `post-view`.`plink`, `post-view`.`title`, `post-view`.`body`, `report-post`.`rid` + FROM `report-post` INNER JOIN `post-view` ON `report-post`.`uri-id` = `post-view`.`uri-id`"]; + $condition = DBA::mergeConditions($condition, ['rid' => array_keys($reports)]); + $posts = $this->database->p(array_shift($condition), $condition); + while ($post = $this->database->fetch($posts)) { + if (in_array($post['rid'], array_keys($reports))) { + $post['created'] = DateTimeFormat::local($post['created'], DateTimeFormat::MYSQL); + $post['body'] = BBCode::toPlaintext($post['body']); + + $reports[$post['rid']]['posts'][] = $post; + } + } + $this->database->close($posts); + + $t = Renderer::getMarkupTemplate('moderation/report/overview.tpl'); + return Renderer::replaceMacros($t, [ + // strings // + '$title' => $this->t('Moderation'), + '$page' => $this->t('List of reports'), + '$description' => $this->t('This page display reports created by our or remote users.'), + '$no_data' => $this->t('No report exists at this node.'), + + '$h_reports' => $this->t('Reports'), + '$th_reports' => [$this->t('Created'), $this->t('Photo'), $this->t('Name'), $this->t('Comment'), $this->t('Category')], + + // values // + '$baseurl' => $this->baseUrl, + + '$reports' => $reports, + '$total_reports' => $this->tt('%s total report', '%s total reports', $total), + '$paginate' => $pager->renderFull($total), + + '$contacturl' => ['contact_url', $this->t('Profile URL'), '', $this->t('URL of the reported contact.')], + ]); + } +} diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 318a836efd..cc03bb98e4 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -56,7 +56,7 @@ use Friendica\Database\DBA; // This file is required several times during the test in DbaDefinition which justifies this condition if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1531); + define('DB_UPDATE_VERSION', 1532); } return [ @@ -1328,7 +1328,7 @@ return [ "comment" => "Engagement data per post", "fields" => [ "uri-id" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the item uri"], - "owner-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id", "on delete" => "restrict"], "comment" => "Item owner"], + "owner-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "comment" => "Item owner"], "contact-type" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => "Person, organisation, news, community, relay"], "media-type" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => "Type of media in a bit array (1 = image, 2 = video, 4 = audio"], "language" => ["type" => "varbinary(128)", "comment" => "Language information about this post"], @@ -1736,7 +1736,7 @@ return [ "assigned-uid" => ["type" => "mediumint unsigned", "foreign" => ["user" => "uid"], "comment" => "Assigned moderator user"], "status" => ["type" => "tinyint unsigned", "not null" => "1", "comment" => "Status of the report, one of Entity\Report::STATUS_*"], "resolution" => ["type" => "tinyint unsigned", "comment" => "Resolution of the report, one of Entity\Report::RESOLUTION_*"], - "created" => ["type" => "datetime(6)", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""], + "created" => ["type" => "datetime(6)", "not null" => "1", "default" => DBA::NULL_DATETIME6, "comment" => ""], "edited" => ["type" => "datetime(6)", "comment" => "Last time the report has been edited"], ], "indexes" => [ diff --git a/static/routes.config.php b/static/routes.config.php index 6cd979d8e4..afad469fd3 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -510,6 +510,7 @@ return [ '/item/source[/{guid}]' => [Module\Moderation\Item\Source::class, [R::GET, R::POST]], '/report/create' => [Module\Moderation\Report\Create::class, [R::GET, R::POST]], + '/reports' => [Module\Moderation\Reports::class, [R::GET, R::POST]], '/users[/{action}/{uid}]' => [Module\Moderation\Users\Index::class, [R::GET, R::POST]], '/users/active[/{action}/{uid}]' => [Module\Moderation\Users\Active::class, [R::GET, R::POST]], diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index 849f61285c..8720f34231 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2023.09-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 20:42+0000\n" +"POT-Creation-Date: 2023-09-10 07:51+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -606,7 +606,8 @@ msgid "This is you" msgstr "" #: mod/photos.php:1141 mod/photos.php:1197 mod/photos.php:1277 -#: src/Object/Post.php:572 src/Object/Post.php:1094 +#: src/Module/Moderation/Reports.php:96 src/Object/Post.php:572 +#: src/Object/Post.php:1094 msgid "Comment" msgstr "" @@ -2119,13 +2120,13 @@ msgstr "" msgid "Site setup and configuration" msgstr "" -#: src/Content/Nav.php:335 src/Module/BaseModeration.php:127 +#: src/Content/Nav.php:335 src/Module/BaseModeration.php:128 #: src/Module/Moderation/Blocklist/Contact.php:110 #: src/Module/Moderation/Blocklist/Server/Add.php:121 #: src/Module/Moderation/Blocklist/Server/Import.php:118 #: src/Module/Moderation/Blocklist/Server/Index.php:95 #: src/Module/Moderation/Item/Delete.php:61 -#: src/Module/Moderation/Summary.php:76 +#: src/Module/Moderation/Reports.php:90 src/Module/Moderation/Summary.php:76 #: src/Module/Moderation/Users/Active.php:133 #: src/Module/Moderation/Users/Blocked.php:133 #: src/Module/Moderation/Users/Deleted.php:80 @@ -4275,7 +4276,8 @@ msgstr "" msgid "Job Parameters" msgstr "" -#: src/Module/Admin/Queue.php:78 src/Module/Settings/OAuth.php:74 +#: src/Module/Admin/Queue.php:78 src/Module/Moderation/Reports.php:96 +#: src/Module/Settings/OAuth.php:74 msgid "Created" msgstr "" @@ -5575,7 +5577,7 @@ msgstr "" msgid "Overview" msgstr "" -#: src/Module/BaseAdmin.php:89 src/Module/BaseModeration.php:111 +#: src/Module/BaseAdmin.php:89 src/Module/BaseModeration.php:112 msgid "Configuration" msgstr "" @@ -5599,7 +5601,7 @@ msgstr "" msgid "Inspect worker Queue" msgstr "" -#: src/Module/BaseAdmin.php:106 src/Module/BaseModeration.php:119 +#: src/Module/BaseAdmin.php:106 src/Module/BaseModeration.php:120 msgid "Diagnostics" msgstr "" @@ -5627,7 +5629,7 @@ msgstr "" msgid "Addon Features" msgstr "" -#: src/Module/BaseAdmin.php:121 src/Module/BaseModeration.php:128 +#: src/Module/BaseAdmin.php:121 src/Module/BaseModeration.php:129 msgid "User registrations waiting for confirmation" msgstr "" @@ -5668,28 +5670,32 @@ msgid "" "the main account." msgstr "" -#: src/Module/BaseModeration.php:112 src/Module/Moderation/Users/Index.php:148 +#: src/Module/BaseModeration.php:110 src/Module/Moderation/Reports.php:95 +msgid "Reports" +msgstr "" + +#: src/Module/BaseModeration.php:113 src/Module/Moderation/Users/Index.php:148 #: src/Module/Moderation/Users/Index.php:158 msgid "Users" msgstr "" -#: src/Module/BaseModeration.php:114 +#: src/Module/BaseModeration.php:115 msgid "Tools" msgstr "" -#: src/Module/BaseModeration.php:115 +#: src/Module/BaseModeration.php:116 msgid "Contact Blocklist" msgstr "" -#: src/Module/BaseModeration.php:116 +#: src/Module/BaseModeration.php:117 msgid "Server Blocklist" msgstr "" -#: src/Module/BaseModeration.php:117 src/Module/Moderation/Item/Delete.php:62 +#: src/Module/BaseModeration.php:118 src/Module/Moderation/Item/Delete.php:62 msgid "Delete Item" msgstr "" -#: src/Module/BaseModeration.php:120 src/Module/Moderation/Item/Source.php:76 +#: src/Module/BaseModeration.php:121 src/Module/Moderation/Item/Source.php:76 msgid "Item Source" msgstr "" @@ -6160,6 +6166,7 @@ msgstr "" #: src/Module/Contact/Advanced.php:134 #: src/Module/Moderation/Blocklist/Contact.php:122 +#: src/Module/Moderation/Reports.php:96 #: src/Module/Moderation/Users/Active.php:126 #: src/Module/Moderation/Users/Blocked.php:126 #: src/Module/Moderation/Users/Create.php:70 @@ -6283,6 +6290,7 @@ msgstr "" #: src/Module/Contact/Follow.php:170 src/Module/Contact/Profile.php:396 #: src/Module/Contact/Unfollow.php:129 #: src/Module/Moderation/Blocklist/Contact.php:133 +#: src/Module/Moderation/Reports.php:105 #: src/Module/Notifications/Introductions.php:129 #: src/Module/Notifications/Introductions.php:198 msgid "Profile URL" @@ -7577,6 +7585,7 @@ msgid "Block New Remote Contact" msgstr "" #: src/Module/Moderation/Blocklist/Contact.php:122 +#: src/Module/Moderation/Reports.php:96 msgid "Photo" msgstr "" @@ -8138,6 +8147,33 @@ msgstr "" msgid "3. Pick posts" msgstr "" +#: src/Module/Moderation/Reports.php:91 +msgid "List of reports" +msgstr "" + +#: src/Module/Moderation/Reports.php:92 +msgid "This page display reports created by our or remote users." +msgstr "" + +#: src/Module/Moderation/Reports.php:93 +msgid "No report exists at this node." +msgstr "" + +#: src/Module/Moderation/Reports.php:96 +msgid "Category" +msgstr "" + +#: src/Module/Moderation/Reports.php:102 +#, php-format +msgid "%s total report" +msgid_plural "%s total reports" +msgstr[0] "" +msgstr[1] "" + +#: src/Module/Moderation/Reports.php:105 +msgid "URL of the reported contact." +msgstr "" + #: src/Module/Moderation/Summary.php:53 msgid "Normal Account" msgstr "" diff --git a/view/templates/moderation/report/overview.tpl b/view/templates/moderation/report/overview.tpl new file mode 100644 index 0000000000..38fd603474 --- /dev/null +++ b/view/templates/moderation/report/overview.tpl @@ -0,0 +1,56 @@ +
+

{{$title}} - {{$page}}

+

{{$description nofilter}}

+ +

{{$h_reports}}

+ {{if $reports}} + + + + {{foreach $th_reports as $th}} + + {{/foreach}} + + + + {{foreach $reports as $report}} + + + + + + + + {{if $report.posts}} + + + + {{/if}} + {{/foreach}} + +
+ {{$th}} +
+ {{$report.created}} + {{$report.nickname}} + {{$report.name}}
+
{{if $report.addr}}{{$report.addr}}{{else}}{{$report.url}}{{/if}} +
{{if $report.comment}}{{$report.comment}}{{else}}N/A{{/if}}{{if $report.category}}{{$report.category}}{{else}}N/A{{/if}}
+ + {{foreach $report.posts as $post}} + + + + + {{/foreach}} +
+ {{$post.created}}
+
+ {{$post.body}} +
+
+ {{$paginate nofilter}} + {{else}} +

{{$no_data}}

+ {{/if}} +