From 18f54f4425cd849c857ffd3ec00e08cf31f4adec Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 5 Nov 2022 21:01:06 -0400 Subject: [PATCH] New area "moderation" - Moved several admin pages to the moderation area - ACL still is checking for administrator credentials --- doc/Accesskeys.md | 1 + src/Content/Nav.php | 4 +- src/Module/Admin/BaseUsers.php | 130 ------------- src/Module/Admin/Blocklist/Contact.php | 122 ------------ src/Module/Admin/Blocklist/Server/Add.php | 145 -------------- src/Module/Admin/Blocklist/Server/Import.php | 136 ------------- src/Module/Admin/Blocklist/Server/Index.php | 119 ------------ src/Module/Admin/Item/Delete.php | 73 ------- src/Module/Admin/Summary.php | 24 --- src/Module/Admin/Users/Active.php | 164 ---------------- src/Module/Admin/Users/Blocked.php | 164 ---------------- src/Module/Admin/Users/Index.php | 181 ------------------ src/Module/BaseAdmin.php | 7 - src/Module/BaseModeration.php | 134 +++++++++++++ src/Module/Moderation/BaseUsers.php | 149 ++++++++++++++ src/Module/Moderation/Blocklist/Contact.php | 138 +++++++++++++ .../Moderation/Blocklist/Server/Add.php | 143 ++++++++++++++ .../Moderation/Blocklist/Server/Import.php | 134 +++++++++++++ .../Moderation/Blocklist/Server/Index.php | 121 ++++++++++++ src/Module/Moderation/Item/Delete.php | 71 +++++++ .../{Admin => Moderation}/Item/Source.php | 40 ++-- src/Module/Moderation/Summary.php | 84 ++++++++ src/Module/Moderation/Users/Active.php | 161 ++++++++++++++++ src/Module/Moderation/Users/Blocked.php | 160 ++++++++++++++++ .../{Admin => Moderation}/Users/Create.php | 34 ++-- .../{Admin => Moderation}/Users/Deleted.php | 41 ++-- src/Module/Moderation/Users/Index.php | 179 +++++++++++++++++ .../{Admin => Moderation}/Users/Pending.php | 61 +++--- static/routes.config.php | 34 ++-- view/templates/admin/summary.tpl | 16 -- view/templates/moderation/aside.tpl | 21 ++ .../blocklist/contact.tpl | 0 .../blocklist/server/add.tpl | 0 .../blocklist/server/import.tpl | 0 .../blocklist/server/index.tpl | 0 .../{admin => moderation}/item/delete.tpl | 0 .../{admin => moderation}/item/source.tpl | 0 view/templates/moderation/settings_head.tpl | 9 + view/templates/moderation/summary.tpl | 16 ++ .../{admin => moderation}/users/active.tpl | 0 .../{admin => moderation}/users/blocked.tpl | 0 .../{admin => moderation}/users/create.tpl | 0 .../{admin => moderation}/users/deleted.tpl | 0 .../{admin => moderation}/users/index.tpl | 0 .../{admin => moderation}/users/pending.tpl | 0 view/templates/nav.tpl | 2 + view/theme/duepuntozero/templates/nav.tpl | 2 + view/theme/frio/templates/admin/summary.tpl | 23 --- .../theme/frio/templates/moderation/aside.tpl | 30 +++ .../blocklist/contact.tpl | 0 .../frio/templates/moderation/summary.tpl | 33 ++++ .../{admin => moderation}/users/active.tpl | 0 .../{admin => moderation}/users/blocked.tpl | 0 .../{admin => moderation}/users/create.tpl | 0 .../{admin => moderation}/users/deleted.tpl | 0 .../{admin => moderation}/users/index.tpl | 0 .../{admin => moderation}/users/pending.tpl | 0 view/theme/frio/templates/nav.tpl | 9 + view/theme/quattro/templates/nav.tpl | 1 + view/theme/smoothly/templates/nav.tpl | 1 + view/theme/vier/templates/nav.tpl | 7 +- 61 files changed, 1707 insertions(+), 1417 deletions(-) delete mode 100644 src/Module/Admin/BaseUsers.php delete mode 100644 src/Module/Admin/Blocklist/Contact.php delete mode 100644 src/Module/Admin/Blocklist/Server/Add.php delete mode 100644 src/Module/Admin/Blocklist/Server/Import.php delete mode 100644 src/Module/Admin/Blocklist/Server/Index.php delete mode 100644 src/Module/Admin/Item/Delete.php delete mode 100644 src/Module/Admin/Users/Active.php delete mode 100644 src/Module/Admin/Users/Blocked.php delete mode 100644 src/Module/Admin/Users/Index.php create mode 100644 src/Module/BaseModeration.php create mode 100644 src/Module/Moderation/BaseUsers.php create mode 100644 src/Module/Moderation/Blocklist/Contact.php create mode 100644 src/Module/Moderation/Blocklist/Server/Add.php create mode 100644 src/Module/Moderation/Blocklist/Server/Import.php create mode 100644 src/Module/Moderation/Blocklist/Server/Index.php create mode 100644 src/Module/Moderation/Item/Delete.php rename src/Module/{Admin => Moderation}/Item/Source.php (61%) create mode 100644 src/Module/Moderation/Summary.php create mode 100644 src/Module/Moderation/Users/Active.php create mode 100644 src/Module/Moderation/Users/Blocked.php rename src/Module/{Admin => Moderation}/Users/Create.php (62%) rename src/Module/{Admin => Moderation}/Users/Deleted.php (59%) create mode 100644 src/Module/Moderation/Users/Index.php rename src/Module/{Admin => Moderation}/Users/Pending.php (58%) create mode 100644 view/templates/moderation/aside.tpl rename view/templates/{admin => moderation}/blocklist/contact.tpl (100%) rename view/templates/{admin => moderation}/blocklist/server/add.tpl (100%) rename view/templates/{admin => moderation}/blocklist/server/import.tpl (100%) rename view/templates/{admin => moderation}/blocklist/server/index.tpl (100%) rename view/templates/{admin => moderation}/item/delete.tpl (100%) rename view/templates/{admin => moderation}/item/source.tpl (100%) create mode 100644 view/templates/moderation/settings_head.tpl create mode 100644 view/templates/moderation/summary.tpl rename view/templates/{admin => moderation}/users/active.tpl (100%) rename view/templates/{admin => moderation}/users/blocked.tpl (100%) rename view/templates/{admin => moderation}/users/create.tpl (100%) rename view/templates/{admin => moderation}/users/deleted.tpl (100%) rename view/templates/{admin => moderation}/users/index.tpl (100%) rename view/templates/{admin => moderation}/users/pending.tpl (100%) create mode 100644 view/theme/frio/templates/moderation/aside.tpl rename view/theme/frio/templates/{admin => moderation}/blocklist/contact.tpl (100%) create mode 100644 view/theme/frio/templates/moderation/summary.tpl rename view/theme/frio/templates/{admin => moderation}/users/active.tpl (100%) rename view/theme/frio/templates/{admin => moderation}/users/blocked.tpl (100%) rename view/theme/frio/templates/{admin => moderation}/users/create.tpl (100%) rename view/theme/frio/templates/{admin => moderation}/users/deleted.tpl (100%) rename view/theme/frio/templates/{admin => moderation}/users/index.tpl (100%) rename view/theme/frio/templates/{admin => moderation}/users/pending.tpl (100%) diff --git a/doc/Accesskeys.md b/doc/Accesskeys.md index f79524c1b3..a94fd1ea96 100644 --- a/doc/Accesskeys.md +++ b/doc/Accesskeys.md @@ -19,6 +19,7 @@ General * c - Community * s - Search * a - Admin +* m - Moderation * f - Notifications * u - User menu diff --git a/src/Content/Nav.php b/src/Content/Nav.php index 17b6412df2..0000b3d296 100644 --- a/src/Content/Nav.php +++ b/src/Content/Nav.php @@ -162,6 +162,7 @@ class Nav $nav = [ 'admin' => null, + 'moderation' => null, 'apps' => null, 'community' => null, 'home' => null, @@ -298,7 +299,8 @@ class Nav // Show the link to the admin configuration page if user is admin if ($a->isSiteAdmin()) { - $nav['admin'] = ['admin/', DI::l10n()->t('Admin'), '', DI::l10n()->t('Site setup and configuration')]; + $nav['admin'] = ['admin/', DI::l10n()->t('Admin'), '', DI::l10n()->t('Site setup and configuration')]; + $nav['moderation'] = ['moderation/', DI::l10n()->t('Moderation'), '', DI::l10n()->t('Content and user moderation')]; } $nav['navigation'] = ['navigation/', DI::l10n()->t('Navigation'), '', DI::l10n()->t('Site map')]; diff --git a/src/Module/Admin/BaseUsers.php b/src/Module/Admin/BaseUsers.php deleted file mode 100644 index 93433efbe6..0000000000 --- a/src/Module/Admin/BaseUsers.php +++ /dev/null @@ -1,130 +0,0 @@ -. - * - */ - -namespace Friendica\Module\Admin; - -use Friendica\Core\Renderer; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\Register; -use Friendica\Model\User; -use Friendica\Module\BaseAdmin; -use Friendica\Util\Temporal; - -abstract class BaseUsers extends BaseAdmin -{ - /** - * Get the users admin tabs menu - * - * @param string $selectedTab - * @return string HTML - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - protected static function getTabsHTML(string $selectedTab) - { - $all = DBA::count('user', ["`uid` != ?", 0]); - $active = DBA::count('user', ["NOT `blocked` AND `verified` AND NOT `account_removed` AND `uid` != ?", 0]); - $pending = Register::getPendingCount(); - $blocked = DBA::count('user', ['blocked' => true, 'verified' => true, 'account_removed' => false]); - $deleted = DBA::count('user', ['account_removed' => true]); - - $tabs = [ - [ - 'label' => DI::l10n()->t('All') . ' (' . $all . ')', - 'url' => 'admin/users', - 'sel' => !$selectedTab || $selectedTab == 'all' ? 'active' : '', - 'title' => DI::l10n()->t('List of all users'), - 'id' => 'admin-users-all', - 'accesskey' => 'a', - ], - [ - 'label' => DI::l10n()->t('Active') . ' (' . $active . ')', - 'url' => 'admin/users/active', - 'sel' => $selectedTab == 'active' ? 'active' : '', - 'title' => DI::l10n()->t('List of active accounts'), - 'id' => 'admin-users-active', - 'accesskey' => 'k', - ], - [ - 'label' => DI::l10n()->t('Pending') . ($pending ? ' (' . $pending . ')' : ''), - 'url' => 'admin/users/pending', - 'sel' => $selectedTab == 'pending' ? 'active' : '', - 'title' => DI::l10n()->t('List of pending registrations'), - 'id' => 'admin-users-pending', - 'accesskey' => 'p', - ], - [ - 'label' => DI::l10n()->t('Blocked') . ($blocked ? ' (' . $blocked . ')' : ''), - 'url' => 'admin/users/blocked', - 'sel' => $selectedTab == 'blocked' ? 'active' : '', - 'title' => DI::l10n()->t('List of blocked users'), - 'id' => 'admin-users-blocked', - 'accesskey' => 'b', - ], - [ - 'label' => DI::l10n()->t('Deleted') . ($deleted ? ' (' . $deleted . ')' : ''), - 'url' => 'admin/users/deleted', - 'sel' => $selectedTab == 'deleted' ? 'active' : '', - 'title' => DI::l10n()->t('List of pending user deletions'), - 'id' => 'admin-users-deleted', - 'accesskey' => 'd', - ], - ]; - - $tpl = Renderer::getMarkupTemplate('common_tabs.tpl'); - return Renderer::replaceMacros($tpl, ['$tabs' => $tabs]); - } - - protected static function setupUserCallback() { - $adminlist = explode(',', str_replace(' ', '', DI::config()->get('config', 'admin_email'))); - return function ($user) use ($adminlist) { - $page_types = [ - User::PAGE_FLAGS_NORMAL => DI::l10n()->t('Normal Account Page'), - User::PAGE_FLAGS_SOAPBOX => DI::l10n()->t('Soapbox Page'), - User::PAGE_FLAGS_COMMUNITY => DI::l10n()->t('Public Forum'), - User::PAGE_FLAGS_FREELOVE => DI::l10n()->t('Automatic Friend Page'), - User::PAGE_FLAGS_PRVGROUP => DI::l10n()->t('Private Forum') - ]; - $account_types = [ - User::ACCOUNT_TYPE_PERSON => DI::l10n()->t('Personal Page'), - User::ACCOUNT_TYPE_ORGANISATION => DI::l10n()->t('Organisation Page'), - User::ACCOUNT_TYPE_NEWS => DI::l10n()->t('News Page'), - User::ACCOUNT_TYPE_COMMUNITY => DI::l10n()->t('Community Forum'), - User::ACCOUNT_TYPE_RELAY => DI::l10n()->t('Relay'), - ]; - - $user['page_flags_raw'] = $user['page-flags']; - $user['page_flags'] = $page_types[$user['page-flags']]; - - $user['account_type_raw'] = ($user['page_flags_raw'] == 0) ? $user['account-type'] : -1; - $user['account_type'] = ($user['page_flags_raw'] == 0) ? $account_types[$user['account-type']] : ''; - - $user['register_date'] = Temporal::getRelativeDate($user['register_date']); - $user['login_date'] = Temporal::getRelativeDate($user['login_date']); - $user['lastitem_date'] = Temporal::getRelativeDate($user['last-item']); - $user['is_admin'] = in_array($user['email'], $adminlist); - $user['is_deletable'] = !$user['account_removed'] && intval($user['uid']) != DI::userSession()->getLocalUserId(); - $user['deleted'] = ($user['account_removed'] ? Temporal::getRelativeDate($user['account_expires_on']) : False); - - return $user; - }; - } -} diff --git a/src/Module/Admin/Blocklist/Contact.php b/src/Module/Admin/Blocklist/Contact.php deleted file mode 100644 index 85e7f2146a..0000000000 --- a/src/Module/Admin/Blocklist/Contact.php +++ /dev/null @@ -1,122 +0,0 @@ -. - * - */ - -namespace Friendica\Module\Admin\Blocklist; - -use Friendica\Content\Pager; -use Friendica\Core\Renderer; -use Friendica\Core\Worker; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model; -use Friendica\Module\BaseAdmin; -use Friendica\Util\Network; - -class Contact extends BaseAdmin -{ - protected function post(array $request = []) - { - self::checkAdminAccess(); - - self::checkFormSecurityTokenRedirectOnError('/admin/blocklist/contact', 'admin_contactblock'); - - $contact_url = $_POST['contact_url'] ?? ''; - $block_reason = $_POST['contact_block_reason'] ?? ''; - $block_purge = $_POST['contact_block_purge'] ?? false; - $contacts = $_POST['contacts'] ?? []; - - if (!empty($_POST['page_contactblock_block'])) { - $contact = Model\Contact::getByURL($contact_url, null, ['id', 'nurl']); - if (empty($contact)) { - DI::sysmsg()->addNotice(DI::l10n()->t('Could not find any contact entry for this URL (%s)', $contact_url)); - DI::baseUrl()->redirect('admin/blocklist/contact'); - } - - if (Network::isLocalLink($contact['nurl'])) { - DI::sysmsg()->addNotice(DI::l10n()->t('You can\'t block a local contact, please block the user instead')); - DI::baseUrl()->redirect('admin/blocklist/contact'); - } - - Model\Contact::block($contact['id'], $block_reason); - - if ($block_purge) { - foreach (Model\Contact::selectToArray(['id'], ['nurl' => $contact['nurl']]) as $contact) { - Worker::add(Worker::PRIORITY_LOW, 'Contact\RemoveContent', $contact['id']); - } - } - - DI::sysmsg()->addInfo(DI::l10n()->t('The contact has been blocked from the node')); - } - - if (!empty($_POST['page_contactblock_unblock'])) { - foreach ($contacts as $uid) { - Model\Contact::unblock($uid); - } - DI::sysmsg()->addInfo(DI::l10n()->tt('%s contact unblocked', '%s contacts unblocked', count($contacts))); - } - - DI::baseUrl()->redirect('admin/blocklist/contact'); - } - - protected function content(array $request = []): string - { - parent::content(); - - $condition = ['uid' => 0, 'blocked' => true]; - - $total = DBA::count('contact', $condition); - - $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), 30); - - $contacts = Model\Contact::selectToArray([], $condition, ['limit' => [$pager->getStart(), $pager->getItemsPerPage()]]); - - $t = Renderer::getMarkupTemplate('admin/blocklist/contact.tpl'); - $o = Renderer::replaceMacros($t, [ - // strings // - '$title' => DI::l10n()->t('Administration'), - '$page' => DI::l10n()->t('Remote Contact Blocklist'), - '$description' => DI::l10n()->t('This page allows you to prevent any message from a remote contact to reach your node.'), - '$submit' => DI::l10n()->t('Block Remote Contact'), - '$select_all' => DI::l10n()->t('select all'), - '$select_none' => DI::l10n()->t('select none'), - '$block' => DI::l10n()->t('Block'), - '$unblock' => DI::l10n()->t('Unblock'), - '$no_data' => DI::l10n()->t('No remote contact is blocked from this node.'), - - '$h_contacts' => DI::l10n()->t('Blocked Remote Contacts'), - '$h_newblock' => DI::l10n()->t('Block New Remote Contact'), - '$th_contacts' => [DI::l10n()->t('Photo'), DI::l10n()->t('Name'), DI::l10n()->t('Reason')], - - '$form_security_token' => self::getFormSecurityToken('admin_contactblock'), - - // values // - '$baseurl' => DI::baseUrl()->get(true), - - '$contacts' => $contacts, - '$total_contacts' => DI::l10n()->tt('%s total blocked contact', '%s total blocked contacts', $total), - '$paginate' => $pager->renderFull($total), - '$contacturl' => ['contact_url', DI::l10n()->t('Profile URL'), '', DI::l10n()->t('URL of the remote contact to block.')], - '$contact_block_purge' => ['contact_block_purge', DI::l10n()->t('Also purge contact'), false, DI::l10n()->t('Removes all content related to this contact from the node. Keeps the contact record. This action cannot be undone.')], - '$contact_block_reason' => ['contact_block_reason', DI::l10n()->t('Block Reason')], - ]); - return $o; - } -} diff --git a/src/Module/Admin/Blocklist/Server/Add.php b/src/Module/Admin/Blocklist/Server/Add.php deleted file mode 100644 index f060bcd060..0000000000 --- a/src/Module/Admin/Blocklist/Server/Add.php +++ /dev/null @@ -1,145 +0,0 @@ -. - * - */ - -namespace Friendica\Module\Admin\Blocklist\Server; - -use Friendica\App; -use Friendica\Content\ContactSelector; -use Friendica\Core\L10n; -use Friendica\Core\Renderer; -use Friendica\Core\Worker; -use Friendica\Model\Contact; -use Friendica\Model\GServer; -use Friendica\Moderation\DomainPatternBlocklist; -use Friendica\Module\BaseAdmin; -use Friendica\Module\Response; -use Friendica\Navigation\SystemMessages; -use Friendica\Util\Profiler; -use GuzzleHttp\Psr7\Uri; -use Psr\Log\LoggerInterface; - -class Add extends BaseAdmin -{ - /** @var SystemMessages */ - private $sysmsg; - - /** @var DomainPatternBlocklist */ - private $blocklist; - - public function __construct(SystemMessages $sysmsg, DomainPatternBlocklist $blocklist, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = []) - { - parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); - - $this->sysmsg = $sysmsg; - $this->blocklist = $blocklist; - } - - /** - * @param array $request - * @return void - * @throws \Friendica\Network\HTTPException\ForbiddenException - * @throws \Friendica\Network\HTTPException\FoundException - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \Friendica\Network\HTTPException\MovedPermanentlyException - * @throws \Friendica\Network\HTTPException\TemporaryRedirectException - * @throws \Exception - */ - protected function post(array $request = []) - { - self::checkAdminAccess(); - - if (empty($request['page_blocklist_add'])) { - return; - } - - self::checkFormSecurityTokenRedirectOnError('/admin/blocklist/server/add', 'admin_blocklist_add'); - - $pattern = trim($request['pattern']); - - // Add new item to blocklist - $this->blocklist->addPattern($pattern, trim($request['reason'])); - - $this->sysmsg->addInfo($this->l10n->t('Server domain pattern added to the blocklist.')); - - if (!empty($request['purge'])) { - $gservers = GServer::listByDomainPattern($pattern); - foreach (Contact::selectToArray(['id'], ['gsid' => array_column($gservers, 'id')]) as $contact) { - Worker::add(Worker::PRIORITY_LOW, 'Contact\RemoveContent', $contact['id']); - } - - $this->sysmsg->addInfo($this->l10n->tt('%s server scheduled to be purged.', '%s servers scheduled to be purged.', count($gservers))); - } - - $this->baseUrl->redirect('admin/blocklist/server'); - } - - /** - * @param array $request - * @return string - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \Friendica\Network\HTTPException\ServiceUnavailableException - * @throws \Exception - */ - protected function content(array $request = []): string - { - parent::content(); - - $gservers = []; - - if ($pattern = trim($request['pattern'] ?? '')) { - $gservers = GServer::listByDomainPattern($pattern); - } - - array_walk($gservers, function (array &$gserver) { - $gserver['domain'] = (new Uri($gserver['url']))->getHost(); - $gserver['network_icon'] = ContactSelector::networkToIcon($gserver['network']); - $gserver['network_name'] = ContactSelector::networkToName($gserver['network']); - }); - - $t = Renderer::getMarkupTemplate('admin/blocklist/server/add.tpl'); - return Renderer::replaceMacros($t, [ - '$l10n' => [ - 'return_list' => $this->l10n->t('← Return to the list'), - 'title' => $this->l10n->t('Administration'), - 'page' => $this->l10n->t('Block A New Server Domain Pattern'), - 'syntax' => $this->l10n->t('

The server domain pattern syntax is case-insensitive shell wildcard, comprising the following special characters:

-'), - 'submit' => $this->l10n->t('Check pattern'), - 'matching_servers' => $this->l10n->t('Matching known servers'), - 'server_name' => $this->l10n->t('Server Name'), - 'server_domain' => $this->l10n->t('Server Domain'), - 'known_contacts' => $this->l10n->t('Known Contacts'), - 'server_count' => $this->l10n->tt('%d known server', '%d known servers', count($gservers)), - 'add_pattern' => $this->l10n->t('Add pattern to the blocklist'), - ], - '$newdomain' => ['pattern', $this->l10n->t('Server Domain Pattern'), $pattern, $this->l10n->t('The domain pattern of the new server to add to the blocklist. Do not include the protocol.'), $this->l10n->t('Required'), '', ''], - '$newpurge' => ['purge', $this->l10n->t('Purge server'), $request['purge'] ?? false, $this->l10n->tt('Also purges all the locally stored content authored by the known contacts registered on that server. Keeps the contacts and the server records. This action cannot be undone.', 'Also purges all the locally stored content authored by the known contacts registered on these servers. Keeps the contacts and the servers records. This action cannot be undone.', count($gservers))], - '$newreason' => ['reason', $this->l10n->t('Block reason'), $request['reason'] ?? '', $this->l10n->t('The reason why you blocked this server domain pattern. This reason will be shown publicly in the server information page.'), $this->l10n->t('Required'), '', ''], - '$pattern' => $pattern, - '$gservers' => $gservers, - '$baseurl' => $this->baseUrl->get(true), - '$form_security_token' => self::getFormSecurityToken('admin_blocklist_add') - ]); - } -} diff --git a/src/Module/Admin/Blocklist/Server/Import.php b/src/Module/Admin/Blocklist/Server/Import.php deleted file mode 100644 index 166f248e99..0000000000 --- a/src/Module/Admin/Blocklist/Server/Import.php +++ /dev/null @@ -1,136 +0,0 @@ -. - * - */ - -namespace Friendica\Module\Admin\Blocklist\Server; - -use Friendica\App; -use Friendica\Core\L10n; -use Friendica\Core\Renderer; -use Friendica\Moderation\DomainPatternBlocklist; -use Friendica\Module\Response; -use Friendica\Navigation\SystemMessages; -use Friendica\Util\Profiler; -use Psr\Log\LoggerInterface; - -class Import extends \Friendica\Module\BaseAdmin -{ - /** @var DomainPatternBlocklist */ - private $localBlocklist; - - /** @var SystemMessages */ - private $sysmsg; - - /** @var array of blocked server domain patterns */ - private $blocklist = []; - - public function __construct(DomainPatternBlocklist $localBlocklist, SystemMessages $sysmsg, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = []) - { - parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); - - $this->localBlocklist = $localBlocklist; - $this->sysmsg = $sysmsg; - } - - /** - * @param array $request - * @return void - * @throws \Friendica\Network\HTTPException\ForbiddenException - * @throws \Friendica\Network\HTTPException\FoundException - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \Friendica\Network\HTTPException\MovedPermanentlyException - * @throws \Friendica\Network\HTTPException\TemporaryRedirectException - */ - protected function post(array $request = []) - { - self::checkAdminAccess(); - - if (!isset($request['page_blocklist_upload']) && !isset($request['page_blocklist_import'])) { - return; - } - - self::checkFormSecurityTokenRedirectOnError('/admin/blocklist/server/import', 'admin_blocklist_import'); - - if (isset($request['page_blocklist_upload'])) { - try { - $this->blocklist = $this->localBlocklist::extractFromCSVFile($_FILES['listfile']['tmp_name']); - } catch (\Throwable $e) { - $this->sysmsg->addNotice($this->l10n->t('Error importing pattern file')); - } - - return; - } - - if (isset($request['page_blocklist_import'])) { - $blocklist = json_decode($request['blocklist'], true); - if ($blocklist === null) { - $this->sysmsg->addNotice($this->l10n->t('Error importing pattern file')); - return; - } - - if (($request['mode'] ?? 'append') == 'replace') { - $this->localBlocklist->set($blocklist); - $this->sysmsg->addNotice($this->l10n->t('Local blocklist replaced with the provided file.')); - } else { - $count = $this->localBlocklist->append($blocklist); - if ($count) { - $this->sysmsg->addNotice($this->l10n->tt('%d pattern was added to the local blocklist.', '%d patterns were added to the local blocklist.', $count)); - } else { - $this->sysmsg->addNotice($this->l10n->t('No pattern was added to the local blocklist.')); - } - } - - $this->baseUrl->redirect('/admin/blocklist/server'); - } - } - - /** - * @param array $request - * @return string - * @throws \Friendica\Network\HTTPException\ServiceUnavailableException - */ - protected function content(array $request = []): string - { - parent::content(); - - $t = Renderer::getMarkupTemplate('admin/blocklist/server/import.tpl'); - return Renderer::replaceMacros($t, [ - '$l10n' => [ - 'return_list' => $this->l10n->t('← Return to the list'), - 'title' => $this->l10n->t('Administration'), - 'page' => $this->l10n->t('Import a Server Domain Pattern Blocklist'), - 'download' => $this->l10n->t('

This file can be downloaded from the /friendica path of any Friendica server.

'), - 'upload' => $this->l10n->t('Upload file'), - 'patterns' => $this->l10n->t('Patterns to import'), - 'domain_pattern' => $this->l10n->t('Domain Pattern'), - 'block_reason' => $this->l10n->t('Block Reason'), - 'mode' => $this->l10n->t('Import Mode'), - 'import' => $this->l10n->t('Import Patterns'), - 'pattern_count' => $this->l10n->tt('%d total pattern', '%d total patterns', count($this->blocklist)), - ], - '$listfile' => ['listfile', $this->l10n->t('Server domain pattern blocklist CSV file'), '', '', $this->l10n->t('Required'), '', 'file'], - '$mode_append' => ['mode', $this->l10n->t('Append'), 'append', $this->l10n->t('Imports patterns from the file that weren\'t already existing in the current blocklist.'), 'checked="checked"'], - '$mode_replace' => ['mode', $this->l10n->t('Replace'), 'replace', $this->l10n->t('Replaces the current blocklist by the imported patterns.')], - '$blocklist' => $this->blocklist, - '$baseurl' => $this->baseUrl->get(true), - '$form_security_token' => self::getFormSecurityToken('admin_blocklist_import') - ]); - } -} diff --git a/src/Module/Admin/Blocklist/Server/Index.php b/src/Module/Admin/Blocklist/Server/Index.php deleted file mode 100644 index a4884ca7fc..0000000000 --- a/src/Module/Admin/Blocklist/Server/Index.php +++ /dev/null @@ -1,119 +0,0 @@ -. - * - */ - -namespace Friendica\Module\Admin\Blocklist\Server; - -use Friendica\App; -use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\L10n; -use Friendica\Core\Renderer; -use Friendica\Moderation\DomainPatternBlocklist; -use Friendica\Module\BaseAdmin; -use Friendica\Module\Response; -use Friendica\Util\Profiler; -use Psr\Log\LoggerInterface; - -class Index extends BaseAdmin -{ - /** @var DomainPatternBlocklist */ - private $blocklist; - - public function __construct(DomainPatternBlocklist $blocklist, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = []) - { - parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); - - $this->blocklist = $blocklist; - } - - protected function post(array $request = []) - { - self::checkAdminAccess(); - - if (empty($request['page_blocklist_edit'])) { - return; - } - - self::checkFormSecurityTokenRedirectOnError('/admin/blocklist/server', 'admin_blocklist'); - - // Edit the entries from blocklist - $blocklist = []; - foreach ($request['domain'] as $id => $domain) { - // Trimming whitespaces as well as any lingering slashes - $domain = trim($domain); - $reason = trim($request['reason'][$id]); - if (empty($request['delete'][$id])) { - $blocklist[] = [ - 'domain' => $domain, - 'reason' => $reason - ]; - } - } - - $this->blocklist->set($blocklist); - - $this->baseUrl->redirect('admin/blocklist/server'); - } - - protected function content(array $request = []): string - { - parent::content(); - - $blocklistform = []; - foreach ($this->blocklist->get() as $id => $b) { - $blocklistform[] = [ - 'domain' => ["domain[$id]", $this->l10n->t('Blocked server domain pattern'), $b['domain'], '', $this->l10n->t('Required'), '', ''], - 'reason' => ["reason[$id]", $this->l10n->t("Reason for the block"), $b['reason'], '', $this->l10n->t('Required'), '', ''], - 'delete' => ["delete[$id]", $this->l10n->t("Delete server domain pattern") . ' (' . $b['domain'] . ')', false, $this->l10n->t("Check to delete this entry from the blocklist")] - ]; - } - - $t = Renderer::getMarkupTemplate('admin/blocklist/server/index.tpl'); - return Renderer::replaceMacros($t, [ - '$l10n' => [ - 'title' => $this->l10n->t('Administration'), - 'page' => $this->l10n->t('Server Domain Pattern Blocklist'), - 'intro' => $this->l10n->t('This page can be used to define a blocklist of server domain patterns from the federated network that are not allowed to interact with your node. For each domain pattern you should also provide the reason why you block it.'), - 'public' => $this->l10n->t('The list of blocked server domain patterns will be made publically available on the /friendica page so that your users and people investigating communication problems can find the reason easily.'), - 'syntax' => $this->l10n->t('

The server domain pattern syntax is case-insensitive shell wildcard, comprising the following special characters:

-'), - 'importtitle' => $this->l10n->t('Import server domain pattern blocklist'), - 'addtitle' => $this->l10n->t('Add new entry to the blocklist'), - 'importsubmit' => $this->l10n->t('Upload file'), - 'addsubmit' => $this->l10n->t('Check pattern'), - 'savechanges' => $this->l10n->t('Save changes to the blocklist'), - 'currenttitle' => $this->l10n->t('Current Entries in the Blocklist'), - 'thurl' => $this->l10n->t('Blocked server domain pattern'), - 'threason' => $this->l10n->t('Reason for the block'), - 'delentry' => $this->l10n->t('Delete entry from the blocklist'), - 'confirm_delete' => $this->l10n->t('Delete entry from the blocklist?'), - ], - '$listfile' => ['listfile', $this->l10n->t('Server domain pattern blocklist CSV file'), '', '', $this->l10n->t('Required'), '', 'file'], - '$newdomain' => ['pattern', $this->l10n->t('Server Domain Pattern'), '', $this->l10n->t('The domain pattern of the new server to add to the blocklist. Do not include the protocol.'), $this->l10n->t('Required'), '', ''], - '$entries' => $blocklistform, - '$baseurl' => $this->baseUrl->get(true), - '$form_security_token' => self::getFormSecurityToken('admin_blocklist'), - '$form_security_token_import' => self::getFormSecurityToken('admin_blocklist_import'), - ]); - } -} diff --git a/src/Module/Admin/Item/Delete.php b/src/Module/Admin/Item/Delete.php deleted file mode 100644 index d291e7b840..0000000000 --- a/src/Module/Admin/Item/Delete.php +++ /dev/null @@ -1,73 +0,0 @@ -. - * - */ - -namespace Friendica\Module\Admin\Item; - -use Friendica\Core\Renderer; -use Friendica\DI; -use Friendica\Model\Item; -use Friendica\Module\BaseAdmin; - -class Delete extends BaseAdmin -{ - protected function post(array $request = []) - { - self::checkAdminAccess(); - - if (empty($_POST['page_deleteitem_submit'])) { - return; - } - - self::checkFormSecurityTokenRedirectOnError('/admin/item/delete', 'admin_deleteitem'); - - if (!empty($_POST['page_deleteitem_submit'])) { - $guid = trim($_POST['deleteitemguid']); - // The GUID should not include a "/", so if there is one, we got an URL - // and the last part of it is most likely the GUID. - if (strpos($guid, '/')) { - $guid = substr($guid, strrpos($guid, '/') + 1); - } - // Now that we have the GUID, drop those items, which will also delete the - // associated threads. - Item::markForDeletion(['guid' => $guid]); - } - - DI::sysmsg()->addInfo(DI::l10n()->t('Item marked for deletion.')); - DI::baseUrl()->redirect('admin/item/delete'); - } - - protected function content(array $request = []): string - { - parent::content(); - - $t = Renderer::getMarkupTemplate('admin/item/delete.tpl'); - - return Renderer::replaceMacros($t, [ - '$title' => DI::l10n()->t('Administration'), - '$page' => DI::l10n()->t('Delete Item'), - '$submit' => DI::l10n()->t('Delete this Item'), - '$intro1' => DI::l10n()->t('On this page you can delete an item from your node. If the item is a top level posting, the entire thread will be deleted.'), - '$intro2' => DI::l10n()->t('You need to know the GUID of the item. You can find it e.g. by looking at the display URL. The last part of http://example.com/display/123456 is the GUID, here 123456.'), - '$deleteitemguid' => ['deleteitemguid', DI::l10n()->t("GUID"), '', DI::l10n()->t("The GUID of the item you want to delete."), DI::l10n()->t('Required'), 'autofocus'], - '$form_security_token' => self::getFormSecurityToken("admin_deleteitem") - ]); - } -} diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index 7d159a483c..46d1ee9ad1 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -187,27 +187,6 @@ class Summary extends BaseAdmin } } - $accounts = [ - [DI::l10n()->t('Normal Account'), 0], - [DI::l10n()->t('Automatic Follower Account'), 0], - [DI::l10n()->t('Public Forum Account'), 0], - [DI::l10n()->t('Automatic Friend Account'), 0], - [DI::l10n()->t('Blog Account'), 0], - [DI::l10n()->t('Private Forum Account'), 0] - ]; - - $users = 0; - $pageFlagsCountStmt = DBA::p('SELECT `page-flags`, COUNT(`uid`) AS `count` FROM `user` WHERE `uid` != ? GROUP BY `page-flags`', 0); - while ($pageFlagsCount = DBA::fetch($pageFlagsCountStmt)) { - $accounts[$pageFlagsCount['page-flags']][1] = $pageFlagsCount['count']; - $users += $pageFlagsCount['count']; - } - DBA::close($pageFlagsCountStmt); - - Logger::debug('accounts', ['accounts' => $accounts]); - - $pending = Register::getPendingCount(); - $deferred = DBA::count('workerqueue', ['NOT `done` AND `retrial` > ?', 0]); $workerqueue = DBA::count('workerqueue', ['NOT `done` AND `retrial` = ?', 0]); @@ -235,9 +214,6 @@ class Summary extends BaseAdmin '$title' => DI::l10n()->t('Administration'), '$page' => DI::l10n()->t('Summary'), '$queues' => $queues, - '$users' => [DI::l10n()->t('Registered users'), $users], - '$accounts' => $accounts, - '$pending' => [DI::l10n()->t('Pending registrations'), $pending], '$version' => [DI::l10n()->t('Version'), App::VERSION], '$platform' => App::PLATFORM, '$codename' => App::CODENAME, diff --git a/src/Module/Admin/Users/Active.php b/src/Module/Admin/Users/Active.php deleted file mode 100644 index 4e021e2981..0000000000 --- a/src/Module/Admin/Users/Active.php +++ /dev/null @@ -1,164 +0,0 @@ -. - * - */ - -namespace Friendica\Module\Admin\Users; - -use Friendica\Content\Pager; -use Friendica\Core\Renderer; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\User; -use Friendica\Module\Admin\BaseUsers; - -class Active extends BaseUsers -{ - protected function post(array $request = []) - { - self::checkAdminAccess(); - - self::checkFormSecurityTokenRedirectOnError(DI::baseUrl()->get(true), 'admin_users_active'); - - $users = $_POST['user'] ?? []; - - if (!empty($_POST['page_users_block'])) { - foreach ($users as $uid) { - User::block($uid); - } - DI::sysmsg()->addInfo(DI::l10n()->tt('%s user blocked', '%s users blocked', count($users))); - } - - if (!empty($_POST['page_users_delete'])) { - foreach ($users as $uid) { - if (DI::userSession()->getLocalUserId() != $uid) { - User::remove($uid); - } else { - DI::sysmsg()->addNotice(DI::l10n()->t('You can\'t remove yourself')); - } - } - - DI::sysmsg()->addInfo(DI::l10n()->tt('%s user deleted', '%s users deleted', count($users))); - } - - DI::baseUrl()->redirect(DI::args()->getQueryString()); - } - - protected function content(array $request = []): string - { - parent::content(); - - $action = $this->parameters['action'] ?? ''; - $uid = $this->parameters['uid'] ?? 0; - - if ($uid) { - $user = User::getById($uid, ['username', 'blocked']); - if (!DBA::isResult($user)) { - DI::sysmsg()->addNotice(DI::l10n()->t('User not found')); - DI::baseUrl()->redirect('admin/users'); - return ''; // NOTREACHED - } - } - - switch ($action) { - case 'delete': - if (DI::userSession()->getLocalUserId() != $uid) { - self::checkFormSecurityTokenRedirectOnError('admin/users/active', 'admin_users_active', 't'); - // delete user - User::remove($uid); - - DI::sysmsg()->addNotice(DI::l10n()->t('User "%s" deleted', $user['username'])); - } else { - DI::sysmsg()->addNotice(DI::l10n()->t('You can\'t remove yourself')); - } - - DI::baseUrl()->redirect('admin/users/active'); - break; - case 'block': - self::checkFormSecurityTokenRedirectOnError('admin/users/active', 'admin_users_active', 't'); - User::block($uid); - DI::sysmsg()->addNotice(DI::l10n()->t('User "%s" blocked', $user['username'])); - DI::baseUrl()->redirect('admin/users/active'); - break; - } - $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), 100); - - $valid_orders = [ - 'name', - 'email', - 'register_date', - 'login_date', - 'last-item', - 'page-flags' - ]; - - $order = 'name'; - $order_direction = '+'; - if (!empty($_GET['o'])) { - $new_order = $_GET['o']; - if ($new_order[0] === '-') { - $order_direction = '-'; - $new_order = substr($new_order, 1); - } - - if (in_array($new_order, $valid_orders)) { - $order = $new_order; - } - } - - $users = User::getList($pager->getStart(), $pager->getItemsPerPage(), 'active', $order, ($order_direction == '-')); - - $users = array_map(self::setupUserCallback(), $users); - - $th_users = array_map(null, [DI::l10n()->t('Name'), DI::l10n()->t('Email'), DI::l10n()->t('Register date'), DI::l10n()->t('Last login'), DI::l10n()->t('Last public item'), DI::l10n()->t('Type')], $valid_orders); - - $count = DBA::count('user', ["NOT `blocked` AND `verified` AND NOT `account_removed` AND `uid` != ?", 0]); - - $t = Renderer::getMarkupTemplate('admin/users/active.tpl'); - return self::getTabsHTML('active') . Renderer::replaceMacros($t, [ - // strings // - '$title' => DI::l10n()->t('Administration'), - '$page' => DI::l10n()->t('Active Accounts'), - '$select_all' => DI::l10n()->t('select all'), - '$delete' => DI::l10n()->t('Delete'), - '$block' => DI::l10n()->t('Block'), - '$blocked' => DI::l10n()->t('User blocked'), - '$siteadmin' => DI::l10n()->t('Site admin'), - '$accountexpired' => DI::l10n()->t('Account expired'), - '$h_newuser' => DI::l10n()->t('Create a new user'), - - '$th_users' => $th_users, - '$order_users' => $order, - '$order_direction_users' => $order_direction, - - '$confirm_delete_multi' => DI::l10n()->t('Selected users will be deleted!\n\nEverything these users had posted on this site will be permanently deleted!\n\nAre you sure?'), - '$confirm_delete' => DI::l10n()->t('The user {0} will be deleted!\n\nEverything this user has posted on this site will be permanently deleted!\n\nAre you sure?'), - - '$form_security_token' => self::getFormSecurityToken('admin_users_active'), - - // values // - '$baseurl' => DI::baseUrl()->get(true), - '$query_string' => DI::args()->getQueryString(), - - '$users' => $users, - '$count' => $count, - '$pager' => $pager->renderFull($count), - ]); - } -} diff --git a/src/Module/Admin/Users/Blocked.php b/src/Module/Admin/Users/Blocked.php deleted file mode 100644 index cedbccb22c..0000000000 --- a/src/Module/Admin/Users/Blocked.php +++ /dev/null @@ -1,164 +0,0 @@ -. - * - */ - -namespace Friendica\Module\Admin\Users; - -use Friendica\Content\Pager; -use Friendica\Core\Renderer; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\User; -use Friendica\Module\Admin\BaseUsers; -use Friendica\Util\Temporal; - -class Blocked extends BaseUsers -{ - protected function post(array $request = []) - { - self::checkAdminAccess(); - - self::checkFormSecurityTokenRedirectOnError('/admin/users/blocked', 'admin_users_blocked'); - - $users = $_POST['user'] ?? []; - - if (!empty($_POST['page_users_unblock'])) { - foreach ($users as $uid) { - User::block($uid, false); - } - DI::sysmsg()->addInfo(DI::l10n()->tt('%s user unblocked', '%s users unblocked', count($users))); - } - - if (!empty($_POST['page_users_delete'])) { - foreach ($users as $uid) { - if (DI::userSession()->getLocalUserId() != $uid) { - User::remove($uid); - } else { - DI::sysmsg()->addNotice(DI::l10n()->t('You can\'t remove yourself')); - } - } - - DI::sysmsg()->addInfo(DI::l10n()->tt('%s user deleted', '%s users deleted', count($users))); - } - - DI::baseUrl()->redirect('admin/users/blocked'); - } - - protected function content(array $request = []): string - { - parent::content(); - - $action = $this->parameters['action'] ?? ''; - $uid = $this->parameters['uid'] ?? 0; - - if ($uid) { - $user = User::getById($uid, ['username', 'blocked']); - if (!DBA::isResult($user)) { - DI::sysmsg()->addNotice(DI::l10n()->t('User not found')); - DI::baseUrl()->redirect('admin/users'); - return ''; // NOTREACHED - } - } - - switch ($action) { - case 'delete': - if (DI::userSession()->getLocalUserId() != $uid) { - self::checkFormSecurityTokenRedirectOnError('/admin/users/blocked', 'admin_users_blocked', 't'); - // delete user - User::remove($uid); - - DI::sysmsg()->addNotice(DI::l10n()->t('User "%s" deleted', $user['username'])); - } else { - DI::sysmsg()->addNotice(DI::l10n()->t('You can\'t remove yourself')); - } - DI::baseUrl()->redirect('admin/users/blocked'); - break; - case 'unblock': - self::checkFormSecurityTokenRedirectOnError('/admin/users/blocked', 'admin_users_blocked', 't'); - User::block($uid, false); - DI::sysmsg()->addNotice(DI::l10n()->t('User "%s" unblocked', $user['username'])); - DI::baseUrl()->redirect('admin/users/blocked'); - break; - } - - $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), 100); - - $valid_orders = [ - 'name', - 'email', - 'register_date', - 'login_date', - 'last-item', - 'page-flags' - ]; - - $order = 'name'; - $order_direction = '+'; - if (!empty($_GET['o'])) { - $new_order = $_GET['o']; - if ($new_order[0] === '-') { - $order_direction = '-'; - $new_order = substr($new_order, 1); - } - - if (in_array($new_order, $valid_orders)) { - $order = $new_order; - } - } - - $users = User::getList($pager->getStart(), $pager->getItemsPerPage(), 'blocked', $order, ($order_direction == '-')); - - $users = array_map(self::setupUserCallback(), $users); - - $th_users = array_map(null, [DI::l10n()->t('Name'), DI::l10n()->t('Email'), DI::l10n()->t('Register date'), DI::l10n()->t('Last login'), DI::l10n()->t('Last public item'), DI::l10n()->t('Type')], $valid_orders); - - $count = DBA::count('user', ['blocked' => true, 'verified' => true]); - - $t = Renderer::getMarkupTemplate('admin/users/blocked.tpl'); - return self::getTabsHTML('blocked') . Renderer::replaceMacros($t, [ - // strings // - '$title' => DI::l10n()->t('Administration'), - '$page' => DI::l10n()->t('Blocked Users'), - '$select_all' => DI::l10n()->t('select all'), - '$delete' => DI::l10n()->t('Delete'), - '$blocked' => DI::l10n()->t('User blocked'), - '$unblock' => DI::l10n()->t('Unblock'), - '$siteadmin' => DI::l10n()->t('Site admin'), - '$accountexpired' => DI::l10n()->t('Account expired'), - - '$th_users' => $th_users, - '$order_users' => $order, - '$order_direction_users' => $order_direction, - - '$confirm_delete_multi' => DI::l10n()->t('Selected users will be deleted!\n\nEverything these users had posted on this site will be permanently deleted!\n\nAre you sure?'), - '$confirm_delete' => DI::l10n()->t('The user {0} will be deleted!\n\nEverything this user has posted on this site will be permanently deleted!\n\nAre you sure?'), - - '$form_security_token' => self::getFormSecurityToken('admin_users_blocked'), - - // values // - '$baseurl' => DI::baseUrl()->get(true), - '$query_string' => DI::args()->getQueryString(), - - '$users' => $users, - '$count' => $count, - '$pager' => $pager->renderFull($count) - ]); - } -} diff --git a/src/Module/Admin/Users/Index.php b/src/Module/Admin/Users/Index.php deleted file mode 100644 index b0757dab47..0000000000 --- a/src/Module/Admin/Users/Index.php +++ /dev/null @@ -1,181 +0,0 @@ -. - * - */ - -namespace Friendica\Module\Admin\Users; - -use Friendica\Content\Pager; -use Friendica\Core\Renderer; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\User; -use Friendica\Module\Admin\BaseUsers; - -class Index extends BaseUsers -{ - protected function post(array $request = []) - { - self::checkAdminAccess(); - - self::checkFormSecurityTokenRedirectOnError('admin/users', 'admin_users'); - - $users = $_POST['user'] ?? []; - - if (!empty($_POST['page_users_block'])) { - foreach ($users as $uid) { - User::block($uid); - } - DI::sysmsg()->addInfo(DI::l10n()->tt('%s user blocked', '%s users blocked', count($users))); - } - - if (!empty($_POST['page_users_unblock'])) { - foreach ($users as $uid) { - User::block($uid, false); - } - DI::sysmsg()->addInfo(DI::l10n()->tt('%s user unblocked', '%s users unblocked', count($users))); - } - - if (!empty($_POST['page_users_delete'])) { - foreach ($users as $uid) { - if (DI::userSession()->getLocalUserId() != $uid) { - User::remove($uid); - } else { - DI::sysmsg()->addNotice(DI::l10n()->t('You can\'t remove yourself')); - } - } - - DI::sysmsg()->addInfo(DI::l10n()->tt('%s user deleted', '%s users deleted', count($users))); - } - - DI::baseUrl()->redirect(DI::args()->getQueryString()); - } - - protected function content(array $request = []): string - { - parent::content(); - - $action = $this->parameters['action'] ?? ''; - $uid = $this->parameters['uid'] ?? 0; - - if ($uid) { - $user = User::getById($uid, ['username', 'blocked']); - if (!DBA::isResult($user)) { - DI::sysmsg()->addNotice(DI::l10n()->t('User not found')); - DI::baseUrl()->redirect('admin/users'); - return ''; // NOTREACHED - } - } - - switch ($action) { - case 'delete': - if (DI::userSession()->getLocalUserId() != $uid) { - self::checkFormSecurityTokenRedirectOnError(DI::baseUrl()->get(true), 'admin_users', 't'); - // delete user - User::remove($uid); - - DI::sysmsg()->addNotice(DI::l10n()->t('User "%s" deleted', $user['username'])); - } else { - DI::sysmsg()->addNotice(DI::l10n()->t('You can\'t remove yourself')); - } - - DI::baseUrl()->redirect('admin/users'); - break; - case 'block': - self::checkFormSecurityTokenRedirectOnError('admin/users', 'admin_users', 't'); - User::block($uid); - DI::sysmsg()->addNotice(DI::l10n()->t('User "%s" blocked', $user['username'])); - DI::baseUrl()->redirect('admin/users'); - break; - case 'unblock': - self::checkFormSecurityTokenRedirectOnError('admin/users', 'admin_users', 't'); - User::block($uid, false); - DI::sysmsg()->addNotice(DI::l10n()->t('User "%s" unblocked', $user['username'])); - DI::baseUrl()->redirect('admin/users'); - break; - } - $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), 100); - - $valid_orders = [ - 'name', - 'email', - 'register_date', - 'login_date', - 'last-item', - 'page-flags' - ]; - - $order = 'name'; - $order_direction = '+'; - if (!empty($_GET['o'])) { - $new_order = $_GET['o']; - if ($new_order[0] === '-') { - $order_direction = '-'; - $new_order = substr($new_order, 1); - } - - if (in_array($new_order, $valid_orders)) { - $order = $new_order; - } - } - - $users = User::getList($pager->getStart(), $pager->getItemsPerPage(), 'all', $order, ($order_direction == '-')); - - $users = array_map(self::setupUserCallback(), $users); - - $th_users = array_map(null, [DI::l10n()->t('Name'), DI::l10n()->t('Email'), DI::l10n()->t('Register date'), DI::l10n()->t('Last login'), DI::l10n()->t('Last public item'), DI::l10n()->t('Type')], $valid_orders); - - $count = DBA::count('user', ["`uid` != ?", 0]); - - $t = Renderer::getMarkupTemplate('admin/users/index.tpl'); - return self::getTabsHTML('all') . Renderer::replaceMacros($t, [ - // strings // - '$title' => DI::l10n()->t('Administration'), - '$page' => DI::l10n()->t('Users'), - '$select_all' => DI::l10n()->t('select all'), - '$h_deleted' => DI::l10n()->t('User waiting for permanent deletion'), - '$delete' => DI::l10n()->t('Delete'), - '$block' => DI::l10n()->t('Block'), - '$blocked' => DI::l10n()->t('User blocked'), - '$unblock' => DI::l10n()->t('Unblock'), - '$siteadmin' => DI::l10n()->t('Site admin'), - '$accountexpired' => DI::l10n()->t('Account expired'), - - '$h_users' => DI::l10n()->t('Users'), - '$h_newuser' => DI::l10n()->t('Create a new user'), - '$th_deleted' => [DI::l10n()->t('Name'), DI::l10n()->t('Email'), DI::l10n()->t('Register date'), DI::l10n()->t('Last login'), DI::l10n()->t('Last public item'), DI::l10n()->t('Permanent deletion')], - '$th_users' => $th_users, - '$order_users' => $order, - '$order_direction_users' => $order_direction, - - '$confirm_delete_multi' => DI::l10n()->t('Selected users will be deleted!\n\nEverything these users had posted on this site will be permanently deleted!\n\nAre you sure?'), - '$confirm_delete' => DI::l10n()->t('The user {0} will be deleted!\n\nEverything this user has posted on this site will be permanently deleted!\n\nAre you sure?'), - - '$form_security_token' => self::getFormSecurityToken('admin_users'), - - // values // - '$baseurl' => DI::baseUrl()->get(true), - '$query_string' => DI::args()->getQueryString(), - - '$users' => $users, - '$count' => $count, - '$pager' => $pager->renderFull($count), - ]); - } -} diff --git a/src/Module/BaseAdmin.php b/src/Module/BaseAdmin.php index 1c9fc0f244..6181202a3e 100644 --- a/src/Module/BaseAdmin.php +++ b/src/Module/BaseAdmin.php @@ -89,7 +89,6 @@ abstract class BaseAdmin extends BaseModule 'configuration' => [DI::l10n()->t('Configuration'), [ 'site' => ['admin/site' , DI::l10n()->t('Site') , 'site'], 'storage' => ['admin/storage' , DI::l10n()->t('Storage') , 'storage'], - 'users' => ['admin/users' , DI::l10n()->t('Users') , 'users'], 'addons' => ['admin/addons' , DI::l10n()->t('Addons') , 'addons'], 'themes' => ['admin/themes' , DI::l10n()->t('Themes') , 'themes'], 'features' => ['admin/features' , DI::l10n()->t('Additional features') , 'features'], @@ -100,11 +99,6 @@ abstract class BaseAdmin extends BaseModule 'deferred' => ['admin/queue/deferred', DI::l10n()->t('Inspect Deferred Workers'), 'deferred'], 'workerqueue' => ['admin/queue' , DI::l10n()->t('Inspect worker Queue') , 'workerqueue'], ]], - 'tools' => [DI::l10n()->t('Tools'), [ - 'contactblock' => ['admin/blocklist/contact', DI::l10n()->t('Contact Blocklist') , 'contactblock'], - 'blocklist' => ['admin/blocklist/server' , DI::l10n()->t('Server Blocklist') , 'blocklist'], - 'deleteitem' => ['admin/item/delete' , DI::l10n()->t('Delete Item') , 'deleteitem'], - ]], 'logs' => [DI::l10n()->t('Logs'), [ 'logsconfig' => ['admin/logs/', DI::l10n()->t('Logs') , 'logs'], 'logsview' => ['admin/logs/view' , DI::l10n()->t('View Logs') , 'viewlogs'], @@ -113,7 +107,6 @@ abstract class BaseAdmin extends BaseModule 'phpinfo' => ['admin/phpinfo' , DI::l10n()->t('PHP Info') , 'phpinfo'], 'probe' => ['probe' , DI::l10n()->t('probe address') , 'probe'], 'webfinger' => ['webfinger' , DI::l10n()->t('check webfinger') , 'webfinger'], - 'itemsource' => ['admin/item/source' , DI::l10n()->t('Item Source') , 'itemsource'], 'babel' => ['babel' , DI::l10n()->t('Babel') , 'babel'], 'debug/ap' => ['debug/ap' , DI::l10n()->t('ActivityPub Conversion') , 'debug/ap'], ]], diff --git a/src/Module/BaseModeration.php b/src/Module/BaseModeration.php new file mode 100644 index 0000000000..1261eebb1c --- /dev/null +++ b/src/Module/BaseModeration.php @@ -0,0 +1,134 @@ +. + * + */ + +namespace Friendica\Module; + +use Friendica\App; +use Friendica\BaseModule; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Navigation\SystemMessages; +use Friendica\Network\HTTPException; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; + +/** + * This abstract module is meant to be extended by all modules that are reserved to moderator users. + * + * It performs a blanket permission check in all the module methods as long as the relevant `parent::method()` is + * called in the inheriting module. + * + * Additionally, it puts together the moderation page aside with all the moderation links. + * + * @package Friendica\Module + */ +abstract class BaseModeration extends BaseModule +{ + /** @var IHandleUserSessions */ + protected $session; + /** @var SystemMessages */ + protected $systemMessages; + /** @var App */ + protected $app; + /** @var App\Page */ + protected $page; + + public function __construct(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($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); + + $this->session = $session; + $this->systemMessages = $systemMessages; + $this->app = $app; + $this->page = $page; + } + + /** + * Checks moderator access and throws exceptions if not logged-in moderator + * + * @param bool $interactive + * @return void + * @throws HTTPException\ForbiddenException + * @throws HTTPException\InternalServerErrorException + */ + public function checkModerationAccess(bool $interactive = false) + { + if (!$this->session->getLocalUserId()) { + if ($interactive) { + $this->systemMessages->addNotice($this->t('Please login to continue.')); + $this->session->set('return_path', $this->args->getQueryString()); + $this->baseUrl->redirect('login'); + } else { + throw new HTTPException\UnauthorizedException($this->t('Please login to continue.')); + } + } + + if (!$this->app->isSiteAdmin()) { + throw new HTTPException\ForbiddenException($this->t('You don\'t have access to administration pages.')); + } + + if ($this->session->getSubManagedUserId()) { + throw new HTTPException\ForbiddenException($this->t('Submanaged account can\'t access the administration pages. Please log back in as the main account.')); + } + } + + protected function content(array $request = []): string + { + $this->checkModerationAccess(true); + + // Header stuff + $this->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('moderation/settings_head.tpl'), []); + + /* + * Side bar links + */ + + // array(url, name, extra css classes) + // not part of $aside to make the template more adjustable + $aside_sub = [ + 'information' => [$this->t('Information'), [ + 'overview' => ['moderation', $this->t('Overview'), 'overview'], + ]], + 'configuration' => [$this->t('Configuration'), [ + 'users' => ['moderation/users', $this->t('Users'), 'users'], + ]], + 'tools' => [$this->t('Tools'), [ + 'contactblock' => ['moderation/blocklist/contact', $this->t('Contact Blocklist'), 'contactblock'], + 'blocklist' => ['moderation/blocklist/server', $this->t('Server Blocklist'), 'blocklist'], + 'deleteitem' => ['moderation/item/delete', $this->t('Delete Item'), 'deleteitem'], + ]], + 'diagnostics' => [$this->t('Diagnostics'), [ + 'itemsource' => ['moderation/item/source', $this->t('Item Source'), 'itemsource'], + ]], + ]; + + $t = Renderer::getMarkupTemplate('moderation/aside.tpl'); + $this->page['aside'] .= Renderer::replaceMacros($t, [ + '$subpages' => $aside_sub, + '$admtxt' => $this->t('Moderation'), + '$h_pending' => $this->t('User registrations waiting for confirmation'), + '$modurl' => 'moderation/' + ]); + + return ''; + } +} diff --git a/src/Module/Moderation/BaseUsers.php b/src/Module/Moderation/BaseUsers.php new file mode 100644 index 0000000000..eddfc09097 --- /dev/null +++ b/src/Module/Moderation/BaseUsers.php @@ -0,0 +1,149 @@ +. + * + */ + +namespace Friendica\Module\Moderation; + +use Friendica\App; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Database\Database; +use Friendica\DI; +use Friendica\Model\Register; +use Friendica\Model\User; +use Friendica\Module\BaseModeration; +use Friendica\Module\Response; +use Friendica\Navigation\SystemMessages; +use Friendica\Network\HTTPException\ServiceUnavailableException; +use Friendica\Util\Profiler; +use Friendica\Util\Temporal; +use Psr\Log\LoggerInterface; + +abstract class BaseUsers extends BaseModeration +{ + /** @var Database */ + protected $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; + } + + /** + * Get the users moderation tabs menu + * + * @param string $selectedTab + * @return string HTML + * @throws ServiceUnavailableException + */ + protected function getTabsHTML(string $selectedTab): string + { + $all = $this->database->count('user', ["`uid` != ?", 0]); + $active = $this->database->count('user', ["NOT `blocked` AND `verified` AND NOT `account_removed` AND `uid` != ?", 0]); + $pending = Register::getPendingCount(); + $blocked = $this->database->count('user', ['blocked' => true, 'verified' => true, 'account_removed' => false]); + $deleted = $this->database->count('user', ['account_removed' => true]); + + $tabs = [ + [ + 'label' => $this->t('All') . ' (' . $all . ')', + 'url' => 'moderation/users', + 'sel' => !$selectedTab || $selectedTab == 'all' ? 'active' : '', + 'title' => $this->t('List of all users'), + 'id' => 'admin-users-all', + 'accesskey' => 'a', + ], + [ + 'label' => $this->t('Active') . ' (' . $active . ')', + 'url' => 'moderation/users/active', + 'sel' => $selectedTab == 'active' ? 'active' : '', + 'title' => $this->t('List of active accounts'), + 'id' => 'admin-users-active', + 'accesskey' => 'k', + ], + [ + 'label' => $this->t('Pending') . ($pending ? ' (' . $pending . ')' : ''), + 'url' => 'moderation/users/pending', + 'sel' => $selectedTab == 'pending' ? 'active' : '', + 'title' => $this->t('List of pending registrations'), + 'id' => 'admin-users-pending', + 'accesskey' => 'p', + ], + [ + 'label' => $this->t('Blocked') . ($blocked ? ' (' . $blocked . ')' : ''), + 'url' => 'moderation/users/blocked', + 'sel' => $selectedTab == 'blocked' ? 'active' : '', + 'title' => $this->t('List of blocked users'), + 'id' => 'admin-users-blocked', + 'accesskey' => 'b', + ], + [ + 'label' => $this->t('Deleted') . ($deleted ? ' (' . $deleted . ')' : ''), + 'url' => 'moderation/users/deleted', + 'sel' => $selectedTab == 'deleted' ? 'active' : '', + 'title' => $this->t('List of pending user deletions'), + 'id' => 'admin-users-deleted', + 'accesskey' => 'd', + ], + ]; + + $tpl = Renderer::getMarkupTemplate('common_tabs.tpl'); + return Renderer::replaceMacros($tpl, ['$tabs' => $tabs]); + } + + protected function setupUserCallback(): \Closure + { + $adminlist = explode(',', str_replace(' ', '', DI::config()->get('config', 'admin_email'))); + return function ($user) use ($adminlist) { + $page_types = [ + User::PAGE_FLAGS_NORMAL => $this->t('Normal Account Page'), + User::PAGE_FLAGS_SOAPBOX => $this->t('Soapbox Page'), + User::PAGE_FLAGS_COMMUNITY => $this->t('Public Forum'), + User::PAGE_FLAGS_FREELOVE => $this->t('Automatic Friend Page'), + User::PAGE_FLAGS_PRVGROUP => $this->t('Private Forum') + ]; + $account_types = [ + User::ACCOUNT_TYPE_PERSON => $this->t('Personal Page'), + User::ACCOUNT_TYPE_ORGANISATION => $this->t('Organisation Page'), + User::ACCOUNT_TYPE_NEWS => $this->t('News Page'), + User::ACCOUNT_TYPE_COMMUNITY => $this->t('Community Forum'), + User::ACCOUNT_TYPE_RELAY => $this->t('Relay'), + ]; + + $user['page_flags_raw'] = $user['page-flags']; + $user['page_flags'] = $page_types[$user['page-flags']]; + + $user['account_type_raw'] = ($user['page_flags_raw'] == 0) ? $user['account-type'] : -1; + $user['account_type'] = ($user['page_flags_raw'] == 0) ? $account_types[$user['account-type']] : ''; + + $user['register_date'] = Temporal::getRelativeDate($user['register_date']); + $user['login_date'] = Temporal::getRelativeDate($user['login_date']); + $user['lastitem_date'] = Temporal::getRelativeDate($user['last-item']); + $user['is_admin'] = in_array($user['email'], $adminlist); + $user['is_deletable'] = !$user['account_removed'] && intval($user['uid']) != $this->session->getLocalUserId(); + $user['deleted'] = $user['account_removed'] ? Temporal::getRelativeDate($user['account_expires_on']) : false; + + return $user; + }; + } +} diff --git a/src/Module/Moderation/Blocklist/Contact.php b/src/Module/Moderation/Blocklist/Contact.php new file mode 100644 index 0000000000..f3583bce30 --- /dev/null +++ b/src/Module/Moderation/Blocklist/Contact.php @@ -0,0 +1,138 @@ +. + * + */ + +namespace Friendica\Module\Moderation\Blocklist; + +use Friendica\App; +use Friendica\Content\Pager; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Core\Worker; +use Friendica\Database\Database; +use Friendica\Model; +use Friendica\Module\BaseModeration; +use Friendica\Module\Response; +use Friendica\Navigation\SystemMessages; +use Friendica\Util\Network; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; + +class Contact 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 post(array $request = []) + { + $this->checkModerationAccess(); + + self::checkFormSecurityTokenRedirectOnError('/moderation/blocklist/contact', 'moderation_contactblock'); + + $contact_url = $request['contact_url'] ?? ''; + $block_reason = $request['contact_block_reason'] ?? ''; + $block_purge = $request['contact_block_purge'] ?? false; + $contacts = $request['contacts'] ?? []; + + if (!empty($request['page_contactblock_block'])) { + $contact = Model\Contact::getByURL($contact_url, null, ['id', 'nurl']); + if (empty($contact)) { + $this->systemMessages->addNotice($this->t('Could not find any contact entry for this URL (%s)', $contact_url)); + $this->baseUrl->redirect('moderation/blocklist/contact'); + } + + if (Network::isLocalLink($contact['nurl'])) { + $this->systemMessages->addNotice($this->t('You can\'t block a local contact, please block the user instead')); + $this->baseUrl->redirect('moderation/blocklist/contact'); + } + + Model\Contact::block($contact['id'], $block_reason); + + if ($block_purge) { + foreach (Model\Contact::selectToArray(['id'], ['nurl' => $contact['nurl']]) as $contact) { + Worker::add(Worker::PRIORITY_LOW, 'Contact\RemoveContent', $contact['id']); + } + } + + $this->systemMessages->addInfo($this->t('The contact has been blocked from the node')); + } + + if (!empty($request['page_contactblock_unblock'])) { + foreach ($contacts as $uid) { + Model\Contact::unblock($uid); + } + $this->systemMessages->addInfo($this->tt('%s contact unblocked', '%s contacts unblocked', count($contacts))); + } + + $this->baseUrl->redirect('moderation/blocklist/contact'); + } + + protected function content(array $request = []): string + { + parent::content(); + + $condition = ['uid' => 0, 'blocked' => true]; + + $total = $this->database->count('contact', $condition); + + $pager = new Pager($this->l10n, $this->args->getQueryString(), 30); + + $contacts = Model\Contact::selectToArray([], $condition, ['limit' => [$pager->getStart(), $pager->getItemsPerPage()]]); + + $t = Renderer::getMarkupTemplate('moderation/blocklist/contact.tpl'); + return Renderer::replaceMacros($t, [ + // strings // + '$title' => $this->t('Moderation'), + '$page' => $this->t('Remote Contact Blocklist'), + '$description' => $this->t('This page allows you to prevent any message from a remote contact to reach your node.'), + '$submit' => $this->t('Block Remote Contact'), + '$select_all' => $this->t('select all'), + '$select_none' => $this->t('select none'), + '$block' => $this->t('Block'), + '$unblock' => $this->t('Unblock'), + '$no_data' => $this->t('No remote contact is blocked from this node.'), + + '$h_contacts' => $this->t('Blocked Remote Contacts'), + '$h_newblock' => $this->t('Block New Remote Contact'), + '$th_contacts' => [$this->t('Photo'), $this->t('Name'), $this->t('Reason')], + + '$form_security_token' => self::getFormSecurityToken('moderation_contactblock'), + + // values // + '$baseurl' => $this->baseUrl->get(true), + + '$contacts' => $contacts, + '$total_contacts' => $this->tt('%s total blocked contact', '%s total blocked contacts', $total), + '$paginate' => $pager->renderFull($total), + + '$contacturl' => ['contact_url', $this->t('Profile URL'), '', $this->t('URL of the remote contact to block.')], + '$contact_block_purge' => ['contact_block_purge', $this->t('Also purge contact'), false, $this->t('Removes all content related to this contact from the node. Keeps the contact record. This action cannot be undone.')], + '$contact_block_reason' => ['contact_block_reason', $this->t('Block Reason')], + ]); + } +} diff --git a/src/Module/Moderation/Blocklist/Server/Add.php b/src/Module/Moderation/Blocklist/Server/Add.php new file mode 100644 index 0000000000..54cae5ccf4 --- /dev/null +++ b/src/Module/Moderation/Blocklist/Server/Add.php @@ -0,0 +1,143 @@ +. + * + */ + +namespace Friendica\Module\Moderation\Blocklist\Server; + +use Friendica\App; +use Friendica\Content\ContactSelector; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Core\Worker; +use Friendica\Model\Contact; +use Friendica\Model\GServer; +use Friendica\Moderation\DomainPatternBlocklist; +use Friendica\Module\BaseModeration; +use Friendica\Module\Response; +use Friendica\Navigation\SystemMessages; +use Friendica\Network\HTTPException; +use Friendica\Util\Profiler; +use GuzzleHttp\Psr7\Uri; +use Psr\Log\LoggerInterface; + +class Add extends BaseModeration +{ + /** @var DomainPatternBlocklist */ + private $blocklist; + + public function __construct(DomainPatternBlocklist $blocklist, 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->blocklist = $blocklist; + } + + /** + * @param array $request + * @return void + * @throws HTTPException\ForbiddenException + * @throws HTTPException\FoundException + * @throws HTTPException\InternalServerErrorException + * @throws HTTPException\MovedPermanentlyException + * @throws HTTPException\TemporaryRedirectException + * @throws \Exception + */ + protected function post(array $request = []) + { + $this->checkModerationAccess(); + + if (empty($request['page_blocklist_add'])) { + return; + } + + self::checkFormSecurityTokenRedirectOnError('/moderation/blocklist/server/add', 'moderation_blocklist_add'); + + $pattern = trim($request['pattern']); + + // Add new item to blocklist + $this->blocklist->addPattern($pattern, trim($request['reason'])); + + $this->systemMessages->addInfo($this->t('Server domain pattern added to the blocklist.')); + + if (!empty($request['purge'])) { + $gservers = GServer::listByDomainPattern($pattern); + foreach (Contact::selectToArray(['id'], ['gsid' => array_column($gservers, 'id')]) as $contact) { + Worker::add(Worker::PRIORITY_LOW, 'Contact\RemoveContent', $contact['id']); + } + + $this->systemMessages->addInfo($this->tt('%s server scheduled to be purged.', '%s servers scheduled to be purged.', count($gservers))); + } + + $this->baseUrl->redirect('moderation/blocklist/server'); + } + + /** + * @param array $request + * @return string + * @throws HTTPException\InternalServerErrorException + * @throws HTTPException\ServiceUnavailableException + * @throws \Exception + */ + protected function content(array $request = []): string + { + parent::content(); + + $gservers = []; + + if ($pattern = trim($request['pattern'] ?? '')) { + $gservers = GServer::listByDomainPattern($pattern); + } + + array_walk($gservers, function (array &$gserver) { + $gserver['domain'] = (new Uri($gserver['url']))->getHost(); + $gserver['network_icon'] = ContactSelector::networkToIcon($gserver['network']); + $gserver['network_name'] = ContactSelector::networkToName($gserver['network']); + }); + + $t = Renderer::getMarkupTemplate('moderation/blocklist/server/add.tpl'); + return Renderer::replaceMacros($t, [ + '$l10n' => [ + 'return_list' => $this->t('← Return to the list'), + 'title' => $this->t('Moderation'), + 'page' => $this->t('Block A New Server Domain Pattern'), + 'syntax' => $this->t('

The server domain pattern syntax is case-insensitive shell wildcard, comprising the following special characters:

+'), + 'submit' => $this->t('Check pattern'), + 'matching_servers' => $this->t('Matching known servers'), + 'server_name' => $this->t('Server Name'), + 'server_domain' => $this->t('Server Domain'), + 'known_contacts' => $this->t('Known Contacts'), + 'server_count' => $this->tt('%d known server', '%d known servers', count($gservers)), + 'add_pattern' => $this->t('Add pattern to the blocklist'), + ], + '$newdomain' => ['pattern', $this->t('Server Domain Pattern'), $pattern, $this->t('The domain pattern of the new server to add to the blocklist. Do not include the protocol.'), $this->t('Required'), '', ''], + '$newpurge' => ['purge', $this->t('Purge server'), $request['purge'] ?? false, $this->tt('Also purges all the locally stored content authored by the known contacts registered on that server. Keeps the contacts and the server records. This action cannot be undone.', 'Also purges all the locally stored content authored by the known contacts registered on these servers. Keeps the contacts and the servers records. This action cannot be undone.', count($gservers))], + '$newreason' => ['reason', $this->t('Block reason'), $request['reason'] ?? '', $this->t('The reason why you blocked this server domain pattern. This reason will be shown publicly in the server information page.'), $this->t('Required'), '', ''], + '$pattern' => $pattern, + '$gservers' => $gservers, + '$baseurl' => $this->baseUrl->get(true), + '$form_security_token' => self::getFormSecurityToken('moderation_blocklist_add') + ]); + } +} diff --git a/src/Module/Moderation/Blocklist/Server/Import.php b/src/Module/Moderation/Blocklist/Server/Import.php new file mode 100644 index 0000000000..98e35f70e8 --- /dev/null +++ b/src/Module/Moderation/Blocklist/Server/Import.php @@ -0,0 +1,134 @@ +. + * + */ + +namespace Friendica\Module\Moderation\Blocklist\Server; + +use Friendica\App; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Moderation\DomainPatternBlocklist; +use Friendica\Module\Response; +use Friendica\Navigation\SystemMessages; +use Friendica\Network\HTTPException; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; + +class Import extends \Friendica\Module\BaseModeration +{ + /** @var DomainPatternBlocklist */ + private $localBlocklist; + + /** @var array of blocked server domain patterns */ + private $blocklist = []; + + public function __construct(DomainPatternBlocklist $localBlocklist, 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->localBlocklist = $localBlocklist; + } + + /** + * @param array $request + * @return void + * @throws HTTPException\ForbiddenException + * @throws HTTPException\FoundException + * @throws HTTPException\InternalServerErrorException + * @throws HTTPException\MovedPermanentlyException + * @throws HTTPException\TemporaryRedirectException + */ + protected function post(array $request = []) + { + $this->checkModerationAccess(); + + if (!isset($request['page_blocklist_upload']) && !isset($request['page_blocklist_import'])) { + return; + } + + self::checkFormSecurityTokenRedirectOnError('/moderation/blocklist/server/import', 'moderation_blocklist_import'); + + if (isset($request['page_blocklist_upload'])) { + try { + $this->blocklist = $this->localBlocklist::extractFromCSVFile($_FILES['listfile']['tmp_name']); + } catch (\Throwable $e) { + $this->systemMessages->addNotice($this->t('Error importing pattern file')); + } + + return; + } + + if (isset($request['page_blocklist_import'])) { + $blocklist = json_decode($request['blocklist'], true); + if ($blocklist === null) { + $this->systemMessages->addNotice($this->t('Error importing pattern file')); + return; + } + + if (($request['mode'] ?? 'append') == 'replace') { + $this->localBlocklist->set($blocklist); + $this->systemMessages->addNotice($this->t('Local blocklist replaced with the provided file.')); + } else { + $count = $this->localBlocklist->append($blocklist); + if ($count) { + $this->systemMessages->addNotice($this->tt('%d pattern was added to the local blocklist.', '%d patterns were added to the local blocklist.', $count)); + } else { + $this->systemMessages->addNotice($this->t('No pattern was added to the local blocklist.')); + } + } + + $this->baseUrl->redirect('/moderation/blocklist/server'); + } + } + + /** + * @param array $request + * @return string + * @throws HTTPException\ServiceUnavailableException + */ + protected function content(array $request = []): string + { + parent::content(); + + $t = Renderer::getMarkupTemplate('moderation/blocklist/server/import.tpl'); + return Renderer::replaceMacros($t, [ + '$l10n' => [ + 'return_list' => $this->t('← Return to the list'), + 'title' => $this->t('Moderation'), + 'page' => $this->t('Import a Server Domain Pattern Blocklist'), + 'download' => $this->t('

This file can be downloaded from the /friendica path of any Friendica server.

'), + 'upload' => $this->t('Upload file'), + 'patterns' => $this->t('Patterns to import'), + 'domain_pattern' => $this->t('Domain Pattern'), + 'block_reason' => $this->t('Block Reason'), + 'mode' => $this->t('Import Mode'), + 'import' => $this->t('Import Patterns'), + 'pattern_count' => $this->tt('%d total pattern', '%d total patterns', count($this->blocklist)), + ], + '$listfile' => ['listfile', $this->t('Server domain pattern blocklist CSV file'), '', '', $this->t('Required'), '', 'file'], + '$mode_append' => ['mode', $this->t('Append'), 'append', $this->t('Imports patterns from the file that weren\'t already existing in the current blocklist.'), 'checked="checked"'], + '$mode_replace' => ['mode', $this->t('Replace'), 'replace', $this->t('Replaces the current blocklist by the imported patterns.')], + '$blocklist' => $this->blocklist, + '$baseurl' => $this->baseUrl->get(true), + '$form_security_token' => self::getFormSecurityToken('moderation_blocklist_import') + ]); + } +} diff --git a/src/Module/Moderation/Blocklist/Server/Index.php b/src/Module/Moderation/Blocklist/Server/Index.php new file mode 100644 index 0000000000..e75d50dd71 --- /dev/null +++ b/src/Module/Moderation/Blocklist/Server/Index.php @@ -0,0 +1,121 @@ +. + * + */ + +namespace Friendica\Module\Moderation\Blocklist\Server; + +use Friendica\App; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Moderation\DomainPatternBlocklist; +use Friendica\Module\BaseModeration; +use Friendica\Module\Response; +use Friendica\Navigation\SystemMessages; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; + +class Index extends BaseModeration +{ + /** @var DomainPatternBlocklist */ + private $blocklist; + + public function __construct(DomainPatternBlocklist $blocklist, 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->blocklist = $blocklist; + } + + protected function post(array $request = []) + { + $this->checkModerationAccess(); + + if (empty($request['page_blocklist_edit'])) { + return; + } + + self::checkFormSecurityTokenRedirectOnError('/moderation/blocklist/server', 'moderation_blocklist'); + + // Edit the entries from blocklist + $blocklist = []; + foreach ($request['domain'] as $id => $domain) { + // Trimming whitespaces as well as any lingering slashes + $domain = trim($domain); + $reason = trim($request['reason'][$id]); + if (empty($request['delete'][$id])) { + $blocklist[] = [ + 'domain' => $domain, + 'reason' => $reason + ]; + } + } + + $this->blocklist->set($blocklist); + + $this->baseUrl->redirect('moderation/blocklist/server'); + } + + protected function content(array $request = []): string + { + parent::content(); + + $blocklistform = []; + foreach ($this->blocklist->get() as $id => $b) { + $blocklistform[] = [ + 'domain' => ["domain[$id]", $this->t('Blocked server domain pattern'), $b['domain'], '', $this->t('Required'), '', ''], + 'reason' => ["reason[$id]", $this->t("Reason for the block"), $b['reason'], '', $this->t('Required'), '', ''], + 'delete' => ["delete[$id]", $this->t("Delete server domain pattern") . ' (' . $b['domain'] . ')', false, $this->t("Check to delete this entry from the blocklist")] + ]; + } + + $t = Renderer::getMarkupTemplate('moderation/blocklist/server/index.tpl'); + return Renderer::replaceMacros($t, [ + '$l10n' => [ + 'title' => $this->t('Moderation'), + 'page' => $this->t('Server Domain Pattern Blocklist'), + 'intro' => $this->t('This page can be used to define a blocklist of server domain patterns from the federated network that are not allowed to interact with your node. For each domain pattern you should also provide the reason why you block it.'), + 'public' => $this->t('The list of blocked server domain patterns will be made publically available on the /friendica page so that your users and people investigating communication problems can find the reason easily.'), + 'syntax' => $this->t('

The server domain pattern syntax is case-insensitive shell wildcard, comprising the following special characters:

+'), + 'importtitle' => $this->t('Import server domain pattern blocklist'), + 'addtitle' => $this->t('Add new entry to the blocklist'), + 'importsubmit' => $this->t('Upload file'), + 'addsubmit' => $this->t('Check pattern'), + 'savechanges' => $this->t('Save changes to the blocklist'), + 'currenttitle' => $this->t('Current Entries in the Blocklist'), + 'thurl' => $this->t('Blocked server domain pattern'), + 'threason' => $this->t('Reason for the block'), + 'delentry' => $this->t('Delete entry from the blocklist'), + 'confirm_delete' => $this->t('Delete entry from the blocklist?'), + ], + '$listfile' => ['listfile', $this->t('Server domain pattern blocklist CSV file'), '', '', $this->t('Required'), '', 'file'], + '$newdomain' => ['pattern', $this->t('Server Domain Pattern'), '', $this->t('The domain pattern of the new server to add to the blocklist. Do not include the protocol.'), $this->t('Required'), '', ''], + '$entries' => $blocklistform, + '$baseurl' => $this->baseUrl->get(true), + + '$form_security_token' => self::getFormSecurityToken('moderation_blocklist'), + '$form_security_token_import' => self::getFormSecurityToken('moderation_blocklist_import'), + ]); + } +} diff --git a/src/Module/Moderation/Item/Delete.php b/src/Module/Moderation/Item/Delete.php new file mode 100644 index 0000000000..6367988014 --- /dev/null +++ b/src/Module/Moderation/Item/Delete.php @@ -0,0 +1,71 @@ +. + * + */ + +namespace Friendica\Module\Moderation\Item; + +use Friendica\Core\Renderer; +use Friendica\Model\Item; +use Friendica\Module\BaseModeration; + +class Delete extends BaseModeration +{ + protected function post(array $request = []) + { + $this->checkModerationAccess(); + + if (empty($request['page_deleteitem_submit'])) { + return; + } + + self::checkFormSecurityTokenRedirectOnError('/moderation/item/delete', 'moderation_deleteitem'); + + $guid = trim($request['deleteitemguid']); + // The GUID should not include a "/", so if there is one, we got an URL + // and the last part of it is most likely the GUID. + if (strpos($guid, '/')) { + $guid = substr($guid, strrpos($guid, '/') + 1); + } + // Now that we have the GUID, drop those items, which will also delete the + // associated threads. + Item::markForDeletion(['guid' => $guid]); + + $this->systemMessages->addInfo($this->t('Item marked for deletion.')); + $this->baseUrl->redirect('moderation/item/delete'); + } + + protected function content(array $request = []): string + { + parent::content(); + + $t = Renderer::getMarkupTemplate('moderation/item/delete.tpl'); + + return Renderer::replaceMacros($t, [ + '$title' => $this->t('Moderation'), + '$page' => $this->t('Delete Item'), + '$submit' => $this->t('Delete this Item'), + '$intro1' => $this->t('On this page you can delete an item from your node. If the item is a top level posting, the entire thread will be deleted.'), + '$intro2' => $this->t('You need to know the GUID of the item. You can find it e.g. by looking at the display URL. The last part of http://example.com/display/123456 is the GUID, here 123456.'), + + '$deleteitemguid' => ['deleteitemguid', $this->t("GUID"), '', $this->t("The GUID of the item you want to delete."), $this->t('Required'), 'autofocus'], + '$form_security_token' => self::getFormSecurityToken("moderation_deleteitem") + ]); + } +} diff --git a/src/Module/Admin/Item/Source.php b/src/Module/Moderation/Item/Source.php similarity index 61% rename from src/Module/Admin/Item/Source.php rename to src/Module/Moderation/Item/Source.php index fe3f3674b7..38304883a1 100644 --- a/src/Module/Admin/Item/Source.php +++ b/src/Module/Moderation/Item/Source.php @@ -19,21 +19,19 @@ * */ -namespace Friendica\Module\Admin\Item; +namespace Friendica\Module\Moderation\Item; use Friendica\Core\Renderer; -use Friendica\DI; use Friendica\Model; -use Friendica\Module\BaseAdmin; - -class Source extends BaseAdmin +use Friendica\Module\BaseModeration; +class Source extends BaseModeration { protected function content(array $request = []): string { parent::content(); - $guid = basename($_REQUEST['guid'] ?? $this->parameters['guid'] ?? ''); + $guid = basename($request['guid'] ?? $this->parameters['guid'] ?? ''); $item_uri = ''; $item_id = ''; @@ -48,25 +46,23 @@ class Source extends BaseAdmin } } - $tpl = Renderer::getMarkupTemplate('admin/item/source.tpl'); - $o = Renderer::replaceMacros($tpl, [ - '$title' => DI::l10n()->t('Item Source'), - '$guid' => ['guid', DI::l10n()->t('Item Guid'), $guid, ''], + $tpl = Renderer::getMarkupTemplate('moderation/item/source.tpl'); + return Renderer::replaceMacros($tpl, [ + '$title' => $this->t('Item Source'), + '$guid' => ['guid', $this->t('Item Guid'), $guid, ''], '$item_uri' => $item_uri, '$item_id' => $item_id, '$terms' => $terms, - '$itemidlbl' => DI::l10n()->t('Item Id'), - '$itemurilbl' => DI::l10n()->t('Item URI'), - '$submit' => DI::l10n()->t('Submit'), - '$termslbl' => DI::l10n()->t('Terms'), - '$taglbl' => DI::l10n()->t('Tag'), - '$typelbl' => DI::l10n()->t('Type'), - '$termlbl' => DI::l10n()->t('Term'), - '$urllbl' => DI::l10n()->t('URL'), - '$mentionlbl' => DI::l10n()->t('Mention'), - '$implicitlbl' => DI::l10n()->t('Implicit Mention'), + '$itemidlbl' => $this->t('Item Id'), + '$itemurilbl' => $this->t('Item URI'), + '$submit' => $this->t('Submit'), + '$termslbl' => $this->t('Terms'), + '$taglbl' => $this->t('Tag'), + '$typelbl' => $this->t('Type'), + '$termlbl' => $this->t('Term'), + '$urllbl' => $this->t('URL'), + '$mentionlbl' => $this->t('Mention'), + '$implicitlbl' => $this->t('Implicit Mention'), ]); - - return $o; } } diff --git a/src/Module/Moderation/Summary.php b/src/Module/Moderation/Summary.php new file mode 100644 index 0000000000..c8719c14dc --- /dev/null +++ b/src/Module/Moderation/Summary.php @@ -0,0 +1,84 @@ +. + * + */ + +namespace Friendica\Module\Moderation; + +use Friendica\App; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Database\Database; +use Friendica\Model\Register; +use Friendica\Module\BaseModeration; +use Friendica\Module\Response; +use Friendica\Navigation\SystemMessages; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; + +class Summary 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(); + + $accounts = [ + [$this->t('Normal Account'), 0], + [$this->t('Automatic Follower Account'), 0], + [$this->t('Public Forum Account'), 0], + [$this->t('Automatic Friend Account'), 0], + [$this->t('Blog Account'), 0], + [$this->t('Private Forum Account'), 0] + ]; + + $users = 0; + + $pageFlagsCountStmt = $this->database->p('SELECT `page-flags`, COUNT(`uid`) AS `count` FROM `user` WHERE `uid` != ? GROUP BY `page-flags`', 0); + while ($pageFlagsCount = $this->database->fetch($pageFlagsCountStmt)) { + $accounts[$pageFlagsCount['page-flags']][1] = $pageFlagsCount['count']; + $users += $pageFlagsCount['count']; + } + $this->database->close($pageFlagsCountStmt); + + $this->logger->debug('accounts', ['accounts' => $accounts]); + + $pending = Register::getPendingCount(); + + $t = Renderer::getMarkupTemplate('moderation/summary.tpl'); + return Renderer::replaceMacros($t, [ + '$title' => $this->t('Moderation'), + '$page' => $this->t('Summary'), + '$users' => [$this->t('Registered users'), $users], + '$accounts' => $accounts, + '$pending' => [$this->t('Pending registrations'), $pending], + '$warningtext' => [], + ]); + } +} diff --git a/src/Module/Moderation/Users/Active.php b/src/Module/Moderation/Users/Active.php new file mode 100644 index 0000000000..922351dc73 --- /dev/null +++ b/src/Module/Moderation/Users/Active.php @@ -0,0 +1,161 @@ +. + * + */ + +namespace Friendica\Module\Moderation\Users; + +use Friendica\Content\Pager; +use Friendica\Core\Renderer; +use Friendica\Model\User; +use Friendica\Module\Moderation\BaseUsers; + +class Active extends BaseUsers +{ + protected function post(array $request = []) + { + $this->checkModerationAccess(); + + self::checkFormSecurityTokenRedirectOnError($this->baseUrl->get(true), 'moderation_users_active'); + + $users = $request['user'] ?? []; + + if (!empty($request['page_users_block'])) { + foreach ($users as $uid) { + User::block($uid); + } + $this->systemMessages->addInfo($this->tt('%s user blocked', '%s users blocked', count($users))); + } + + if (!empty($request['page_users_delete'])) { + foreach ($users as $uid) { + if ($this->session->getLocalUserId() != $uid) { + User::remove($uid); + } else { + $this->systemMessages->addNotice($this->t('You can\'t remove yourself')); + } + } + + $this->systemMessages->addInfo($this->tt('%s user deleted', '%s users deleted', count($users))); + } + + $this->baseUrl->redirect($this->args->getQueryString()); + } + + protected function content(array $request = []): string + { + parent::content(); + + $action = $this->parameters['action'] ?? ''; + $uid = $this->parameters['uid'] ?? 0; + + if ($uid) { + $user = User::getById($uid, ['username', 'blocked']); + if (!$user) { + $this->systemMessages->addNotice($this->t('User not found')); + $this->baseUrl->redirect('moderation/users'); + } + } + + switch ($action) { + case 'delete': + if ($this->session->getLocalUserId() != $uid) { + self::checkFormSecurityTokenRedirectOnError('moderation/users/active', 'moderation_users_active', 't'); + // delete user + User::remove($uid); + + $this->systemMessages->addNotice($this->t('User "%s" deleted', $user['username'])); + } else { + $this->systemMessages->addNotice($this->t('You can\'t remove yourself')); + } + + $this->baseUrl->redirect('moderation/users/active'); + break; + case 'block': + self::checkFormSecurityTokenRedirectOnError('moderation/users/active', 'moderation_users_active', 't'); + User::block($uid); + $this->systemMessages->addNotice($this->t('User "%s" blocked', $user['username'])); + $this->baseUrl->redirect('moderation/users/active'); + break; + } + $pager = new Pager($this->l10n, $this->args->getQueryString(), 100); + + $valid_orders = [ + 'name', + 'email', + 'register_date', + 'login_date', + 'last-item', + 'page-flags', + ]; + + $order = 'name'; + $order_direction = '+'; + if (!empty($request['o'])) { + $new_order = $request['o']; + if ($new_order[0] === '-') { + $order_direction = '-'; + $new_order = substr($new_order, 1); + } + + if (in_array($new_order, $valid_orders)) { + $order = $new_order; + } + } + + $users = User::getList($pager->getStart(), $pager->getItemsPerPage(), 'active', $order, ($order_direction == '-')); + + $users = array_map($this->setupUserCallback(), $users); + + $th_users = array_map(null, [$this->t('Name'), $this->t('Email'), $this->t('Register date'), $this->t('Last login'), $this->t('Last public item'), $this->t('Type')], $valid_orders); + + $count = $this->database->count('user', ["NOT `blocked` AND `verified` AND NOT `account_removed` AND `uid` != ?", 0]); + + $t = Renderer::getMarkupTemplate('moderation/users/active.tpl'); + return self::getTabsHTML('active') . Renderer::replaceMacros($t, [ + // strings // + '$title' => $this->t('Moderation'), + '$page' => $this->t('Active Accounts'), + '$select_all' => $this->t('select all'), + '$delete' => $this->t('Delete'), + '$block' => $this->t('Block'), + '$blocked' => $this->t('User blocked'), + '$siteadmin' => $this->t('Site admin'), + '$accountexpired' => $this->t('Account expired'), + '$h_newuser' => $this->t('Create a new user'), + + '$th_users' => $th_users, + '$order_users' => $order, + '$order_direction_users' => $order_direction, + + '$confirm_delete_multi' => $this->t('Selected users will be deleted!\n\nEverything these users had posted on this site will be permanently deleted!\n\nAre you sure?'), + '$confirm_delete' => $this->t('The user {0} will be deleted!\n\nEverything this user has posted on this site will be permanently deleted!\n\nAre you sure?'), + + '$form_security_token' => self::getFormSecurityToken('moderation_users_active'), + + // values // + '$baseurl' => $this->baseUrl->get(true), + '$query_string' => $this->args->getQueryString(), + + '$users' => $users, + '$count' => $count, + '$pager' => $pager->renderFull($count), + ]); + } +} diff --git a/src/Module/Moderation/Users/Blocked.php b/src/Module/Moderation/Users/Blocked.php new file mode 100644 index 0000000000..022262907d --- /dev/null +++ b/src/Module/Moderation/Users/Blocked.php @@ -0,0 +1,160 @@ +. + * + */ + +namespace Friendica\Module\Moderation\Users; + +use Friendica\Content\Pager; +use Friendica\Core\Renderer; +use Friendica\Model\User; +use Friendica\Module\Moderation\BaseUsers; + +class Blocked extends BaseUsers +{ + protected function post(array $request = []) + { + $this->checkModerationAccess(); + + self::checkFormSecurityTokenRedirectOnError('/moderation/users/blocked', 'moderation_users_blocked'); + + $users = $request['user'] ?? []; + + if (!empty($request['page_users_unblock'])) { + foreach ($users as $uid) { + User::block($uid, false); + } + $this->systemMessages->addInfo($this->tt('%s user unblocked', '%s users unblocked', count($users))); + } + + if (!empty($request['page_users_delete'])) { + foreach ($users as $uid) { + if ($this->session->getLocalUserId() != $uid) { + User::remove($uid); + } else { + $this->systemMessages->addNotice($this->t('You can\'t remove yourself')); + } + } + + $this->systemMessages->addInfo($this->tt('%s user deleted', '%s users deleted', count($users))); + } + + $this->baseUrl->redirect('moderation/users/blocked'); + } + + protected function content(array $request = []): string + { + parent::content(); + + $action = $this->parameters['action'] ?? ''; + $uid = $this->parameters['uid'] ?? 0; + + if ($uid) { + $user = User::getById($uid, ['username', 'blocked']); + if (!$user) { + $this->systemMessages->addNotice($this->t('User not found')); + $this->baseUrl->redirect('moderation/users'); + } + } + + switch ($action) { + case 'delete': + if ($this->session->getLocalUserId() != $uid) { + self::checkFormSecurityTokenRedirectOnError('/moderation/users/blocked', 'moderation_users_blocked', 't'); + // delete user + User::remove($uid); + + $this->systemMessages->addNotice($this->t('User "%s" deleted', $user['username'])); + } else { + $this->systemMessages->addNotice($this->t('You can\'t remove yourself')); + } + $this->baseUrl->redirect('moderation/users/blocked'); + break; + case 'unblock': + self::checkFormSecurityTokenRedirectOnError('/moderation/users/blocked', 'moderation_users_blocked', 't'); + User::block($uid, false); + $this->systemMessages->addNotice($this->t('User "%s" unblocked', $user['username'])); + $this->baseUrl->redirect('moderation/users/blocked'); + break; + } + + $pager = new Pager($this->l10n, $this->args->getQueryString(), 100); + + $valid_orders = [ + 'name', + 'email', + 'register_date', + 'login_date', + 'last-item', + 'page-flags', + ]; + + $order = 'name'; + $order_direction = '+'; + if (!empty($request['o'])) { + $new_order = $request['o']; + if ($new_order[0] === '-') { + $order_direction = '-'; + $new_order = substr($new_order, 1); + } + + if (in_array($new_order, $valid_orders)) { + $order = $new_order; + } + } + + $users = User::getList($pager->getStart(), $pager->getItemsPerPage(), 'blocked', $order, ($order_direction == '-')); + + $users = array_map($this->setupUserCallback(), $users); + + $th_users = array_map(null, [$this->t('Name'), $this->t('Email'), $this->t('Register date'), $this->t('Last login'), $this->t('Last public item'), $this->t('Type')], $valid_orders); + + $count = $this->database->count('user', ['blocked' => true, 'verified' => true]); + + $t = Renderer::getMarkupTemplate('moderation/users/blocked.tpl'); + return self::getTabsHTML('blocked') . Renderer::replaceMacros($t, [ + // strings // + '$title' => $this->t('Moderation'), + '$page' => $this->t('Blocked Users'), + '$select_all' => $this->t('select all'), + '$delete' => $this->t('Delete'), + '$blocked' => $this->t('User blocked'), + '$unblock' => $this->t('Unblock'), + '$siteadmin' => $this->t('Site admin'), + '$accountexpired' => $this->t('Account expired'), + + '$th_users' => $th_users, + '$order_users' => $order, + '$order_direction_users' => $order_direction, + + '$confirm_delete_multi' => $this->t('Selected users will be deleted!\n\nEverything these users had posted on this site will be permanently deleted!\n\nAre you sure?'), + '$confirm_delete' => $this->t('The user {0} will be deleted!\n\nEverything this user has posted on this site will be permanently deleted!\n\nAre you sure?'), + + '$form_security_token' => self::getFormSecurityToken('moderation_users_blocked'), + + // values // + '$baseurl' => $this->baseUrl->get(true), + '$query_string' => $this->args->getQueryString(), + + '$users' => $users, + '$count' => $count, + '$pager' => $pager->renderFull($count) + ]); + } +} diff --git a/src/Module/Admin/Users/Create.php b/src/Module/Moderation/Users/Create.php similarity index 62% rename from src/Module/Admin/Users/Create.php rename to src/Module/Moderation/Users/Create.php index 29dbf5d7ee..a017cf681e 100644 --- a/src/Module/Admin/Users/Create.php +++ b/src/Module/Moderation/Users/Create.php @@ -19,36 +19,36 @@ * */ -namespace Friendica\Module\Admin\Users; +namespace Friendica\Module\Moderation\Users; use Friendica\Core\Renderer; use Friendica\DI; use Friendica\Model\User; -use Friendica\Module\Admin\BaseUsers; +use Friendica\Module\Moderation\BaseUsers; class Create extends BaseUsers { protected function post(array $request = []) { - self::checkAdminAccess(); + $this->checkModerationAccess(); self::checkFormSecurityTokenRedirectOnError('/admin/users/create', 'admin_users_create'); - $nu_name = $_POST['new_user_name'] ?? ''; - $nu_nickname = $_POST['new_user_nickname'] ?? ''; - $nu_email = $_POST['new_user_email'] ?? ''; + $nu_name = $request['new_user_name'] ?? ''; + $nu_nickname = $request['new_user_nickname'] ?? ''; + $nu_email = $request['new_user_email'] ?? ''; $nu_language = DI::config()->get('system', 'language'); if ($nu_name !== '' && $nu_email !== '' && $nu_nickname !== '') { try { User::createMinimal($nu_name, $nu_email, $nu_nickname, $nu_language); - DI::baseUrl()->redirect('admin/users'); + $this->baseUrl->redirect('admin/users'); } catch (\Exception $ex) { - DI::sysmsg()->addNotice($ex->getMessage()); + $this->systemMessages->addNotice($ex->getMessage()); } } - DI::baseUrl()->redirect('admin/users/create'); + $this->baseUrl->redirect('admin/users/create'); } protected function content(array $request = []): string @@ -58,19 +58,19 @@ class Create extends BaseUsers $t = Renderer::getMarkupTemplate('admin/users/create.tpl'); return self::getTabsHTML('all') . Renderer::replaceMacros($t, [ // strings // - '$title' => DI::l10n()->t('Administration'), - '$page' => DI::l10n()->t('New User'), - '$submit' => DI::l10n()->t('Add User'), + '$title' => $this->t('Administration'), + '$page' => $this->t('New User'), + '$submit' => $this->t('Add User'), '$form_security_token' => self::getFormSecurityToken('admin_users_create'), // values // - '$baseurl' => DI::baseUrl()->get(true), - '$query_string' => DI::args()->getQueryString(), + '$baseurl' => $this->baseUrl->get(true), + '$query_string' => $this->args->getQueryString(), - '$newusername' => ['new_user_name', DI::l10n()->t('Name'), '', DI::l10n()->t('Name of the new user.')], - '$newusernickname' => ['new_user_nickname', DI::l10n()->t('Nickname'), '', DI::l10n()->t('Nickname of the new user.')], - '$newuseremail' => ['new_user_email', DI::l10n()->t('Email'), '', DI::l10n()->t('Email address of the new user.'), '', '', 'email'], + '$newusername' => ['new_user_name', $this->t('Name'), '', $this->t('Name of the new user.')], + '$newusernickname' => ['new_user_nickname', $this->t('Nickname'), '', $this->t('Nickname of the new user.')], + '$newuseremail' => ['new_user_email', $this->t('Email'), '', $this->t('Email address of the new user.'), '', '', 'email'], ]); } } diff --git a/src/Module/Admin/Users/Deleted.php b/src/Module/Moderation/Users/Deleted.php similarity index 59% rename from src/Module/Admin/Users/Deleted.php rename to src/Module/Moderation/Users/Deleted.php index d60ecbe51a..2dd6d6a1b2 100644 --- a/src/Module/Admin/Users/Deleted.php +++ b/src/Module/Moderation/Users/Deleted.php @@ -19,36 +19,31 @@ * */ -namespace Friendica\Module\Admin\Users; +namespace Friendica\Module\Moderation\Users; use Friendica\Content\Pager; use Friendica\Core\Renderer; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\Register; use Friendica\Model\User; -use Friendica\Module\Admin\BaseUsers; -use Friendica\Module\BaseAdmin; -use Friendica\Util\Temporal; +use Friendica\Module\Moderation\BaseUsers; class Deleted extends BaseUsers { protected function post(array $request = []) { - self::checkAdminAccess(); + $this->checkModerationAccess(); - self::checkFormSecurityTokenRedirectOnError('/admin/users/deleted', 'admin_users_deleted'); + self::checkFormSecurityTokenRedirectOnError('/moderation/users/deleted', 'moderation_users_deleted'); // @TODO: Implement user deletion cancellation - DI::baseUrl()->redirect('admin/users/deleted'); + $this->baseUrl->redirect('moderation/users/deleted'); } protected function content(array $request = []): string { parent::content(); - $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), 100); + $pager = new Pager($this->l10n, $this->args->getQueryString(), 100); $valid_orders = [ 'name', @@ -56,13 +51,13 @@ class Deleted extends BaseUsers 'register_date', 'login_date', 'last-item', - 'page-flags' + 'page-flags', ]; $order = 'name'; $order_direction = '+'; - if (!empty($_GET['o'])) { - $new_order = $_GET['o']; + if (!empty($request['o'])) { + $new_order = $request['o']; if ($new_order[0] === '-') { $order_direction = '-'; $new_order = substr($new_order, 1); @@ -75,23 +70,23 @@ class Deleted extends BaseUsers $users = User::getList($pager->getStart(), $pager->getItemsPerPage(), 'removed', $order, ($order_direction == '-')); - $users = array_map(self::setupUserCallback(), $users); + $users = array_map($this->setupUserCallback(), $users); - $count = DBA::count('user', ['account_removed' => true]); + $count = $this->database->count('user', ['account_removed' => true]); - $t = Renderer::getMarkupTemplate('admin/users/deleted.tpl'); + $t = Renderer::getMarkupTemplate('moderation/users/deleted.tpl'); return self::getTabsHTML('deleted') . Renderer::replaceMacros($t, [ // strings // - '$title' => DI::l10n()->t('Administration'), - '$page' => DI::l10n()->t('Users awaiting permanent deletion'), + '$title' => $this->t('Moderation'), + '$page' => $this->t('Users awaiting permanent deletion'), - '$th_deleted' => [DI::l10n()->t('Name'), DI::l10n()->t('Email'), DI::l10n()->t('Register date'), DI::l10n()->t('Last login'), DI::l10n()->t('Last public item'), DI::l10n()->t('Permanent deletion')], + '$th_deleted' => [$this->t('Name'), $this->t('Email'), $this->t('Register date'), $this->t('Last login'), $this->t('Last public item'), $this->t('Permanent deletion')], - '$form_security_token' => self::getFormSecurityToken('admin_users_deleted'), + '$form_security_token' => self::getFormSecurityToken('moderation_users_deleted'), // values // - '$baseurl' => DI::baseUrl()->get(true), - '$query_string' => DI::args()->getQueryString(), + '$baseurl' => $this->baseUrl->get(true), + '$query_string' => $this->args->getQueryString(), '$users' => $users, '$count' => $count, diff --git a/src/Module/Moderation/Users/Index.php b/src/Module/Moderation/Users/Index.php new file mode 100644 index 0000000000..06528650ce --- /dev/null +++ b/src/Module/Moderation/Users/Index.php @@ -0,0 +1,179 @@ +. + * + */ + +namespace Friendica\Module\Moderation\Users; + +use Friendica\Content\Pager; +use Friendica\Core\Renderer; +use Friendica\Model\User; +use Friendica\Module\Moderation\BaseUsers; + +class Index extends BaseUsers +{ + protected function post(array $request = []) + { + $this->checkModerationAccess(); + + self::checkFormSecurityTokenRedirectOnError('moderation/users', 'moderation_users'); + + $users = $request['user'] ?? []; + + if (!empty($request['page_users_block'])) { + foreach ($users as $uid) { + User::block($uid); + } + $this->systemMessages->addInfo($this->tt('%s user blocked', '%s users blocked', count($users))); + } + + if (!empty($request['page_users_unblock'])) { + foreach ($users as $uid) { + User::block($uid, false); + } + $this->systemMessages->addInfo($this->tt('%s user unblocked', '%s users unblocked', count($users))); + } + + if (!empty($request['page_users_delete'])) { + foreach ($users as $uid) { + if ($this->session->getLocalUserId() != $uid) { + User::remove($uid); + } else { + $this->systemMessages->addNotice($this->t('You can\'t remove yourself')); + } + } + + $this->systemMessages->addInfo($this->tt('%s user deleted', '%s users deleted', count($users))); + } + + $this->baseUrl->redirect($this->args->getQueryString()); + } + + protected function content(array $request = []): string + { + parent::content(); + + $action = $this->parameters['action'] ?? ''; + $uid = $this->parameters['uid'] ?? 0; + + if ($uid) { + $user = User::getById($uid, ['username', 'blocked']); + if (!$user) { + $this->systemMessages->addNotice($this->t('User not found')); + $this->baseUrl->redirect('moderation/users'); + } + } + + switch ($action) { + case 'delete': + if ($this->session->getLocalUserId() != $uid) { + self::checkFormSecurityTokenRedirectOnError($this->baseUrl->get(true), 'moderation_users', 't'); + // delete user + User::remove($uid); + + $this->systemMessages->addNotice($this->t('User "%s" deleted', $user['username'])); + } else { + $this->systemMessages->addNotice($this->t('You can\'t remove yourself')); + } + + $this->baseUrl->redirect('moderation/users'); + break; + case 'block': + self::checkFormSecurityTokenRedirectOnError('moderation/users', 'moderation_users', 't'); + User::block($uid); + $this->systemMessages->addNotice($this->t('User "%s" blocked', $user['username'])); + $this->baseUrl->redirect('moderation/users'); + break; + case 'unblock': + self::checkFormSecurityTokenRedirectOnError('moderation/users', 'moderation_users', 't'); + User::block($uid, false); + $this->systemMessages->addNotice($this->t('User "%s" unblocked', $user['username'])); + $this->baseUrl->redirect('moderation/users'); + break; + } + + $pager = new Pager($this->l10n, $this->args->getQueryString(), 100); + + $valid_orders = [ + 'name', + 'email', + 'register_date', + 'login_date', + 'last-item', + 'page-flags', + ]; + + $order = 'name'; + $order_direction = '+'; + if (!empty($request['o'])) { + $new_order = $request['o']; + if ($new_order[0] === '-') { + $order_direction = '-'; + $new_order = substr($new_order, 1); + } + + if (in_array($new_order, $valid_orders)) { + $order = $new_order; + } + } + + $users = User::getList($pager->getStart(), $pager->getItemsPerPage(), 'all', $order, ($order_direction == '-')); + + $users = array_map($this->setupUserCallback(), $users); + + $th_users = array_map(null, [$this->t('Name'), $this->t('Email'), $this->t('Register date'), $this->t('Last login'), $this->t('Last public item'), $this->t('Type')], $valid_orders); + + $count = $this->database->count('user', ["`uid` != ?", 0]); + + $t = Renderer::getMarkupTemplate('moderation/users/index.tpl'); + return self::getTabsHTML('all') . Renderer::replaceMacros($t, [ + // strings // + '$title' => $this->t('Moderation'), + '$page' => $this->t('Users'), + '$select_all' => $this->t('select all'), + '$h_deleted' => $this->t('User waiting for permanent deletion'), + '$delete' => $this->t('Delete'), + '$block' => $this->t('Block'), + '$blocked' => $this->t('User blocked'), + '$unblock' => $this->t('Unblock'), + '$siteadmin' => $this->t('Site admin'), + '$accountexpired' => $this->t('Account expired'), + + '$h_users' => $this->t('Users'), + '$h_newuser' => $this->t('Create a new user'), + '$th_deleted' => [$this->t('Name'), $this->t('Email'), $this->t('Register date'), $this->t('Last login'), $this->t('Last public item'), $this->t('Permanent deletion')], + '$th_users' => $th_users, + '$order_users' => $order, + '$order_direction_users' => $order_direction, + + '$confirm_delete_multi' => $this->t('Selected users will be deleted!\n\nEverything these users had posted on this site will be permanently deleted!\n\nAre you sure?'), + '$confirm_delete' => $this->t('The user {0} will be deleted!\n\nEverything this user has posted on this site will be permanently deleted!\n\nAre you sure?'), + + '$form_security_token' => self::getFormSecurityToken('moderation_users'), + + // values // + '$baseurl' => $this->baseUrl->get(true), + '$query_string' => $this->args->getQueryString(), + + '$users' => $users, + '$count' => $count, + '$pager' => $pager->renderFull($count), + ]); + } +} diff --git a/src/Module/Admin/Users/Pending.php b/src/Module/Moderation/Users/Pending.php similarity index 58% rename from src/Module/Admin/Users/Pending.php rename to src/Module/Moderation/Users/Pending.php index 484a128ebf..569d0404c7 100644 --- a/src/Module/Admin/Users/Pending.php +++ b/src/Module/Moderation/Users/Pending.php @@ -19,43 +19,39 @@ * */ -namespace Friendica\Module\Admin\Users; +namespace Friendica\Module\Moderation\Users; use Friendica\Content\Pager; use Friendica\Core\Renderer; -use Friendica\Database\DBA; -use Friendica\DI; use Friendica\Model\Register; use Friendica\Model\User; -use Friendica\Module\Admin\BaseUsers; -use Friendica\Module\BaseAdmin; -use Friendica\Util\Temporal; +use Friendica\Module\Moderation\BaseUsers; class Pending extends BaseUsers { protected function post(array $request = []) { - self::checkAdminAccess(); + $this->checkModerationAccess(); self::checkFormSecurityTokenRedirectOnError('/admin/users/pending', 'admin_users_pending'); - $pending = $_POST['pending'] ?? []; + $pending = $request['pending'] ?? []; - if (!empty($_POST['page_users_approve'])) { + if (!empty($request['page_users_approve'])) { foreach ($pending as $hash) { User::allow($hash); } - DI::sysmsg()->addInfo(DI::l10n()->tt('%s user approved', '%s users approved', count($pending))); + $this->systemMessages->addInfo($this->tt('%s user approved', '%s users approved', count($pending))); } - if (!empty($_POST['page_users_deny'])) { + if (!empty($request['page_users_deny'])) { foreach ($pending as $hash) { User::deny($hash); } - DI::sysmsg()->addInfo(DI::l10n()->tt('%s registration revoked', '%s registrations revoked', count($pending))); + $this->systemMessages->addInfo($this->tt('%s registration revoked', '%s registrations revoked', count($pending))); } - DI::baseUrl()->redirect('admin/users/pending'); + $this->baseUrl->redirect('admin/users/pending'); } protected function content(array $request = []): string @@ -63,14 +59,13 @@ class Pending extends BaseUsers parent::content(); $action = $this->parameters['action'] ?? ''; - $uid = $this->parameters['uid'] ?? 0; + $uid = $this->parameters['uid'] ?? 0; if ($uid) { $user = User::getById($uid, ['username', 'blocked']); - if (!DBA::isResult($user)) { - DI::sysmsg()->addNotice(DI::l10n()->t('User not found')); - DI::baseUrl()->redirect('admin/users'); - return ''; // NOTREACHED + if (!$user) { + $this->systemMessages->addNotice($this->t('User not found')); + $this->baseUrl->redirect('admin/users'); } } @@ -78,18 +73,18 @@ class Pending extends BaseUsers case 'allow': self::checkFormSecurityTokenRedirectOnError('/admin/users/pending', 'admin_users_pending', 't'); User::allow(Register::getPendingForUser($uid)['hash'] ?? ''); - DI::sysmsg()->addNotice(DI::l10n()->t('Account approved.')); - DI::baseUrl()->redirect('admin/users/pending'); + $this->systemMessages->addNotice($this->t('Account approved.')); + $this->baseUrl->redirect('admin/users/pending'); break; case 'deny': self::checkFormSecurityTokenRedirectOnError('/admin/users/pending', 'admin_users_pending', 't'); User::deny(Register::getPendingForUser($uid)['hash'] ?? ''); - DI::sysmsg()->addNotice(DI::l10n()->t('Registration revoked')); - DI::baseUrl()->redirect('admin/users/pending'); + $this->systemMessages->addNotice($this->t('Registration revoked')); + $this->baseUrl->redirect('admin/users/pending'); break; } - $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), 100); + $pager = new Pager($this->l10n, $this->args->getQueryString(), 100); $pending = Register::getPending($pager->getStart(), $pager->getItemsPerPage()); @@ -98,20 +93,20 @@ class Pending extends BaseUsers $t = Renderer::getMarkupTemplate('admin/users/pending.tpl'); return self::getTabsHTML('pending') . Renderer::replaceMacros($t, [ // strings // - '$title' => DI::l10n()->t('Administration'), - '$page' => DI::l10n()->t('User registrations awaiting review'), - '$select_all' => DI::l10n()->t('select all'), - '$th_pending' => [DI::l10n()->t('Request date'), DI::l10n()->t('Name'), DI::l10n()->t('Email')], - '$no_pending' => DI::l10n()->t('No registrations.'), - '$pendingnotetext' => DI::l10n()->t('Note from the user'), - '$approve' => DI::l10n()->t('Approve'), - '$deny' => DI::l10n()->t('Deny'), + '$title' => $this->t('Administration'), + '$page' => $this->t('User registrations awaiting review'), + '$select_all' => $this->t('select all'), + '$th_pending' => [$this->t('Request date'), $this->t('Name'), $this->t('Email')], + '$no_pending' => $this->t('No registrations.'), + '$pendingnotetext' => $this->t('Note from the user'), + '$approve' => $this->t('Approve'), + '$deny' => $this->t('Deny'), '$form_security_token' => self::getFormSecurityToken('admin_users_pending'), // values // - '$baseurl' => DI::baseUrl()->get(true), - '$query_string' => DI::args()->getQueryString(), + '$baseurl' => $this->baseUrl->get(true), + '$query_string' => $this->args->getQueryString(), '$pending' => $pending, '$count' => $count, diff --git a/static/routes.config.php b/static/routes.config.php index e64b138307..321e7659c4 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -315,20 +315,11 @@ return [ '/addons' => [Module\Admin\Addons\Index::class, [R::GET, R::POST]], '/addons/{addon}' => [Module\Admin\Addons\Details::class, [R::GET, R::POST]], - - '/blocklist/contact' => [Module\Admin\Blocklist\Contact::class, [R::GET, R::POST]], - '/blocklist/server' => [Module\Admin\Blocklist\Server\Index::class, [R::GET, R::POST]], - '/blocklist/server/add' => [Module\Admin\Blocklist\Server\Add::class, [R::GET, R::POST]], - '/blocklist/server/import' => [Module\Admin\Blocklist\Server\Import::class, [R::GET, R::POST]], - '/dbsync[/{action}[/{update:\d+}]]' => [Module\Admin\DBSync::class, [R::GET]], '/features' => [Module\Admin\Features::class, [R::GET, R::POST]], '/federation' => [Module\Admin\Federation::class, [R::GET]], - '/item/delete' => [Module\Admin\Item\Delete::class, [R::GET, R::POST]], - '/item/source[/{guid}]' => [Module\Admin\Item\Source::class, [R::GET, R::POST]], - '/logs/view' => [Module\Admin\Logs\View::class, [R::GET]], '/logs' => [Module\Admin\Logs\Settings::class, [R::GET, R::POST]], @@ -346,13 +337,6 @@ return [ '/themes/{theme}/embed' => [Module\Admin\Themes\Embed::class, [R::GET, R::POST]], '/tos' => [Module\Admin\Tos::class, [R::GET, R::POST]], - - '/users[/{action}/{uid}]' => [Module\Admin\Users\Index::class, [R::GET, R::POST]], - '/users/active[/{action}/{uid}]' => [Module\Admin\Users\Active::class, [R::GET, R::POST]], - '/users/pending[/{action}/{uid}]' => [Module\Admin\Users\Pending::class, [R::GET, R::POST]], - '/users/blocked[/{action}/{uid}]' => [Module\Admin\Users\Blocked::class, [R::GET, R::POST]], - '/users/deleted' => [Module\Admin\Users\Deleted::class, [R::GET ]], - '/users/create' => [Module\Admin\Users\Create::class, [R::GET, R::POST]], ], '/amcd' => [Module\AccountManagementControlDocument::class, [R::GET]], '/acctlink' => [Module\Acctlink::class, [R::GET]], @@ -474,6 +458,24 @@ return [ '/magic' => [Module\Magic::class, [R::GET]], '/manifest' => [Module\Manifest::class, [R::GET]], '/friendica.webmanifest' => [Module\Manifest::class, [R::GET]], + '/moderation' => [ + '[/]' => [Module\Moderation\Summary::class, [R::GET]], + + '/blocklist/contact' => [Module\Moderation\Blocklist\Contact::class, [R::GET, R::POST]], + '/blocklist/server' => [Module\Moderation\Blocklist\Server\Index::class, [R::GET, R::POST]], + '/blocklist/server/add' => [Module\Moderation\Blocklist\Server\Add::class, [R::GET, R::POST]], + '/blocklist/server/import' => [Module\Moderation\Blocklist\Server\Import::class, [R::GET, R::POST]], + + '/item/delete' => [Module\Moderation\Item\Delete::class, [R::GET, R::POST]], + '/item/source[/{guid}]' => [Module\Moderation\Item\Source::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]], + '/users/pending[/{action}/{uid}]' => [Module\Moderation\Users\Pending::class, [R::GET, R::POST]], + '/users/blocked[/{action}/{uid}]' => [Module\Moderation\Users\Blocked::class, [R::GET, R::POST]], + '/users/deleted' => [Module\Moderation\Users\Deleted::class, [R::GET ]], + '/users/create' => [Module\Moderation\Users\Create::class, [R::GET, R::POST]], + ], '/modexp/{nick}' => [Module\PublicRSAKey::class, [R::GET]], '/newmember' => [Module\Welcome::class, [R::GET]], '/nodeinfo/1.0' => [Module\NodeInfo110::class, [R::GET]], diff --git a/view/templates/admin/summary.tpl b/view/templates/admin/summary.tpl index 2421658d6c..9e69b67326 100644 --- a/view/templates/admin/summary.tpl +++ b/view/templates/admin/summary.tpl @@ -13,22 +13,6 @@
{{$queues.label}}
{{$queues.deferred}} - {{$queues.workerq}}
-
-
{{$pending.0}}
-
{{$pending.1}} -
- -
-
{{$users.0}}
-
{{$users.1}}
-
- {{foreach $accounts as $p}} -
-
{{$p.0}}
-
{{if $p.1}}{{$p.1}}{{else}}0{{/if}}
-
- {{/foreach}} -
{{$addons.0}}
diff --git a/view/templates/moderation/aside.tpl b/view/templates/moderation/aside.tpl new file mode 100644 index 0000000000..38f5392732 --- /dev/null +++ b/view/templates/moderation/aside.tpl @@ -0,0 +1,21 @@ + + +{{foreach $subpages as $page}} +

{{$page.0}}

+ +{{/foreach}} diff --git a/view/templates/admin/blocklist/contact.tpl b/view/templates/moderation/blocklist/contact.tpl similarity index 100% rename from view/templates/admin/blocklist/contact.tpl rename to view/templates/moderation/blocklist/contact.tpl diff --git a/view/templates/admin/blocklist/server/add.tpl b/view/templates/moderation/blocklist/server/add.tpl similarity index 100% rename from view/templates/admin/blocklist/server/add.tpl rename to view/templates/moderation/blocklist/server/add.tpl diff --git a/view/templates/admin/blocklist/server/import.tpl b/view/templates/moderation/blocklist/server/import.tpl similarity index 100% rename from view/templates/admin/blocklist/server/import.tpl rename to view/templates/moderation/blocklist/server/import.tpl diff --git a/view/templates/admin/blocklist/server/index.tpl b/view/templates/moderation/blocklist/server/index.tpl similarity index 100% rename from view/templates/admin/blocklist/server/index.tpl rename to view/templates/moderation/blocklist/server/index.tpl diff --git a/view/templates/admin/item/delete.tpl b/view/templates/moderation/item/delete.tpl similarity index 100% rename from view/templates/admin/item/delete.tpl rename to view/templates/moderation/item/delete.tpl diff --git a/view/templates/admin/item/source.tpl b/view/templates/moderation/item/source.tpl similarity index 100% rename from view/templates/admin/item/source.tpl rename to view/templates/moderation/item/source.tpl diff --git a/view/templates/moderation/settings_head.tpl b/view/templates/moderation/settings_head.tpl new file mode 100644 index 0000000000..25c0f804e4 --- /dev/null +++ b/view/templates/moderation/settings_head.tpl @@ -0,0 +1,9 @@ + diff --git a/view/templates/moderation/summary.tpl b/view/templates/moderation/summary.tpl new file mode 100644 index 0000000000..8769dcd057 --- /dev/null +++ b/view/templates/moderation/summary.tpl @@ -0,0 +1,16 @@ + +
+

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

+ +
+
{{$users.0}}
+
{{$users.1}}
+
+ {{foreach $accounts as $p}} +
+
{{$p.0}}
+
{{if $p.1}}{{$p.1}}{{else}}0{{/if}}
+
+ {{/foreach}} + +
diff --git a/view/templates/admin/users/active.tpl b/view/templates/moderation/users/active.tpl similarity index 100% rename from view/templates/admin/users/active.tpl rename to view/templates/moderation/users/active.tpl diff --git a/view/templates/admin/users/blocked.tpl b/view/templates/moderation/users/blocked.tpl similarity index 100% rename from view/templates/admin/users/blocked.tpl rename to view/templates/moderation/users/blocked.tpl diff --git a/view/templates/admin/users/create.tpl b/view/templates/moderation/users/create.tpl similarity index 100% rename from view/templates/admin/users/create.tpl rename to view/templates/moderation/users/create.tpl diff --git a/view/templates/admin/users/deleted.tpl b/view/templates/moderation/users/deleted.tpl similarity index 100% rename from view/templates/admin/users/deleted.tpl rename to view/templates/moderation/users/deleted.tpl diff --git a/view/templates/admin/users/index.tpl b/view/templates/moderation/users/index.tpl similarity index 100% rename from view/templates/admin/users/index.tpl rename to view/templates/moderation/users/index.tpl diff --git a/view/templates/admin/users/pending.tpl b/view/templates/moderation/users/pending.tpl similarity index 100% rename from view/templates/admin/users/pending.tpl rename to view/templates/moderation/users/pending.tpl diff --git a/view/templates/nav.tpl b/view/templates/nav.tpl index 3c1b9f26b2..b7a332d990 100644 --- a/view/templates/nav.tpl +++ b/view/templates/nav.tpl @@ -22,6 +22,8 @@ {{if $nav.admin}}{{$nav.admin.1}}{{/if}} + {{if $nav.moderation}}{{$nav.moderation.1}}{{/if}} + {{if $nav.network}} {{$nav.network.1}} diff --git a/view/theme/duepuntozero/templates/nav.tpl b/view/theme/duepuntozero/templates/nav.tpl index 8e386d5e6a..b242323fdf 100644 --- a/view/theme/duepuntozero/templates/nav.tpl +++ b/view/theme/duepuntozero/templates/nav.tpl @@ -22,6 +22,8 @@ {{if $nav.admin}}{{$nav.admin.1}}{{/if}} + {{if $nav.moderation}}{{$nav.moderation.1}}{{/if}} + {{if $nav.network}} {{$nav.network.1}} diff --git a/view/theme/frio/templates/admin/summary.tpl b/view/theme/frio/templates/admin/summary.tpl index 6f5645dca7..78c4f6e35c 100644 --- a/view/theme/frio/templates/admin/summary.tpl +++ b/view/theme/frio/templates/admin/summary.tpl @@ -17,29 +17,6 @@
{{$queues.deferred}} - {{$queues.workerq}}
- {{* Number of pending registrations. *}} -
-
-
{{$pending.0}}
-
{{$pending.1}}
-
- - {{* Number of registered users *}} -
-
-
{{$users.0}}
-
{{$users.1}}
-
- - {{* Account types of registered users. *}} - {{foreach $accounts as $p}} -
-
-
{{$p.0}}
-
{{if $p.1}}{{$p.1}}{{else}}0{{/if}}
-
- {{/foreach}} - {{* List enabled addons. *}}

diff --git a/view/theme/frio/templates/moderation/aside.tpl b/view/theme/frio/templates/moderation/aside.tpl new file mode 100644 index 0000000000..1e6ad08503 --- /dev/null +++ b/view/theme/frio/templates/moderation/aside.tpl @@ -0,0 +1,30 @@ + + +{{foreach $subpages as $page}} +
+

{{$page.0}}

+ +
+{{/foreach}} diff --git a/view/theme/frio/templates/admin/blocklist/contact.tpl b/view/theme/frio/templates/moderation/blocklist/contact.tpl similarity index 100% rename from view/theme/frio/templates/admin/blocklist/contact.tpl rename to view/theme/frio/templates/moderation/blocklist/contact.tpl diff --git a/view/theme/frio/templates/moderation/summary.tpl b/view/theme/frio/templates/moderation/summary.tpl new file mode 100644 index 0000000000..71c4ad58eb --- /dev/null +++ b/view/theme/frio/templates/moderation/summary.tpl @@ -0,0 +1,33 @@ + +
+

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

+ +
+ {{* Number of pending registrations. *}} +
+
+
{{$pending.0}}
+
{{$pending.1}}
+
+ + {{* Number of registered users *}} +
+
+
{{$users.0}}
+
{{$users.1}}
+
+ + {{* Account types of registered users. *}} + {{foreach $accounts as $p}} +
+
+
{{$p.0}}
+
{{if $p.1}}{{$p.1}}{{else}}0{{/if}}
+
+ {{/foreach}} + +
+ +
+ +
diff --git a/view/theme/frio/templates/admin/users/active.tpl b/view/theme/frio/templates/moderation/users/active.tpl similarity index 100% rename from view/theme/frio/templates/admin/users/active.tpl rename to view/theme/frio/templates/moderation/users/active.tpl diff --git a/view/theme/frio/templates/admin/users/blocked.tpl b/view/theme/frio/templates/moderation/users/blocked.tpl similarity index 100% rename from view/theme/frio/templates/admin/users/blocked.tpl rename to view/theme/frio/templates/moderation/users/blocked.tpl diff --git a/view/theme/frio/templates/admin/users/create.tpl b/view/theme/frio/templates/moderation/users/create.tpl similarity index 100% rename from view/theme/frio/templates/admin/users/create.tpl rename to view/theme/frio/templates/moderation/users/create.tpl diff --git a/view/theme/frio/templates/admin/users/deleted.tpl b/view/theme/frio/templates/moderation/users/deleted.tpl similarity index 100% rename from view/theme/frio/templates/admin/users/deleted.tpl rename to view/theme/frio/templates/moderation/users/deleted.tpl diff --git a/view/theme/frio/templates/admin/users/index.tpl b/view/theme/frio/templates/moderation/users/index.tpl similarity index 100% rename from view/theme/frio/templates/admin/users/index.tpl rename to view/theme/frio/templates/moderation/users/index.tpl diff --git a/view/theme/frio/templates/admin/users/pending.tpl b/view/theme/frio/templates/moderation/users/pending.tpl similarity index 100% rename from view/theme/frio/templates/admin/users/pending.tpl rename to view/theme/frio/templates/moderation/users/pending.tpl diff --git a/view/theme/frio/templates/nav.tpl b/view/theme/frio/templates/nav.tpl index dd71acff29..ec8d54bccb 100644 --- a/view/theme/frio/templates/nav.tpl +++ b/view/theme/frio/templates/nav.tpl @@ -265,6 +265,15 @@ {{/if}} + {{if $nav.moderation}} +
  • + + {{$nav.moderation.1}} + +
  • + {{/if}} {{if $nav.tos}}
  • diff --git a/view/theme/quattro/templates/nav.tpl b/view/theme/quattro/templates/nav.tpl index 5ad091408e..6bd59b5240 100644 --- a/view/theme/quattro/templates/nav.tpl +++ b/view/theme/quattro/templates/nav.tpl @@ -75,6 +75,7 @@ {{if $nav.settings}}
  • {{$nav.settings.1}}
  • {{/if}} {{if $nav.admin}}
  • {{$nav.admin.1}}
  • {{/if}} + {{if $nav.moderation}}
  • {{$nav.moderation.1}}
  • {{/if}} {{if $nav.logout}}
  • {{$nav.logout.1}}
  • {{/if}} {{if $nav.login}}
  • {{$nav.login.1}}
  • {{/if}} diff --git a/view/theme/smoothly/templates/nav.tpl b/view/theme/smoothly/templates/nav.tpl index aff22a79ef..8df8136f16 100644 --- a/view/theme/smoothly/templates/nav.tpl +++ b/view/theme/smoothly/templates/nav.tpl @@ -48,6 +48,7 @@ {{if $nav.delegation}}
  • {{$nav.delegation.1}}
  • {{/if}} {{if $nav.admin}}
  • {{$nav.admin.1}}
  • {{/if}} + {{if $nav.moderation}}
  • {{$nav.moderation.1}}
  • {{/if}} {{if $nav.help}}
  • {{$nav.help.1}}
  • {{/if}} {{if $nav.tos}}
  • {{$nav.tos.1}}
  • {{/if}} diff --git a/view/theme/vier/templates/nav.tpl b/view/theme/vier/templates/nav.tpl index e102b30594..bf4a7efe9b 100644 --- a/view/theme/vier/templates/nav.tpl +++ b/view/theme/vier/templates/nav.tpl @@ -92,7 +92,12 @@ {{$nav.admin.1}} {{/if}} - {{if $nav.logout}}
  • {{$nav.logout.1}}
  • {{/if}} + {{if $nav.moderation}} +
  • + {{$nav.moderation.1}} +
  • + {{/if}} + {{if $nav.logout}}
  • {{$nav.logout.1}}
  • {{/if}} {{/if}}