mirror of
https://github.com/friendica/friendica
synced 2024-12-23 04:00:15 +00:00
Improved server detection
This commit is contained in:
parent
cc75eb5d18
commit
85c7bacb00
3 changed files with 87 additions and 48 deletions
|
@ -186,11 +186,12 @@ class GServer
|
||||||
return self::check($server, $network, $force);
|
return self::check($server, $network, $force);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getNextUpdateDate(bool $success, string $created = '', string $last_contact = '')
|
public static function getNextUpdateDate(bool $success, string $created = '', string $last_contact = '', bool $undetected = false)
|
||||||
{
|
{
|
||||||
// On successful contact process check again next week
|
// On successful contact process check again next week when it is a detected system.
|
||||||
|
// When we haven't detected the system, it could be a static website or a really old system.
|
||||||
if ($success) {
|
if ($success) {
|
||||||
return DateTimeFormat::utc('now +7 day');
|
return DateTimeFormat::utc($undetected ? 'now +1 month' : 'now +7 day');
|
||||||
}
|
}
|
||||||
|
|
||||||
$now = strtotime(DateTimeFormat::utcNow());
|
$now = strtotime(DateTimeFormat::utcNow());
|
||||||
|
@ -331,6 +332,11 @@ class GServer
|
||||||
|
|
||||||
// Remove URL content that is not supposed to exist for a server url
|
// Remove URL content that is not supposed to exist for a server url
|
||||||
$url = rtrim(self::cleanURL($url), '/');
|
$url = rtrim(self::cleanURL($url), '/');
|
||||||
|
if (empty($url)) {
|
||||||
|
Logger::notice('Empty URL.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!Network::isUrlValid($url)) {
|
if (!Network::isUrlValid($url)) {
|
||||||
self::setFailure($url);
|
self::setFailure($url);
|
||||||
return false;
|
return false;
|
||||||
|
@ -352,6 +358,11 @@ class GServer
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (empty($finalurl)) {
|
||||||
|
Logger::notice('Empty redirected URL.', ['url' => $url]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// We only follow redirects when the path stays the same or the target url has no path.
|
// We only follow redirects when the path stays the same or the target url has no path.
|
||||||
// Some systems have got redirects on their landing page to a single account page. This check handles it.
|
// Some systems have got redirects on their landing page to a single account page. This check handles it.
|
||||||
if (((parse_url($url, PHP_URL_HOST) != parse_url($finalurl, PHP_URL_HOST)) && (parse_url($url, PHP_URL_PATH) == parse_url($finalurl, PHP_URL_PATH))) ||
|
if (((parse_url($url, PHP_URL_HOST) != parse_url($finalurl, PHP_URL_HOST)) && (parse_url($url, PHP_URL_PATH) == parse_url($finalurl, PHP_URL_PATH))) ||
|
||||||
|
@ -367,10 +378,10 @@ class GServer
|
||||||
(parse_url($url, PHP_URL_SCHEME) != parse_url($finalurl, PHP_URL_SCHEME))) {
|
(parse_url($url, PHP_URL_SCHEME) != parse_url($finalurl, PHP_URL_SCHEME))) {
|
||||||
if (!Network::isUrlValid($finalurl)) {
|
if (!Network::isUrlValid($finalurl)) {
|
||||||
self::setFailure($finalurl);
|
self::setFailure($finalurl);
|
||||||
return false;
|
} else {
|
||||||
}
|
|
||||||
$url = $finalurl;
|
$url = $finalurl;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$in_webroot = empty(parse_url($url, PHP_URL_PATH));
|
$in_webroot = empty(parse_url($url, PHP_URL_PATH));
|
||||||
|
|
||||||
|
@ -410,11 +421,11 @@ class GServer
|
||||||
|
|
||||||
if ($curlResult->isSuccess()) {
|
if ($curlResult->isSuccess()) {
|
||||||
$json = json_decode($curlResult->getBody(), true);
|
$json = json_decode($curlResult->getBody(), true);
|
||||||
if (!empty($json)) {
|
if (!empty($json) && is_array($json)) {
|
||||||
$data = self::fetchDataFromSystemActor($json, $serverdata);
|
$data = self::fetchDataFromSystemActor($json, $serverdata);
|
||||||
$serverdata = $data['server'];
|
$serverdata = $data['server'];
|
||||||
$systemactor = $data['actor'];
|
$systemactor = $data['actor'];
|
||||||
if (!$html_fetched && ($serverdata['detection-method'] == self::DETECT_AP_ACTOR)) {
|
if (!$html_fetched && !in_array($serverdata['detection-method'], [self::DETECT_SYSTEM_ACTOR, self::DETECT_AP_COLLECTION])) {
|
||||||
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML);
|
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML);
|
||||||
}
|
}
|
||||||
} elseif (!$html_fetched && (strlen($curlResult->getBody()) < 1000)) {
|
} elseif (!$html_fetched && (strlen($curlResult->getBody()) < 1000)) {
|
||||||
|
@ -447,9 +458,8 @@ class GServer
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($validHostMeta) {
|
if ($validHostMeta) {
|
||||||
if ($serverdata['detection-method'] == self::DETECT_MANUAL) {
|
if (in_array($serverdata['detection-method'], [self::DETECT_MANUAL, self::DETECT_HEADER, self::DETECT_BODY])) {
|
||||||
$serverdata['detection-method'] = self::DETECT_HOST_META;
|
$serverdata['detection-method'] = self::DETECT_HOST_META;
|
||||||
$serverdata['platform'] = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($serverdata['network'] == Protocol::PHANTOM) || in_array($serverdata['detection-method'], self::DETECT_UNSPECIFIC)) {
|
if (($serverdata['network'] == Protocol::PHANTOM) || in_array($serverdata['detection-method'], self::DETECT_UNSPECIFIC)) {
|
||||||
|
@ -476,6 +486,8 @@ class GServer
|
||||||
$serverdata = self::detectGNUSocial($url, $serverdata);
|
$serverdata = self::detectGNUSocial($url, $serverdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} elseif (in_array($serverdata['platform'], ['friendica', 'friendika']) && in_array($serverdata['detection-method'], self::DETECT_UNSPECIFIC)) {
|
||||||
|
$serverdata = self::detectFriendica($url, $serverdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($serverdata['network'] == Protocol::PHANTOM) || in_array($serverdata['detection-method'], self::DETECT_UNSPECIFIC)) {
|
if (($serverdata['network'] == Protocol::PHANTOM) || in_array($serverdata['detection-method'], self::DETECT_UNSPECIFIC)) {
|
||||||
|
@ -507,7 +519,8 @@ class GServer
|
||||||
// When a server is new, then there is no gserver entry yet.
|
// When a server is new, then there is no gserver entry yet.
|
||||||
// But in "detectNetworkViaContacts" it could happen that a contact is updated,
|
// But in "detectNetworkViaContacts" it could happen that a contact is updated,
|
||||||
// and this can call this function here as well.
|
// and this can call this function here as well.
|
||||||
if (in_array($serverdata['network'], [Protocol::PHANTOM, Protocol::FEED]) && self::getID($url, true)) {
|
if (self::getID($url, true) && (in_array($serverdata['network'], [Protocol::PHANTOM, Protocol::FEED]) ||
|
||||||
|
in_array($serverdata['detection-method'], [self::DETECT_MANUAL, self::DETECT_HEADER, self::DETECT_BODY, self::DETECT_HOST_META]))) {
|
||||||
$serverdata = self::detectNetworkViaContacts($url, $serverdata);
|
$serverdata = self::detectNetworkViaContacts($url, $serverdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,7 +548,7 @@ class GServer
|
||||||
$serverdata['registered-users'] = 0;
|
$serverdata['registered-users'] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$serverdata['next_contact'] = self::getNextUpdateDate(true);
|
$serverdata['next_contact'] = self::getNextUpdateDate(true, '', '', in_array($serverdata['network'], [Protocol::PHANTOM, Protocol::FEED]));
|
||||||
|
|
||||||
$serverdata['last_contact'] = DateTimeFormat::utcNow();
|
$serverdata['last_contact'] = DateTimeFormat::utcNow();
|
||||||
$serverdata['failed'] = false;
|
$serverdata['failed'] = false;
|
||||||
|
@ -1222,7 +1235,7 @@ class GServer
|
||||||
return ['server' => $serverdata, 'actor' => ''];
|
return ['server' => $serverdata, 'actor' => ''];
|
||||||
}
|
}
|
||||||
|
|
||||||
$actor = JsonLD::compact($data);
|
$actor = JsonLD::compact($data, false);
|
||||||
if (in_array(JsonLD::fetchElement($actor, '@type'), ActivityPub\Receiver::ACCOUNT_TYPES)) {
|
if (in_array(JsonLD::fetchElement($actor, '@type'), ActivityPub\Receiver::ACCOUNT_TYPES)) {
|
||||||
$serverdata['network'] = Protocol::ACTIVITYPUB;
|
$serverdata['network'] = Protocol::ACTIVITYPUB;
|
||||||
$serverdata['site_name'] = JsonLD::fetchElement($actor, 'as:name', '@value');
|
$serverdata['site_name'] = JsonLD::fetchElement($actor, 'as:name', '@value');
|
||||||
|
@ -1842,16 +1855,17 @@ class GServer
|
||||||
}
|
}
|
||||||
|
|
||||||
$platforms = array_merge($ap_platforms, $dfrn_platforms, $zap_platforms, $platforms);
|
$platforms = array_merge($ap_platforms, $dfrn_platforms, $zap_platforms, $platforms);
|
||||||
$valid_platforms = array_values($platforms);
|
|
||||||
|
|
||||||
$doc = new DOMDocument();
|
$doc = new DOMDocument();
|
||||||
@$doc->loadHTML($curlResult->getBody());
|
@$doc->loadHTML($curlResult->getBody());
|
||||||
$xpath = new DOMXPath($doc);
|
$xpath = new DOMXPath($doc);
|
||||||
|
$assigned = false;
|
||||||
|
|
||||||
// We can only detect honk via some HTML element on their page
|
// We can only detect honk via some HTML element on their page
|
||||||
if ($xpath->query('//div[@id="honksonpage"]')->count() == 1) {
|
if ($xpath->query('//div[@id="honksonpage"]')->count() == 1) {
|
||||||
$serverdata['platform'] = 'honk';
|
$serverdata['platform'] = 'honk';
|
||||||
$serverdata['network'] = Protocol::ACTIVITYPUB;
|
$serverdata['network'] = Protocol::ACTIVITYPUB;
|
||||||
|
$assigned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$title = trim(XML::getFirstNodeValue($xpath, '//head/title/text()'));
|
$title = trim(XML::getFirstNodeValue($xpath, '//head/title/text()'));
|
||||||
|
@ -1884,27 +1898,23 @@ class GServer
|
||||||
|
|
||||||
if (in_array($attr['name'], ['application-name', 'al:android:app_name', 'al:ios:app_name',
|
if (in_array($attr['name'], ['application-name', 'al:android:app_name', 'al:ios:app_name',
|
||||||
'twitter:app:name:googleplay', 'twitter:app:name:iphone', 'twitter:app:name:ipad', 'generator'])) {
|
'twitter:app:name:googleplay', 'twitter:app:name:iphone', 'twitter:app:name:ipad', 'generator'])) {
|
||||||
$platform = str_replace(array_keys($platforms), array_values($platforms), $attr['content']);
|
$platform = str_ireplace(array_keys($platforms), array_values($platforms), $attr['content']);
|
||||||
$platform = strtolower(str_replace('/', ' ', $platform));
|
$platform = str_replace('/', ' ', $platform);
|
||||||
$version_part = explode(' ', $platform);
|
$platform_parts = explode(' ', $platform);
|
||||||
|
if ((count($platform_parts) >= 2) && in_array(strtolower($platform_parts[0]), array_values($platforms))) {
|
||||||
if (count($version_part) >= 2) {
|
$platform = $platform_parts[0];
|
||||||
if (in_array($version_part[0], array_values($dfrn_platforms))) {
|
$serverdata['version'] = $platform_parts[1];
|
||||||
|
}
|
||||||
|
if (in_array($platform, array_values($dfrn_platforms))) {
|
||||||
$serverdata['network'] = Protocol::DFRN;
|
$serverdata['network'] = Protocol::DFRN;
|
||||||
} elseif (in_array($version_part[0], array_values($ap_platforms))) {
|
} elseif (in_array($platform, array_values($ap_platforms))) {
|
||||||
$serverdata['network'] = Protocol::ACTIVITYPUB;
|
$serverdata['network'] = Protocol::ACTIVITYPUB;
|
||||||
} elseif (in_array($version_part[0], array_values($zap_platforms))) {
|
} elseif (in_array($platform, array_values($zap_platforms))) {
|
||||||
$serverdata['network'] = Protocol::ZOT;
|
$serverdata['network'] = Protocol::ZOT;
|
||||||
}
|
}
|
||||||
if (in_array(strtolower($version_part[0]), $valid_platforms)) {
|
|
||||||
$platform = strtolower($version_part[0]);
|
|
||||||
$serverdata['version'] = $version_part[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (in_array($platform, array_values($platforms))) {
|
if (in_array($platform, array_values($platforms))) {
|
||||||
$serverdata['platform'] = $platform;
|
$serverdata['platform'] = $platform;
|
||||||
} elseif (empty($serverdata['platform'])) {
|
$assigned = true;
|
||||||
print_r($attr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1939,8 +1949,7 @@ class GServer
|
||||||
if (in_array($attr['property'], ['og:platform', 'generator'])) {
|
if (in_array($attr['property'], ['og:platform', 'generator'])) {
|
||||||
if (in_array($attr['content'], array_keys($platforms))) {
|
if (in_array($attr['content'], array_keys($platforms))) {
|
||||||
$serverdata['platform'] = $platforms[$attr['content']];
|
$serverdata['platform'] = $platforms[$attr['content']];
|
||||||
} else {
|
$assigned = true;
|
||||||
print_r($attr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($attr['content'], array_keys($ap_platforms))) {
|
if (in_array($attr['content'], array_keys($ap_platforms))) {
|
||||||
|
@ -1951,7 +1960,33 @@ class GServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($serverdata['platform'], $valid_platforms) && ($serverdata['detection-method'] == self::DETECT_MANUAL)) {
|
$list = $xpath->query('//link[@rel="me"]');
|
||||||
|
foreach ($list as $node) {
|
||||||
|
foreach ($node->attributes as $attribute) {
|
||||||
|
if (parse_url(trim($attribute->value), PHP_URL_HOST) == 'micro.blog') {
|
||||||
|
$serverdata['version'] = trim($serverdata['platform'] . ' ' . $serverdata['version']);
|
||||||
|
$serverdata['platform'] = 'microblog';
|
||||||
|
$serverdata['network'] = Protocol::ACTIVITYPUB;
|
||||||
|
$assigned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($serverdata['platform'] != 'microblog') {
|
||||||
|
$list = $xpath->query('//link[@rel="micropub"]');
|
||||||
|
foreach ($list as $node) {
|
||||||
|
foreach ($node->attributes as $attribute) {
|
||||||
|
if (trim($attribute->value) == 'https://micro.blog/micropub') {
|
||||||
|
$serverdata['version'] = trim($serverdata['platform'] . ' ' . $serverdata['version']);
|
||||||
|
$serverdata['platform'] = 'microblog';
|
||||||
|
$serverdata['network'] = Protocol::ACTIVITYPUB;
|
||||||
|
$assigned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($assigned && in_array($serverdata['detection-method'], [self::DETECT_MANUAL, self::DETECT_HEADER])) {
|
||||||
$serverdata['detection-method'] = self::DETECT_BODY;
|
$serverdata['detection-method'] = self::DETECT_BODY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,10 +46,11 @@ class Federation extends BaseAdmin
|
||||||
'gnusocial' => ['name' => 'GNU Social/Statusnet', 'color' => '#a22430'], // dark red from the logo
|
'gnusocial' => ['name' => 'GNU Social/Statusnet', 'color' => '#a22430'], // dark red from the logo
|
||||||
'gotosocial' => ['name' => 'GoToSocial', 'color' => '#df8958'], // Some color from their mascot
|
'gotosocial' => ['name' => 'GoToSocial', 'color' => '#df8958'], // Some color from their mascot
|
||||||
'hometown' => ['name' => 'Hometown', 'color' => '#1f70c1'], // Color from the Patreon page
|
'hometown' => ['name' => 'Hometown', 'color' => '#1f70c1'], // Color from the Patreon page
|
||||||
|
'honk' => ['name' => 'Honk', 'color' => '##0d0d0d'], // Background color from the page
|
||||||
'hubzilla' => ['name' => 'Hubzilla/Red Matrix', 'color' => '#43488a'], // blue from the logo
|
'hubzilla' => ['name' => 'Hubzilla/Red Matrix', 'color' => '#43488a'], // blue from the logo
|
||||||
'hugo' => ['name' => 'Hugo', 'color' => '#0a1922'], // Color from the homepage background
|
|
||||||
'lemmy' => ['name' => 'Lemmy', 'color' => '#00c853'], // Green from the page
|
'lemmy' => ['name' => 'Lemmy', 'color' => '#00c853'], // Green from the page
|
||||||
'mastodon' => ['name' => 'Mastodon', 'color' => '#1a9df9'], // blue from the Mastodon logo
|
'mastodon' => ['name' => 'Mastodon', 'color' => '#1a9df9'], // blue from the Mastodon logo
|
||||||
|
'microblog' => ['name' => 'Microblog', 'color' => '#fdb52b'], // Color from the page
|
||||||
'misskey' => ['name' => 'Misskey', 'color' => '#ccfefd'], // Font color of the homepage
|
'misskey' => ['name' => 'Misskey', 'color' => '#ccfefd'], // Font color of the homepage
|
||||||
'mobilizon' => ['name' => 'Mobilizon', 'color' => '#ffd599'], // Background color of parts of the homepage
|
'mobilizon' => ['name' => 'Mobilizon', 'color' => '#ffd599'], // Background color of parts of the homepage
|
||||||
'nextcloud' => ['name' => 'Nextcloud', 'color' => '#1cafff'], // Logo color
|
'nextcloud' => ['name' => 'Nextcloud', 'color' => '#1cafff'], // Logo color
|
||||||
|
@ -85,7 +86,8 @@ class Federation extends BaseAdmin
|
||||||
SUM(IFNULL(`active-month-users`, `active-week-users`)) AS `month`,
|
SUM(IFNULL(`active-month-users`, `active-week-users`)) AS `month`,
|
||||||
SUM(IFNULL(`active-halfyear-users`, `active-week-users`)) AS `halfyear`, `platform`,
|
SUM(IFNULL(`active-halfyear-users`, `active-week-users`)) AS `halfyear`, `platform`,
|
||||||
ANY_VALUE(`network`) AS `network`, MAX(`version`) AS `version`
|
ANY_VALUE(`network`) AS `network`, MAX(`version`) AS `version`
|
||||||
FROM `gserver` WHERE NOT `failed` AND `detection-method` != ? AND NOT `network` IN (?, ?) GROUP BY `platform`", GServer::DETECT_MANUAL, Protocol::PHANTOM, Protocol::FEED);
|
FROM `gserver` WHERE NOT `failed` AND `platform` != ? AND `detection-method` != ? AND NOT `network` IN (?, ?) GROUP BY `platform`",
|
||||||
|
'', GServer::DETECT_MANUAL, Protocol::PHANTOM, Protocol::FEED);
|
||||||
while ($gserver = DBA::fetch($gservers)) {
|
while ($gserver = DBA::fetch($gservers)) {
|
||||||
$total += $gserver['total'];
|
$total += $gserver['total'];
|
||||||
$users += $gserver['users'];
|
$users += $gserver['users'];
|
||||||
|
@ -102,7 +104,7 @@ class Federation extends BaseAdmin
|
||||||
|
|
||||||
if (in_array($gserver['platform'], ['Red Matrix', 'redmatrix', 'red'])) {
|
if (in_array($gserver['platform'], ['Red Matrix', 'redmatrix', 'red'])) {
|
||||||
$version['version'] = 'Red ' . $version['version'];
|
$version['version'] = 'Red ' . $version['version'];
|
||||||
} elseif (in_array($gserver['platform'], ['osada', 'mistpark', 'roadhouse', 'zap'])) {
|
} elseif (in_array($gserver['platform'], ['osada', 'mistpark', 'roadhouse', 'zap', 'macgirvin', 'mkultra'])) {
|
||||||
$version['version'] = $gserver['platform'] . ' ' . $version['version'];
|
$version['version'] = $gserver['platform'] . ' ' . $version['version'];
|
||||||
} elseif (in_array($gserver['platform'], ['activityrelay', 'pub-relay', 'selective-relay', 'aoderelay'])) {
|
} elseif (in_array($gserver['platform'], ['activityrelay', 'pub-relay', 'selective-relay', 'aoderelay'])) {
|
||||||
$version['version'] = $gserver['platform'] . '-' . $version['version'];
|
$version['version'] = $gserver['platform'] . '-' . $version['version'];
|
||||||
|
@ -118,7 +120,7 @@ class Federation extends BaseAdmin
|
||||||
$platform = 'friendica';
|
$platform = 'friendica';
|
||||||
} elseif (in_array($platform, ['red matrix', 'redmatrix', 'red'])) {
|
} elseif (in_array($platform, ['red matrix', 'redmatrix', 'red'])) {
|
||||||
$platform = 'hubzilla';
|
$platform = 'hubzilla';
|
||||||
} elseif (in_array($platform, ['mistpark', 'osada', 'roadhouse', 'zap'])) {
|
} elseif (in_array($platform, ['osada', 'mistpark', 'roadhouse', 'zap', 'macgirvin', 'mkultra'])) {
|
||||||
$platform = 'mistpark';
|
$platform = 'mistpark';
|
||||||
} elseif(stristr($platform, 'pleroma')) {
|
} elseif(stristr($platform, 'pleroma')) {
|
||||||
$platform = 'pleroma';
|
$platform = 'pleroma';
|
||||||
|
|
|
@ -25,7 +25,7 @@ $platforms = [
|
||||||
'BaseKit' => 'basekit',
|
'BaseKit' => 'basekit',
|
||||||
'BBEdit' => 'bbedit',
|
'BBEdit' => 'bbedit',
|
||||||
'Big Cartel' => 'big-cartel',
|
'Big Cartel' => 'big-cartel',
|
||||||
'blogger' => 'blogger',
|
'Blogger' => 'blogger',
|
||||||
'Bloom' => 'bloom',
|
'Bloom' => 'bloom',
|
||||||
'Bludit' => 'bludit',
|
'Bludit' => 'bludit',
|
||||||
'BunnyPress' => 'bunnypress',
|
'BunnyPress' => 'bunnypress',
|
||||||
|
@ -48,25 +48,31 @@ $platforms = [
|
||||||
'filerun' => 'filerun',
|
'filerun' => 'filerun',
|
||||||
'FlatPress' => 'flatpress',
|
'FlatPress' => 'flatpress',
|
||||||
'Gatsby' => 'gatsby',
|
'Gatsby' => 'gatsby',
|
||||||
|
'Ghost' => 'ghost',
|
||||||
'gitweb' => 'gitweb',
|
'gitweb' => 'gitweb',
|
||||||
'gnusocial' => 'gnusocial',
|
'gnusocial' => 'gnusocial',
|
||||||
|
'Government Site Builder' => 'government-site-builder',
|
||||||
'GravCMS' => 'gravcms',
|
'GravCMS' => 'gravcms',
|
||||||
'grocy' => 'grocy',
|
'grocy' => 'grocy',
|
||||||
'Gruta' => 'gruta',
|
'Gruta' => 'gruta',
|
||||||
'hakyll' => 'hakyll',
|
'hakyll' => 'hakyll',
|
||||||
'HedgeDoc - Collaborative markdown notes' => 'hedgedoc',
|
'HedgeDoc - Collaborative markdown notes' => 'hedgedoc',
|
||||||
'helloworld' => 'helloworld',
|
'Hello, world. https://github.com/mimecuvalo/helloworld' => 'helloworld',
|
||||||
'Hexo' => 'hexo',
|
'Hexo' => 'hexo',
|
||||||
'honk' => 'honk',
|
'Hugo' => 'hugo',
|
||||||
'ian' => 'ian',
|
'ian' => 'ian',
|
||||||
'InterRed' => 'interred',
|
'InterRed' => 'interred',
|
||||||
'Ikiwiki' => 'ikiwiki',
|
'Ikiwiki' => 'ikiwiki',
|
||||||
|
'Jekyll' => 'jekyll',
|
||||||
'Joomla!' => 'joomla',
|
'Joomla!' => 'joomla',
|
||||||
'KeyHelp' => 'keyhelp',
|
'KeyHelp' => 'keyhelp',
|
||||||
|
'Known https://withknown.com' => 'known',
|
||||||
'KONTEXT-CMS (c) WARENFORM [www.warenform.net]' => 'kontext-cms',
|
'KONTEXT-CMS (c) WARENFORM [www.warenform.net]' => 'kontext-cms',
|
||||||
'ktistec' => 'ktistec',
|
'ktistec' => 'ktistec',
|
||||||
|
'lemoncurry' => 'lemoncurry',
|
||||||
'LibreOffice' => 'libreoffice',
|
'LibreOffice' => 'libreoffice',
|
||||||
'Magazine News Byte' => 'magazine-news-byte',
|
'Magazine News Byte' => 'magazine-news-byte',
|
||||||
|
'Magnet' => 'magnet',
|
||||||
'mastodon' => 'mastodon',
|
'mastodon' => 'mastodon',
|
||||||
'Mattermost' => 'mattermost',
|
'Mattermost' => 'mattermost',
|
||||||
'MediaWiki' => 'mediawiki',
|
'MediaWiki' => 'mediawiki',
|
||||||
|
@ -84,7 +90,6 @@ $platforms = [
|
||||||
'Org mode' => 'org-mode',
|
'Org mode' => 'org-mode',
|
||||||
'Org-mode' => 'org-mode',
|
'Org-mode' => 'org-mode',
|
||||||
'Org Mode' => 'org-mode',
|
'Org Mode' => 'org-mode',
|
||||||
'orig4' => 'orig4',
|
|
||||||
'Osclass' => 'osclass',
|
'Osclass' => 'osclass',
|
||||||
'pamphlets/vinyl-press' => 'pamphlets',
|
'pamphlets/vinyl-press' => 'pamphlets',
|
||||||
'peertube' => 'peertube',
|
'peertube' => 'peertube',
|
||||||
|
@ -100,6 +105,7 @@ $platforms = [
|
||||||
'Sedo' => 'sedo',
|
'Sedo' => 'sedo',
|
||||||
'sitebaker' => 'sitebaker',
|
'sitebaker' => 'sitebaker',
|
||||||
'SitePad' => 'sitepad',
|
'SitePad' => 'sitepad',
|
||||||
|
'SMAR' => 'smar',
|
||||||
'SPIP' => 'spip',
|
'SPIP' => 'spip',
|
||||||
'STUDIO' => 'studio',
|
'STUDIO' => 'studio',
|
||||||
'Synology - Synology DiskStation' => 'synology',
|
'Synology - Synology DiskStation' => 'synology',
|
||||||
|
@ -116,20 +122,16 @@ $platforms = [
|
||||||
'Webflow' => 'webflow',
|
'Webflow' => 'webflow',
|
||||||
'WikkaWiki' => 'wikkawiki',
|
'WikkaWiki' => 'wikkawiki',
|
||||||
'Wix.com' => 'wix.com',
|
'Wix.com' => 'wix.com',
|
||||||
'WordPress' => 'wordpress',
|
|
||||||
'WordPress.com' => 'wordpress',
|
'WordPress.com' => 'wordpress',
|
||||||
|
'WordPress' => 'wordpress',
|
||||||
'Write.as' => 'write.as',
|
'Write.as' => 'write.as',
|
||||||
'XAG/CMS' => 'xagcms',
|
'XAG/CMS' => 'xagcms',
|
||||||
'Zim' => 'zim',
|
'Zim' => 'zim',
|
||||||
];
|
];
|
||||||
|
|
||||||
$ap_platforms = [
|
$ap_platforms = [
|
||||||
|
'honk' => 'honk',
|
||||||
'PeerTube' => 'peertube',
|
'PeerTube' => 'peertube',
|
||||||
'Hugo' => 'hugo',
|
|
||||||
'lemoncurry' => 'lemoncurry',
|
|
||||||
'Ghost' => 'ghost',
|
|
||||||
'Jekyll' => 'jekyll',
|
|
||||||
'Known https://withknown.com' => 'known',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$dfrn_platforms = [
|
$dfrn_platforms = [
|
||||||
|
|
Loading…
Reference in a new issue