Compare commits

..

No commits in common. "develop" and "develop" have entirely different histories.

115 changed files with 792 additions and 2343 deletions

View file

@ -4,9 +4,6 @@ pipeline:
clone_friendica_base: clone_friendica_base:
image: alpine/git image: alpine/git
commands: commands:
- git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica"
- git config --global --add safe.directory $CI_WORKSPACE
- git clone https://github.com/friendica/friendica.git . - git clone https://github.com/friendica/friendica.git .
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
when: when:

View file

@ -9,9 +9,6 @@ pipeline:
clone_friendica_base: clone_friendica_base:
image: alpine/git image: alpine/git
commands: commands:
- git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica"
- git config --global --add safe.directory $CI_WORKSPACE
- git clone https://github.com/friendica/friendica.git . - git clone https://github.com/friendica/friendica.git .
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
when: when:

View file

@ -4,9 +4,6 @@ pipeline:
clone_friendica_base: clone_friendica_base:
image: alpine/git image: alpine/git
commands: commands:
- git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica"
- git config --global --add safe.directory $CI_WORKSPACE
- git clone https://github.com/friendica/friendica.git . - git clone https://github.com/friendica/friendica.git .
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
when: when:

View file

@ -21,9 +21,6 @@ pipeline:
clone_friendica_base: clone_friendica_base:
image: alpine/git image: alpine/git
commands: commands:
- git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica"
- git config --global --add safe.directory $CI_WORKSPACE
- git clone https://github.com/friendica/friendica.git . - git clone https://github.com/friendica/friendica.git .
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
clone_friendica_addon: clone_friendica_addon:

View file

@ -9,9 +9,6 @@ pipeline:
clone_friendica_base: clone_friendica_base:
image: alpine/git image: alpine/git
commands: commands:
- git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica"
- git config --global --add safe.directory $CI_WORKSPACE
- git clone https://github.com/friendica/friendica.git . - git clone https://github.com/friendica/friendica.git .
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
when: when:

View file

@ -5,7 +5,7 @@
# #
# Translators: # Translators:
# fabrixxm <fabrix.xm@gmail.com>, 2018 # fabrixxm <fabrix.xm@gmail.com>, 2018
# Sylke Vicious <silkevicious@gmail.com>, 2023 # Sylke Vicious <silkevicious@gmail.com>, 2021
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
@ -14,7 +14,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-05-11 08:54-0400\n" "POT-Creation-Date: 2022-05-11 08:54-0400\n"
"PO-Revision-Date: 2018-05-24 06:41+0000\n" "PO-Revision-Date: 2018-05-24 06:41+0000\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2023\n" "Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2021\n"
"Language-Team: Italian (https://app.transifex.com/Friendica/teams/12172/it/)\n" "Language-Team: Italian (https://app.transifex.com/Friendica/teams/12172/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -125,7 +125,7 @@ msgstr "Annulla"
#: advancedcontentfilter.php:295 #: advancedcontentfilter.php:295
msgid "This addon requires this node having at least one post" msgid "This addon requires this node having at least one post"
msgstr "Questo addon richiede che questo nodo abbia almeno un messaggio" msgstr ""
#: advancedcontentfilter.php:325 advancedcontentfilter.php:336 #: advancedcontentfilter.php:325 advancedcontentfilter.php:336
#: advancedcontentfilter.php:347 advancedcontentfilter.php:383 #: advancedcontentfilter.php:347 advancedcontentfilter.php:383

View file

@ -27,7 +27,6 @@ $a->strings['Add new rule'] = 'Aggiungi nuova regola';
$a->strings['Rule Name'] = 'Nome Regola'; $a->strings['Rule Name'] = 'Nome Regola';
$a->strings['Rule Expression'] = 'Espressione Regola'; $a->strings['Rule Expression'] = 'Espressione Regola';
$a->strings['Cancel'] = 'Annulla'; $a->strings['Cancel'] = 'Annulla';
$a->strings['This addon requires this node having at least one post'] = 'Questo addon richiede che questo nodo abbia almeno un messaggio';
$a->strings['You must be logged in to use this method'] = 'Devi essere autenticato per usare questo metodo'; $a->strings['You must be logged in to use this method'] = 'Devi essere autenticato per usare questo metodo';
$a->strings['Invalid form security token, please refresh the page.'] = 'Token di sicurezza invalido, aggiorna la pagina.'; $a->strings['Invalid form security token, please refresh the page.'] = 'Token di sicurezza invalido, aggiorna la pagina.';
$a->strings['The rule name and expression are required.'] = 'Il nome e l\'espressione della regola sono richiesti.'; $a->strings['The rule name and expression are required.'] = 'Il nome e l\'espressione della regola sono richiesti.';

View file

