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