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