]> git.mxchange.org Git - friendica.git/blob - src/Module/Admin/Federation.php
Merge pull request #12416 from MrPetovan/task/remove-clear-body-attach
[friendica.git] / src / Module / Admin / Federation.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2022, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Module\Admin;
23
24 use Friendica\App;
25 use Friendica\Core\Protocol;
26 use Friendica\Core\Renderer;
27 use Friendica\Database\DBA;
28 use Friendica\DI;
29 use Friendica\Model\GServer;
30 use Friendica\Module\BaseAdmin;
31
32 class Federation extends BaseAdmin
33 {
34         protected function content(array $request = []): string
35         {
36                 parent::content();
37
38                 // get counts on active federation systems this node is knowing
39                 // We list the more common systems by name. The rest is counted as "other"
40                 $systems = [
41                         'friendica'    => ['name' => 'Friendica', 'color' => '#ffc018'], // orange from the logo
42                         'akkoma'       => ['name' => 'Akkoma', 'color' => '#9574cd'], // Color from the page
43                         'birdsitelive' => ['name' => 'BirdsiteLIVE', 'color' => '#1b6ec2'], // Color from the page
44                         'bookwyrm'     => ['name' => 'BookWyrm', 'color' => '#00d1b2'], // Color from the page
45                         'castopod'     => ['name' => 'Castopod', 'color' => '#00564a'], // Background color from the page
46                         'diaspora'     => ['name' => 'Diaspora', 'color' => '#a1a1a1'], // logo is black and white, makes a gray
47                         'funkwhale'    => ['name' => 'Funkwhale', 'color' => '#4082B4'], // From the homepage
48                         'gnusocial'    => ['name' => 'GNU Social/Statusnet', 'color' => '#a22430'], // dark red from the logo
49                         'gotosocial'   => ['name' => 'GoToSocial', 'color' => '#df8958'], // Some color from their mascot
50                         'hometown'     => ['name' => 'Hometown', 'color' => '#1f70c1'], // Color from the Patreon page
51                         'honk'         => ['name' => 'Honk', 'color' => '##0d0d0d'], // Background color from the page
52                         'hubzilla'     => ['name' => 'Hubzilla/Red Matrix', 'color' => '#43488a'], // blue from the logo
53                         'lemmy'        => ['name' => 'Lemmy', 'color' => '#00c853'], // Green from the page
54                         'mastodon'     => ['name' => 'Mastodon', 'color' => '#1a9df9'], // blue from the Mastodon logo
55                         'microblog'    => ['name' => 'Microblog', 'color' => '#fdb52b'], // Color from the page
56                         'misskey'      => ['name' => 'Misskey', 'color' => '#ccfefd'], // Font color of the homepage
57                         'mobilizon'    => ['name' => 'Mobilizon', 'color' => '#ffd599'], // Background color of parts of the homepage
58                         'nextcloud'    => ['name' => 'Nextcloud', 'color' => '#1cafff'], // Logo color
59                         'mistpark'     => ['name' => 'Nomad projects (Mistpark, Osada, Roadhouse, Zap)', 'color' => '#348a4a'], // Green like the Mistpark green
60                         'owncast'      => ['name' => 'Owncast', 'color' => '#007bff'], // Font color of the homepage
61                         'peertube'     => ['name' => 'Peertube', 'color' => '#ffad5c'], // One of the logo colors
62                         'pixelfed'     => ['name' => 'Pixelfed', 'color' => '#11da47'], // One of the logo colors
63                         'pleroma'      => ['name' => 'Pleroma', 'color' => '#E46F0F'], // Orange from the text that is used on Pleroma instances
64                         'plume'        => ['name' => 'Plume', 'color' => '#7765e3'], // From the homepage
65                         'relay'        => ['name' => 'ActivityPub Relay', 'color' => '#888888'], // Grey like the second color of the ActivityPub logo
66                         'socialhome'   => ['name' => 'SocialHome', 'color' => '#52056b'], // lilac from the Django Image used at the Socialhome homepage
67                         'wordpress'    => ['name' => 'WordPress', 'color' => '#016087'], // Background color of the homepage
68                         'write.as'     => ['name' => 'Write.as', 'color' => '#00ace3'], // Border color of the homepage
69                         'writefreely'  => ['name' => 'WriteFreely', 'color' => '#292929'], // Font color of the homepage
70                         'other'        => ['name' => DI::l10n()->t('Other'), 'color' => '#F1007E'], // ActivityPub main color
71                 ];
72
73                 $platforms = array_keys($systems);
74
75                 $counts = [];
76                 foreach ($platforms as $platform) {
77                         $counts[$platform] = [];
78                 }
79
80                 $total    = 0;
81                 $users    = 0;
82                 $month    = 0;
83                 $halfyear = 0;
84                 $posts    = 0;
85
86                 $gservers = DBA::p("SELECT COUNT(*) AS `total`, SUM(`registered-users`) AS `users`,
87                         SUM(IFNULL(`local-posts`, 0) + IFNULL(`local-comments`, 0)) AS `posts`,
88                         SUM(IFNULL(`active-month-users`, `active-week-users`)) AS `month`,
89                         SUM(IFNULL(`active-halfyear-users`, `active-week-users`)) AS `halfyear`, `platform`,
90                         ANY_VALUE(`network`) AS `network`, MAX(`version`) AS `version`
91                         FROM `gserver` WHERE NOT `failed` AND `platform` != ? AND `detection-method` != ? AND NOT `network` IN (?, ?) GROUP BY `platform`",
92                                 '', GServer::DETECT_MANUAL, Protocol::PHANTOM, Protocol::FEED);
93                 while ($gserver = DBA::fetch($gservers)) {
94                         $total    += $gserver['total'];
95                         $users    += $gserver['users'];
96                         $month    += $gserver['month'];
97                         $halfyear += $gserver['halfyear'];
98                         $posts    += $gserver['posts'];
99
100                         $versionCounts = [];
101                         $versions = DBA::p("SELECT COUNT(*) AS `total`, `version` FROM `gserver`
102                                 WHERE NOT `failed` AND `platform` = ? AND `detection-method` != ? AND NOT `network` IN (?, ?)
103                                 GROUP BY `version` ORDER BY `version`", $gserver['platform'], GServer::DETECT_MANUAL, Protocol::PHANTOM, Protocol::FEED);
104                         while ($version = DBA::fetch($versions)) {
105                                 $version['version'] = str_replace(["\n", "\r", "\t"], " ", $version['version']);
106
107                                 if (in_array($gserver['platform'], ['Red Matrix', 'redmatrix', 'red'])) {
108                                         $version['version'] = 'Red ' . $version['version'];
109                                 } elseif (in_array($gserver['platform'], ['osada', 'mistpark', 'roadhouse', 'zap', 'macgirvin', 'mkultra'])) {
110                                         $version['version'] = $gserver['platform'] . ' ' . $version['version'];
111                                 } elseif (in_array($gserver['platform'], ['activityrelay', 'pub-relay', 'selective-relay', 'aoderelay'])) {
112                                         $version['version'] = $gserver['platform'] . '-' . $version['version'];
113                                 }
114
115                                 $versionCounts[] = $version;
116                         }
117                         DBA::close($versions);
118
119                         $platform = $gserver['platform'] = strtolower($gserver['platform']);
120
121                         if ($platform == 'friendika') {
122                                 $platform = 'friendica';
123                         } elseif (in_array($platform, ['red matrix', 'redmatrix', 'red'])) {
124                                 $platform = 'hubzilla';
125                         } elseif (in_array($platform, ['osada', 'mistpark', 'roadhouse', 'zap', 'macgirvin', 'mkultra'])) {
126                                 $platform = 'mistpark';
127                         } elseif(stristr($platform, 'pleroma')) {
128                                 $platform = 'pleroma';
129                         } elseif(stristr($platform, 'statusnet')) {
130                                 $platform = 'gnusocial';
131                         } elseif(stristr($platform, 'wordpress')) {
132                                 $platform = 'wordpress';
133                         } elseif (in_array($platform, ['activityrelay', 'pub-relay', 'selective-relay', 'aoderelay'])) {
134                                 $platform = 'relay';
135                         } elseif (!in_array($platform, $platforms)) {
136                                 $platform = 'other';
137                         }
138
139                         if ($platform != $gserver['platform']) {
140                                 if ($platform == 'other') {
141                                         $versionCounts = $counts[$platform][1] ?? [];
142                                         $versionCounts[] = ['version' => $gserver['platform'] ?: DI::l10n()->t('unknown'), 'total' => $gserver['total']];
143                                         $gserver['version'] = '';
144                                 } else {
145                                         $versionCounts = array_merge($versionCounts, $counts[$platform][1] ?? []);
146                                 }
147
148                                 $gserver['platform']  = $platform;
149                                 $gserver['total']    += $counts[$platform][0]['total'] ?? 0;
150                                 $gserver['users']    += $counts[$platform][0]['users'] ?? 0;
151                                 $gserver['month']    += $counts[$platform][0]['month'] ?? 0;
152                                 $gserver['halfyear'] += $counts[$platform][0]['halfyear'] ?? 0;
153                                 $gserver['posts']    += $counts[$platform][0]['posts'] ?? 0;
154                         }
155
156                         if ($platform == 'friendica') {
157                                 $versionCounts = self::reformaFriendicaVersions($versionCounts);
158                         } elseif (in_array($platform, ['pleroma', 'akkoma'])) {
159                                 $versionCounts = self::reformaPleromaVersions($versionCounts);
160                         } elseif ($platform == 'diaspora') {
161                                 $versionCounts = self::reformaDiasporaVersions($versionCounts);
162                         } elseif ($platform == 'relay') {
163                                 $versionCounts = self::reformatRelayVersions($versionCounts);
164                         } elseif (in_array($platform, ['funkwhale', 'mastodon', 'mobilizon', 'misskey', 'gotosocial'])) {
165                                 $versionCounts = self::removeVersionSuffixes($versionCounts);
166                         }
167
168                         if (!in_array($platform, ['other', 'relay', 'mistpark'])) {
169                                 $versionCounts = self::sortVersion($versionCounts);
170                         } else {
171                                 ksort($versionCounts);
172                         }
173
174                         $gserver['platform']    = $systems[$platform]['name'];
175                         $gserver['totallbl']    = DI::l10n()->tt('%2$s total system'                   , '%2$s total systems'                     , $gserver['total'], number_format($gserver['total']));
176                         $gserver['monthlbl']    = DI::l10n()->tt('%2$s active user last month'         , '%2$s active users last month'           , $gserver['month'] ?? 0, number_format($gserver['month']));
177                         $gserver['halfyearlbl'] = DI::l10n()->tt('%2$s active user last six months'    , '%2$s active users last six months'      , $gserver['halfyear'] ?? 0, number_format($gserver['halfyear']));
178                         $gserver['userslbl']    = DI::l10n()->tt('%2$s registered user'                , '%2$s registered users'                  , $gserver['users'], number_format($gserver['users']));
179                         $gserver['postslbl']    = DI::l10n()->tt('%2$s locally created post or comment', '%2$s locally created posts and comments', $gserver['posts'], number_format($gserver['posts']));
180
181                         if (($gserver['users'] > 0) && ($gserver['posts'] > 0)) {
182                                 $gserver['postsuserlbl'] = DI::l10n()->tt('%2$s post per user', '%2$s posts per user', $gserver['posts'] / $gserver['users'], number_format($gserver['posts'] / $gserver['users'], 1));
183                         } else {
184                                 $gserver['postsuserlbl'] = '';
185                         }
186                         if (($gserver['users'] > 0) && ($gserver['total'] > 0)) {
187                                 $gserver['userssystemlbl'] = DI::l10n()->tt('%2$s user per system', '%2$s users per system', $gserver['users'] / $gserver['total'], number_format($gserver['users'] / $gserver['total'], 1));
188                         } else {
189                                 $gserver['userssystemlbl'] = '';
190                         }
191
192                         $counts[$platform] = [$gserver, $versionCounts, str_replace([' ', '%', '.'], '', $platform), $systems[$platform]['color']];
193                 }
194                 DBA::close($gservers);
195
196                 // some helpful text
197                 $intro = DI::l10n()->t('This page offers you some numbers to the known part of the federated social network your Friendica node is part of. These numbers are not complete but only reflect the part of the network your node is aware of.');
198
199                 // load the template, replace the macros and return the page content
200                 $t = Renderer::getMarkupTemplate('admin/federation.tpl');
201                 return Renderer::replaceMacros($t, [
202                         '$title' => DI::l10n()->t('Administration'),
203                         '$page' => DI::l10n()->t('Federation Statistics'),
204                         '$intro' => $intro,
205                         '$counts' => $counts,
206                         '$version' => App::VERSION,
207                         '$legendtext' => DI::l10n()->tt('Currently this node is aware of %2$s node (%3$s active users last month, %4$s active users last six months, %5$s registered users in total) from the following platforms:', 'Currently this node is aware of %2$s nodes (%3$s active users last month, %4$s active users last six months, %5$s registered users in total) from the following platforms:', $total, number_format($total), number_format($month), number_format($halfyear), number_format($users)),
208                 ]);
209         }
210
211         /**
212          * early friendica versions have the format x.x.xxxx where xxxx is the
213          * DB version stamp; those should be operated out and versions be combined
214          *
215          * @param array $versionCounts list of version numbers
216          * @return array with cleaned version numbers
217          */
218         private static function reformaFriendicaVersions(array $versionCounts)
219         {
220                 $newV = [];
221                 $newVv = [];
222                 foreach ($versionCounts as $vv) {
223                         $newVC = $vv['total'];
224                         $newVV = $vv['version'];
225                         $lastDot = strrpos($newVV, '.');
226                         $firstDash = strpos($newVV, '-');
227                         $len = strlen($newVV) - 1;
228                         if (($lastDot == $len - 4) && (!strrpos($newVV, '-rc') == $len - 3) && (!$firstDash == $len - 1)) {
229                                 $newVV = substr($newVV, 0, $lastDot);
230                         }
231                         if (isset($newV[$newVV])) {
232                                 $newV[$newVV] += $newVC;
233                         } else {
234                                 $newV[$newVV] = $newVC;
235                         }
236                 }
237                 foreach ($newV as $key => $value) {
238                         array_push($newVv, ['total' => $value, 'version' => $key]);
239                 }
240                 $versionCounts = $newVv;
241
242                 return $versionCounts;
243         }
244
245         /**
246          * in the DB the Diaspora versions have the format x.x.x.x-xx the last
247          * part (-xx) should be removed to clean up the versions from the "head
248          * commit" information and combined into a single entry for x.x.x.x
249          *
250          * @param array $versionCounts list of version numbers
251          * @return array with cleaned version numbers
252          */
253         private static function reformaDiasporaVersions(array $versionCounts)
254         {
255                 $newV = [];
256                 $newVv = [];
257                 foreach ($versionCounts as $vv) {
258                         $newVC = $vv['total'];
259                         $newVV = $vv['version'];
260                         $posDash = strpos($newVV, '-');
261                         if ($posDash) {
262                                 $newVV = substr($newVV, 0, $posDash);
263                         }
264                         if (isset($newV[$newVV])) {
265                                 $newV[$newVV] += $newVC;
266                         } else {
267                                 $newV[$newVV] = $newVC;
268                         }
269                 }
270                 foreach ($newV as $key => $value) {
271                         array_push($newVv, ['total' => $value, 'version' => $key]);
272                 }
273                 $versionCounts = $newVv;
274
275                 return $versionCounts;
276         }
277
278         /**
279          * Clean up Pleroma version numbers
280          *
281          * @param array $versionCounts list of version numbers
282          * @return array with cleaned version numbers
283          */
284         private static function reformaPleromaVersions(array $versionCounts)
285         {
286                 $compacted = [];
287                 foreach ($versionCounts as $key => $value) {
288                         $version = $versionCounts[$key]['version'];
289                         $parts = explode(' ', trim($version));
290                         do {
291                                 $part = array_pop($parts);
292                         } while (!empty($parts) && ((strlen($part) >= 40) || (strlen($part) <= 3)));
293                         // only take the x.x.x part of the version, not the "release" after the dash
294                         if (!empty($part) && strpos($part, '-')) {
295                                 $part = explode('-', $part)[0];
296                         }
297                         if (!empty($part)) {
298                                 if (empty($compacted[$part])) {
299                                         $compacted[$part] = $versionCounts[$key]['total'];
300                                 } else {
301                                         $compacted[$part] += $versionCounts[$key]['total'];
302                                 }
303                         }
304                 }
305
306                 $versionCounts = [];
307                 foreach ($compacted as $version => $pl_total) {
308                         $versionCounts[] = ['version' => $version, 'total' => $pl_total];
309                 }
310
311                 return $versionCounts;
312         }
313
314         /**
315          * Clean up version numbers
316          *
317          * @param array $versionCounts list of version numbers
318          * @return array with cleaned version numbers
319          */
320         private static function removeVersionSuffixes(array $versionCounts)
321         {
322                 $compacted = [];
323                 foreach ($versionCounts as $key => $value) {
324                         $version = $versionCounts[$key]['version'];
325
326                         foreach ([' ', '+', '-', '#', '_', '~'] as $delimiter) {
327                                 $parts = explode($delimiter, trim($version));
328                                 $version = array_shift($parts);
329                         }
330
331                         if (empty($compacted[$version])) {
332                                 $compacted[$version] = $versionCounts[$key]['total'];
333                         } else {
334                                 $compacted[$version] += $versionCounts[$key]['total'];
335                         }
336                 }
337
338                 $versionCounts = [];
339                 foreach ($compacted as $version => $pl_total) {
340                         $versionCounts[] = ['version' => $version, 'total' => $pl_total];
341                 }
342
343                 return $versionCounts;
344         }
345
346         /**
347          * Clean up relay version numbers
348          *
349          * @param array $versionCounts list of version numbers
350          * @return array with cleaned version numbers
351          */
352         private static function reformatRelayVersions(array $versionCounts)
353         {
354                 $compacted = [];
355                 foreach ($versionCounts as $key => $value) {
356                         $version = $versionCounts[$key]['version'];
357
358                         $parts = explode(' ', trim($version));
359                         $version = array_shift($parts);
360
361                         if (empty($compacted[$version])) {
362                                 $compacted[$version] = $versionCounts[$key]['total'];
363                         } else {
364                                 $compacted[$version] += $versionCounts[$key]['total'];
365                         }
366                 }
367
368                 $versionCounts = [];
369                 foreach ($compacted as $version => $pl_total) {
370                         $versionCounts[] = ['version' => $version, 'total' => $pl_total];
371                 }
372
373                 return $versionCounts;
374         }
375
376         /**
377          * Reformat, sort and compact version numbers
378          *
379          * @param array $versionCounts list of version numbers
380          * @return array with reformatted version numbers
381          */
382         private static function sortVersion(array $versionCounts)
383         {
384                 //
385                 // clean up version numbers
386                 //
387                 // some platforms do not provide version information, add a unkown there
388                 // to the version string for the displayed list.
389                 foreach ($versionCounts as $key => $value) {
390                         if ($versionCounts[$key]['version'] == '') {
391                                 $versionCounts[$key] = ['total' => $versionCounts[$key]['total'], 'version' => DI::l10n()->t('unknown')];
392                         }
393                 }
394
395                 // Assure that the versions are sorted correctly
396                 $v2 = [];
397                 $versions = [];
398                 foreach ($versionCounts as $vv) {
399                         $version = trim(strip_tags($vv["version"]));
400                         $v2[$version] = $vv;
401                         $versions[] = $version;
402                 }
403
404                 usort($versions, 'version_compare');
405
406                 $versionCounts = [];
407                 foreach ($versions as $version) {
408                         $versionCounts[] = $v2[$version];
409                 }
410
411                 return $versionCounts;
412         }
413 }