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