]> git.mxchange.org Git - friendica-addons.git/commitdiff
New addon providing additional statistics for moderation
authorMatthew Exon <git.mexon@spamgourmet.com>
Sat, 22 Jun 2024 16:56:32 +0000 (18:56 +0200)
committerMatthew Exon <git.mexon@spamgourmet.com>
Sat, 22 Jun 2024 16:56:32 +0000 (18:56 +0200)
ratioed/ratioed.php [new file with mode: 0644]
ratioed/templates/ratioed.tpl [new file with mode: 0644]

diff --git a/ratioed/ratioed.php b/ratioed/ratioed.php
new file mode 100644 (file)
index 0000000..3c88705
--- /dev/null
@@ -0,0 +1,264 @@
+<?php
+/**
+ * Name: Ratioed
+ * Description: Additional moderation user table with statistics about user behaviour
+ * Version: 0.1
+ * Author: Matthew Exon <http://mat.exon.name>
+ */
+
+use Friendica\Content\Pager;
+use Friendica\Core\Hook;
+use Friendica\Core\Logger;
+use Friendica\Core\Renderer;
+use Friendica\Database\DBA;
+use Friendica\DI;
+use Friendica\Model\User;
+use Friendica\Module\Moderation\Users\Active;
+
+/**
+ * Sets up the addon hooks and updates data in the database if needed
+ */
+function ratioed_install()
+{
+       Hook::register('moderation_users_tabs', 'addon/ratioed/ratioed.php', 'ratioed_users_tabs');
+
+       Logger::info("ratioed: installed");
+}
+
+/**
+ * @brief Uninstallation hook for ratioed plugin
+ */
+function ratioed_uninstall() {
+       Hook::unregister('moderation_users_tabs', 'addon/ratioed/ratioed.php', 'ratioed_users_tabs');
+
+       Logger::info("ratioed: uninstalled");
+}
+
+/**
+ * This is a statement rather than an actual function definition. The simple
+ * existence of this method is checked to figure out if the addon offers a
+ * module.
+ */
+function ratioed_module() {}
+
+/**
+ * @brief Adds additional users tab to the moderation panel
+ *
+ * @param array $arr Parameters, including "tabs" which is the list to modify, and "selectedTab", which is the currently selected tab ID
+ */
+function ratioed_users_tabs(array &$arr) {
+       Logger::debug("ratioed: users tabs");
+
+       array_push($arr['tabs'], [
+               'label'  => DI::l10n()->t('Behaviour'),
+               'url'      => 'ratioed',
+               'sel'      => $arr['selectedTab'] == 'ratioed' ? 'active' : '',
+               'title'  => DI::l10n()->t('Statistics about users behaviour'),
+               'id'            => 'admin-users-ratioed',
+               'accesskey' => 'r',
+       ]);
+}
+
+class Ratioed extends Friendica\Module\Moderation\Users\Active
+{
+       protected function content(array $request = []): string
+       {
+               Friendica\Module\Moderation\Users\Active::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',
+                       'last-activity',
+                       'last-item',
+                       'page-flags',
+               ];
+
+               $order             = 'last-item';
+               $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);
+
+               $header_titles = [
+                       $this->t('Name'),
+                       $this->t('Email'),
+                       $this->t('Register date'),
+                       $this->t('Last login'),
+                       $this->t('Last public item'),
+                       $this->t('Type'),
+                       $this->t('Blocked by'),
+                       $this->t('Comments last 24h'),
+                       $this->t('Reactions last 24h'),
+                       $this->t('Ratio last 24h'),
+               ];
+               $field_names = [
+                       'name',
+                       'email',
+                       'register_date',
+                       'login_date',
+                       'lastitem_date',
+                       'page_flags',
+            'blocked_by',
+                       'comments',
+                       'reactions',
+                       'ratio',
+               ];
+               $th_users = array_map(null, $header_titles, $valid_orders, $field_names);
+
+               $count = $this->database->count('user', ["`verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired` AND `uid` != ?", 0]);
+
+               $t = Renderer::getMarkupTemplate('ratioed.tpl', 'addon/ratioed');
+               return self::getTabsHTML('ratioed') . Renderer::replaceMacros($t, [
+                       // strings //
+                       '$title'                  => $this->t('Moderation'),
+                       '$page'            => $this->t('Behaviour'),
+                       '$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,
+                       '$query_string' => $this->args->getQueryString(),
+
+                       '$users' => $users,
+                       '$count' => $count,
+                       '$pager' => $pager->renderFull($count),
+               ]);
+       }
+
+       protected function setupUserCallback(): \Closure
+       {
+       Logger::debug("ratioed: setupUserCallback");
+               $parentCallback = parent::setupUserCallback();
+               return function ($user) use ($parentCallback) {
+            $blocked_count = DBA::count('user-contact', ['uid' => $user['uid'], 'is-blocked' => 1]);
+                       $user['blocked_by'] = $blocked_count;
+
+            $self_contact_result = DBA::p('SELECT admin_contact.id AS user_contact_uid FROM contact AS admin_contact JOIN contact AS user_contact ON admin_contact.`uri-id` = user_contact.`uri-id` AND admin_contact.self = 0 AND user_contact.self = 1 WHERE user_contact.uid = ?', $user['uid']);
+            if (DBA::isResult($self_contact_result)) {
+                $self_contact_result_row = DBA::fetch($self_contact_result);
+                $user['user_contact_uid'] = $self_contact_result_row['user_contact_uid'];
+            }
+            else {
+                $user['user_contact_uid'] = NULL;
+            }
+
+            if ($user['user_contact_uid']) {
+                $post_engagement_result = DBA::p('SELECT SUM(`comments`) AS `comment_count`, SUM(`activities`) AS `activities_count` FROM `post-engagement` WHERE `post-engagement`.created > DATE_SUB(now(), INTERVAL 1 DAY) AND `post-engagement`.`owner-id` = ?', $user['user_contact_uid']);
+                if (DBA::isResult($post_engagement_result)) {
+                    $post_engagement_result_row = DBA::fetch($post_engagement_result);
+                    $user['comments'] = $post_engagement_result_row['comment_count'];
+                    $user['reactions'] = $post_engagement_result_row['activities_count'];
+                    if ($user['reactions'] > 0) {
+                        $user['ratio'] = number_format($user['comments'] / $user['reactions'], 1, '.', '');
+                        $user['ratioed'] = (float)($user['ratio']) >= 2.0;
+                    }
+                    else {
+                        if ($user['comments'] == 0) {
+                            $user['ratio'] = '0';
+                            $user['ratioed'] = false;
+                        }
+                        else {
+                            $user['ratio'] = '∞';
+                            $user['ratioed'] = false;
+                        }
+                    }
+                }
+                else {
+                    $user['comments'] = 'error';
+                    $user['reactions'] = 'error';
+                    $user['ratio'] = 'error';
+                    $user['ratioed'] = false;
+                }
+            }
+            else {
+                $user['comments'] = 'error';
+                $user['reactions'] = 'error';
+                $user['ratio'] = 'error';
+                $user['ratioed'] = false;
+            }
+
+                       $user = $parentCallback($user);
+            Logger::debug("ratioed: setupUserCallback", [
+                'uid' => $user['uid'],
+                'blocked_by' => $user['blocked_by'],
+                'comments' => $user['comments'],
+                'reactions' => $user['reactions'],
+                'ratio' => $user['ratio'],
+                'ratioed' => $user['ratioed'],
+            ]);
+                       return $user;
+               };
+       }
+}
+
+/**
+ * @brief Displays the ratioed tab in the moderation panel
+ */
+function ratioed_content() {
+       Logger::debug("ratioed: content");
+
+       $ratioed = DI::getDice()->create(Ratioed::class, [$_SERVER]);
+       $httpException = DI::getDice()->create(Friendica\Module\Special\HTTPException::class);
+       $ratioed->run($httpException);
+}
diff --git a/ratioed/templates/ratioed.tpl b/ratioed/templates/ratioed.tpl
new file mode 100644 (file)
index 0000000..0d81b0d
--- /dev/null
@@ -0,0 +1,164 @@
+<script type="text/javascript" src="view/theme/frio/js/mod_admin.js?v={{constant('\Friendica\App::VERSION')}}"></script>
+<link rel="stylesheet" href="view/theme/frio/css/mod_admin.css?v={{constant('\Friendica\App::VERSION')}}" type="text/css" media="screen"/>
+
+<div id="admin-users" class="adminpage generic-page-wrapper">
+       <h1>{{$title}} - {{$page}} ({{$count}})</h1>
+       <p>
+               <a href="{{$base_url}}/moderation/users/create" class="btn btn-primary"><i class="fa fa-user-plus"></i> {{$h_newuser}}</a>
+       </p>
+       <form action="{{$baseurl}}/{{$query_string}}" method="post">
+               <input type="hidden" name="form_security_token" value="{{$form_security_token}}">
+               <table id="users" class="table table-hover">
+                       <thead>
+                               <tr>
+                                       <th>
+                                               <div class="checkbox">
+                                                       <input type="checkbox" id="admin-settings-users-select" class="selecttoggle" data-select-class="users_ckbx"/>
+                                                       <label for="admin-settings-users-select"></label>
+                                               </div>
+                                       </th>
+                                       <th></th>
+                                       {{foreach $th_users as $k=>$th}}
+                                               {{if $k < 2 || $order_users == $th.1 || ($k==5 && !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1])) }}
+                                               <th class="th-{{$k}}">
+                                                       <a href="{{$baseurl}}/moderation/users/active?o={{if $order_direction_users == "+"}}-{{/if}}{{$th.1}}" class="table-order">
+                                                               {{if $order_users == $th.1}}
+                                                                       {{if $order_direction_users == "+"}}
+                                                                       &#8595;
+                                                                       {{else}}
+                                                                       &#8593;
+                                                                       {{/if}}
+                                                               {{else}}
+                                                               &#8597;
+                                                               {{/if}}
+                                                               {{$th.0}}
+                                                       </a>
+                                               </th>
+                                               {{/if}}
+                                       {{/foreach}}
+                                       <th></th>
+                               </tr>
+                       </thead>
+                       <tbody>
+                       {{foreach $users as $u}}
+                               <tr id="user-{{$u.uid}}" class="{{if $u.ratioed}}blocked{{/if}}">
+                                       <td>
+                                               {{if $u.is_deletable}}
+                                               <div class="checkbox">
+                                                       <input type="checkbox" class="users_ckbx" id="id_user_{{$u.uid}}" name="user[]" value="{{$u.uid}}"/>
+                                                       <label for="id_user_{{$u.uid}}"></label>
+                                               </div>
+                                               {{else}}
+                                               &nbsp;
+                                               {{/if}}
+                                       </td>
+                                       <td><img class="avatar-nano" src="{{$u.micro}}" title="{{$u.nickname}}"></td>
+                                       <td><a href="{{$u.url}}" title="{{$u.nickname}}"> {{$u.name}}</a></td>
+                                       <td>{{$u.email}}</td>
+                               {{if $order_users == $th_users.2.1}}
+                                       <td>{{$u.register_date}}</td>
+                               {{/if}}
+
+                               {{if $order_users == $th_users.3.1}}
+                                       <td>{{$u.login_date}}</td>
+                               {{/if}}
+
+                               {{if $order_users == $th_users.4.1}}
+                                       <td>{{$u.lastitem_date}}</td>
+                               {{/if}}
+
+                               {{if !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }}
+                                       <td>
+                                               <i class="fa
+                                                       {{if $u.page_flags_raw==0}}fa-user{{/if}}               {{* PAGE_NORMAL *}}
+                                                       {{if $u.page_flags_raw==1}}fa-bullhorn{{/if}}           {{* PAGE_SOAPBOX *}}
+                                                       {{if $u.page_flags_raw==2}}fa-users{{/if}}              {{* PAGE_COMMUNITY *}}
+                                                       {{if $u.page_flags_raw==3}}fa-heart{{/if}}              {{* PAGE_FREELOVE *}}
+                                                       {{if $u.page_flags_raw==4}}fa-rss{{/if}}                {{* PAGE_BLOG *}}
+                                                       {{if $u.page_flags_raw==5}}fa-user-secret{{/if}}        {{* PAGE_PRVGROUP *}}
+                                                       " title="{{$u.page_flags}}">
+                                               </i>
+                                               {{if $u.page_flags_raw==0 && $u.account_type_raw > 0}}
+                                               <i class="fa
+                                                       {{if $u.account_type_raw==1}}fa-sitemap{{/if}}          {{* ACCOUNT_TYPE_ORGANISATION *}}
+                                                       {{if $u.account_type_raw==2}}fa-newspaper-o{{/if}}      {{* ACCOUNT_TYPE_NEWS *}}
+                                                       {{if $u.account_type_raw==3}}fa-comments{{/if}}         {{* ACCOUNT_TYPE_COMMUNITY *}}
+                                                       " title="{{$u.account_type}}">
+                                               </i>
+                                               {{/if}}
+                                               {{if $u.is_admin}}<i class="fa fa-user-secret text-primary" title="{{$siteadmin}}"></i>{{/if}}
+                                               {{if $u.account_expired}}<i class="fa fa-clock-o text-warning" title="{{$accountexpired}}"></i>{{/if}}
+                                       </td>
+                               {{/if}}
+
+                                       <td class="text-right">
+                                               <button type="button" class="btn-link admin-settings-action-link" onclick="return details({{$u.uid}})"><span class="caret"></span></button>
+                                       </td>
+                               </tr>
+                               <tr id="user-{{$u.uid}}-detail" class=" details hidden {{if $u.blocked != 0}}blocked{{/if}}">
+                                       <td>&nbsp;</td>
+                                       <td colspan="4">
+                                       {{if $order_users != $th_users.2.1}}
+                                               <p>
+                                                       <a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.2.1}}" class="btn-link table-order">
+                                                       &#8597; {{$th_users.2.0}}</a> : {{$u.register_date}}
+                                               </p>
+                                       {{/if}}
+
+                                       {{if $order_users != $th_users.3.1}}
+                                               <p>
+                                                       <a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.3.1}}" class="btn-link table-order">
+                                                               &#8597; {{$th_users.3.0}}</a> : {{$u.login_date}}
+                                               </p>
+                                       {{/if}}
+
+                                       {{if $order_users != $th_users.4.1}}
+                                               <p>
+                                                       <a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.4.1}}" class="btn-link table-order">
+                                                               &#8597; {{$th_users.4.0}}</a> : {{$u.lastitem_date}}
+                                               </p>
+                                       {{/if}}
+
+                                       {{if in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }}
+                                               <p>
+                                                       <a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.5.1}}" class="btn-link table-order">
+                                                               &#8597; {{$th_users.5.0}}</a> : {{$u.page_flags}}{{if $u.page_flags_raw==0 && $u.account_type_raw > 0}}, {{$u.account_type}}{{/if}} {{if $u.is_admin}}({{$siteadmin}}){{/if}} {{if $u.account_expired}}({{$accountexpired}}){{/if}}
+                                               </p>
+                                       {{/if}}
+
+                                       {{foreach $th_users as $k=>$th}}
+                                       {{if $order_users != $th.1 && $k > 5}}
+                                               <p>
+                                                       {{$th.0}} : {{$u[$th.2]}}
+                                               </p>
+                                       {{/if}}
+                                       {{/foreach}}
+
+                                       </td>
+                                       <td class="text-right">
+                               {{if $u.is_deletable}}
+                                               <a href="{{$baseurl}}/moderation/users/active/block/{{$u.uid}}?t={{$form_security_token}}" class="admin-settings-action-link" title="{{$block}}">
+                                                       <i class="fa fa-ban" aria-hidden="true"></i>
+                                               </a>
+                                               <a href="{{$baseurl}}/moderation/users/active/delete/{{$u.uid}}?t={{$form_security_token}}" class="admin-settings-action-link" title="{{$delete}}" onclick="return confirm_delete('{{$confirm_delete}}','{{$u.name}}')">
+                                                       <i class="fa fa-trash" aria-hidden="true"></i>
+                                               </a>
+                               {{else}}
+                                               &nbsp;
+                               {{/if}}
+                                       </td>
+                               </tr>
+                       {{/foreach}}
+                       </tbody>
+               </table>
+               <div class="panel-footer">
+                       <button type="submit" name="page_users_block" value="1" class="btn btn-warning">
+                               <i class="fa fa-ban" aria-hidden="true"></i> {{$block}}
+                       </button>
+                       <button type="submit" name="page_users_delete" value="1" class="btn btn-danger" onclick="return confirm_delete('{{$confirm_delete_multi}}')">
+                               <i class="fa fa-trash" aria-hidden="true"></i> {{$delete}}
+                       </button>
+               </div>
+               {{$pager nofilter}}
+       </form>
+</div>