3 namespace Friendica\Module\Admin;
5 use Friendica\Core\Config;
6 use Friendica\Core\L10n;
7 use Friendica\Core\Renderer;
8 use Friendica\Database\DBA;
9 use Friendica\Module\BaseAdminModule;
11 class Federation extends BaseAdminModule
13 public static function content(array $parameters = [])
15 parent::content($parameters);
17 // get counts on active friendica, diaspora, redmatrix, hubzilla, gnu
18 // social and statusnet nodes this node is knowing
20 // We are looking for the following platforms in the DB, "Red" should find
21 // all variants of that platform ID string as the q() function is stripping
22 // off one % two of them are needed in the query
23 // Add more platforms if you like, when one returns 0 known nodes it is not
24 // displayed on the stats page.
26 'Friendica' => ['name' => 'Friendica', 'color' => '#ffc018'], // orange from the logo
27 'diaspora' => ['name' => 'Diaspora', 'color' => '#a1a1a1'], // logo is black and white, makes a gray
28 'red' => ['name' => 'Red Matrix', 'color' => '#c50001'], // fire red from the logo
29 'hubzilla' => ['name' => 'Hubzilla', 'color' => '#43488a'], // blue from the logo
30 'gnusocial' => ['name' => 'GNU Social', 'color' => '#a22430'], // dark red from the logo
31 'statusnet' => ['name' => 'StatusNet', 'color' => '#789240'], // the green from the logo (red and blue have already others
32 'mastodon' => ['name' => 'Mastodon', 'color' => '#1a9df9'], // blue from the Mastodon logo
33 'pleroma' => ['name' => 'Pleroma', 'color' => '#E46F0F'], // Orange from the text that is used on Pleroma instances
34 'socialhome' => ['name' => 'SocialHome', 'color' => '#52056b'], // lilac from the Django Image used at the Socialhome homepage
35 'wordpress' => ['name' => 'WordPress', 'color' => '#016087'], // Background color of the homepage
36 'misskey' => ['name' => 'Misskey', 'color' => '#ccfefd'], // Font color of the homepage
37 'funkwhale' => ['name' => 'Funkwhale', 'color' => '#4082B4'], // From the homepage
38 'plume' => ['name' => 'Plume', 'color' => '#7765e3'], // From the homepage
39 'pixelfed' => ['name' => 'Pixelfed', 'color' => '#11da47'], // One of the logo colors
40 'peertube' => ['name' => 'Peertube', 'color' => '#ffad5c'], // One of the logo colors
41 'writefreely' => ['name' => 'WriteFreely', 'color' => '#292929'], // Font color of the homepage
42 'other' => ['name' => L10n::t('Other'), 'color' => '#F1007E'], // ActivityPub main color
45 $platforms = array_keys($systems);
51 $gservers = DBA::p("SELECT COUNT(*) AS `total`, SUM(`registered-users`) AS `users`, `platform`,
52 ANY_VALUE(`network`) AS `network`, MAX(`version`) AS `version`
53 FROM `gserver` WHERE `last_contact` >= `last_failure` GROUP BY `platform`");
54 while ($gserver = DBA::fetch($gservers)) {
55 $total += $gserver['total'];
56 $users += $gserver['users'];
59 $versions = DBA::p("SELECT COUNT(*) AS `total`, `version` FROM `gserver`
60 WHERE `last_contact` >= `last_failure` AND `platform` = ?
61 GROUP BY `version` ORDER BY `version`", $gserver['platform']);
62 while ($version = DBA::fetch($versions)) {
63 $version['version'] = str_replace(["\n", "\r", "\t"], " ", $version['version']);
64 $versionCounts[] = $version;
66 DBA::close($versions);
68 $platform = $gserver['platform'];
70 if ($platform == 'Friendika') {
71 $platform = 'Friendica';
72 } elseif (in_array($platform, ['Red Matrix', 'redmatrix'])) {
74 } elseif(stristr($platform, 'pleroma')) {
75 $platform = 'pleroma';
76 } elseif(stristr($platform, 'wordpress')) {
77 $platform = 'wordpress';
78 } elseif (!in_array($platform, $platforms)) {
82 if ($platform != $gserver['platform']) {
83 if ($platform == 'other') {
84 $versionCounts = $counts[$platform][1] ?? [];
85 $versionCounts[] = ['version' => $gserver['platform'] ?: L10n::t('unknown'), 'total' => $gserver['total']];
86 $gserver['version'] = '';
88 $versionCounts = array_merge($versionCounts, $counts[$platform][1] ?? []);
91 $gserver['platform'] = $platform;
92 $gserver['total'] += $counts[$platform][0]['total'] ?? 0;
93 $gserver['users'] += $counts[$platform][0]['users'] ?? 0;
96 if ($platform == 'Friendica') {
97 $versionCounts = self::reformaFriendicaVersions($versionCounts);
98 } elseif ($platform == 'pleroma') {
99 $versionCounts = self::reformaPleromaVersions($versionCounts);
100 } elseif ($platform == 'diaspora') {
101 $versionCounts = self::reformaDiasporaVersions($versionCounts);
104 $versionCounts = self::sortVersion($versionCounts);
106 $gserver['platform'] = $systems[$platform]['name'];
108 $counts[$platform] = [$gserver, $versionCounts, str_replace([' ', '%'], '', $platform), $systems[$platform]['color']];
110 DBA::close($gserver);
113 $intro = 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.');
114 $hint = L10n::t('The <em>Auto Discovered Contact Directory</em> feature is not enabled, it will improve the data displayed here.');
116 // load the template, replace the macros and return the page content
117 $t = Renderer::getMarkupTemplate('admin/federation.tpl');
118 return Renderer::replaceMacros($t, [
119 '$title' => L10n::t('Administration'),
120 '$page' => L10n::t('Federation Statistics'),
123 '$autoactive' => Config::get('system', 'poco_completion'),
124 '$counts' => $counts,
125 '$version' => FRIENDICA_VERSION,
126 '$legendtext' => L10n::t('Currently this node is aware of %d nodes with %d registered users from the following platforms:', $total, $users),
130 // early friendica versions have the format x.x.xxxx where xxxx is the
131 // DB version stamp; those should be operated out and versions be
133 private static function reformaFriendicaVersions($versionCounts)
137 foreach ($versionCounts as $vv) {
138 $newVC = $vv['total'];
139 $newVV = $vv['version'];
140 $lastDot = strrpos($newVV, '.');
141 $len = strlen($newVV) - 1;
142 if (($lastDot == $len - 4) && (!strrpos($newVV, '-rc') == $len - 3)) {
143 $newVV = substr($newVV, 0, $lastDot);
145 if (isset($newV[$newVV])) {
146 $newV[$newVV] += $newVC;
148 $newV[$newVV] = $newVC;
151 foreach ($newV as $key => $value) {
152 array_push($newVv, ['total' => $value, 'version' => $key]);
154 $versionCounts = $newVv;
156 return $versionCounts;
159 // in the DB the Diaspora versions have the format x.x.x.x-xx the last
160 // part (-xx) should be removed to clean up the versions from the "head
161 // commit" information and combined into a single entry for x.x.x.x
162 private static function reformaDiasporaVersions($versionCounts)
166 foreach ($versionCounts as $vv) {
167 $newVC = $vv['total'];
168 $newVV = $vv['version'];
169 $posDash = strpos($newVV, '-');
171 $newVV = substr($newVV, 0, $posDash);
173 if (isset($newV[$newVV])) {
174 $newV[$newVV] += $newVC;
176 $newV[$newVV] = $newVC;
179 foreach ($newV as $key => $value) {
180 array_push($newVv, ['total' => $value, 'version' => $key]);
182 $versionCounts = $newVv;
184 return $versionCounts;
187 private static function reformaPleromaVersions($versionCounts)
190 foreach ($versionCounts as $key => $value) {
191 $version = $versionCounts[$key]['version'];
192 $parts = explode(' ', trim($version));
194 $part = array_pop($parts);
195 } while (!empty($parts) && ((strlen($part) >= 40) || (strlen($part) <= 3)));
196 // only take the x.x.x part of the version, not the "release" after the dash
197 if (!empty($part) && strpos($part, '-')) {
198 $part = explode('-', $part)[0];
201 if (empty($compacted[$part])) {
202 $compacted[$part] = $versionCounts[$key]['total'];
204 $compacted[$part] += $versionCounts[$key]['total'];
210 foreach ($compacted as $version => $pl_total) {
211 $versionCounts[] = ['version' => $version, 'total' => $pl_total];
214 return $versionCounts;
217 // Reformat and compact version numbers
218 private static function sortVersion($versionCounts)
221 // clean up version numbers
223 // some platforms do not provide version information, add a unkown there
224 // to the version string for the displayed list.
225 foreach ($versionCounts as $key => $value) {
226 if ($versionCounts[$key]['version'] == '') {
227 $versionCounts[$key] = ['total' => $versionCounts[$key]['total'], 'version' => L10n::t('unknown')];
231 // Assure that the versions are sorted correctly
234 foreach ($versionCounts as $vv) {
235 $version = trim(strip_tags($vv["version"]));
237 $versions[] = $version;
240 usort($versions, 'version_compare');
243 foreach ($versions as $version) {
244 $versionCounts[] = $v2[$version];
247 return $versionCounts;