@ -2,7 +2,7 @@
/** /**
* Name: blockbot * Name: blockbot
* Description: Blocking bots based on detecting bots/crawlers/spiders via the user agent and http_from header. * Description: Blocking bots based on detecting bots/crawlers/spiders via the user agent and http_from header.
* Version: 1.0 * Version: 0.2
* Author: Philipp Holzer <admin@philipp.info> * Author: Philipp Holzer <admin@philipp.info>
* Author: Michael Vogel <https://pirati.ca/profile/heluecht> * Author: Michael Vogel <https://pirati.ca/profile/heluecht>
* *
@ -13,10 +13,7 @@ use Friendica\DI;
use Jaybizzle\CrawlerDetect\CrawlerDetect; use Jaybizzle\CrawlerDetect\CrawlerDetect;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Core\System;
use Friendica\Network\HTTPException\ForbiddenException; use Friendica\Network\HTTPException\ForbiddenException;
use Friendica\Util\HTTPSignature;
use Friendica\Util\Network;
require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
@ -31,812 +28,160 @@ function blockbot_addon_admin(string &$o)
$o = Renderer::replaceMacros($t, [ $o = Renderer::replaceMacros($t, [
'$submit' => DI::l10n()->t('Save Settings'), '$submit' => DI::l10n()->t('Save Settings'),
'$security_checker' => ['security_checker', DI::l10n()->t('Allow security checkers'), DI::config()->get('blockbot', 'security_checker'), DI::l10n()->t("Don't block security checkers. They can be used for good or bad.")], '$good_crawlers' => ['good_crawlers', DI::l10n()->t('Allow "good" crawlers'), DI::config()->get('blockbot', 'good_crawlers'), DI::l10n()->t("Don't block fediverse crawlers, relay servers and other bots with good purposes.")],
'$socialmedia_agents' => ['socialmedia_agents', DI::l10n()->t('Allow preview agents'), DI::config()->get('blockbot', 'socialmedia_agents'), DI::l10n()->t("Don't block agents from social media systems that want to generate preview data for links that had been set by their users.")],
'$http_libraries' => ['http_libraries', DI::l10n()->t('Allow generic HTTP libraries'), DI::config()->get('blockbot', 'http_libraries'), DI::l10n()->t("Don't block agents from generic HTTP libraries that could be used for good or for bad and that currently can't be traced back to any known Fediverse project.")], '$http_libraries' => ['http_libraries', DI::l10n()->t('Allow generic HTTP libraries'), DI::config()->get('blockbot', 'http_libraries'), DI::l10n()->t("Don't block agents from generic HTTP libraries that could be used for good or for bad and that currently can't be traced back to any known Fediverse project.")],
'$block_gab' => ['block_gab', DI::l10n()->t('Block GabSocial'), DI::config()->get('blockbot', 'block_gab'), DI::l10n()->t('Block the software GabSocial. This will block every access for that software. You can block dedicated gab instances in the blocklist settings in the admin section.')],
'$training' => ['training', DI::l10n()->t('Training mode'), DI::config()->get('blockbot', 'training'), DI::l10n()->t("Activates the training mode. This is only meant for developing purposes. Don't activate this on a production machine. This can cut communication with some systems.")], '$training' => ['training', DI::l10n()->t('Training mode'), DI::config()->get('blockbot', 'training'), DI::l10n()->t("Activates the training mode. This is only meant for developing purposes. Don't activate this on a production machine. This can cut communication with some systems.")],
]); ]);
} }
function blockbot_addon_admin_post() function blockbot_addon_admin_post()
{ {
DI::config()->set('blockbot', 'security_checker', $_POST['security_checker'] ?? false); DI::config()->set('blockbot', 'good_crawlers', $_POST['good_crawlers'] ?? false);
DI::config()->set('blockbot', 'socialmedia_agents', $_POST['socialmedia_agents'] ?? false);
DI::config()->set('blockbot', 'http_libraries', $_POST['http_libraries'] ?? false); DI::config()->set('blockbot', 'http_libraries', $_POST['http_libraries'] ?? false);
DI::config()->set('blockbot', 'block_gab', $_POST['block_gab'] ?? false);
DI::config()->set('blockbot', 'training', $_POST['training'] ?? false); DI::config()->set('blockbot', 'training', $_POST['training'] ?? false);
} }
function blockbot_reject()
{
throw new ForbiddenException('Bots are not allowed. If you consider this a mistake, create an issue at https://github.com/friendica/friendica');
}
function blockbot_init_1() function blockbot_init_1()
{ {
if (empty($_SERVER['HTTP_USER_AGENT'])) { if (empty($_SERVER['HTTP_USER_AGENT'])) {
return; return;
} }
$crawlerDetect = new CrawlerDetect(); $logdata = ['agent' => $_SERVER['HTTP_USER_AGENT'], 'uri' => $_SERVER['REQUEST_URI']];
$isCrawler = $crawlerDetect->isCrawler(); // List of known unwanted crawlers.
$agents = [
'SemrushBot', 's~feedly-nikon3', 'Qwantify/Bleriot/', 'ltx71', 'Sogou web spider/',
'Diffbot/', 'YisouSpider', 'evc-batch/', 'LivelapBot/', 'TrendsmapResolver/',
'PaperLiBot/', 'Nuzzel', 'um-LN/', 'Google Favicon', 'Datanyze', 'BLEXBot/', '360Spider',
'adscanner/', 'HeadlessChrome', 'wpif', 'startmebot/', 'Googlebot/', 'Applebot/',
'GoogleImageProxy', 'bingbot/', 'heritrix/', 'ldspider',
'AwarioRssBot/', 'TweetmemeBot/', 'dcrawl/', 'PhantomJS/', 'Googlebot-Image/',
'CrowdTanglebot/', 'Mediapartners-Google', 'Baiduspider', 'datagnionbot',
'MegaIndex.ru/', 'SMUrlExpander', 'Hatena-Favicon/', 'Wappalyzer', 'FlipboardProxy/',
'NetcraftSurveyAgent/', 'Dataprovider.com', 'SMTBot/', 'Nimbostratus-Bot/',
'DuckDuckGo-Favicons-Bot/', 'IndieWebCards/', 'proximic', 'netEstate NE Crawler',
'AhrefsBot/', 'YandexBot/', 'Exabot/', 'Mediumbot-MetaTagFetcher/',
'SurdotlyBot/', 'BingPreview/', 'SabsimBot/', 'CCBot/', 'WbSrch/',
'DuckDuckBot-Https/', 'HTTP Banner Detection', 'YandexImages/', 'archive.org_bot',
'ArchiveTeam ArchiveBot/', 'yacybot', 'https://developers.google.com/+/web/snippet/',
'Scrapy/', 'MJ12bot/', 'DotBot/', 'Pinterestbot/', 'Jooblebot/',
'Cliqzbot/', 'YaK/', 'Mediatoolkitbot', 'Snacktory', 'FunWebProducts', 'oBot/',
'7Siters/', 'KOCMOHABT', 'Google-SearchByImage', 'FemtosearchBot/',
'HubSpot Crawler', 'DomainStatsBot/', 'Re-re Studio', 'AwarioSmartBot/',
'DNSResearchBot/', 'PetalBot;', 'Nmap Scripting Engine;',
'Google-Apps-Script; beanserver;', 'woorankreview/', 'Seekport Crawler;', 'AHC/',
'Semanticbot/', 'XoviOnpageCrawler;', 'Pinterest/',
'GetHPinfo.com-Bot/', 'BoardReader Favicon Fetcher', 'Google-Adwords-Instant', 'newspaper/',
'YurichevBot/', 'Crawling at Home Project', 'InfoTigerBot/', 'AdIdxBot/',
'MicrosoftPreview/', 'masscan/', 'Timpibot/', 'everyfeed-spider/', 'AndroidDownloadManager/',
'WebZIP/', 'WDG_Validator/', 'Screaming Frog SEO Spider/', ' Bytespider;', 'ISSCyberRiskCrawler/',
'BitSightBot/', 'ev-crawler/', 'CensysInspect/1.1', 'Protopage/', 'Gaisbot/', 'WellKnownBot/',
'SuperBot/', 'Googlebot-Mobile/', 'GPTBot/', 'GenomeCrawlerd/', '2ip bot/', 'Ocarinabot',
'Yahoo! Slurp;', 'AdsBot-Google', 'Gregarius/', 'FAST-WebCrawler/', 'Xenu Link Sleuth/',
'Ask Jeeves', 'alexa site audit/', 'Yahoo! Slurp China;', 'Microsoft URL Control',
'Facebot', 'Googlebot-Video/', 'msnbot/', 'Offline Explorer/', 'YandexNews/', 'msnbot-media/',
'EmailWolf', 'Download Demon/', 'FeedFetcher-Google;', 'WebCopier', '+ONB_Bot_Btrix',
'scoopit-crawler/', 'ia_archiver', 'Quora-Bot/', 'WebwikiBot/', 'FullStoryBot/',
'wpbot/', 'SearchExpress', 'DuckDuckBot/', 'Google Web Preview',
];
blockbot_save('all-agents', $_SERVER['HTTP_USER_AGENT']); if (DI::config()->get('blockbot', 'block_gab')) {
$agents[] = 'GabSocial/';
$parts = blockbot_get_parts($_SERVER['HTTP_USER_AGENT']);
$logdata = ['isCrawler' => $isCrawler, 'agent' => $_SERVER['HTTP_USER_AGENT'], 'method' => $_SERVER['REQUEST_METHOD'], 'uri' => $_SERVER['REQUEST_URI'], 'parts' => $parts];
if ($isCrawler) {
blockbot_check_login_attempt($_SERVER['REQUEST_URI'], $logdata);
} }
if (empty($parts)) { // List of "good" crawlers, mostly from the fediverse.
Logger::debug('Known frontend found - accept', $logdata); $good_agents = [
if ($isCrawler) { 'fediverse.space crawler', 'fediverse.network crawler', 'Active_Pods_CheckBot_3.0',
blockbot_save('badly-parsed-agents', $_SERVER['HTTP_USER_AGENT']); 'Social-Relay/', 'Test Certificate Info', 'Uptimebot/', 'GNUSocialBot', 'UptimeRobot/',
} 'PTST/', 'Zabbix', 'Poduptime/', 'FediFetcher', 'lemmy-stats-crawler',
'FedditLemmyverseCrawler/', 'lemmy-explorer-crawler/', 'URIports Validator',
'rss-is-dead.lol web bot;', 'fedistatsCrawler/', 'W3C_CSS_Validator_JFouffa/',
'IABot/', 'Slackbot 1', 'BeeperBot/', 'Matrix-Media-Repo/', 'P3P Validator',
'KeybaseBot;',
];
if (!DI::config()->get('blockbot', 'good_crawlers')) {
$agents = array_merge($agents, $good_agents);
} elseif (blockbot_match($good_agents)) {
return; return;
} }
blockbot_log_activitypub($_SERVER['REQUEST_URI'], $_SERVER['HTTP_USER_AGENT']); // List of agents from social media systems that fetch preview data via opem graph or twitter cards.
$socialmedia_agents = ['Twitterbot', 'facebookexternalhit/', 'SkypeUriPreview Preview/',
'TelegramBot', 'WhatsApp/', 'github-camo', 'Bluesky Cardyb/', 'XING-contenttabreceiver/',
'LinkedInBot/', 'Instagram ', 'Synapse (bot; ', 'Discordbot/', 'SummalyBot/',
'Slackbot-LinkExpanding', 'Slack-ImgProxy', 'Iframely/',
];
if (blockbot_is_crawler($parts)) { if (!DI::config()->get('blockbot', 'socialmedia_agents')) {
Logger::debug('Crawler found - reject', $logdata); $agents = array_merge($agents, $socialmedia_agents);
blockbot_reject(); } elseif (blockbot_match($socialmedia_agents)) {
}
if (blockbot_is_searchbot($parts)) {
Logger::debug('Search bot found - reject', $logdata);
blockbot_reject();
}
if (blockbot_is_unwanted($parts)) {
Logger::debug('Uncategorized unwanted agent found - reject', $logdata);
blockbot_reject();
}
if (blockbot_is_security_checker($parts)) {
if (!DI::config()->get('blockbot', 'security_checker')) {
Logger::debug('Security checker found - reject', $logdata);
blockbot_reject();
}
Logger::debug('Security checker found - accept', $logdata);
return; return;
} }
if (blockbot_is_social_media($parts)) { // HTTP Libraries
Logger::debug('Social media service found - accept', $logdata); $http_libraries = ['ReactorNetty/', 'GuzzleHttp/', 'Embed PHP library', 'python-urllib3/',
return; 'EventMachine HttpClient', 'HTMLParser/'
} ];
if (blockbot_is_fediverse_client($parts)) {
Logger::debug('Fediverse client found - accept', $logdata);
return;
}
if (blockbot_is_feed_reader($parts)) {
Logger::debug('Feed reader found - accept', $logdata);
return;
}
if (blockbot_is_fediverse_tool($parts)) {
Logger::debug('Fediverse tool found - accept', $logdata);
return;
}
if (blockbot_is_service_agent($parts)) {
Logger::debug('Service agent found - accept', $logdata);
return;
}
if (blockbot_is_monitor($parts)) {
Logger::debug('Monitoring service found - accept', $logdata);
return;
}
if (blockbot_is_validator($parts)) {
Logger::debug('Validation service found - accept', $logdata);
return;
}
if (blockbot_is_good_tool($parts)) {
Logger::debug('Uncategorized helpful service found - accept', $logdata);
return;
}
// Needs to be checked at the end, since other services might use these libraries
if (blockbot_is_http_library($parts)) {
blockbot_check_login_attempt($_SERVER['REQUEST_URI'], $logdata);
if (!DI::config()->get('blockbot', 'http_libraries')) { if (!DI::config()->get('blockbot', 'http_libraries')) {
Logger::debug('HTTP Library found - reject', $logdata); $agents = array_merge($agents, $http_libraries);
blockbot_reject(); } elseif (blockbot_match($http_libraries)) {
}
Logger::debug('HTTP Library found - accept', $logdata);
return; return;
} }
if (blockbot_match($agents)) {
throw new ForbiddenException('Bots are not allowed. If you consider this a mistake, create an issue at https://github.com/friendica/friendica');
}
// This switch here is only meant for developers who want to add more bots to the list above, it is not safe for production. // This switch here is only meant for developers who want to add more bots to the list above, it is not safe for production.
if (!DI::config()->get('blockbot', 'training')) { if (!DI::config()->get('blockbot', 'training')) {
return; return;
} }
if (!$isCrawler) { $crawlerDetect = new CrawlerDetect();
blockbot_save('good-agents', $_SERVER['HTTP_USER_AGENT']);
Logger::debug('Non-bot user agent detected', $logdata); if (!$crawlerDetect->isCrawler()) {
logger::debug('Good user agent detected', $logdata);
return; return;
} }
blockbot_save('bad-agents', $_SERVER['HTTP_USER_AGENT']); // List of known "good" agents, mostly used by Fediverse systems, feed readers, ...
Logger::notice('Possible bot found - reject', $logdata); $agents = [
blockbot_reject(); 'curl', 'zgrab', 'Go-http-client', 'curb', 'github.com', 'reqwest', 'Feedly/',
} 'Python-urllib/', 'Liferea/', 'aiohttp/', 'WordPress.com Reader', 'hackney/',
'Faraday v', 'okhttp', 'UniversalFeedParser', 'PixelFedBot', 'python-requests',
'WordPress/', 'http.rb/', 'Apache-HttpClient/', 'WordPress.com;', 'Pleroma',
'Dispatch/', 'Ruby', 'Java/', 'libwww-perl/', 'Mastodon/', 'FeedlyApp/',
'lua-resty-http/', 'Tiny Tiny RSS/', 'Wget/', 'PostmanRuntime/',
'W3C_Validator/', 'NetNewsWire', 'FeedValidator/', 'theoldreader.com', 'axios/',
'Paw/', 'PeerTube/', 'fedi.inex.dev', 'FediDB/', 'index.community crawler',
'Slackbot-LinkExpanding', 'Firefish/', 'Takahe/', 'Akkoma ', 'Misskey/', 'Lynx/',
'camo-rs asset proxy', 'gotosocial/', 'incestoma ', 'SpaceCowboys Android RSS Reader',
'NewsBlur Feed Finder', 'Lemmy/', 'enby-town/', 'rss2tg bot;', '; HTTrack ',
'MbinBot', 'kbinBot', 'Pixelfed/', 'NewsBlur Feed Fetcher', 'NewsBlur Page Fetcher',
];
function blockbot_save($database, $userAgent) if (blockbot_match($agents)) {
{ logger::info('False positive', $logdata);
if (!DI::config()->get('blockbot', 'logging') || !function_exists('dba_open')) {
return; return;
} }
$resource = dba_open(System::getTempPath() . '/' . $database, 'cl'); logger::notice('Blocked bot', $logdata);
$result = dba_fetch($userAgent, $resource); throw new ForbiddenException('Bots are not allowed. If you consider this a mistake, create an issue at https://github.com/friendica/friendica');
if ($result === false) {
dba_insert($userAgent, true, $resource);
}
dba_close($resource);
} }
function blockbot_log_activitypub(string $url, string $agent) function blockbot_match(array $agents)
{ {
if (!DI::config()->get('blockbot', 'logging')) {
return;
}
$bot = ['/.well-known/nodeinfo', '/nodeinfo/2.0', '/nodeinfo/1.0'];
if (in_array($url, $bot)) {
blockbot_save('activitypub-stats', $agent);
}
$bot = ['/api/v1/instance', '/api/v2/instance', '/api/v1/instance/extended_description',
'/api/v1/instance/peers'];
if (in_array($url, $bot)) {
blockbot_save('activitypub-api-stats', $agent);
}
if (substr($url, 0, 6) == '/api/v') {
blockbot_save('activitypub-api', $agent);
}
if (($_SERVER['REQUEST_METHOD'] == 'POST') && in_array('inbox', explode('/', parse_url($url, PHP_URL_PATH)))) {
blockbot_save('activitypub-inbox-agents', $agent);
}
if (!empty($_SERVER['HTTP_SIGNATURE']) && !empty(HTTPSignature::getSigner('', $_SERVER))) {
blockbot_save('activitypub-signature-agents', $agent);
}
}
function blockbot_check_login_attempt(string $url, array $logdata)
{
if (in_array(trim(parse_url($url, PHP_URL_PATH), '/'), ['login', 'lostpass', 'register'])) {
Logger::debug('Login attempt detected - reject', $logdata);
blockbot_reject();
}
}
/**
* Uncategorized and unwanted services
*
* @param array $parts
* @return boolean
*/
function blockbot_is_unwanted(array $parts): bool
{
$agents = [
'oii-research', 'yisouspider', 'bots.retroverse.social', 'gaisbot', 'bloglines', 'emailwolf',
'webtech', 'facebookscraper', 'www.ecsl.cs.sunysb.edu/~maxim/cgi-bin/link',
'gulper', 'magellan', 'linkcheck', 'nerdybot', 'ms search robot', 'fast-webcrawler',
'yioopbot', 'webster', 'www.admantx.com', 'openhosebot', 'lssrocketcrawler', 'dow jones searchbot',
'gomezagent', 'domainsigmacrawler', 'netseer crawler', 'superbot', 'searchexpress',
'alittle client', 'amazon-kendra', 'scanner.ducks.party', 'isscyberriskcrawler',
'google wireless transcoder',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
/**
* Services defined as "crawlers"
*
* @param array $parts
* @return boolean
*/
function blockbot_is_crawler(array $parts): bool
{
$agents = [
'+http://yourls.org', 'adbeat.com/policy', 'https://gtmetrix.com', 'hubspot', 'nutch-',
'openwebspider'
];
foreach ($parts as $part) {
foreach ($agents as $agent) { foreach ($agents as $agent) {
if (strpos($part, $agent) !== false) { if (stristr($_SERVER['HTTP_USER_AGENT'], $agent)) {
return true;
}
}
}
$agents = [
'ahrefsbot', 'pinterest', 'proximic', 'applebot', 'synapseworkstation.3.2.1',
'slackbot-linkexpanding', 'semrushbot-sa', 'qwantify', 'google search console',
'tbot-nutch', 'screaming frog seo spider', 'exaleadcloudview', 'dotbot', 'exabot',
'spbot', 'surdotlybot', 'tweetmemebot', 'cliqzbot', 'startmebot', 'ccbot', 'zoombot',
'domain re-animator bot', 'nutch', 'archive.org_bot http://www.archive.org/details',
'yahoo link preview', 'mxt', 'grapeshotcrawler', 'maxpointcrawler', 'vagabondo',
'archive.org_bot', 'infegyatlas', '2ip bot', 'accompanybot', 'antbot', 'anthropic-ai',
'aspiegelbot', 'cispa web analyzer', 'claudebot', 'colly', 'petalbot', 'ioncrawl',
'embedly +support@embed.ly', 'gitcrawlerbot', 'google favicon', 'httpx', 'seokicks',
'kocmohabt', 'masscan-ng', 'mixnodecache', 'nicecrawler', 'birdcrawlerbot', 'seolyt',
'dataprovider.com', 'dnsresearchbot', 'domains project', 'evc-batch', 'ev-crawler',
'example3', 'geedobot', 'internetmeasurement', 'ips-agent', 'semanticscholarbot',
'sputnikfaviconbot', 't3versionsbot', 'tchelebi', 'thinkchaos', 'velenpublicwebcrawler',
'webwikibot', 'woobot', 'project-resonance', 'mtrobot', 'webprosbot', 'youbot',
'queryseekerspider', 'scanning for research', 'semrushbot', 'senutobot', 'spawning-ai',
'statista.com publication finder crawler', 'turnitin', 'who.is bot', 'zaldamosearchbot',
'nuzzel', 'boardreader blog indexer', 'hatena-favicon', 'nbertaupete95', 'scrapy',
"electronic frontier foundation's do not track verifier", 'synapse', 'trendsmapresolver',
'pinterestbot', 'um-ln', 'slack-imgproxy', 'diffbot', 'dataforseobot', 'bw', 'bitlybot',
'twingly recon-klondike', 'imagesiftbot', 'rogerbot', 'yahoocachesystem', 'favicon',
'vkshare', 'appid: s~virustotalcloud', 'clickagy intelligence bot v2', 'gptbot',
'archive.org_bot http://archive.org/details', 'wellknownbot', 'archiveteam archivebot',
'megaindex.ru', 'adbeat_bot', 'masscan', 'embedly', 'cloudflare-amp', 'exabot-thumbnails',
'yahoo ad monitoring', 'seokicks-robot', 'trendiction search', 'semrushbot-si', 'plukkie',
'hubpages v0.2.2', 'aream.bot', 'safednsbot', 'linkpadbot', 'gluten free crawler',
'turnitinbot', 'xovibot', 'domaincrawler', 'nettrack', 'domaincrawler', 'yak', 'bubing',
'netestate ne crawler', 'blexbot', 'the knowledge ai', 'optimizer', 'hubspot webcrawler',
'venuscrawler', 'adstxtcrawler', 'iframely', 'checkmarknetwork', 'semrushbot-ba',
'archive.org bot', 'aihitbot', 'sitesucker', 'adstxtlab.com crawler', 'jobboersebot',
'http://www.archive.org/details/archive.org_bot', 'heritrix', 'appid: s~snapchat-proxy',
'icc-crawler', 'mbcrawler', 'slackbot', 'trumind-crawler', 'newspaper', 'online-webceo-bot',
'haena-pepper', 'y! crawler', 'linkwalker', 'seznamemailproxy', 'seekport crawler',
'domainstatsbot', 'qwantify/mermoz', 'sprinklr', 'komodiabot', 'seoscanners.net',
'domainappender', 'mixrankbot', 'abonti', 'urlappendbot', 'sistrix crawler',
'hatenabookmark', 'metainspector', 'ezooms', 'quora link preview', 'semrushbot-bm',
'barkrowler', 'panscient.com', 'http://tweetedtimes.com', 'twingly recon',
'collection@infegy.com', 'mediatoolkitbot', 'cloudflare-amphtml', 'ramblermail',
'tineye', 'adscanner', 'datagnionbot', 'aa_crawler', 'http://www.profound.net/domainappender',
'appid: e~arsnova-filter-system', 'kinglandsystemscorp', 'crmnlcrawlagent', 'techfetch-bot',
];
foreach ($parts as $part) {
if (substr($part, -13) == ' accompanybot') {
return true;
}
if (in_array($part, $agents)) {
return true; return true;
} }
} }
return false; return false;
} }
/**
* Services defined as search bots
*
* @param array $parts
* @return boolean
*/
function blockbot_is_searchbot(array $parts): bool
{
$agents = ['baiduspider'];
foreach ($parts as $part) {
foreach ($agents as $agent) {
if (strpos($part, $agent) !== false) {
return true;
}
}
}
$agents = [
'yahoo! slurp', 'linkcheck by siteimprove.com', 'googlebot', '360spider', 'haosouspider',
'mj12bot', 'feedfetcher-google', 'mediapartners-google', 'duckduckgo-favicons-bot',
'googlebot-mobile', 'gigablastopensource', 'bingbot', 'surveybot', 'yandexbot',
'google web preview', 'meanpathbot', 'wesee_bot:we_help_monitize_your_site',
'seznambot', 'sogou web spider', 'linkdexbot', 'msnbot', 'smtbot', 'yandexmetrika',
'google-site-verification', 'netcraft ssl server survey - contact info@netcraft.com',
'orangebot', 'google-adwords-instant', 'googlebot-richsnippets', 'google-lens',
'googleother', 'google-test', 'linkdex.com', 'mail.ru', 'awariobot', 'bytespider',
'coccocbot-image', 'discobot', 'google-inspectiontool', 'netcraftsurveyagent',
'tineye-bot', 'tineye-bot-live', 'bingpreview', 'ask jeeves', 'adsbot-google', "msnbot-media ",
'googlebot-image', 'googlebot-news', 'googlebot-video', 'msnbot-media', 'yahoo! slurp china',
'inoreader.com-like feedfetcher-google', 'google-amphtml', 'duckduckbot', 'coccocbot-web',
'googleassociationservice', 'yandexwebmaster', 'yacybot', 'duckduckbot-https', 'yandexmobilebot',
'mail.ru_bot/fast', 'yandeximages', 'mail.ru_bot/img', 'ia_archiver', 'yandexblogs',
'yandexaccessibilitybot', 'yandeximageresizer', 'mail.ru_bot', 'yeti', 'obot', 'baiduspider-render',
'netcraft web server survey', 'yandexnews', 'google', 'yandexrenderresourcesbot',
'match by siteimprove.com', 'yandexsitelinks', 'yandexantivirus', 'daum', 'mail.ru_bot/robots',
'yandexmedia', 'msnbot-products', 'yandexvideo', 'yandexvertis', 'catexplorador', 'yandexcalendar',
'yandexfavicons', 'user-agent\x09baiduspider', 'baiduspider-image', 'yandexpagechecker', 'mojeekbot',
'adsbot-google-mobile', 'google-adwords-displayads-webrender', 'seznam screenshot-generator',
'yandexscreenshotbot', 'zumbot', 'tracemyfile', 'wotbox', 'google-adwords-express',
'google-adwords-displayads', 'google-youtube-links', 'yandexvideoparser', 'paperlibot',
'weborama-fetcher', 'googleproducer', 'coccoc', 'acoonbot', 'psbot', 'sosospider', 'voilabot',
'blekkobot', 'easouspider', 'omgili', 'yadirectfetcher', 'sogou pic spider', 'daumoa',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
/**
* Services in the "security" context
*
* @param array $parts
* @return boolean
*/
function blockbot_is_security_checker(array $parts): bool
{
$agents = [
'http banner detection', 'l9explore', 'l9tcpid', 'lkx-apache2449traversalplugin',
'bitsightbot', 'censysinspect', 'pathspider', 'repolookoutbot', 'sqlmap', 'ltx71',
'netsystemsresearch studies the availability of various services across the internet. our website is netsystemsresearch.com',
'expanse a palo alto networks company searches across the global ipv4 space multiple times per day to identify customers&#39',
'zgrab', 'nmap scripting engine', 'l9scan', 'riddler', 'cloud mapping experiment. contact research@pdrlabs.net',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
/**
* Services that check pages for e.g. valid HTML
*
* @param array $parts
* @return boolean
*/
function blockbot_is_validator(array $parts): bool
{
$agents = [
'jigsaw', 'ssl labs', 'w3c_validator', 'w3c-checklink', 'p3p validator', 'csscheck', 'validator.nu',
'google-structured-data-testing-tool https://search.google.com/structured-data', 'w3c_unicorn',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
/**
* Services that monitor a page
*
* @param array $parts
* @return boolean
*/
function blockbot_is_monitor(array $parts): bool
{
$agents = [
'alexa site audit', 'catchpoint', 'google page speed insights', 'checkhost',
'poduptime', 'chrome-lighthouse', 'zabbix', 'cloudflare-alwaysonline', 'ptst',
'pingadmin.ru', 'pingdomtms', 'nimbostratus-bot', 'uptimebot', 'uptimerobot',
'http://notifyninja.com/monitoring', 'http://www.freewebmonitoring.com',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
/**
* Services in the centralized and decentralized social media environment
*
* @param array $parts
* @return boolean
*/
function blockbot_is_social_media(array $parts): bool
{
$agents = ['camo-rs asset proxy', 'camo asset proxy'];
foreach ($parts as $part) {
foreach ($agents as $agent) {
if (strpos($part, $agent) !== false) {
return true;
}
}
}
$agents = [
'facebookexternalhit', 'twitterbot', 'mastodon', 'facebookexternalua',
'friendica', 'diasporafederation', 'buzzrelay', 'activityrelay',
'aoderelay', 'ap-relay', 'peertube', 'misskey', 'pleroma', 'foundkey', 'akkoma',
'lemmy', 'calckey', 'mobilizon', 'zot', 'camo-rs', 'gotosocial', 'pixelfed',
'pixelfedbot', 'app.wafrn.net', 'go-camo', 'http://a.gup.pe', 'iceshrimp',
'firefish', 'activity-relay', 'juick', 'camo', 'python/federation', 'nextcloud',
'snac', 'bovine', 'takahe', 'freedica', 'gnu social', 'microblogpub',
'mbin', 'mammoth', 'kbinbot', 'honksnonk', 'misskeymediaproxy', 'kbinbot', 'jistflow',
'mastodon/3.4.1 fedibird', 'fedibird', 'funkwhale', 'linkedinbot',
'wafrn-cache-generator', 'simple social network', 'mbinbot', 'wordpress.com',
'catnip', 'castopod', 'enby-town', 'vernissage', 'iceshrimp.net', 'plasmatrap',
'imgproxy', 'rustypub', 'flipboard activitypub', 'gnu social activitypub plugin',
'micro.blog', 'mastodon-bookmark-rss', 'bookwyrm', 'damus', 'primal', 'misskeyadmin',
'ruby, mastodon', 'nextcloud social', 'camo asset proxy', 'smithereen', 'sorasns',
'cherrypick', 'bonfire activitypub federation', 'upub+0.1.0', 'plume', 'incestoma',
'gyptazyfedi', 'apogee', 'quolibet', 'magpie-crawler', 'redditbot', 'facebookplatform',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
/**
* Fediverse clients
*
* @param array $parts
* @return boolean
*/
function blockbot_is_fediverse_client(array $parts): bool
{
$agents = [
'mastodonandroid', 'tootdeck-worker', 'piefed', 'brighteon', 'pachli', 'tusky', 'mona', 'mitra',
'megalodonandroid', 'fedilab', 'mastodonapp', 'toot!', 'intravnews',
'pixeldroid', 'greatnews', 'protopage', 'newsfox', 'vienna', 'wp-urldetails', 'husky',
'activitypub-go-http-client', 'mobilesafari', 'mastodon-ios', 'mastodonpy', 'techniverse',
'relatica',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
/**
* Feed reading clients and services
*
* @param array $parts
* @return boolean
*/
function blockbot_is_feed_reader(array $parts): bool
{
$agents = [
'tiny tiny rss', 'mlem', 'feedly', 'flipboardproxy', 'reeder', 'netnewswire',
'freshrss', 'feedlyapp', 'feedlybot', 'feeddemon', 'rssowl', 'simplepie',
'magpierss', 'universalfeedparser', 'newsgatoronline', 'theoldreader.com',
'quiterss', 'feedburner', 'digg feed fetcher', 'r6_feedfetcher', 'apple-pubsub',
'netvibes', 'newsblur page fetcher', 'newsblur favicon fetcher', 'newsblur favicon fetcher',
'liferea', 'http://www.jetbrains.com/omea_reader/', 'feedblitz', 'bloglovin',
'windows-rss-platform', 'feedshow', 'feedreader', 'rssbandit', 'everyfeed-spider',
'feeeed', 'spacecowboys android rss reader', 'gregarius', 'feedspot',
'feedspot ssl asset proxy', 'newsgator', 'newsgator fetchlinks extension',
'akregator', 'appid: s~feedly-nikon3',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
function blockbot_is_fediverse_tool(array $parts): bool
{
$agents = [
'diaspora-connection-tester', 'fediblock.manalejandro.com',
'mastodoninstances', 'fedilist agent', 'https://fedilist.com/', 'fedidb',
'https://wiki.communitydata.science/communitydata:fediverse_research', 'mastofeed.com',
'lemmy-explorer-crawler', 'fedicheck.online v1.0', 'momostr', 'fedditlemmyversecrawler',
'fediseer', 'fedistatscrawler', 'gnusocialbot', 'fedifetcher', 'fedineko', 'bird.makeup',
'fediverse', 'fedicheck.online', 'https://fed.brid.gy/', 'lemmy-stats-crawler',
"fediverse's stats", 'friendicadirectory', 'rss discovery engine',
'python-opengraph-jaywink', 'connect.rocks', 'tootsdk',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
/**
* General services
*
* @param array $parts
* @return boolean
*/
function blockbot_is_service_agent(array $parts): bool
{
$agents = ['wordpress.com'];
foreach ($parts as $part) {
foreach ($agents as $agent) {
if (strpos($part, $agent) !== false) {
return true;
}
}
}
$agents = [
'chrome privacy preserving prefetch proxy', 'http compression test', 'microsoftpreview',
'pocketimagecache', 'wordpress', 'skypeuripreview preview', 'wordpress.com', 'discordbot',
'summalybot', 'livelapbot', 'whatsapp', 'facebot', 'skypeuripreview',
'plasmatrap image proxy server', 'grammarly', 'browsershots', 'google-apps-script',
'yahoomailproxy', 'pocketparser', 'apachebench',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
/**
* Libraries that perform HTTP requests
*
* @param array $parts
* @return boolean
*/
function blockbot_is_http_library(array $parts): bool
{
if ((count($parts) == 1) && in_array($parts[0], ['okhttp', 'useragent', 'faraday'])) {
return true;
}
$agents = ['faraday '];
foreach ($parts as $part) {
foreach ($agents as $agent) {
if (strpos($part, $agent) !== false) {
return true;
}
}
}
$agents = [
'python-urllib', 'go-http-client', 'axios', 'java', 'undici', 'node', 'ruby',
'mint', 'wget', 'dart:io', 'dart', 'caveman-sieve', 'guzzlehttp', 'deno',
'aiohttp', 'networkingextension', 'python-asks', 'fasthttp', 't7', 'scalaj-http',
'curl', 'python-requests', 'node-fetch', 'offline explorer', 'aria2',
'link_thumbnailer', 'python-httpx', 'com.apple.safari.searchhelper',
'com.apple.webkit.networking', 'luasocket', 'libwww-perl', 'google-http-java-client',
'appengine-google', 'reqwest', 'htmlparser', 'headlesschrome', 'winhttp',
'webcopier', 'webzip', 'http.jl', 'got', 'hackney', 'oca\mail\vendor\favicon',
'winhttp.winhttprequest.5', 'go package http', 'jakarta commons-httpclient',
'cpp-httplib', 'fuzz faster u fool v1.3.1-dev', 'fuzz faster u fool v1.5.0-dev',
'go http package', 'go-resty', 'http.rb', 'ivre-masscan', 'java1.0.21.0',
'jsdom', 'python-urllib3', 'reactornetty', 'req', 'restsharp', 'ruby-rdf-distiller',
'pycurl', 'fdm', 'fdmx', 'lua-resty-http', 'python-httplib2', 'anyevent-http',
'node-superagent', 'unirest-java', 'gvfs', 'http_request2', 'java browser', 'cakephp',
'curly http client', 'lavf', 'typhoeus',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
/**
* Uncategorized helpful services
*
* @param array $parts
* @return boolean
*/
function blockbot_is_good_tool(array $parts): bool
{
$agents = [
'easy-feed-oven', 'cutycapt', 'rss-is-dead.lol web bot', 'dnt-policy@eff.org',
'https://socnetv.org', 'opengraphreader', 'trendfetcher', 'iabot', 'rss-is-dead.lol feed bot',
'androiddownloadmanager', 'readybot.io', 'hydra', 'httrack', 'vlc', 'wdg_validator', 'download demon',
];
foreach ($parts as $part) {
if (in_array($part, $agents)) {
return true;
}
}
return false;
}
function blockbot_get_parts(string $agent): array
{
$parts = [];
$level = 0;
$start = 0;
$end = 0;
$has_brackets = false;
for ($pos = 0; $pos < strlen($agent); $pos++) {
if ((strpos(substr($agent, $pos), '(') === false) && ($level == 0)) {
$part = substr($agent, $pos);
$parts = array_merge($parts, blockbot_split_parts($part, strpos($part, '/'), !$has_brackets));
break;
} elseif (substr($agent, $pos, 1) == '(') {
$level++;
$has_brackets = true;
if ($level == 1) {
$part = substr($agent, $end, $pos - $end);
$parts = array_merge($parts, blockbot_split_parts($part, $start != 0, false));
$start = $pos + 1;
}
} elseif (substr($agent, $pos, 1) == ')') {
$level--;
if ($level == 0) {
$part = substr($agent, $start, $pos - $start);
$parts = array_merge($parts, blockbot_split_parts($part, false, true));
$end = $pos + 1;
}
}
}
return blockbot_remove_browser_parts($parts);
}
function blockbot_remove_browser_parts(array $parts): array
{
$cleaned = [];
foreach ($parts as $part) {
if (substr($part, -6) == ' build') {
continue;
}
$known = [
'mozilla', 'x11', 'ubuntu', 'linux x86_64', 'gecko', 'firefox', 'windows nt',
'win64', 'x64', 'android', 'applewebkit', 'khtml', 'like', 'chrome', 'safari', 'edg',
'unsupported', 'compatible', 'macintosh', 'intel mac os x', 'version', 'windows',
'u', 'en-us', '.net', '.net', 'wow64', 'linux', 'k', 'mobile', 'opr', 'msie',
'dalvik', 'build', 'nt', 'mobile safari', 'gecko/firefox', 'zh-cn', 'en-gb', 'clr',
'trident', '.net clr', 'qtwebengine', 'linux i686', 'tablet pc', 'ppc mac os x',
'en', 'fedora', 'ppc', 'edge', 'yabrowser', 'yowser', 'media center pc', 'arm_64',
'android 9', 'cros x86_64', 'iphone', 'cpu iphone os like mac os x', 'core',
'qqbrowser', 'beta', 'khtml like gecko', 'psp (playstation portable)', 'ia64',
'firephp', 'live', 'slcc2', 'infopath.2', 'bidubrowser', 'ubrowser', 'baiduboxapp',
'waterfox', 'lynx', 'libwww-fm', 'ssl-mm', 'openssl', 'gnutls', '.net4.0c', '.net4.0e',
'infopath.3', 'opera', 'palemoon', 'goanna', 'vivaldi', 'presto', 'intrepid', 'ru',
'ipad', 'cpu os like mac os x', 'omniweb', 'chromium', 'thunderbird', 'ubuntu lts',
'os', 'qupzilla', 'seamonkey', 'warp', 'konqueror', 'meego', 'nokian9', 'nokiabrowser',
'profile', 'configuration', 'untrusted', 'samsungbrowser', 'es-us', 'pocophone f1',
'sonyericssonw995', 'crios', 'lbbrowser', 'gwx:qualified', 'gwx:red', 'gwx:reserved',
'microsoft outlook', 'word', 'microsoft', 'office', 'powerpoint', 'excel',
'internet explorer', 'like gecko', 'shuame', 'qianniu', 'khtml, like gecko',
'cutycapt version', 'khtml, live gecko', '68k', 'sv1', 'aef', 'gtb7.5', 'gfe',
'embedded web browser from: http://bsalsa.com', 'wv', 'malnjs', '2.00',
'fsl', 'lcjb', 'malcjs', 'touch', 'masmjs', 'malc', 'maln', 'foxy', 'bri', 'lcte',
'embeddedwb from: http://www.bsalsa.com', '2345explorer', 'hpntdfjs', 'h4213',
'rb.gy', 'sm-a505fn', 'lenovo tb-8504x', 'silk', 'lya-al00', 'windows xp', 'openbsd',
'netbsd amd64', 'sa', 'samsung sm-g950f', 'redmi note', 'hry-lx1', 'cph2205',
'16th', 'redmi note pro', 'xiaomi/miuibrowser', 'sk-sk', 'linux i686 on x86_64',
'debian iceweasel', 'rmx2101', 'mi note pro', 'rmx1921', 'nokia6100', '04.01',
'fr-fr', 'slackware', 'sm-a225f', 'fennec', 'links', 'i386', 'windows phone os',
'blackberry', 'maxthon', 'opera mini', 'j2me', 'winnt4.0', 'phoenix', 'avant browser',
'iceweasel', 'moto e(7) plus', 'like geckoo', 'wpdesktop', 'nokia', 'lumia', 'arm',
'de-at', 'pixel', 'puffin', 'zte blade a7', 'linux armv7l', 'hd1913', 'symbianos',
'symbian os', 'de', '452', 'opera [en-us]', 'iemobile', 'windows phone', 'sm-g991b',
'sm-j810g', 'da-dk', 'symbian', 'series60', 'nokiax7-00', 'freebsd amd64', 'openbsd amd64',
'sm-n920c', 'blazer', 'palmsource', '16;320x320', 'sm-g998b', 'sm-a505g', 'freebsd i386',
'jaunty', 'shiretoko', 'playbook', 'rim tablet os', 'asus;galaxy6', 'minimo',
'linux arm7tdmi', 'blackberry7520', 'dl1036', '100011886a', 'lt-gtklauncher',
'browserng', 'nokiae7-00', 'ubuntu chromium', 'silk-accelerated=true', 'openbsd i386',
'windows ce', 'microsoft zunehd', 'epiphany', 'es-es', 'ru-ru', 'netbsd', 'ipod',
'safari', 'xbox', 'xbox one', 'fxios', 'opx', 'ucbrowser', 'u3',
'webos', 'desktop', 'compatible msie windows nt', 'sm-a525f', 'sm-g991u', 'ze520kl',
'cros i686', 'de-de', 'en-ca', 'config', 'i686', 'sm-g970u', 'win95', 'i',
'nokia7250', 'oneplus a6003', 'i2126', 'nintendo wii', 'vog-l29', 'msoffice', 'ms-office',
'oneplus a5010', 'linux mint', 'blackberry8320', 'observatory', 'qdesk',
'alexatoolbar', 'se metasr', 'qqdownload', 'alexa toolbar', 'baiduclient', 'ddg_android',
'com.duckduckgo.mobile.android', 'android api', 'duckduckgo', 'googletoolbar', 'amaya',
];
if (!in_array($part, $known) && !preg_match('=^rv:[\d]+\S*$=', $part)) {
$cleaned[] = $part;
}
}
return $cleaned;
}
function blockbot_clean_part(string $part): string
{
$part = trim($part);
$subparts = [];
foreach (explode(' ', $part) as $subpart) {
$subpart = trim($subpart, ' +,');
if (!empty($subpart) && (!preg_match('=^\d+[\w\-\+\.]+$=', $subpart) || empty($subparts))) {
$subparts[] = $subpart;
}
}
return implode(' ', $subparts);
}
function blockbot_split_parts(string $agent, bool $parse_spaces, bool $parse_semicolon): array
{
$agent = strtolower(trim($agent, ' ;'));
$cleaned = [];
while (preg_match('=\w+[\s\w/\._\-]*/\d+[^;\s]*=', $agent, $matches)) {
$part = $matches[0];
if (preg_match('=/\d+[^;\s]*=', $part, $matches, PREG_OFFSET_CAPTURE)) {
$cleaned[] = substr($part, 0, $matches[0][1]);
$part = substr($part, 0, $matches[0][1] + strlen($matches[0][0]));
}
$agent = trim(str_replace($part, '', $agent));
}
if ($parse_semicolon && strpos($agent, ';') !== false) {
$parse_spaces = false;
$parts = [];
foreach (explode(';', $agent) as $part) {
$parts[] = blockbot_clean_part($part);
}
} elseif (strpos($agent, ' - ') !== false) {
$parts = [];
foreach (explode(' - ', $agent) as $part) {
$parts[] = blockbot_clean_part($part);
}
} elseif ($parse_spaces) {
$parts = explode(' ', $agent);
} else {
$parts = [$agent];
}
if ($parse_spaces) {
$subparts = [];
foreach ($parts as $part) {
while (($pos_space = strpos($part, ' ')) !== false && ($pos_slash = strpos($part, '/')) !== false) {
if ($pos_space > $pos_slash) {
$subparts[] = substr($part, 0, $pos_space);
$part = trim(substr($part, $pos_space + 1), ' +,-;');
} else {
$subparts[] = $part;
$part = '';
}
}
if ($part != '') {
$subparts[] = $part;
}
}
$parts = $subparts;
}
foreach ($parts as $part) {
$part = trim($part, ' +');
if (!Network::isValidHttpUrl($part) && strpos($part, '/') !== false) {
$split = explode('/', $part);
array_pop($split);
$part = implode('/', $split);
}
$pos1 = strpos($part, "'");
$pos2 = strrpos($part, "'");
if ($pos1 != $pos2) {
$part = substr($part, 0, $pos1 - 1) . substr($part, $pos2 + 1);
}
$part = trim(preg_replace('=(.*) [\d\.]+=', '$1', $part), " +,-;\u{00AD}");
if (!empty($part)) {
$cleaned[] = $part;
}
}
return $cleaned;
}

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-30 12:12+0000\n" "POT-Creation-Date: 2024-03-05 04:51+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,29 +17,52 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: blockbot.php:32 #: blockbot.php:30
msgid "Save Settings" msgid "Save Settings"
msgstr "" msgstr ""
#: blockbot.php:33 #: blockbot.php:31
msgid "Allow security checkers" msgid "Allow \"good\" crawlers"
msgstr ""
#: blockbot.php:31
msgid ""
"Don't block fediverse crawlers, relay servers and other bots with good "
"purposes."
msgstr ""
#: blockbot.php:32
msgid "Allow preview agents"
msgstr ""
#: blockbot.php:32
msgid ""
"Don't block agents from social media systems that want to generate preview "
"data for links that had been set by their users."
msgstr "" msgstr ""
#: blockbot.php:33 #: blockbot.php:33
msgid "Don't block security checkers. They can be used for good or bad."
msgstr ""
#: blockbot.php:34
msgid "Allow generic HTTP libraries" msgid "Allow generic HTTP libraries"
msgstr "" msgstr ""
#: blockbot.php:34 #: blockbot.php:33
msgid "" msgid ""
"Don't block agents from generic HTTP libraries that could be used for good " "Don't block agents from generic HTTP libraries that could be used for good "
"or for bad and that currently can't be traced back to any known Fediverse " "or for bad and that currently can't be traced back to any known Fediverse "
"project." "project."
msgstr "" msgstr ""
#: blockbot.php:34
msgid "Block GabSocial"
msgstr ""
#: blockbot.php:34
msgid ""
"Block the software GabSocial. This will block every access for that "
"software. You can block dedicated gab instances in the blocklist settings in "
"the admin section."
msgstr ""
#: blockbot.php:35 #: blockbot.php:35
msgid "Training mode" msgid "Training mode"
msgstr "" msgstr ""

View file

@ -1,4 +1,6 @@
{{include file="field_checkbox.tpl" field=$security_checker}} {{include file="field_checkbox.tpl" field=$good_crawlers}}
{{include file="field_checkbox.tpl" field=$socialmedia_agents}}
{{include file="field_checkbox.tpl" field=$http_libraries}} {{include file="field_checkbox.tpl" field=$http_libraries}}
{{include file="field_checkbox.tpl" field=$block_gab}}
{{include file="field_checkbox.tpl" field=$training}} {{include file="field_checkbox.tpl" field=$training}}
<div class="submit"><button type="submit" class="btn btn-primary" name="page_site" value="{{$submit}}">{{$submit}}</button></div> <div class="submit"><button type="submit" class="btn btn-primary" name="page_site" value="{{$submit}}">{{$submit}}</button></div>

View file

@ -45,12 +45,10 @@ use Friendica\Model\Tag;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions; use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Object\Image; use Friendica\Object\Image;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Protocol\Relay; use Friendica\Protocol\Relay;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Strings; use Friendica\Util\Strings;
const BLUESKY_DEFAULT_POLL_INTERVAL = 10; // given in minutes const BLUESKY_DEFAULT_POLL_INTERVAL = 10; // given in minutes
@ -70,7 +68,6 @@ const BLUEKSY_STATUS_TOKEN_FAIL = 13;
const BLUESKY_DIRECTORY = 'https://plc.directory'; // Path to the directory server service to fetch the PDS of a given DID const BLUESKY_DIRECTORY = 'https://plc.directory'; // Path to the directory server service to fetch the PDS of a given DID
const BLUESKY_PDS = 'https://bsky.social'; // Path to the personal data server service (PDS) to fetch the DID for a given handle const BLUESKY_PDS = 'https://bsky.social'; // Path to the personal data server service (PDS) to fetch the DID for a given handle
const BLUESKY_WEB = 'https://bsky.app'; // Path to the web interface with the user profile and posts const BLUESKY_WEB = 'https://bsky.app'; // Path to the web interface with the user profile and posts
const BLUESKY_HOSTNAME = 'bsky.social'; // Host name to be added to the handle if incomplete
function bluesky_install() function bluesky_install()
{ {
@ -100,16 +97,11 @@ function bluesky_load_config(ConfigFileManager $loader)
function bluesky_check_item_notification(array &$notification_data) function bluesky_check_item_notification(array &$notification_data)
{ {
if (empty($notification_data['uid'])) { $did = DI::pConfig()->get($notification_data['uid'], 'bluesky', 'did');
return;
}
$did = bluesky_get_user_did($notification_data['uid']);
if (empty($did)) {
return;
}
if (!empty($did)) {
$notification_data['profiles'][] = $did; $notification_data['profiles'][] = $did;
}
} }
function bluesky_probe_detect(array &$hookData) function bluesky_probe_detect(array &$hookData)
@ -131,13 +123,8 @@ function bluesky_probe_detect(array &$hookData)
if (parse_url($hookData['uri'], PHP_URL_SCHEME) == 'did') { if (parse_url($hookData['uri'], PHP_URL_SCHEME) == 'did') {
$did = $hookData['uri']; $did = $hookData['uri'];
} elseif (parse_url($hookData['uri'], PHP_URL_PATH) == $hookData['uri'] && strpos($hookData['uri'], '@') === false) { } elseif (preg_match('#^' . BLUESKY_WEB . '/profile/(.+)#', $hookData['uri'], $matches)) {
$did = bluesky_get_did($hookData['uri'], $pconfig['uid']); $did = bluesky_get_did($matches[1]);
if (empty($did)) {
return;
}
} elseif (Network::isValidHttpUrl($hookData['uri'])) {
$did = bluesky_get_did_by_profile($hookData['uri']);
if (empty($did)) { if (empty($did)) {
return; return;
} }
@ -155,14 +142,19 @@ function bluesky_probe_detect(array &$hookData)
return; return;
} }
$hookData['result'] = bluesky_get_contact_fields($data, 0, $pconfig['uid'], true); $hookData['result'] = bluesky_get_contact_fields($data, 0, $pconfig['uid'], false);
$hookData['result']['baseurl'] = bluesky_get_pds($did);
// Preparing probe data. This differs slightly from the contact array // Preparing probe data. This differs slightly from the contact array
$hookData['result']['about'] = HTML::toBBCode($data->description ?? '');
$hookData['result']['photo'] = $data->avatar ?? ''; $hookData['result']['photo'] = $data->avatar ?? '';
$hookData['result']['header'] = $data->banner ?? '';
$hookData['result']['batch'] = ''; $hookData['result']['batch'] = '';
$hookData['result']['notify'] = ''; $hookData['result']['notify'] = '';
$hookData['result']['poll'] = ''; $hookData['result']['poll'] = '';
$hookData['result']['poco'] = ''; $hookData['result']['poco'] = '';
$hookData['result']['pubkey'] = '';
$hookData['result']['priority'] = 0; $hookData['result']['priority'] = 0;
$hookData['result']['guid'] = ''; $hookData['result']['guid'] = '';
} }
@ -179,21 +171,21 @@ function bluesky_item_by_link(array &$hookData)
return; return;
} }
$did = bluesky_get_did_by_profile($hookData['uri']); if (!preg_match('#^' . BLUESKY_WEB . '/profile/(.+)/post/(.+)#', $hookData['uri'], $matches)) {
return;
}
$did = bluesky_get_did($matches[1]);
if (empty($did)) { if (empty($did)) {
return; return;
} }
if (!preg_match('#/profile/.+/post/(.+)#', $hookData['uri'], $matches)) { Logger::debug('Found bluesky post', ['url' => $hookData['uri'], 'handle' => $matches[1], 'did' => $did, 'cid' => $matches[2]]);
return;
}
Logger::debug('Found bluesky post', ['url' => $hookData['uri'], 'did' => $did, 'cid' => $matches[1]]); $uri = 'at://' . $did . '/app.bsky.feed.post/' . $matches[2];
$uri = 'at://' . $did . '/app.bsky.feed.post/' . $matches[1];
$uri = bluesky_fetch_missing_post($uri, $hookData['uid'], $hookData['uid'], Item::PR_FETCHED, 0, 0, 0); $uri = bluesky_fetch_missing_post($uri, $hookData['uid'], $hookData['uid'], Item::PR_FETCHED, 0, 0, 0);
Logger::debug('Got post', ['did' => $did, 'cid' => $matches[1], 'result' => $uri]); Logger::debug('Got post', ['profile' => $matches[1], 'cid' => $matches[2], 'result' => $uri]);
if (!empty($uri)) { if (!empty($uri)) {
$item = Post::selectFirst(['id'], ['uri' => $uri, 'uid' => $hookData['uid']]); $item = Post::selectFirst(['id'], ['uri' => $uri, 'uid' => $hookData['uid']]);
if (!empty($item['id'])) { if (!empty($item['id'])) {
@ -237,7 +229,7 @@ function bluesky_follow(array &$hook_data)
$post = [ $post = [
'collection' => 'app.bsky.graph.follow', 'collection' => 'app.bsky.graph.follow',
'repo' => bluesky_get_user_did($hook_data['uid']), 'repo' => DI::pConfig()->get($hook_data['uid'], 'bluesky', 'did'),
'record' => $record 'record' => $record
]; ];
@ -290,15 +282,15 @@ function bluesky_block(array &$hook_data)
$post = [ $post = [
'collection' => 'app.bsky.graph.block', 'collection' => 'app.bsky.graph.block',
'repo' => bluesky_get_user_did($hook_data['uid']), 'repo' => DI::pConfig()->get($hook_data['uid'], 'bluesky', 'did'),
'record' => $record 'record' => $record
]; ];
$activity = bluesky_xrpc_post($hook_data['uid'], 'com.atproto.repo.createRecord', $post); $activity = bluesky_xrpc_post($hook_data['uid'], 'com.atproto.repo.createRecord', $post);
if (!empty($activity->uri)) { if (!empty($activity->uri)) {
$ucid = Contact::getUserContactId($hook_data['contact']['id'], $hook_data['uid']); $cdata = Contact::getPublicAndUserContactID($hook_data['contact']['id'], $hook_data['uid']);
if ($ucid) { if (!empty($cdata['user'])) {
Contact::remove($ucid); Contact::remove($cdata['user']);
} }
Logger::debug('Successfully blocked contact', ['url' => $hook_data['contact']['url'], 'uri' => $activity->uri]); Logger::debug('Successfully blocked contact', ['url' => $hook_data['contact']['url'], 'uri' => $activity->uri]);
} }
@ -350,7 +342,7 @@ function bluesky_settings(array &$data)
$def_enabled = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'post_by_default') ?? false; $def_enabled = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'post_by_default') ?? false;
$pds = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'pds'); $pds = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'pds');
$handle = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'handle'); $handle = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'handle');
$did = bluesky_get_user_did(DI::userSession()->getLocalUserId()); $did = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'did');
$token = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'access_token'); $token = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'access_token');
$import = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'import') ?? false; $import = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'import') ?? false;
$import_feeds = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'import_feeds') ?? false; $import_feeds = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'import_feeds') ?? false;
@ -358,11 +350,8 @@ function bluesky_settings(array &$data)
if (DI::config()->get('bluesky', 'friendica_handles')) { if (DI::config()->get('bluesky', 'friendica_handles')) {
$self = User::getById(DI::userSession()->getLocalUserId(), ['nickname']); $self = User::getById(DI::userSession()->getLocalUserId(), ['nickname']);
$host_handle = $self['nickname'] . '.' . DI::baseUrl()->getHost(); $handle = $self['nickname'] . '.' . DI::baseUrl()->getHost();
$friendica_handle = ['bluesky_friendica_handle', DI::l10n()->t('Allow to use %s as your Bluesky handle.', $host_handle), $custom_handle, DI::l10n()->t('When enabled, you can use %s as your Bluesky handle. After you enabled this option, please go to https://bsky.app/settings and select to change your handle. Select that you have got your own domain. Then enter %s and select "No DNS Panel". Then select "Verify Text File".', $host_handle, $host_handle)]; $friendica_handle = ['bluesky_friendica_handle', DI::l10n()->t('Allow to use %s as your Bluesky handle.', $handle), $custom_handle, DI::l10n()->t('When enabled, you can use %s as your Bluesky handle. After you enabled this option, please go to https://bsky.app/settings and select to change your handle. Select that you have got your own domain. Then enter %s and select "No DNS Panel". Then select "Verify Text File".', $handle, $handle)];
if ($custom_handle) {
$handle = $host_handle;
}
} else { } else {
$friendica_handle = []; $friendica_handle = [];
} }
@ -375,7 +364,7 @@ function bluesky_settings(array &$data)
'$import_feeds' => ['bluesky_import_feeds', DI::l10n()->t('Import the pinned feeds'), $import_feeds, DI::l10n()->t('When activated, Posts will be imported from all the feeds that you pinned in Bluesky.')], '$import_feeds' => ['bluesky_import_feeds', DI::l10n()->t('Import the pinned feeds'), $import_feeds, DI::l10n()->t('When activated, Posts will be imported from all the feeds that you pinned in Bluesky.')],
'$custom_handle' => $friendica_handle, '$custom_handle' => $friendica_handle,
'$pds' => ['bluesky_pds', DI::l10n()->t('Personal Data Server'), $pds, DI::l10n()->t('The personal data server (PDS) is the system that hosts your profile.'), '', 'readonly'], '$pds' => ['bluesky_pds', DI::l10n()->t('Personal Data Server'), $pds, DI::l10n()->t('The personal data server (PDS) is the system that hosts your profile.'), '', 'readonly'],
'$handle' => ['bluesky_handle', DI::l10n()->t('Bluesky handle'), $handle, '', '', $custom_handle ? 'readonly' : ''], '$handle' => ['bluesky_handle', DI::l10n()->t('Bluesky handle'), $handle],
'$did' => ['bluesky_did', DI::l10n()->t('Bluesky DID'), $did, DI::l10n()->t('This is the unique identifier. It will be fetched automatically, when the handle is entered.'), '', 'readonly'], '$did' => ['bluesky_did', DI::l10n()->t('Bluesky DID'), $did, DI::l10n()->t('This is the unique identifier. It will be fetched automatically, when the handle is entered.'), '', 'readonly'],
'$password' => ['bluesky_password', DI::l10n()->t('Bluesky app password'), '', DI::l10n()->t("Please don't add your real password here, but instead create a specific app password in the Bluesky settings.")], '$password' => ['bluesky_password', DI::l10n()->t('Bluesky app password'), '', DI::l10n()->t("Please don't add your real password here, but instead create a specific app password in the Bluesky settings.")],
'$status' => bluesky_get_status($handle, $did, $pds, $token), '$status' => bluesky_get_status($handle, $did, $pds, $token),
@ -446,10 +435,18 @@ function bluesky_settings_post(array &$b)
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'handle', $handle); DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'handle', $handle);
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'import', intval($_POST['bluesky_import'])); DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'import', intval($_POST['bluesky_import']));
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'import_feeds', intval($_POST['bluesky_import_feeds'])); DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'import_feeds', intval($_POST['bluesky_import_feeds']));
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'friendica_handle', intval($_POST['bluesky_friendica_handle'] ?? false)); DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'friendica_handle', intval($_POST['bluesky_friendica_handle']));
if (!empty($handle)) { if (!empty($handle)) {
$did = bluesky_get_user_did(DI::userSession()->getLocalUserId(), empty($old_did) || $old_handle != $handle); if (empty($old_did) || $old_handle != $handle) {
$did = bluesky_get_did(DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'handle'));
if (empty($did)) {
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'status', BLUEKSY_STATUS_DID_FAIL);
}
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'bluesky', 'did', $did);
} else {
$did = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'bluesky', 'did');
}
if (!empty($did) && (empty($old_pds) || $old_handle != $handle)) { if (!empty($did) && (empty($old_pds) || $old_handle != $handle)) {
$pds = bluesky_get_pds($did); $pds = bluesky_get_pds($did);
if (empty($pds)) { if (empty($pds)) {
@ -516,13 +513,8 @@ function bluesky_cron()
$abandon_limit = date(DateTimeFormat::MYSQL, time() - $abandon_days * 86400); $abandon_limit = date(DateTimeFormat::MYSQL, time() - $abandon_days * 86400);
$pconfigs = DBA::selectToArray('pconfig', [], ["`cat` = ? AND `k` IN (?, ?) AND `v`", 'bluesky', 'import', 'import_feeds']); $pconfigs = DBA::selectToArray('pconfig', [], ['cat' => 'bluesky', 'k' => 'import', 'v' => true]);
foreach ($pconfigs as $pconfig) { foreach ($pconfigs as $pconfig) {
if (empty(bluesky_get_user_did($pconfig['uid']))) {
Logger::debug('User has got no valid DID', ['uid' => $pconfig['uid']]);
continue;
}
if ($abandon_days != 0) { if ($abandon_days != 0) {
if (!DBA::exists('user', ["`uid` = ? AND `login_date` >= ?", $pconfig['uid'], $abandon_limit])) { if (!DBA::exists('user', ["`uid` = ? AND `login_date` >= ?", $pconfig['uid'], $abandon_limit])) {
Logger::notice('abandoned account: timeline from user will not be imported', ['user' => $pconfig['uid']]); Logger::notice('abandoned account: timeline from user will not be imported', ['user' => $pconfig['uid']]);
@ -531,27 +523,19 @@ function bluesky_cron()
} }
// Refresh the token now, so that it doesn't need to be refreshed in parallel by the following workers // Refresh the token now, so that it doesn't need to be refreshed in parallel by the following workers
Logger::debug('Refresh the token', ['uid' => $pconfig['uid']]);
bluesky_get_token($pconfig['uid']); bluesky_get_token($pconfig['uid']);
Worker::add(['priority' => Worker::PRIORITY_MEDIUM, 'force_priority' => true], 'addon/bluesky/bluesky_notifications.php', $pconfig['uid'], $last); Worker::add(['priority' => Worker::PRIORITY_MEDIUM, 'force_priority' => true], 'addon/bluesky/bluesky_notifications.php', $pconfig['uid'], $last);
if (DI::pConfig()->get($pconfig['uid'], 'bluesky', 'import')) {
Worker::add(['priority' => Worker::PRIORITY_MEDIUM, 'force_priority' => true], 'addon/bluesky/bluesky_timeline.php', $pconfig['uid'], $last); Worker::add(['priority' => Worker::PRIORITY_MEDIUM, 'force_priority' => true], 'addon/bluesky/bluesky_timeline.php', $pconfig['uid'], $last);
}
if (DI::pConfig()->get($pconfig['uid'], 'bluesky', 'import_feeds')) { if (DI::pConfig()->get($pconfig['uid'], 'bluesky', 'import_feeds')) {
Logger::debug('Fetch feeds for user', ['uid' => $pconfig['uid']]);
$feeds = bluesky_get_feeds($pconfig['uid']); $feeds = bluesky_get_feeds($pconfig['uid']);
foreach ($feeds as $feed) { foreach ($feeds as $feed) {
Worker::add(['priority' => Worker::PRIORITY_MEDIUM, 'force_priority' => true], 'addon/bluesky/bluesky_feed.php', $pconfig['uid'], $feed, $last); Worker::add(['priority' => Worker::PRIORITY_MEDIUM, 'force_priority' => true], 'addon/bluesky/bluesky_feed.php', $pconfig['uid'], $feed, $last);
} }
} }
Logger::debug('Polling done for user', ['uid' => $pconfig['uid']]);
} }
Logger::notice('Polling done for all users');
DI::keyValue()->set('bluesky_last_poll', time());
$last_clean = DI::keyValue()->get('bluesky_last_clean'); $last_clean = DI::keyValue()->get('bluesky_last_clean');
if (empty($last_clean) || ($last_clean + 86400 < time())) { if (empty($last_clean) || ($last_clean + 86400 < time())) {
Logger::notice('Start contact cleanup'); Logger::notice('Start contact cleanup');
@ -565,6 +549,8 @@ function bluesky_cron()
} }
Logger::notice('cron_end'); Logger::notice('cron_end');
DI::keyValue()->set('bluesky_last_poll', time());
} }
function bluesky_hook_fork(array &$b) function bluesky_hook_fork(array &$b)
@ -678,10 +664,7 @@ function bluesky_create_activity(array $item, stdClass $parent = null)
return; return;
} }
$did = bluesky_get_user_did($uid); $did = DI::pConfig()->get($uid, 'bluesky', 'did');
if (empty($did)) {
return;
}
if ($item['verb'] == Activity::LIKE) { if ($item['verb'] == Activity::LIKE) {
$record = [ $record = [
@ -789,7 +772,7 @@ function bluesky_create_post(array $item, stdClass $root = null, stdClass $paren
$post = [ $post = [
'collection' => 'app.bsky.feed.post', 'collection' => 'app.bsky.feed.post',
'repo' => bluesky_get_user_did($uid), 'repo' => DI::pConfig()->get($uid, 'bluesky', 'did'),
'record' => $record 'record' => $record
]; ];
@ -977,7 +960,6 @@ function bluesky_upload_blob(int $uid, array $photo): ?stdClass
return null; return null;
} }
Item::incrementOutbound(Protocol::BLUESKY);
Logger::debug('Uploaded blob', ['return' => $data, 'uid' => $uid, 'retrial' => $retrial, 'height' => $new_height, 'width' => $new_width, 'size' => $new_size, 'orig-height' => $height, 'orig-width' => $width, 'orig-size' => $size]); Logger::debug('Uploaded blob', ['return' => $data, 'uid' => $uid, 'retrial' => $retrial, 'height' => $new_height, 'width' => $new_width, 'size' => $new_size, 'orig-height' => $height, 'orig-width' => $width, 'orig-size' => $size]);
return $data->blob; return $data->blob;
} }
@ -1051,8 +1033,8 @@ function bluesky_process_reason(stdClass $reason, string $uri, int $uid)
$item['owner-link'] = $item['author-link']; $item['owner-link'] = $item['author-link'];
$item['owner-avatar'] = $item['author-avatar']; $item['owner-avatar'] = $item['author-avatar'];
if (Item::insert($item)) { if (Item::insert($item)) {
$pcid = Contact::getPublicContactId($contact['id'], $uid); $cdata = Contact::getPublicAndUserContactID($contact['id'], $uid);
Item::update(['post-reason' => Item::PR_ANNOUNCEMENT, 'causer-id' => $pcid], ['uri' => $uri, 'uid' => $uid]); Item::update(['post-reason' => Item::PR_ANNOUNCEMENT, 'causer-id' => $cdata['public']], ['uri' => $uri, 'uid' => $uid]);
} }
} }
@ -1231,11 +1213,7 @@ function bluesky_get_header(stdClass $post, string $uri, int $uid, int $fetch_ui
'source' => json_encode($post), 'source' => json_encode($post),
]; ];
$account = Contact::selectFirstAccountUser(['pid'], ['id' => $contact['id']]);
$item['author-id'] = $account['pid'];
$item['uri-id'] = ItemURI::getIdByURI($uri); $item['uri-id'] = ItemURI::getIdByURI($uri);
$item['owner-id'] = $item['author-id'];
$item['owner-name'] = $item['author-name']; $item['owner-name'] = $item['author-name'];
$item['owner-link'] = $item['author-link']; $item['owner-link'] = $item['author-link'];
$item['owner-avatar'] = $item['author-avatar']; $item['owner-avatar'] = $item['author-avatar'];
@ -1244,17 +1222,6 @@ function bluesky_get_header(stdClass $post, string $uri, int $uid, int $fetch_ui
$item['post-reason'] = Item::PR_FOLLOWER; $item['post-reason'] = Item::PR_FOLLOWER;
} }
if (!empty($post->labels)) {
foreach ($post->labels as $label) {
// Only flag posts as sensitive based on labels that had been provided by the author.
// When "ver" is set to "1" it was flagged by some automated process.
if (empty($label->ver)) {
$item['sensitive'] = true;
Logger::debug('Sensitive content', ['uri-id' => $item['uri-id'], 'label' => $label]);
}
}
}
return $item; return $item;
} }
@ -1264,7 +1231,7 @@ function bluesky_get_restrictions_for_user(stdClass $post, array $item, int $pos
return Item::CANT_REPLY; return Item::CANT_REPLY;
} }
if (empty($post->threadgate)) { if(empty($post->threadgate)) {
return null; return null;
} }
@ -1410,19 +1377,6 @@ function bluesky_add_media(stdClass $embed, array $item, int $fetch_uid, int $le
} }
break; break;
case 'app.bsky.embed.video#view':
$media = [
'uri-id' => $item['uri-id'],
'type' => Post\Media::HLS,
'url' => $embed->playlist,
'preview' => $embed->thumbnail,
'description' => $embed->alt ?? '',
'height' => $embed->aspectRatio->height,
'width' => $embed->aspectRatio->width,
];
Post\Media::insert($media);
break;
case 'app.bsky.embed.external#view': case 'app.bsky.embed.external#view':
$media = [ $media = [
'uri-id' => $item['uri-id'], 'uri-id' => $item['uri-id'],
@ -1537,10 +1491,6 @@ function bluesky_fetch_missing_post(string $uri, int $uid, int $fetch_uid, int $
} }
$class = bluesky_get_uri_class($uri); $class = bluesky_get_uri_class($uri);
if (empty($class)) {
return $fallback;
}
$fetch_uri = $class->uri; $fetch_uri = $class->uri;
Logger::debug('Fetch missing post', ['level' => $level, 'uid' => $uid, 'uri' => $uri]); Logger::debug('Fetch missing post', ['level' => $level, 'uid' => $uid, 'uri' => $uri]);
@ -1553,7 +1503,8 @@ function bluesky_fetch_missing_post(string $uri, int $uid, int $fetch_uid, int $
Logger::debug('Reply count', ['level' => $level, 'uid' => $uid, 'uri' => $uri]); Logger::debug('Reply count', ['level' => $level, 'uid' => $uid, 'uri' => $uri]);
if ($causer != 0) { if ($causer != 0) {
$causer = Contact::getPublicContactId($causer, $uid); $cdata = Contact::getPublicAndUserContactID($causer, $uid);
$causer = $cdata['public'] ?? 0;
} }
return bluesky_process_thread($data->thread, $uid, $fetch_uid, $post_reason, $causer, $level, $last_poll); return bluesky_process_thread($data->thread, $uid, $fetch_uid, $post_reason, $causer, $level, $last_poll);
@ -1622,7 +1573,7 @@ function bluesky_process_thread(stdClass $thread, int $uid, int $fetch_uid, int
function bluesky_get_contact(stdClass $author, int $uid, int $fetch_uid): array function bluesky_get_contact(stdClass $author, int $uid, int $fetch_uid): array
{ {
$condition = ['network' => Protocol::BLUESKY, 'uid' => 0, 'nurl' => $author->did]; $condition = ['network' => Protocol::BLUESKY, 'uid' => 0, 'url' => $author->did];
$contact = Contact::selectFirst(['id', 'updated'], $condition); $contact = Contact::selectFirst(['id', 'updated'], $condition);
$update = empty($contact) || $contact['updated'] < DateTimeFormat::utc('now -24 hours'); $update = empty($contact) || $contact['updated'] < DateTimeFormat::utc('now -24 hours');
@ -1640,7 +1591,7 @@ function bluesky_get_contact(stdClass $author, int $uid, int $fetch_uid): array
} }
if ($uid != 0) { if ($uid != 0) {
$condition = ['network' => Protocol::BLUESKY, 'uid' => $uid, 'nurl' => $author->did]; $condition = ['network' => Protocol::BLUESKY, 'uid' => $uid, 'url' => $author->did];
$contact = Contact::selectFirst(['id', 'rel', 'uid'], $condition); $contact = Contact::selectFirst(['id', 'rel', 'uid'], $condition);
if (!isset($fields['rel']) && isset($contact['rel'])) { if (!isset($fields['rel']) && isset($contact['rel'])) {
@ -1693,15 +1644,11 @@ function bluesky_get_contact_fields(stdClass $author, int $uid, int $fetch_uid,
return $fields; return $fields;
} }
$data = bluesky_get(BLUESKY_DIRECTORY . '/' . $author->did); $fields['baseurl'] = bluesky_get_pds($author->did);
if (!empty($data)) {
$fields['baseurl'] = bluesky_get_pds('', $data);
if (!empty($fields['baseurl'])) { if (!empty($fields['baseurl'])) {
GServer::check($fields['baseurl'], Protocol::BLUESKY); GServer::check($fields['baseurl'], Protocol::BLUESKY);
$fields['gsid'] = GServer::getID($fields['baseurl'], true); $fields['gsid'] = GServer::getID($fields['baseurl'], true);
} }
$fields['pubkey'] = bluesky_get_public_key('', $data);
}
$data = bluesky_xrpc_get($fetch_uid, 'app.bsky.actor.getProfile', ['actor' => $author->did]); $data = bluesky_xrpc_get($fetch_uid, 'app.bsky.actor.getProfile', ['actor' => $author->did]);
if (empty($data)) { if (empty($data)) {
@ -1739,9 +1686,6 @@ function bluesky_get_feeds(int $uid): array
{ {
$type = '$type'; $type = '$type';
$preferences = bluesky_get_preferences($uid); $preferences = bluesky_get_preferences($uid);
if (empty($preferences) || empty($preferences->preferences)) {
return [];
}
foreach ($preferences->preferences as $preference) { foreach ($preferences->preferences as $preference) {
if ($preference->$type == 'app.bsky.actor.defs#savedFeedsPref') { if ($preference->$type == 'app.bsky.actor.defs#savedFeedsPref') {
return $preference->pinned ?? []; return $preference->pinned ?? [];
@ -1750,7 +1694,7 @@ function bluesky_get_feeds(int $uid): array
return []; return [];
} }
function bluesky_get_preferences(int $uid): ?stdClass function bluesky_get_preferences(int $uid): stdClass
{ {
$cachekey = 'bluesky:preferences:' . $uid; $cachekey = 'bluesky:preferences:' . $uid;
$data = DI::cache()->get($cachekey); $data = DI::cache()->get($cachekey);
@ -1759,57 +1703,11 @@ function bluesky_get_preferences(int $uid): ?stdClass
} }
$data = bluesky_xrpc_get($uid, 'app.bsky.actor.getPreferences'); $data = bluesky_xrpc_get($uid, 'app.bsky.actor.getPreferences');
if (empty($data)) {
return null;
}
DI::cache()->set($cachekey, $data, Duration::HOUR); DI::cache()->set($cachekey, $data, Duration::HOUR);
return $data; return $data;
} }
function bluesky_get_did_by_profile(string $url): string
{
try {
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML, [HttpClientOptions::REQUEST => HttpClientRequest::CONTACTINFO]);
} catch (\Throwable $th) {
return '';
}
if (!$curlResult->isSuccess()) {
return '';
}
$profile = $curlResult->getBodyString();
if (empty($profile)) {
return '';
}
$doc = new DOMDocument();
try {
@$doc->loadHTML($profile);
} catch (\Throwable $th) {
return '';
}
$xpath = new DOMXPath($doc);
$list = $xpath->query('//p[@id]');
foreach ($list as $node) {
foreach ($node->attributes as $attribute) {
if ($attribute->name == 'id') {
$ids[$attribute->value] = $node->textContent;
}
}
}
if (empty($ids['bsky_handle']) || empty($ids['bsky_did'])) {
return '';
}
if (!bluesky_valid_did($ids['bsky_did'], $ids['bsky_handle'])) {
Logger::notice('Invalid DID', ['handle' => $ids['bsky_handle'], 'did' => $ids['bsky_did']]);
return '';
}
return $ids['bsky_did'];
}
function bluesky_get_did_by_wellknown(string $handle): string function bluesky_get_did_by_wellknown(string $handle): string
{ {
$curlResult = DI::httpClient()->get('http://' . $handle . '/.well-known/atproto-did'); $curlResult = DI::httpClient()->get('http://' . $handle . '/.well-known/atproto-did');
@ -1819,6 +1717,7 @@ function bluesky_get_did_by_wellknown(string $handle): string
Logger::notice('Invalid DID', ['handle' => $handle, 'did' => $did]); Logger::notice('Invalid DID', ['handle' => $handle, 'did' => $did]);
return ''; return '';
} }
Logger::debug('Got DID by wellknown', ['handle' => $handle, 'did' => $did]);
return $did; return $did;
} }
return ''; return '';
@ -1837,107 +1736,53 @@ function bluesky_get_did_by_dns(string $handle): string
Logger::notice('Invalid DID', ['handle' => $handle, 'did' => $did]); Logger::notice('Invalid DID', ['handle' => $handle, 'did' => $did]);
return ''; return '';
} }
return $did;
}
}
return '';
}
function bluesky_get_did(string $handle, int $uid): string
{
if ($handle == '') {
return '';
}
if (strpos($handle, '.') === false) {
$handle .= '.' . BLUESKY_HOSTNAME;
}
// At first we use the user PDS. That should cover most cases.
$pds = DI::pConfig()->get($uid, 'bluesky', 'pds');
if (!empty($pds)) {
$data = bluesky_get($pds . '/xrpc/com.atproto.identity.resolveHandle?handle=' . urlencode($handle));
if (!empty($data) && !empty($data->did)) {
Logger::debug('Got DID by user PDS call', ['handle' => $handle, 'did' => $data->did]);
return $data->did;
}
}
// Then we query the DNS, which is used for third party handles (DNS should be faster than wellknown)
$did = bluesky_get_did_by_dns($handle);
if ($did != '') {
Logger::debug('Got DID by DNS', ['handle' => $handle, 'did' => $did]); Logger::debug('Got DID by DNS', ['handle' => $handle, 'did' => $did]);
return $did; return $did;
} }
// Then we query wellknown, which should mostly cover the rest.
$did = bluesky_get_did_by_wellknown($handle);
if ($did != '') {
Logger::debug('Got DID by wellknown', ['handle' => $handle, 'did' => $did]);
return $did;
} }
// And finally we use the default PDS from Bluesky.
$data = bluesky_get(BLUESKY_PDS . '/xrpc/com.atproto.identity.resolveHandle?handle=' . urlencode($handle));
if (!empty($data) && !empty($data->did)) {
Logger::debug('Got DID by system PDS call', ['handle' => $handle, 'did' => $data->did]);
return $data->did;
}
Logger::notice('No DID detected', ['handle' => $handle]);
return ''; return '';
} }
function bluesky_get_user_did(int $uid, bool $refresh = false): ?string function bluesky_get_did(string $handle): string
{ {
if (!$refresh) { // Deactivated at the moment, since it isn't reliable by now
$did = DI::pConfig()->get($uid, 'bluesky', 'did'); //$did = bluesky_get_did_by_dns($handle);
if (!empty($did)) { //if ($did != '') {
return $did; // return $did;
} //}
}
$handle = DI::pConfig()->get($uid, 'bluesky', 'handle'); //$did = bluesky_get_did_by_wellknown($handle);
if (empty($handle)) { //if ($did != '') {
return null; // return $did;
} //}
$did = bluesky_get_did($handle, $uid); $data = bluesky_get(BLUESKY_PDS . '/xrpc/com.atproto.identity.resolveHandle?handle=' . urlencode($handle));
if (empty($did)) { if (empty($data) || empty($data->did)) {
return null; return '';
} }
Logger::debug('Got DID by PDS call', ['handle' => $handle, 'did' => $data->did]);
Logger::debug('Got DID for user', ['uid' => $uid, 'handle' => $handle, 'did' => $did]); return $data->did;
DI::pConfig()->set($uid, 'bluesky', 'did', $did);
return $did;
} }
function bluesky_get_user_pds(int $uid): ?string function bluesky_get_user_pds(int $uid): string
{ {
$pds = DI::pConfig()->get($uid, 'bluesky', 'pds'); $pds = DI::pConfig()->get($uid, 'bluesky', 'pds');
if (!empty($pds)) { if (!empty($pds)) {
return $pds; return $pds;
} }
$did = DI::pConfig()->get($uid, 'bluesky', 'did');
$did = bluesky_get_user_did($uid);
if (empty($did)) { if (empty($did)) {
return null; Logger::notice('Empty did for user', ['uid' => $uid]);
return '';
} }
$pds = bluesky_get_pds($did); $pds = bluesky_get_pds($did);
if (empty($pds)) {
return null;
}
DI::pConfig()->set($uid, 'bluesky', 'pds', $pds); DI::pConfig()->set($uid, 'bluesky', 'pds', $pds);
return $pds; return $pds;
} }
function bluesky_get_pds(string $did, stdClass $data = null): ?string function bluesky_get_pds(string $did): ?string
{ {
if (empty($data)) {
$data = bluesky_get(BLUESKY_DIRECTORY . '/' . $did); $data = bluesky_get(BLUESKY_DIRECTORY . '/' . $did);
}
if (empty($data) || empty($data->service)) { if (empty($data) || empty($data->service)) {
return null; return null;
} }
@ -1951,22 +1796,6 @@ function bluesky_get_pds(string $did, stdClass $data = null): ?string
return null; return null;
} }
function bluesky_get_public_key(string $did, stdClass $data = null): ?string
{
if (empty($data)) {
$data = bluesky_get(BLUESKY_DIRECTORY . '/' . $did);
}
if (empty($data) || empty($data->verificationMethod)) {
return null;
}
foreach ($data->verificationMethod as $method) {
if (!empty($method->publicKeyMultibase)) {
return $method->publicKeyMultibase;
}
}
return null;
}
function bluesky_valid_did(string $did, string $handle): bool function bluesky_valid_did(string $did, string $handle): bool
{ {
$data = bluesky_get(BLUESKY_DIRECTORY . '/' . $did); $data = bluesky_get(BLUESKY_DIRECTORY . '/' . $did);
@ -2009,10 +1838,7 @@ function bluesky_refresh_token(int $uid): string
function bluesky_create_token(int $uid, string $password): string function bluesky_create_token(int $uid, string $password): string
{ {
$did = bluesky_get_user_did($uid); $did = DI::pConfig()->get($uid, 'bluesky', 'did');
if (empty($did)) {
return '';
}
$data = bluesky_post($uid, '/xrpc/com.atproto.server.createSession', json_encode(['identifier' => $did, 'password' => $password]), ['Content-type' => 'application/json']); $data = bluesky_post($uid, '/xrpc/com.atproto.server.createSession', json_encode(['identifier' => $did, 'password' => $password]), ['Content-type' => 'application/json']);
if (empty($data)) { if (empty($data)) {
@ -2030,22 +1856,13 @@ function bluesky_create_token(int $uid, string $password): string
function bluesky_xrpc_post(int $uid, string $url, $parameters): ?stdClass function bluesky_xrpc_post(int $uid, string $url, $parameters): ?stdClass
{ {
$data = bluesky_post($uid, '/xrpc/' . $url, json_encode($parameters), ['Content-type' => 'application/json', 'Authorization' => ['Bearer ' . bluesky_get_token($uid)]]); return bluesky_post($uid, '/xrpc/' . $url, json_encode($parameters), ['Content-type' => 'application/json', 'Authorization' => ['Bearer ' . bluesky_get_token($uid)]]);
if (!empty($data)) {
Item::incrementOutbound(Protocol::BLUESKY);
}
return $data;
} }
function bluesky_post(int $uid, string $url, string $params, array $headers): ?stdClass function bluesky_post(int $uid, string $url, string $params, array $headers): ?stdClass
{ {
$pds = bluesky_get_user_pds($uid);
if (empty($pds)) {
return null;
}
try { try {
$curlResult = DI::httpClient()->post($pds . $url, $params, $headers); $curlResult = DI::httpClient()->post(bluesky_get_user_pds($uid) . $url, $params, $headers);
} catch (\Exception $e) { } catch (\Exception $e) {
Logger::notice('Exception on post', ['exception' => $e]); Logger::notice('Exception on post', ['exception' => $e]);
DI::pConfig()->set($uid, 'bluesky', 'status', BLUEKSY_STATUS_API_FAIL); DI::pConfig()->set($uid, 'bluesky', 'status', BLUEKSY_STATUS_API_FAIL);
@ -2068,12 +1885,7 @@ function bluesky_xrpc_get(int $uid, string $url, array $parameters = []): ?stdCl
$url .= '?' . http_build_query($parameters); $url .= '?' . http_build_query($parameters);
} }
$pds = bluesky_get_user_pds($uid); $data = bluesky_get(bluesky_get_user_pds($uid) . '/xrpc/' . $url, HttpClientAccept::JSON, [HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . bluesky_get_token($uid)]]]);
if (empty($pds)) {
return null;
}
$data = bluesky_get($pds . '/xrpc/' . $url, HttpClientAccept::JSON, [HttpClientOptions::HEADERS => ['Authorization' => ['Bearer ' . bluesky_get_token($uid)]]]);
DI::pConfig()->set($uid, 'bluesky', 'status', is_null($data) ? BLUEKSY_STATUS_API_FAIL : BLUEKSY_STATUS_SUCCESS); DI::pConfig()->set($uid, 'bluesky', 'status', is_null($data) ? BLUEKSY_STATUS_API_FAIL : BLUEKSY_STATUS_SUCCESS);
return $data; return $data;
} }
@ -2083,15 +1895,14 @@ function bluesky_get(string $url, string $accept_content = HttpClientAccept::DEF
try { try {
$curlResult = DI::httpClient()->get($url, $accept_content, $opts); $curlResult = DI::httpClient()->get($url, $accept_content, $opts);
} catch (\Exception $e) { } catch (\Exception $e) {
Logger::notice('Exception on get', ['url' => $url, 'exception' => $e]); Logger::notice('Exception on get', ['exception' => $e]);
return null; return null;
} }
if (!$curlResult->isSuccess()) { if (!$curlResult->isSuccess()) {
Logger::notice('API Error', ['url' => $url, 'error' => json_decode($curlResult->getBodyString()) ?: $curlResult->getBodyString()]); Logger::notice('API Error', ['error' => json_decode($curlResult->getBodyString()) ?: $curlResult->getBodyString()]);
return null; return null;
} }
Item::incrementInbound(Protocol::BLUESKY);
return json_decode($curlResult->getBodyString()); return json_decode($curlResult->getBodyString());
} }

View file

@ -6,55 +6,55 @@
# Translators: # Translators:
# fabrixxm <fabrix.xm@gmail.com>, 2018 # fabrixxm <fabrix.xm@gmail.com>, 2018
# Davide Pesenti <mrjive@mrjive.it>, 2018 # Davide Pesenti <mrjive@mrjive.it>, 2018
# Sylke Vicious <silkevicious@gmail.com>, 2023 # Sylke Vicious <silkevicious@gmail.com>, 2021
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2020-12-29 00:53+0000\n"
"PO-Revision-Date: 2018-04-07 05:23+0000\n" "PO-Revision-Date: 2018-04-07 05:23+0000\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2023\n" "Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2021\n"
"Language-Team: Italian (https://app.transifex.com/Friendica/teams/12172/it/)\n" "Language-Team: Italian (https://www.transifex.com/Friendica/teams/12172/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: catavatar.php:48 #: catavatar.php:48
msgid "Set default profile avatar or randomize the cat."
msgstr "Imposta l'immagine di profilo predefinita o crea un gatto casuale."
#: catavatar.php:53
msgid "Cat Avatar Settings"
msgstr "Impostazioni Avatar Gatto"
#: catavatar.php:56
msgid "Use Cat as Avatar" msgid "Use Cat as Avatar"
msgstr "Usa il Gatto come avatar" msgstr "Usa il Gatto come avatar"
#: catavatar.php:57 #: catavatar.php:49
msgid "Another random Cat!" msgid "More Random Cat!"
msgstr "Un altro Gatto casuale!" msgstr "Altro Gatto a caso!"
#: catavatar.php:58 #: catavatar.php:50
msgid "Reset to email Cat" msgid "Reset to email Cat"
msgstr "Reimposta Gatto" msgstr "Reimposta Gatto"
#: catavatar.php:77 #: catavatar.php:52
msgid "Cat Avatar Settings"
msgstr "Impostazioni Avatar Gatto"
#: catavatar.php:53
msgid "Set default profile avatar or randomize the cat."
msgstr "Imposta l'immagine di profilo predefinita o crea un gatto casuale."
#: catavatar.php:78
msgid "The cat hadn't found itself." msgid "The cat hadn't found itself."
msgstr "Il gatto non ha trovato sé stesso." msgstr "Il gatto non ha trovato sé stesso."
#: catavatar.php:86 #: catavatar.php:87
msgid "There was an error, the cat ran away." msgid "There was an error, the cat ran away."
msgstr "Si è verificato un errore, il gatto è scappato." msgstr "Si è verificato un errore, il gatto è scappato."
#: catavatar.php:92 #: catavatar.php:93
msgid "Profile Photos" msgid "Profile Photos"
msgstr "Foto del profilo" msgstr "Foto del profilo"
#: catavatar.php:102 #: catavatar.php:108
msgid "Meow!" msgid "Meow!"
msgstr "Miao!" msgstr "Miao!"

View file

@ -3,13 +3,13 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['Set default profile avatar or randomize the cat.'] = 'Imposta l\'immagine di profilo predefinita o crea un gatto casuale.';
$a->strings['Cat Avatar Settings'] = 'Impostazioni Avatar Gatto';
$a->strings['Use Cat as Avatar'] = 'Usa il Gatto come avatar'; $a->strings['Use Cat as Avatar'] = 'Usa il Gatto come avatar';
$a->strings['Another random Cat!'] = 'Un altro Gatto casuale!'; $a->strings['More Random Cat!'] = 'Altro Gatto a caso!';
$a->strings['Reset to email Cat'] = 'Reimposta Gatto'; $a->strings['Reset to email Cat'] = 'Reimposta Gatto';
$a->strings['Cat Avatar Settings'] = 'Impostazioni Avatar Gatto';
$a->strings['Set default profile avatar or randomize the cat.'] = 'Imposta l\'immagine di profilo predefinita o crea un gatto casuale.';
$a->strings['The cat hadn\'t found itself.'] = 'Il gatto non ha trovato sé stesso.'; $a->strings['The cat hadn\'t found itself.'] = 'Il gatto non ha trovato sé stesso.';
$a->strings['There was an error, the cat ran away.'] = 'Si è verificato un errore, il gatto è scappato.'; $a->strings['There was an error, the cat ran away.'] = 'Si è verificato un errore, il gatto è scappato.';
$a->strings['Profile Photos'] = 'Foto del profilo'; $a->strings['Profile Photos'] = 'Foto del profilo';

View file

@ -9,10 +9,12 @@
* *
*/ */
use Friendica\App;
use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\DI; use Friendica\DI;
use Friendica\Util\Proxy as ProxyUtils;
function curweather_install() function curweather_install()
{ {
@ -125,7 +127,7 @@ function curweather_network_mod_init(string &$body)
$t = Renderer::getMarkupTemplate("widget.tpl", "addon/curweather/" ); $t = Renderer::getMarkupTemplate("widget.tpl", "addon/curweather/" );
$curweather = Renderer::replaceMacros($t, [ $curweather = Renderer::replaceMacros($t, [
'$title' => DI::l10n()->t("Current Weather"), '$title' => DI::l10n()->t("Current Weather"),
'$icon' => 'http://openweathermap.org/img/w/'.$res['icon'].'.png', '$icon' => ProxyUtils::proxifyUrl('http://openweathermap.org/img/w/'.$res['icon'].'.png'),
'$city' => $res['city'], '$city' => $res['city'],
'$lon' => $res['lon'], '$lon' => $res['lon'],
'$lat' => $res['lat'], '$lat' => $res['lat'],
@ -152,7 +154,7 @@ function curweather_network_mod_init(string &$body)
function curweather_addon_settings_post($post) function curweather_addon_settings_post($post)
{ {
if (!DI::userSession()->getLocalUserId() || empty($_POST['curweather-submit'])) { if (!DI::userSession()->getLocalUserId() || empty($_POST['curweather-settings-submit'])) {
return; return;
} }

View file

@ -5,7 +5,6 @@
# #
# Translators: # Translators:
# bob lebonche <lebonche@tutanota.com>, 2021 # bob lebonche <lebonche@tutanota.com>, 2021
# cracrayol, 2024
# Hypolite Petovan <hypolite@mrpetovan.com>, 2022 # Hypolite Petovan <hypolite@mrpetovan.com>, 2022
# Hypolite Petovan <hypolite@mrpetovan.com>, 2016 # Hypolite Petovan <hypolite@mrpetovan.com>, 2016
# ea1cd8241cb389ffb6f92bc6891eff5d_dc12308 <70dced5587d47e18d88f9298024d96f8_93383>, 2015 # ea1cd8241cb389ffb6f92bc6891eff5d_dc12308 <70dced5587d47e18d88f9298024d96f8_93383>, 2015
@ -16,8 +15,8 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2014-06-22 11:34+0000\n" "PO-Revision-Date: 2014-06-22 11:34+0000\n"
"Last-Translator: cracrayol, 2024\n" "Last-Translator: Hypolite Petovan <hypolite@mrpetovan.com>, 2022\n"
"Language-Team: French (http://app.transifex.com/Friendica/friendica/language/fr/)\n" "Language-Team: French (http://www.transifex.com/Friendica/friendica/language/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@ -46,7 +45,7 @@ msgstr "Vent"
#: curweather.php:140 #: curweather.php:140
msgid "Last Updated" msgid "Last Updated"
msgstr "Dernière mise à jour" msgstr "Dernière mise-à-jour"
#: curweather.php:141 #: curweather.php:141
msgid "Data by" msgid "Data by"

View file

@ -10,7 +10,7 @@ $a->strings['Current Weather'] = 'Météo actuelle';
$a->strings['Relative Humidity'] = 'Humidité relative'; $a->strings['Relative Humidity'] = 'Humidité relative';
$a->strings['Pressure'] = 'Pression'; $a->strings['Pressure'] = 'Pression';
$a->strings['Wind'] = 'Vent'; $a->strings['Wind'] = 'Vent';
$a->strings['Last Updated'] = 'Dernière mise à jour'; $a->strings['Last Updated'] = 'Dernière mise-à-jour';
$a->strings['Data by'] = 'Données de'; $a->strings['Data by'] = 'Données de';
$a->strings['Show on map'] = 'Montrer sur la carte'; $a->strings['Show on map'] = 'Montrer sur la carte';
$a->strings['There was a problem accessing the weather data. But have a look'] = 'Une erreur est survenue lors de l\'accès aux données météo. Vous pouvez quand même jeter un oeil'; $a->strings['There was a problem accessing the weather data. But have a look'] = 'Une erreur est survenue lors de l\'accès aux données météo. Vous pouvez quand même jeter un oeil';

View file

@ -5,27 +5,27 @@
# #
# Translators: # Translators:
# fabrixxm <fabrix.xm@gmail.com>, 2014-2015 # fabrixxm <fabrix.xm@gmail.com>, 2014-2015
# Sylke Vicious <silkevicious@gmail.com>, 2021,2023 # Sylke Vicious <silkevicious@gmail.com>, 2021
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2016 # Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2016
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2014-06-22 11:34+0000\n" "PO-Revision-Date: 2021-02-16 12:57+0000\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2021,2023\n" "Last-Translator: Sylke Vicious <silkevicious@gmail.com>\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: curweather.php:47 #: curweather.php:47
msgid "Error fetching weather data. Error was: " msgid "Error fetching weather data. Error was: "
msgstr "Errore durante il recupero dei dati meteo. L'errore è stato:" msgstr "Errore durante il recupero dei dati meteo. L'errore è stato:"
#: curweather.php:130 #: curweather.php:130 curweather.php:192
msgid "Current Weather" msgid "Current Weather"
msgstr "Meteo" msgstr "Meteo"
@ -61,66 +61,66 @@ msgstr "C'è stato un problema accedendo ai dati meteo, ma dai un'occhiata"
msgid "at OpenWeatherMap" msgid "at OpenWeatherMap"
msgstr "a OpenWeatherMap" msgstr "a OpenWeatherMap"
#: curweather.php:178 #: curweather.php:179
msgid "No APPID found, please contact your admin to obtain one." msgid "No APPID found, please contact your admin to obtain one."
msgstr "APPID non trovata, contatta il tuo amministratore per averne una." msgstr "APPID non trovata, contatta il tuo amministratore per averne una."
#: curweather.php:188 #: curweather.php:191 curweather.php:229
msgid "Save Settings"
msgstr "Salva Impostazioni"
#: curweather.php:192
msgid "Settings"
msgstr "Impostazioni"
#: curweather.php:194
msgid "Enter either the name of your location or the zip code." msgid "Enter either the name of your location or the zip code."
msgstr "Inserisci il nome della tua posizione o il CAP" msgstr "Inserisci il nome della tua posizione o il CAP"
#: curweather.php:189 #: curweather.php:195
msgid "Your Location" msgid "Your Location"
msgstr "La tua Posizione" msgstr "La tua Posizione"
#: curweather.php:189 #: curweather.php:195
msgid "" msgid ""
"Identifier of your location (name or zip code), e.g. <em>Berlin,DE</em> or " "Identifier of your location (name or zip code), e.g. <em>Berlin,DE</em> or "
"<em>14476,DE</em>." "<em>14476,DE</em>."
msgstr "Identificatore della tua posizione (nome o CAP), p.e. <em>Roma, IT</em> or <em>00186,IT</em>." msgstr "Identificatore della tua posizione (nome o CAP), p.e. <em>Roma, IT</em> or <em>00186,IT</em>."
#: curweather.php:190 #: curweather.php:196
msgid "Units" msgid "Units"
msgstr "Unità" msgstr "Unità"
#: curweather.php:190 #: curweather.php:196
msgid "select if the temperature should be displayed in &deg;C or &deg;F" msgid "select if the temperature should be displayed in &deg;C or &deg;F"
msgstr "scegli se la temperatura deve essere mostrata in °C o in °F" msgstr "scegli se la temperatura deve essere mostrata in °C o in °F"
#: curweather.php:191 #: curweather.php:197
msgid "Show weather data" msgid "Show weather data"
msgstr "Mostra dati meteo" msgstr "Mostra dati meteo"
#: curweather.php:196 #: curweather.php:232
msgid "Current Weather Settings"
msgstr "Impostazioni Meteo"
#: curweather.php:227
msgid "Save Settings"
msgstr "Salva Impostazioni"
#: curweather.php:230
msgid "Caching Interval" msgid "Caching Interval"
msgstr "Intervallo di cache" msgstr "Intervallo di cache"
#: curweather.php:232 #: curweather.php:234
msgid "" msgid ""
"For how long should the weather data be cached? Choose according your " "For how long should the weather data be cached? Choose according your "
"OpenWeatherMap account type." "OpenWeatherMap account type."
msgstr "Per quanto tempo i dati meteo devono essere memorizzati? Scegli a seconda del tuo tipo di account su OpenWeatherMap." msgstr "Per quanto tempo i dati meteo devono essere memorizzati? Scegli a seconda del tuo tipo di account su OpenWeatherMap."
#: curweather.php:233 #: curweather.php:235
msgid "no cache" msgid "no cache"
msgstr "nessuna cache" msgstr "nessuna cache"
#: curweather.php:234 curweather.php:235 curweather.php:236 curweather.php:237 #: curweather.php:236 curweather.php:237 curweather.php:238 curweather.php:239
msgid "minutes" msgid "minutes"
msgstr "minuti" msgstr "minuti"
#: curweather.php:240 #: curweather.php:242
msgid "Your APPID" msgid "Your APPID"
msgstr "Il tuo APPID" msgstr "Il tuo APPID"
#: curweather.php:240 #: curweather.php:242
msgid "Your API key provided by OpenWeatherMap" msgid "Your API key provided by OpenWeatherMap"
msgstr "La tua chiave API da OpenWeatherMap" msgstr "La tua chiave API da OpenWeatherMap"

View file

@ -3,7 +3,7 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['Error fetching weather data. Error was: '] = 'Errore durante il recupero dei dati meteo. L\'errore è stato:'; $a->strings['Error fetching weather data. Error was: '] = 'Errore durante il recupero dei dati meteo. L\'errore è stato:';
$a->strings['Current Weather'] = 'Meteo'; $a->strings['Current Weather'] = 'Meteo';
@ -16,14 +16,14 @@ $a->strings['Show on map'] = 'Mostra sulla mappa';
$a->strings['There was a problem accessing the weather data. But have a look'] = 'C\'è stato un problema accedendo ai dati meteo, ma dai un\'occhiata'; $a->strings['There was a problem accessing the weather data. But have a look'] = 'C\'è stato un problema accedendo ai dati meteo, ma dai un\'occhiata';
$a->strings['at OpenWeatherMap'] = 'a OpenWeatherMap'; $a->strings['at OpenWeatherMap'] = 'a OpenWeatherMap';
$a->strings['No APPID found, please contact your admin to obtain one.'] = 'APPID non trovata, contatta il tuo amministratore per averne una.'; $a->strings['No APPID found, please contact your admin to obtain one.'] = 'APPID non trovata, contatta il tuo amministratore per averne una.';
$a->strings['Save Settings'] = 'Salva Impostazioni';
$a->strings['Settings'] = 'Impostazioni';
$a->strings['Enter either the name of your location or the zip code.'] = 'Inserisci il nome della tua posizione o il CAP'; $a->strings['Enter either the name of your location or the zip code.'] = 'Inserisci il nome della tua posizione o il CAP';
$a->strings['Your Location'] = 'La tua Posizione'; $a->strings['Your Location'] = 'La tua Posizione';
$a->strings['Identifier of your location (name or zip code), e.g. <em>Berlin,DE</em> or <em>14476,DE</em>.'] = 'Identificatore della tua posizione (nome o CAP), p.e. <em>Roma, IT</em> or <em>00186,IT</em>.'; $a->strings['Identifier of your location (name or zip code), e.g. <em>Berlin,DE</em> or <em>14476,DE</em>.'] = 'Identificatore della tua posizione (nome o CAP), p.e. <em>Roma, IT</em> or <em>00186,IT</em>.';
$a->strings['Units'] = 'Unità'; $a->strings['Units'] = 'Unità';
$a->strings['select if the temperature should be displayed in &deg;C or &deg;F'] = 'scegli se la temperatura deve essere mostrata in °C o in °F'; $a->strings['select if the temperature should be displayed in &deg;C or &deg;F'] = 'scegli se la temperatura deve essere mostrata in °C o in °F';
$a->strings['Show weather data'] = 'Mostra dati meteo'; $a->strings['Show weather data'] = 'Mostra dati meteo';
$a->strings['Current Weather Settings'] = 'Impostazioni Meteo';
$a->strings['Save Settings'] = 'Salva Impostazioni';
$a->strings['Caching Interval'] = 'Intervallo di cache'; $a->strings['Caching Interval'] = 'Intervallo di cache';
$a->strings['For how long should the weather data be cached? Choose according your OpenWeatherMap account type.'] = 'Per quanto tempo i dati meteo devono essere memorizzati? Scegli a seconda del tuo tipo di account su OpenWeatherMap.'; $a->strings['For how long should the weather data be cached? Choose according your OpenWeatherMap account type.'] = 'Per quanto tempo i dati meteo devono essere memorizzati? Scegli a seconda del tuo tipo di account su OpenWeatherMap.';
$a->strings['no cache'] = 'nessuna cache'; $a->strings['no cache'] = 'nessuna cache';

View file

@ -5,41 +5,45 @@
# #
# Translators: # Translators:
# fabrixxm <fabrix.xm@gmail.com>, 2014,2018 # fabrixxm <fabrix.xm@gmail.com>, 2014,2018
# Sylke Vicious <silkevicious@gmail.com>, 2020-2021,2023 # Sylke Vicious <silkevicious@gmail.com>, 2020-2021
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2014-06-22 11:41+0000\n" "PO-Revision-Date: 2021-02-16 12:55+0000\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2020-2021,2023\n" "Last-Translator: Sylke Vicious <silkevicious@gmail.com>\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: dwpost.php:43 #: dwpost.php:41
msgid "Post to Dreamwidth" msgid "Post to Dreamwidth"
msgstr "Invia a Dreamwidth" msgstr "Invia a Dreamwidth"
#: dwpost.php:63 #: dwpost.php:72 dwpost.php:76
msgid "Enable Dreamwidth Post Addon"
msgstr "Abilita il componente aggiuntivo di pubblicazione Dreamwidth"
#: dwpost.php:64
msgid "Dreamwidth username"
msgstr "Nome utente Dreamwidth"
#: dwpost.php:65
msgid "Dreamwidth password"
msgstr "Password Dreamwidth"
#: dwpost.php:66
msgid "Post to Dreamwidth by default"
msgstr "Pubblica su dreamwidth per impostazione predefinita"
#: dwpost.php:71
msgid "Dreamwidth Export" msgid "Dreamwidth Export"
msgstr "Esporta Dreamwidth" msgstr "Esporta Dreamwidth"
#: dwpost.php:80
msgid "Enable dreamwidth Post Addon"
msgstr "Abilita il componente aggiuntivo di invio a Dreamwidth"
#: dwpost.php:85
msgid "dreamwidth username"
msgstr "Nome utente Dreamwidth"
#: dwpost.php:90
msgid "dreamwidth password"
msgstr "password Dreamwidth"
#: dwpost.php:95
msgid "Post to dreamwidth by default"
msgstr "Invia sempre a Dreamwidth"
#: dwpost.php:100
msgid "Save Settings"
msgstr "Salva Impostazioni"

View file

@ -3,11 +3,12 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['Post to Dreamwidth'] = 'Invia a Dreamwidth'; $a->strings['Post to Dreamwidth'] = 'Invia a Dreamwidth';
$a->strings['Enable Dreamwidth Post Addon'] = 'Abilita il componente aggiuntivo di pubblicazione Dreamwidth';
$a->strings['Dreamwidth username'] = 'Nome utente Dreamwidth';
$a->strings['Dreamwidth password'] = 'Password Dreamwidth';
$a->strings['Post to Dreamwidth by default'] = 'Pubblica su dreamwidth per impostazione predefinita';
$a->strings['Dreamwidth Export'] = 'Esporta Dreamwidth'; $a->strings['Dreamwidth Export'] = 'Esporta Dreamwidth';
$a->strings['Enable dreamwidth Post Addon'] = 'Abilita il componente aggiuntivo di invio a Dreamwidth';
$a->strings['dreamwidth username'] = 'Nome utente Dreamwidth';
$a->strings['dreamwidth password'] = 'password Dreamwidth';
$a->strings['Post to dreamwidth by default'] = 'Invia sempre a Dreamwidth';
$a->strings['Save Settings'] = 'Salva Impostazioni';

View file

@ -8,6 +8,7 @@
* Note: Please use Group Directory instead * Note: Please use Group Directory instead
*/ */
use Friendica\App;
use Friendica\Content\Nav; use Friendica\Content\Nav;
use Friendica\Content\Pager; use Friendica\Content\Pager;
use Friendica\Content\Widget; use Friendica\Content\Widget;
@ -15,8 +16,8 @@ use Friendica\Core\Hook;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Profile;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Security\OpenWebAuth;
global $forumdirectory_search; global $forumdirectory_search;
@ -81,7 +82,7 @@ function forumdirectory_content()
$gdirpath = ''; $gdirpath = '';
$dirurl = DI::config()->get('system', 'directory'); $dirurl = DI::config()->get('system', 'directory');
if (strlen($dirurl)) { if (strlen($dirurl)) {
$gdirpath = OpenWebAuth::getZrlUrl($dirurl, true); $gdirpath = Profile::zrl($dirurl, true);
} }
$sql_extra = ''; $sql_extra = '';
@ -103,8 +104,8 @@ function forumdirectory_content()
$total = 0; $total = 0;
$cnt = DBA::fetchFirst("SELECT COUNT(*) AS `total` FROM `profile` $cnt = DBA::fetchFirst("SELECT COUNT(*) AS `total` FROM `profile`
INNER JOIN `user` ON `user`.`uid` = `profile`.`uid` INNER JOIN `user` ON `user`.`uid` = `profile`.`uid`
WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `user`.`page-flags` IN (?, ?) $sql_extra", WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `user`.`page-flags` = ? $sql_extra",
User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN); User::PAGE_FLAGS_COMMUNITY);
if (DBA::isResult($cnt)) { if (DBA::isResult($cnt)) {
$total = $cnt['total']; $total = $cnt['total'];
} }
@ -119,8 +120,8 @@ function forumdirectory_content()
`contact`.`addr`, `contact`.`url` FROM `profile` `contact`.`addr`, `contact`.`url` FROM `profile`
INNER JOIN `user` ON `user`.`uid` = `profile`.`uid` INNER JOIN `user` ON `user`.`uid` = `profile`.`uid`
INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid`
WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `user`.`page-flags` IN (?, ?) AND `contact`.`self` WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `user`.`page-flags` = ? AND `contact`.`self`
$sql_extra $order LIMIT $limit", User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN $sql_extra $order LIMIT $limit", User::PAGE_FLAGS_COMMUNITY
); );
if (DBA::isResult($r)) { if (DBA::isResult($r)) {

View file

@ -6,7 +6,6 @@
# Translators: # Translators:
# bob lebonche <lebonche@tutanota.com>, 2021 # bob lebonche <lebonche@tutanota.com>, 2021
# ButterflyOfFire, 2020 # ButterflyOfFire, 2020
# cracrayol, 2024
# Hypolite Petovan <hypolite@mrpetovan.com>, 2016 # Hypolite Petovan <hypolite@mrpetovan.com>, 2016
msgid "" msgid ""
msgstr "" msgstr ""
@ -14,8 +13,8 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2014-06-23 08:27+0000\n" "PO-Revision-Date: 2014-06-23 08:27+0000\n"
"Last-Translator: cracrayol, 2024\n" "Last-Translator: bob lebonche <lebonche@tutanota.com>, 2021\n"
"Language-Team: French (http://app.transifex.com/Friendica/friendica/language/fr/)\n" "Language-Team: French (http://www.transifex.com/Friendica/friendica/language/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@ -30,7 +29,7 @@ msgstr "Remplacer les coordonnées par le nom de la localité la plus proche dan
#: geonames.php:136 #: geonames.php:136
msgid "Enable Geonames Addon" msgid "Enable Geonames Addon"
msgstr "Activer l'extension Geonames" msgstr "Activer l'application complémentaire Geonames"
#: geonames.php:141 #: geonames.php:141
msgid "Geonames Settings" msgid "Geonames Settings"

View file

@ -6,5 +6,5 @@ function string_plural_select_fr($n){
if (($n == 0 || $n == 1)) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } if (($n == 0 || $n == 1)) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; }
}} }}
$a->strings['Replace numerical coordinates by the nearest populated location name in your posts.'] = 'Remplacer les coordonnées par le nom de la localité la plus proche dans votre publication.'; $a->strings['Replace numerical coordinates by the nearest populated location name in your posts.'] = 'Remplacer les coordonnées par le nom de la localité la plus proche dans votre publication.';
$a->strings['Enable Geonames Addon'] = 'Activer l\'extension Geonames'; $a->strings['Enable Geonames Addon'] = 'Activer l\'application complémentaire Geonames';
$a->strings['Geonames Settings'] = 'Paramètres Geonames'; $a->strings['Geonames Settings'] = 'Paramètres Geonames';

View file

@ -6,7 +6,6 @@
# Translators: # Translators:
# bob lebonche <lebonche@tutanota.com>, 2021 # bob lebonche <lebonche@tutanota.com>, 2021
# ButterflyOfFire, 2020 # ButterflyOfFire, 2020
# cracrayol, 2024
# Hypolite Petovan <hypolite@mrpetovan.com>, 2016 # Hypolite Petovan <hypolite@mrpetovan.com>, 2016
msgid "" msgid ""
msgstr "" msgstr ""
@ -14,8 +13,8 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2014-06-23 08:30+0000\n" "PO-Revision-Date: 2014-06-23 08:30+0000\n"
"Last-Translator: cracrayol, 2024\n" "Last-Translator: bob lebonche <lebonche@tutanota.com>, 2021\n"
"Language-Team: French (http://app.transifex.com/Friendica/friendica/language/fr/)\n" "Language-Team: French (http://www.transifex.com/Friendica/friendica/language/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@ -30,7 +29,7 @@ msgstr "Permettre le filtrage des notifications de commentaires par courriel sur
#: gnot.php:64 #: gnot.php:64
msgid "Enable this addon?" msgid "Enable this addon?"
msgstr "Activer cette extension ?" msgstr "Activer cette application complémentaire ?"
#: gnot.php:69 #: gnot.php:69
msgid "Gnot Settings" msgid "Gnot Settings"

View file

@ -6,6 +6,6 @@ function string_plural_select_fr($n){
if (($n == 0 || $n == 1)) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } if (($n == 0 || $n == 1)) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; }
}} }}
$a->strings['Allows threading of email comment notifications on Gmail and anonymising the subject line.'] = 'Permettre le filtrage des notifications de commentaires par courriel sur Gmail et l\'anonymisation de l\'objet.'; $a->strings['Allows threading of email comment notifications on Gmail and anonymising the subject line.'] = 'Permettre le filtrage des notifications de commentaires par courriel sur Gmail et l\'anonymisation de l\'objet.';
$a->strings['Enable this addon?'] = 'Activer cette extension ?'; $a->strings['Enable this addon?'] = 'Activer cette application complémentaire ?';
$a->strings['Gnot Settings'] = 'Paramètres Gnot'; $a->strings['Gnot Settings'] = 'Paramètres Gnot';
$a->strings['[Friendica:Notify] Comment to conversation #%d'] = '[Friendica:Notify] Commentaire vers conversation #%d'; $a->strings['[Friendica:Notify] Comment to conversation #%d'] = '[Friendica:Notify] Commentaire vers conversation #%d';

View file

@ -5,7 +5,6 @@
# #
# Translators: # Translators:
# bob lebonche <lebonche@tutanota.com>, 2021 # bob lebonche <lebonche@tutanota.com>, 2021
# cracrayol, 2024
# Marie Olive <lacellule101@gmail.com>, 2018 # Marie Olive <lacellule101@gmail.com>, 2018
# ea1cd8241cb389ffb6f92bc6891eff5d_dc12308 <70dced5587d47e18d88f9298024d96f8_93383>, 2015 # ea1cd8241cb389ffb6f92bc6891eff5d_dc12308 <70dced5587d47e18d88f9298024d96f8_93383>, 2015
msgid "" msgid ""
@ -14,8 +13,8 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-01 18:15+0100\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2014-06-23 08:33+0000\n" "PO-Revision-Date: 2014-06-23 08:33+0000\n"
"Last-Translator: cracrayol, 2024\n" "Last-Translator: bob lebonche <lebonche@tutanota.com>, 2021\n"
"Language-Team: French (http://app.transifex.com/Friendica/friendica/language/fr/)\n" "Language-Team: French (http://www.transifex.com/Friendica/friendica/language/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@ -51,7 +50,7 @@ msgid ""
"Libravatar addon is installed, too. Please disable Libravatar addon or this " "Libravatar addon is installed, too. Please disable Libravatar addon or this "
"Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if " "Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if "
"nothing was found at Libravatar." "nothing was found at Libravatar."
msgstr "L'extension Libravatar est aussi installée. Merci de désactiver l'extension Libravatar ou cette extension Gravatar. L'extension se repliera sur Gravatar si rien n'est trouvé dans Libravatar." msgstr "L'application complémentaire Libravatar est aussi installée. Merci de désactiver l'application complémentaire Libravatar ou cette application complémentaire Gravatar. L'application complémentaire se repliera sur Gravatar si rien n'est trouvé dans Libravatar."
#: gravatar.php:102 #: gravatar.php:102
msgid "Save Settings" msgid "Save Settings"

View file

@ -11,7 +11,7 @@ $a->strings['monster face'] = 'Face de monstre';
$a->strings['computer generated face'] = 'visage généré par ordinateur'; $a->strings['computer generated face'] = 'visage généré par ordinateur';
$a->strings['retro arcade style face'] = 'Face style retro arcade'; $a->strings['retro arcade style face'] = 'Face style retro arcade';
$a->strings['Information'] = 'Information'; $a->strings['Information'] = 'Information';
$a->strings['Libravatar addon is installed, too. Please disable Libravatar addon or this Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if nothing was found at Libravatar.'] = 'L\'extension Libravatar est aussi installée. Merci de désactiver l\'extension Libravatar ou cette extension Gravatar. L\'extension se repliera sur Gravatar si rien n\'est trouvé dans Libravatar.'; $a->strings['Libravatar addon is installed, too. Please disable Libravatar addon or this Gravatar addon.<br>The Libravatar addon will fall back to Gravatar if nothing was found at Libravatar.'] = 'L\'application complémentaire Libravatar est aussi installée. Merci de désactiver l\'application complémentaire Libravatar ou cette application complémentaire Gravatar. L\'application complémentaire se repliera sur Gravatar si rien n\'est trouvé dans Libravatar.';
$a->strings['Save Settings'] = 'Sauvegarder les paramètres.'; $a->strings['Save Settings'] = 'Sauvegarder les paramètres.';
$a->strings['Default avatar image'] = 'Image par défaut d\'avatar'; $a->strings['Default avatar image'] = 'Image par défaut d\'avatar';
$a->strings['Select default avatar image if none was found at Gravatar. See README'] = 'Sélectionner l\'avatar par défaut, si aucun n\'est trouvé sur Gravatar. Voir Lisezmoi.'; $a->strings['Select default avatar image if none was found at Gravatar. See README'] = 'Sélectionner l\'avatar par défaut, si aucun n\'est trouvé sur Gravatar. Voir Lisezmoi.';

View file

@ -13,8 +13,8 @@ use Friendica\Core\Hook;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Profile;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Security\OpenWebAuth;
global $groupdirectory_search; global $groupdirectory_search;
@ -79,7 +79,7 @@ function groupdirectory_content()
$gdirpath = ''; $gdirpath = '';
$dirurl = DI::config()->get('system', 'directory'); $dirurl = DI::config()->get('system', 'directory');
if (strlen($dirurl)) { if (strlen($dirurl)) {
$gdirpath = OpenWebAuth::getZrlUrl($dirurl, true); $gdirpath = Profile::zrl($dirurl, true);
} }
$sql_extra = ''; $sql_extra = '';
@ -101,8 +101,8 @@ function groupdirectory_content()
$total = 0; $total = 0;
$cnt = DBA::fetchFirst("SELECT COUNT(*) AS `total` FROM `profile` $cnt = DBA::fetchFirst("SELECT COUNT(*) AS `total` FROM `profile`
INNER JOIN `user` ON `user`.`uid` = `profile`.`uid` INNER JOIN `user` ON `user`.`uid` = `profile`.`uid`
WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `user`.`page-flags` IN (?, ?) $sql_extra", WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `user`.`page-flags` = ? $sql_extra",
User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN); User::PAGE_FLAGS_COMMUNITY);
if (DBA::isResult($cnt)) { if (DBA::isResult($cnt)) {
$total = $cnt['total']; $total = $cnt['total'];
} }
@ -117,8 +117,8 @@ function groupdirectory_content()
`contact`.`addr`, `contact`.`url` FROM `profile` `contact`.`addr`, `contact`.`url` FROM `profile`
INNER JOIN `user` ON `user`.`uid` = `profile`.`uid` INNER JOIN `user` ON `user`.`uid` = `profile`.`uid`
INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid`
WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `user`.`page-flags` IN (?, ?) AND `contact`.`self` WHERE $publish AND NOT `user`.`blocked` AND NOT `user`.`account_removed` AND `user`.`page-flags` = ? AND `contact`.`self`
$sql_extra $order LIMIT $limit", User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_COMM_MAN $sql_extra $order LIMIT $limit", User::PAGE_FLAGS_COMMUNITY
); );
if (DBA::isResult($r)) { if (DBA::isResult($r)) {

View file

@ -5,7 +5,6 @@
# #
# Translators: # Translators:
# bob lebonche <lebonche@tutanota.com>, 2021 # bob lebonche <lebonche@tutanota.com>, 2021
# cracrayol, 2024
# Hypolite Petovan <hypolite@mrpetovan.com>, 2016 # Hypolite Petovan <hypolite@mrpetovan.com>, 2016
msgid "" msgid ""
msgstr "" msgstr ""
@ -13,8 +12,8 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:17-0500\n" "POT-Creation-Date: 2021-11-21 19:17-0500\n"
"PO-Revision-Date: 2014-06-23 08:37+0000\n" "PO-Revision-Date: 2014-06-23 08:37+0000\n"
"Last-Translator: cracrayol, 2024\n" "Last-Translator: bob lebonche <lebonche@tutanota.com>, 2021\n"
"Language-Team: French (http://app.transifex.com/Friendica/friendica/language/fr/)\n" "Language-Team: French (http://www.transifex.com/Friendica/friendica/language/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@ -27,7 +26,7 @@ msgstr "Publier sur Insanejournal"
#: ijpost.php:61 #: ijpost.php:61
msgid "Enable InsaneJournal Post Addon" msgid "Enable InsaneJournal Post Addon"
msgstr "Activer l'extension InsaneJournal" msgstr "Activer l'application complémentaire InsaneJournalPost"
#: ijpost.php:62 #: ijpost.php:62
msgid "InsaneJournal username" msgid "InsaneJournal username"

View file

@ -6,7 +6,7 @@ function string_plural_select_fr($n){
if (($n == 0 || $n == 1)) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } if (($n == 0 || $n == 1)) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; }
}} }}
$a->strings['Post to Insanejournal'] = 'Publier sur Insanejournal'; $a->strings['Post to Insanejournal'] = 'Publier sur Insanejournal';
$a->strings['Enable InsaneJournal Post Addon'] = 'Activer l\'extension InsaneJournal'; $a->strings['Enable InsaneJournal Post Addon'] = 'Activer l\'application complémentaire InsaneJournalPost';
$a->strings['InsaneJournal username'] = 'Identifiant du InsaneJournal'; $a->strings['InsaneJournal username'] = 'Identifiant du InsaneJournal';
$a->strings['InsaneJournal password'] = 'Mot de passe du InsaneJournal'; $a->strings['InsaneJournal password'] = 'Mot de passe du InsaneJournal';
$a->strings['Post to InsaneJournal by default'] = 'Publier sur le InsaneJournal par défaut'; $a->strings['Post to InsaneJournal by default'] = 'Publier sur le InsaneJournal par défaut';

View file

@ -2,7 +2,7 @@
/* /*
* Name: invidious * Name: invidious
* Description: Replaces links to youtube.com to an invidious instance in all displays of postings on a node. * Description: Replaces links to youtube.com to an invidious instance in all displays of postings on a node.
* Version: 0.4 * Version: 0.3
* Author: Matthias Ebers <https://loma.ml/profile/feb> * Author: Matthias Ebers <https://loma.ml/profile/feb>
* Author: Michael Vogel <https://pirati.ca/profile/heluecht> * Author: Michael Vogel <https://pirati.ca/profile/heluecht>
* Status: Unsupported * Status: Unsupported
@ -93,11 +93,9 @@ function invidious_render(array &$b)
$original = $b['html']; $original = $b['html'];
$server = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'invidious', 'server', DI::config()->get('invidious', 'server', INVIDIOUS_DEFAULT)); $server = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'invidious', 'server', DI::config()->get('invidious', 'server', INVIDIOUS_DEFAULT));
$b['html'] = preg_replace("~https?://(?:www\.)?youtube\.com/watch\?v=(.*?)~ism", $server . '/watch?v=$1', $b['html']); $b['html'] = preg_replace("/https?:\/\/www.youtube.com\/watch\?v\=(.*?)/ism", $server . '/watch?v=$1', $b['html']);
$b['html'] = preg_replace("~https?://(?:www\.)?youtube\.com/embed/(.*?)~ism", $server . '/embed/$1', $b['html']); $b['html'] = preg_replace("/https?:\/\/www.youtube.com\/embed\/(.*?)/ism", $server . '/embed/$1', $b['html']);
$b['html'] = preg_replace("~https?://(?:www\.)?youtube\.com/shorts/(.*?)~ism", $server . '/shorts/$1', $b['html']); $b['html'] = preg_replace("/https?:\/\/www.youtube.com\/shorts\/(.*?)/ism", $server . '/shorts/$1', $b['html']);
$b['html'] = preg_replace ("/https?:\/\/music.youtube.com\/(.*?)/ism", $server . '/watch?v=$1', $b['html']);
$b['html'] = preg_replace ("/https?:\/\/m.youtube.com\/(.*?)/ism", $server . '/watch?v=$1', $b['html']);
$b['html'] = preg_replace("/https?:\/\/youtu.be\/(.*?)/ism", $server . '/watch?v=$1', $b['html']); $b['html'] = preg_replace("/https?:\/\/youtu.be\/(.*?)/ism", $server . '/watch?v=$1', $b['html']);
if ($original != $b['html']) { if ($original != $b['html']) {

View file

@ -7,11 +7,11 @@
* Maintainer: Hypolite Petovan <https://friendica.mrpetovan.com/profile/hypolite> * Maintainer: Hypolite Petovan <https://friendica.mrpetovan.com/profile/hypolite>
*/ */
use Friendica\App;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\DI; use Friendica\DI;
use Friendica\Util\Images;
use Friendica\Util\Strings; use Friendica\Util\Strings;
global $js_upload_jsonresponse; global $js_upload_jsonresponse;
@ -48,14 +48,7 @@ function js_upload_post_init(array &$b)
global $js_upload_result, $js_upload_jsonresponse; global $js_upload_result, $js_upload_jsonresponse;
// list of valid extensions // list of valid extensions
$allowedExtensions = []; $allowedExtensions = ['jpeg', 'gif', 'png', 'jpg'];
foreach (Images::IMAGETYPES as $type) {
$extension = image_type_to_extension($type, false);
if ($extension == 'jpeg') {
$allowedExtensions[] = 'jpg';
}
$allowedExtensions[] = $extension;
}
// max file size in bytes // max file size in bytes
$sizeLimit = Strings::getBytesFromShorthand(DI::config()->get('system', 'maximagesize')); $sizeLimit = Strings::getBytesFromShorthand(DI::config()->get('system', 'maximagesize'));
@ -85,6 +78,7 @@ function js_upload_post_file(array &$b)
$b['src'] = $result['path']; $b['src'] = $result['path'];
$b['filename'] = $result['filename']; $b['filename'] = $result['filename'];
$b['filesize'] = filesize($b['src']); $b['filesize'] = filesize($b['src']);
} }
function js_upload_post_end(int &$b) function js_upload_post_end(int &$b)
@ -185,11 +179,11 @@ class qqUploadedFileForm
class qqFileUploader class qqFileUploader
{ {
private $allowedExtensions; private $allowedExtensions = [];
private $sizeLimit; private $sizeLimit = 10485760;
private $file; private $file;
function __construct(array $allowedExtensions = [], $sizeLimit) function __construct(array $allowedExtensions = [], $sizeLimit = 10485760)
{ {
$allowedExtensions = array_map('strtolower', $allowedExtensions); $allowedExtensions = array_map('strtolower', $allowedExtensions);
@ -203,6 +197,7 @@ class qqFileUploader
} else { } else {
$this->file = false; $this->file = false;
} }
} }
/** /**
@ -220,9 +215,11 @@ class qqFileUploader
return ['error' => DI::l10n()->t('Uploaded file is empty')]; return ['error' => DI::l10n()->t('Uploaded file is empty')];
} }
// if ($size > $this->sizeLimit) { // if ($size > $this->sizeLimit) {
// return array('error' => DI::l10n()->t('Uploaded file is too large'));
// } // return array('error' => DI::l10n()->t('Uploaded file is too large'));
// }
$maximagesize = Strings::getBytesFromShorthand(DI::config()->get('system', 'maximagesize')); $maximagesize = Strings::getBytesFromShorthand(DI::config()->get('system', 'maximagesize'));

View file

@ -5,15 +5,14 @@
# #
# Translators: # Translators:
# bob lebonche <lebonche@tutanota.com>, 2021 # bob lebonche <lebonche@tutanota.com>, 2021
# cracrayol, 2024
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:14-0500\n" "POT-Creation-Date: 2021-11-21 19:14-0500\n"
"PO-Revision-Date: 2015-07-07 15:14+0000\n" "PO-Revision-Date: 2015-07-07 15:14+0000\n"
"Last-Translator: cracrayol, 2024\n" "Last-Translator: bob lebonche <lebonche@tutanota.com>, 2021\n"
"Language-Team: French (http://app.transifex.com/Friendica/friendica/language/fr/)\n" "Language-Team: French (http://www.transifex.com/Friendica/friendica/language/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@ -22,7 +21,7 @@ msgstr ""
#: krynn.php:127 #: krynn.php:127
msgid "Enable Krynn Addon" msgid "Enable Krynn Addon"
msgstr "Activer l'extension Krynn" msgstr "Activer l'application complémentaire Krynn"
#: krynn.php:132 #: krynn.php:132
msgid "Krynn Settings" msgid "Krynn Settings"

View file

@ -5,5 +5,5 @@ function string_plural_select_fr($n){
$n = intval($n); $n = intval($n);
if (($n == 0 || $n == 1)) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } if (($n == 0 || $n == 1)) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; }
}} }}
$a->strings['Enable Krynn Addon'] = 'Activer l\'extension Krynn'; $a->strings['Enable Krynn Addon'] = 'Activer l\'application complémentaire Krynn';
$a->strings['Krynn Settings'] = 'Paramètres de Krynn'; $a->strings['Krynn Settings'] = 'Paramètres de Krynn';

View file

@ -5,41 +5,45 @@
# #
# Translators: # Translators:
# fabrixxm <fabrix.xm@gmail.com>, 2014-2015,2018 # fabrixxm <fabrix.xm@gmail.com>, 2014-2015,2018
# Sylke Vicious <silkevicious@gmail.com>, 2021,2023 # Sylke Vicious <silkevicious@gmail.com>, 2021
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-03 15:49-0400\n" "POT-Creation-Date: 2021-02-01 18:15+0100\n"
"PO-Revision-Date: 2014-06-23 09:44+0000\n" "PO-Revision-Date: 2021-02-16 12:47+0000\n"
"Last-Translator: Sylke Vicious <silkevicious@gmail.com>, 2021,2023\n" "Last-Translator: Sylke Vicious <silkevicious@gmail.com>\n"
"Language-Team: Italian (http://app.transifex.com/Friendica/friendica/language/it/)\n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: libertree.php:39 #: libertree.php:37
msgid "Post to libertree" msgid "Post to libertree"
msgstr "Invia a Libertree" msgstr "Invia a Libertree"
#: libertree.php:59 #: libertree.php:71 libertree.php:75
msgid "libertree Export"
msgstr "Esporta libertree"
#: libertree.php:79
msgid "Enable Libertree Post Addon" msgid "Enable Libertree Post Addon"
msgstr "Abilita il componente aggiuntivo di invio a Libertree" msgstr "Abilita il componente aggiuntivo di invio a Libertree"
#: libertree.php:60 #: libertree.php:84
msgid "Libertree site URL"
msgstr "Indirizzo sito Libertree"
#: libertree.php:61
msgid "Libertree API token" msgid "Libertree API token"
msgstr "Token API Libertree" msgstr "Token API Libertree"
#: libertree.php:62 #: libertree.php:89
msgid "Libertree site URL"
msgstr "Indirizzo sito Libertree"
#: libertree.php:94
msgid "Post to Libertree by default" msgid "Post to Libertree by default"
msgstr "Invia sempre a Libertree" msgstr "Invia sempre a Libertree"
#: libertree.php:67 #: libertree.php:100
msgid "Libertree Export" msgid "Save Settings"
msgstr "Esporta Libertree" msgstr "Salva Impostazioni"

View file

@ -3,11 +3,12 @@
if(! function_exists("string_plural_select_it")) { if(! function_exists("string_plural_select_it")) {
function string_plural_select_it($n){ function string_plural_select_it($n){
$n = intval($n); $n = intval($n);
if ($n == 1) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } return intval($n != 1);
}} }}
$a->strings['Post to libertree'] = 'Invia a Libertree'; $a->strings['Post to libertree'] = 'Invia a Libertree';
$a->strings['libertree Export'] = 'Esporta libertree';
$a->strings['Enable Libertree Post Addon'] = 'Abilita il componente aggiuntivo di invio a Libertree'; $a->strings['Enable Libertree Post Addon'] = 'Abilita il componente aggiuntivo di invio a Libertree';
$a->strings['Libertree site URL'] = 'Indirizzo sito Libertree';
$a->strings['Libertree API token'] = 'Token API Libertree'; $a->strings['Libertree API token'] = 'Token API Libertree';
$a->strings['Libertree site URL'] = 'Indirizzo sito Libertree';
$a->strings['Post to Libertree by default'] = 'Invia sempre a Libertree'; $a->strings['Post to Libertree by default'] = 'Invia sempre a Libertree';
$a->strings['Libertree Export'] = 'Esporta Libertree'; $a->strings['Save Settings'] = 'Salva Impostazioni';

View file

@ -5,18 +5,16 @@
# #
# Translators: # Translators:
# Andreas H., 2014 # Andreas H., 2014
# foss <oss@disr.it>, 2022
# Raroun, 2023
# Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018 # Tobias Diekershoff <tobias.diekershoff@gmx.net>, 2018
# Ulf Rompe <transifex.com@rompe.org>, 2019 # Ulf Rompe <transifex.com@rompe.org>, 2019
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:15-0500\n" "POT-Creation-Date: 2018-03-11 19:13+0100\n"
"PO-Revision-Date: 2014-06-23 09:54+0000\n" "PO-Revision-Date: 2019-02-18 15:05+0000\n"
"Last-Translator: Raroun, 2023\n" "Last-Translator: Ulf Rompe <transifex.com@rompe.org>\n"
"Language-Team: German (http://app.transifex.com/Friendica/friendica/language/de/)\n" "Language-Team: German (http://www.transifex.com/Friendica/friendica/language/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
@ -31,76 +29,72 @@ msgstr "Absender"
msgid "Email address that stream items will appear to be from." msgid "Email address that stream items will appear to be from."
msgstr "E-Mail-Adresse, die in hochgeladenen Artikeln erscheint." msgstr "E-Mail-Adresse, die in hochgeladenen Artikeln erscheint."
#: mailstream.php:82 #: mailstream.php:82 mailstream.php:380
msgid "Save Settings" msgid "Save Settings"
msgstr "Einstellungen speichern" msgstr "Einstellungen speichern"
#: mailstream.php:311 #: mailstream.php:223
msgid "Re:" msgid "Re:"
msgstr "Re:" msgstr "Re:"
#: mailstream.php:324 mailstream.php:327 #: mailstream.php:231
msgid "Friendica post" msgid "Friendica post"
msgstr "Friendica-Veröffentlichung" msgstr "Friendica-Veröffentlichung"
#: mailstream.php:330 #: mailstream.php:234
msgid "Diaspora post" msgid "Diaspora post"
msgstr "Diaspora-Veröffentlichung" msgstr "Diaspora-Veröffentlichung"
#: mailstream.php:340 #: mailstream.php:244
msgid "Feed item" msgid "Feed item"
msgstr "Artikel-Feed" msgstr "Artikel-Feed"
#: mailstream.php:343 #: mailstream.php:247
msgid "Email" msgid "Email"
msgstr "E-Mail" msgstr "E-Mail"
#: mailstream.php:345 #: mailstream.php:249
msgid "Friendica Item" msgid "Friendica Item"
msgstr "Friendica-Artikel" msgstr "Friendica-Artikel"
#: mailstream.php:419 #: mailstream.php:293
msgid "Upstream" msgid "Upstream"
msgstr "Upstream" msgstr "Upstream"
#: mailstream.php:420 #: mailstream.php:294
msgid "URI"
msgstr "URI"
#: mailstream.php:421
msgid "Local" msgid "Local"
msgstr "Lokal" msgstr "Lokal"
#: mailstream.php:499 #: mailstream.php:362
msgid "Enabled" msgid "Enabled"
msgstr "Aktiv" msgstr "eingeschaltet"
#: mailstream.php:504 #: mailstream.php:366
msgid "Email Address" msgid "Email Address"
msgstr "E-Mail-Adresse" msgstr "E-Mail-Adresse"
#: mailstream.php:506 #: mailstream.php:368
msgid "Leave blank to use your account email address" msgid "Leave blank to use your account email address"
msgstr "Leer lassen für deine Konto-E-Mail-Addresse" msgstr "Leer lassen für deine Konto-E-Mail-Addresse"
#: mailstream.php:510 #: mailstream.php:371
msgid "Exclude Likes" msgid "Exclude Likes"
msgstr "Likes ignorieren" msgstr "Likes ignorieren"
#: mailstream.php:512 #: mailstream.php:373
msgid "Check this to omit mailing \"Like\" notifications" msgid "Check this to omit mailing \"Like\" notifications"
msgstr "Diese Option verhindert das Versenden von \"Like\"-Benachrichtigungen per E-Mail." msgstr "Diese Option verhindert das Versenden von \"Like\"-Benachrichtigungen per E-Mail."
#: mailstream.php:516 #: mailstream.php:376
msgid "Attach Images" msgid "Attach Images"
msgstr "Bilder anhängen" msgstr "Bilder anhängen"
#: mailstream.php:518 #: mailstream.php:378
msgid "" msgid ""
"Download images in posts and attach them to the email. Useful for reading " "Download images in posts and attach them to the email. Useful for reading "
"email while offline." "email while offline."
msgstr "Sollen Bilder, die im Beitrag eingebettet sind, als Dateianhang in den E-Mails verschickt werden?" msgstr "Sollen Bilder, die im Beitrag eingebettet sind, als Dateianhang in den E-Mails verschickt werden?"
#: mailstream.php:525 #: mailstream.php:379
msgid "Mail Stream Settings" msgid "Mail Stream Settings"
msgstr "Mail-Nachrichten-Einstellungen" msgstr "Mail-Nachrichten-Einstellungen"

View file

@ -15,9 +15,8 @@ $a->strings['Feed item'] = 'Artikel-Feed';
$a->strings['Email'] = 'E-Mail'; $a->strings['Email'] = 'E-Mail';
$a->strings['Friendica Item'] = 'Friendica-Artikel'; $a->strings['Friendica Item'] = 'Friendica-Artikel';
$a->strings['Upstream'] = 'Upstream'; $a->strings['Upstream'] = 'Upstream';
$a->strings['URI'] = 'URI';
$a->strings['Local'] = 'Lokal'; $a->strings['Local'] = 'Lokal';
$a->strings['Enabled'] = 'Aktiv'; $a->strings['Enabled'] = 'eingeschaltet';
$a->strings['Email Address'] = 'E-Mail-Adresse'; $a->strings['Email Address'] = 'E-Mail-Adresse';
$a->strings['Leave blank to use your account email address'] = 'Leer lassen für deine Konto-E-Mail-Addresse'; $a->strings['Leave blank to use your account email address'] = 'Leer lassen für deine Konto-E-Mail-Addresse';
$a->strings['Exclude Likes'] = 'Likes ignorieren'; $a->strings['Exclude Likes'] = 'Likes ignorieren';

View file

@ -6,6 +6,7 @@
* Author: Matthew Exon <http://mat.exon.name> * Author: Matthew Exon <http://mat.exon.name>
*/ */
use Friendica\App;
use Friendica\Content\Text\BBCode; use Friendica\Content\Text\BBCode;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Logger; use Friendica\Core\Logger;
@ -15,11 +16,12 @@ use Friendica\Core\Worker;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Model\Post; use Friendica\Model\Post;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Util\DateTimeFormat;
/** /**
* Sets up the addon hooks and the database table * Sets up the addon hooks and the database table
@ -32,7 +34,26 @@ function mailstream_install()
Hook::register('post_remote_end', 'addon/mailstream/mailstream.php', 'mailstream_post_hook'); Hook::register('post_remote_end', 'addon/mailstream/mailstream.php', 'mailstream_post_hook');
Hook::register('mailstream_send_hook', 'addon/mailstream/mailstream.php', 'mailstream_send_hook'); Hook::register('mailstream_send_hook', 'addon/mailstream/mailstream.php', 'mailstream_send_hook');
Logger::info("installed mailstream"); Logger::info("mailstream: installed");
}
/**
* Enforces that mailstream_install has set up the current version
*/
function mailstream_check_version()
{
if (!is_null(DI::config()->get('mailstream', 'dbversion'))) {
DI::config()->delete('mailstream', 'dbversion');
Logger::info("mailstream_check_version: old version detected, reinstalling");
mailstream_install();
Hook::loadHooks();
Hook::add(
'mailstream_convert_table_entries',
'addon/mailstream/mailstream.php',
'mailstream_convert_table_entries'
);
Hook::fork(Worker::PRIORITY_LOW, 'mailstream_convert_table_entries');
}
} }
/** /**
@ -51,12 +72,10 @@ function mailstream_addon_admin(string &$o)
{ {
$frommail = DI::config()->get('mailstream', 'frommail'); $frommail = DI::config()->get('mailstream', 'frommail');
$template = Renderer::getMarkupTemplate('admin.tpl', 'addon/mailstream/'); $template = Renderer::getMarkupTemplate('admin.tpl', 'addon/mailstream/');
$config = [ $config = ['frommail',
'frommail',
DI::l10n()->t('From Address'), DI::l10n()->t('From Address'),
$frommail, $frommail,
DI::l10n()->t('Email address that stream items will appear to be from.') DI::l10n()->t('Email address that stream items will appear to be from.')];
];
$o .= Renderer::replaceMacros($template, [ $o .= Renderer::replaceMacros($template, [
'$frommail' => $config, '$frommail' => $config,
'$submit' => DI::l10n()->t('Save Settings') '$submit' => DI::l10n()->t('Save Settings')
@ -86,7 +105,7 @@ function mailstream_generate_id(string $uri): string
$host = DI::baseUrl()->getHost(); $host = DI::baseUrl()->getHost();
$resource = hash('md5', $uri); $resource = hash('md5', $uri);
$message_id = "<" . $resource . "@" . $host . ">"; $message_id = "<" . $resource . "@" . $host . ">";
Logger::debug('generated message ID', ['id' => $message_id, 'uri' => $uri]); Logger::debug('mailstream: Generated message ID ' . $message_id . ' for URI ' . $uri);
return $message_id; return $message_id;
} }
@ -95,20 +114,20 @@ function mailstream_send_hook(array $data)
$criteria = array('uid' => $data['uid'], 'contact-id' => $data['contact-id'], 'uri' => $data['uri']); $criteria = array('uid' => $data['uid'], 'contact-id' => $data['contact-id'], 'uri' => $data['uri']);
$item = Post::selectFirst([], $criteria); $item = Post::selectFirst([], $criteria);
if (empty($item)) { if (empty($item)) {
Logger::error('could not find item'); Logger::error('mailstream_send_hook could not find item');
return; return;
} }
$user = User::getById($item['uid']); $user = User::getById($item['uid']);
if (empty($user)) { if (empty($user)) {
Logger::error('could not find user', ['uid' => $item['uid']]); Logger::error('mailstream_send_hook could not fund user', ['uid' => $item['uid']]);
return; return;
} }
if (!mailstream_send($data['message_id'], $item, $user)) { if (!mailstream_send($data['message_id'], $item, $user)) {
Logger::debug('send failed, will retry', $data); Logger::debug('mailstream_send_hook send failed, will retry', $data);
if (!Worker::defer()) { if (!Worker::defer()) {
Logger::error('failed and could not defer', $data); Logger::error('mailstream_send_hook failed and could not defer', $data);
} }
} }
} }
@ -123,33 +142,35 @@ function mailstream_send_hook(array $data)
*/ */
function mailstream_post_hook(array &$item) function mailstream_post_hook(array &$item)
{ {
if ($item['uid'] === 0) { mailstream_check_version();
Logger::debug('mailstream: root user, skipping item ' . $item['id']);
return;
}
if (!DI::pConfig()->get($item['uid'], 'mailstream', 'enabled')) { if (!DI::pConfig()->get($item['uid'], 'mailstream', 'enabled')) {
Logger::debug('mailstream: not enabled.', ['item' => $item['id'], ' uid ' => $item['uid']]); Logger::debug('mailstream: not enabled.', ['item' => $item['id'], ' uid ' => $item['uid']]);
return; return;
} }
if (!$item['uid']) {
Logger::debug('mailstream: no uid for item ' . $item['id']);
return;
}
if (!$item['contact-id']) { if (!$item['contact-id']) {
Logger::debug('no contact-id', ['item' => $item['id']]); Logger::debug('mailstream: no contact-id for item ' . $item['id']);
return; return;
} }
if (!$item['uri']) { if (!$item['uri']) {
Logger::debug('no uri', ['item' => $item['id']]); Logger::debug('mailstream: no uri for item ' . $item['id']);
return; return;
} }
if ($item['verb'] == Activity::ANNOUNCE) { if ($item['verb'] == Activity::ANNOUNCE) {
Logger::debug('ignoring announce', ['item' => $item['id']]); Logger::debug('mailstream: announce item ', ['item' => $item['id']]);
return; return;
} }
if (DI::pConfig()->get($item['uid'], 'mailstream', 'nolikes')) { if (DI::pConfig()->get($item['uid'], 'mailstream', 'nolikes')) {
if ($item['verb'] == Activity::LIKE) { if ($item['verb'] == Activity::LIKE) {
Logger::debug('ignoring like', ['item' => $item['id']]); Logger::debug('mailstream: like item ' . $item['id']);
return; return;
} }
if ($item['verb'] == Activity::DISLIKE) { if ($item['verb'] == Activity::DISLIKE) {
Logger::debug('ignoring dislike', ['item' => $item['id']]); Logger::debug('mailstream: dislike item ' . $item['id']);
return; return;
} }
} }
@ -198,17 +219,9 @@ function mailstream_do_images(array &$item, array &$attachments)
$cookiejar = tempnam(System::getTempPath(), 'cookiejar-mailstream-'); $cookiejar = tempnam(System::getTempPath(), 'cookiejar-mailstream-');
try { try {
$curlResult = DI::httpClient()->get($url, HttpClientAccept::DEFAULT, [HttpClientOptions::COOKIEJAR => $cookiejar]); $curlResult = DI::httpClient()->fetchFull($url, HttpClientAccept::DEFAULT, 0, $cookiejar);
if (!$curlResult->isSuccess()) {
Logger::debug('mailstream: fetch image url failed', [
'url' => $url,
'item_id' => $item['id'],
'return_code' => $curlResult->getReturnCode()
]);
continue;
}
} catch (InvalidArgumentException $e) { } catch (InvalidArgumentException $e) {
Logger::error('exception fetching url', ['url' => $url, 'item_id' => $item['id']]); Logger::error('mailstream_do_images exception fetching url', ['url' => $url, 'item_id' => $item['id']]);
continue; continue;
} }
$attachments[$url] = [ $attachments[$url] = [
@ -309,12 +322,13 @@ function mailstream_subject(array $item): string
} }
$contact = Contact::selectFirst([], ['id' => $item['contact-id'], 'uid' => $item['uid']]); $contact = Contact::selectFirst([], ['id' => $item['contact-id'], 'uid' => $item['uid']]);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
Logger::error('no contact', [ Logger::error(
'item' => $item['id'], 'mailstream_subject no contact for item',
['id' => $item['id'],
'plink' => $item['plink'], 'plink' => $item['plink'],
'contact id' => $item['contact-id'], 'contact id' => $item['contact-id'],
'uid' => $item['uid'] 'uid' => $item['uid']]
]); );
return DI::l10n()->t("Friendica post"); return DI::l10n()->t("Friendica post");
} }
if ($contact['network'] === 'dfrn') { if ($contact['network'] === 'dfrn') {
@ -351,20 +365,21 @@ function mailstream_subject(array $item): string
function mailstream_send(string $message_id, array $item, array $user): bool function mailstream_send(string $message_id, array $item, array $user): bool
{ {
if (!is_array($item)) { if (!is_array($item)) {
Logger::error('item is empty', ['message_id' => $message_id]); Logger::error('mailstream_send item is empty', ['message_id' => $message_id]);
return false; return false;
} }
if (!$item['visible']) { if (!$item['visible']) {
Logger::debug('item not yet visible', ['item uri' => $item['uri']]); Logger::debug('mailstream_send item not yet visible', ['item uri' => $item['uri']]);
return false; return false;
} }
if (!$message_id) { if (!$message_id) {
Logger::error('no message ID supplied', ['item uri' => $item['uri'], 'user email' => $user['email']]); Logger::error('mailstream_send no message ID supplied', ['item uri' => $item['uri'],
'user email' => $user['email']]);
return true; return true;
} }
require_once(dirname(__file__) . '/phpmailer/class.phpmailer.php'); require_once (dirname(__file__) . '/phpmailer/class.phpmailer.php');
$item['body'] = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']); $item['body'] = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']);
@ -412,21 +427,18 @@ function mailstream_send(string $message_id, array $item, array $user): bool
'$upstream' => DI::l10n()->t('Upstream'), '$upstream' => DI::l10n()->t('Upstream'),
'$uri' => DI::l10n()->t('URI'), '$uri' => DI::l10n()->t('URI'),
'$local' => DI::l10n()->t('Local'), '$local' => DI::l10n()->t('Local'),
'$item' => $item '$item' => $item]);
]);
$mail->Body = mailstream_html_wrap($mail->Body); $mail->Body = mailstream_html_wrap($mail->Body);
if (!$mail->Send()) { if (!$mail->Send()) {
throw new Exception($mail->ErrorInfo); throw new Exception($mail->ErrorInfo);
} }
Logger::debug('sent message', [ Logger::debug('mailstream_send sent message', ['message ID' => $mail->MessageID,
'message ID' => $mail->MessageID,
'subject' => $mail->Subject, 'subject' => $mail->Subject,
'address' => $address 'address' => $address]);
]);
} catch (phpmailerException $e) { } catch (phpmailerException $e) {
Logger::debug('PHPMailer exception sending message', ['id' => $message_id, 'error' => $e->errorMessage()]); Logger::debug('mailstream_send PHPMailer exception sending message ' . $message_id . ': ' . $e->errorMessage());
} catch (Exception $e) { } catch (Exception $e) {
Logger::debug('exception sending message', ['id' => $message_id, 'error' => $e->getMessage()]); Logger::debug('mailstream_send exception sending message ' . $message_id . ': ' . $e->getMessage());
} }
return true; return true;
@ -450,6 +462,29 @@ function mailstream_html_wrap(string &$text)
return $text; return $text;
} }
/**
* Convert v1 mailstream table entries to v2 workerqueue items
*/
function mailstream_convert_table_entries()
{
$ms_item_ids = DBA::selectToArray('mailstream_item', [], ['message-id', 'uri', 'uid', 'contact-id'], ["`mailstream_item`.`completed` IS NULL"]);
Logger::debug('mailstream_convert_table_entries processing ' . count($ms_item_ids) . ' items');
foreach ($ms_item_ids as $ms_item_id) {
$send_hook_data = array('uid' => $ms_item_id['uid'],
'contact-id' => $ms_item_id['contact-id'],
'uri' => $ms_item_id['uri'],
'message_id' => $ms_item_id['message-id'],
'tries' => 0);
if (!$ms_item_id['message-id'] || !strlen($ms_item_id['message-id'])) {
Logger::info('mailstream_convert_table_entries: item has no message-id.', ['item' => $ms_item_id['id'], 'uri' => $ms_item_id['uri']]);
continue;
}
Logger::info('mailstream_convert_table_entries: convert item to workerqueue', $send_hook_data);
Hook::fork(Worker::PRIORITY_LOW, 'mailstream_send_hook', $send_hook_data);
}
DBA::e('DROP TABLE `mailstream_item`');
}
/** /**
* Form for configuring mailstream features for a user * Form for configuring mailstream features for a user
* *

View file

@ -1,4 +1,5 @@
<?php <?php
/* /*
* Name: Mastodon Custom Emojis * Name: Mastodon Custom Emojis
* Description: Replace emojis shortcodes in Mastodon posts with their originating server custom emojis images. * Description: Replace emojis shortcodes in Mastodon posts with their originating server custom emojis images.
@ -8,11 +9,13 @@
* Status: Unsupported * Status: Unsupported
*/ */
use Friendica\App;
use Friendica\Content\Smilies; use Friendica\Content\Smilies;
use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Protocol; use Friendica\Core\Protocol;
use Friendica\DI; use Friendica\DI;
use Friendica\Util\Proxy as ProxyUtils;
function mastodoncustomemojis_install() function mastodoncustomemojis_install()
{ {
@ -76,7 +79,7 @@ function mastodoncustomemojis_fetch_custom_emojis_for_url($api_base_url)
$api_url = $api_base_url . '/api/v1/custom_emojis'; $api_url = $api_base_url . '/api/v1/custom_emojis';
$fetchResult = DI::httpClient()->get($api_url); $fetchResult = DI::httpClient()->fetchFull($api_url);
if ($fetchResult->isSuccess()) { if ($fetchResult->isSuccess()) {
$emojis_array = json_decode($fetchResult->getBodyString(), true); $emojis_array = json_decode($fetchResult->getBodyString(), true);
@ -85,7 +88,7 @@ function mastodoncustomemojis_fetch_custom_emojis_for_url($api_base_url)
foreach ($emojis_array as $emoji) { foreach ($emojis_array as $emoji) {
if (!empty($emoji['shortcode']) && !empty($emoji['static_url'])) { if (!empty($emoji['shortcode']) && !empty($emoji['static_url'])) {
$return['texts'][] = ':' . $emoji['shortcode'] . ':'; $return['texts'][] = ':' . $emoji['shortcode'] . ':';
$return['icons'][] = '<img class="emoji mastodon" src="' . $emoji['static_url'] . '" alt=":' . $emoji['shortcode'] . ':" title=":' . $emoji['shortcode'] . ':"/>'; $return['icons'][] = '<img class="emoji mastodon" src="' . ProxyUtils::proxifyUrl($emoji['static_url']) . '" alt=":' . $emoji['shortcode'] . ':" title=":' . $emoji['shortcode'] . ':"/>';
} }
} }
} }

View file

@ -4,7 +4,7 @@
# #
# #
# Translators: # Translators:
# cracrayol, 2023-2024 # Florent C., 2023
# Nicolas Derive, 2022-2023 # Nicolas Derive, 2022-2023
# StefOfficiel <pichard.stephane@free.fr>, 2015 # StefOfficiel <pichard.stephane@free.fr>, 2015
# Vincent Vindarel <vindarel@mailz.org>, 2018 # Vincent Vindarel <vindarel@mailz.org>, 2018
@ -14,7 +14,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-12-10 14:42-0500\n" "POT-Creation-Date: 2022-12-10 14:42-0500\n"
"PO-Revision-Date: 2014-06-23 10:34+0000\n" "PO-Revision-Date: 2014-06-23 10:34+0000\n"
"Last-Translator: cracrayol, 2023-2024\n" "Last-Translator: Florent C., 2023\n"
"Language-Team: French (http://app.transifex.com/Friendica/friendica/language/fr/)\n" "Language-Team: French (http://app.transifex.com/Friendica/friendica/language/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -42,7 +42,7 @@ msgstr "Liste de mots-clés - séparés par des virgules - à cacher"
msgid "" msgid ""
"Use /expression/ to provide regular expressions, #tag to specfically match " "Use /expression/ to provide regular expressions, #tag to specfically match "
"hashtags (case-insensitive), or regular words (case-sensitive)" "hashtags (case-insensitive), or regular words (case-sensitive)"
msgstr "Utiliser /expression/ pour fournir des expressions régulières, #etiquette pour correspondre à une étiquette (insensible à la casse), ou des mots classiques (sensible à la casse)" msgstr "Utiliser /expression/ pour fournir des expressions régulières, #tag pour correspondre à un tag (insensible à la casse), ou des mots classiques (sensible à la casse)"
#: nsfw.php:72 #: nsfw.php:72
msgid "Content Filter (NSFW and more)" msgid "Content Filter (NSFW and more)"

View file

@ -8,7 +8,7 @@ function string_plural_select_fr($n){
$a->strings['This addon searches for specified words/text in posts and collapses them. It can be used to filter content tagged with for instance #NSFW that may be deemed inappropriate at certain times or places, such as being at work. It is also useful for hiding irrelevant or annoying content from direct view.'] = 'Cette extension recherche des mots/textes spécifiés dans les publications et les masque. Elle peut être utilisée pour filtrer le contenu étiqueté par exemple avec #NSFW qui peut être considéré comme inapproprié à certains moments ou endroits, comme par exemple au travail. Elle est aussi utile pour cacher du contenu non pertinent ou ennuyeux d\'une vue directe.'; $a->strings['This addon searches for specified words/text in posts and collapses them. It can be used to filter content tagged with for instance #NSFW that may be deemed inappropriate at certain times or places, such as being at work. It is also useful for hiding irrelevant or annoying content from direct view.'] = 'Cette extension recherche des mots/textes spécifiés dans les publications et les masque. Elle peut être utilisée pour filtrer le contenu étiqueté par exemple avec #NSFW qui peut être considéré comme inapproprié à certains moments ou endroits, comme par exemple au travail. Elle est aussi utile pour cacher du contenu non pertinent ou ennuyeux d\'une vue directe.';
$a->strings['Enable Content filter'] = 'Activer le filtrage de contenu'; $a->strings['Enable Content filter'] = 'Activer le filtrage de contenu';
$a->strings['Comma separated list of keywords to hide'] = 'Liste de mots-clés - séparés par des virgules - à cacher'; $a->strings['Comma separated list of keywords to hide'] = 'Liste de mots-clés - séparés par des virgules - à cacher';
$a->strings['Use /expression/ to provide regular expressions, #tag to specfically match hashtags (case-insensitive), or regular words (case-sensitive)'] = 'Utiliser /expression/ pour fournir des expressions régulières, #etiquette pour correspondre à une étiquette (insensible à la casse), ou des mots classiques (sensible à la casse)'; $a->strings['Use /expression/ to provide regular expressions, #tag to specfically match hashtags (case-insensitive), or regular words (case-sensitive)'] = 'Utiliser /expression/ pour fournir des expressions régulières, #tag pour correspondre à un tag (insensible à la casse), ou des mots classiques (sensible à la casse)';
$a->strings['Content Filter (NSFW and more)'] = 'Filtre de contenu (NSFW et autres)'; $a->strings['Content Filter (NSFW and more)'] = 'Filtre de contenu (NSFW et autres)';
$a->strings['Regular expression "%s" fails to compile'] = 'La compilation de l\'expression régulière "%s" a échoué'; $a->strings['Regular expression "%s" fails to compile'] = 'La compilation de l\'expression régulière "%s" a échoué';
$a->strings['Filtered tag: %s'] = 'Tag filtré : %s'; $a->strings['Filtered tag: %s'] = 'Tag filtré : %s';

View file

@ -1,213 +0,0 @@
<?php
namespace Friendica\Addon\ratioed;
use Friendica\Content\Pager;
use Friendica\Core\Logger;
use Friendica\Core\Renderer;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\User;
use Friendica\Module\Moderation\Users\Active;
/**
* This class implements the "Behaviour" panel in Moderation/Users
*/
class RatioedPanel extends Active
{
protected function content(array $request = []): string
{
Active::content();
if (isset(DI::args()->getArgv()[1]) and DI::args()->getArgv()[1] === 'help') {
$template = Renderer::getMarkupTemplate('/help.tpl', 'addon/ratioed/');
return Renderer::replaceMacros($template, array('$config' => DI::baseUrl() . '/settings/addon'));
}
$action = $this->parameters['action'] ?? '';
$uid = $this->parameters['uid'] ?? 0;
if ($uid) {
$user = User::getById($uid, ['username', 'blocked']);
if (!$user) {
$this->systemMessages->addNotice($this->t('User not found'));
$this->baseUrl->redirect('moderation/users');
}
}
switch ($action) {
case 'delete':
if ($this->session->getLocalUserId() != $uid) {
self::checkFormSecurityTokenRedirectOnError('moderation/users/active', 'moderation_users_active', 't');
// delete user
User::remove($uid);
$this->systemMessages->addNotice($this->t('User "%s" deleted', $user['username']));
} else {
$this->systemMessages->addNotice($this->t('You can\'t remove yourself'));
}
$this->baseUrl->redirect('moderation/users/active');
break;
case 'block':
self::checkFormSecurityTokenRedirectOnError('moderation/users/active', 'moderation_users_active', 't');
User::block($uid);
$this->systemMessages->addNotice($this->t('User "%s" blocked', $user['username']));
$this->baseUrl->redirect('moderation/users/active');
break;
}
$pager = new Pager($this->l10n, $this->args->getQueryString(), 100);
$valid_orders = [
'name',
'email',
'register_date',
'last-activity',
'last-item',
'page-flags',
];
$order = 'last-item';
$order_direction = '-';
if (!empty($request['o'])) {
$new_order = $request['o'];
if ($new_order[0] === '-') {
$order_direction = '-';
$new_order = substr($new_order, 1);
}
if (in_array($new_order, $valid_orders)) {
$order = $new_order;
}
}
$users = User::getList($pager->getStart(), $pager->getItemsPerPage(), 'active', $order, ($order_direction == '-'));
$users = array_map($this->setupUserCallback(), $users);
$header_titles = [
$this->t('Name'),
$this->t('Email'),
$this->t('Register date'),
$this->t('Last login'),
$this->t('Last public item'),
$this->t('Type'),
$this->t('Blocked by'),
$this->t('Comments last 24h'),
$this->t('Reactions last 24h'),
$this->t('Ratio last 24h'),
];
$field_names = [
'name',
'email',
'register_date',
'login_date',
'lastitem_date',
'page_flags',
'blocked_by',
'comments',
'reactions',
'ratio',
];
$th_users = array_map(null, $header_titles, $valid_orders, $field_names);
$count = $this->database->count('user', ["`verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired` AND `uid` != ?", 0]);
$t = Renderer::getMarkupTemplate('ratioed.tpl', 'addon/ratioed');
return self::getTabsHTML('ratioed') . Renderer::replaceMacros($t, [
// strings //
'$title' => $this->t('Moderation'),
'$help_url' => $this->baseUrl . '/ratioed/help',
'$page' => $this->t('Behaviour'),
'$select_all' => $this->t('select all'),
'$delete' => $this->t('Delete'),
'$block' => $this->t('Block'),
'$blocked' => $this->t('User blocked'),
'$siteadmin' => $this->t('Site admin'),
'$accountexpired' => $this->t('Account expired'),
'$h_newuser' => $this->t('Create a new user'),
'$th_users' => $th_users,
'$order_users' => $order,
'$order_direction_users' => $order_direction,
'$confirm_delete_multi' => $this->t('Selected users will be deleted!\n\nEverything these users had posted on this site will be permanently deleted!\n\nAre you sure?'),
'$confirm_delete' => $this->t('The user {0} will be deleted!\n\nEverything this user has posted on this site will be permanently deleted!\n\nAre you sure?'),
'$form_security_token' => self::getFormSecurityToken('moderation_users_active'),
// values //
'$baseurl' => $this->baseUrl,
'$query_string' => $this->args->getQueryString(),
'$users' => $users,
'$count' => $count,
'$pager' => $pager->renderFull($count),
]);
}
protected function setupUserCallback(): \Closure
{
Logger::debug("ratioed: setupUserCallback");
$parentCallback = parent::setupUserCallback();
return function ($user) use ($parentCallback) {
$blocked_count = DBA::count('user-contact', ['uid' => $user['uid'], 'is-blocked' => 1]);
$user['blocked_by'] = $blocked_count;
$self_contact_result = DBA::p('SELECT admin_contact.id AS user_contact_uid FROM contact AS admin_contact JOIN contact AS user_contact ON admin_contact.`uri-id` = user_contact.`uri-id` AND admin_contact.self = 0 AND user_contact.self = 1 WHERE user_contact.uid = ?', $user['uid']);
if (DBA::isResult($self_contact_result)) {
$self_contact_result_row = DBA::fetch($self_contact_result);
$user['user_contact_uid'] = $self_contact_result_row['user_contact_uid'];
}
else {
$user['user_contact_uid'] = NULL;
}
if ($user['user_contact_uid']) {
$post_engagement_result = DBA::p('SELECT SUM(`comments`) AS `comment_count`, SUM(`activities`) AS `activities_count` FROM `post-engagement` WHERE `post-engagement`.created > DATE_SUB(now(), INTERVAL 1 DAY) AND `post-engagement`.`owner-id` = ?', $user['user_contact_uid']);
if (DBA::isResult($post_engagement_result)) {
$post_engagement_result_row = DBA::fetch($post_engagement_result);
$user['comments'] = $post_engagement_result_row['comment_count'];
$user['reactions'] = $post_engagement_result_row['activities_count'];
if ($user['reactions'] > 0) {
$user['ratio'] = number_format($user['comments'] / $user['reactions'], 1, '.', '');
$user['ratioed'] = (float)($user['ratio']) >= 2.0;
}
else {
if ($user['comments'] == 0) {
$user['ratio'] = '0';
$user['ratioed'] = false;
}
else {
$user['ratio'] = '∞';
$user['ratioed'] = false;
}
}
}
else {
$user['comments'] = 'error';
$user['reactions'] = 'error';
$user['ratio'] = 'error';
$user['ratioed'] = false;
}
}
else {
$user['comments'] = 'error';
$user['reactions'] = 'error';
$user['ratio'] = 'error';
$user['ratioed'] = false;
}
$user = $parentCallback($user);
Logger::debug("ratioed: setupUserCallback", [
'uid' => $user['uid'],
'blocked_by' => $user['blocked_by'],
'comments' => $user['comments'],
'reactions' => $user['reactions'],
'ratio' => $user['ratio'],
'ratioed' => $user['ratioed'],
]);
return $user;
};
}
}

View file

@ -1,58 +0,0 @@
<?php
/**
* Name: Ratioed
* Description: Additional moderation user table with statistics about user behaviour
* Version: 0.1
* Author: Matthew Exon <http://mat.exon.name>
*/
use Friendica\Addon\ratioed\RatioedPanel;
use Friendica\Core\Hook;
use Friendica\Core\Logger;
use Friendica\DI;
/**
* Sets up the addon hooks and updates data in the database if needed
*/
function ratioed_install()
{
Hook::register('moderation_users_tabs', 'addon/ratioed/ratioed.php', 'ratioed_users_tabs');
Logger::info("ratioed: installed");
}
/**
* This is a statement rather than an actual function definition. The simple
* existence of this method is checked to figure out if the addon offers a
* module.
*/
function ratioed_module() {}
/**
* @brief Adds additional users tab to the moderation panel
*
* @param array $arr Parameters, including "tabs" which is the list to modify, and "selectedTab", which is the currently selected tab ID
*/
function ratioed_users_tabs(array &$arr) {
Logger::debug("ratioed: users tabs");
array_push($arr['tabs'], [
'label' => DI::l10n()->t('Behaviour'),
'url' => 'ratioed',
'sel' => $arr['selectedTab'] == 'ratioed' ? 'active' : '',
'title' => DI::l10n()->t('Statistics about users behaviour'),
'id' => 'admin-users-ratioed',
'accesskey' => 'r',
]);
}
/**
* @brief Displays the ratioed tab in the moderation panel
*/
function ratioed_content() {
Logger::debug("ratioed: content");
$ratioed = DI::getDice()->create(RatioedPanel::class, [$_SERVER]);
$httpException = DI::getDice()->create(Friendica\Module\Special\HTTPException::class);
$ratioed->run($httpException);
}

View file

@ -1,92 +0,0 @@
<div class="panel 'help-content-wrapper">
<div class="panel-body">
<h2>Ratioed Plugin Help</h2>
<p>
This plugin provides administrators with additional statistics about
the behaviour of users. These may be useful as early warning signs
that warrant more carefully watching the behaviour of a user. They
are <em>not</em> suitable as a trigger for instantly blocking,
muting, or reporting a user, since they lack context.
</p>
<p>
The name of the plugin comes
from <a href="https://knowyourmeme.com/editorials/guides/what-is-the-ratio-and-what-does-it-mean-to-get-ratioed-twitters-1-rule-explained">"The
Ratio"</a>, a well-known quick rule of thumb:
</p>
<blockquote>
If the Replies:RT ratio is greater than 2:1, you done messed up.
</blockquote>
<p>
To "get ratioed" is to receive a large number of comments in a short
space of time, with relatively few likes or boosts. If commenters
were enthusiastic about the posts, they would also have liked or
boosted them. Receiving many comments without such likes or boosts
indicates the comments were probably angry. This anger may or may
not be justified, but either way this is probably something
moderators should be aware of.
</p>
<p>
This plugin allows viewing of an actual ratio, calculated over the
last 24 hours. This is a useful timeframe for sudden dogpiling
events that administrators might not otherwise notice. The plugin
also calculates other statistics.
</p>
<h3>Explanation of Statistics</h3>
<h4>Blocked by</h4>
<p>
This summarises the number of users on remote servers that have
blocked this user.
</p>
<p>
Note that the ActivityPub spec expressly says that
implementations "SHOULD NOT" forward such block messages to
remote servers. Nevertheless some implementations do this
anyway, notably Mastodon. This statistic can only count block
messages from servers that do this, as well as blocks from local
users. As such, it is usually an undercount.
</p>
<p>
The reason the spec recommends against forwarding these messages
is that they can lead to retaliation. For this reason, this
plugin deliberately does not provide any way to investigate
exactly who blocked the user.
</p>
<h4>Comments last 24h</h4>
<p>
This gives the number of comments made on the top-level posts that
this user made within the last 24 hours.
</p>
<h4>Reactions last 24h</h4>
<p>
This collects the number of likes, boosts, or other "one-click"
interactions made on the user's top-level posts within the last 24
hours.
</p>
<h4>Ratio last 24h</h4>
<p>
This is the ratio between "Comments last 24h" and "Reactions last
24h". It is intended to approximate the traditional ratio as
understood on Twitter.
</p>
<h3>Performance</h3>
<p>
The statistics are computed from scratch each time the page loads.
It's possible that this might put a heavy load on the database. and
the page may take a long time to load.
</p>
<h3>Extending</h3>
<p>
Suggestions for additional statistics are welcome, especially from
moderators. This plugin should be considered a sandbox for
experimentation, so it is not necessary to prove that any statistic
is correlated with unwanted behaviour.
</p>
<p>
However, this plugin does deal with potentially sensitive
information. Even if moderators do in principle have access to all
information, it should not necessarily be highlighted. Statistics
should be kept anonymous and neutral. Also, they should be
presented only to moderators, not to the users themselves.
</p>
</div>
</div>

View file

@ -1,164 +0,0 @@
<script type="text/javascript" src="view/theme/frio/js/mod_admin.js?v={{constant('\Friendica\App::VERSION')}}"></script>
<link rel="stylesheet" href="view/theme/frio/css/mod_admin.css?v={{constant('\Friendica\App::VERSION')}}" type="text/css" media="screen"/>
<div id="admin-users" class="adminpage generic-page-wrapper">
<h1>{{$title}} - {{$page}} ({{$count}})</h1>
<p>
<a href="{{$base_url}}/moderation/users/create" class="btn btn-primary"><i class="fa fa-user-plus"></i> {{$h_newuser}}</a>
</p>
<form action="{{$baseurl}}/{{$query_string}}" method="post">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
<table id="users" class="table table-hover">
<thead>
<tr>
<th>
<div class="checkbox">
<input type="checkbox" id="admin-settings-users-select" class="selecttoggle" data-select-class="users_ckbx"/>
<label for="admin-settings-users-select"></label>
</div>
</th>
<th></th>
{{foreach $th_users as $k=>$th}}
{{if $k < 2 || $order_users == $th.1 || ($k==5 && !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1])) }}
<th class="th-{{$k}}">
<a href="{{$baseurl}}/moderation/users/active?o={{if $order_direction_users == "+"}}-{{/if}}{{$th.1}}" class="table-order">
{{if $order_users == $th.1}}
{{if $order_direction_users == "+"}}
&#8595;
{{else}}
&#8593;
{{/if}}
{{else}}
&#8597;
{{/if}}
{{$th.0}}
</a>
</th>
{{/if}}
{{/foreach}}
<th></th>
</tr>
</thead>
<tbody>
{{foreach $users as $u}}
<tr id="user-{{$u.uid}}" class="{{if $u.ratioed}}blocked{{/if}}">
<td>
{{if $u.is_deletable}}
<div class="checkbox">
<input type="checkbox" class="users_ckbx" id="id_user_{{$u.uid}}" name="user[]" value="{{$u.uid}}"/>
<label for="id_user_{{$u.uid}}"></label>
</div>
{{else}}
&nbsp;
{{/if}}
</td>
<td><img class="avatar-nano" src="{{$u.micro}}" title="{{$u.nickname}}"></td>
<td><a href="{{$u.url}}" title="{{$u.nickname}}"> {{$u.name}}</a></td>
<td>{{$u.email}}</td>
{{if $order_users == $th_users.2.1}}
<td>{{$u.register_date}}</td>
{{/if}}
{{if $order_users == $th_users.3.1}}
<td>{{$u.login_date}}</td>
{{/if}}
{{if $order_users == $th_users.4.1}}
<td>{{$u.lastitem_date}}</td>
{{/if}}
{{if !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }}
<td>
<i class="fa
{{if $u.page_flags_raw==0}}fa-user{{/if}} {{* PAGE_NORMAL *}}
{{if $u.page_flags_raw==1}}fa-bullhorn{{/if}} {{* PAGE_SOAPBOX *}}
{{if $u.page_flags_raw==2}}fa-users{{/if}} {{* PAGE_COMMUNITY *}}
{{if $u.page_flags_raw==3}}fa-heart{{/if}} {{* PAGE_FREELOVE *}}
{{if $u.page_flags_raw==4}}fa-rss{{/if}} {{* PAGE_BLOG *}}
{{if $u.page_flags_raw==5}}fa-user-secret{{/if}} {{* PAGE_PRVGROUP *}}
" title="{{$u.page_flags}}">
</i>
{{if $u.page_flags_raw==0 && $u.account_type_raw > 0}}
<i class="fa
{{if $u.account_type_raw==1}}fa-sitemap{{/if}} {{* ACCOUNT_TYPE_ORGANISATION *}}
{{if $u.account_type_raw==2}}fa-newspaper-o{{/if}} {{* ACCOUNT_TYPE_NEWS *}}
{{if $u.account_type_raw==3}}fa-comments{{/if}} {{* ACCOUNT_TYPE_COMMUNITY *}}
" title="{{$u.account_type}}">
</i>
{{/if}}
{{if $u.is_admin}}<i class="fa fa-user-secret text-primary" title="{{$siteadmin}}"></i>{{/if}}
{{if $u.account_expired}}<i class="fa fa-clock-o text-warning" title="{{$accountexpired}}"></i>{{/if}}
</td>
{{/if}}
<td class="text-right">
<button type="button" class="btn-link admin-settings-action-link" onclick="return details({{$u.uid}})"><span class="caret"></span></button>
</td>
</tr>
<tr id="user-{{$u.uid}}-detail" class=" details hidden {{if $u.blocked != 0}}blocked{{/if}}">
<td>&nbsp;</td>
<td colspan="4">
{{if $order_users != $th_users.2.1}}
<p>
<a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.2.1}}" class="btn-link table-order">
&#8597; {{$th_users.2.0}}</a> : {{$u.register_date}}
</p>
{{/if}}
{{if $order_users != $th_users.3.1}}
<p>
<a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.3.1}}" class="btn-link table-order">
&#8597; {{$th_users.3.0}}</a> : {{$u.login_date}}
</p>
{{/if}}
{{if $order_users != $th_users.4.1}}
<p>
<a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.4.1}}" class="btn-link table-order">
&#8597; {{$th_users.4.0}}</a> : {{$u.lastitem_date}}
</p>
{{/if}}
{{if in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }}
<p>
<a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.5.1}}" class="btn-link table-order">
&#8597; {{$th_users.5.0}}</a> : {{$u.page_flags}}{{if $u.page_flags_raw==0 && $u.account_type_raw > 0}}, {{$u.account_type}}{{/if}} {{if $u.is_admin}}({{$siteadmin}}){{/if}} {{if $u.account_expired}}({{$accountexpired}}){{/if}}
</p>
{{/if}}
{{foreach $th_users as $k=>$th}}
{{if $order_users != $th.1 && $k > 5}}
<p>
{{$th.0}} : {{$u[$th.2]}}
</p>
{{/if}}
{{/foreach}}
</td>
<td class="text-right">
{{if $u.is_deletable}}
<a href="{{$baseurl}}/moderation/users/active/block/{{$u.uid}}?t={{$form_security_token}}" class="admin-settings-action-link" title="{{$block}}">
<i class="fa fa-ban" aria-hidden="true"></i>
</a>
<a href="{{$baseurl}}/moderation/users/active/delete/{{$u.uid}}?t={{$form_security_token}}" class="admin-settings-action-link" title="{{$delete}}" onclick="return confirm_delete('{{$confirm_delete}}','{{$u.name}}')">
<i class="fa fa-trash" aria-hidden="true"></i>
</a>
{{else}}
&nbsp;
{{/if}}
</td>
</tr>
{{/foreach}}
</tbody>
</table>
<div class="panel-footer">
<button type="submit" name="page_users_block" value="1" class="btn btn-warning">
<i class="fa fa-ban" aria-hidden="true"></i> {{$block}}
</button>
<button type="submit" name="page_users_delete" value="1" class="btn btn-danger" onclick="return confirm_delete('{{$confirm_delete_multi}}')">
<i class="fa fa-trash" aria-hidden="true"></i> {{$delete}}
</button>
</div>
{{$pager nofilter}}
</form>
</div>

View file

@ -5,7 +5,7 @@
# #
# Translators: # Translators:
# Hypolite Petovan <hypolite@mrpetovan.com>, 2022 # Hypolite Petovan <hypolite@mrpetovan.com>, 2022
# cracrayol, 2024 # Florent C., 2023
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
@ -14,7 +14,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-21 19:16-0500\n" "POT-Creation-Date: 2021-11-21 19:16-0500\n"
"PO-Revision-Date: 2018-03-20 07:26+0000\n" "PO-Revision-Date: 2018-03-20 07:26+0000\n"
"Last-Translator: cracrayol, 2024\n" "Last-Translator: Florent C., 2023\n"
"Language-Team: French (https://app.transifex.com/Friendica/teams/12172/fr/)\n" "Language-Team: French (https://app.transifex.com/Friendica/teams/12172/fr/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -24,7 +24,7 @@ msgstr ""
#: securemail.php:50 #: securemail.php:50
msgid "Enable Secure Mail" msgid "Enable Secure Mail"
msgstr "Activer l'extension des courriels sécurisés" msgstr "Activer l'extension des emails sécurisés"
#: securemail.php:51 #: securemail.php:51
msgid "Public key" msgid "Public key"
@ -36,7 +36,7 @@ msgstr "Votre clé publique PGP formatée compatible ASCII"
#: securemail.php:56 #: securemail.php:56
msgid "\"Secure Mail\" Settings" msgid "\"Secure Mail\" Settings"
msgstr "Paramètres des courriels sécurisés" msgstr "Paramètres des emails sécurisés"
#: securemail.php:59 #: securemail.php:59
msgid "Save Settings" msgid "Save Settings"

View file

@ -5,10 +5,10 @@ function string_plural_select_fr($n){
$n = intval($n); $n = intval($n);
if (($n == 0 || $n == 1)) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; } if (($n == 0 || $n == 1)) { return 0; } else if ($n != 0 && $n % 1000000 == 0) { return 1; } else { return 2; }
}} }}
$a->strings['Enable Secure Mail'] = 'Activer l\'extension des courriels sécurisés'; $a->strings['Enable Secure Mail'] = 'Activer l\'extension des emails sécurisés';
$a->strings['Public key'] = 'Clé publique'; $a->strings['Public key'] = 'Clé publique';
$a->strings['Your public PGP key, ascii armored format'] = 'Votre clé publique PGP formatée compatible ASCII'; $a->strings['Your public PGP key, ascii armored format'] = 'Votre clé publique PGP formatée compatible ASCII';
$a->strings['"Secure Mail" Settings'] = 'Paramètres des courriels sécurisés'; $a->strings['"Secure Mail" Settings'] = 'Paramètres des emails sécurisés';
$a->strings['Save Settings'] = 'Enregistrer les paramètres'; $a->strings['Save Settings'] = 'Enregistrer les paramètres';
$a->strings['Save and send test'] = 'Enregistrer et envoyer un message de test'; $a->strings['Save and send test'] = 'Enregistrer et envoyer un message de test';
$a->strings['Test email sent'] = 'Courriel de test envoyé avec succès'; $a->strings['Test email sent'] = 'Courriel de test envoyé avec succès';

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 B

After

Width:  |  Height:  |  Size: 100 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 908 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View file

@ -2,28 +2,30 @@
/* /*
* Name: Smiley Pack * Name: Smiley Pack
* Description: Pack of smileys that make master too AOLish. * Description: Pack of smileys that make master too AOLish.
* Version: 1.06 * Version: 1.05
* Author: Thomas Willingham (based on Mike Macgirvin's Adult Smile template) * Author: Thomas Willingham (based on Mike Macgirvin's Adult Smile template)
* Author: Matthias Ebers <https://loma.ml/profile/one> * Author: Matthias Ebers <https://loma.ml/profile/one>
* All smileys from sites offering them as Public Domain * All smileys from sites offering them as Public Domain
*/ */
use Friendica\App;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\DI; use Friendica\DI;
function smiley_pack_install() function smiley_pack_install() {
{
Hook::register('smilie', 'addon/smiley_pack/smiley_pack.php', 'smiley_pack_smilies'); Hook::register('smilie', 'addon/smiley_pack/smiley_pack.php', 'smiley_pack_smilies');
} }
function smiley_pack_smilies(array &$b) function smiley_pack_smilies(array &$b)
{ {
#Smileys are split into various directories by the intended range of emotions. This is in case we get too big and need to modularise things. We can then cut and paste the right lines, move the right directory, and just change the name of the addon to happy_smilies or whatever. #Smileys are split into various directories by the intended range of emotions. This is in case we get too big and need to modularise things. We can then cut and paste the right lines, move the right directory, and just change the name of the addon to happy_smilies or whatever.
#Be careful with invocation strings. If you have a smiley called foo, and another called foobar, typing :foobar will call foo. Avoid this with clever naming, using ~ instead of : #Be careful with invocation strings. If you have a smiley called foo, and another called foobar, typing :foobar will call foo. Avoid this with clever naming, using ~ instead of :
#when all else fails. #when all else fails.
#Animal smileys.
#Animal smileys.
$b['texts'][] = ':bunnyflowers:'; $b['texts'][] = ':bunnyflowers:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/animals/bunnyflowers.gif' . '" alt="' . ':bunnyflowers:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/animals/bunnyflowers.gif' . '" alt="' . ':bunnyflowers:' . '" />';
@ -97,7 +99,9 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':pig:'; $b['texts'][] = ':pig:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/animals/pig.gif' . '" alt="' . ':pig:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/animals/pig.gif' . '" alt="' . ':pig:' . '" />';
#Baby Smileys
#Baby Smileys
$b['texts'][] = ':baby:'; $b['texts'][] = ':baby:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/baby.gif' . '" alt="' . ':baby:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/baby.gif' . '" alt="' . ':baby:' . '" />';
@ -105,13 +109,15 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':babycot:'; $b['texts'][] = ':babycot:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/babycot.gif' . '" alt="' . ':babycot:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/babycot.gif' . '" alt="' . ':babycot:' . '" />';
$b['texts'][] = ':pregnant:'; $b['texts'][] = ':pregnant:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/pregnant.gif' . '" alt="' . ':pregnant:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/pregnant.gif' . '" alt="' . ':pregnant:' . '" />';
$b['texts'][] = ':stork:'; $b['texts'][] = ':stork:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/stork.gif' . '" alt="' . ':stork:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/stork.gif' . '" alt="' . ':stork:' . '" />';
#Confused Smileys
#Confused Smileys
$b['texts'][] = ':confused:'; $b['texts'][] = ':confused:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/confused/confused.gif' . '" alt="' . ':confused:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/confused/confused.gif' . '" alt="' . ':confused:' . '" />';
@ -124,12 +130,13 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':dazed:'; $b['texts'][] = ':dazed:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/confused/dazed.gif' . '" alt="' . ':dazed:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/confused/dazed.gif' . '" alt="' . ':dazed:' . '" />';
#Cool Smileys
#Cool Smileys
$b['texts'][] = ':affro:'; $b['texts'][] = ':affro:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/cool/affro.gif' . '" alt="' . ':affro:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/cool/affro.gif' . '" alt="' . ':affro:' . '" />';
#Devil/Angel Smileys #Devil/Angel Smileys
$b['texts'][] = ':angel:'; $b['texts'][] = ':angel:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/devilangel/angel.gif' . '" alt="' . ':angel:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/devilangel/angel.gif' . '" alt="' . ':angel:' . '" />';
@ -158,7 +165,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':graveside:'; $b['texts'][] = ':graveside:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/devilangel/graveside.gif' . '" alt="' . ':graveside:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/devilangel/graveside.gif' . '" alt="' . ':graveside:' . '" />';
#Unpleasent smileys. #Unpleasent smileys.
$b['texts'][] = ':toilet:'; $b['texts'][] = ':toilet:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/disgust/toilet.gif' . '" alt="' . ':toilet:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/disgust/toilet.gif' . '" alt="' . ':toilet:' . '" />';
@ -169,7 +176,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':fartblush:'; $b['texts'][] = ':fartblush:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/disgust/fartblush.gif' . '" alt="' . ':fartblush:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/disgust/fartblush.gif' . '" alt="' . ':fartblush:' . '" />';
#Drinks #Drinks
$b['texts'][] = ':tea:'; $b['texts'][] = ':tea:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/drink/tea.gif' . '" alt="' . ':tea:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/drink/tea.gif' . '" alt="' . ':tea:' . '" />';
@ -177,7 +184,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':drool:'; $b['texts'][] = ':drool:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/drool/drool.gif' . '" alt="' . ':drool:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/drool/drool.gif' . '" alt="' . ':drool:' . '" />';
#Sad smileys #Sad smileys
$b['texts'][] = ':crying:'; $b['texts'][] = ':crying:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sad/crying.png' . '" alt="' . ':crying:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sad/crying.png' . '" alt="' . ':crying:' . '" />';
@ -188,12 +195,12 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':sigh:'; $b['texts'][] = ':sigh:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sad/sigh.gif' . '" alt="' . ':sigh:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sad/sigh.gif' . '" alt="' . ':sigh:' . '" />';
#Smoking - only one smiley in here, maybe it needs moving elsewhere? #Smoking - only one smiley in here, maybe it needs moving elsewhere?
$b['texts'][] = ':smoking:'; $b['texts'][] = ':smoking:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/smoking/smoking.gif' . '" alt="' . ':smoking:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/smoking/smoking.gif' . '" alt="' . ':smoking:' . '" />';
#Sport smileys #Sport smileys
$b['texts'][] = ':basketball:'; $b['texts'][] = ':basketball:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sport/basketball.gif' . '" alt="' . ':basketball:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sport/basketball.gif' . '" alt="' . ':basketball:' . '" />';
@ -228,7 +235,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':horseriding:'; $b['texts'][] = ':horseriding:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sport/horseriding.gif' . '" alt="' . ':horseriding:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sport/horseriding.gif' . '" alt="' . ':horseriding:' . '" />';
#Love smileys #Love smileys
$b['texts'][] = ':iloveyou:'; $b['texts'][] = ':iloveyou:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/love/iloveyou.gif' . '" alt="' . ':iloveyou:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/love/iloveyou.gif' . '" alt="' . ':iloveyou:' . '" />';
@ -248,7 +255,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':loveheart:'; $b['texts'][] = ':loveheart:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/love/loveheart.gif' . '" alt="' . ':loveheart:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/love/loveheart.gif' . '" alt="' . ':loveheart:' . '" />';
#Tired/Sleep smileys #Tired/Sleep smileys
$b['texts'][] = ':countsheep'; $b['texts'][] = ':countsheep';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/tired/countsheep.gif' . '" alt="' . ':countsheep:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/tired/countsheep.gif' . '" alt="' . ':countsheep:' . '" />';
@ -262,7 +269,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':yawn:'; $b['texts'][] = ':yawn:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/tired/yawn.gif' . '" alt="' . ':yawn:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/tired/yawn.gif' . '" alt="' . ':yawn:' . '" />';
#Fight/Flame/Violent smileys #Fight/Flame/Violent smileys
$b['texts'][] = ':2guns:'; $b['texts'][] = ':2guns:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/2guns.gif' . '" alt="' . ':2guns:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/2guns.gif' . '" alt="' . ':2guns:' . '" />';
@ -306,7 +313,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':acid:'; $b['texts'][] = ':acid:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/acid.gif' . '" alt="' . ':acid:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/acid.gif' . '" alt="' . ':acid:' . '" />';
#Fantasy smileys - monsters and dragons fantasy. The other type of fantasy belongs in adult smileys #Fantasy smileys - monsters and dragons fantasy. The other type of fantasy belongs in adult smileys
$b['texts'][] = ':alienmonster:'; $b['texts'][] = ':alienmonster:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fantasy/alienmonster.gif' . '" alt="' . ':alienmonster:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fantasy/alienmonster.gif' . '" alt="' . ':alienmonster:' . '" />';
@ -329,7 +336,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':mummy:'; $b['texts'][] = ':mummy:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fantasy/mummy.gif' . '" alt="' . ':mummy:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fantasy/mummy.gif' . '" alt="' . ':mummy:' . '" />';
#Food smileys #Food smileys
$b['texts'][] = ':apple:'; $b['texts'][] = ':apple:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/food/apple.gif' . '" alt="' . ':apple:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/food/apple.gif' . '" alt="' . ':apple:' . '" />';
@ -361,7 +368,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':birthdaycake:'; $b['texts'][] = ':birthdaycake:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/food/birthdaycake.gif' . '" alt="' . ':birthdaycake:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/food/birthdaycake.gif' . '" alt="' . ':birthdaycake:' . '" />';
#Happy smileys #Happy smileys
$b['texts'][] = ':cloud9:'; $b['texts'][] = ':cloud9:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/happy/cloud9.gif' . '" alt="' . ':cloud9:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/happy/cloud9.gif' . '" alt="' . ':cloud9:' . '" />';
@ -369,7 +376,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':tearsofjoy:'; $b['texts'][] = ':tearsofjoy:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/happy/tearsofjoy.gif' . '" alt="' . ':tearsofjoy:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/happy/tearsofjoy.gif' . '" alt="' . ':tearsofjoy:' . '" />';
#Repsect smileys #Repsect smileys
$b['texts'][] = ':bow:'; $b['texts'][] = ':bow:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/bow.gif' . '" alt="' . ':bow:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/bow.gif' . '" alt="' . ':bow:' . '" />';
@ -383,19 +390,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':number1:'; $b['texts'][] = ':number1:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/number1.gif' . '" alt="' . ':number1:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/number1.gif' . '" alt="' . ':number1:' . '" />';
$b['texts'][] = ':cc_cc:'; #Laugh smileys
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/cc.png' . '" alt="' . ':cc_cc:' . '" />';
$b['texts'][] = ':cc_by:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/ccby.png' . '" alt="' . ':cc_by:' . '" />';
$b['texts'][] = ':cc_sa:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/ccsa.png' . '" alt="' . ':cc_sa:' . '" />';
$b['texts'][] = ':cc_0:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/cc0.png' . '" alt="' . ':cc_0:' . '" />';
#Laugh smileys
$b['texts'][] = ':hahaha:'; $b['texts'][] = ':hahaha:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/laugh/hahaha.gif' . '" alt="' . ':hahaha:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/laugh/hahaha.gif' . '" alt="' . ':hahaha:' . '" />';
@ -406,18 +401,19 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':rofl:'; $b['texts'][] = ':rofl:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/laugh/rofl.gif' . '" alt="' . ':rofl:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/laugh/rofl.gif' . '" alt="' . ':rofl:' . '" />';
#Music smileys #Music smileys
$b['texts'][] = ':drums:'; $b['texts'][] = ':drums:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/drums.gif' . '" alt="' . ':drums:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/drums.gif' . '" alt="' . ':drums:' . '" />';
$b['texts'][] = ':guitar:'; $b['texts'][] = ':guitar:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/guitar.gif' . '" alt="' . ':guitar:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/guitar.gif' . '" alt="' . ':guitar:' . '" />';
$b['texts'][] = ':trumpet:'; $b['texts'][] = ':trumpet:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/trumpet.gif' . '" alt="' . ':trumpet:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/trumpet.gif' . '" alt="' . ':trumpet:' . '" />';
#Smileys that used to be in core #Smileys that used to be in core
$b['texts'][] = ':headbang:'; $b['texts'][] = ':headbang:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/headbang.gif' . '" alt="' . ':headbang:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/headbang.gif' . '" alt="' . ':headbang:' . '" />';
@ -440,7 +436,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':headdesk:'; $b['texts'][] = ':headdesk:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/headbang.gif' . '" alt="' . ':headdesk:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/headbang.gif' . '" alt="' . ':headdesk:' . '" />';
#These two are still in core, so oldcore isn't strictly right, but we don't want too many directories #These two are still in core, so oldcore isn't strictly right, but we don't want too many directories
$b['texts'][] = ':-d'; $b['texts'][] = ':-d';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/laughing.gif' . '" alt="' . ':-d' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/laughing.gif' . '" alt="' . ':-d' . '" />';
@ -448,8 +444,8 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':-o'; $b['texts'][] = ':-o';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/surprised.gif' . '" alt="' . ':-o' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/surprised.gif' . '" alt="' . ':-o' . '" />';
# Regex killers - stick these at the bottom so they appear at the end of the English and # Regex killers - stick these at the bottom so they appear at the end of the English and
# at the start of $OtherLanguage. # at the start of $OtherLanguage.
$b['texts'][] = ':cool:'; $b['texts'][] = ':cool:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/cool/cool.gif' . '" alt="' . ':cool:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/cool/cool.gif' . '" alt="' . ':cool:' . '" />';
@ -484,23 +480,22 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':gangs:'; $b['texts'][] = ':gangs:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/gangs.gif' . '" alt="' . ':gangs:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/gangs.gif' . '" alt="' . ':gangs:' . '" />';
$b['texts'][] = ':dj:'; $b['texts'][] = ':dj:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/dj.gif' . '" alt="' . ':dj:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/dj.gif' . '" alt="' . ':dj:' . '" />';
$b['texts'][] = ':elvis:'; $b['texts'][] = ':elvis:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/elvis.gif' . '" alt="' . ':elivs:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/elvis.gif' . '" alt="' . ':elivs:' . '" />';
$b['texts'][] = ':violin:'; $b['texts'][] = ':violin:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/violin.gif' . '" alt="' . ':violin:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/violin.gif' . '" alt="' . ':violin:' . '" />';
# New Gif Emoji (@one@loma.ml) # New Gif Emoji (@one@loma.ml)
# Fediverse # Fediverse
$b['texts'][] = ':friendica:'; $b['texts'][] = ':friendica:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/friendica.png' . '" alt="' . ':friendica:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/friendica.gif' . '" alt="' . ':friendica:' . '" />';
$b['texts'][] = ':fediverse:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/fediverse.gif' . '" alt="' . ':fediverse:' . '" />';
$b['texts'][] = ':mastodon:'; $b['texts'][] = ':mastodon:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/mastodon.gif' . '" alt="' . ':mastodon:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/mastodon.gif' . '" alt="' . ':mastodon:' . '" />';
@ -512,10 +507,10 @@ function smiley_pack_smilies(array &$b)
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/misskey.gif' . '" alt="' . ':misskey:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/misskey.gif' . '" alt="' . ':misskey:' . '" />';
$b['texts'][] = ':diaspora:'; $b['texts'][] = ':diaspora:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/diaspora.png' . '" alt="' . ':diaspora:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/diaspora.gif' . '" alt="' . ':diaspora:' . '" />';
$b['texts'][] = ':hubzilla:'; $b['texts'][] = ':hubzilla:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/hubzilla.png' . '" alt="' . ':hubzilla:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/hubzilla.gif' . '" alt="' . ':hubzilla:' . '" />';
$b['texts'][] = ':pixelfed:'; $b['texts'][] = ':pixelfed:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/pixelfed.gif' . '" alt="' . ':pixelfeed:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/pixelfed.gif' . '" alt="' . ':pixelfeed:' . '" />';
@ -526,12 +521,12 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':activitypub:'; $b['texts'][] = ':activitypub:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/ap.gif' . '" alt="' . ':activitypub:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/ap.gif' . '" alt="' . ':activitypub:' . '" />';
# ccc # ccc
$b['texts'][] = ':ccc event:'; $b['texts'][] = ':ccc event:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/ccc/ccc.gif' . '" alt="' . ':ccc event:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/ccc/ccc.gif' . '" alt="' . ':ccc event:' . '" />';
# Commercial # Commercial
$b['texts'][] = ':youtube:'; $b['texts'][] = ':youtube:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/youtube.gif' . '" alt="' . ':youtube:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/youtube.gif' . '" alt="' . ':youtube:' . '" />';
@ -544,107 +539,4 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':twitch:'; $b['texts'][] = ':twitch:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/twitch.gif' . '" alt="' . ':twitch:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/twitch.gif' . '" alt="' . ':twitch:' . '" />';
$b['texts'][] = ':facebook:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/facebook.gif' . '" alt="' . ':facebook:' . '" />';
$b['texts'][] = ':threads:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/threads.png' . '" alt="' . ':threads:' . '" />';
$b['texts'][] = ':google:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/google.gif' . '" alt="' . ':google:' . '" />';
$b['texts'][] = ':signal:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/signal.gif' . '" alt="' . ':signal:' . '" />';
$b['texts'][] = ':tiktok:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/tiktok.gif' . '" alt="' . ':tiktok:' . '" />';
$b['texts'][] = ':whatsapp:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/whatsapp.gif' . '" alt="' . ':whatsapp:' . '" />';
$b['texts'][] = ':instagram:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/instagram.gif' . '" alt="' . ':instagram:' . '" />';
$b['texts'][] = ':telegram:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/telegram.gif' . '" alt="' . ':telegram:' . '" />';
$b['texts'][] = ':windows:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/windows.png' . '" alt="' . ':windows:' . '" />';
$b['texts'][] = ':github:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/github.png' . '" alt="' . ':github:' . '" />';
$b['texts'][] = ':threema:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/threema.png' . '" alt="' . ':threema:' . '" />';
# nonCommercial
$b['texts'][] = ':invidious:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/noncommercial/invidious.gif' . '" alt="' . ':invidious:' . '" />';
$b['texts'][] = ':bluesky:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/noncommercial/bluesky.png' . '" alt="' . ':bluesky:' . '" />';
$b['texts'][] = ':vivaldi:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/noncommercial/vivaldi.png' . '" alt="' . ':vivaldi:' . '" />';
# opensource
$b['texts'][] = ':firefox:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/firefox.png' . '" alt="' . ':firefox:' . '" />';
$b['texts'][] = ':linuxopensuse:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/opensuse.png' . '" alt="' . ':linuxopensuse:' . '" />';
$b['texts'][] = ':linuxdebian:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/debian.png' . '" alt="' . ':linuxdebian:' . '" />';
$b['texts'][] = ':linuxfedora:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/fedora.png' . '" alt="' . ':linuxfedora:' . '" />';
$b['texts'][] = ':linuxubuntu:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/ubuntu.png' . '" alt="' . ':linuxubuntu:' . '" />';
$b['texts'][] = ':linuxmint:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/mint.png' . '" alt="' . ':linuxmint:' . '" />';
$b['texts'][] = ':fdroid:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/fdroid.png' . '" alt="' . ':fdroid:' . '" />';
$b['texts'][] = ':tutanota:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/tutanota.png' . '" alt="' . ':tutanota:' . '" />';
$b['texts'][] = ':raspi:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/raspi.png' . '" alt="' . ':raspi:' . '" />';
$b['texts'][] = ':linux:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/linux.png' . '" alt="' . ':linux:' . '" />';
$b['texts'][] = ':kde:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/kde.png' . '" alt="' . ':kde:' . '" />';
$b['texts'][] = ':firefoxnightly:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/firefoxnightly.png' . '" alt="' . ':firefoxnightly:' . '" />';
$b['texts'][] = ':archlinux:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/archlinux.png' . '" alt="' . ':archlinux:' . '" />';
$b['texts'][] = ':thunderbird:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/thunderbird.png' . '" alt="' . ':thunderbird:' . '" />';
$b['texts'][] = ':vivaldi:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/vivaldi.png' . '" alt="' . ':vivaldi:' . '" />';
$b['texts'][] = ':jabber:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/jabber.png' . '" alt="' . ':jabber:' . '" />';
$b['texts'][] = ':matrix:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/matrix.png' . '" alt="' . ':matrix:' . '" />';
$b['texts'][] = ':xmpp:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/xmpp.png' . '" alt="' . ':xmpp:' . '" />';
$b['texts'][] = ':foss:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/foss.png' . '" alt="' . ':foss:' . '" />';
} }

View file

@ -8,81 +8,64 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: friendica\n" "Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-04-29 06:56+0000\n" "POT-Creation-Date: 2021-07-25 13:15+0000\n"
"PO-Revision-Date: 2014-06-23 12:58+0000\n" "PO-Revision-Date: 2021-02-20 00:23+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: Farida Khalaf <faridakhalaf@hotmail.com>\n"
"Language-Team: Arabic (http://app.transifex.com/Friendica/friendica/language/ar/)\n" "Language-Team: Arabic (http://www.transifex.com/Friendica/friendica/language/ar/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: ar\n" "Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
#: tumblr.php:243 #: tumblr.php:40
msgid "Permission denied." msgid "Permission denied."
msgstr "الطلب مرفوض." msgstr "الطلب مرفوض."
#: tumblr.php:296 #: tumblr.php:70 tumblr.php:284
msgid "Save Settings" msgid "Save Settings"
msgstr "Save Settings" msgstr "Save Settings"
#: tumblr.php:297 #: tumblr.php:72
msgid "Consumer Key" msgid "Consumer Key"
msgstr "" msgstr ""
#: tumblr.php:298 #: tumblr.php:73
msgid "Consumer Secret" msgid "Consumer Secret"
msgstr "" msgstr ""
#: tumblr.php:299 #: tumblr.php:178
msgid "Maximum tags" msgid "You are now authenticated to tumblr."
msgstr "" msgstr ""
#: tumblr.php:299 #: tumblr.php:179
msgid "" msgid "return to the connector page"
"Maximum number of tags that a user can follow. Enter 0 to deactivate the " msgstr "الرجوع إلى صفحة الموصل"
"feature."
#: tumblr.php:195
msgid "Post to Tumblr"
msgstr "" msgstr ""
#: tumblr.php:336 #: tumblr.php:225 tumblr.php:229
msgid "Post to page:" msgid "Tumblr Export"
msgstr "" msgstr ""
#: tumblr.php:342 #: tumblr.php:233
msgid "(Re-)Authenticate your tumblr page" msgid "(Re-)Authenticate your tumblr page"
msgstr "" msgstr ""
#: tumblr.php:343 #: tumblr.php:237
msgid "You are not authenticated to tumblr"
msgstr ""
#: tumblr.php:348
msgid "Enable Tumblr Post Addon" msgid "Enable Tumblr Post Addon"
msgstr "" msgstr ""
#: tumblr.php:349 #: tumblr.php:243
msgid "Post to Tumblr by default" msgid "Post to Tumblr by default"
msgstr "" msgstr ""
#: tumblr.php:350 #: tumblr.php:264
msgid "Import the remote timeline" msgid "Post to page:"
msgstr "" msgstr ""
#: tumblr.php:351 #: tumblr.php:278
msgid "Subscribed tags" msgid "You are not authenticated to tumblr"
msgstr ""
#: tumblr.php:351
#, php-format
msgid ""
"Comma separated list of up to %d tags that will be imported additionally to "
"the timeline"
msgstr ""
#: tumblr.php:357
msgid "Tumblr Import/Export"
msgstr ""
#: tumblr.php:375
msgid "Post to Tumblr"
msgstr "" msgstr ""

Some files were not shown because too many files have changed in this diff Show more