mirror of
https://git.sekbaer.de/Friendica/friendica.git
synced 2025-06-06 15:24:27 +02:00
Merge branch 'develop' into introduce-phpmd
This commit is contained in:
commit
f1af52baa1
100 changed files with 19158 additions and 18342 deletions
|
@ -7,8 +7,13 @@ parameters:
|
|||
|
||||
paths:
|
||||
- addon/
|
||||
- src/
|
||||
- bin/auth_ejabberd.php
|
||||
- bin/console.php
|
||||
- bin/daemon.php
|
||||
- bin/jetstream.php
|
||||
- bin/worker.php
|
||||
- index.php
|
||||
- src/
|
||||
|
||||
excludePaths:
|
||||
analyse:
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
# The code standard check is just triggered for PRs and pushes to non-stable branches of Friendica
|
||||
when:
|
||||
branch:
|
||||
exclude: [ stable ]
|
||||
event: [ pull_request, push ]
|
||||
|
||||
steps:
|
||||
restore_cache:
|
||||
image: meltwater/drone-cache:dev
|
||||
|
@ -31,12 +37,20 @@ steps:
|
|||
volumes:
|
||||
- /tmp/drone-cache:/tmp/cache
|
||||
check:
|
||||
image: friendicaci/php-cs
|
||||
image: php:8.3
|
||||
commands:
|
||||
- echo "**** Use bin/dev/fix-codestyle.sh in case of errors ****"
|
||||
- apt-get update -q
|
||||
- DEBIAN_FRONTEND=noninteractive apt-get install -q -y git
|
||||
- if [ ! -z "$${CI_COMMIT_PULL_REQUEST}" ]; then
|
||||
git fetch --no-tags origin ${CI_COMMIT_TARGET_BRANCH};
|
||||
export CHANGED_FILES="$(git diff --name-status $(git merge-base FETCH_HEAD origin/${CI_COMMIT_TARGET_BRANCH})..${CI_COMMIT_SHA} | grep ^A | cut -f2)";
|
||||
CHANGED_FILES="$(git diff --name-only --diff-filter=ACMRTUXB $(git merge-base FETCH_HEAD origin/${CI_COMMIT_TARGET_BRANCH})..${CI_COMMIT_SHA})";
|
||||
else
|
||||
export CHANGED_FILES="$(git diff --name-status ${CI_COMMIT_SHA} | grep ^A | cut -f2)";
|
||||
CHANGED_FILES="$(git diff --name-only --diff-filter=ACMRTUXB ${CI_COMMIT_SHA})";
|
||||
fi
|
||||
- /check-php-cs.sh
|
||||
- if ! echo "$${CHANGED_FILES}" | grep -qE "^(\\.php-cs-fixer(\\.dist)?\\.php|composer\\.lock)$"; then
|
||||
EXTRA_ARGS=$(printf -- '--path-mode=intersection\n--\n%s' "$${CHANGED_FILES}");
|
||||
else
|
||||
EXTRA_ARGS='';
|
||||
fi
|
||||
- ./bin/dev/php-cs-fixer/vendor/bin/php-cs-fixer check --config=.php-cs-fixer.dist.php -v --diff --using-cache=no $${EXTRA_ARGS}
|
||||
|
|
|
@ -13,6 +13,12 @@ labels:
|
|||
location: friendica
|
||||
type: releaser
|
||||
|
||||
# CD is triggered after pushing new code to the develop or *-rc branch, excluding the stable branch
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: [ develop, '*-rc' ]
|
||||
event: push
|
||||
|
||||
skip_clone: true
|
||||
|
||||
steps:
|
||||
|
@ -23,10 +29,6 @@ steps:
|
|||
- git checkout $CI_COMMIT_BRANCH
|
||||
- git fetch origin $CI_COMMIT_REF
|
||||
- git merge $CI_COMMIT_SHA
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: [ develop, '*-rc' ]
|
||||
event: push
|
||||
restore_cache:
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
|
@ -38,10 +40,6 @@ steps:
|
|||
- '.composer'
|
||||
volumes:
|
||||
- /tmp/drone-cache:/tmp/cache
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: [ develop, '*-rc' ]
|
||||
event: push
|
||||
composer_install:
|
||||
image: friendicaci/php8.2:php8.2.16
|
||||
commands:
|
||||
|
@ -51,10 +49,6 @@ steps:
|
|||
- composer install --no-dev --optimize-autoloader
|
||||
volumes:
|
||||
- /etc/hosts:/etc/hosts
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: [ develop, '*-rc' ]
|
||||
event: push
|
||||
create_artifacts:
|
||||
image: debian
|
||||
commands:
|
||||
|
@ -75,10 +69,6 @@ steps:
|
|||
- ls -lh
|
||||
- cat "$ARTIFACT.sum256"
|
||||
- sha256sum "$ARTIFACT"
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: [ develop, '*-rc' ]
|
||||
event: push
|
||||
sign_artifacts:
|
||||
image: plugins/gpgsign
|
||||
settings:
|
||||
|
@ -91,17 +81,9 @@ steps:
|
|||
exclude:
|
||||
- build/*.sum256
|
||||
detach_sign: true
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: [ develop, '*-rc' ]
|
||||
event: push
|
||||
publish_artifacts:
|
||||
image: alpine
|
||||
commands:
|
||||
- cp -fr build/* /tmp/friendica_files/
|
||||
volumes:
|
||||
- files:/tmp/friendica_files
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: [ develop, '*-rc' ]
|
||||
event: push
|
||||
|
|
|
@ -8,8 +8,9 @@ matrix:
|
|||
PHP_VERSION: 8.2.16
|
||||
|
||||
when:
|
||||
- branch:
|
||||
exclude: [ stable ]
|
||||
branch:
|
||||
exclude: [ stable ]
|
||||
event: [ pull_request, push ]
|
||||
|
||||
# This forces CI executions at the "opensocial" labeled location (because of much more power...)
|
||||
labels:
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
when:
|
||||
branch:
|
||||
exclude: [ stable ]
|
||||
event: [ pull_request, push ]
|
||||
|
||||
steps:
|
||||
check:
|
||||
image: fsfe/reuse:latest
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
when:
|
||||
branch:
|
||||
exclude: [ stable ]
|
||||
event: [ pull_request, push ]
|
||||
|
||||
steps:
|
||||
build_xgettext:
|
||||
image: friendicaci/transifex
|
||||
|
@ -11,7 +16,3 @@ steps:
|
|||
image: friendicaci/transifex
|
||||
commands:
|
||||
- /check-messages.sh
|
||||
|
||||
when:
|
||||
- branch:
|
||||
exclude: [ stable ]
|
||||
|
|
|
@ -19,6 +19,11 @@ matrix:
|
|||
labels:
|
||||
location: opensocial
|
||||
|
||||
when:
|
||||
branch:
|
||||
exclude: [ stable ]
|
||||
event: [ pull_request, push ]
|
||||
|
||||
steps:
|
||||
php-lint:
|
||||
image: php:${PHP_MAJOR_VERSION}
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
depends_on:
|
||||
- phpunit
|
||||
- code_standards_check
|
||||
|
||||
# This prevents executing this pipeline at other servers than ci.friendi.ca
|
||||
labels:
|
||||
location: friendica
|
||||
|
@ -13,6 +9,11 @@ labels:
|
|||
|
||||
skip_clone: true
|
||||
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: stable
|
||||
event: tag
|
||||
|
||||
steps:
|
||||
clone:
|
||||
image: alpine/git
|
||||
|
@ -21,10 +22,7 @@ steps:
|
|||
- git checkout $CI_COMMIT_BRANCH
|
||||
- git fetch origin $CI_COMMIT_REF
|
||||
- git merge $CI_COMMIT_SHA
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: stable
|
||||
event: tag
|
||||
|
||||
restore_cache:
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
|
@ -36,10 +34,6 @@ steps:
|
|||
- '.composer'
|
||||
volumes:
|
||||
- /tmp/drone-cache:/tmp/cache
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: stable
|
||||
event: tag
|
||||
composer_install:
|
||||
image: friendicaci/php8.2:php8.2.16
|
||||
commands:
|
||||
|
@ -47,10 +41,6 @@ steps:
|
|||
- export COMPOSER_HOME=.composer
|
||||
- composer validate
|
||||
- composer install --no-dev --optimize-autoloader
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: stable
|
||||
event: tag
|
||||
volumes:
|
||||
- /etc/hosts:/etc/hosts
|
||||
create_artifacts:
|
||||
|
@ -73,10 +63,6 @@ steps:
|
|||
- ls -lh
|
||||
- cat "$ARTIFACT.sum256"
|
||||
- sha256sum "$ARTIFACT"
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: stable
|
||||
event: tag
|
||||
sign_artifacts:
|
||||
image: plugins/gpgsign
|
||||
settings:
|
||||
|
@ -99,7 +85,3 @@ steps:
|
|||
- cp -fr build/* /tmp/friendica_files/
|
||||
volumes:
|
||||
- files:/tmp/friendica_files
|
||||
when:
|
||||
repo: friendica/friendica
|
||||
branch: stable
|
||||
event: tag
|
||||
|
|
64
CHANGELOG
64
CHANGELOG
|
@ -1,16 +1,76 @@
|
|||
Version 2024.12 (unreleased)
|
||||
Version 2025.02 (unreleased)
|
||||
Friendica Core
|
||||
Deprecated bin/daemon in favor of bin/console daemon (PR 14642) [nupplaphil]
|
||||
Deprecated bin/jetstream in favor of bin/console jetstream (PR 14655) [nupplaphil]
|
||||
|
||||
Friendica Addons
|
||||
|
||||
Closed Issues
|
||||
|
||||
Version 2024.09 (unreleased)
|
||||
Version 2024.12 (2024-12-31)
|
||||
Friendica Core
|
||||
Updates to the translations AR, BG, CA, CS, DE, EO, ES, ET, FR, GD, HU, IS, IT, JA, NL, PL, RU, SV
|
||||
Updates to the documentation [annando, bmillwood, tobiasd]
|
||||
Updates to the themes (frio) [haheute]
|
||||
Friendica Core is now REUSE compliant [tobiasd]
|
||||
General code cleanup [annando, nupplaphil, mexon]
|
||||
Improved federation with Bluesky, Hubzilla, Peertube, threads, Wordpress [annando]
|
||||
Improved the API [annando]
|
||||
Improved display of contact connection state [annando]
|
||||
Improved handling of bad webfinger requests [annando, mexon, zotanmew]
|
||||
Improved the order of actions on the 2FA settings page [tobiasd]
|
||||
Improved server type detection [annando]
|
||||
Improved content negotiation [annando]
|
||||
Improved expiration [annando]
|
||||
Improved contact archiving [annando]
|
||||
Improved delivery of content [annando]
|
||||
Improved displayed project icons [annando]
|
||||
Improved splitting of long postings via connectors [annando]
|
||||
Improved contact import [annando]
|
||||
Improved URL detection in searches [annando]
|
||||
Improved handling of blocked users [annando]
|
||||
Fixed a bug in creating app specific passwords [nupplaphil]
|
||||
Fixed a bug in importing some notes from Mastodon [annando]
|
||||
Fixed a bug with postings from buffer including images [annando]
|
||||
Fixed a apache2 problem with unsafe URLs [annando]
|
||||
Fixed a bug in the contact settings [annando]
|
||||
Fixed a bug with latin1 encoded databases [annando]
|
||||
Fixed a bug while uploading server blocklists [ne20002]
|
||||
Fixed a bug while parsing events [annando]
|
||||
Fixed a bug in the initial registry settings [annando]
|
||||
Fixed a bug in 0Auth with buffer [annando]
|
||||
Fixed a problem with rich HTML content [annando]
|
||||
Fixed a bug with private comments [annando]
|
||||
Fixed a bug in gettext [tobiasd]
|
||||
Fixed a bug in the installation process [tobiasd]
|
||||
Fixed schema.org issue [annando]
|
||||
Added admin info to stats module [nupplaphil]
|
||||
Added an option to exclude postings with images without ALT text [annando]
|
||||
Added an option to hide custom emojis [annando]
|
||||
Added support for HLS [annando]
|
||||
Added devcontainer for Friendica [ne20002]
|
||||
Added jetstream support for AT protocol [annando]
|
||||
Added native probe support for AT protocol [annando]
|
||||
Removed custom emojis from contact names [annando]
|
||||
Removed OStatus support [annando]
|
||||
|
||||
Friendica Addons
|
||||
bluesky
|
||||
Added block functionality [annando]
|
||||
Added option to complete threads [annando]
|
||||
Fixed issue with blocking contacts [annando]
|
||||
Improved handling of startersets [annando]
|
||||
Improved fetching of postings [annando]
|
||||
invidious [loma-one]
|
||||
unicode_smileys [loma-one]
|
||||
fancybox
|
||||
Deprecated the addon [tobiasd]
|
||||
|
||||
Closed Issues
|
||||
13270, 13943, 14121, 14126, 14145, 14174, 14212, 14244, 14281,
|
||||
14292, 14294, 14303, 14307, 14344, 14368, 14370, 14373, 14377,
|
||||
14381, 14413, 14421, 14525, 14450, 14451, 14464, 14487, 14488,
|
||||
14491, 14495, 14512, 14587, 14609, 14630
|
||||
|
||||
Version 2024.08 (2024-08-17)
|
||||
Friendica Core
|
||||
|
|
13
CREDITS.txt
13
CREDITS.txt
|
@ -23,6 +23,7 @@ Andi Stadler
|
|||
Andreas H.
|
||||
Andreas Neustifter
|
||||
Andrej Stieben
|
||||
Andrey Esin
|
||||
André Alves
|
||||
André Lohan
|
||||
Andy
|
||||
|
@ -46,6 +47,7 @@ beardyunixer
|
|||
Beatriz Vital
|
||||
Beluga
|
||||
Ben
|
||||
Ben Millwood
|
||||
Ben Roberts
|
||||
ben-utzer
|
||||
Beringer Zsolt
|
||||
|
@ -72,6 +74,7 @@ Christian Wiwie
|
|||
Cohan Robinson
|
||||
Colby Sollars
|
||||
Copiis
|
||||
cracrayol
|
||||
CrystalStiletto
|
||||
csolisr
|
||||
Cyboulette
|
||||
|
@ -91,9 +94,11 @@ Dean Townsley
|
|||
Denis Chenu
|
||||
dependabot[bot]
|
||||
Devlon Duthie
|
||||
dew-git
|
||||
Diego Souza
|
||||
Domovoy
|
||||
Doru DEACONU
|
||||
Dr. Tobias Quathamer
|
||||
Dylan Thiedeke
|
||||
Développeur égaré
|
||||
eddy2508
|
||||
|
@ -188,8 +193,10 @@ Kris
|
|||
Kristoffer Grundström
|
||||
ktlinux
|
||||
KulikAlex
|
||||
Laura Hausmann
|
||||
Lea1995polish
|
||||
Leberwurscht
|
||||
Leonard
|
||||
Leonard Lausen
|
||||
Lionel Triay
|
||||
loma-one
|
||||
|
@ -227,9 +234,11 @@ Mike Macgirvin
|
|||
miqrogroove
|
||||
Morgan McMillian
|
||||
mpanhans
|
||||
MrPetovan
|
||||
mytbk
|
||||
nathilia-peirce
|
||||
Nicola Spanti
|
||||
ne20002
|
||||
Nicolas Derive
|
||||
nnsrymni
|
||||
nobody
|
||||
|
@ -308,7 +317,6 @@ snajafov
|
|||
softmetz
|
||||
soko1
|
||||
Spencer Dub
|
||||
SpencerDub
|
||||
St John Karp
|
||||
Stanislav N.
|
||||
Steffen K9
|
||||
|
@ -340,6 +348,7 @@ Tom
|
|||
Tom Aurlund
|
||||
Tom Hu
|
||||
tomamplius
|
||||
tommy tomson
|
||||
tomtom84
|
||||
Tony Baldwin
|
||||
Torbjörn Andersson
|
||||
|
@ -370,6 +379,7 @@ Wanting Chen
|
|||
Wil Tur
|
||||
Wladimir Palant
|
||||
Wouter Broers
|
||||
www-data
|
||||
Xiaofei Xu
|
||||
XMPPはいいぞ
|
||||
xundeenergie
|
||||
|
@ -382,6 +392,7 @@ Zered
|
|||
zotlabs
|
||||
zottel
|
||||
Zvi ben Yaakov (a.k.a rdc)
|
||||
Éibhear Ó hAnluain
|
||||
Михаил
|
||||
Олексій Замковий
|
||||
朱陈锬
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
2024.12-dev
|
||||
2025.02-dev
|
||||
|
|
2
Vagrantfile
vendored
2
Vagrantfile
vendored
|
@ -7,7 +7,7 @@ public_folder = "/vagrant"
|
|||
|
||||
Vagrant.configure(2) do |config|
|
||||
# Set server to Debian 11 / Bullseye 64bit
|
||||
config.vm.box = "debian/bullseye64"
|
||||
config.vm.box = "debian/bookworm64"
|
||||
|
||||
# Disable automatic box update checking. If you disable this, then
|
||||
# boxes will only be checked for updates when the user runs
|
||||
|
|
|
@ -52,6 +52,7 @@ require dirname(__FILE__, 2) . '/vendor/autoload.php';
|
|||
|
||||
$dice = (new Dice())->addRules(require(dirname(__FILE__, 2) . '/static/dependencies.config.php'));
|
||||
|
||||
$app = \Friendica\App::fromDice($dice);
|
||||
$container = \Friendica\Core\Container::fromDice($dice);
|
||||
$app = \Friendica\App::fromContainer($container);
|
||||
|
||||
$app->processEjabberd();
|
||||
|
|
|
@ -14,20 +14,10 @@ if (php_sapi_name() !== 'cli') {
|
|||
}
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\DI;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
$dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php');
|
||||
/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
|
||||
$addonLoader = $dice->create(\Friendica\Core\Addon\Capability\ICanLoadAddons::class);
|
||||
$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies'));
|
||||
$dice = $dice->addRule(LoggerInterface::class, ['constructParams' => [LogChannel::CONSOLE]]);
|
||||
$dice = (new Dice())->addRules(require(dirname(__DIR__) . '/static/dependencies.config.php'));
|
||||
|
||||
/// @fixme Necessary until Hooks inside the Logger can get loaded without the DI-class
|
||||
DI::init($dice);
|
||||
\Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class));
|
||||
|
||||
(new Friendica\Core\Console($dice, $argv))->execute();
|
||||
$container = \Friendica\Core\Container::fromDice($dice);
|
||||
\Friendica\Core\Console::create($container, $_SERVER['argv'] ?? [])->execute();
|
||||
|
|
228
bin/daemon.php
228
bin/daemon.php
|
@ -5,6 +5,8 @@
|
|||
* SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* @deprecated 2025.02 use bin/console.php daemon instead
|
||||
*/
|
||||
|
||||
/**
|
||||
|
@ -18,230 +20,16 @@ if (php_sapi_name() !== 'cli') {
|
|||
}
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Update;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
// Get options
|
||||
$shortopts = 'f';
|
||||
$longopts = ['foreground'];
|
||||
$options = getopt($shortopts, $longopts);
|
||||
|
||||
// Ensure that daemon.php is executed from the base path of the installation
|
||||
if (!file_exists('index.php') && (sizeof($_SERVER['argv']) != 0)) {
|
||||
$directory = dirname($_SERVER['argv'][0]);
|
||||
|
||||
if (substr($directory, 0, 1) != '/') {
|
||||
$directory = $_SERVER['PWD'] . '/' . $directory;
|
||||
}
|
||||
$directory = realpath($directory . '/..');
|
||||
|
||||
chdir($directory);
|
||||
}
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
$dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php');
|
||||
/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
|
||||
$addonLoader = $dice->create(\Friendica\Core\Addon\Capability\ICanLoadAddons::class);
|
||||
$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies'));
|
||||
$dice = $dice->addRule(LoggerInterface::class, ['constructParams' => [Logger\Capability\LogChannel::DAEMON]]);
|
||||
$dice = (new Dice())->addRules(require(dirname(__DIR__) . '/static/dependencies.config.php'));
|
||||
|
||||
DI::init($dice);
|
||||
\Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class));
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
array_splice($argv, 1, 0, "daemon");
|
||||
|
||||
if (DI::mode()->isInstall()) {
|
||||
die("Friendica isn't properly installed yet.\n");
|
||||
}
|
||||
|
||||
DI::mode()->setExecutor(Mode::DAEMON);
|
||||
|
||||
DI::config()->reload();
|
||||
|
||||
if (empty(DI::config()->get('system', 'pidfile'))) {
|
||||
die(<<<TXT
|
||||
Please set system.pidfile in config/local.config.php. For example:
|
||||
|
||||
'system' => [
|
||||
'pidfile' => '/path/to/daemon.pid',
|
||||
],
|
||||
TXT
|
||||
);
|
||||
}
|
||||
|
||||
$pidfile = DI::config()->get('system', 'pidfile');
|
||||
|
||||
if (in_array('start', $_SERVER['argv'])) {
|
||||
$mode = 'start';
|
||||
}
|
||||
|
||||
if (in_array('stop', $_SERVER['argv'])) {
|
||||
$mode = 'stop';
|
||||
}
|
||||
|
||||
if (in_array('status', $_SERVER['argv'])) {
|
||||
$mode = 'status';
|
||||
}
|
||||
|
||||
$foreground = array_key_exists('f', $options) || array_key_exists('foreground', $options);
|
||||
|
||||
if (!isset($mode)) {
|
||||
die("Please use either 'start', 'stop' or 'status'.\n");
|
||||
}
|
||||
|
||||
if (empty($_SERVER['argv'][0])) {
|
||||
die("Unexpected script behaviour. This message should never occur.\n");
|
||||
}
|
||||
|
||||
$pid = null;
|
||||
|
||||
if (is_readable($pidfile)) {
|
||||
$pid = intval(file_get_contents($pidfile));
|
||||
}
|
||||
|
||||
if (empty($pid) && in_array($mode, ['stop', 'status'])) {
|
||||
DI::keyValue()->set('worker_daemon_mode', false);
|
||||
die("Pidfile wasn't found. Is the daemon running?\n");
|
||||
}
|
||||
|
||||
if ($mode == 'status') {
|
||||
if (posix_kill($pid, 0)) {
|
||||
die("Daemon process $pid is running.\n");
|
||||
}
|
||||
|
||||
unlink($pidfile);
|
||||
|
||||
DI::keyValue()->set('worker_daemon_mode', false);
|
||||
die("Daemon process $pid isn't running.\n");
|
||||
}
|
||||
|
||||
if ($mode == 'stop') {
|
||||
posix_kill($pid, SIGTERM);
|
||||
|
||||
unlink($pidfile);
|
||||
|
||||
Logger::notice('Worker daemon process was killed', ['pid' => $pid]);
|
||||
|
||||
DI::keyValue()->set('worker_daemon_mode', false);
|
||||
die("Worker daemon process $pid was killed.\n");
|
||||
}
|
||||
|
||||
if (!empty($pid) && posix_kill($pid, 0)) {
|
||||
die("Daemon process $pid is already running.\n");
|
||||
}
|
||||
|
||||
Logger::notice('Starting worker daemon.', ['pid' => $pid]);
|
||||
|
||||
if (!$foreground) {
|
||||
echo "Starting worker daemon.\n";
|
||||
|
||||
DBA::disconnect();
|
||||
|
||||
// Fork a daemon process
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
echo "Daemon couldn't be forked.\n";
|
||||
Logger::warning('Could not fork daemon');
|
||||
exit(1);
|
||||
} elseif ($pid) {
|
||||
// The parent process continues here
|
||||
if (!file_put_contents($pidfile, $pid)) {
|
||||
echo "Pid file wasn't written.\n";
|
||||
Logger::warning('Could not store pid file');
|
||||
posix_kill($pid, SIGTERM);
|
||||
exit(1);
|
||||
}
|
||||
echo 'Child process started with pid ' . $pid . ".\n";
|
||||
Logger::notice('Child process started', ['pid' => $pid]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// We now are in the child process
|
||||
register_shutdown_function('shutdown');
|
||||
|
||||
// Make the child the main process, detach it from the terminal
|
||||
if (posix_setsid() < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Closing all existing connections with the outside
|
||||
fclose(STDIN);
|
||||
|
||||
// And now connect the database again
|
||||
DBA::connect();
|
||||
}
|
||||
|
||||
DI::keyValue()->set('worker_daemon_mode', true);
|
||||
|
||||
// Just to be sure that this script really runs endlessly
|
||||
set_time_limit(0);
|
||||
|
||||
$wait_interval = intval(DI::config()->get('system', 'cron_interval', 5)) * 60;
|
||||
|
||||
$do_cron = true;
|
||||
$last_cron = 0;
|
||||
|
||||
// Now running as a daemon.
|
||||
while (true) {
|
||||
// Check the database structure and possibly fixes it
|
||||
Update::check(DI::basePath(), true);
|
||||
|
||||
if (!$do_cron && ($last_cron + $wait_interval) < time()) {
|
||||
Logger::info('Forcing cron worker call.', ['pid' => $pid]);
|
||||
$do_cron = true;
|
||||
}
|
||||
|
||||
if ($do_cron || (!DI::system()->isMaxLoadReached() && Worker::entriesExists() && Worker::isReady())) {
|
||||
Worker::spawnWorker($do_cron);
|
||||
} else {
|
||||
Logger::info('Cool down for 5 seconds', ['pid' => $pid]);
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
if ($do_cron) {
|
||||
// We force a reconnect of the database connection.
|
||||
// This is done to ensure that the connection don't get lost over time.
|
||||
DBA::reconnect();
|
||||
|
||||
$last_cron = time();
|
||||
}
|
||||
|
||||
$start = time();
|
||||
Logger::info('Sleeping', ['pid' => $pid, 'until' => gmdate(DateTimeFormat::MYSQL, $start + $wait_interval)]);
|
||||
|
||||
do {
|
||||
$seconds = (time() - $start);
|
||||
|
||||
// logarithmic wait time calculation.
|
||||
// Background: After jobs had been started, they often fork many workers.
|
||||
// To not waste too much time, the sleep period increases.
|
||||
$arg = (($seconds + 1) / ($wait_interval / 9)) + 1;
|
||||
$sleep = min(1000000, round(log10($arg) * 1000000, 0));
|
||||
usleep($sleep);
|
||||
|
||||
$pid = pcntl_waitpid(-1, $status, WNOHANG);
|
||||
if ($pid > 0) {
|
||||
Logger::info('Children quit via pcntl_waitpid', ['pid' => $pid, 'status' => $status]);
|
||||
}
|
||||
|
||||
$timeout = ($seconds >= $wait_interval);
|
||||
} while (!$timeout && !Worker\IPC::JobsExists());
|
||||
|
||||
if ($timeout) {
|
||||
$do_cron = true;
|
||||
Logger::info('Woke up after $wait_interval seconds.', ['pid' => $pid, 'sleep' => $wait_interval]);
|
||||
} else {
|
||||
$do_cron = false;
|
||||
Logger::info('Worker jobs are calling to be forked.', ['pid' => $pid]);
|
||||
}
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
posix_kill(posix_getpid(), SIGTERM);
|
||||
posix_kill(posix_getpid(), SIGHUP);
|
||||
}
|
||||
$container = \Friendica\Core\Container::fromDice($dice);
|
||||
\Friendica\Core\Console::create($container, $argv)->execute();
|
||||
|
|
24
bin/dev/fix-codestyle.sh
Executable file
24
bin/dev/fix-codestyle.sh
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash
|
||||
# SPDX-FileCopyrightText: 2010-2025 the Friendica project
|
||||
#
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
#
|
||||
# this script checks or fixes php-files, based on the php-cs rules
|
||||
#
|
||||
# You can use the following variables:
|
||||
# COMMAND ... the php-cs command to execute (default is "check --diff")
|
||||
# TARGET_BRANCH ... set the target branch for the current branch to create a diff between them
|
||||
#
|
||||
##
|
||||
|
||||
COMMAND=${COMMAND:-"check --diff"}
|
||||
|
||||
if [ -n "${TARGET_BRANCH}" ]; then
|
||||
CHANGED_FILES="$(git diff --name-only --diff-filter=ACMRTUXB "$(git ls-remote -q | grep refs/heads/"${TARGET_BRANCH}"$ | awk '{print $1}' | xargs git rev-parse )".."$(git rev-parse HEAD)")";
|
||||
else
|
||||
CHANGED_FILES="$(git diff --name-only --diff-filter=ACMRTUXB "$(git rev-parse HEAD)")";
|
||||
fi
|
||||
|
||||
EXTRA_ARGS=$(printf -- '--path-mode=intersection\n--\n%s' "${CHANGED_FILES}");
|
||||
|
||||
./bin/dev/php-cs-fixer/vendor/bin/php-cs-fixer ${COMMAND} --config=.php-cs-fixer.dist.php -v --using-cache=no ${EXTRA_ARGS}
|
|
@ -42,6 +42,18 @@ openssl genrsa -out "$SSL_DIR/xip.io.key" 4096
|
|||
openssl req -new -subj "$(echo -n "$SUBJ" | tr "\n" "/")" -key "$SSL_DIR/xip.io.key" -out "$SSL_DIR/xip.io.csr" -passin pass:$PASSPHRASE
|
||||
openssl x509 -req -days 365 -in "$SSL_DIR/xip.io.csr" -signkey "$SSL_DIR/xip.io.key" -out "$SSL_DIR/xip.io.crt"
|
||||
|
||||
#Install php
|
||||
echo ">>> Add PHP repository"
|
||||
apt-get install -qq -y lsb-release ca-certificates apt-transport-https software-properties-common gnupg
|
||||
echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/sury-php.list
|
||||
wget -qO - https://packages.sury.org/php/apt.gpg | sudo gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/php.gpg
|
||||
apt update
|
||||
|
||||
echo ">>> Installing PHP8"
|
||||
apt-get install -qq php libapache2-mod-php php8.3-cli php8.3-mysql php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml imagemagick php8.3-imagick php8.3-zip php8.3-gmp php8.3-intl
|
||||
|
||||
echo ">>> Installing PHP7"
|
||||
apt-get install -qq php7.4 php7.4-cli php7.4-mysql php7.4-curl php7.4-gd php7.4-mbstring php7.4-xml php7.4-imagick php7.4-zip php7.4-gmp php7.4-intl
|
||||
|
||||
#Install apache2
|
||||
echo ">>> Installing Apache2 webserver"
|
||||
|
@ -53,19 +65,6 @@ vhost -s 192.168.56.10.xip.io -d /var/www -p /etc/ssl/xip.io -c xip.io -a friend
|
|||
a2dissite 000-default
|
||||
service apache2 restart
|
||||
|
||||
#Install php
|
||||
echo ">>> Installing PHP7"
|
||||
apt-get install -qq php libapache2-mod-php php-cli php-mysql php-curl php-gd php-mbstring php-xml imagemagick php-imagick php-zip php-gmp
|
||||
systemctl restart apache2
|
||||
|
||||
echo ">>> Installing PHP8"
|
||||
apt-get install -qq -y lsb-release ca-certificates apt-transport-https software-properties-common gnupg
|
||||
echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/sury-php.list
|
||||
wget -qO - https://packages.sury.org/php/apt.gpg | sudo apt-key add -
|
||||
apt update
|
||||
apt-get install -qq php8.0 php8.0-cli php8.0-mysql php8.0-curl php8.0-gd php8.0-mbstring php8.0-xml php8.0-imagick php8.0-zip php8.0-gmp
|
||||
systemctl restart apache2
|
||||
|
||||
#Install mysql
|
||||
echo ">>> Installing Mysql"
|
||||
debconf-set-selections <<< "mariadb-server mariadb-server/root_password password root"
|
||||
|
@ -130,7 +129,7 @@ bin/console user password "$USER_NICK" "$USER_PASSW"
|
|||
# create cronjob - activate if you have enough memory in you dev VM
|
||||
# cronjob runs as www-data user
|
||||
echo ">>> Installing cronjob"
|
||||
echo "*/10 * * * * www-data cd /vagrant; /usr/bin/php bin/worker.php" >> /etc/cron.d/friendica
|
||||
echo "*/10 * * * * www-data cd /vagrant; /usr/bin/php bin/console.php worker" >> /etc/cron.d/friendica
|
||||
|
||||
# friendica needs write access to /tmp
|
||||
chmod 777 /tmp
|
||||
|
|
|
@ -6,16 +6,10 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* @deprecated 2025.02 use bin/console.php jetstream instead
|
||||
*/
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\Core\Addon;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Friendica\Protocol\ATProtocol\Jetstream;
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
|
||||
|
@ -23,162 +17,14 @@ if (php_sapi_name() !== 'cli') {
|
|||
}
|
||||
|
||||
// Ensure that Jetstream.php is executed from the base path of the installation
|
||||
if (!file_exists('index.php') && (sizeof((array)$_SERVER['argv']) != 0)) {
|
||||
$directory = dirname($_SERVER['argv'][0]);
|
||||
|
||||
if (substr($directory, 0, 1) != '/') {
|
||||
$directory = $_SERVER['PWD'] . '/' . $directory;
|
||||
}
|
||||
$directory = realpath($directory . '/..');
|
||||
|
||||
chdir($directory);
|
||||
}
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
$dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php');
|
||||
/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
|
||||
$addonLoader = $dice->create(\Friendica\Core\Addon\Capability\ICanLoadAddons::class);
|
||||
$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies'));
|
||||
$dice = $dice->addRule(LoggerInterface::class, ['constructParams' => [Logger\Capability\LogChannel::DAEMON]]);
|
||||
$dice = (new Dice())->addRules(require(dirname(__DIR__) . '/static/dependencies.config.php'));
|
||||
|
||||
DI::init($dice);
|
||||
\Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class));
|
||||
Addon::loadAddons();
|
||||
Hook::loadHooks();
|
||||
DI::config()->reload();
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
array_splice($argv, 1, 0, "jetstream");
|
||||
|
||||
if (DI::mode()->isInstall()) {
|
||||
die("Friendica isn't properly installed yet.\n");
|
||||
}
|
||||
|
||||
if (empty(DI::config()->get('jetstream', 'pidfile'))) {
|
||||
die(<<<TXT
|
||||
Please set jetstream.pidfile in config/local.config.php. For example:
|
||||
|
||||
'jetstream' => [
|
||||
'pidfile' => '/path/to/jetstream.pid',
|
||||
],
|
||||
TXT);
|
||||
}
|
||||
|
||||
if (!Addon::isEnabled('bluesky')) {
|
||||
die("Bluesky has to be enabled.\n");
|
||||
}
|
||||
|
||||
$pidfile = DI::config()->get('jetstream', 'pidfile');
|
||||
|
||||
if (in_array('start', (array)$_SERVER['argv'])) {
|
||||
$mode = 'start';
|
||||
}
|
||||
|
||||
if (in_array('stop', (array)$_SERVER['argv'])) {
|
||||
$mode = 'stop';
|
||||
}
|
||||
|
||||
if (in_array('status', (array)$_SERVER['argv'])) {
|
||||
$mode = 'status';
|
||||
}
|
||||
|
||||
if (!isset($mode)) {
|
||||
die("Please use either 'start', 'stop' or 'status'.\n");
|
||||
}
|
||||
|
||||
// Get options
|
||||
$shortopts = 'f';
|
||||
$longopts = ['foreground'];
|
||||
$options = getopt($shortopts, $longopts);
|
||||
|
||||
$foreground = array_key_exists('f', $options) || array_key_exists('foreground', $options);
|
||||
|
||||
if (empty($_SERVER['argv'][0])) {
|
||||
die("Unexpected script behaviour. This message should never occur.\n");
|
||||
}
|
||||
|
||||
$pid = null;
|
||||
|
||||
if (is_readable($pidfile)) {
|
||||
$pid = intval(file_get_contents($pidfile));
|
||||
}
|
||||
|
||||
if (empty($pid) && in_array($mode, ['stop', 'status'])) {
|
||||
die("Pidfile wasn't found. Is jetstream running?\n");
|
||||
}
|
||||
|
||||
if ($mode == 'status') {
|
||||
if (posix_kill($pid, 0)) {
|
||||
die("Jetstream process $pid is running.\n");
|
||||
}
|
||||
|
||||
unlink($pidfile);
|
||||
|
||||
die("Jetstream process $pid isn't running.\n");
|
||||
}
|
||||
|
||||
if ($mode == 'stop') {
|
||||
posix_kill($pid, SIGTERM);
|
||||
|
||||
unlink($pidfile);
|
||||
|
||||
Logger::notice('Jetstream process was killed', ['pid' => $pid]);
|
||||
|
||||
die("Jetstream process $pid was killed.\n");
|
||||
}
|
||||
|
||||
if (!empty($pid) && posix_kill($pid, 0)) {
|
||||
die("Jetstream process $pid is already running.\n");
|
||||
}
|
||||
|
||||
Logger::notice('Starting jetstream daemon.', ['pid' => $pid]);
|
||||
|
||||
if (!$foreground) {
|
||||
echo "Starting jetstream daemon.\n";
|
||||
|
||||
DBA::disconnect();
|
||||
|
||||
// Fork a daemon process
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
echo "Daemon couldn't be forked.\n";
|
||||
Logger::warning('Could not fork daemon');
|
||||
exit(1);
|
||||
} elseif ($pid) {
|
||||
// The parent process continues here
|
||||
if (!file_put_contents($pidfile, $pid)) {
|
||||
echo "Pid file wasn't written.\n";
|
||||
Logger::warning('Could not store pid file');
|
||||
posix_kill($pid, SIGTERM);
|
||||
exit(1);
|
||||
}
|
||||
echo 'Child process started with pid ' . $pid . ".\n";
|
||||
Logger::notice('Child process started', ['pid' => $pid]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// We now are in the child process
|
||||
register_shutdown_function('shutdown');
|
||||
|
||||
// Make the child the main process, detach it from the terminal
|
||||
if (posix_setsid() < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Closing all existing connections with the outside
|
||||
fclose(STDIN);
|
||||
|
||||
// And now connect the database again
|
||||
DBA::connect();
|
||||
}
|
||||
|
||||
// Just to be sure that this script really runs endlessly
|
||||
set_time_limit(0);
|
||||
|
||||
// Now running as a daemon.
|
||||
$jetstream = $dice->create(Jetstream::class);
|
||||
$jetstream->listen();
|
||||
|
||||
function shutdown()
|
||||
{
|
||||
posix_kill(posix_getpid(), SIGTERM);
|
||||
posix_kill(posix_getpid(), SIGHUP);
|
||||
}
|
||||
$container = \Friendica\Core\Container::fromDice($dice);
|
||||
\Friendica\Core\Console::create($container, $argv)->execute();
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*
|
||||
* Starts the background processing
|
||||
*
|
||||
* @deprecated 2025.02 use bin/console.php worker instead
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
|
@ -15,64 +17,16 @@ if (php_sapi_name() !== 'cli') {
|
|||
}
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\Core\Update;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\DI;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
// Get options
|
||||
$shortopts = 'sn';
|
||||
$longopts = ['spawn', 'no_cron'];
|
||||
$options = getopt($shortopts, $longopts);
|
||||
|
||||
// Ensure that worker.php is executed from the base path of the installation
|
||||
if (!file_exists("index.php") && (sizeof($_SERVER["argv"]) != 0)) {
|
||||
$directory = dirname($_SERVER["argv"][0]);
|
||||
|
||||
if (substr($directory, 0, 1) != '/') {
|
||||
$directory = $_SERVER["PWD"] . '/' . $directory;
|
||||
}
|
||||
$directory = realpath($directory . '/..');
|
||||
|
||||
chdir($directory);
|
||||
}
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
$dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php');
|
||||
/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
|
||||
$addonLoader = $dice->create(\Friendica\Core\Addon\Capability\ICanLoadAddons::class);
|
||||
$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies'));
|
||||
$dice = $dice->addRule(LoggerInterface::class, ['constructParams' => [LogChannel::WORKER]]);
|
||||
$dice = (new Dice())->addRules(require(dirname(__DIR__) . '/static/dependencies.config.php'));
|
||||
|
||||
DI::init($dice);
|
||||
\Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class));
|
||||
$argv = $_SERVER['argv'] ?? [];
|
||||
array_splice($argv, 1, 0, "worker");
|
||||
|
||||
DI::mode()->setExecutor(Mode::WORKER);
|
||||
|
||||
// Check the database structure and possibly fixes it
|
||||
Update::check(DI::basePath(), true);
|
||||
|
||||
// Quit when in maintenance
|
||||
if (!DI::mode()->has(Mode::MAINTENANCEDISABLED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$spawn = array_key_exists('s', $options) || array_key_exists('spawn', $options);
|
||||
|
||||
if ($spawn) {
|
||||
Worker::spawnWorker();
|
||||
exit();
|
||||
}
|
||||
|
||||
$run_cron = !array_key_exists('n', $options) && !array_key_exists('no_cron', $options);
|
||||
|
||||
$process = DI::process()->create(getmypid(), basename(__FILE__));
|
||||
|
||||
Worker::processQueue($run_cron, $process);
|
||||
|
||||
Worker::unclaimProcess($process);
|
||||
|
||||
DI::process()->delete($process);
|
||||
$container = \Friendica\Core\Container::fromDice($dice);
|
||||
\Friendica\Core\Console::create($container, $argv)->execute();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
-- ------------------------------------------
|
||||
-- Friendica 2024.12-dev (Yellow Archangel)
|
||||
-- Friendica 2025.02-dev (Interrupted Fern)
|
||||
-- DB_UPDATE_VERSION 1576
|
||||
-- ------------------------------------------
|
||||
|
||||
|
|
|
@ -268,7 +268,7 @@ You might wish to delete/rename `config/local.config.php` to another name and dr
|
|||
Set up a cron job or scheduled task to run the worker once every 5-10 minutes in order to perform background processing.
|
||||
Example:
|
||||
|
||||
cd /base/directory; /path/to/php bin/worker.php
|
||||
cd /base/directory; /path/to/php bin/console.php worker
|
||||
|
||||
Change "/base/directory", and "/path/to/php" as appropriate for your situation.
|
||||
|
||||
|
@ -277,7 +277,7 @@ Change "/base/directory", and "/path/to/php" as appropriate for your situation.
|
|||
If you are using a Linux server, run "crontab -e" and add a line like the
|
||||
one shown, substituting for your unique paths and settings:
|
||||
|
||||
*/10 * * * * cd /home/myname/mywebsite; /usr/bin/php bin/worker.php
|
||||
*/10 * * * * cd /home/myname/mywebsite; /usr/bin/php bin/console.php worker
|
||||
|
||||
You can generally find the location of PHP by executing "which php".
|
||||
If you run into trouble with this section please contact your hosting provider for assistance.
|
||||
|
@ -290,11 +290,11 @@ Once you have installed Friendica and created an admin account as part of the pr
|
|||
#### worker alternative: daemon
|
||||
Otherwise, you’ll need to use the command line on your remote server and start the Friendica daemon (background task) using the following command:
|
||||
|
||||
cd /path/to/friendica; php bin/daemon.php start
|
||||
cd /path/to/friendica; php bin/console.php daemon start
|
||||
|
||||
Once started, you can check the daemon status using the following command:
|
||||
|
||||
cd /path/to/friendica; php bin/daemon.php status
|
||||
cd /path/to/friendica; php bin/console.php daemon status
|
||||
|
||||
After a server restart or any other failure, the daemon needs to be restarted.
|
||||
This could be achieved by a cronjob.
|
||||
|
@ -426,7 +426,7 @@ provided by one of our members.
|
|||
>
|
||||
> */10 * * * * cd /var/www/friendica/friendica/ && sudo -u www-data /usr/bin/php \
|
||||
> -d suhosin.executor.func.blacklist=none \
|
||||
> -d suhosin.executor.eval.blacklist=none -f bin/worker.php
|
||||
> -d suhosin.executor.eval.blacklist=none -f bin/console.php
|
||||
>
|
||||
> This worked well for simple test cases, but the friendica-cron still failed
|
||||
> with a fatal error:
|
||||
|
@ -435,7 +435,7 @@ provided by one of our members.
|
|||
> (attacker 'REMOTE_ADDR not set', file '/var/www/friendica/friendica/boot.php',
|
||||
> line 1341)
|
||||
>
|
||||
> After a while I noticed, that `bin/worker.php` calls further PHP script via `proc_open`.
|
||||
> After a while I noticed, that `bin/console.php worker` calls further PHP script via `proc_open`.
|
||||
> These scripts themselves also use `proc_open` and fail, because they are NOT
|
||||
> called with `-d suhosin.executor.func.blacklist=none`.
|
||||
>
|
||||
|
|
|
@ -28,7 +28,7 @@ Jetstream is a service that connects to the Bluesky firehose.
|
|||
With Jetstream, messages arrive in real time rather than having to be polled.
|
||||
It also enables real-time processing of blocks or tracking activities performed by the user via the Bluesky website or application.
|
||||
|
||||
To enable Jetstream processing, run `bin/jetstream.php' from the command line.
|
||||
To enable Jetstream processing, run `bin/console.php jetstream' from the command line.
|
||||
You will need to define the process id file in local.config.php in the 'jetstream' section using the key 'pidfile'.
|
||||
|
||||
To keep track of the messages processed and the drift (the time difference between the date of the message and the date the system processed that message), some fields are added to the statistics endpoint.
|
||||
To keep track of the messages processed and the drift (the time difference between the date of the message and the date the system processed that message), some fields are added to the statistics endpoint.
|
||||
|
|
|
@ -55,9 +55,9 @@ You should see an output like this:
|
|||
|
||||
Finally, you may also want to optimise your database with the following command: ``mysqloptimize -p friendica-db``
|
||||
|
||||
### Going offline
|
||||
### Going offline
|
||||
Stop background tasks and put your server in maintenance mode.
|
||||
1. If you had set up a worker cron job like this ``*/10 * * * * cd /var/www/friendica; /usr/bin/php bin/worker.php`` run ``crontab -e`` and comment out this line. Alternatively if you deploy a worker daemon, disable this instead.
|
||||
1. If you had set up a worker cron job like this ``*/10 * * * * cd /var/www/friendica; /usr/bin/php bin/console.php worker`` run ``crontab -e`` and comment out this line. Alternatively if you deploy a worker daemon, disable this instead.
|
||||
2. Put your server into maintenance mode: ``bin/console maintenance 1 "We are currently upgrading our system and will be back soon."``
|
||||
|
||||
## Dumping DB
|
||||
|
@ -73,12 +73,12 @@ Import your database on your new server: ``mysql -p friendica_db < your-friendic
|
|||
|
||||
### Configuration file
|
||||
Copy your old server's configuration file to ``config/local.config.php``.
|
||||
Ensure the newly created database credentials are identical to the setting in the configuration file; otherwise update them accordingly.
|
||||
Ensure the newly created database credentials are identical to the setting in the configuration file; otherwise update them accordingly.
|
||||
|
||||
### Cron job for worker
|
||||
Set up the required daily cron job.
|
||||
Run ``crontab -e`` and add the following line according to your system specification
|
||||
``*/10 * * * * cd /var/www/friendica; /usr/bin/php bin/worker.php``
|
||||
``*/10 * * * * cd /var/www/friendica; /usr/bin/php bin/console.php worker``
|
||||
|
||||
### DNS settings
|
||||
Adjust your DNS records by pointing them to your new server.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
This is your Suggested Friends page.
|
||||
If you get lost, you can <a href="help/Quick-Start-makenewfriends">click this link</a> to bring yourself back here.
|
||||
If you get lost, you can <a href="help/Quick-Start-makingnewfriends">click this link</a> to bring yourself back here.
|
||||
|
||||
This is a bit like the Friend Suggestions page of Facebook.
|
||||
Everybody on this list has agreed that they may be suggested as a friend.
|
||||
|
@ -16,6 +16,6 @@ Click the link at the top of this page to go back to the suggested friends list
|
|||
Feel uncomfortable adding people you don't know?
|
||||
Don't worry - that's where <a href="help/Quick-Start-groupsandpages">Groups and Pages</a> come in!
|
||||
|
||||
<iframe src="suggest" width="950" height="600"></iframe>
|
||||
<iframe src="contact/suggestions" width="950" height="600"></iframe>
|
||||
|
||||
|
||||
|
|
|
@ -210,13 +210,13 @@ Gehe in den Friendica-Hauptordner und führe den Kommandozeilen Befehl aus:
|
|||
Erstelle einen Cron job oder einen regelmäßigen Task, um den Poller alle 5-10 Minuten im Hintergrund ablaufen zu lassen.
|
||||
Beispiel:
|
||||
|
||||
cd /base/directory; /path/to/php bin/worker.php
|
||||
cd /base/directory; /path/to/php bin/console.php worker
|
||||
|
||||
Ändere "/base/directory" und "/path/to/php" auf deine Systemvorgaben.
|
||||
|
||||
Wenn du einen Linux-Server nutzt, benutze den Befehl "crontab -e" und ergänze eine Zeile wie die Folgende; angepasst an dein System
|
||||
|
||||
`*/10 * * * * cd /home/myname/mywebsite; /usr/bin/php bin/worker.php`
|
||||
`*/10 * * * * cd /home/myname/mywebsite; /usr/bin/php bin/console.php worker`
|
||||
|
||||
Du kannst den PHP-Pfad finden, indem du den Befehl „which php“ ausführst.
|
||||
Wenn du Schwierigkeiten mit diesem Schritt hast, kannst du deinen Hosting-Anbieter kontaktieren.
|
||||
|
|
|
@ -27,7 +27,7 @@ Jetstream ist ein Dienst, der sich mit dem Bluesky-Firehose verbindet.
|
|||
Mit Jetstream kommen die Nachrichten in Echtzeit an und müssen nicht erst abgefragt werden.
|
||||
Es ermöglicht auch die Echtzeitverarbeitung von Blöcken oder Tracking-Aktivitäten, die über die Bluesky-Website oder -Anwendung durchgeführt werden.
|
||||
|
||||
Um die Jetstream-Verarbeitung zu aktivieren, führe `bin/jetstream.php' über die Befehlszeile aus.
|
||||
Um die Jetstream-Verarbeitung zu aktivieren, führe `bin/console.php daemon' über die Befehlszeile aus.
|
||||
Du musst vorher die Prozess-ID-Datei in local.config.php im Abschnitt „jetstream“ mit dem Schlüssel „pidfile“ definieren.
|
||||
|
||||
Um die verarbeiteten Nachrichten und die Drift (die Zeitdifferenz zwischen dem Datum der Nachricht und dem Datum, an dem das System diese Nachricht verarbeitet hat) zu verfolgen, wurden dem Statistik-Endpunkt einige Felder hinzugefügt.
|
||||
|
|
|
@ -3,26 +3,26 @@ Neue Freunde finden
|
|||
|
||||
* [Zur Startseite der Hilfe](help)
|
||||
|
||||
Hier siehst Du die Kontaktvorschläge.
|
||||
Wenn Du Dich mal verirrt hast, kannst Du diesen Link klicken und wieder hierher kommen.
|
||||
Hier siehst Du die Kontaktvorschläge.
|
||||
Wenn Du Dich mal verirrt hast, kannst Du <a href="help/Quick-Start-makingnewfriends">diesen Link klicken</a> und wieder hierher kommen.
|
||||
|
||||
Diese Seite funktioniert in etwa wie die Seite für Kontaktvorschläge in Facebook.
|
||||
Jeder auf dieser Liste hat zugestimmt, als Kontaktvorschlag zu erscheinen.
|
||||
Diese Seite funktioniert in etwa wie die Seite für Kontaktvorschläge in Facebook.
|
||||
Jeder auf dieser Liste hat zugestimmt, als Kontaktvorschlag zu erscheinen.
|
||||
Das bedeutet, das sie Anfragen meist nicht ablehnen, da sie neue Leute kennenlernen wollen.
|
||||
|
||||
Siehst Du jemanden, der Dir interessant erscheint?
|
||||
Klicke auf den "Verbinden"-Knopf beim Foto.
|
||||
Als nächstes kommst Du zur Seite "Freundschafts-/Kontaktanfrage".
|
||||
Fülle das Formular wie vorgegeben aus und trage optional eine kleine Notiz ein.
|
||||
Nun musst Du nur noch auf die Bestätigung warten.
|
||||
Siehst Du jemanden, der Dir interessant erscheint?
|
||||
Klicke auf den "Verbinden"-Knopf beim Foto.
|
||||
Als nächstes kommst Du zur Seite "Freundschafts-/Kontaktanfrage".
|
||||
Fülle das Formular wie vorgegeben aus und trage optional eine kleine Notiz ein.
|
||||
Nun musst Du nur noch auf die Bestätigung warten.
|
||||
Beachte dabei, dass es sich um reale Personen handelt und es somit etwas dauern kann.
|
||||
|
||||
Jetzt, nachdem Du jemanden hinzugefügt hast, weißt Du vielleicht nicht mehr, wie Du zurückkommst.
|
||||
Jetzt, nachdem Du jemanden hinzugefügt hast, weißt Du vielleicht nicht mehr, wie Du zurückkommst.
|
||||
Klicke einfach auf den Link oben auf dieser Seite und Du gelangst zur Seite mit den Kontaktvorschlägen zurück, um weitere Personen hinzuzufügen.
|
||||
|
||||
Du willst nicht einfach Personen hinzufügen, die du nicht kennst?
|
||||
Du willst nicht einfach Personen hinzufügen, die du nicht kennst?
|
||||
Kein Problem - an dieser Stelle kommen wir zu den <a href="help/Quick-Start-groupsandpages">Gruppen und Seiten</a>.
|
||||
|
||||
<iframe src="suggest" width="950" height="600"></iframe>
|
||||
<iframe src="contact/suggestions" width="950" height="600"></iframe>
|
||||
|
||||
|
||||
|
|
|
@ -17,8 +17,9 @@ require __DIR__ . '/vendor/autoload.php';
|
|||
|
||||
$request = \GuzzleHttp\Psr7\ServerRequest::fromGlobals();
|
||||
|
||||
$dice = (new Dice())->addRules(include __DIR__ . '/static/dependencies.config.php');
|
||||
$dice = (new Dice())->addRules(require(__DIR__ . '/static/dependencies.config.php'));
|
||||
|
||||
$app = \Friendica\App::fromDice($dice);
|
||||
$container = \Friendica\Core\Container::fromDice($dice);
|
||||
$app = \Friendica\App::fromContainer($container);
|
||||
|
||||
$app->processRequest($request, $start_time);
|
||||
|
|
|
@ -6,4 +6,4 @@ Description=Friendica Worker
|
|||
User=http
|
||||
#Adapt the path in the following line to your system, use 'which php' to find php path,
|
||||
#provide the absolute path for worker.php
|
||||
ExecStart=/usr/bin/php /www/path/bin/worker.php &
|
||||
ExecStart=/usr/bin/php /www/path/bin/console.php worker &
|
||||
|
|
152
src/App.php
152
src/App.php
|
@ -15,23 +15,28 @@ use Friendica\App\Page;
|
|||
use Friendica\App\Request;
|
||||
use Friendica\App\Router;
|
||||
use Friendica\Capabilities\ICanCreateResponses;
|
||||
use Friendica\Capabilities\ICanHandleRequests;
|
||||
use Friendica\Content\Nav;
|
||||
use Friendica\Core\Config\Factory\Config;
|
||||
use Friendica\Core\Container;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Session\Capability\IHandleUserSessions;
|
||||
use Friendica\Database\Definition\DbaDefinition;
|
||||
use Friendica\Database\Definition\ViewDefinition;
|
||||
use Friendica\Module\Maintenance;
|
||||
use Friendica\Security\Authentication;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Core\Update;
|
||||
use Friendica\Module\Special\HTTPException as ModuleHTTPException;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\ATProtocol\DID;
|
||||
use Friendica\Security\ExAuth;
|
||||
use Friendica\Security\OpenWebAuth;
|
||||
use Friendica\Util\BasePath;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\HTTPInputData;
|
||||
use Friendica\Util\HTTPSignature;
|
||||
|
@ -52,16 +57,16 @@ use Psr\Log\LoggerInterface;
|
|||
class App
|
||||
{
|
||||
const PLATFORM = 'Friendica';
|
||||
const CODENAME = 'Yellow Archangel';
|
||||
const VERSION = '2024.12-dev';
|
||||
const CODENAME = 'Interrupted Fern';
|
||||
const VERSION = '2025.02-dev';
|
||||
|
||||
public static function fromDice(Dice $dice): self
|
||||
public static function fromContainer(Container $container): self
|
||||
{
|
||||
return new self($dice);
|
||||
return new self($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var Dice
|
||||
* @var Container
|
||||
*/
|
||||
private $container;
|
||||
|
||||
|
@ -116,24 +121,20 @@ class App
|
|||
*/
|
||||
private $appHelper;
|
||||
|
||||
private function __construct(Dice $container)
|
||||
private function __construct(Container $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public function processRequest(ServerRequestInterface $request, float $start_time): void
|
||||
{
|
||||
$this->setupContainerForAddons();
|
||||
|
||||
$this->container = $this->container->addRule(Mode::class, [
|
||||
$this->container->addRule(Mode::class, [
|
||||
'call' => [
|
||||
['determineRunMode', [false, $request->getServerParams()], Dice::CHAIN_CALL],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->setupLegacyServiceLocator();
|
||||
|
||||
$this->registerErrorHandler();
|
||||
$this->container->setup(LogChannel::APP, false);
|
||||
|
||||
$this->requestId = $this->container->create(Request::class)->getRequestId();
|
||||
$this->auth = $this->container->create(Authentication::class);
|
||||
|
@ -147,39 +148,35 @@ class App
|
|||
$this->session = $this->container->create(IHandleUserSessions::class);
|
||||
$this->appHelper = $this->container->create(AppHelper::class);
|
||||
|
||||
$this->load(
|
||||
$this->loadSetupForFrontend(
|
||||
$request,
|
||||
$this->container->create(DbaDefinition::class),
|
||||
$this->container->create(ViewDefinition::class),
|
||||
);
|
||||
|
||||
$this->registerTemplateEngine();
|
||||
|
||||
$this->mode->setExecutor(Mode::INDEX);
|
||||
|
||||
$this->runFrontend(
|
||||
$this->container->create(Router::class),
|
||||
$this->container->create(IManagePersonalConfigValues::class),
|
||||
$this->container->create(Page::class),
|
||||
$this->container->create(Nav::class),
|
||||
$this->container->create(ModuleHTTPException::class),
|
||||
new HTTPInputData($request->getServerParams()),
|
||||
$start_time,
|
||||
$request->getServerParams()
|
||||
$request
|
||||
);
|
||||
}
|
||||
|
||||
public function processEjabberd(): void
|
||||
{
|
||||
$this->setupContainerForAddons();
|
||||
$this->container->setup(LogChannel::AUTH_JABBERED, false);
|
||||
|
||||
$this->container = $this->container->addRule(LoggerInterface::class,[
|
||||
'constructParams' => [LogChannel::AUTH_JABBERED],
|
||||
]);
|
||||
|
||||
$this->setupLegacyServiceLocator();
|
||||
|
||||
$this->registerErrorHandler();
|
||||
/** @var BasePath */
|
||||
$basePath = $this->container->create(BasePath::class);
|
||||
|
||||
// Check the database structure and possibly fixes it
|
||||
\Friendica\Core\Update::check(\Friendica\DI::basePath(), true);
|
||||
Update::check($basePath->getPath(), true);
|
||||
|
||||
$appMode = $this->container->create(Mode::class);
|
||||
|
||||
|
@ -190,28 +187,15 @@ class App
|
|||
}
|
||||
}
|
||||
|
||||
private function setupContainerForAddons(): void
|
||||
private function registerTemplateEngine(): void
|
||||
{
|
||||
/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
|
||||
$addonLoader = $this->container->create(\Friendica\Core\Addon\Capability\ICanLoadAddons::class);
|
||||
|
||||
$this->container = $this->container->addRules($addonLoader->getActiveAddonConfig('dependencies'));
|
||||
}
|
||||
|
||||
private function setupLegacyServiceLocator(): void
|
||||
{
|
||||
\Friendica\DI::init($this->container);
|
||||
}
|
||||
|
||||
private function registerErrorHandler(): void
|
||||
{
|
||||
\Friendica\Core\Logger\Handler\ErrorHandler::register($this->container->create(LoggerInterface::class));
|
||||
Renderer::registerTemplateEngine('Friendica\Render\FriendicaSmartyEngine');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the whole app instance
|
||||
*/
|
||||
private function load(DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition)
|
||||
private function loadSetupForFrontend(ServerRequestInterface $request, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition)
|
||||
{
|
||||
if ($this->config->get('system', 'ini_max_execution_time') !== false) {
|
||||
set_time_limit((int)$this->config->get('system', 'ini_max_execution_time'));
|
||||
|
@ -233,7 +217,7 @@ class App
|
|||
|
||||
if ($this->mode->has(Mode::DBAVAILABLE)) {
|
||||
Core\Hook::loadHooks();
|
||||
$loader = (new Config())->createConfigFileManager($this->appHelper->getBasePath(), $_SERVER);
|
||||
$loader = (new Config())->createConfigFileManager($this->appHelper->getBasePath(), $request->getServerParams());
|
||||
Core\Hook::callAll('load_config', $loader);
|
||||
|
||||
// Hooks are now working, reload the whole definitions with hook enabled
|
||||
|
@ -242,8 +226,6 @@ class App
|
|||
}
|
||||
|
||||
$this->loadDefaultTimezone();
|
||||
// Register template engines
|
||||
Core\Renderer::registerTemplateEngine('Friendica\Render\FriendicaSmartyEngine');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,31 +255,30 @@ class App
|
|||
*
|
||||
* This probably should change to limit the size of this monster method.
|
||||
*
|
||||
* @param Router $router
|
||||
* @param IManagePersonalConfigValues $pconfig
|
||||
* @param Page $page The Friendica page printing container
|
||||
* @param ModuleHTTPException $httpException The possible HTTP Exception container
|
||||
* @param HTTPInputData $httpInput A library for processing PHP input streams
|
||||
* @param float $start_time The start time of the overall script execution
|
||||
* @param array $server The $_SERVER array
|
||||
*
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private function runFrontend(
|
||||
Router $router,
|
||||
IManagePersonalConfigValues $pconfig,
|
||||
Page $page,
|
||||
Nav $nav,
|
||||
ModuleHTTPException $httpException,
|
||||
HTTPInputData $httpInput,
|
||||
float $start_time,
|
||||
array $server
|
||||
ServerRequestInterface $request
|
||||
) {
|
||||
$requeststring = ($server['REQUEST_METHOD'] ?? '') . ' ' . ($server['REQUEST_URI'] ?? '') . ' ' . ($server['SERVER_PROTOCOL'] ?? '');
|
||||
$this->logger->debug('Request received', ['address' => $server['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $server['HTTP_REFERER'] ?? '', 'user-agent' => $server['HTTP_USER_AGENT'] ?? '']);
|
||||
$httpInput = new HTTPInputData($request->getServerParams());
|
||||
$serverVars = $request->getServerParams();
|
||||
$queryVars = $request->getQueryParams();
|
||||
|
||||
$requeststring = ($serverVars['REQUEST_METHOD'] ?? '') . ' ' . ($serverVars['REQUEST_URI'] ?? '') . ' ' . ($serverVars['SERVER_PROTOCOL'] ?? '');
|
||||
$this->logger->debug('Request received', ['address' => $serverVars['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $serverVars['HTTP_REFERER'] ?? '', 'user-agent' => $serverVars['HTTP_USER_AGENT'] ?? '']);
|
||||
$request_start = microtime(true);
|
||||
$request = $_REQUEST;
|
||||
$request = $_REQUEST;
|
||||
|
||||
$this->profiler->set($start_time, 'start');
|
||||
$this->profiler->set(microtime(true), 'classinit');
|
||||
|
@ -314,42 +295,42 @@ class App
|
|||
if (!$this->mode->isInstall()) {
|
||||
// Force SSL redirection
|
||||
if ($this->config->get('system', 'force_ssl') &&
|
||||
(empty($server['HTTPS']) || $server['HTTPS'] === 'off') &&
|
||||
(empty($server['HTTP_X_FORWARDED_PROTO']) || $server['HTTP_X_FORWARDED_PROTO'] === 'http') &&
|
||||
!empty($server['REQUEST_METHOD']) &&
|
||||
$server['REQUEST_METHOD'] === 'GET') {
|
||||
(empty($serverVars['HTTPS']) || $serverVars['HTTPS'] === 'off') &&
|
||||
(empty($serverVars['HTTP_X_FORWARDED_PROTO']) || $serverVars['HTTP_X_FORWARDED_PROTO'] === 'http') &&
|
||||
!empty($serverVars['REQUEST_METHOD']) &&
|
||||
$serverVars['REQUEST_METHOD'] === 'GET') {
|
||||
System::externalRedirect($this->baseURL . '/' . $this->args->getQueryString());
|
||||
}
|
||||
Core\Hook::callAll('init_1');
|
||||
}
|
||||
|
||||
DID::routeRequest($this->args->getCommand(), $server);
|
||||
DID::routeRequest($this->args->getCommand(), $serverVars);
|
||||
|
||||
if ($this->mode->isNormal() && !$this->mode->isBackend()) {
|
||||
$requester = HTTPSignature::getSigner('', $server);
|
||||
$requester = HTTPSignature::getSigner('', $serverVars);
|
||||
if (!empty($requester)) {
|
||||
OpenWebAuth::addVisitorCookieForHandle($requester);
|
||||
}
|
||||
}
|
||||
|
||||
// ZRL
|
||||
if (!empty($_GET['zrl']) && $this->mode->isNormal() && !$this->mode->isBackend() && !$this->session->getLocalUserId()) {
|
||||
if (!empty($queryVars['zrl']) && $this->mode->isNormal() && !$this->mode->isBackend() && !$this->session->getLocalUserId()) {
|
||||
// Only continue when the given profile link seems valid.
|
||||
// Valid profile links contain a path with "/profile/" and no query parameters
|
||||
if ((parse_url($_GET['zrl'], PHP_URL_QUERY) == '') &&
|
||||
strpos(parse_url($_GET['zrl'], PHP_URL_PATH) ?? '', '/profile/') !== false) {
|
||||
$this->auth->setUnauthenticatedVisitor($_GET['zrl']);
|
||||
if ((parse_url($queryVars['zrl'], PHP_URL_QUERY) == '') &&
|
||||
strpos(parse_url($queryVars['zrl'], PHP_URL_PATH) ?? '', '/profile/') !== false) {
|
||||
$this->auth->setUnauthenticatedVisitor($queryVars['zrl']);
|
||||
OpenWebAuth::zrlInit();
|
||||
} else {
|
||||
// Someone came with an invalid parameter, maybe as a DDoS attempt
|
||||
// We simply stop processing here
|
||||
$this->logger->debug('Invalid ZRL parameter.', ['zrl' => $_GET['zrl']]);
|
||||
$this->logger->debug('Invalid ZRL parameter.', ['zrl' => $queryVars['zrl']]);
|
||||
throw new HTTPException\ForbiddenException();
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($_GET['owt']) && $this->mode->isNormal()) {
|
||||
$token = $_GET['owt'];
|
||||
if (!empty($queryVars['owt']) && $this->mode->isNormal()) {
|
||||
$token = $queryVars['owt'];
|
||||
OpenWebAuth::init($token);
|
||||
}
|
||||
|
||||
|
@ -420,11 +401,11 @@ class App
|
|||
|
||||
// The "view" module is required to show the theme CSS
|
||||
if (!$this->mode->isInstall() && !$this->mode->has(Mode::MAINTENANCEDISABLED) && $moduleName !== 'view') {
|
||||
$module = $router->getModule(Maintenance::class);
|
||||
$module = $this->createModuleInstance(Maintenance::class);
|
||||
} else {
|
||||
// determine the module class and save it to the module instance
|
||||
// @todo there's an implicit dependency due SESSION::start(), so it has to be called here (yet)
|
||||
$module = $router->getModule();
|
||||
$module = $this->createModuleInstance(null);
|
||||
}
|
||||
|
||||
// Display can change depending on the requested language, so it shouldn't be cached whole
|
||||
|
@ -444,17 +425,42 @@ class App
|
|||
$response = $page->run($this->appHelper, $this->session, $this->baseURL, $this->args, $this->mode, $response, $this->l10n, $this->profiler, $this->config, $pconfig, $nav, $this->session->getLocalUserId());
|
||||
}
|
||||
|
||||
$this->logger->debug('Request processed sucessfully', ['response' => $response->getStatusCode(), 'address' => $server['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $server['HTTP_REFERER'] ?? '', 'user-agent' => $server['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
|
||||
$this->logSlowCalls(microtime(true) - $request_start, $response->getStatusCode(), $requeststring, $server['HTTP_USER_AGENT'] ?? '');
|
||||
$this->logger->debug('Request processed sucessfully', ['response' => $response->getStatusCode(), 'address' => $serverVars['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $serverVars['HTTP_REFERER'] ?? '', 'user-agent' => $serverVars['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
|
||||
$this->logSlowCalls(microtime(true) - $request_start, $response->getStatusCode(), $requeststring, $serverVars['HTTP_USER_AGENT'] ?? '');
|
||||
System::echoResponse($response);
|
||||
} catch (HTTPException $e) {
|
||||
$this->logger->debug('Request processed with exception', ['response' => $e->getCode(), 'address' => $server['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $server['HTTP_REFERER'] ?? '', 'user-agent' => $server['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
|
||||
$this->logSlowCalls(microtime(true) - $request_start, $e->getCode(), $requeststring, $server['HTTP_USER_AGENT'] ?? '');
|
||||
$this->logger->debug('Request processed with exception', ['response' => $e->getCode(), 'address' => $serverVars['REMOTE_ADDR'] ?? '', 'request' => $requeststring, 'referer' => $serverVars['HTTP_REFERER'] ?? '', 'user-agent' => $serverVars['HTTP_USER_AGENT'] ?? '', 'duration' => number_format(microtime(true) - $request_start, 3)]);
|
||||
$this->logSlowCalls(microtime(true) - $request_start, $e->getCode(), $requeststring, $serverVars['HTTP_USER_AGENT'] ?? '');
|
||||
$httpException->rawContent($e);
|
||||
}
|
||||
$page->logRuntime($this->config, 'runFrontend');
|
||||
}
|
||||
|
||||
private function createModuleInstance(?string $moduleClass = null): ICanHandleRequests
|
||||
{
|
||||
/** @var Router $router */
|
||||
$router = $this->container->create(Router::class);
|
||||
|
||||
$moduleClass = $moduleClass ?? $router->getModuleClass();
|
||||
$parameters = $router->getParameters();
|
||||
|
||||
$dice_profiler_threshold = $this->config->get('system', 'dice_profiler_threshold', 0);
|
||||
|
||||
$stamp = microtime(true);
|
||||
|
||||
/** @var ICanHandleRequests $module */
|
||||
$module = $this->container->create($moduleClass, $parameters);
|
||||
|
||||
if ($dice_profiler_threshold > 0) {
|
||||
$dur = floatval(microtime(true) - $stamp);
|
||||
if ($dur >= $dice_profiler_threshold) {
|
||||
$this->logger->notice('Dice module creation lasts too long.', ['duration' => round($dur, 3), 'module' => $moduleClass, 'parameters' => $parameters]);
|
||||
}
|
||||
}
|
||||
|
||||
return $module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log slow page executions
|
||||
*
|
||||
|
|
|
@ -89,12 +89,6 @@ class Router
|
|||
/** @var bool */
|
||||
private $isLocalUser;
|
||||
|
||||
/** @var float */
|
||||
private $dice_profiler_threshold;
|
||||
|
||||
/** @var Dice */
|
||||
private $dice;
|
||||
|
||||
/** @var string */
|
||||
private $baseRoutesFilepath;
|
||||
|
||||
|
@ -113,11 +107,10 @@ class Router
|
|||
* @param IManageConfigValues $config
|
||||
* @param Arguments $args
|
||||
* @param LoggerInterface $logger
|
||||
* @param Dice $dice
|
||||
* @param IHandleUserSessions $userSession
|
||||
* @param RouteCollector|null $routeCollector
|
||||
*/
|
||||
public function __construct(array $server, string $baseRoutesFilepath, L10n $l10n, ICanCache $cache, ICanLock $lock, IManageConfigValues $config, Arguments $args, LoggerInterface $logger, Dice $dice, IHandleUserSessions $userSession, RouteCollector $routeCollector = null)
|
||||
public function __construct(array $server, string $baseRoutesFilepath, L10n $l10n, ICanCache $cache, ICanLock $lock, IManageConfigValues $config, Arguments $args, LoggerInterface $logger, IHandleUserSessions $userSession, RouteCollector $routeCollector = null)
|
||||
{
|
||||
$this->baseRoutesFilepath = $baseRoutesFilepath;
|
||||
$this->l10n = $l10n;
|
||||
|
@ -125,11 +118,9 @@ class Router
|
|||
$this->lock = $lock;
|
||||
$this->args = $args;
|
||||
$this->config = $config;
|
||||
$this->dice = $dice;
|
||||
$this->server = $server;
|
||||
$this->logger = $logger;
|
||||
$this->isLocalUser = !empty($userSession->getLocalUserId());
|
||||
$this->dice_profiler_threshold = $config->get('system', 'dice_profiler_threshold', 0);
|
||||
|
||||
$this->routeCollector = $routeCollector ?? new RouteCollector(new Std(), new GroupCountBased());
|
||||
|
||||
|
@ -328,23 +319,9 @@ class Router
|
|||
}
|
||||
}
|
||||
|
||||
public function getModule(?string $module_class = null): ICanHandleRequests
|
||||
public function getParameters(): array
|
||||
{
|
||||
$moduleClass = $module_class ?? $this->getModuleClass();
|
||||
|
||||
$stamp = microtime(true);
|
||||
|
||||
/** @var ICanHandleRequests $module */
|
||||
$module = $this->dice->create($moduleClass, $this->parameters);
|
||||
|
||||
if ($this->dice_profiler_threshold > 0) {
|
||||
$dur = floatval(microtime(true) - $stamp);
|
||||
if ($dur >= $this->dice_profiler_threshold) {
|
||||
$this->logger->notice('Dice module creation lasts too long.', ['duration' => round($dur, 3), 'module' => $moduleClass, 'parameters' => $this->parameters]);
|
||||
}
|
||||
}
|
||||
|
||||
return $module;
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -164,6 +164,19 @@ abstract class BaseModule implements ICanHandleRequests
|
|||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Module GET method to process submitted data
|
||||
*
|
||||
* Extend this method if the module is supposed to process GET requests.
|
||||
* Doesn't display any content
|
||||
*
|
||||
* @param string[] $request The $_REQUEST content
|
||||
* @return void
|
||||
*/
|
||||
protected function get(array $request = [])
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -210,17 +223,20 @@ abstract class BaseModule implements ICanHandleRequests
|
|||
switch ($this->args->getMethod()) {
|
||||
case Router::DELETE:
|
||||
$this->delete($request);
|
||||
return $this->response->generate();
|
||||
break;
|
||||
case Router::PATCH:
|
||||
$this->patch($request);
|
||||
return $this->response->generate();
|
||||
break;
|
||||
case Router::POST:
|
||||
Core\Hook::callAll($this->args->getModuleName() . '_mod_post', $request);
|
||||
$this->post($request);
|
||||
return $this->response->generate();
|
||||
break;
|
||||
case Router::PUT:
|
||||
$this->put($request);
|
||||
return $this->response->generate();
|
||||
break;
|
||||
case Router::GET:
|
||||
$this->get($request);
|
||||
break;
|
||||
}
|
||||
|
||||
$timestamp = microtime(true);
|
||||
|
|
39
src/Console/AbstractConsole.php
Normal file
39
src/Console/AbstractConsole.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
// Copyright (C) 2010-2024, the Friendica project
|
||||
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Friendica\Console;
|
||||
|
||||
use Asika\SimpleConsole\Console;
|
||||
use Friendica\Core\Console as CoreConsole;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
|
||||
/**
|
||||
* Abstract Console class for common settings
|
||||
*/
|
||||
abstract class AbstractConsole extends Console
|
||||
{
|
||||
/**
|
||||
* Overwrite this const in case you want to switch the LogChannel for this console command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const LOG_CHANNEL = LogChannel::CONSOLE;
|
||||
|
||||
/**
|
||||
* Checks, if the Console command was executed outside `bin/console.php` and prints the correct execution
|
||||
*
|
||||
* @param string $command the current command
|
||||
*/
|
||||
protected function checkDeprecated(string $command): void
|
||||
{
|
||||
if (substr($this->executable, -strlen(CoreConsole::getDefaultExecutable())) === CoreConsole::getDefaultExecutable()) {
|
||||
$this->out(sprintf("'%s' is deprecated and will removed. Please use 'bin/console.php %s' instead", $this->executable, $command));
|
||||
}
|
||||
}
|
||||
}
|
228
src/Console/Daemon.php
Normal file
228
src/Console/Daemon.php
Normal file
|
@ -0,0 +1,228 @@
|
|||
<?php
|
||||
|
||||
// Copyright (C) 2010-2024, the Friendica project
|
||||
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Friendica\Console;
|
||||
|
||||
use Asika\SimpleConsole\CommandArgsException;
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\KeyValueStorage\Capability\IManageKeyValuePairs;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Core\Update;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\System\Daemon as SysDaemon;
|
||||
use Friendica\Util\BasePath;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Console command for interacting with the daemon
|
||||
*/
|
||||
final class Daemon extends AbstractConsole
|
||||
{
|
||||
public const LOG_CHANNEL = LogChannel::DAEMON;
|
||||
|
||||
private Mode $mode;
|
||||
private IManageConfigValues $config;
|
||||
private IManageKeyValuePairs $keyValue;
|
||||
private BasePath $basePath;
|
||||
private System $system;
|
||||
private LoggerInterface $logger;
|
||||
private Database $dba;
|
||||
private SysDaemon $daemon;
|
||||
|
||||
/**
|
||||
* @param Mode $mode
|
||||
* @param IManageConfigValues $config
|
||||
* @param IManageKeyValuePairs $keyValue
|
||||
* @param BasePath $basePath
|
||||
* @param System $system
|
||||
* @param LoggerInterface $logger
|
||||
* @param Database $dba
|
||||
* @param SysDaemon $daemon
|
||||
* @param array|null $argv
|
||||
*/
|
||||
public function __construct(Mode $mode, IManageConfigValues $config, IManageKeyValuePairs $keyValue, BasePath $basePath, System $system, LoggerInterface $logger, Database $dba, SysDaemon $daemon, array $argv = null)
|
||||
{
|
||||
parent::__construct($argv);
|
||||
|
||||
$this->mode = $mode;
|
||||
$this->config = $config;
|
||||
$this->keyValue = $keyValue;
|
||||
$this->basePath = $basePath;
|
||||
$this->system = $system;
|
||||
$this->logger = $logger;
|
||||
$this->dba = $dba;
|
||||
$this->daemon = $daemon;
|
||||
}
|
||||
|
||||
protected function getHelp(): string
|
||||
{
|
||||
return <<<HELP
|
||||
Daemon - Interact with the Friendica daemon
|
||||
Synopsis
|
||||
bin/console daemon start [-h|--help|-?] [-v] [-f]
|
||||
bin/console daemon stop [-h|--help|-?] [-v]
|
||||
bin/console daemon status [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
Interact with the Friendica daemon
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
-f|--foreground Runs the daemon in the foreground
|
||||
|
||||
Examples
|
||||
bin/console daemon start -f
|
||||
Starts the daemon in the foreground
|
||||
|
||||
bin/console daemon status
|
||||
Gets the status of the daemon
|
||||
HELP;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
$this->checkDeprecated('daemon');
|
||||
|
||||
if ($this->mode->isInstall()) {
|
||||
throw new RuntimeException("Friendica isn't properly installed yet");
|
||||
}
|
||||
|
||||
$this->mode->setExecutor(Mode::DAEMON);
|
||||
|
||||
$this->config->reload();
|
||||
|
||||
if (empty($this->config->get('system', 'pidfile'))) {
|
||||
throw new RuntimeException(
|
||||
<<< TXT
|
||||
Please set system.pidfile in config/local.config.php. For example:
|
||||
|
||||
'system' => [
|
||||
'pidfile' => '/path/to/daemon.pid',
|
||||
],
|
||||
TXT
|
||||
);
|
||||
}
|
||||
|
||||
$pidfile = $this->config->get('system', 'pidfile');
|
||||
|
||||
$daemonMode = $this->getArgument(0);
|
||||
$foreground = $this->getOption(['f', 'foreground']) ?? false;
|
||||
|
||||
if (empty($daemonMode)) {
|
||||
throw new CommandArgsException("Please use either 'start', 'stop' or 'status'");
|
||||
}
|
||||
|
||||
$this->daemon->init($pidfile);
|
||||
|
||||
if ($daemonMode == 'status') {
|
||||
if ($this->daemon->isRunning()) {
|
||||
$this->out(sprintf("Daemon process %s is running (%s)", $this->daemon->getPid(), $this->daemon->getPidfile()));
|
||||
} else {
|
||||
$this->out(sprintf("Daemon process %s isn't running (%s)", $this->daemon->getPid(), $this->daemon->getPidfile()));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($daemonMode == 'stop') {
|
||||
if (!$this->daemon->isRunning()) {
|
||||
$this->out(sprintf("Daemon process %s isn't running (%s)", $this->daemon->getPid(), $this->daemon->getPidfile()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($this->daemon->stop()) {
|
||||
$this->keyValue->set('worker_daemon_mode', false);
|
||||
$this->out(sprintf("Daemon process %s was killed (%s)", $this->daemon->getPid(), $this->daemon->getPidfile()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($this->daemon->isRunning()) {
|
||||
$this->out(sprintf("Daemon process %s is already running (%s)", $this->daemon->getPid(), $this->daemon->getPidfile()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($daemonMode == "start") {
|
||||
$this->out("Starting Friendica daemon");
|
||||
|
||||
$this->daemon->start(function () {
|
||||
$wait_interval = intval($this->config->get('system', 'cron_interval', 5)) * 60;
|
||||
|
||||
$do_cron = true;
|
||||
$last_cron = 0;
|
||||
|
||||
$path = $this->basePath->getPath();
|
||||
|
||||
// Now running as a daemon.
|
||||
while (true) {
|
||||
// Check the database structure and possibly fixes it
|
||||
Update::check($path, true);
|
||||
|
||||
if (!$do_cron && ($last_cron + $wait_interval) < time()) {
|
||||
$this->logger->info('Forcing cron worker call.', ['pid' => $this->daemon->getPid()]);
|
||||
$do_cron = true;
|
||||
}
|
||||
|
||||
if ($do_cron || (!$this->system->isMaxLoadReached() && Worker::entriesExists() && Worker::isReady())) {
|
||||
Worker::spawnWorker($do_cron);
|
||||
} else {
|
||||
$this->logger->info('Cool down for 5 seconds', ['pid' => $this->daemon->getPid()]);
|
||||
sleep(5);
|
||||
}
|
||||
|
||||
if ($do_cron) {
|
||||
// We force a reconnect of the database connection.
|
||||
// This is done to ensure that the connection don't get lost over time.
|
||||
$this->dba->reconnect();
|
||||
|
||||
$last_cron = time();
|
||||
}
|
||||
|
||||
$start = time();
|
||||
$this->logger->info('Sleeping', ['pid' => $this->daemon->getPid(), 'until' => gmdate(DateTimeFormat::MYSQL, $start + $wait_interval)]);
|
||||
|
||||
do {
|
||||
$seconds = (time() - $start);
|
||||
|
||||
// logarithmic wait time calculation.
|
||||
// Background: After jobs had been started, they often fork many workers.
|
||||
// To not waste too much time, the sleep period increases.
|
||||
$arg = (($seconds + 1) / ($wait_interval / 9)) + 1;
|
||||
$sleep = min(1000000, round(log10($arg) * 1000000, 0));
|
||||
|
||||
$this->daemon->sleep((int)$sleep);
|
||||
|
||||
$timeout = ($seconds >= $wait_interval);
|
||||
} while (!$timeout && !Worker\IPC::JobsExists());
|
||||
|
||||
if ($timeout) {
|
||||
$do_cron = true;
|
||||
$this->logger->info('Woke up after $wait_interval seconds.', ['pid' => $this->daemon->getPid(), 'sleep' => $wait_interval]);
|
||||
} else {
|
||||
$do_cron = false;
|
||||
$this->logger->info('Worker jobs are calling to be forked.', ['pid' => $this->daemon->getPid()]);
|
||||
}
|
||||
}
|
||||
}, $foreground);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->err('Invalid command');
|
||||
$this->out($this->getHelp());
|
||||
return 1;
|
||||
}
|
||||
}
|
163
src/Console/JetstreamDaemon.php
Normal file
163
src/Console/JetstreamDaemon.php
Normal file
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
|
||||
// Copyright (C) 2010-2025, the Friendica project
|
||||
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Friendica\Console;
|
||||
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core\Addon;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\KeyValueStorage\Capability\IManageKeyValuePairs;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\Protocol\ATProtocol\Jetstream;
|
||||
use Friendica\System\Daemon as SysDaemon;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Console command for interacting with the daemon
|
||||
*/
|
||||
final class JetstreamDaemon extends AbstractConsole
|
||||
{
|
||||
public const LOG_CHANNEL = LogChannel::DAEMON;
|
||||
|
||||
private Mode $mode;
|
||||
private IManageConfigValues $config;
|
||||
private IManageKeyValuePairs $keyValue;
|
||||
private SysDaemon $daemon;
|
||||
private Jetstream $jetstream;
|
||||
|
||||
/**
|
||||
* @param Mode $mode
|
||||
* @param IManageConfigValues $config
|
||||
* @param IManageKeyValuePairs $keyValue
|
||||
* @param SysDaemon $daemon
|
||||
* @param Jetstream $jetstream
|
||||
* @param array|null $argv
|
||||
*/
|
||||
public function __construct(Mode $mode, IManageConfigValues $config, IManageKeyValuePairs $keyValue, SysDaemon $daemon, Jetstream $jetstream, array $argv = null)
|
||||
{
|
||||
parent::__construct($argv);
|
||||
|
||||
$this->mode = $mode;
|
||||
$this->config = $config;
|
||||
$this->keyValue = $keyValue;
|
||||
$this->jetstream = $jetstream;
|
||||
$this->daemon = $daemon;
|
||||
}
|
||||
|
||||
protected function getHelp(): string
|
||||
{
|
||||
return <<<HELP
|
||||
jetstream - Interact with the Jetstream daemon
|
||||
Synopsis
|
||||
bin/console jetstream start [-h|--help|-?] [-v] [-f]
|
||||
bin/console jetstream stop [-h|--help|-?] [-v]
|
||||
bin/console jetstream status [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
Interact with the Jetstream daemon
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
-f|--foreground Runs the daemon in the foreground
|
||||
|
||||
Examples
|
||||
bin/console jetstream start -f
|
||||
Starts the daemon in the foreground
|
||||
|
||||
bin/console jetstream status
|
||||
Gets the status of the daemon
|
||||
HELP;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
$this->checkDeprecated('jetstream');
|
||||
|
||||
if ($this->mode->isInstall()) {
|
||||
throw new RuntimeException("Friendica isn't properly installed yet");
|
||||
}
|
||||
|
||||
$this->config->reload();
|
||||
|
||||
if (empty($this->config->get('jetstream', 'pidfile'))) {
|
||||
throw new RuntimeException(
|
||||
<<< TXT
|
||||
Please set jetstream.pidfile in config/local.config.php. For example:
|
||||
|
||||
'jetstream' => [
|
||||
'pidfile' => '/path/to/jetstream.pid',
|
||||
],
|
||||
TXT
|
||||
);
|
||||
}
|
||||
|
||||
Addon::loadAddons();
|
||||
Hook::loadHooks();
|
||||
|
||||
if (!Addon::isEnabled('bluesky')) {
|
||||
throw new RuntimeException("Bluesky has to be enabled.\n");
|
||||
}
|
||||
|
||||
$pidfile = $this->config->get('jetstream', 'pidfile');
|
||||
|
||||
$daemonMode = $this->getArgument(0);
|
||||
$foreground = $this->getOption(['f', 'foreground']) ?? false;
|
||||
|
||||
if (empty($daemonMode)) {
|
||||
throw new RuntimeException("Please use either 'start', 'stop' or 'status'");
|
||||
}
|
||||
|
||||
$this->daemon->init($pidfile);
|
||||
|
||||
if ($daemonMode == 'status') {
|
||||
if ($this->daemon->isRunning()) {
|
||||
$this->out(sprintf("Daemon process %s is running (%s)", $this->daemon->getPid(), $this->daemon->getPidfile()));
|
||||
} else {
|
||||
$this->out(sprintf("Daemon process %s isn't running (%s)", $this->daemon->getPid(), $this->daemon->getPidfile()));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($daemonMode == 'stop') {
|
||||
if (!$this->daemon->isRunning()) {
|
||||
$this->out(sprintf("Daemon process %s isn't running (%s)", $this->daemon->getPid(), $this->daemon->getPidfile()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($this->daemon->stop()) {
|
||||
$this->keyValue->set('worker_daemon_mode', false);
|
||||
$this->out(sprintf("Daemon process %s was killed (%s)", $this->daemon->getPid(), $this->daemon->getPidfile()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($this->daemon->isRunning()) {
|
||||
$this->out(sprintf("Daemon process %s is already running (%s)", $this->daemon->getPid(), $this->daemon->getPidfile()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($daemonMode == "start") {
|
||||
$this->out("Starting Jetstream daemon");
|
||||
|
||||
$this->daemon->start(function () {
|
||||
$this->jetstream->listen();
|
||||
}, $foreground);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->err('Invalid command');
|
||||
$this->out($this->getHelp());
|
||||
return 1;
|
||||
}
|
||||
}
|
100
src/Console/Worker.php
Normal file
100
src/Console/Worker.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
// Copyright (C) 2010-2024, the Friendica project
|
||||
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Friendica\Console;
|
||||
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\Core\Update;
|
||||
use Friendica\Core\Worker as CoreWorker;
|
||||
use Friendica\Core\Worker\Repository\Process as ProcessRepository;
|
||||
use Friendica\Util\BasePath;
|
||||
|
||||
/**
|
||||
* Console command for starting worker
|
||||
*/
|
||||
final class Worker extends AbstractConsole
|
||||
{
|
||||
public const LOG_CHANNEL = LogChannel::WORKER;
|
||||
|
||||
private Mode $mode;
|
||||
private BasePath $basePath;
|
||||
private ProcessRepository $processRepo;
|
||||
|
||||
/**
|
||||
* @param Mode $mode
|
||||
* @param BasePath $basePath
|
||||
* @param ProcessRepository $processRepo
|
||||
* @param array|null $argv
|
||||
*/
|
||||
public function __construct(Mode $mode, BasePath $basePath, ProcessRepository $processRepo, array $argv = null)
|
||||
{
|
||||
parent::__construct($argv);
|
||||
|
||||
$this->mode = $mode;
|
||||
$this->basePath = $basePath;
|
||||
$this->processRepo = $processRepo;
|
||||
}
|
||||
|
||||
protected function getHelp(): string
|
||||
{
|
||||
return <<<HELP
|
||||
Worker - Start a worker
|
||||
Synopsis
|
||||
bin/console worker [-h|--help|-?] [-v] [-n|--no_cron] [-s|--spawn]
|
||||
|
||||
Description
|
||||
Start a worker process
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
-n|--no_cron Don't executes the Cronjob
|
||||
-s|--spawn Spawn an additional worker
|
||||
|
||||
Examples
|
||||
bin/console worker -n
|
||||
Starts the worker without executing other recurring tasks
|
||||
|
||||
bin/console worker -s
|
||||
Starts the worker and immediately spawn another worker process
|
||||
HELP;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
$this->checkDeprecated('worker');
|
||||
|
||||
$this->mode->setExecutor(Mode::WORKER);
|
||||
|
||||
// Check the database structure and possibly fixes it
|
||||
Update::check($this->basePath->getPath(), true);
|
||||
|
||||
// Quit when in maintenance
|
||||
if (!$this->mode->has(Mode::MAINTENANCEDISABLED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$spawn = $this->getOption(['s', 'spawn'], false);
|
||||
|
||||
if ($spawn) {
|
||||
CoreWorker::spawnWorker();
|
||||
exit();
|
||||
}
|
||||
|
||||
$run_cron = !$this->getOption(['n', 'no_cron'], false);
|
||||
|
||||
$process = $this->processRepo->create(getmypid(), 'worker.php');
|
||||
|
||||
CoreWorker::processQueue($run_cron, $process);
|
||||
CoreWorker::unclaimProcess($process);
|
||||
|
||||
$this->processRepo->delete($process);
|
||||
}
|
||||
}
|
|
@ -69,7 +69,7 @@ class VCard
|
|||
} else {
|
||||
$pcontact = Contact::selectFirst([], ['uid' => DI::userSession()->getLocalUserId(), 'uri-id' => $contact['uri-id'], 'deleted' => false]);
|
||||
|
||||
$id = $pcontact['id'] ?? 0;
|
||||
$id = $pcontact['id'] ?? $contact['id'];
|
||||
$rel = $pcontact['rel'] ?? Contact::NOTHING;
|
||||
$pending = $pcontact['pending'] ?? false;
|
||||
|
||||
|
|
|
@ -7,23 +7,34 @@
|
|||
|
||||
namespace Friendica\Core;
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica;
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
|
||||
/**
|
||||
* Description of Console
|
||||
*/
|
||||
class Console extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
// Disables the default help handling
|
||||
protected $helpOptions = [];
|
||||
protected $customHelpOptions = ['h', 'help', '?'];
|
||||
/** @var string The default executable for a console call */
|
||||
private const CONSOLE_EXECUTABLE = 'bin/console.php';
|
||||
|
||||
/**
|
||||
* @var Dice The DI library
|
||||
* @return string The default executable for a console call
|
||||
*/
|
||||
protected $dice;
|
||||
public static function getDefaultExecutable(): string
|
||||
{
|
||||
return self::CONSOLE_EXECUTABLE;
|
||||
}
|
||||
|
||||
// Disables the default help handling
|
||||
protected $helpOptions = [];
|
||||
protected array $customHelpOptions = ['h', 'help', '?'];
|
||||
|
||||
/**
|
||||
* @var Container The Container
|
||||
*/
|
||||
protected Container $container;
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
|
@ -37,6 +48,9 @@ Commands:
|
|||
config Edit site config
|
||||
contact Contact management
|
||||
createdoxygen Generate Doxygen headers
|
||||
daemon Interact with the Friendica daemon
|
||||
jetstream Interact with the Jetstream daemon
|
||||
worker Start worker process
|
||||
dbstructure Do database updates
|
||||
docbloxerrorchecker Check the file tree for DocBlox errors
|
||||
extract Generate translation string file for the Friendica project (deprecated)
|
||||
|
@ -66,48 +80,55 @@ HELP;
|
|||
return $help;
|
||||
}
|
||||
|
||||
protected $subConsoles = [
|
||||
'addon' => Friendica\Console\Addon::class,
|
||||
'archivecontact' => Friendica\Console\ArchiveContact::class,
|
||||
'autoinstall' => Friendica\Console\AutomaticInstallation::class,
|
||||
'cache' => Friendica\Console\Cache::class,
|
||||
'clearavatarcache' => Friendica\Console\ClearAvatarCache::class,
|
||||
'config' => Friendica\Console\Config::class,
|
||||
'contact' => Friendica\Console\Contact::class,
|
||||
'createdoxygen' => Friendica\Console\CreateDoxygen::class,
|
||||
'docbloxerrorchecker' => Friendica\Console\DocBloxErrorChecker::class,
|
||||
'dbstructure' => Friendica\Console\DatabaseStructure::class,
|
||||
'extract' => Friendica\Console\Extract::class,
|
||||
protected array $subConsoles = [
|
||||
'addon' => Friendica\Console\Addon::class,
|
||||
'archivecontact' => Friendica\Console\ArchiveContact::class,
|
||||
'autoinstall' => Friendica\Console\AutomaticInstallation::class,
|
||||
'cache' => Friendica\Console\Cache::class,
|
||||
'clearavatarcache' => Friendica\Console\ClearAvatarCache::class,
|
||||
'config' => Friendica\Console\Config::class,
|
||||
'contact' => Friendica\Console\Contact::class,
|
||||
'createdoxygen' => Friendica\Console\CreateDoxygen::class,
|
||||
'daemon' => Friendica\Console\Daemon::class,
|
||||
'jetstream' => Friendica\Console\JetstreamDaemon::class,
|
||||
'worker' => Friendica\Console\Worker::class,
|
||||
'docbloxerrorchecker' => Friendica\Console\DocBloxErrorChecker::class,
|
||||
'dbstructure' => Friendica\Console\DatabaseStructure::class,
|
||||
'extract' => Friendica\Console\Extract::class,
|
||||
'fixapdeliveryworkertaskparameters' => Friendica\Console\FixAPDeliveryWorkerTaskParameters::class,
|
||||
'globalcommunityblock' => Friendica\Console\GlobalCommunityBlock::class,
|
||||
'globalcommunitysilence' => Friendica\Console\GlobalCommunitySilence::class,
|
||||
'lock' => Friendica\Console\Lock::class,
|
||||
'maintenance' => Friendica\Console\Maintenance::class,
|
||||
'mergecontacts' => Friendica\Console\MergeContacts::class,
|
||||
'movetoavatarcache' => Friendica\Console\MoveToAvatarCache::class,
|
||||
'php2po' => Friendica\Console\PhpToPo::class,
|
||||
'postupdate' => Friendica\Console\PostUpdate::class,
|
||||
'po2php' => Friendica\Console\PoToPhp::class,
|
||||
'relay' => Friendica\Console\Relay::class,
|
||||
'relocate' => Friendica\Console\Relocate::class,
|
||||
'serverblock' => Friendica\Console\ServerBlock::class,
|
||||
'storage' => Friendica\Console\Storage::class,
|
||||
'test' => Friendica\Console\Test::class,
|
||||
'typo' => Friendica\Console\Typo::class,
|
||||
'user' => Friendica\Console\User::class,
|
||||
'globalcommunityblock' => Friendica\Console\GlobalCommunityBlock::class,
|
||||
'globalcommunitysilence' => Friendica\Console\GlobalCommunitySilence::class,
|
||||
'lock' => Friendica\Console\Lock::class,
|
||||
'maintenance' => Friendica\Console\Maintenance::class,
|
||||
'mergecontacts' => Friendica\Console\MergeContacts::class,
|
||||
'movetoavatarcache' => Friendica\Console\MoveToAvatarCache::class,
|
||||
'php2po' => Friendica\Console\PhpToPo::class,
|
||||
'postupdate' => Friendica\Console\PostUpdate::class,
|
||||
'po2php' => Friendica\Console\PoToPhp::class,
|
||||
'relay' => Friendica\Console\Relay::class,
|
||||
'relocate' => Friendica\Console\Relocate::class,
|
||||
'serverblock' => Friendica\Console\ServerBlock::class,
|
||||
'storage' => Friendica\Console\Storage::class,
|
||||
'test' => Friendica\Console\Test::class,
|
||||
'typo' => Friendica\Console\Typo::class,
|
||||
'user' => Friendica\Console\User::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* CliInput Friendica constructor.
|
||||
*
|
||||
* @param Dice $dice The DI library
|
||||
* @param array $argv
|
||||
* @param Container $container The Friendica container
|
||||
*/
|
||||
public function __construct(Dice $dice, array $argv = null)
|
||||
public function __construct(Container $container, array $argv = null)
|
||||
{
|
||||
parent::__construct($argv);
|
||||
|
||||
$this->dice = $dice;
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public static function create(Container $container, array $argv = null): Console
|
||||
{
|
||||
return new self($container, $argv);
|
||||
}
|
||||
|
||||
protected function doExecute(): int
|
||||
|
@ -166,12 +187,14 @@ HELP;
|
|||
|
||||
$className = $this->subConsoles[$command];
|
||||
|
||||
Friendica\DI::init($this->dice);
|
||||
|
||||
Renderer::registerTemplateEngine('Friendica\Render\FriendicaSmartyEngine');
|
||||
if (is_subclass_of($className, Friendica\Console\AbstractConsole::class)) {
|
||||
$this->container->setup($className::LOG_CHANNEL);
|
||||
} else {
|
||||
$this->container->setup(LogChannel::CONSOLE);
|
||||
}
|
||||
|
||||
/** @var Console $subconsole */
|
||||
$subconsole = $this->dice->create($className, [$subargs]);
|
||||
$subconsole = $this->container->create($className, [$subargs]);
|
||||
|
||||
foreach ($this->options as $name => $value) {
|
||||
$subconsole->setOption($name, $value);
|
||||
|
@ -179,5 +202,4 @@ HELP;
|
|||
|
||||
return $subconsole;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
118
src/Core/Container.php
Normal file
118
src/Core/Container.php
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
// Copyright (C) 2010-2024, the Friendica project
|
||||
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Friendica\Core;
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\Core\Addon\Capability\ICanLoadAddons;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\Core\Logger\Handler\ErrorHandler;
|
||||
use Friendica\DI;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Wrapper for the Dice class to make some basic setups
|
||||
*/
|
||||
class Container
|
||||
{
|
||||
private Dice $container;
|
||||
|
||||
protected function __construct(Dice $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance with Dice
|
||||
*
|
||||
* @param Dice $container
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function fromDice(Dice $container): self
|
||||
{
|
||||
return new self($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the container with the given parameters
|
||||
*
|
||||
* @param string $logChannel The Log Channel of this call
|
||||
* @param bool $withTemplateEngine true, if the template engine should be set too
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setup(string $logChannel = LogChannel::DEFAULT, bool $withTemplateEngine = true)
|
||||
{
|
||||
$this->setupContainerForAddons();
|
||||
$this->setupContainerForLogger($logChannel);
|
||||
$this->setupLegacyServiceLocator();
|
||||
$this->registerErrorHandler();
|
||||
|
||||
if ($withTemplateEngine) {
|
||||
$this->registerTemplateEngine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fully constructed object based on $name using $args and $share as constructor arguments if supplied
|
||||
* @param string $name name The name of the class to instantiate
|
||||
* @param array $args An array with any additional arguments to be passed into the constructor upon instantiation
|
||||
* @param array $share a list of defined in shareInstances for objects higher up the object graph, should only be used internally
|
||||
* @return object A fully constructed object based on the specified input arguments
|
||||
*
|
||||
* @see Dice::create()
|
||||
*/
|
||||
public function create(string $name, array $args = [], array $share = []): object
|
||||
{
|
||||
return $this->container->create($name, $args, $share);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a rule $rule to the class $name
|
||||
* @param string $name The name of the class to add the rule for
|
||||
* @param array $rule The container can be fully configured using rules provided by associative arrays. See {@link https://r.je/dice.html#example3} for a description of the rules.
|
||||
*
|
||||
* @see Dice::addRule()
|
||||
*/
|
||||
public function addRule(string $name, array $rule): void
|
||||
{
|
||||
$this->container = $this->container->addRule($name, $rule);
|
||||
}
|
||||
|
||||
private function setupContainerForAddons(): void
|
||||
{
|
||||
/** @var ICanLoadAddons $addonLoader */
|
||||
$addonLoader = $this->container->create(ICanLoadAddons::class);
|
||||
|
||||
$this->container = $this->container->addRules($addonLoader->getActiveAddonConfig('dependencies'));
|
||||
}
|
||||
|
||||
private function setupContainerForLogger(string $logChannel): void
|
||||
{
|
||||
$this->container = $this->container->addRule(LoggerInterface::class, [
|
||||
'constructParams' => [$logChannel],
|
||||
]);
|
||||
}
|
||||
|
||||
private function setupLegacyServiceLocator(): void
|
||||
{
|
||||
DI::init($this->container);
|
||||
}
|
||||
|
||||
private function registerErrorHandler(): void
|
||||
{
|
||||
ErrorHandler::register($this->container->create(LoggerInterface::class));
|
||||
}
|
||||
|
||||
private function registerTemplateEngine(): void
|
||||
{
|
||||
Renderer::registerTemplateEngine('Friendica\Render\FriendicaSmartyEngine');
|
||||
}
|
||||
}
|
|
@ -164,9 +164,10 @@ class System
|
|||
* Executes a child process with 'proc_open'
|
||||
*
|
||||
* @param string $command The command to execute
|
||||
* @param array $args Arguments to pass to the command ( [ 'key' => value, 'key2' => value2, ... ]
|
||||
* @param array $args Arguments to pass to the command ( ['arg1', 'arg2', ... ] )
|
||||
* @param array $options Options to pass to the command ( [ 'key' => value, 'key2' => value2, ... ]
|
||||
*/
|
||||
public function run(string $command, array $args)
|
||||
public function run(string $command, array $args = [], array $options = [])
|
||||
{
|
||||
if (!function_exists('proc_open')) {
|
||||
$this->logger->warning('"proc_open" not available - quitting');
|
||||
|
@ -175,7 +176,11 @@ class System
|
|||
|
||||
$cmdline = $this->config->get('config', 'php_path', 'php') . ' ' . escapeshellarg($command);
|
||||
|
||||
foreach ($args as $key => $value) {
|
||||
foreach ($args as $argumment) {
|
||||
$cmdline .= ' ' . $argumment;
|
||||
}
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
if (!is_null($value) && is_bool($value) && !$value) {
|
||||
continue;
|
||||
}
|
||||
|
@ -225,7 +230,7 @@ class System
|
|||
$trace = array_slice($trace, 2 + $offset);
|
||||
|
||||
$callstack = [];
|
||||
$previous = ['class' => '', 'function' => '', 'database' => false];
|
||||
$previous = ['class' => '', 'function' => '', 'database' => false];
|
||||
|
||||
// The ignore list contains all functions that are only wrapper functions
|
||||
$ignore = ['call_user_func_array'];
|
||||
|
@ -245,15 +250,15 @@ class System
|
|||
// Don't show multiple calls from the Database classes to show the essential parts of the callstack
|
||||
$func['database'] = in_array($func['class'], ['Friendica\Database\DBA', 'Friendica\Database\Database']);
|
||||
if ($full || !$previous['database'] || !$func['database']) {
|
||||
$classparts = explode("\\", $func['class']);
|
||||
$classparts = explode("\\", $func['class']);
|
||||
$callstack[] = array_pop($classparts).'::'.$func['function'] . (isset($func['line']) ? ' (' . $func['line'] . ')' : '');
|
||||
$previous = $func;
|
||||
$previous = $func;
|
||||
}
|
||||
} elseif (!in_array($func['function'], $ignore)) {
|
||||
$func['database'] = ($func['function'] == 'q');
|
||||
$callstack[] = $func['function'] . (isset($func['line']) ? ' (' . $func['line'] . ')' : '');
|
||||
$func['class'] = '';
|
||||
$previous = $func;
|
||||
$callstack[] = $func['function'] . (isset($func['line']) ? ' (' . $func['line'] . ')' : '');
|
||||
$func['class'] = '';
|
||||
$previous = $func;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,10 +277,13 @@ class System
|
|||
*/
|
||||
public static function echoResponse(ResponseInterface $response)
|
||||
{
|
||||
header(sprintf("HTTP/%s %s %s",
|
||||
header(
|
||||
sprintf(
|
||||
"HTTP/%s %s %s",
|
||||
$response->getProtocolVersion(),
|
||||
$response->getStatusCode(),
|
||||
$response->getReasonPhrase())
|
||||
$response->getReasonPhrase()
|
||||
)
|
||||
);
|
||||
|
||||
foreach ($response->getHeaders() as $key => $header) {
|
||||
|
@ -696,7 +704,7 @@ class System
|
|||
|
||||
if (DI::config()->get('system', 'tosdisplay')) {
|
||||
$rulelist = DI::config()->get('system', 'tosrules') ?: DI::config()->get('system', 'tostext');
|
||||
$msg = BBCode::toPlaintext($rulelist, false);
|
||||
$msg = BBCode::toPlaintext($rulelist, false);
|
||||
foreach (explode("\n", trim($msg)) as $line) {
|
||||
$line = trim($line);
|
||||
if ($line) {
|
||||
|
|
|
@ -45,11 +45,11 @@ class Worker
|
|||
const LAST_CHECK = 'worker::check';
|
||||
|
||||
private static $up_start;
|
||||
private static $db_duration = 0;
|
||||
private static $db_duration = 0;
|
||||
private static $db_duration_count = 0;
|
||||
private static $db_duration_write = 0;
|
||||
private static $db_duration_stat = 0;
|
||||
private static $lock_duration = 0;
|
||||
private static $db_duration_stat = 0;
|
||||
private static $lock_duration = 0;
|
||||
private static $last_update;
|
||||
private static $state;
|
||||
/** @var Process */
|
||||
|
@ -93,7 +93,7 @@ class Worker
|
|||
Worker\Cron::run();
|
||||
}
|
||||
|
||||
$last_check = $starttime = time();
|
||||
$last_check = $starttime = time();
|
||||
self::$state = self::STATE_STARTUP;
|
||||
|
||||
// We fetch the next queue entry that is about to be executed
|
||||
|
@ -116,7 +116,7 @@ class Worker
|
|||
self::findWorkerProcesses();
|
||||
DI::lock()->release(self::LOCK_PROCESS);
|
||||
self::$state = self::STATE_REFETCH;
|
||||
$refetched = true;
|
||||
$refetched = true;
|
||||
} else {
|
||||
self::$state = self::STATE_SHORT_LOOP;
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ class Worker
|
|||
self::$state = self::STATE_LONG_LOOP;
|
||||
|
||||
if (DI::lock()->acquire(self::LOCK_WORKER, 0)) {
|
||||
// Count active workers and compare them with a maximum value that depends on the load
|
||||
// Count active workers and compare them with a maximum value that depends on the load
|
||||
if (self::tooMuchWorkers()) {
|
||||
Logger::info('Active worker limit reached, quitting.');
|
||||
DI::lock()->release(self::LOCK_WORKER);
|
||||
|
@ -209,7 +209,7 @@ class Worker
|
|||
*/
|
||||
public static function entriesExists(): bool
|
||||
{
|
||||
$stamp = (float)microtime(true);
|
||||
$stamp = (float)microtime(true);
|
||||
$exists = DBA::exists('workerqueue', ["NOT `done` AND `pid` = 0 AND `next_try` < ?", DateTimeFormat::utcNow()]);
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
return $exists;
|
||||
|
@ -253,8 +253,8 @@ class Worker
|
|||
*/
|
||||
private static function highestPriority(): int
|
||||
{
|
||||
$stamp = (float)microtime(true);
|
||||
$condition = ["`pid` = 0 AND NOT `done` AND `next_try` < ?", DateTimeFormat::utcNow()];
|
||||
$stamp = (float)microtime(true);
|
||||
$condition = ["`pid` = 0 AND NOT `done` AND `next_try` < ?", DateTimeFormat::utcNow()];
|
||||
$workerqueue = DBA::selectFirst('workerqueue', ['priority'], $condition, ['order' => ['priority']]);
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
if (DBA::isResult($workerqueue)) {
|
||||
|
@ -359,7 +359,7 @@ class Worker
|
|||
self::$last_update = strtotime($queue['executed']);
|
||||
}
|
||||
|
||||
$age = (time() - self::$last_update) / 60;
|
||||
$age = (time() - self::$last_update) / 60;
|
||||
self::$last_update = time();
|
||||
|
||||
if ($age > 1) {
|
||||
|
@ -373,7 +373,7 @@ class Worker
|
|||
|
||||
self::execFunction($queue, $include, $argv, true);
|
||||
|
||||
$stamp = (float)microtime(true);
|
||||
$stamp = (float)microtime(true);
|
||||
$condition = ["`id` = ? AND `next_try` < ?", $queue['id'], DateTimeFormat::utcNow()];
|
||||
if (DBA::update('workerqueue', ['done' => true], $condition)) {
|
||||
DI::keyValue()->set('last_worker_execution', DateTimeFormat::utcNow());
|
||||
|
@ -403,7 +403,7 @@ class Worker
|
|||
self::$last_update = strtotime($queue['executed']);
|
||||
}
|
||||
|
||||
$age = (time() - self::$last_update) / 60;
|
||||
$age = (time() - self::$last_update) / 60;
|
||||
self::$last_update = time();
|
||||
|
||||
if ($age > 1) {
|
||||
|
@ -595,21 +595,21 @@ class Worker
|
|||
|
||||
self::coolDown();
|
||||
|
||||
self::$up_start = microtime(true);
|
||||
self::$db_duration = 0;
|
||||
self::$up_start = microtime(true);
|
||||
self::$db_duration = 0;
|
||||
self::$db_duration_count = 0;
|
||||
self::$db_duration_stat = 0;
|
||||
self::$db_duration_stat = 0;
|
||||
self::$db_duration_write = 0;
|
||||
self::$lock_duration = 0;
|
||||
self::$lock_duration = 0;
|
||||
|
||||
if ($duration > 3600) {
|
||||
Logger::info('Longer than 1 hour.', ['priority' => $queue['priority'], 'id' => $queue['id'], 'duration' => round($duration/60, 3)]);
|
||||
Logger::info('Longer than 1 hour.', ['priority' => $queue['priority'], 'id' => $queue['id'], 'duration' => round($duration / 60, 3)]);
|
||||
} elseif ($duration > 600) {
|
||||
Logger::info('Longer than 10 minutes.', ['priority' => $queue['priority'], 'id' => $queue['id'], 'duration' => round($duration/60, 3)]);
|
||||
Logger::info('Longer than 10 minutes.', ['priority' => $queue['priority'], 'id' => $queue['id'], 'duration' => round($duration / 60, 3)]);
|
||||
} elseif ($duration > 300) {
|
||||
Logger::info('Longer than 5 minutes.', ['priority' => $queue['priority'], 'id' => $queue['id'], 'duration' => round($duration/60, 3)]);
|
||||
Logger::info('Longer than 5 minutes.', ['priority' => $queue['priority'], 'id' => $queue['id'], 'duration' => round($duration / 60, 3)]);
|
||||
} elseif ($duration > 120) {
|
||||
Logger::info('Longer than 2 minutes.', ['priority' => $queue['priority'], 'id' => $queue['id'], 'duration' => round($duration/60, 3)]);
|
||||
Logger::info('Longer than 2 minutes.', ['priority' => $queue['priority'], 'id' => $queue['id'], 'duration' => round($duration / 60, 3)]);
|
||||
}
|
||||
|
||||
Logger::info('Process done.', ['function' => $funcname, 'priority' => $queue['priority'], 'retrial' => $queue['retrial'], 'id' => $queue['id'], 'duration' => round($duration, 3)]);
|
||||
|
@ -639,7 +639,7 @@ class Worker
|
|||
}
|
||||
// Or it can be granted. This overrides the system variable
|
||||
$stamp = (float)microtime(true);
|
||||
$r = DBA::p('SHOW GRANTS');
|
||||
$r = DBA::p('SHOW GRANTS');
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
while ($grants = DBA::fetch($r)) {
|
||||
$grant = array_pop($grants);
|
||||
|
@ -655,7 +655,7 @@ class Worker
|
|||
$stamp = (float)microtime(true);
|
||||
$used = 0;
|
||||
$sleep = 0;
|
||||
$data = DBA::p("SHOW PROCESSLIST");
|
||||
$data = DBA::p("SHOW PROCESSLIST");
|
||||
while ($row = DBA::fetch($data)) {
|
||||
if ($row['Command'] != 'Sleep') {
|
||||
++$used;
|
||||
|
@ -734,13 +734,13 @@ class Worker
|
|||
* With exponent 1, you could have 20 max queues at idle and 13 at 37% of $maxsysload.
|
||||
*/
|
||||
$exponent = intval(DI::config()->get('system', 'worker_load_exponent', 3));
|
||||
$slope = pow(max(0, $maxsysload - $load) / $maxsysload, $exponent);
|
||||
$queues = intval(ceil($slope * $maxqueues));
|
||||
$slope = pow(max(0, $maxsysload - $load) / $maxsysload, $exponent);
|
||||
$queues = intval(ceil($slope * $maxqueues));
|
||||
|
||||
$processlist = '';
|
||||
|
||||
if (DI::config()->get('system', 'worker_jpm')) {
|
||||
$intervals = explode(',', DI::config()->get('system', 'worker_jpm_range'));
|
||||
$intervals = explode(',', DI::config()->get('system', 'worker_jpm_range'));
|
||||
$jobs_per_minute = [];
|
||||
foreach ($intervals as $interval) {
|
||||
if ($interval == 0) {
|
||||
|
@ -750,7 +750,7 @@ class Worker
|
|||
}
|
||||
|
||||
$stamp = (float)microtime(true);
|
||||
$jobs = DBA::count('workerqueue', ["`done` AND `executed` > ?", DateTimeFormat::utc('now - ' . $interval . ' minute')]);
|
||||
$jobs = DBA::count('workerqueue', ["`done` AND `executed` > ?", DateTimeFormat::utc('now - ' . $interval . ' minute')]);
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
self::$db_duration_stat += (microtime(true) - $stamp);
|
||||
$jobs_per_minute[$interval] = number_format($jobs / $interval, 0);
|
||||
|
@ -769,11 +769,11 @@ class Worker
|
|||
$waiting_processes = 0;
|
||||
// Now adding all processes with workerqueue entries
|
||||
$stamp = (float)microtime(true);
|
||||
$jobs = DBA::p("SELECT COUNT(*) AS `entries`, `priority` FROM `workerqueue` WHERE NOT `done` GROUP BY `priority`");
|
||||
$jobs = DBA::p("SELECT COUNT(*) AS `entries`, `priority` FROM `workerqueue` WHERE NOT `done` GROUP BY `priority`");
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
self::$db_duration_stat += (microtime(true) - $stamp);
|
||||
while ($entry = DBA::fetch($jobs)) {
|
||||
$stamp = (float)microtime(true);
|
||||
$stamp = (float)microtime(true);
|
||||
$running = DBA::count('workerqueue-view', ['priority' => $entry['priority']]);
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
self::$db_duration_stat += (microtime(true) - $stamp);
|
||||
|
@ -783,9 +783,9 @@ class Worker
|
|||
}
|
||||
DBA::close($jobs);
|
||||
} else {
|
||||
$waiting_processes = self::totalEntries();
|
||||
$stamp = (float)microtime(true);
|
||||
$jobs = DBA::p("SELECT COUNT(*) AS `running`, `priority` FROM `workerqueue-view` GROUP BY `priority` ORDER BY `priority`");
|
||||
$waiting_processes = self::totalEntries();
|
||||
$stamp = (float)microtime(true);
|
||||
$jobs = DBA::p("SELECT COUNT(*) AS `running`, `priority` FROM `workerqueue-view` GROUP BY `priority` ORDER BY `priority`");
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
self::$db_duration_stat += (microtime(true) - $stamp);
|
||||
|
||||
|
@ -871,7 +871,7 @@ class Worker
|
|||
*/
|
||||
private static function getWorkerPIDList(): array
|
||||
{
|
||||
$ids = [];
|
||||
$ids = [];
|
||||
$stamp = (float)microtime(true);
|
||||
|
||||
$queues = DBA::p("SELECT `process`.`pid`, COUNT(`workerqueue`.`pid`) AS `entries` FROM `process`
|
||||
|
@ -896,7 +896,7 @@ class Worker
|
|||
private static function getWaitingJobForPID()
|
||||
{
|
||||
$stamp = (float)microtime(true);
|
||||
$r = DBA::select('workerqueue', [], ['pid' => getmypid(), 'done' => false]);
|
||||
$r = DBA::select('workerqueue', [], ['pid' => getmypid(), 'done' => false]);
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
if (DBA::isResult($r)) {
|
||||
return DBA::toArray($r);
|
||||
|
@ -920,10 +920,10 @@ class Worker
|
|||
return [];
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
$stamp = (float)microtime(true);
|
||||
$ids = [];
|
||||
$stamp = (float)microtime(true);
|
||||
$condition = ["`priority` = ? AND `pid` = 0 AND NOT `done` AND `next_try` < ?", $priority, DateTimeFormat::utcNow()];
|
||||
$tasks = DBA::select('workerqueue', ['id', 'command', 'parameter'], $condition, ['limit' => $limit, 'order' => ['retrial', 'created']]);
|
||||
$tasks = DBA::select('workerqueue', ['id', 'command', 'parameter'], $condition, ['limit' => $limit, 'order' => ['retrial', 'created']]);
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
while ($task = DBA::fetch($tasks)) {
|
||||
$ids[] = $task['id'];
|
||||
|
@ -952,7 +952,7 @@ class Worker
|
|||
*/
|
||||
private static function nextPriority()
|
||||
{
|
||||
$waiting = [];
|
||||
$waiting = [];
|
||||
$priorities = [self::PRIORITY_CRITICAL, self::PRIORITY_HIGH, self::PRIORITY_MEDIUM, self::PRIORITY_LOW, self::PRIORITY_NEGLIGIBLE];
|
||||
foreach ($priorities as $priority) {
|
||||
$stamp = (float)microtime(true);
|
||||
|
@ -966,10 +966,10 @@ class Worker
|
|||
return self::PRIORITY_CRITICAL;
|
||||
}
|
||||
|
||||
$running = [];
|
||||
$running = [];
|
||||
$running_total = 0;
|
||||
$stamp = (float)microtime(true);
|
||||
$processes = DBA::p("SELECT COUNT(DISTINCT(`pid`)) AS `running`, `priority` FROM `workerqueue-view` GROUP BY `priority`");
|
||||
$stamp = (float)microtime(true);
|
||||
$processes = DBA::p("SELECT COUNT(DISTINCT(`pid`)) AS `running`, `priority` FROM `workerqueue-view` GROUP BY `priority`");
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
while ($process = DBA::fetch($processes)) {
|
||||
$running[$process['priority']] = $process['running'];
|
||||
|
@ -984,9 +984,9 @@ class Worker
|
|||
}
|
||||
}
|
||||
|
||||
$active = max(self::activeWorkers(), $running_total);
|
||||
$active = max(self::activeWorkers(), $running_total);
|
||||
$priorities = max(count($waiting), count($running));
|
||||
$exponent = 2;
|
||||
$exponent = 2;
|
||||
|
||||
$total = 0;
|
||||
for ($i = 1; $i <= $priorities; ++$i) {
|
||||
|
@ -1037,7 +1037,7 @@ class Worker
|
|||
}
|
||||
$limit = $fetch_limit * count($pids);
|
||||
} else {
|
||||
$pids = [getmypid()];
|
||||
$pids = [getmypid()];
|
||||
$limit = $fetch_limit;
|
||||
}
|
||||
|
||||
|
@ -1046,9 +1046,9 @@ class Worker
|
|||
|
||||
// If there is not enough results we check without priority limit
|
||||
if ($limit > 0) {
|
||||
$stamp = (float)microtime(true);
|
||||
$stamp = (float)microtime(true);
|
||||
$condition = ["`pid` = 0 AND NOT `done` AND `next_try` < ?", DateTimeFormat::utcNow()];
|
||||
$tasks = DBA::select('workerqueue', ['id', 'command', 'parameter'], $condition, ['limit' => $limit, 'order' => ['priority', 'retrial', 'created']]);
|
||||
$tasks = DBA::select('workerqueue', ['id', 'command', 'parameter'], $condition, ['limit' => $limit, 'order' => ['priority', 'retrial', 'created']]);
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
|
||||
while ($task = DBA::fetch($tasks)) {
|
||||
|
@ -1083,8 +1083,11 @@ class Worker
|
|||
$stamp = (float)microtime(true);
|
||||
foreach ($worker as $worker_pid => $worker_ids) {
|
||||
Logger::info('Set queue entry', ['pid' => $worker_pid, 'ids' => $worker_ids]);
|
||||
DBA::update('workerqueue', ['executed' => DateTimeFormat::utcNow(), 'pid' => $worker_pid],
|
||||
['id' => $worker_ids, 'done' => false, 'pid' => 0]);
|
||||
DBA::update(
|
||||
'workerqueue',
|
||||
['executed' => DateTimeFormat::utcNow(), 'pid' => $worker_pid],
|
||||
['id' => $worker_ids, 'done' => false, 'pid' => 0]
|
||||
);
|
||||
}
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
self::$db_duration_write += (microtime(true) - $stamp);
|
||||
|
@ -1207,7 +1210,7 @@ class Worker
|
|||
if (Worker\Daemon::isMode() && DI::config()->get('system', 'worker_fork')) {
|
||||
self::forkProcess($do_cron);
|
||||
} else {
|
||||
DI::system()->run('bin/worker.php', ['no_cron' => !$do_cron]);
|
||||
DI::system()->run('bin/console.php', ['worker'], ['no_cron' => !$do_cron]);
|
||||
}
|
||||
if (Worker\Daemon::isMode()) {
|
||||
Worker\IPC::SetJobState(false);
|
||||
|
@ -1246,9 +1249,9 @@ class Worker
|
|||
|
||||
$priority = self::PRIORITY_MEDIUM;
|
||||
// Don't fork from frontend tasks by default
|
||||
$dont_fork = DI::config()->get('system', 'worker_dont_fork', false) || !DI::mode()->isBackend();
|
||||
$created = DateTimeFormat::utcNow();
|
||||
$delayed = DBA::NULL_DATETIME;
|
||||
$dont_fork = DI::config()->get('system', 'worker_dont_fork', false) || !DI::mode()->isBackend();
|
||||
$created = DateTimeFormat::utcNow();
|
||||
$delayed = DBA::NULL_DATETIME;
|
||||
$force_priority = false;
|
||||
|
||||
$run_parameter = array_shift($args);
|
||||
|
@ -1275,10 +1278,10 @@ class Worker
|
|||
throw new \InvalidArgumentException('Priority number or task parameter array expected as first argument');
|
||||
}
|
||||
|
||||
$command = array_shift($args);
|
||||
$command = array_shift($args);
|
||||
$parameters = json_encode($args);
|
||||
$queue = DBA::selectFirst('workerqueue', ['id', 'priority'], ['command' => $command, 'parameter' => $parameters, 'done' => false]);
|
||||
$added = 0;
|
||||
$queue = DBA::selectFirst('workerqueue', ['id', 'priority'], ['command' => $command, 'parameter' => $parameters, 'done' => false]);
|
||||
$added = 0;
|
||||
|
||||
if (!is_int($priority) || !in_array($priority, self::PRIORITIES)) {
|
||||
Logger::warning('Invalid priority', ['priority' => $priority, 'command' => $command]);
|
||||
|
@ -1292,7 +1295,7 @@ class Worker
|
|||
|
||||
if (empty($queue)) {
|
||||
if (!DBA::insert('workerqueue', ['command' => $command, 'parameter' => $parameters, 'created' => $created,
|
||||
'priority' => $priority, 'next_try' => $delayed])) {
|
||||
'priority' => $priority, 'next_try' => $delayed])) {
|
||||
return 0;
|
||||
}
|
||||
$added = DBA::lastInsertId();
|
||||
|
@ -1354,11 +1357,11 @@ class Worker
|
|||
*/
|
||||
private static function getNextRetrial(array $queue, int $max_level): int
|
||||
{
|
||||
$created = strtotime($queue['created']);
|
||||
$created = strtotime($queue['created']);
|
||||
$retrial_time = time() - $created;
|
||||
|
||||
$new_retrial = $queue['retrial'] + 1;
|
||||
$total = 0;
|
||||
$total = 0;
|
||||
for ($retrial = 0; $retrial <= $max_level + 1; ++$retrial) {
|
||||
$delay = (($retrial + 3) ** 4) + (rand(1, 30) * ($retrial + 1));
|
||||
$total += $delay;
|
||||
|
@ -1396,7 +1399,7 @@ class Worker
|
|||
return false;
|
||||
}
|
||||
|
||||
$id = $queue['id'];
|
||||
$id = $queue['id'];
|
||||
$priority = $queue['priority'];
|
||||
|
||||
$max_level = DI::config()->get('system', 'worker_defer_limit');
|
||||
|
@ -1414,7 +1417,7 @@ class Worker
|
|||
|
||||
// Calculate the delay until the next trial
|
||||
$delay = (($new_retrial + 2) ** 4) + (rand(1, 30) * ($new_retrial));
|
||||
$next = DateTimeFormat::utc('now + ' . $delay . ' seconds');
|
||||
$next = DateTimeFormat::utc('now + ' . $delay . ' seconds');
|
||||
|
||||
if (($priority < self::PRIORITY_MEDIUM) && ($new_retrial > 3)) {
|
||||
$priority = self::PRIORITY_MEDIUM;
|
||||
|
@ -1426,7 +1429,7 @@ class Worker
|
|||
|
||||
Logger::info('Deferred task', ['id' => $id, 'retrial' => $new_retrial, 'created' => $queue['created'], 'next_execution' => $next, 'old_prio' => $queue['priority'], 'new_prio' => $priority]);
|
||||
|
||||
$stamp = (float)microtime(true);
|
||||
$stamp = (float)microtime(true);
|
||||
$fields = ['retrial' => $new_retrial, 'next_try' => $next, 'executed' => DBA::NULL_DATETIME, 'pid' => 0, 'priority' => $priority];
|
||||
DBA::update('workerqueue', $fields, ['id' => $id]);
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
|
@ -1445,7 +1448,7 @@ class Worker
|
|||
{
|
||||
// Calculate the seconds of the start and end of the maintenance window
|
||||
$start = strtotime(DI::config()->get('system', 'maintenance_start')) % 86400;
|
||||
$end = strtotime(DI::config()->get('system', 'maintenance_end')) % 86400;
|
||||
$end = strtotime(DI::config()->get('system', 'maintenance_end')) % 86400;
|
||||
|
||||
Logger::info('Maintenance window', ['start' => date('H:i:s', $start), 'end' => date('H:i:s', $end)]);
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ class Daemon
|
|||
private static function spawn()
|
||||
{
|
||||
Logger::notice('Starting new daemon process');
|
||||
DI::system()->run('bin/daemon.php', ['start']);
|
||||
DI::system()->run('bin/console.php', ['start']);
|
||||
Logger::notice('New daemon process started');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -233,6 +233,14 @@ class Item
|
|||
|
||||
$content_fields = ['raw-body' => trim($fields['raw-body'] ?? $fields['body'])];
|
||||
|
||||
if ($item['origin'] && empty($item['quote-uri-id'])) {
|
||||
$quote_id = Post\Media::getActivityUriId($item['uri-id']);
|
||||
if (!empty($quote_id)) {
|
||||
Logger::notice('Found attached post', ['id' => $quote_id, 'guid' => $item['guid'], 'uri-id' => $item['uri-id']]);
|
||||
$content_fields['quote-uri-id'] = $quote_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all media attachments from the body and store them in the post-media table
|
||||
// @todo On shared postings (Diaspora style and commented reshare) don't fetch content from the shared part
|
||||
$content_fields['raw-body'] = Post\Media::insertFromBody($item['uri-id'], $content_fields['raw-body']);
|
||||
|
@ -1221,6 +1229,14 @@ class Item
|
|||
Post\Media::insertFromAttachment($item['uri-id'], $item['attach']);
|
||||
}
|
||||
|
||||
if ($item['origin'] && empty($item['quote-uri-id'])) {
|
||||
$quote_id = Post\Media::getActivityUriId($item['uri-id']);
|
||||
if (!empty($quote_id)) {
|
||||
Logger::notice('Found attached post', ['id' => $quote_id, 'guid' => $item['guid'], 'uri-id' => $item['uri-id']]);
|
||||
$item['quote-uri-id'] = $quote_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($item['event-id'])) {
|
||||
unset($item['event-id']);
|
||||
|
||||
|
@ -4252,9 +4268,10 @@ class Item
|
|||
}
|
||||
|
||||
/**
|
||||
* Fetch the uri-id of a quote
|
||||
* Fetch the uri-id of a quoted post by searching for data in the body or attached media
|
||||
*
|
||||
* @param string $body
|
||||
* @param string $body The body of the
|
||||
* @param int $uid The id of the user
|
||||
* @return integer
|
||||
*/
|
||||
public static function getQuoteUriId(string $body, int $uid = 0): int
|
||||
|
|
|
@ -101,7 +101,7 @@ class Photo
|
|||
public static function getPhotosForUser(int $uid, string $resourceid, array $conditions = [], array $params = [])
|
||||
{
|
||||
$conditions['resource-id'] = $resourceid;
|
||||
$conditions['uid'] = $uid;
|
||||
$conditions['uid'] = $uid;
|
||||
|
||||
return self::selectToArray([], $conditions, $params);
|
||||
}
|
||||
|
@ -123,8 +123,8 @@ class Photo
|
|||
public static function getPhotoForUser(int $uid, $resourceid, $scale = 0, array $conditions = [], array $params = [])
|
||||
{
|
||||
$conditions['resource-id'] = $resourceid;
|
||||
$conditions['uid'] = $uid;
|
||||
$conditions['scale'] = $scale;
|
||||
$conditions['uid'] = $uid;
|
||||
$conditions['scale'] = $scale;
|
||||
|
||||
return self::selectFirst([], $conditions, $params);
|
||||
}
|
||||
|
@ -161,8 +161,8 @@ class Photo
|
|||
}
|
||||
|
||||
$conditions = ["`resource-id` = ? AND `scale` <= ? " . $sql_acl, $resourceid, $scale];
|
||||
$params = ['order' => ['scale' => true]];
|
||||
$photo = self::selectFirst([], $conditions, $params);
|
||||
$params = ['order' => ['scale' => true]];
|
||||
$photo = self::selectFirst([], $conditions, $params);
|
||||
|
||||
return $photo;
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ class Photo
|
|||
|
||||
if (!empty($album)) {
|
||||
$sqlExtra = "AND `album` = ? ";
|
||||
$values[] = $album;
|
||||
$values[] = $album;
|
||||
$sqlExtra2 = "";
|
||||
} else {
|
||||
$sqlExtra = '';
|
||||
|
@ -303,7 +303,7 @@ class Photo
|
|||
private static function getFields(): array
|
||||
{
|
||||
$allfields = DI::dbaDefinition()->getAll();
|
||||
$fields = array_keys($allfields['photo']['fields']);
|
||||
$fields = array_keys($allfields['photo']['fields']);
|
||||
array_splice($fields, array_search('data', $fields), 1);
|
||||
return $fields;
|
||||
}
|
||||
|
@ -322,10 +322,11 @@ class Photo
|
|||
$fields = self::getFields();
|
||||
$values = array_fill(0, count($fields), '');
|
||||
|
||||
$photo = array_combine($fields, $values);
|
||||
$photo['data'] = $image_data;
|
||||
$photo['type'] = $mimetype ?: Images::getMimeTypeByData($image_data);
|
||||
$photo['cacheable'] = false;
|
||||
$photo = array_combine($fields, $values);
|
||||
|
||||
$photo['data'] = $image_data;
|
||||
$photo['type'] = $mimetype ?: Images::getMimeTypeByData($image_data);
|
||||
$photo['cacheable'] = false;
|
||||
|
||||
return $photo;
|
||||
}
|
||||
|
@ -384,7 +385,7 @@ class Photo
|
|||
$photo['backend-class'] = ExternalResource::NAME;
|
||||
$photo['backend-ref'] = json_encode(['url' => $url, 'uid' => $uid]);
|
||||
$photo['type'] = $mimetype;
|
||||
$photo['filename'] = basename(parse_url($url, PHP_URL_PATH));
|
||||
$photo['filename'] = basename(parse_url($url, PHP_URL_PATH));
|
||||
$photo['cacheable'] = true;
|
||||
$photo['blurhash'] = $blurhash;
|
||||
$photo['width'] = $width;
|
||||
|
@ -423,7 +424,7 @@ class Photo
|
|||
}
|
||||
|
||||
$existing_photo = self::selectFirst(['id', 'created', 'backend-class', 'backend-ref'], ['resource-id' => $rid, 'uid' => $uid, 'contact-id' => $cid, 'scale' => $scale]);
|
||||
$created = DateTimeFormat::utcNow();
|
||||
$created = DateTimeFormat::utcNow();
|
||||
if (DBA::isResult($existing_photo)) {
|
||||
$created = $existing_photo['created'];
|
||||
}
|
||||
|
@ -449,33 +450,35 @@ class Photo
|
|||
}
|
||||
|
||||
$fields = [
|
||||
'uid' => $uid,
|
||||
'contact-id' => $cid,
|
||||
'guid' => $guid,
|
||||
'resource-id' => $rid,
|
||||
'hash' => md5($img_str),
|
||||
'created' => $created,
|
||||
'edited' => DateTimeFormat::utcNow(),
|
||||
'filename' => basename($filename),
|
||||
'type' => $image->getType(),
|
||||
'album' => $album,
|
||||
'height' => $image->getHeight(),
|
||||
'width' => $image->getWidth(),
|
||||
'datasize' => strlen($img_str),
|
||||
'blurhash' => $image->getBlurHash($img_str),
|
||||
'data' => $data,
|
||||
'scale' => $scale,
|
||||
'photo-type' => $type,
|
||||
'profile' => false,
|
||||
'allow_cid' => $allow_cid,
|
||||
'allow_gid' => $allow_gid,
|
||||
'deny_cid' => $deny_cid,
|
||||
'deny_gid' => $deny_gid,
|
||||
'desc' => $desc,
|
||||
'uid' => $uid,
|
||||
'contact-id' => $cid,
|
||||
'guid' => $guid,
|
||||
'resource-id' => $rid,
|
||||
'hash' => md5($img_str),
|
||||
'created' => $created,
|
||||
'edited' => DateTimeFormat::utcNow(),
|
||||
'filename' => basename($filename),
|
||||
'type' => $image->getType(),
|
||||
'album' => $album,
|
||||
'height' => $image->getHeight(),
|
||||
'width' => $image->getWidth(),
|
||||
'datasize' => strlen($img_str),
|
||||
'blurhash' => $image->getBlurHash($img_str),
|
||||
'data' => $data,
|
||||
'scale' => $scale,
|
||||
'photo-type' => $type,
|
||||
'profile' => false,
|
||||
'allow_cid' => $allow_cid,
|
||||
'allow_gid' => $allow_gid,
|
||||
'deny_cid' => $deny_cid,
|
||||
'deny_gid' => $deny_gid,
|
||||
'desc' => $desc,
|
||||
'backend-class' => (string)$storage,
|
||||
'backend-ref' => $backend_ref
|
||||
'backend-ref' => $backend_ref
|
||||
];
|
||||
|
||||
$fields = DI::dbaDefinition()->truncateFieldsForTable('photo', $fields);
|
||||
|
||||
if (DBA::isResult($existing_photo)) {
|
||||
$r = DBA::update('photo', $fields, ['id' => $existing_photo['id']]);
|
||||
} else {
|
||||
|
@ -594,10 +597,10 @@ class Photo
|
|||
}
|
||||
Logger::debug('Got picture', ['Content-Type' => $ret->getHeader('Content-Type'), 'url' => $image_url]);
|
||||
$img_str = $ret->getBodyString();
|
||||
$type = $ret->getContentType();
|
||||
$type = $ret->getContentType();
|
||||
} else {
|
||||
$img_str = '';
|
||||
$type = '';
|
||||
$type = '';
|
||||
}
|
||||
|
||||
if ($quit_on_error && ($img_str == '')) {
|
||||
|
@ -608,7 +611,7 @@ class Photo
|
|||
if ($image->isValid()) {
|
||||
$image->scaleToSquare(300);
|
||||
|
||||
$filesize = strlen($image->asString());
|
||||
$filesize = strlen($image->asString());
|
||||
$maximagesize = Strings::getBytesFromShorthand(DI::config()->get('system', 'maximagesize'));
|
||||
|
||||
if ($maximagesize && ($filesize > $maximagesize)) {
|
||||
|
@ -657,8 +660,8 @@ class Photo
|
|||
$suffix = '?ts=' . time();
|
||||
|
||||
$image_url = DI::baseUrl() . '/photo/' . $resource_id . '-4' . $image->getExt() . $suffix;
|
||||
$thumb = DI::baseUrl() . '/photo/' . $resource_id . '-5' . $image->getExt() . $suffix;
|
||||
$micro = DI::baseUrl() . '/photo/' . $resource_id . '-6' . $image->getExt() . $suffix;
|
||||
$thumb = DI::baseUrl() . '/photo/' . $resource_id . '-5' . $image->getExt() . $suffix;
|
||||
$micro = DI::baseUrl() . '/photo/' . $resource_id . '-6' . $image->getExt() . $suffix;
|
||||
} else {
|
||||
$photo_failure = true;
|
||||
}
|
||||
|
@ -668,10 +671,10 @@ class Photo
|
|||
}
|
||||
|
||||
if ($photo_failure) {
|
||||
$contact = Contact::getById($cid) ?: [];
|
||||
$contact = Contact::getById($cid) ?: [];
|
||||
$image_url = Contact::getDefaultAvatar($contact, Proxy::SIZE_SMALL);
|
||||
$thumb = Contact::getDefaultAvatar($contact, Proxy::SIZE_THUMB);
|
||||
$micro = Contact::getDefaultAvatar($contact, Proxy::SIZE_MICRO);
|
||||
$thumb = Contact::getDefaultAvatar($contact, Proxy::SIZE_THUMB);
|
||||
$micro = Contact::getDefaultAvatar($contact, Proxy::SIZE_MICRO);
|
||||
}
|
||||
|
||||
$photo = DBA::selectFirst(
|
||||
|
@ -740,7 +743,7 @@ class Photo
|
|||
$avatar_type = (DI::userSession()->getLocalUserId() && (DI::userSession()->getLocalUserId() == $uid)) ? self::USER_AVATAR : self::DEFAULT;
|
||||
$banner_type = (DI::userSession()->getLocalUserId() && (DI::userSession()->getLocalUserId() == $uid)) ? self::USER_BANNER : self::DEFAULT;
|
||||
|
||||
$key = 'photo_albums:' . $uid . ':' . DI::userSession()->getLocalUserId() . ':' . DI::userSession()->getRemoteUserId();
|
||||
$key = 'photo_albums:' . $uid . ':' . DI::userSession()->getLocalUserId() . ':' . DI::userSession()->getRemoteUserId();
|
||||
$albums = DI::cache()->get($key);
|
||||
|
||||
if (is_null($albums) || $update) {
|
||||
|
@ -860,7 +863,7 @@ class Photo
|
|||
$srch = '<' . intval($original_contact_id) . '>';
|
||||
|
||||
$condition = [
|
||||
'allow_cid' => $srch, 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '',
|
||||
'allow_cid' => $srch, 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '',
|
||||
'resource-id' => $image_rid, 'uid' => $uid
|
||||
];
|
||||
if (!self::exists($condition)) {
|
||||
|
@ -901,8 +904,8 @@ class Photo
|
|||
public static function setPermissionForResource(string $image_rid, int $uid, string $str_contact_allow, string $str_circle_allow, string $str_contact_deny, string $str_circle_deny)
|
||||
{
|
||||
$fields = [
|
||||
'allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow,
|
||||
'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny,
|
||||
'allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow,
|
||||
'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny,
|
||||
'accessible' => DI::pConfig()->get($uid, 'system', 'accessible-photos', false)
|
||||
];
|
||||
|
||||
|
@ -1010,8 +1013,8 @@ class Photo
|
|||
Logger::info('Resize', ['size' => $filesize, 'width' => $width, 'height' => $height, 'max' => $maximagesize, 'pixels' => $pixels]);
|
||||
$image->scaleDown($pixels);
|
||||
$filesize = strlen($image->asString());
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1054,10 +1057,10 @@ class Photo
|
|||
}
|
||||
Logger::debug('Got picture', ['Content-Type' => $ret->getHeader('Content-Type'), 'url' => $image_url]);
|
||||
$img_str = $ret->getBodyString();
|
||||
$type = $ret->getContentType();
|
||||
$type = $ret->getContentType();
|
||||
} else {
|
||||
$img_str = '';
|
||||
$type = '';
|
||||
$type = '';
|
||||
}
|
||||
|
||||
if (empty($img_str)) {
|
||||
|
@ -1138,7 +1141,7 @@ class Photo
|
|||
Logger::info('File upload', ['src' => $src, 'filename' => $filename, 'size' => $filesize, 'type' => $filetype]);
|
||||
|
||||
$imagedata = @file_get_contents($src);
|
||||
$image = new Image($imagedata, $filetype, $filename);
|
||||
$image = new Image($imagedata, $filetype, $filename);
|
||||
if (!$image->isValid()) {
|
||||
Logger::notice('Image is unvalid', ['files' => $files]);
|
||||
return [];
|
||||
|
@ -1203,7 +1206,7 @@ class Photo
|
|||
}
|
||||
|
||||
$condition = ['resource-id' => $resource_id];
|
||||
$photo = self::selectFirst(['id', 'datasize', 'width', 'height', 'type'], $condition, ['order' => ['width' => true]]);
|
||||
$photo = self::selectFirst(['id', 'datasize', 'width', 'height', 'type'], $condition, ['order' => ['width' => true]]);
|
||||
if (empty($photo)) {
|
||||
Logger::notice('Photo not found', ['condition' => $condition]);
|
||||
return [];
|
||||
|
|
|
@ -90,6 +90,7 @@ class Media
|
|||
}
|
||||
|
||||
$media['url'] = Network::sanitizeUrl($media['url']);
|
||||
|
||||
$media = self::unsetEmptyFields($media);
|
||||
$media = DI::dbaDefinition()->truncateFieldsForTable('post-media', $media);
|
||||
|
||||
|
@ -157,8 +158,11 @@ class Media
|
|||
public static function getAttachElement(string $href, int $length, string $type, string $title = ''): string
|
||||
{
|
||||
$media = self::fetchAdditionalData([
|
||||
'type' => self::DOCUMENT, 'url' => $href,
|
||||
'size' => $length, 'mimetype' => $type, 'description' => $title
|
||||
'type' => self::DOCUMENT,
|
||||
'url' => $href,
|
||||
'size' => $length,
|
||||
'mimetype' => $type,
|
||||
'description' => $title
|
||||
]);
|
||||
|
||||
return '[attach]href="' . $media['url'] . '" length="' . $media['size'] .
|
||||
|
@ -184,7 +188,7 @@ class Media
|
|||
}
|
||||
|
||||
// Fetch the mimetype or size if missing.
|
||||
if (Network::isValidHttpUrl($media['url']) && empty($media['mimetype']) && !in_array($media['type'], [self::IMAGE, self::HLS])) {
|
||||
if (Network::isValidHttpUrl($media['url']) && (empty($media['mimetype']) || $media['type'] == self::HTML) && !in_array($media['type'], [self::IMAGE, self::HLS])) {
|
||||
$timeout = DI::config()->get('system', 'xrd_timeout');
|
||||
try {
|
||||
$curlResult = DI::httpClient()->head($media['url'], [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::AS_DEFAULT, HttpClientOptions::TIMEOUT => $timeout, HttpClientOptions::REQUEST => HttpClientRequest::CONTENTTYPE]);
|
||||
|
@ -194,8 +198,8 @@ class Media
|
|||
$curlResult = DI::httpClient()->get($media['url'], HttpClientAccept::AS_DEFAULT, [HttpClientOptions::TIMEOUT => $timeout]);
|
||||
}
|
||||
if ($curlResult->isSuccess()) {
|
||||
if (empty($media['mimetype'])) {
|
||||
$media['mimetype'] = $curlResult->getContentType() ?? '';
|
||||
if (!empty($curlResult->getContentType())) {
|
||||
$media['mimetype'] = $curlResult->getContentType();
|
||||
}
|
||||
if (empty($media['size'])) {
|
||||
$media['size'] = (int)($curlResult->getHeader('Content-Length')[0] ?? strlen($curlResult->getBodyString() ?? ''));
|
||||
|
@ -218,9 +222,9 @@ class Media
|
|||
$imagedata = Images::getInfoFromURLCached($media['url'], empty($media['description']));
|
||||
if ($imagedata) {
|
||||
$media['mimetype'] = $imagedata['mime'];
|
||||
$media['size'] = $imagedata['size'];
|
||||
$media['width'] = $imagedata[0];
|
||||
$media['height'] = $imagedata[1];
|
||||
$media['size'] = $imagedata['size'];
|
||||
$media['width'] = $imagedata[0];
|
||||
$media['height'] = $imagedata[1];
|
||||
$media['blurhash'] = $imagedata['blurhash'] ?? null;
|
||||
if (!empty($imagedata['description']) && empty($media['description'])) {
|
||||
$media['description'] = $imagedata['description'];
|
||||
|
@ -276,13 +280,13 @@ class Media
|
|||
|
||||
// When the original picture is potentially animated but the preview isn't, we override the preview
|
||||
if (in_array($media['mimetype'] ?? '', ['image/gif', 'image/png']) && !in_array($imagedata['mime'], ['image/gif', 'image/png'])) {
|
||||
$media['preview'] = $media['url'];
|
||||
$media['preview-width'] = $media['width'];
|
||||
$media['preview'] = $media['url'];
|
||||
$media['preview-width'] = $media['width'];
|
||||
$media['preview-height'] = $media['height'];
|
||||
return $media;
|
||||
}
|
||||
|
||||
$media['preview-width'] = $imagedata[0];
|
||||
$media['preview-width'] = $imagedata[0];
|
||||
$media['preview-height'] = $imagedata[1];
|
||||
}
|
||||
|
||||
|
@ -336,21 +340,21 @@ class Media
|
|||
$gserver = DBA::selectFirst('gserver', ['url', 'site_name'], ['id' => $contact['gsid']]);
|
||||
}
|
||||
|
||||
$media['type'] = self::ACTIVITY;
|
||||
$media['media-uri-id'] = $item['uri-id'];
|
||||
$media['height'] = null;
|
||||
$media['width'] = null;
|
||||
$media['preview'] = null;
|
||||
$media['preview-height'] = null;
|
||||
$media['preview-width'] = null;
|
||||
$media['blurhash'] = null;
|
||||
$media['description'] = $item['body'];
|
||||
$media['name'] = $item['title'];
|
||||
$media['author-url'] = $item['author-link'];
|
||||
$media['author-name'] = $item['author-name'];
|
||||
$media['author-image'] = $contact['avatar'] ?? $item['author-avatar'];
|
||||
$media['publisher-url'] = $gserver['url'] ?? null;
|
||||
$media['publisher-name'] = $gserver['site_name'] ?? null;
|
||||
$media['type'] = self::ACTIVITY;
|
||||
$media['media-uri-id'] = $item['uri-id'];
|
||||
$media['height'] = null;
|
||||
$media['width'] = null;
|
||||
$media['preview'] = null;
|
||||
$media['preview-height'] = null;
|
||||
$media['preview-width'] = null;
|
||||
$media['blurhash'] = null;
|
||||
$media['description'] = $item['body'];
|
||||
$media['name'] = $item['title'];
|
||||
$media['author-url'] = $item['author-link'];
|
||||
$media['author-name'] = $item['author-name'];
|
||||
$media['author-image'] = $contact['avatar'] ?? $item['author-avatar'];
|
||||
$media['publisher-url'] = $gserver['url'] ?? null;
|
||||
$media['publisher-name'] = $gserver['site_name'] ?? null;
|
||||
$media['publisher-image'] = null;
|
||||
|
||||
Logger::debug('Activity detected', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'plink' => $item['plink'], 'uri' => $item['uri']]);
|
||||
|
@ -378,21 +382,21 @@ class Media
|
|||
$gserver = DBA::selectFirst('gserver', ['url', 'site_name'], ['id' => $contact['gsid']]);
|
||||
}
|
||||
|
||||
$media['type'] = self::ACCOUNT;
|
||||
$media['media-uri-id'] = $contact['uri-id'];
|
||||
$media['height'] = null;
|
||||
$media['width'] = null;
|
||||
$media['preview'] = null;
|
||||
$media['preview-height'] = null;
|
||||
$media['preview-width'] = null;
|
||||
$media['blurhash'] = null;
|
||||
$media['description'] = $contact['about'];
|
||||
$media['name'] = $contact['name'];
|
||||
$media['author-url'] = $contact['url'];
|
||||
$media['author-name'] = $contact['name'];
|
||||
$media['author-image'] = $contact['avatar'];
|
||||
$media['publisher-url'] = $gserver['url'] ?? null;
|
||||
$media['publisher-name'] = $gserver['site_name'] ?? null;
|
||||
$media['type'] = self::ACCOUNT;
|
||||
$media['media-uri-id'] = $contact['uri-id'];
|
||||
$media['height'] = null;
|
||||
$media['width'] = null;
|
||||
$media['preview'] = null;
|
||||
$media['preview-height'] = null;
|
||||
$media['preview-width'] = null;
|
||||
$media['blurhash'] = null;
|
||||
$media['description'] = $contact['about'];
|
||||
$media['name'] = $contact['name'];
|
||||
$media['author-url'] = $contact['url'];
|
||||
$media['author-name'] = $contact['name'];
|
||||
$media['author-image'] = $contact['avatar'];
|
||||
$media['publisher-url'] = $gserver['url'] ?? null;
|
||||
$media['publisher-name'] = $gserver['site_name'] ?? null;
|
||||
$media['publisher-image'] = null;
|
||||
|
||||
Logger::debug('Account detected', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'uri' => $contact['url']]);
|
||||
|
@ -414,22 +418,22 @@ class Media
|
|||
Logger::debug('Detected site data is empty, use suggested media data instead', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'type' => $data['type']]);
|
||||
}
|
||||
} else {
|
||||
$media['preview'] = $data['images'][0]['src'] ?? null;
|
||||
$media['preview-height'] = $data['images'][0]['height'] ?? null;
|
||||
$media['preview-width'] = $data['images'][0]['width'] ?? null;
|
||||
$media['blurhash'] = $data['images'][0]['blurhash'] ?? null;
|
||||
$media['description'] = $data['text'] ?? null;
|
||||
$media['name'] = $data['title'] ?? null;
|
||||
$media['preview'] = $data['images'][0]['src'] ?? null;
|
||||
$media['preview-height'] = $data['images'][0]['height'] ?? null;
|
||||
$media['preview-width'] = $data['images'][0]['width'] ?? null;
|
||||
$media['blurhash'] = $data['images'][0]['blurhash'] ?? null;
|
||||
$media['description'] = $data['text'] ?? null;
|
||||
$media['name'] = $data['title'] ?? null;
|
||||
}
|
||||
|
||||
$media['type'] = self::HTML;
|
||||
$media['size'] = $data['size'] ?? null;
|
||||
$media['author-url'] = $data['author_url'] ?? null;
|
||||
$media['author-name'] = $data['author_name'] ?? null;
|
||||
$media['author-image'] = $data['author_img'] ?? null;
|
||||
$media['publisher-url'] = $data['publisher_url'] ?? null;
|
||||
$media['publisher-name'] = $data['publisher_name'] ?? null;
|
||||
$media['publisher-image'] = $data['publisher_img'] ?? null;
|
||||
|
||||
$media['type'] = self::HTML;
|
||||
$media['size'] = $data['size'] ?? null;
|
||||
$media['author-url'] = $data['author_url'] ?? null;
|
||||
$media['author-name'] = $data['author_name'] ?? null;
|
||||
$media['author-image'] = $data['author_img'] ?? null;
|
||||
$media['publisher-url'] = $data['publisher_url'] ?? null;
|
||||
$media['publisher-name'] = $data['publisher_name'] ?? null;
|
||||
$media['publisher-image'] = $data['publisher_img'] ?? null;
|
||||
|
||||
return $media;
|
||||
}
|
||||
|
@ -458,9 +462,9 @@ class Media
|
|||
$photo = Photo::selectFirst(['type', 'datasize', 'width', 'height', 'blurhash'], ['resource-id' => $matches[1], 'scale' => $matches[2]]);
|
||||
if (!empty($photo)) {
|
||||
$media['mimetype'] = $photo['type'];
|
||||
$media['size'] = $photo['datasize'];
|
||||
$media['width'] = $photo['width'];
|
||||
$media['height'] = $photo['height'];
|
||||
$media['size'] = $photo['datasize'];
|
||||
$media['width'] = $photo['width'];
|
||||
$media['height'] = $photo['height'];
|
||||
$media['blurhash'] = $photo['blurhash'];
|
||||
}
|
||||
|
||||
|
@ -469,7 +473,7 @@ class Media
|
|||
}
|
||||
$photo = Photo::selectFirst(['width', 'height'], ['resource-id' => $matches[1], 'scale' => $matches[2]]);
|
||||
if (!empty($photo)) {
|
||||
$media['preview-width'] = $photo['width'];
|
||||
$media['preview-width'] = $photo['width'];
|
||||
$media['preview-height'] = $photo['height'];
|
||||
}
|
||||
|
||||
|
@ -502,7 +506,7 @@ class Media
|
|||
}
|
||||
|
||||
$filetype = strtolower($type[0]);
|
||||
$subtype = strtolower($type[1]);
|
||||
$subtype = strtolower($type[1]);
|
||||
|
||||
if ($filetype == 'image') {
|
||||
$type = self::IMAGE;
|
||||
|
@ -609,23 +613,35 @@ class Media
|
|||
if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]\s*\[/url\]$endmatchpattern#ism", $body, $pictures, PREG_SET_ORDER)) {
|
||||
foreach ($pictures as $picture) {
|
||||
if (self::isLinkToImagePage($picture[1], $picture[2])) {
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
$image = str_replace(['-1.', '-2.'], '-0.', $picture[2]);
|
||||
|
||||
$attachments[$image] = [
|
||||
'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $image,
|
||||
'preview' => $picture[2], 'description' => $picture[3]
|
||||
'uri-id' => $uriid,
|
||||
'type' => self::IMAGE,
|
||||
'url' => $image,
|
||||
'preview' => $picture[2],
|
||||
'description' => $picture[3]
|
||||
];
|
||||
} elseif (self::isLinkToPhoto($picture[1], $picture[2])) {
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
|
||||
$attachments[$picture[1]] = [
|
||||
'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $picture[1],
|
||||
'preview' => $picture[2], 'description' => $picture[3]
|
||||
'uri-id' => $uriid,
|
||||
'type' => self::IMAGE,
|
||||
'url' => $picture[1],
|
||||
'preview' => $picture[2],
|
||||
'description' => $picture[3]
|
||||
];
|
||||
} elseif ($removepicturelinks) {
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
|
||||
$attachments[$picture[1]] = [
|
||||
'uri-id' => $uriid, 'type' => self::UNKNOWN, 'url' => $picture[1],
|
||||
'preview' => $picture[2], 'description' => $picture[3]
|
||||
'uri-id' => $uriid,
|
||||
'type' => self::UNKNOWN,
|
||||
'url' => $picture[1],
|
||||
'preview' => $picture[2],
|
||||
'description' => $picture[3]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -634,6 +650,7 @@ class Media
|
|||
if (preg_match_all("/\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]$endmatchpattern/Usi", $body, $pictures, PREG_SET_ORDER)) {
|
||||
foreach ($pictures as $picture) {
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
|
||||
$attachments[$picture[1]] = ['uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $picture[1], 'description' => $picture[2]];
|
||||
}
|
||||
}
|
||||
|
@ -641,23 +658,35 @@ class Media
|
|||
if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img\]([^\[]+?)\[/img\]\s*\[/url\]$endmatchpattern#ism", $body, $pictures, PREG_SET_ORDER)) {
|
||||
foreach ($pictures as $picture) {
|
||||
if (self::isLinkToImagePage($picture[1], $picture[2])) {
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
$image = str_replace(['-1.', '-2.'], '-0.', $picture[2]);
|
||||
|
||||
$attachments[$image] = [
|
||||
'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $image,
|
||||
'preview' => $picture[2], 'description' => null
|
||||
'uri-id' => $uriid,
|
||||
'type' => self::IMAGE,
|
||||
'url' => $image,
|
||||
'preview' => $picture[2],
|
||||
'description' => null
|
||||
];
|
||||
} elseif (self::isLinkToPhoto($picture[1], $picture[2])) {
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
|
||||
$attachments[$picture[1]] = [
|
||||
'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $picture[1],
|
||||
'preview' => $picture[2], 'description' => null
|
||||
'uri-id' => $uriid,
|
||||
'type' => self::IMAGE,
|
||||
'url' => $picture[1],
|
||||
'preview' => $picture[2],
|
||||
'description' => null
|
||||
];
|
||||
} elseif ($removepicturelinks) {
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
|
||||
$attachments[$picture[1]] = [
|
||||
'uri-id' => $uriid, 'type' => self::UNKNOWN, 'url' => $picture[1],
|
||||
'preview' => $picture[2], 'description' => null
|
||||
'uri-id' => $uriid,
|
||||
'type' => self::UNKNOWN,
|
||||
'url' => $picture[1],
|
||||
'preview' => $picture[2],
|
||||
'description' => null
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -666,6 +695,7 @@ class Media
|
|||
if (preg_match_all("/\[img\]([^\[\]]*)\[\/img\]$endmatchpattern/ism", $body, $pictures, PREG_SET_ORDER)) {
|
||||
foreach ($pictures as $picture) {
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
|
||||
$attachments[$picture[1]] = ['uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $picture[1]];
|
||||
}
|
||||
}
|
||||
|
@ -673,6 +703,7 @@ class Media
|
|||
if (preg_match_all("/\[audio\]([^\[\]]*)\[\/audio\]$endmatchpattern/ism", $body, $audios, PREG_SET_ORDER)) {
|
||||
foreach ($audios as $audio) {
|
||||
$body = str_replace($audio[0], '', $body);
|
||||
|
||||
$attachments[$audio[1]] = ['uri-id' => $uriid, 'type' => self::AUDIO, 'url' => $audio[1]];
|
||||
}
|
||||
}
|
||||
|
@ -680,6 +711,7 @@ class Media
|
|||
if (preg_match_all("/\[video\]([^\[\]]*)\[\/video\]$endmatchpattern/ism", $body, $videos, PREG_SET_ORDER)) {
|
||||
foreach ($videos as $video) {
|
||||
$body = str_replace($video[0], '', $body);
|
||||
|
||||
$attachments[$video[1]] = ['uri-id' => $uriid, 'type' => self::VIDEO, 'url' => $video[1]];
|
||||
}
|
||||
}
|
||||
|
@ -710,7 +742,7 @@ class Media
|
|||
{
|
||||
do {
|
||||
$prebody = $body;
|
||||
$body = self::insertFromBody(0, $body, true);
|
||||
$body = self::insertFromBody(0, $body, true);
|
||||
} while ($prebody != $body);
|
||||
return $body;
|
||||
}
|
||||
|
@ -725,7 +757,7 @@ class Media
|
|||
{
|
||||
do {
|
||||
$prebody = $body;
|
||||
$body = self::insertFromBody(0, $body, false, true);
|
||||
$body = self::insertFromBody(0, $body, false, true);
|
||||
} while ($prebody != $body);
|
||||
return $body;
|
||||
}
|
||||
|
@ -808,15 +840,15 @@ class Media
|
|||
|
||||
Logger::info('Adding attachment data', ['data' => $data]);
|
||||
$attachment = [
|
||||
'uri-id' => $uriid,
|
||||
'type' => self::HTML,
|
||||
'url' => $data['url'],
|
||||
'preview' => $data['preview'] ?? null,
|
||||
'description' => $data['description'] ?? null,
|
||||
'name' => $data['title'] ?? null,
|
||||
'author-url' => $data['author_url'] ?? null,
|
||||
'author-name' => $data['author_name'] ?? null,
|
||||
'publisher-url' => $data['provider_url'] ?? null,
|
||||
'uri-id' => $uriid,
|
||||
'type' => self::HTML,
|
||||
'url' => $data['url'],
|
||||
'preview' => $data['preview'] ?? null,
|
||||
'description' => $data['description'] ?? null,
|
||||
'name' => $data['title'] ?? null,
|
||||
'author-url' => $data['author_url'] ?? null,
|
||||
'author-name' => $data['author_name'] ?? null,
|
||||
'publisher-url' => $data['provider_url'] ?? null,
|
||||
'publisher-name' => $data['provider_name'] ?? null,
|
||||
];
|
||||
if (!empty($data['image'])) {
|
||||
|
@ -839,11 +871,11 @@ class Media
|
|||
}
|
||||
|
||||
foreach ($matches as $attachment) {
|
||||
$media['type'] = self::DOCUMENT;
|
||||
$media['uri-id'] = $uriid;
|
||||
$media['url'] = $attachment[1];
|
||||
$media['size'] = $attachment[2];
|
||||
$media['mimetype'] = $attachment[3];
|
||||
$media['type'] = self::DOCUMENT;
|
||||
$media['uri-id'] = $uriid;
|
||||
$media['url'] = $attachment[1];
|
||||
$media['size'] = $attachment[2];
|
||||
$media['mimetype'] = $attachment[3];
|
||||
$media['description'] = $attachment[4] ?? '';
|
||||
|
||||
self::insert($media);
|
||||
|
@ -1036,20 +1068,20 @@ class Media
|
|||
}
|
||||
|
||||
$data = [
|
||||
'type' => 'link',
|
||||
'url' => $links[0]['url'],
|
||||
'title' => $links[0]['name'],
|
||||
'text' => $links[0]['description'],
|
||||
'type' => 'link',
|
||||
'url' => $links[0]['url'],
|
||||
'title' => $links[0]['name'],
|
||||
'text' => $links[0]['description'],
|
||||
'publisher_name' => $links[0]['publisher-name'],
|
||||
'publisher_url' => $links[0]['publisher-url'],
|
||||
'publisher_img' => $links[0]['publisher-image'],
|
||||
'author_name' => $links[0]['author-name'],
|
||||
'author_url' => $links[0]['author-url'],
|
||||
'author_img' => $links[0]['author-image'],
|
||||
'images' => [[
|
||||
'src' => $links[0]['preview'],
|
||||
'publisher_url' => $links[0]['publisher-url'],
|
||||
'publisher_img' => $links[0]['publisher-image'],
|
||||
'author_name' => $links[0]['author-name'],
|
||||
'author_url' => $links[0]['author-url'],
|
||||
'author_img' => $links[0]['author-image'],
|
||||
'images' => [[
|
||||
'src' => $links[0]['preview'],
|
||||
'height' => $links[0]['preview-height'],
|
||||
'width' => $links[0]['preview-width'],
|
||||
'width' => $links[0]['preview-width'],
|
||||
]]
|
||||
];
|
||||
$body .= "\n" . PageInfo::getFooterFromData($data);
|
||||
|
@ -1130,4 +1162,19 @@ class Media
|
|||
(Proxy::getPixelsFromSize($size) ? Proxy::getPixelsFromSize($size) . '/' : '') .
|
||||
$id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the uri-id of an attached uri-post for a given uri-id
|
||||
*
|
||||
* @param integer $uri_id Uri-Id of the post
|
||||
* @return integer uri-id of the first attached post
|
||||
*/
|
||||
public static function getActivityUriId(int $uri_id): int
|
||||
{
|
||||
$posts = self::getByURIId($uri_id, [self::ACTIVITY]);
|
||||
if (!$posts) {
|
||||
return 0;
|
||||
}
|
||||
return reset($posts)['media-uri-id'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -677,11 +677,12 @@ class User
|
|||
* @param mixed $user_info
|
||||
* @param string $password
|
||||
* @param bool $third_party
|
||||
* @param bool $with_blocked
|
||||
* @return int User Id if authentication is successful
|
||||
* @throws HTTPException\ForbiddenException
|
||||
* @throws HTTPException\NotFoundException
|
||||
*/
|
||||
public static function getIdFromPasswordAuthentication($user_info, string $password, bool $third_party = false): int
|
||||
public static function getIdFromPasswordAuthentication($user_info, string $password, bool $third_party = false, bool $with_blocked = false): int
|
||||
{
|
||||
// Addons registered with the "authenticate" hook may create the user on the
|
||||
// fly. `getAuthenticationInfo` will fail if the user doesn't exist yet. If
|
||||
|
@ -689,7 +690,7 @@ class User
|
|||
// user in our database, if applicable, before re-throwing the exception if
|
||||
// they fail.
|
||||
try {
|
||||
$user = self::getAuthenticationInfo($user_info);
|
||||
$user = self::getAuthenticationInfo($user_info, $with_blocked);
|
||||
} catch (Exception $e) {
|
||||
$username = (is_string($user_info) ? $user_info : $user_info['nickname'] ?? '');
|
||||
|
||||
|
@ -782,10 +783,11 @@ class User
|
|||
* - User array with at least the uid and the hashed password
|
||||
*
|
||||
* @param mixed $user_info
|
||||
* @param bool $with_blocked
|
||||
* @return array|null Null if not found/determined
|
||||
* @throws HTTPException\NotFoundException
|
||||
*/
|
||||
public static function getAuthenticationInfo($user_info)
|
||||
public static function getAuthenticationInfo($user_info, bool $with_blocked = false)
|
||||
{
|
||||
$user = null;
|
||||
|
||||
|
@ -804,25 +806,27 @@ class User
|
|||
throw new Exception(DI::l10n()->t('Not enough information to authenticate'));
|
||||
}
|
||||
} elseif (is_int($user_info) || is_string($user_info)) {
|
||||
$fields = ['uid', 'nickname', 'password', 'legacy_password'];
|
||||
if (is_int($user_info)) {
|
||||
$user = DBA::selectFirst(
|
||||
'user',
|
||||
['uid', 'nickname', 'password', 'legacy_password'],
|
||||
[
|
||||
'uid' => $user_info,
|
||||
'blocked' => 0,
|
||||
'account_expired' => 0,
|
||||
'account_removed' => 0,
|
||||
'verified' => 1
|
||||
]
|
||||
);
|
||||
$condition = [
|
||||
'uid' => $user_info,
|
||||
'account_expired' => false,
|
||||
'account_removed' => false,
|
||||
'verified' => true
|
||||
];
|
||||
if (!$with_blocked) {
|
||||
$condition = DBA::mergeConditions($condition, ['blocked' => false]);
|
||||
}
|
||||
$user = DBA::selectFirst('user', $fields, $condition);
|
||||
} else {
|
||||
$fields = ['uid', 'nickname', 'password', 'legacy_password'];
|
||||
$condition = [
|
||||
"(`email` = ? OR `username` = ? OR `nickname` = ?)
|
||||
AND `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`",
|
||||
AND `verified` AND NOT `account_removed` AND NOT `account_expired`",
|
||||
$user_info, $user_info, $user_info
|
||||
];
|
||||
if (!$with_blocked) {
|
||||
$condition = DBA::mergeConditions($condition, ['blocked' => false]);
|
||||
}
|
||||
$user = DBA::selectFirst('user', $fields, $condition);
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,6 @@ class UpdateCredentials extends BaseApi
|
|||
}
|
||||
|
||||
$account = DI::mstdnAccount()->createFromContactId($ucid, $uid);
|
||||
$this->response->addJsonContent($account->toArray());
|
||||
$this->jsonExit($account->toArray());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ class Lists extends BaseApi
|
|||
/**
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
protected function rawContent(array $request = [])
|
||||
protected function get(array $request = [])
|
||||
{
|
||||
$this->checkAllowedScope(self::SCOPE_READ);
|
||||
$uid = self::getCurrentUserID();
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Friendica\Module\Api\Mastodon\Lists;
|
||||
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Circle;
|
||||
|
@ -40,7 +39,7 @@ class Accounts extends BaseApi
|
|||
$this->checkAllowedScope(self::SCOPE_WRITE);
|
||||
|
||||
$request = $this->getRequest([
|
||||
'account_ids' => [], // Array of account IDs to add to the list
|
||||
'account_ids' => [], // Array of account IDs to add to the list
|
||||
], $request);
|
||||
|
||||
if (empty($request['account_ids']) || empty($this->parameters['id'])) {
|
||||
|
@ -53,7 +52,7 @@ class Accounts extends BaseApi
|
|||
/**
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
protected function rawContent(array $request = [])
|
||||
protected function get(array $request = [])
|
||||
{
|
||||
$this->checkAllowedScope(self::SCOPE_READ);
|
||||
$uid = self::getCurrentUserID();
|
||||
|
|
|
@ -36,7 +36,7 @@ class Markers extends BaseApi
|
|||
}
|
||||
|
||||
$condition = ['application-id' => $application['id'], 'uid' => $uid, 'timeline' => $timeline];
|
||||
$marker = DBA::selectFirst('application-marker', [], $condition);
|
||||
$marker = DBA::selectFirst('application-marker', [], $condition);
|
||||
if (!empty($marker['version'])) {
|
||||
$version = $marker['version'] + 1;
|
||||
} else {
|
||||
|
@ -51,7 +51,7 @@ class Markers extends BaseApi
|
|||
/**
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
protected function rawContent(array $request = [])
|
||||
protected function get(array $request = [])
|
||||
{
|
||||
$this->checkAllowedScope(self::SCOPE_READ);
|
||||
$uid = self::getCurrentUserID();
|
||||
|
@ -62,7 +62,7 @@ class Markers extends BaseApi
|
|||
|
||||
private function fetchTimelines(int $application_id, int $uid): \stdClass
|
||||
{
|
||||
$values = new \stdClass();
|
||||
$values = new \stdClass();
|
||||
$markers = DBA::select('application-marker', [], ['application-id' => $application_id, 'uid' => $uid]);
|
||||
while ($marker = DBA::fetch($markers)) {
|
||||
$values->{$marker['timeline']} = [
|
||||
|
|
|
@ -116,7 +116,7 @@ class Media extends BaseApi
|
|||
/**
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
protected function rawContent(array $request = [])
|
||||
protected function get(array $request = [])
|
||||
{
|
||||
$this->checkAllowedScope(self::SCOPE_READ);
|
||||
$uid = self::getCurrentUserID();
|
||||
|
|
|
@ -49,9 +49,9 @@ class PushSubscription extends BaseApi
|
|||
$subscription = [
|
||||
'application-id' => $application['id'],
|
||||
'uid' => $uid,
|
||||
'endpoint' => $request['subscription']['endpoint'] ?? '',
|
||||
'endpoint' => $request['subscription']['endpoint'] ?? '',
|
||||
'pubkey' => $request['subscription']['keys']['p256dh'] ?? '',
|
||||
'secret' => $request['subscription']['keys']['auth'] ?? '',
|
||||
'secret' => $request['subscription']['keys']['auth'] ?? '',
|
||||
Notification::TYPE_FOLLOW => filter_var($request['data']['alerts'][Notification::TYPE_FOLLOW] ?? false, FILTER_VALIDATE_BOOLEAN),
|
||||
Notification::TYPE_LIKE => filter_var($request['data']['alerts'][Notification::TYPE_LIKE] ?? false, FILTER_VALIDATE_BOOLEAN),
|
||||
Notification::TYPE_RESHARE => filter_var($request['data']['alerts'][Notification::TYPE_RESHARE] ?? false, FILTER_VALIDATE_BOOLEAN),
|
||||
|
@ -66,7 +66,7 @@ class PushSubscription extends BaseApi
|
|||
$this->logger->info('Subscription stored', ['ret' => $ret, 'subscription' => $subscription]);
|
||||
|
||||
$subscriptionObj = $this->subscriptionFac->createForApplicationIdAndUserId($application['id'], $uid);
|
||||
$this->response->addJsonContent($subscriptionObj->toArray());
|
||||
$this->jsonExit($subscriptionObj->toArray());
|
||||
}
|
||||
|
||||
public function put(array $request = []): void
|
||||
|
@ -105,7 +105,7 @@ class PushSubscription extends BaseApi
|
|||
]);
|
||||
|
||||
$subscriptionObj = $this->subscriptionFac->createForApplicationIdAndUserId($application['id'], $uid);
|
||||
$this->response->addJsonContent($subscriptionObj->toArray());
|
||||
$this->jsonExit($subscriptionObj->toArray());
|
||||
}
|
||||
|
||||
private function setBoolean($input): bool
|
||||
|
@ -130,10 +130,10 @@ class PushSubscription extends BaseApi
|
|||
'uid' => $uid,
|
||||
]);
|
||||
|
||||
$this->response->addJsonContent([]);
|
||||
$this->jsonExit([]);
|
||||
}
|
||||
|
||||
protected function rawContent(array $request = []): void
|
||||
protected function get(array $request = []): void
|
||||
{
|
||||
$this->checkAllowedScope(self::SCOPE_PUSH);
|
||||
$uid = self::getCurrentUserID();
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
namespace Friendica\Module\Api\Mastodon;
|
||||
|
||||
use Friendica\App\Router;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Post;
|
||||
|
@ -48,7 +47,7 @@ class ScheduledStatuses extends BaseApi
|
|||
/**
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
protected function rawContent(array $request = [])
|
||||
protected function get(array $request = [])
|
||||
{
|
||||
$this->checkAllowedScope(self::SCOPE_READ);
|
||||
$uid = self::getCurrentUserID();
|
||||
|
@ -58,10 +57,10 @@ class ScheduledStatuses extends BaseApi
|
|||
}
|
||||
|
||||
$request = $this->getRequest([
|
||||
'limit' => 20, // Max number of results to return. Defaults to 20.
|
||||
'max_id' => 0, // Return results older than ID
|
||||
'since_id' => 0, // Return results newer than ID
|
||||
'min_id' => 0, // Return results immediately newer than ID
|
||||
'limit' => 20, // Max number of results to return. Defaults to 20.
|
||||
'max_id' => 0, // Return results older than ID
|
||||
'since_id' => 0, // Return results newer than ID
|
||||
'min_id' => 0, // Return results immediately newer than ID
|
||||
], $request);
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => $request['limit']];
|
||||
|
@ -77,7 +76,7 @@ class ScheduledStatuses extends BaseApi
|
|||
}
|
||||
|
||||
if (!empty($request['min_id'])) {
|
||||
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $request['min_id']]);
|
||||
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $request['min_id']]);
|
||||
$params['order'] = ['uri-id'];
|
||||
}
|
||||
|
||||
|
|
|
@ -64,18 +64,18 @@ class Statuses extends BaseApi
|
|||
throw new HTTPException\NotFoundException('Item with URI ID ' . $this->parameters['id'] . ' not found for user ' . $uid . '.');
|
||||
}
|
||||
|
||||
$item['title'] = '';
|
||||
$item['uid'] = $post['uid'];
|
||||
$item['body'] = $this->formatStatus($request['status'], $uid);
|
||||
$item['network'] = $post['network'];
|
||||
$item['gravity'] = $post['gravity'];
|
||||
$item['verb'] = $post['verb'];
|
||||
$item['allow_cid'] = $post['allow_cid'];
|
||||
$item['allow_gid'] = $post['allow_gid'];
|
||||
$item['deny_cid'] = $post['deny_cid'];
|
||||
$item['deny_gid'] = $post['deny_gid'];
|
||||
$item['app'] = $this->getApp();
|
||||
$item['sensitive'] = $request['sensitive'];
|
||||
$item['title'] = '';
|
||||
$item['uid'] = $post['uid'];
|
||||
$item['body'] = $this->formatStatus($request['status'], $uid);
|
||||
$item['network'] = $post['network'];
|
||||
$item['gravity'] = $post['gravity'];
|
||||
$item['verb'] = $post['verb'];
|
||||
$item['allow_cid'] = $post['allow_cid'];
|
||||
$item['allow_gid'] = $post['allow_gid'];
|
||||
$item['deny_cid'] = $post['deny_cid'];
|
||||
$item['deny_gid'] = $post['deny_gid'];
|
||||
$item['app'] = $this->getApp();
|
||||
$item['sensitive'] = $request['sensitive'];
|
||||
|
||||
if (!empty($request['language'])) {
|
||||
$item['language'] = json_encode([$request['language'] => 1]);
|
||||
|
@ -91,7 +91,7 @@ class Statuses extends BaseApi
|
|||
if (!isset($request['friendica']['title']) && $post['gravity'] == Item::GRAVITY_PARENT && DI::pConfig()->get($uid, 'system', 'api_spoiler_title', true)) {
|
||||
$item['title'] = $spoiler_text;
|
||||
} else {
|
||||
$item['body'] = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $spoiler_text . "[/abstract]\n" . $item['body'];
|
||||
$item['body'] = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $spoiler_text . "[/abstract]\n" . $item['body'];
|
||||
$item['content-warning'] = BBCode::toPlaintext($spoiler_text);
|
||||
}
|
||||
}
|
||||
|
@ -307,7 +307,8 @@ class Statuses extends BaseApi
|
|||
|
||||
if (!empty($request['scheduled_at'])) {
|
||||
$item['guid'] = Item::guid($item, true);
|
||||
$item['uri'] = Item::newURI($item['guid']);
|
||||
$item['uri'] = Item::newURI($item['guid']);
|
||||
|
||||
$id = Post\Delayed::add($item['uri'], $item, Worker::PRIORITY_HIGH, Post\Delayed::PREPARED, DateTimeFormat::utc($request['scheduled_at']));
|
||||
if (empty($id)) {
|
||||
$this->logAndJsonError(500, $this->errorFactory->InternalError());
|
||||
|
@ -350,7 +351,7 @@ class Statuses extends BaseApi
|
|||
/**
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
protected function rawContent(array $request = [])
|
||||
protected function get(array $request = [])
|
||||
{
|
||||
$uid = self::getCurrentUserID();
|
||||
|
||||
|
@ -385,7 +386,7 @@ class Statuses extends BaseApi
|
|||
|
||||
foreach ($media_ids as $id) {
|
||||
if (DI::mstdnAttachment()->isAttach($id) && Attach::exists(['id' => substr($id, 7)])) {
|
||||
$attach = Attach::selectFirst([], ['id' => substr($id, 7)]);
|
||||
$attach = Attach::selectFirst([], ['id' => substr($id, 7)]);
|
||||
$attachment = [
|
||||
'type' => Post\Media::getType($attach['filetype']),
|
||||
'mimetype' => $attach['filetype'],
|
||||
|
@ -422,8 +423,8 @@ class Statuses extends BaseApi
|
|||
];
|
||||
|
||||
if (count($media) > 1) {
|
||||
$attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . $ext;
|
||||
$attachment['preview-width'] = $media[1]['width'];
|
||||
$attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . $ext;
|
||||
$attachment['preview-width'] = $media[1]['width'];
|
||||
$attachment['preview-height'] = $media[1]['height'];
|
||||
}
|
||||
$item['attachments'][] = $attachment;
|
||||
|
|
|
@ -18,7 +18,7 @@ use Friendica\Module\BaseApi;
|
|||
*/
|
||||
class Inbox extends DirectMessagesEndpoint
|
||||
{
|
||||
protected function rawContent(array $request = [])
|
||||
protected function get(array $request = [])
|
||||
{
|
||||
$this->checkAllowedScope(BaseApi::SCOPE_READ);
|
||||
$uid = BaseApi::getCurrentUserID();
|
||||
|
|
|
@ -165,7 +165,6 @@ class Install extends BaseModule
|
|||
|
||||
break;
|
||||
}
|
||||
DI::baseUrl()->redirect('install?pass=' . $this->currentWizardStep);
|
||||
}
|
||||
|
||||
protected function content(array $request = []): string
|
||||
|
@ -180,20 +179,20 @@ class Install extends BaseModule
|
|||
|
||||
$status = $this->installer->checkEnvironment($this->baseUrl, $php_path);
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('install/01_checks.tpl');
|
||||
$tpl = Renderer::getMarkupTemplate('install/01_checks.tpl');
|
||||
$output .= Renderer::replaceMacros($tpl, [
|
||||
'$title' => $install_title,
|
||||
'$pass' => $this->t('System check'),
|
||||
'$required' => $this->t('Required'),
|
||||
'$requirement_not_satisfied' => $this->t('Requirement not satisfied'),
|
||||
'$title' => $install_title,
|
||||
'$pass' => $this->t('System check'),
|
||||
'$required' => $this->t('Required'),
|
||||
'$requirement_not_satisfied' => $this->t('Requirement not satisfied'),
|
||||
'$optional_requirement_not_satisfied' => $this->t('Optional requirement not satisfied'),
|
||||
'$ok' => $this->t('OK'),
|
||||
'$checks' => $this->installer->getChecks(),
|
||||
'$passed' => $status,
|
||||
'$see_install' => $this->t('Please see the file "doc/INSTALL.md".'),
|
||||
'$next' => $this->t('Next'),
|
||||
'$reload' => $this->t('Check again'),
|
||||
'$php_path' => $php_path,
|
||||
'$ok' => $this->t('OK'),
|
||||
'$checks' => $this->installer->getChecks(),
|
||||
'$passed' => $status,
|
||||
'$see_install' => $this->t('Please see the file "doc/INSTALL.md".'),
|
||||
'$next' => $this->t('Next'),
|
||||
'$reload' => $this->t('Check again'),
|
||||
'$php_path' => $php_path,
|
||||
]);
|
||||
break;
|
||||
|
||||
|
@ -202,62 +201,62 @@ class Install extends BaseModule
|
|||
new Uri($this->configCache->get('system', 'url')) :
|
||||
$this->baseUrl;
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('install/02_base_config.tpl');
|
||||
$tpl = Renderer::getMarkupTemplate('install/02_base_config.tpl');
|
||||
$output .= Renderer::replaceMacros($tpl, [
|
||||
'$title' => $install_title,
|
||||
'$pass' => $this->t('Base settings'),
|
||||
'$basepath' => ['system-basepath',
|
||||
'$title' => $install_title,
|
||||
'$pass' => $this->t('Base settings'),
|
||||
'$basepath' => ['system-basepath',
|
||||
$this->t("Base path to installation"),
|
||||
$this->configCache->get('system', 'basepath'),
|
||||
$this->t("If the system cannot detect the correct path to your installation, enter the correct path here. This setting should only be set if you are using a restricted system and symbolic links to your webroot."),
|
||||
$this->t('Required')],
|
||||
'$system_url' => ['system-url',
|
||||
'$system_url' => ['system-url',
|
||||
$this->t('The Friendica system URL'),
|
||||
(string)$baseUrl,
|
||||
$this->t("Overwrite this field in case the system URL determination isn't right, otherwise leave it as is."),
|
||||
$this->t('Required')],
|
||||
'$php_path' => $this->configCache->get('config', 'php_path'),
|
||||
'$submit' => $this->t('Submit'),
|
||||
'$php_path' => $this->configCache->get('config', 'php_path'),
|
||||
'$submit' => $this->t('Submit'),
|
||||
]);
|
||||
break;
|
||||
|
||||
case self::DATABASE_CONFIG:
|
||||
$tpl = Renderer::getMarkupTemplate('install/03_database_config.tpl');
|
||||
$tpl = Renderer::getMarkupTemplate('install/03_database_config.tpl');
|
||||
$output .= Renderer::replaceMacros($tpl, [
|
||||
'$title' => $install_title,
|
||||
'$pass' => $this->t('Database connection'),
|
||||
'$info_01' => $this->t('In order to install Friendica we need to know how to connect to your database.'),
|
||||
'$info_02' => $this->t('Please contact your hosting provider or site administrator if you have questions about these settings.'),
|
||||
'$info_03' => $this->t('The database you specify below should already exist. If it does not, please create it before continuing.'),
|
||||
'$required' => $this->t('Required'),
|
||||
'$title' => $install_title,
|
||||
'$pass' => $this->t('Database connection'),
|
||||
'$info_01' => $this->t('In order to install Friendica we need to know how to connect to your database.'),
|
||||
'$info_02' => $this->t('Please contact your hosting provider or site administrator if you have questions about these settings.'),
|
||||
'$info_03' => $this->t('The database you specify below should already exist. If it does not, please create it before continuing.'),
|
||||
'$required' => $this->t('Required'),
|
||||
'$requirement_not_satisfied' => $this->t('Requirement not satisfied'),
|
||||
'$checks' => $this->installer->getChecks(),
|
||||
'$basepath' => $this->configCache->get('system', 'basepath'),
|
||||
'$system_url' => $this->configCache->get('system', 'url'),
|
||||
'$dbhost' => ['database-hostname',
|
||||
'$checks' => $this->installer->getChecks(),
|
||||
'$basepath' => $this->configCache->get('system', 'basepath'),
|
||||
'$system_url' => $this->configCache->get('system', 'url'),
|
||||
'$dbhost' => ['database-hostname',
|
||||
$this->t('Database Server Name'),
|
||||
$this->configCache->get('database', 'hostname'),
|
||||
'',
|
||||
$this->t('Required')],
|
||||
'$dbuser' => ['database-username',
|
||||
'$dbuser' => ['database-username',
|
||||
$this->t('Database Login Name'),
|
||||
$this->configCache->get('database', 'username'),
|
||||
'',
|
||||
$this->t('Required'),
|
||||
'autofocus'],
|
||||
'$dbpass' => ['database-password',
|
||||
'$dbpass' => ['database-password',
|
||||
$this->t('Database Login Password'),
|
||||
$this->configCache->get('database', 'password'),
|
||||
$this->t("For security reasons the password must not be empty"),
|
||||
$this->t('Required')],
|
||||
'$dbdata' => ['database-database',
|
||||
'$dbdata' => ['database-database',
|
||||
$this->t('Database Name'),
|
||||
$this->configCache->get('database', 'database'),
|
||||
'',
|
||||
$this->t('Required')],
|
||||
'$lbl_10' => $this->t('Please select a default timezone for your website'),
|
||||
'$php_path' => $this->configCache->get('config', 'php_path'),
|
||||
'$submit' => $this->t('Submit')
|
||||
'$lbl_10' => $this->t('Please select a default timezone for your website'),
|
||||
'$php_path' => $this->configCache->get('config', 'php_path'),
|
||||
'$submit' => $this->t('Submit')
|
||||
]);
|
||||
break;
|
||||
|
||||
|
@ -265,7 +264,7 @@ class Install extends BaseModule
|
|||
/* Installed langs */
|
||||
$lang_choices = $this->l10n->getAvailableLanguages();
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('install/04_site_settings.tpl');
|
||||
$tpl = Renderer::getMarkupTemplate('install/04_site_settings.tpl');
|
||||
$output .= Renderer::replaceMacros($tpl, [
|
||||
'$title' => $install_title,
|
||||
'$required' => $this->t('Required'),
|
||||
|
@ -282,17 +281,19 @@ class Install extends BaseModule
|
|||
$this->configCache->get('config', 'admin_email'),
|
||||
$this->t('Your account email address must match this in order to use the web admin panel.'),
|
||||
$this->t('Required'), 'autofocus', 'email'],
|
||||
'$timezone' => Temporal::getTimezoneField('system-default_timezone',
|
||||
'$timezone' => Temporal::getTimezoneField(
|
||||
'system-default_timezone',
|
||||
$this->t('Please select a default timezone for your website'),
|
||||
$this->configCache->get('system', 'default_timezone'),
|
||||
''),
|
||||
'$language' => ['system-language',
|
||||
''
|
||||
),
|
||||
'$language' => ['system-language',
|
||||
$this->t('System Language:'),
|
||||
$this->configCache->get('system', 'language'),
|
||||
$this->t('Set the default language for your Friendica installation interface and to send emails.'),
|
||||
$lang_choices],
|
||||
'$php_path' => $this->configCache->get('config', 'php_path'),
|
||||
'$submit' => $this->t('Submit')
|
||||
'$php_path' => $this->configCache->get('config', 'php_path'),
|
||||
'$submit' => $this->t('Submit')
|
||||
]);
|
||||
break;
|
||||
|
||||
|
@ -300,19 +301,19 @@ class Install extends BaseModule
|
|||
$db_return_text = "";
|
||||
|
||||
if (count($this->installer->getChecks()) == 0) {
|
||||
$txt = '<p style="font-size: 130%;">';
|
||||
$txt .= $this->t('Your Friendica site database has been installed.') . '<br />';
|
||||
$txt = '<p style="font-size: 130%;">';
|
||||
$txt .= $this->t('Your Friendica site database has been installed.') . '<br />';
|
||||
$db_return_text .= $txt;
|
||||
}
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('install/05_finished.tpl');
|
||||
$tpl = Renderer::getMarkupTemplate('install/05_finished.tpl');
|
||||
$output .= Renderer::replaceMacros($tpl, [
|
||||
'$title' => $install_title,
|
||||
'$required' => $this->t('Required'),
|
||||
'$title' => $install_title,
|
||||
'$required' => $this->t('Required'),
|
||||
'$requirement_not_satisfied' => $this->t('Requirement not satisfied'),
|
||||
'$checks' => $this->installer->getChecks(),
|
||||
'$pass' => $this->t('Installation finished'),
|
||||
'$text' => $db_return_text . $this->whatNext(),
|
||||
'$checks' => $this->installer->getChecks(),
|
||||
'$pass' => $this->t('Installation finished'),
|
||||
'$text' => $db_return_text . $this->whatNext(),
|
||||
]);
|
||||
|
||||
break;
|
||||
|
|
|
@ -273,7 +273,7 @@ class Index extends BaseSearch
|
|||
*/
|
||||
private static function tryRedirectToPost(string $search)
|
||||
{
|
||||
if (parse_url($search, PHP_URL_SCHEME) == '') {
|
||||
if (!parse_url($search, PHP_URL_SCHEME) && !preg_match('=^[a-z]+://=', $search)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class AppSpecific extends BaseSettings
|
|||
{
|
||||
parent::__construct($session, $page, $l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->pConfig = $pConfig;
|
||||
$this->pConfig = $pConfig;
|
||||
$this->systemMessages = $systemMessages;
|
||||
|
||||
if (!$this->session->getLocalUserId()) {
|
||||
|
@ -77,12 +77,11 @@ class AppSpecific extends BaseSettings
|
|||
$this->baseUrl->redirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
|
||||
} else {
|
||||
$this->appSpecificPassword = AppSpecificPassword::generateForUser($this->session->getLocalUserId(), $request['description'] ?? '');
|
||||
$this->systemMessages->addInfo($this->t('New app-specific password generated: %s', $this->appSpecificPassword['plaintext_password']));
|
||||
$this->baseUrl->redirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
|
||||
$this->systemMessages->addInfo($this->t('New app-specific password generated.'));
|
||||
}
|
||||
|
||||
break;
|
||||
case 'revoke_all' :
|
||||
case 'revoke_all':
|
||||
AppSpecificPassword::deleteAllForUser($this->session->getLocalUserId());
|
||||
$this->systemMessages->addInfo($this->t('App-specific passwords successfully revoked.'));
|
||||
$this->baseUrl->redirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
|
||||
|
@ -115,22 +114,22 @@ class AppSpecific extends BaseSettings
|
|||
'$form_security_token' => self::getFormSecurityToken('settings_2fa_app_specific'),
|
||||
'$password_security_token' => self::getFormSecurityToken('settings_2fa_password'),
|
||||
|
||||
'$title' => $this->t('Two-factor app-specific passwords'),
|
||||
'$help_label' => $this->t('Help'),
|
||||
'$message' => $this->t('<p>App-specific passwords are randomly generated passwords used instead your regular password to authenticate your account on third-party applications that don\'t support two-factor authentication.</p>'),
|
||||
'$generated_message' => $this->t('Make sure to copy your new app-specific password now. You won’t be able to see it again!'),
|
||||
'$title' => $this->t('Two-factor app-specific passwords'),
|
||||
'$help_label' => $this->t('Help'),
|
||||
'$message' => $this->t('<p>App-specific passwords are randomly generated passwords used instead your regular password to authenticate your account on third-party applications that don\'t support two-factor authentication.</p>'),
|
||||
'$generated_message' => $this->t('Make sure to copy your new app-specific password now. You won’t be able to see it again!'),
|
||||
'$generated_app_specific_password' => $this->appSpecificPassword,
|
||||
|
||||
'$description_label' => $this->t('Description'),
|
||||
'$last_used_label' => $this->t('Last Used'),
|
||||
'$revoke_label' => $this->t('Revoke'),
|
||||
'$revoke_all_label' => $this->t('Revoke All'),
|
||||
'$description_label' => $this->t('Description'),
|
||||
'$last_used_label' => $this->t('Last Used'),
|
||||
'$revoke_label' => $this->t('Revoke'),
|
||||
'$revoke_all_label' => $this->t('Revoke All'),
|
||||
|
||||
'$app_specific_passwords' => $appSpecificPasswords,
|
||||
'$generate_message' => $this->t('When you generate a new app-specific password, you must use it right away, it will be shown to you once after you generate it.'),
|
||||
'$generate_title' => $this->t('Generate new app-specific password'),
|
||||
'$app_specific_passwords' => $appSpecificPasswords,
|
||||
'$generate_message' => $this->t('When you generate a new app-specific password, you must use it right away, it will be shown to you once after you generate it.'),
|
||||
'$generate_title' => $this->t('Generate new app-specific password'),
|
||||
'$description_placeholder_label' => $this->t('Friendiqa on my Fairphone 2...'),
|
||||
'$generate_label' => $this->t('Generate'),
|
||||
'$generate_label' => $this->t('Generate'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,10 +291,6 @@ final class ATProtocol
|
|||
*/
|
||||
public function getUserDid(int $uid, bool $refresh = false): ?string
|
||||
{
|
||||
if (!$this->pConfig->get($uid, 'bluesky', 'post')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$refresh) {
|
||||
$did = $this->pConfig->get($uid, 'bluesky', 'did');
|
||||
if (!empty($did)) {
|
||||
|
|
|
@ -38,9 +38,9 @@ use stdClass;
|
|||
*/
|
||||
class Jetstream
|
||||
{
|
||||
private $uids = [];
|
||||
private $self = [];
|
||||
private $capped = false;
|
||||
private $uids = [];
|
||||
private $self = [];
|
||||
private $capped = false;
|
||||
|
||||
/** @var LoggerInterface */
|
||||
private $logger;
|
||||
|
@ -95,6 +95,7 @@ class Jetstream
|
|||
// @todo make the path configurable
|
||||
$this->client = new \WebSocket\Client('wss://jetstream1.us-west.bsky.network/subscribe?requireHello=true' . $cursor);
|
||||
$this->client->setTimeout($timeout);
|
||||
$this->client->setLogger($this->logger);
|
||||
} catch (\WebSocket\ConnectionException $e) {
|
||||
$this->logger->error('Error while trying to establish the connection', ['code' => $e->getCode(), 'message' => $e->getMessage()]);
|
||||
echo "Connection wasn't established.\n";
|
||||
|
@ -212,8 +213,8 @@ class Jetstream
|
|||
|
||||
if (!$this->capped && count($dids) < $did_limit) {
|
||||
$condition = ["`uid` = ? AND `network` = ? AND EXISTS(SELECT `author-id` FROM `post-user` WHERE `author-id` = `contact`.`id` AND `post-user`.`uid` != ?)", 0, Protocol::BLUESKY, 0];
|
||||
$contacts = Contact::selectToArray(['url'], $condition, ['order' => ['last-item' => true], 'limit' => $did_limit]);
|
||||
$dids = $this->addDids($contacts, $uids, $did_limit, $dids);
|
||||
$contacts = Contact::selectToArray(['url'], $condition, ['order' => ['last-item' => true], 'limit' => $did_limit]);
|
||||
$dids = $this->addDids($contacts, $uids, $did_limit, $dids);
|
||||
}
|
||||
|
||||
$this->keyValue->set('jetstream_did_count', count($dids));
|
||||
|
@ -365,7 +366,7 @@ class Jetstream
|
|||
}
|
||||
|
||||
/**
|
||||
* Route app.bsky.feed.post commits
|
||||
* Route app.bsky.feed.post commits
|
||||
*
|
||||
* @param stdClass $data message object
|
||||
* @param integer $drift
|
||||
|
@ -389,7 +390,7 @@ class Jetstream
|
|||
}
|
||||
|
||||
/**
|
||||
* Route app.bsky.feed.repost commits
|
||||
* Route app.bsky.feed.repost commits
|
||||
*
|
||||
* @param stdClass $data message object
|
||||
* @param integer $drift
|
||||
|
@ -413,7 +414,7 @@ class Jetstream
|
|||
}
|
||||
|
||||
/**
|
||||
* Route app.bsky.feed.like commits
|
||||
* Route app.bsky.feed.like commits
|
||||
*
|
||||
* @param stdClass $data message object
|
||||
* @return void
|
||||
|
@ -436,7 +437,7 @@ class Jetstream
|
|||
}
|
||||
|
||||
/**
|
||||
* Route app.bsky.actor.profile commits
|
||||
* Route app.bsky.actor.profile commits
|
||||
*
|
||||
* @param stdClass $data message object
|
||||
* @return void
|
||||
|
@ -463,7 +464,7 @@ class Jetstream
|
|||
}
|
||||
|
||||
/**
|
||||
* Route app.bsky.graph.follow commits
|
||||
* Route app.bsky.graph.follow commits
|
||||
*
|
||||
* @param stdClass $data message object
|
||||
* @return void
|
||||
|
|
|
@ -47,18 +47,19 @@ use Friendica\Util\Strings;
|
|||
class Receiver
|
||||
{
|
||||
const PUBLIC_COLLECTION = 'as:Public';
|
||||
const ACCOUNT_TYPES = ['as:Person', 'as:Organization', 'as:Service', 'as:Group', 'as:Application'];
|
||||
const CONTENT_TYPES = ['as:Note', 'as:Article', 'as:Video', 'as:Image', 'as:Event', 'as:Audio', 'as:Page', 'as:Question'];
|
||||
|
||||
const ACCOUNT_TYPES = ['as:Person', 'as:Organization', 'as:Service', 'as:Group', 'as:Application'];
|
||||
const CONTENT_TYPES = ['as:Note', 'as:Article', 'as:Video', 'as:Image', 'as:Event', 'as:Audio', 'as:Page', 'as:Question'];
|
||||
const ACTIVITY_TYPES = ['as:Like', 'as:Dislike', 'as:Accept', 'as:Reject', 'as:TentativeAccept', 'as:View', 'as:Read', 'litepub:EmojiReact'];
|
||||
|
||||
const TARGET_UNKNOWN = 0;
|
||||
const TARGET_TO = 1;
|
||||
const TARGET_CC = 2;
|
||||
const TARGET_BTO = 3;
|
||||
const TARGET_BCC = 4;
|
||||
const TARGET_UNKNOWN = 0;
|
||||
const TARGET_TO = 1;
|
||||
const TARGET_CC = 2;
|
||||
const TARGET_BTO = 3;
|
||||
const TARGET_BCC = 4;
|
||||
const TARGET_FOLLOWER = 5;
|
||||
const TARGET_ANSWER = 6;
|
||||
const TARGET_GLOBAL = 7;
|
||||
const TARGET_ANSWER = 6;
|
||||
const TARGET_GLOBAL = 7;
|
||||
const TARGET_AUDIENCE = 8;
|
||||
|
||||
const COMPLETION_NONE = 0;
|
||||
|
@ -280,7 +281,7 @@ class Receiver
|
|||
$data = Processor::fetchCachedActivity($object_id, $uid);
|
||||
if (!empty($data)) {
|
||||
$object = JsonLD::compact($data);
|
||||
$type = JsonLD::fetchElement($object, '@type');
|
||||
$type = JsonLD::fetchElement($object, '@type');
|
||||
if (!empty($type)) {
|
||||
return $type;
|
||||
}
|
||||
|
@ -335,7 +336,7 @@ class Receiver
|
|||
if (($fetched_id == $id) && !empty($fetched_type) && ($fetched_type == $type)) {
|
||||
Logger::info('Activity had been fetched successfully', ['id' => $id]);
|
||||
$trust_source = true;
|
||||
$activity = $object;
|
||||
$activity = $object;
|
||||
} elseif (($fetched_id == $object_id) && !empty($fetched_type) && ($fetched_type == $object_type)) {
|
||||
Logger::info('Fetched data is the object instead of the activity', ['id' => $id]);
|
||||
$trust_source = true;
|
||||
|
@ -359,9 +360,9 @@ class Receiver
|
|||
|
||||
// Fetch all receivers from to, cc, bto and bcc
|
||||
$receiverdata = self::getReceivers($activity, $original_actor ?: $actor, [], false, $push || $fetched);
|
||||
$receivers = $reception_types = [];
|
||||
$receivers = $reception_types = [];
|
||||
foreach ($receiverdata as $key => $data) {
|
||||
$receivers[$key] = $data['uid'];
|
||||
$receivers[$key] = $data['uid'];
|
||||
$reception_types[$data['uid']] = $data['type'] ?? self::TARGET_UNKNOWN;
|
||||
}
|
||||
|
||||
|
@ -370,9 +371,10 @@ class Receiver
|
|||
// When it is a delivery to a personal inbox we add that user to the receivers
|
||||
if (!empty($uid)) {
|
||||
$additional = [$uid => $uid];
|
||||
$receivers = array_replace($receivers, $additional);
|
||||
$receivers = array_replace($receivers, $additional);
|
||||
if (empty($activity['thread-completion']) && (empty($reception_types[$uid]) || in_array($reception_types[$uid], [self::TARGET_UNKNOWN, self::TARGET_FOLLOWER, self::TARGET_ANSWER, self::TARGET_GLOBAL]))) {
|
||||
$reception_types[$uid] = self::TARGET_BCC;
|
||||
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
if (!empty($owner['url'])) {
|
||||
$urls['as:bcc'][] = $owner['url'];
|
||||
|
@ -400,19 +402,21 @@ class Receiver
|
|||
// Any activities on account types must not be altered
|
||||
if (in_array($type, ['as:Flag'])) {
|
||||
$object_data = [];
|
||||
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
|
||||
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
|
||||
|
||||
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
|
||||
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
|
||||
$object_data['object_ids'] = JsonLD::fetchElementArray($activity, 'as:object', '@id');
|
||||
$object_data['content'] = JsonLD::fetchElement($activity, 'as:content', '@type');
|
||||
$object_data['content'] = JsonLD::fetchElement($activity, 'as:content', '@type');
|
||||
} elseif (in_array($object_type, self::ACCOUNT_TYPES)) {
|
||||
$object_data = [];
|
||||
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
|
||||
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
|
||||
$object_data['object_actor'] = JsonLD::fetchElement($activity['as:object'], 'as:actor', '@id');
|
||||
|
||||
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
|
||||
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
|
||||
$object_data['object_actor'] = JsonLD::fetchElement($activity['as:object'], 'as:actor', '@id');
|
||||
$object_data['object_object'] = JsonLD::fetchElement($activity['as:object'], 'as:object');
|
||||
$object_data['object_type'] = JsonLD::fetchElement($activity['as:object'], '@type');
|
||||
$object_data['object_type'] = JsonLD::fetchElement($activity['as:object'], '@type');
|
||||
if (!$trust_source && ($type == 'as:Delete')) {
|
||||
$apcontact = APContact::getByURL($object_data['object_id'], true);
|
||||
$apcontact = APContact::getByURL($object_data['object_id'], true);
|
||||
$trust_source = empty($apcontact) || ($apcontact['type'] == 'Tombstone') || $apcontact['suspended'];
|
||||
}
|
||||
} elseif (in_array($type, ['as:Create', 'as:Update', 'as:Invite']) || strpos($type, '#emojiReaction')) {
|
||||
|
@ -436,24 +440,27 @@ class Receiver
|
|||
// Create a mostly empty array out of the activity data (instead of the object).
|
||||
// This way we later don't have to check for the existence of each individual array element.
|
||||
$object_data = self::processObject($activity, $original_actor);
|
||||
$object_data['name'] = $type;
|
||||
$object_data['author'] = JsonLD::fetchElement($activity, 'as:actor', '@id');
|
||||
$object_data['object_id'] = $object_id;
|
||||
|
||||
$object_data['name'] = $type;
|
||||
$object_data['author'] = JsonLD::fetchElement($activity, 'as:actor', '@id');
|
||||
$object_data['object_id'] = $object_id;
|
||||
$object_data['object_type'] = ''; // Since we don't fetch the object, we don't know the type
|
||||
} elseif (in_array($type, ['as:Add', 'as:Remove', 'as:Move'])) {
|
||||
$object_data = [];
|
||||
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
|
||||
$object_data['target_id'] = JsonLD::fetchElement($activity, 'as:target', '@id');
|
||||
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
|
||||
$object_data['object_type'] = JsonLD::fetchElement($activity['as:object'], '@type');
|
||||
|
||||
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
|
||||
$object_data['target_id'] = JsonLD::fetchElement($activity, 'as:target', '@id');
|
||||
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
|
||||
$object_data['object_type'] = JsonLD::fetchElement($activity['as:object'], '@type');
|
||||
$object_data['object_content'] = JsonLD::fetchElement($activity['as:object'], 'as:content', '@type');
|
||||
} else {
|
||||
$object_data = [];
|
||||
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
|
||||
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
|
||||
$object_data['object_actor'] = JsonLD::fetchElement($activity['as:object'], 'as:actor', '@id');
|
||||
|
||||
$object_data['id'] = JsonLD::fetchElement($activity, '@id');
|
||||
$object_data['object_id'] = JsonLD::fetchElement($activity, 'as:object', '@id');
|
||||
$object_data['object_actor'] = JsonLD::fetchElement($activity['as:object'], 'as:actor', '@id');
|
||||
$object_data['object_object'] = JsonLD::fetchElement($activity['as:object'], 'as:object');
|
||||
$object_data['object_type'] = JsonLD::fetchElement($activity['as:object'], '@type');
|
||||
$object_data['object_type'] = JsonLD::fetchElement($activity['as:object'], '@type');
|
||||
|
||||
// An Undo is done on the object of an object, so we need that type as well
|
||||
if (($type == 'as:Undo') && !empty($object_data['object_object'])) {
|
||||
|
@ -482,13 +489,13 @@ class Receiver
|
|||
}
|
||||
}
|
||||
|
||||
$object_data['type'] = $type;
|
||||
$object_data['actor'] = $actor;
|
||||
$object_data['item_receiver'] = $receivers;
|
||||
$object_data['receiver'] = array_replace($object_data['receiver'] ?? [], $receivers);
|
||||
$object_data['type'] = $type;
|
||||
$object_data['actor'] = $actor;
|
||||
$object_data['item_receiver'] = $receivers;
|
||||
$object_data['receiver'] = array_replace($object_data['receiver'] ?? [], $receivers);
|
||||
$object_data['reception_type'] = array_replace($object_data['reception_type'] ?? [], $reception_types);
|
||||
|
||||
$account = Contact::selectFirstAccount(['platform'], ['nurl' => Strings::normaliseLink($actor)]);
|
||||
$account = Contact::selectFirstAccount(['platform'], ['nurl' => Strings::normaliseLink($actor)]);
|
||||
$platform = $account['platform'] ?? '';
|
||||
|
||||
Logger::info('Processing', ['type' => $object_data['type'], 'object_type' => $object_data['object_type'], 'id' => $object_data['id'], 'actor' => $actor, 'platform' => $platform]);
|
||||
|
@ -555,6 +562,7 @@ class Receiver
|
|||
|
||||
$user = User::getById(array_key_first($receivers), ['language']);
|
||||
$l10n = DI::l10n()->withLang($user['language']);
|
||||
|
||||
$object_data['name'] = $l10n->t('Chat');
|
||||
|
||||
$mail = DBA::selectFirst('mail', ['uri'], ['uid' => array_key_first($receivers), 'title' => $object_data['name']], ['order' => ['id' => true]]);
|
||||
|
@ -633,7 +641,7 @@ class Receiver
|
|||
|
||||
if (!empty($published) && $object_id !== null && in_array($type, ['as:Create', 'as:Update']) && in_array($object_type, self::CONTENT_TYPES)
|
||||
&& ($push || ($completion != self::COMPLETION_MANUAL)) && DI::contentItem()->isTooOld($published) && !Post::exists(['uri' => $object_id])) {
|
||||
Logger::debug('Activity is too old. It will not be processed', ['push' => $push, 'completion' => $completion, 'type' => $type, 'object-type' => $object_type, 'published' => $published, 'id' => $id, 'object-id' => $object_id]);
|
||||
Logger::debug('Activity is too old. It will not be processed', ['push' => $push, 'completion' => $completion, 'type' => $type, 'object-type' => $object_type, 'published' => $published, 'id' => $id, 'object-id' => $object_id]);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
|
@ -662,8 +670,8 @@ class Receiver
|
|||
} elseif (in_array($object_type, array_merge(self::ACTIVITY_TYPES, ['as:Delete', 'as:Undo', 'as:Update']))) {
|
||||
Logger::debug('Change announced activity to activity', ['type' => $object_type]);
|
||||
$original_actor = $actor;
|
||||
$type = $object_type;
|
||||
$activity = $activity['as:object'];
|
||||
$type = $object_type;
|
||||
$activity = $activity['as:object'];
|
||||
} else {
|
||||
Logger::info('Unhandled announced activity', ['type' => $object_type, 'object_type' => $object_object_type]);
|
||||
}
|
||||
|
@ -699,7 +707,7 @@ class Receiver
|
|||
}
|
||||
|
||||
if ($type == 'as:Announce') {
|
||||
$object_data['object_activity'] = $activity;
|
||||
$object_data['object_activity'] = $activity;
|
||||
}
|
||||
|
||||
if (($type == 'as:Create') && $trust_source && !in_array($completion, [self::COMPLETION_MANUAL, self::COMPLETION_ANNOUNCE])) {
|
||||
|
@ -717,7 +725,7 @@ class Receiver
|
|||
self::addArrivedId($object_data['object_id']);
|
||||
}
|
||||
|
||||
$object_data['children'] = $activity['children'] ?? [];
|
||||
$object_data['children'] = $activity['children'] ?? [];
|
||||
$object_data['callstack'] = $activity['callstack'] ?? [];
|
||||
|
||||
$decouple = DI::config()->get('system', 'decoupled_receiver') && !in_array($completion, [self::COMPLETION_MANUAL, self::COMPLETION_ANNOUNCE]) && empty($object_data['directmessage']);
|
||||
|
@ -727,7 +735,7 @@ class Receiver
|
|||
}
|
||||
|
||||
if (!$trust_source) {
|
||||
Logger::info('Activity trust could not be achieved.', ['id' => $object_data['object_id'], 'type' => $type, 'signer' => $signer, 'actor' => $actor, 'attributedTo' => $attributed_to]);
|
||||
Logger::info('Activity trust could not be achieved.', ['id' => $object_data['object_id'], 'type' => $type, 'signer' => $signer, 'actor' => $actor, 'attributedTo' => $attributed_to]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1090,7 +1098,7 @@ class Receiver
|
|||
*/
|
||||
private static function getBestUserForActivity(array $activity, string $actor): int
|
||||
{
|
||||
$uid = 0;
|
||||
$uid = 0;
|
||||
$actor = $actor ?: JsonLD::fetchElement($activity, 'as:actor', '@id') ?? '';
|
||||
|
||||
$receivers = self::getReceivers($activity, $actor, [], false, false);
|
||||
|
@ -1177,12 +1185,12 @@ class Receiver
|
|||
if (!empty($actor)) {
|
||||
$profile = APContact::getByURL($actor);
|
||||
$followers = $profile['followers'] ?? '';
|
||||
$isGroup = ($profile['type'] ?? '') == 'Group';
|
||||
$isGroup = ($profile['type'] ?? '') == 'Group';
|
||||
Logger::info('Got actor and followers', ['actor' => $actor, 'followers' => $followers]);
|
||||
} else {
|
||||
Logger::info('Empty actor', ['activity' => $activity]);
|
||||
$followers = '';
|
||||
$isGroup = false;
|
||||
$isGroup = false;
|
||||
}
|
||||
|
||||
$parent_followers = '';
|
||||
|
@ -1228,7 +1236,7 @@ class Receiver
|
|||
|
||||
// Fetching all directly addressed receivers
|
||||
$condition = ['self' => true, 'nurl' => Strings::normaliseLink($receiver)];
|
||||
$contact = DBA::selectFirst('contact', ['uid', 'contact-type'], $condition);
|
||||
$contact = DBA::selectFirst('contact', ['uid', 'contact-type'], $condition);
|
||||
if (!DBA::isResult($contact)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1236,9 +1244,11 @@ class Receiver
|
|||
// Check if the potential receiver is following the actor
|
||||
// Exception: The receiver is targetted via "to" or this is a comment
|
||||
if ((($element != 'as:to') && empty($replyto)) || ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) {
|
||||
$networks = Protocol::FEDERATED;
|
||||
$condition = ['nurl' => Strings::normaliseLink($actor), 'rel' => [Contact::SHARING, Contact::FRIEND],
|
||||
'network' => $networks, 'archive' => false, 'pending' => false, 'uid' => $contact['uid']];
|
||||
$networks = Protocol::FEDERATED;
|
||||
$condition = [
|
||||
'nurl' => Strings::normaliseLink($actor), 'rel' => [Contact::SHARING, Contact::FRIEND],
|
||||
'network' => $networks, 'archive' => false, 'pending' => false, 'uid' => $contact['uid']
|
||||
];
|
||||
|
||||
// Group posts are only accepted from group contacts
|
||||
if ($contact['contact-type'] == Contact::TYPE_COMMUNITY) {
|
||||
|
@ -1277,6 +1287,7 @@ class Receiver
|
|||
|
||||
if (empty($receivers) && !empty($parent['parent-author-link'])) {
|
||||
$uid = User::getIdForURL($parent['parent-author-link']);
|
||||
|
||||
$receivers[$uid] = ['uid' => $uid, 'type' => self::TARGET_BTO];
|
||||
}
|
||||
|
||||
|
@ -1319,11 +1330,13 @@ class Receiver
|
|||
*/
|
||||
private static function getReceiverForActor(array $tags, array $receivers, int $target_type, array $profile): array
|
||||
{
|
||||
$basecondition = ['rel' => [Contact::SHARING, Contact::FRIEND, Contact::FOLLOWER],
|
||||
'network' => Protocol::FEDERATED, 'archive' => false, 'pending' => false];
|
||||
$basecondition = [
|
||||
'rel' => [Contact::SHARING, Contact::FRIEND, Contact::FOLLOWER],
|
||||
'network' => Protocol::FEDERATED, 'archive' => false, 'pending' => false
|
||||
];
|
||||
|
||||
$condition = DBA::mergeConditions($basecondition, ["`uri-id` = ? AND `uid` != ?", $profile['uri-id'], 0]);
|
||||
$contacts = DBA::select('contact', ['uid', 'rel'], $condition);
|
||||
$contacts = DBA::select('contact', ['uid', 'rel'], $condition);
|
||||
while ($contact = DBA::fetch($contacts)) {
|
||||
if (empty($receivers[$contact['uid']]) && self::isValidReceiverForActor($contact, $tags)) {
|
||||
$receivers[$contact['uid']] = ['uid' => $contact['uid'], 'type' => $target_type];
|
||||
|
@ -1434,7 +1447,7 @@ class Receiver
|
|||
return false;
|
||||
}
|
||||
Logger::info('Using already stored item for url ' . $object_id);
|
||||
$data = ActivityPub\Transmitter::createNote($item);
|
||||
$data = ActivityPub\Transmitter::createNote($item);
|
||||
$object = JsonLD::compact($data);
|
||||
}
|
||||
|
||||
|
@ -1511,9 +1524,9 @@ class Receiver
|
|||
}
|
||||
|
||||
$element = [
|
||||
'type' => str_replace('as:', '', JsonLD::fetchElement($tag, '@type') ?? ''),
|
||||
'href' => JsonLD::fetchElement($tag, 'as:href', '@id'),
|
||||
'name' => JsonLD::fetchElement($tag, 'as:name', '@value'),
|
||||
'type' => str_replace('as:', '', JsonLD::fetchElement($tag, '@type') ?? ''),
|
||||
'href' => JsonLD::fetchElement($tag, 'as:href', '@id'),
|
||||
'name' => JsonLD::fetchElement($tag, 'as:name', '@value'),
|
||||
'mediaType' => JsonLD::fetchElement($tag, 'as:mediaType', '@value')
|
||||
];
|
||||
|
||||
|
@ -1545,10 +1558,9 @@ class Receiver
|
|||
continue;
|
||||
}
|
||||
|
||||
$url = JsonLD::fetchElement($emoji['as:icon'], 'as:url', '@id');
|
||||
$element = [
|
||||
'name' => JsonLD::fetchElement($emoji, 'as:name', '@value'),
|
||||
'href' => $url
|
||||
'href' => JsonLD::fetchElement($emoji['as:icon'], 'as:url', '@id')
|
||||
];
|
||||
|
||||
$emojilist[] = $element;
|
||||
|
@ -1574,7 +1586,7 @@ class Receiver
|
|||
foreach ($attachments as $attachment) {
|
||||
switch (JsonLD::fetchElement($attachment, '@type')) {
|
||||
case 'as:Page':
|
||||
$pageUrl = null;
|
||||
$pageUrl = null;
|
||||
$pageImage = null;
|
||||
|
||||
$urls = JsonLD::fetchElementArray($attachment, 'as:url');
|
||||
|
@ -1585,7 +1597,7 @@ class Receiver
|
|||
continue;
|
||||
}
|
||||
|
||||
$href = JsonLD::fetchElement($url, 'as:href', '@id');
|
||||
$href = JsonLD::fetchElement($url, 'as:href', '@id');
|
||||
$mediaType = JsonLD::fetchElement($url, 'as:mediaType', '@value');
|
||||
if (Strings::startsWith($mediaType, 'image')) {
|
||||
$pageImage = $href;
|
||||
|
@ -1603,12 +1615,12 @@ class Receiver
|
|||
];
|
||||
break;
|
||||
case 'as:Image':
|
||||
$mediaType = JsonLD::fetchElement($attachment, 'as:mediaType', '@value');
|
||||
$imageFullUrl = JsonLD::fetchElement($attachment, 'as:url', '@id');
|
||||
$mediaType = JsonLD::fetchElement($attachment, 'as:mediaType', '@value');
|
||||
$imageFullUrl = JsonLD::fetchElement($attachment, 'as:url', '@id');
|
||||
$imagePreviewUrl = null;
|
||||
// Multiple URLs?
|
||||
if (!$imageFullUrl && ($urls = JsonLD::fetchElementArray($attachment, 'as:url'))) {
|
||||
$imageVariants = [];
|
||||
$imageVariants = [];
|
||||
$previewVariants = [];
|
||||
foreach ($urls as $url) {
|
||||
// Scalar URL, no discrimination possible
|
||||
|
@ -1651,22 +1663,22 @@ class Receiver
|
|||
}
|
||||
|
||||
$attachlist[] = [
|
||||
'type' => str_replace('as:', '', JsonLD::fetchElement($attachment, '@type')),
|
||||
'type' => str_replace('as:', '', JsonLD::fetchElement($attachment, '@type')),
|
||||
'mediaType' => $mediaType,
|
||||
'name' => JsonLD::fetchElement($attachment, 'as:name', '@value'),
|
||||
'url' => $imageFullUrl,
|
||||
'image' => $imagePreviewUrl !== $imageFullUrl ? $imagePreviewUrl : null,
|
||||
'name' => JsonLD::fetchElement($attachment, 'as:name', '@value'),
|
||||
'url' => $imageFullUrl,
|
||||
'image' => $imagePreviewUrl !== $imageFullUrl ? $imagePreviewUrl : null,
|
||||
];
|
||||
break;
|
||||
default:
|
||||
$attachlist[] = [
|
||||
'type' => str_replace('as:', '', JsonLD::fetchElement($attachment, '@type')),
|
||||
'type' => str_replace('as:', '', JsonLD::fetchElement($attachment, '@type')),
|
||||
'mediaType' => JsonLD::fetchElement($attachment, 'as:mediaType', '@value'),
|
||||
'name' => JsonLD::fetchElement($attachment, 'as:name', '@value'),
|
||||
'url' => JsonLD::fetchElement($attachment, 'as:url', '@id') ?? JsonLD::fetchElement($attachment, 'as:href', '@id'),
|
||||
'height' => JsonLD::fetchElement($attachment, 'as:height', '@value'),
|
||||
'width' => JsonLD::fetchElement($attachment, 'as:width', '@value'),
|
||||
'image' => JsonLD::fetchElement($attachment, 'as:image', '@id')
|
||||
'name' => JsonLD::fetchElement($attachment, 'as:name', '@value'),
|
||||
'url' => JsonLD::fetchElement($attachment, 'as:url', '@id') ?? JsonLD::fetchElement($attachment, 'as:href', '@id'),
|
||||
'height' => JsonLD::fetchElement($attachment, 'as:height', '@value'),
|
||||
'width' => JsonLD::fetchElement($attachment, 'as:width', '@value'),
|
||||
'image' => JsonLD::fetchElement($attachment, 'as:image', '@id')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1687,10 +1699,10 @@ class Receiver
|
|||
|
||||
if (!empty($object['as:oneOf'])) {
|
||||
$question['multiple'] = false;
|
||||
$options = JsonLD::fetchElementArray($object, 'as:oneOf') ?? [];
|
||||
$options = JsonLD::fetchElementArray($object, 'as:oneOf') ?? [];
|
||||
} elseif (!empty($object['as:anyOf'])) {
|
||||
$question['multiple'] = true;
|
||||
$options = JsonLD::fetchElementArray($object, 'as:anyOf') ?? [];
|
||||
$options = JsonLD::fetchElementArray($object, 'as:anyOf') ?? [];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
@ -1847,6 +1859,7 @@ class Receiver
|
|||
}
|
||||
|
||||
$size = (int)JsonLD::fetchElement($url, 'pt:size', '@value');
|
||||
|
||||
$attachments[] = ['type' => $filetype, 'mediaType' => $mediatype, 'url' => $href, 'height' => $height, 'size' => $size, 'name' => ''];
|
||||
} elseif (in_array($mediatype, ['application/x-bittorrent', 'application/x-bittorrent;x-scheme-handler/magnet'])) {
|
||||
$height = (int)JsonLD::fetchElement($url, 'as:height', '@value');
|
||||
|
@ -1909,7 +1922,8 @@ class Receiver
|
|||
return $object_data;
|
||||
}
|
||||
|
||||
private static function getCapabilities($object) {
|
||||
private static function getCapabilities($object)
|
||||
{
|
||||
$capabilities = [];
|
||||
foreach (['pixelfed:canAnnounce', 'pixelfed:canLike', 'pixelfed:canReply'] as $element) {
|
||||
$capabilities_list = JsonLD::fetchElementArray($object['pixelfed:capabilities'], $element, '@id');
|
||||
|
@ -1931,8 +1945,9 @@ class Receiver
|
|||
public static function getObjectDataFromActivity(array $object): array
|
||||
{
|
||||
$object_data = [];
|
||||
|
||||
$object_data['object_type'] = JsonLD::fetchElement($object, '@type');
|
||||
$object_data['id'] = JsonLD::fetchElement($object, '@id');
|
||||
$object_data['id'] = JsonLD::fetchElement($object, '@id');
|
||||
$object_data['reply-to-id'] = JsonLD::fetchElement($object, 'as:inReplyTo', '@id');
|
||||
|
||||
// An empty "id" field is translated to "./" by the compactor, so we have to check for this content
|
||||
|
@ -1956,7 +1971,7 @@ class Receiver
|
|||
}
|
||||
|
||||
$object_data['published'] = JsonLD::fetchElement($object, 'as:published', '@value');
|
||||
$object_data['updated'] = JsonLD::fetchElement($object, 'as:updated', '@value');
|
||||
$object_data['updated'] = JsonLD::fetchElement($object, 'as:updated', '@value');
|
||||
|
||||
if (empty($object_data['updated'])) {
|
||||
$object_data['updated'] = $object_data['published'];
|
||||
|
@ -1980,37 +1995,37 @@ class Receiver
|
|||
$location = BBCode::toPlaintext($location);
|
||||
}
|
||||
|
||||
$object_data['sc:identifier'] = JsonLD::fetchElement($object, 'sc:identifier', '@value');
|
||||
$object_data['diaspora:guid'] = JsonLD::fetchElement($object, 'diaspora:guid', '@value');
|
||||
$object_data['diaspora:comment'] = JsonLD::fetchElement($object, 'diaspora:comment', '@value');
|
||||
$object_data['diaspora:like'] = JsonLD::fetchElement($object, 'diaspora:like', '@value');
|
||||
$object_data['actor'] = $object_data['author'] = $actor;
|
||||
$element = JsonLD::fetchElement($object, 'as:context', '@id');
|
||||
$object_data['context'] = $element != './' ? $element : null;
|
||||
$element = JsonLD::fetchElement($object, 'ostatus:conversation', '@id');
|
||||
$object_data['conversation'] = $element != './' ? $element : null;
|
||||
$object_data['sensitive'] = JsonLD::fetchElement($object, 'as:sensitive');
|
||||
$object_data['name'] = JsonLD::fetchElement($object, 'as:name', '@value');
|
||||
$object_data['summary'] = JsonLD::fetchElement($object, 'as:summary', '@value');
|
||||
$object_data['content'] = JsonLD::fetchElement($object, 'as:content', '@value');
|
||||
$object_data['mediatype'] = JsonLD::fetchElement($object, 'as:mediaType', '@value');
|
||||
$object_data = self::getSource($object, $object_data);
|
||||
$object_data['start-time'] = JsonLD::fetchElement($object, 'as:startTime', '@value');
|
||||
$object_data['end-time'] = JsonLD::fetchElement($object, 'as:endTime', '@value');
|
||||
$object_data['location'] = $location;
|
||||
$object_data['latitude'] = JsonLD::fetchElement($object, 'as:location', 'as:latitude', '@type', 'as:Place');
|
||||
$object_data['latitude'] = JsonLD::fetchElement($object_data, 'latitude', '@value');
|
||||
$object_data['longitude'] = JsonLD::fetchElement($object, 'as:location', 'as:longitude', '@type', 'as:Place');
|
||||
$object_data['longitude'] = JsonLD::fetchElement($object_data, 'longitude', '@value');
|
||||
$object_data['attachments'] = self::processAttachments(JsonLD::fetchElementArray($object, 'as:attachment') ?? []);
|
||||
$object_data['tags'] = self::processTags(JsonLD::fetchElementArray($object, 'as:tag') ?? []);
|
||||
$object_data['emojis'] = self::processEmojis(JsonLD::fetchElementArray($object, 'as:tag', null, '@type', 'toot:Emoji') ?? []);
|
||||
$object_data['languages'] = self::processLanguages(JsonLD::fetchElementArray($object, 'sc:inLanguage') ?? []);
|
||||
$object_data['sc:identifier'] = JsonLD::fetchElement($object, 'sc:identifier', '@value');
|
||||
$object_data['diaspora:guid'] = JsonLD::fetchElement($object, 'diaspora:guid', '@value');
|
||||
$object_data['diaspora:comment'] = JsonLD::fetchElement($object, 'diaspora:comment', '@value');
|
||||
$object_data['diaspora:like'] = JsonLD::fetchElement($object, 'diaspora:like', '@value');
|
||||
$object_data['actor'] = $object_data['author'] = $actor;
|
||||
$element = JsonLD::fetchElement($object, 'as:context', '@id');
|
||||
$object_data['context'] = $element != './' ? $element : null;
|
||||
$element = JsonLD::fetchElement($object, 'ostatus:conversation', '@id');
|
||||
$object_data['conversation'] = $element != './' ? $element : null;
|
||||
$object_data['sensitive'] = JsonLD::fetchElement($object, 'as:sensitive');
|
||||
$object_data['name'] = JsonLD::fetchElement($object, 'as:name', '@value');
|
||||
$object_data['summary'] = JsonLD::fetchElement($object, 'as:summary', '@value');
|
||||
$object_data['content'] = JsonLD::fetchElement($object, 'as:content', '@value');
|
||||
$object_data['mediatype'] = JsonLD::fetchElement($object, 'as:mediaType', '@value');
|
||||
$object_data = self::getSource($object, $object_data);
|
||||
$object_data['start-time'] = JsonLD::fetchElement($object, 'as:startTime', '@value');
|
||||
$object_data['end-time'] = JsonLD::fetchElement($object, 'as:endTime', '@value');
|
||||
$object_data['location'] = $location;
|
||||
$object_data['latitude'] = JsonLD::fetchElement($object, 'as:location', 'as:latitude', '@type', 'as:Place');
|
||||
$object_data['latitude'] = JsonLD::fetchElement($object_data, 'latitude', '@value');
|
||||
$object_data['longitude'] = JsonLD::fetchElement($object, 'as:location', 'as:longitude', '@type', 'as:Place');
|
||||
$object_data['longitude'] = JsonLD::fetchElement($object_data, 'longitude', '@value');
|
||||
$object_data['attachments'] = self::processAttachments(JsonLD::fetchElementArray($object, 'as:attachment') ?? []);
|
||||
$object_data['tags'] = self::processTags(JsonLD::fetchElementArray($object, 'as:tag') ?? []);
|
||||
$object_data['emojis'] = self::processEmojis(JsonLD::fetchElementArray($object, 'as:tag', null, '@type', 'toot:Emoji') ?? []);
|
||||
$object_data['languages'] = self::processLanguages(JsonLD::fetchElementArray($object, 'sc:inLanguage') ?? []);
|
||||
$object_data['transmitted-languages'] = Processor::getPostLanguages($object);
|
||||
$object_data['generator'] = JsonLD::fetchElement($object, 'as:generator', 'as:name', '@type', 'as:Application');
|
||||
$object_data['generator'] = JsonLD::fetchElement($object_data, 'generator', '@value');
|
||||
$object_data['alternate-url'] = JsonLD::fetchElement($object, 'as:url', '@id');
|
||||
$object_data['replies'] = JsonLD::fetchElement($object, 'as:replies', '@id');
|
||||
$object_data['generator'] = JsonLD::fetchElement($object, 'as:generator', 'as:name', '@type', 'as:Application');
|
||||
$object_data['generator'] = JsonLD::fetchElement($object_data, 'generator', '@value');
|
||||
$object_data['alternate-url'] = JsonLD::fetchElement($object, 'as:url', '@id');
|
||||
$object_data['replies'] = JsonLD::fetchElement($object, 'as:replies', '@id');
|
||||
|
||||
// Special treatment for Hubzilla links
|
||||
if (is_array($object_data['alternate-url'])) {
|
||||
|
@ -2027,7 +2042,7 @@ class Receiver
|
|||
|
||||
if (in_array($object_data['object_type'], ['as:Audio', 'as:Video'])) {
|
||||
$object_data['alternate-url'] = self::extractAlternateUrl($object['as:url'] ?? []) ?: $object_data['alternate-url'];
|
||||
$object_data['attachments'] = array_merge($object_data['attachments'], self::processAttachmentUrls($object['as:url'] ?? []));
|
||||
$object_data['attachments'] = array_merge($object_data['attachments'], self::processAttachmentUrls($object['as:url'] ?? []));
|
||||
}
|
||||
|
||||
$object_data['can-comment'] = JsonLD::fetchElement($object, 'pt:commentsEnabled', '@value');
|
||||
|
@ -2037,9 +2052,15 @@ class Receiver
|
|||
|
||||
// Support for quoted posts (Pleroma, Fedibird and Misskey)
|
||||
$object_data['quote-url'] = JsonLD::fetchElement($object, 'as:quoteUrl', '@id');
|
||||
if (empty($object_data['quote-url'])) {
|
||||
$object_data['quote-url'] = JsonLD::fetchElement($object, 'as:quoteUrl', '@value');
|
||||
}
|
||||
if (empty($object_data['quote-url'])) {
|
||||
$object_data['quote-url'] = JsonLD::fetchElement($object, 'fedibird:quoteUri', '@id');
|
||||
}
|
||||
if (empty($object_data['quote-url'])) {
|
||||
$object_data['quote-url'] = JsonLD::fetchElement($object, 'fedibird:quoteUri', '@value');
|
||||
}
|
||||
if (empty($object_data['quote-url'])) {
|
||||
$object_data['quote-url'] = JsonLD::fetchElement($object, 'misskey:_misskey_quote', '@id');
|
||||
}
|
||||
|
|
|
@ -254,7 +254,7 @@ class Authentication
|
|||
$record = $this->dba->selectFirst(
|
||||
'user',
|
||||
[],
|
||||
['uid' => User::getIdFromPasswordAuthentication($username, $password)]
|
||||
['uid' => User::getIdFromPasswordAuthentication($username, $password, false, true)]
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$this->logger->warning('authenticate: failed login attempt', ['action' => 'login', 'username' => $username, 'ip' => $this->remoteAddress]);
|
||||
|
@ -262,6 +262,12 @@ class Authentication
|
|||
$this->baseUrl->redirect();
|
||||
}
|
||||
|
||||
if ($record['blocked']) {
|
||||
$this->logger->warning('authenticate: user is blocked', ['action' => 'login', 'username' => $username, 'ip' => $this->remoteAddress]);
|
||||
DI::sysmsg()->addNotice($this->l10n->t('Login failed because your account is blocked.'));
|
||||
$this->baseUrl->redirect();
|
||||
}
|
||||
|
||||
if (!$remember) {
|
||||
$trusted = $this->cookie->get('2fa_cookie_hash') ?? null;
|
||||
$this->cookie->clear();
|
||||
|
|
|
@ -189,7 +189,7 @@ class OAuth
|
|||
'created_at' => DateTimeFormat::utcNow()
|
||||
];
|
||||
|
||||
foreach ([BaseApi::SCOPE_READ, BaseApi::SCOPE_WRITE, BaseApi::SCOPE_WRITE, BaseApi::SCOPE_PUSH] as $scope) {
|
||||
foreach ([BaseApi::SCOPE_READ, BaseApi::SCOPE_WRITE, BaseApi::SCOPE_FOLLOW, BaseApi::SCOPE_PUSH] as $scope) {
|
||||
if ($fields[$scope] && !$application[$scope]) {
|
||||
Logger::warning('Requested token scope is not allowed for the application', ['token' => $fields, 'application' => $application]);
|
||||
}
|
||||
|
|
210
src/System/Daemon.php
Normal file
210
src/System/Daemon.php
Normal file
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
|
||||
// Copyright (C) 2010-2025, the Friendica project
|
||||
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
namespace Friendica\System;
|
||||
|
||||
use Friendica\Database\Database;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* class for direct interacting with the daemon commands
|
||||
*/
|
||||
final class Daemon
|
||||
{
|
||||
private LoggerInterface $logger;
|
||||
private Database $dba;
|
||||
private ?string $pidfile = null;
|
||||
private ?int $pid = null;
|
||||
|
||||
/**
|
||||
* The PID of the current daemon (null if either not set or not found)
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getPid(): ?int
|
||||
{
|
||||
return $this->pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to the PID file (null if not set)
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getPidfile(): ?string
|
||||
{
|
||||
return $this->pidfile;
|
||||
}
|
||||
|
||||
public function __construct(LoggerInterface $logger, Database $dba)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->dba = $dba;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the current daemon class with a given PID file
|
||||
*
|
||||
* @param string|null $pidfile the path to the PID file - using a given path if not directly set here
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init(string $pidfile = null): void
|
||||
{
|
||||
if (!empty($pidfile)) {
|
||||
$this->pid = null;
|
||||
$this->pidfile = $pidfile;
|
||||
}
|
||||
|
||||
if (!empty($this->pid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_readable($this->pidfile)) {
|
||||
$this->pid = intval(file_get_contents($this->pidfile));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the daemon
|
||||
*
|
||||
* @param callable $daemonLogic the business logic of the daemon
|
||||
* @param bool $foreground true, if started in foreground, otherwise spawned in the background
|
||||
*
|
||||
* @return bool true, if successfully started, otherwise false
|
||||
*/
|
||||
public function start(callable $daemonLogic, bool $foreground = false): bool
|
||||
{
|
||||
$this->init();
|
||||
|
||||
if (!empty($this->pid)) {
|
||||
$this->logger->notice('process is already running', ['pid' => $this->pid, 'pidfile' => $this->pidfile]);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->logger->notice('starting daemon', ['pid' => $this->pid, 'pidfile' => $this->pidfile]);
|
||||
|
||||
if (!$foreground) {
|
||||
$this->dba->disconnect();
|
||||
|
||||
// fork a daemon process
|
||||
$this->pid = pcntl_fork();
|
||||
if ($this->pid < 0) {
|
||||
$this->logger->warning('Could not fork daemon');
|
||||
return false;
|
||||
} elseif ($this->pid) {
|
||||
// The parent process continues here
|
||||
if (!file_put_contents($this->pidfile, $this->pid)) {
|
||||
$this->logger->warning('Could not store pid file', ['pid' => $this->pid, 'pidfile' => $this->pidfile]);
|
||||
posix_kill($this->pid, SIGTERM);
|
||||
return false;
|
||||
}
|
||||
$this->logger->notice('Child process started', ['pid' => $this->pid, 'pidfile' => $this->pidfile]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// We now are in the child process
|
||||
register_shutdown_function(function (): void {
|
||||
posix_kill(posix_getpid(), SIGTERM);
|
||||
posix_kill(posix_getpid(), SIGHUP);
|
||||
});
|
||||
|
||||
// Make the child the main process, detach it from the terminal
|
||||
if (posix_setsid() < 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Closing all existing connections with the outside
|
||||
fclose(STDIN);
|
||||
|
||||
// And now connect the database again
|
||||
$this->dba->connect();
|
||||
}
|
||||
|
||||
// Just to be sure that this script really runs endlessly
|
||||
set_time_limit(0);
|
||||
|
||||
$daemonLogic();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks, if the current daemon is running
|
||||
*
|
||||
* @return bool true, if the daemon is running, otherwise false (f.e no PID found, no PID file found, PID is not bound to a running process))
|
||||
*/
|
||||
public function isRunning(): bool
|
||||
{
|
||||
$this->init();
|
||||
|
||||
if (empty($this->pid)) {
|
||||
$this->logger->notice("Pid wasn't found");
|
||||
|
||||
if (is_readable($this->pidfile)) {
|
||||
unlink($this->pidfile);
|
||||
$this->logger->notice("Pidfile removed", ['pidfile' => $this->pidfile]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (posix_kill($this->pid, 0)) {
|
||||
$this->logger->notice("daemon process is running");
|
||||
return true;
|
||||
} else {
|
||||
unlink($this->pidfile);
|
||||
$this->logger->notice("daemon process isn't running");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the daemon, if running
|
||||
*
|
||||
* @return bool true, if the daemon was successfully stopped or is already stopped, otherwise false
|
||||
*/
|
||||
public function stop(): bool
|
||||
{
|
||||
$this->init();
|
||||
|
||||
if (empty($this->pid)) {
|
||||
$this->logger->notice("Pidfile wasn't found", ['pidfile' => $this->pidfile]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!posix_kill($this->pid, SIGTERM)) {
|
||||
$this->logger->warning("Cannot kill the given PID", ['pid' => $this->pid, 'pidfile' => $this->pidfile]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!unlink($this->pidfile)) {
|
||||
$this->logger->warning("Cannot delete the given PID file", ['pid' => $this->pid, 'pidfile' => $this->pidfile]);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->logger->notice('daemon process was killed', ['pid' => $this->pid, 'pidfile' => $this->pidfile]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current daemon to sleep and checks the status afterward
|
||||
*
|
||||
* @param int $duration the duration of time for sleeping (in milliseconds)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sleep(int $duration)
|
||||
{
|
||||
usleep($duration);
|
||||
|
||||
$this->pid = pcntl_waitpid(-1, $status, WNOHANG);
|
||||
if ($this->pid > 0) {
|
||||
$this->logger->info('Children quit via pcntl_waitpid', ['pid' => $this->pid, 'status' => $status]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -227,6 +227,11 @@ class JsonLD
|
|||
Logger::debug('schema.org path fixed');
|
||||
$value = 'http://schema.org#';
|
||||
}
|
||||
// Issue 14630: Wordpress Event Bridge uses a URL that cannot be retrieved
|
||||
if (is_int($key) && $value == 'https://schema.org/') {
|
||||
Logger::debug('https schema.org path fixed');
|
||||
$value = 'https://schema.org/docs/jsonldcontext.json#';
|
||||
}
|
||||
});
|
||||
|
||||
// Bookwyrm transmits "id" fields with "null", which isn't allowed.
|
||||
|
|
|
@ -219,7 +219,6 @@ return (function(string $basepath, array $getVars, array $serverVars, array $coo
|
|||
'constructParams' => [
|
||||
$serverVars,
|
||||
__DIR__ . '/routes.config.php',
|
||||
[Dice::INSTANCE => Dice::SELF],
|
||||
null
|
||||
],
|
||||
],
|
||||
|
|
|
@ -5,22 +5,22 @@
|
|||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
declare(strict_types = 1);
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Friendica\Test\Unit;
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Container;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AppTest extends TestCase
|
||||
{
|
||||
public function testFromDiceReturnsApp(): void
|
||||
public function testFromContainerReturnsApp(): void
|
||||
{
|
||||
$dice = $this->createMock(Dice::class);
|
||||
$dice->expects($this->never())->method('create');
|
||||
$container = $this->createMock(Container::class);
|
||||
$container->expects($this->never())->method('create');
|
||||
|
||||
$app = App::fromDice($dice);
|
||||
$app = App::fromContainer($container);
|
||||
|
||||
$this->assertInstanceOf(App::class, $app);
|
||||
}
|
||||
|
|
48
tests/Unit/Core/ContainerTest.php
Normal file
48
tests/Unit/Core/ContainerTest.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
// Copyright (C) 2010-2024, the Friendica project
|
||||
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core;
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\Core\Container;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
class ContainerTest extends TestCase
|
||||
{
|
||||
public function testFromDiceReturnsContainer(): void
|
||||
{
|
||||
$dice = $this->createMock(Dice::class);
|
||||
$dice->expects($this->never())->method('create');
|
||||
|
||||
$container = Container::fromDice($dice);
|
||||
|
||||
$this->assertInstanceOf(Container::class, $container);
|
||||
}
|
||||
|
||||
public function testCreateFromContainer(): void
|
||||
{
|
||||
$dice = $this->createMock(Dice::class);
|
||||
$dice->expects($this->once())->method('create')->with(LoggerInterface::class)->willReturn(new NullLogger());
|
||||
|
||||
$container = Container::fromDice($dice);
|
||||
|
||||
$this->assertInstanceOf(NullLogger::class, $container->create(LoggerInterface::class));
|
||||
}
|
||||
|
||||
public function testAddRuleFromContainer(): void
|
||||
{
|
||||
$dice = $this->createMock(Dice::class);
|
||||
$dice->expects($this->once())->method('addRule')->with(LoggerInterface::class, ['constructParams' => ['console']])->willReturn($dice);
|
||||
|
||||
$container = Container::fromDice($dice);
|
||||
$container->addRule(LoggerInterface::class, ['constructParams' => ['console']]);
|
||||
}
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
# FRIENDICA Distributed Social Network
|
||||
# Copyright (C) 2010-2024, the Friendica project
|
||||
# Copyright (C) 2010-2025, the Friendica project
|
||||
# This file is distributed under the same license as the Friendica package.
|
||||
# Mike Macgirvin, 2010
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 2024.12-dev\n"
|
||||
"Project-Id-Version: 2025.02-dev\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-12-23 10:09+0000\n"
|
||||
"POT-Creation-Date: 2025-01-04 20:55-0500\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -646,7 +646,7 @@ msgstr ""
|
|||
msgid "Map"
|
||||
msgstr ""
|
||||
|
||||
#: src/App.php:255
|
||||
#: src/App.php:394
|
||||
msgid "Apologies but the website is unavailable at the moment."
|
||||
msgstr ""
|
||||
|
||||
|
@ -747,17 +747,17 @@ msgstr ""
|
|||
msgid "toggle mobile"
|
||||
msgstr ""
|
||||
|
||||
#: src/App/Router.php:295
|
||||
#: src/App/Router.php:286
|
||||
#, php-format
|
||||
msgid "Method not allowed for this module. Allowed method(s): %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/App/Router.php:297 src/Module/HTTPException/PageNotFound.php:35
|
||||
#: src/App/Router.php:288 src/Module/HTTPException/PageNotFound.php:35
|
||||
#: src/Module/Stats.php:56
|
||||
msgid "Page not found."
|
||||
msgstr ""
|
||||
|
||||
#: src/App/Router.php:309
|
||||
#: src/App/Router.php:300
|
||||
msgid "You must be logged in to use addons. "
|
||||
msgstr ""
|
||||
|
||||
|
@ -765,15 +765,15 @@ msgstr ""
|
|||
msgid "No system theme config value set."
|
||||
msgstr ""
|
||||
|
||||
#: src/BaseModule.php:393
|
||||
#: src/BaseModule.php:395
|
||||
msgid "The form security token was not correct. This probably happened because the form has been opened for too long (>3 hours) before submitting it."
|
||||
msgstr ""
|
||||
|
||||
#: src/BaseModule.php:420
|
||||
#: src/BaseModule.php:422
|
||||
msgid "All contacts"
|
||||
msgstr ""
|
||||
|
||||
#: src/BaseModule.php:425 src/Content/Conversation/Factory/Channel.php:32
|
||||
#: src/BaseModule.php:427 src/Content/Conversation/Factory/Channel.php:32
|
||||
#: src/Content/Widget.php:254 src/Core/ACL.php:182 src/Module/Contact.php:395
|
||||
#: src/Module/Privacy/PermissionTooltip.php:150
|
||||
#: src/Module/Privacy/PermissionTooltip.php:172
|
||||
|
@ -781,16 +781,16 @@ msgstr ""
|
|||
msgid "Followers"
|
||||
msgstr ""
|
||||
|
||||
#: src/BaseModule.php:430 src/Content/Widget.php:255 src/Module/Contact.php:398
|
||||
#: src/BaseModule.php:432 src/Content/Widget.php:255 src/Module/Contact.php:398
|
||||
#: src/Module/Settings/Channels.php:145
|
||||
msgid "Following"
|
||||
msgstr ""
|
||||
|
||||
#: src/BaseModule.php:435 src/Content/Widget.php:256 src/Module/Contact.php:401
|
||||
#: src/BaseModule.php:437 src/Content/Widget.php:256 src/Module/Contact.php:401
|
||||
msgid "Mutual friends"
|
||||
msgstr ""
|
||||
|
||||
#: src/BaseModule.php:443
|
||||
#: src/BaseModule.php:445
|
||||
msgid "Common"
|
||||
msgstr ""
|
||||
|
||||
|
@ -944,7 +944,7 @@ msgstr ""
|
|||
msgid "Enter user nickname: "
|
||||
msgstr ""
|
||||
|
||||
#: src/Console/User.php:168 src/Model/User.php:830
|
||||
#: src/Console/User.php:168 src/Model/User.php:834
|
||||
#: src/Module/Api/Twitter/ContactEndpoint.php:62
|
||||
#: src/Module/Moderation/Users/Active.php:55
|
||||
#: src/Module/Moderation/Users/Active.php:61
|
||||
|
@ -1711,7 +1711,7 @@ msgstr ""
|
|||
|
||||
#: src/Content/Feature.php:116 src/Content/GroupManager.php:133
|
||||
#: src/Content/Nav.php:264 src/Content/Text/HTML.php:868
|
||||
#: src/Content/Widget.php:555 src/Model/User.php:1390
|
||||
#: src/Content/Widget.php:555 src/Model/User.php:1394
|
||||
msgid "Groups"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1835,7 +1835,7 @@ msgstr ""
|
|||
msgid "Create new group"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Item.php:322 src/Model/Item.php:3281
|
||||
#: src/Content/Item.php:322 src/Model/Item.php:3297
|
||||
msgid "event"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1843,7 +1843,7 @@ msgstr ""
|
|||
msgid "status"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Item.php:331 src/Model/Item.php:3283
|
||||
#: src/Content/Item.php:331 src/Model/Item.php:3299
|
||||
#: src/Module/Post/Tag/Add.php:109
|
||||
msgid "photo"
|
||||
msgstr ""
|
||||
|
@ -2040,7 +2040,7 @@ msgid "Create an account"
|
|||
msgstr ""
|
||||
|
||||
#: src/Content/Nav.php:247 src/Module/Help.php:53
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:119
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:118
|
||||
#: src/Module/Settings/TwoFactor/Index.php:125
|
||||
#: src/Module/Settings/TwoFactor/Recovery.php:96
|
||||
#: src/Module/Settings/TwoFactor/Verify.php:135 view/theme/vier/theme.php:228
|
||||
|
@ -2245,8 +2245,8 @@ msgstr ""
|
|||
msgid "<a href=\"%1$s\" target=\"_blank\" rel=\"noopener noreferrer\">%2$s</a> %3$s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Text/BBCode.php:926 src/Model/Item.php:4080
|
||||
#: src/Model/Item.php:4086 src/Model/Item.php:4087
|
||||
#: src/Content/Text/BBCode.php:926 src/Model/Item.php:4096
|
||||
#: src/Model/Item.php:4102 src/Model/Item.php:4103
|
||||
msgid "Link to source"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2834,7 +2834,7 @@ msgstr ""
|
|||
msgid "Could not connect to database."
|
||||
msgstr ""
|
||||
|
||||
#: src/Core/L10n.php:426 src/Model/Item.php:2324
|
||||
#: src/Core/L10n.php:426 src/Model/Item.php:2340
|
||||
msgid "Undetermined"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3376,92 +3376,92 @@ msgstr ""
|
|||
msgid "Happy Birthday %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:2331
|
||||
#: src/Model/Item.php:2347
|
||||
#, php-format
|
||||
msgid "%s (%s - %s): %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:2333
|
||||
#: src/Model/Item.php:2349
|
||||
#, php-format
|
||||
msgid "%s (%s): %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:2336
|
||||
#: src/Model/Item.php:2352
|
||||
#, php-format
|
||||
msgid ""
|
||||
"Detected languages in this post:\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:3285
|
||||
#: src/Model/Item.php:3301
|
||||
msgid "activity"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:3287
|
||||
#: src/Model/Item.php:3303
|
||||
msgid "comment"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:3290 src/Module/Post/Tag/Add.php:109
|
||||
#: src/Model/Item.php:3306 src/Module/Post/Tag/Add.php:109
|
||||
msgid "post"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:3463
|
||||
#: src/Model/Item.php:3479
|
||||
#, php-format
|
||||
msgid "%s is blocked"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:3465
|
||||
#: src/Model/Item.php:3481
|
||||
#, php-format
|
||||
msgid "%s is ignored"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:3467
|
||||
#: src/Model/Item.php:3483
|
||||
#, php-format
|
||||
msgid "Content from %s is collapsed"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:3471
|
||||
#: src/Model/Item.php:3487
|
||||
msgid "Sensitive content"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:3980
|
||||
#: src/Model/Item.php:3996
|
||||
msgid "bytes"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:4011
|
||||
#: src/Model/Item.php:4027
|
||||
#, php-format
|
||||
msgid "%2$s (%3$d%%, %1$d vote)"
|
||||
msgid_plural "%2$s (%3$d%%, %1$d votes)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/Model/Item.php:4013
|
||||
#: src/Model/Item.php:4029
|
||||
#, php-format
|
||||
msgid "%2$s (%1$d vote)"
|
||||
msgid_plural "%2$s (%1$d votes)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/Model/Item.php:4018
|
||||
#: src/Model/Item.php:4034
|
||||
#, php-format
|
||||
msgid "%d voter. Poll end: %s"
|
||||
msgid_plural "%d voters. Poll end: %s"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/Model/Item.php:4020
|
||||
#: src/Model/Item.php:4036
|
||||
#, php-format
|
||||
msgid "%d voter."
|
||||
msgid_plural "%d voters."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/Model/Item.php:4022
|
||||
#: src/Model/Item.php:4038
|
||||
#, php-format
|
||||
msgid "Poll end: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Item.php:4063 src/Model/Item.php:4064
|
||||
#: src/Model/Item.php:4079 src/Model/Item.php:4080
|
||||
msgid "View on separate page"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3619,138 +3619,138 @@ msgstr ""
|
|||
msgid "Responsible account: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:217 src/Model/User.php:1310
|
||||
#: src/Model/User.php:217 src/Model/User.php:1314
|
||||
msgid "SERIOUS ERROR: Generation of security keys failed."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:739 src/Model/User.php:772
|
||||
#: src/Model/User.php:740 src/Model/User.php:773
|
||||
msgid "Login failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:804
|
||||
#: src/Model/User.php:806
|
||||
msgid "Not enough information to authenticate"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:929
|
||||
#: src/Model/User.php:933
|
||||
msgid "Password can't be empty"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:971
|
||||
#: src/Model/User.php:975
|
||||
msgid "Empty passwords are not allowed."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:975
|
||||
#: src/Model/User.php:979
|
||||
msgid "The new password has been exposed in a public data dump, please choose another."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:979
|
||||
#: src/Model/User.php:983
|
||||
msgid "The password length is limited to 72 characters."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:983
|
||||
#: src/Model/User.php:987
|
||||
msgid "The password can't contain white spaces nor accentuated letters"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1192
|
||||
#: src/Model/User.php:1196
|
||||
msgid "Passwords do not match. Password unchanged."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1199
|
||||
#: src/Model/User.php:1203
|
||||
msgid "An invitation is required."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1203
|
||||
#: src/Model/User.php:1207
|
||||
msgid "Invitation could not be verified."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1211
|
||||
#: src/Model/User.php:1215
|
||||
msgid "Invalid OpenID url"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1225 src/Security/Authentication.php:231
|
||||
#: src/Model/User.php:1229 src/Security/Authentication.php:231
|
||||
msgid "We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1225 src/Security/Authentication.php:231
|
||||
#: src/Model/User.php:1229 src/Security/Authentication.php:231
|
||||
msgid "The error message was:"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1231
|
||||
#: src/Model/User.php:1235
|
||||
msgid "Please enter the required information."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1245
|
||||
#: src/Model/User.php:1249
|
||||
#, php-format
|
||||
msgid "system.username_min_length (%s) and system.username_max_length (%s) are excluding each other, swapping values."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1252
|
||||
#: src/Model/User.php:1256
|
||||
#, php-format
|
||||
msgid "Username should be at least %s character."
|
||||
msgid_plural "Username should be at least %s characters."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/Model/User.php:1256
|
||||
#: src/Model/User.php:1260
|
||||
#, php-format
|
||||
msgid "Username should be at most %s character."
|
||||
msgid_plural "Username should be at most %s characters."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: src/Model/User.php:1264
|
||||
#: src/Model/User.php:1268
|
||||
msgid "That doesn't appear to be your full (First Last) name."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1269
|
||||
#: src/Model/User.php:1273
|
||||
msgid "Your email domain is not among those allowed on this site."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1273
|
||||
#: src/Model/User.php:1277
|
||||
msgid "Not a valid email address."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1276
|
||||
#: src/Model/User.php:1280
|
||||
msgid "The nickname was blocked from registration by the nodes admin."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1280 src/Model/User.php:1286
|
||||
#: src/Model/User.php:1284 src/Model/User.php:1290
|
||||
msgid "Cannot use that email."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1292
|
||||
#: src/Model/User.php:1296
|
||||
msgid "Your nickname can only contain a-z, 0-9 and _."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1300 src/Model/User.php:1350
|
||||
#: src/Model/User.php:1304 src/Model/User.php:1354
|
||||
msgid "Nickname is already registered. Please choose another."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1337 src/Model/User.php:1341
|
||||
#: src/Model/User.php:1341 src/Model/User.php:1345
|
||||
msgid "An error occurred during registration. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1364
|
||||
#: src/Model/User.php:1368
|
||||
msgid "An error occurred creating your default profile. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1371
|
||||
#: src/Model/User.php:1375
|
||||
msgid "An error occurred creating your self contact. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1376
|
||||
#: src/Model/User.php:1380
|
||||
msgid "Friends"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1380
|
||||
#: src/Model/User.php:1384
|
||||
msgid "An error occurred creating your default contact circle. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1428
|
||||
#: src/Model/User.php:1432
|
||||
msgid "Profile Photos"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1616
|
||||
#: src/Model/User.php:1620
|
||||
#, php-format
|
||||
msgid ""
|
||||
"\n"
|
||||
|
@ -3758,7 +3758,7 @@ msgid ""
|
|||
"\t\t\tthe administrator of %2$s has set up an account for you."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1619
|
||||
#: src/Model/User.php:1623
|
||||
#, php-format
|
||||
msgid ""
|
||||
"\n"
|
||||
|
@ -3789,12 +3789,12 @@ msgid ""
|
|||
"\t\tThank you and welcome to %4$s."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1651 src/Model/User.php:1757
|
||||
#: src/Model/User.php:1655 src/Model/User.php:1761
|
||||
#, php-format
|
||||
msgid "Registration details for %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1671
|
||||
#: src/Model/User.php:1675
|
||||
#, php-format
|
||||
msgid ""
|
||||
"\n"
|
||||
|
@ -3809,12 +3809,12 @@ msgid ""
|
|||
"\t\t"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1690
|
||||
#: src/Model/User.php:1694
|
||||
#, php-format
|
||||
msgid "Registration at %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1714
|
||||
#: src/Model/User.php:1718
|
||||
#, php-format
|
||||
msgid ""
|
||||
"\n"
|
||||
|
@ -3823,7 +3823,7 @@ msgid ""
|
|||
"\t\t\t"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1722
|
||||
#: src/Model/User.php:1726
|
||||
#, php-format
|
||||
msgid ""
|
||||
"\n"
|
||||
|
@ -3854,7 +3854,7 @@ msgid ""
|
|||
"\t\t\tThank you and welcome to %2$s."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1784
|
||||
#: src/Model/User.php:1788
|
||||
msgid "User with delegates can't be removed, please remove delegate users first"
|
||||
msgstr ""
|
||||
|
||||
|
@ -9544,7 +9544,7 @@ msgstr ""
|
|||
|
||||
#: src/Module/Settings/Channels.php:177 src/Module/Settings/Channels.php:198
|
||||
#: src/Module/Settings/Display.php:350
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:124
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:123
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
|
@ -10446,55 +10446,54 @@ msgid "App-specific password generation failed: This description already exists.
|
|||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:80
|
||||
#, php-format
|
||||
msgid "New app-specific password generated: %s"
|
||||
msgid "New app-specific password generated."
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:87
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:86
|
||||
msgid "App-specific passwords successfully revoked."
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:97
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:96
|
||||
msgid "App-specific password successfully revoked."
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:118
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:117
|
||||
msgid "Two-factor app-specific passwords"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:120
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:119
|
||||
msgid "<p>App-specific passwords are randomly generated passwords used instead your regular password to authenticate your account on third-party applications that don't support two-factor authentication.</p>"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:121
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:120
|
||||
msgid "Make sure to copy your new app-specific password now. You won’t be able to see it again!"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:125
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:124
|
||||
msgid "Last Used"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:126
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:125
|
||||
msgid "Revoke"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:127
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:126
|
||||
msgid "Revoke All"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:130
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:129
|
||||
msgid "When you generate a new app-specific password, you must use it right away, it will be shown to you once after you generate it."
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:131
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:130
|
||||
msgid "Generate new app-specific password"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:132
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:131
|
||||
msgid "Friendiqa on my Fairphone 2..."
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:133
|
||||
#: src/Module/Settings/TwoFactor/AppSpecific.php:132
|
||||
msgid "Generate"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11750,12 +11749,16 @@ msgstr ""
|
|||
msgid "Login failed. Please check your credentials."
|
||||
msgstr ""
|
||||
|
||||
#: src/Security/Authentication.php:374
|
||||
#: src/Security/Authentication.php:267
|
||||
msgid "Login failed because your account is blocked."
|
||||
msgstr ""
|
||||
|
||||
#: src/Security/Authentication.php:380
|
||||
#, php-format
|
||||
msgid "Welcome %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Security/Authentication.php:375
|
||||
#: src/Security/Authentication.php:381
|
||||
msgid "Please upload a profile photo."
|
||||
msgstr ""
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1106,6 +1106,7 @@ $a->strings['Display'] = 'العرض';
|
|||
$a->strings['Social Networks'] = 'الشبكات الاجتماعية';
|
||||
$a->strings['Manage Accounts'] = 'إدارة الحسابات';
|
||||
$a->strings['Connected apps'] = 'التطبيقات المتصلة';
|
||||
$a->strings['Import Contacts'] = 'استيراد متراسلين';
|
||||
$a->strings['Export personal data'] = 'تصدير البيانات الشخصية';
|
||||
$a->strings['Remove account'] = 'أزل الحساب';
|
||||
$a->strings['This page is missing a url parameter.'] = 'هذه الصفحة تفتقد معطى للرابط.';
|
||||
|
@ -1768,8 +1769,6 @@ $a->strings['Wrong Password.'] = 'كلمة المرور خاطئة.';
|
|||
$a->strings['Invalid email.'] = 'البريد الإلكتروني غير صالح.';
|
||||
$a->strings['Cannot change to that email.'] = 'لا يمكن التغيير إلى هذا البريد الإلكتروني.';
|
||||
$a->strings['Settings were not updated.'] = 'لم تُحدث الإعدادات.';
|
||||
$a->strings['Contact CSV file upload error'] = 'خطأ أثناء رفع ملف CSV';
|
||||
$a->strings['Importing Contacts done'] = 'أُستورد المتراسلون';
|
||||
$a->strings['Relocate message has been send to your contacts'] = 'أُرسلت رسالة تنبيه بانتقالك إلى متراسليك';
|
||||
$a->strings['Unable to find your profile. Please contact your admin.'] = 'تعذر العثور على ملفك الشخصي. من فضلك اتصال بالمدير.';
|
||||
$a->strings['Personal Page Subtypes'] = 'الأنواع الفرعية للصفحة الشخصية';
|
||||
|
@ -1851,9 +1850,6 @@ $a->strings['Show notifications of ignored contacts'] = 'أظهر تنبيهات
|
|||
$a->strings['You don\'t see posts from ignored contacts. But you still see their comments. This setting controls if you want to still receive regular notifications that are caused by ignored contacts or not.'] = 'أنت لا ترى مشاركات المتراسلين المتجاهلين. لكن لا يزال بإمكانك رؤية تعليقاتهم. هذا الإعداد يتحكم إذا كنت ترغب في الاستمرار في تلقي تنبيهات سببها المتراسلون المتجاهلون.';
|
||||
$a->strings['Advanced Account/Page Type Settings'] = 'الإعدادات المتقدمة للحساب/للصفحة';
|
||||
$a->strings['Change the behaviour of this account for special situations'] = 'غيّر سلوك هذا الحساب للحالات الخاصة';
|
||||
$a->strings['Import Contacts'] = 'استيراد متراسلين';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'ارفع ملف CSV معرفات المتراسلين لحسابك القديم، معرفات المتابَعين تكون في العمود الأول.';
|
||||
$a->strings['Upload File'] = 'ارفع ملفًا';
|
||||
$a->strings['Relocate'] = 'الانتقال';
|
||||
$a->strings['If you have moved this profile from another server, and some of your contacts don\'t receive your updates, try pushing this button.'] = 'إذا كنت قد نقلت هذا الملف الشخصي من خادم آخر، وبعض المتراسلين لا يتلقون تحديثاتك، أنقر هذا الزر.';
|
||||
$a->strings['Resend relocate message to contacts'] = 'أعد إرسال رسالة الانتقال للمتراسلين';
|
||||
|
@ -1875,8 +1871,6 @@ $a->strings['Normally the system tries to find the best link to add to shortened
|
|||
$a->strings['Enable simple text shortening'] = 'فعّل اختصار النصوص';
|
||||
$a->strings['Attach the link title'] = 'أرفق عنوان الرابط';
|
||||
$a->strings['When activated, the title of the attached link will be added as a title on posts to Diaspora. This is mostly helpful with "remote-self" contacts that share feed content.'] = 'عند تفعيله سيتم إرفاق عنوان الصفحة بمنشور دياسبورا. هذا مفيد بشكل أساسي مع المتراسلين "الذاتيين" الذين يشاركون تغذيات Rss / Atom.';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'حساب GNU Social\ActivityPub القديم';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'إذا قمت بإدخال اسم حساب ActivityPub/GNU Social/Statusnet القديم هنا (بنسق user@domain.tld)، سيضاف المتراسلون في هذا الحساب تلقائيا. سيصفر الحقل عند الانتهاء.';
|
||||
$a->strings['Email/Mailbox Setup'] = 'إعداد بريد الكتروني/صندوق بريد';
|
||||
$a->strings['If you wish to communicate with email contacts using this service (optional), please specify how to connect to your mailbox.'] = 'إذا كنت ترغب في التواصل مع متراسلي البريد الإلكتروني باستخدام هذه الخدمة (اختيارية)، من فضلك حدد كيفية الاتصال بصندوق بريدك.';
|
||||
$a->strings['Last successful email check:'] = 'آخر تحقق ناجح للبريد الإلكتروني:';
|
||||
|
@ -1890,6 +1884,12 @@ $a->strings['Send public posts to all email contacts:'] = 'أرسل المشار
|
|||
$a->strings['Action after import:'] = 'الإجراء بعد الاستيراد:';
|
||||
$a->strings['Move to folder'] = 'انقل إلى مجلد';
|
||||
$a->strings['Move to folder:'] = 'انقل إلى المجلد:';
|
||||
$a->strings['Contact CSV file upload error'] = 'خطأ أثناء رفع ملف CSV';
|
||||
$a->strings['Importing Contacts done'] = 'أُستورد المتراسلون';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'ارفع ملف CSV معرفات المتراسلين لحسابك القديم، معرفات المتابَعين تكون في العمود الأول.';
|
||||
$a->strings['Upload File'] = 'ارفع ملفًا';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'حساب GNU Social\ActivityPub القديم';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'إذا قمت بإدخال اسم حساب ActivityPub/GNU Social/Statusnet القديم هنا (بنسق user@domain.tld)، سيضاف المتراسلون في هذا الحساب تلقائيا. سيصفر الحقل عند الانتهاء.';
|
||||
$a->strings['Delegation successfully granted.'] = 'منح التفويض بنجاح.';
|
||||
$a->strings['Parent user not found, unavailable or password doesn\'t match.'] = 'لم يُعثر على الولي أو هو غير متوفر أو كلمة مرور غير صحيحة.';
|
||||
$a->strings['Delegation successfully revoked.'] = 'نجح إبطال التفويض.';
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1606,6 +1606,7 @@ $a->strings['Social Networks'] = 'Soziale Netzwerke';
|
|||
$a->strings['Manage Accounts'] = 'Accounts Verwalten';
|
||||
$a->strings['Connected apps'] = 'Verbundene Programme';
|
||||
$a->strings['Remote servers'] = 'Remote Instanzen';
|
||||
$a->strings['Import Contacts'] = 'Kontakte Importieren';
|
||||
$a->strings['Export personal data'] = 'Persönliche Daten exportieren';
|
||||
$a->strings['Remove account'] = 'Konto löschen';
|
||||
$a->strings['This page is missing a url parameter.'] = 'Der Seite fehlt ein URL Parameter.';
|
||||
|
@ -2439,8 +2440,6 @@ $a->strings['Wrong Password.'] = 'Falsches Passwort';
|
|||
$a->strings['Invalid email.'] = 'Ungültige E-Mail-Adresse.';
|
||||
$a->strings['Cannot change to that email.'] = 'Ändern der E-Mail nicht möglich. ';
|
||||
$a->strings['Settings were not updated.'] = 'Einstellungen nicht aktualisiert';
|
||||
$a->strings['Contact CSV file upload error'] = 'Fehler beim Hochladen der Kontakt CSV Datei';
|
||||
$a->strings['Importing Contacts done'] = 'Kontakte wurden importiert.';
|
||||
$a->strings['Relocate message has been send to your contacts'] = 'Die Umzugsbenachrichtigung wurde an Deine Kontakte versendet.';
|
||||
$a->strings['Unable to find your profile. Please contact your admin.'] = 'Konnte dein Profil nicht finden. Bitte kontaktiere den Admin.';
|
||||
$a->strings['Account for a service that automatically shares content based on user defined channels.'] = 'Konto für einen Dienst, der automatisch Inhalte basierend auf vom Benutzer definierten Kanälen teilt.';
|
||||
|
@ -2536,9 +2535,6 @@ $a->strings['Show notifications of ignored contacts'] = 'Zeige Benachrichtigunge
|
|||
$a->strings['You don\'t see posts from ignored contacts. But you still see their comments. This setting controls if you want to still receive regular notifications that are caused by ignored contacts or not.'] = 'Beiträge von ignorierten Kontakten werden dir nicht angezeigt. Aber du siehst immer noch ihre Kommentare. Diese Einstellung legt fest, ob du dazu weiterhin Benachrichtigungen erhalten willst oder ob diese einfach verworfen werden sollen.';
|
||||
$a->strings['Advanced Account/Page Type Settings'] = 'Erweiterte Konto-/Seitentyp-Einstellungen';
|
||||
$a->strings['Change the behaviour of this account for special situations'] = 'Verhalten dieses Kontos in bestimmten Situationen:';
|
||||
$a->strings['Import Contacts'] = 'Kontakte Importieren';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Lade eine CSV Datei hoch, die das Handle der Kontakte deines alten Nutzerkontos in der ersten Spalte enthält.';
|
||||
$a->strings['Upload File'] = 'Datei hochladen';
|
||||
$a->strings['Relocate'] = 'Umziehen';
|
||||
$a->strings['If you have moved this profile from another server, and some of your contacts don\'t receive your updates, try pushing this button.'] = 'Wenn du dein Profil von einem anderen Server umgezogen hast und einige deiner Kontakte deine Beiträge nicht erhalten, verwende diesen Button.';
|
||||
$a->strings['Resend relocate message to contacts'] = 'Umzugsbenachrichtigung erneut an Kontakte senden';
|
||||
|
@ -2606,8 +2602,6 @@ $a->strings['API: Automatically links at the end of the post as attached posts']
|
|||
$a->strings['When activated, added links at the end of the post react the same way as added links in the web interface.'] = 'Wenn dies aktiviert ist, reagieren hinzugefügte Links am Ende des Beitrags genauso wie hinzugefügte Links in der Weboberfläche.';
|
||||
$a->strings['Article Mode'] = 'Artikel Modus';
|
||||
$a->strings['Controls how posts with titles are transmitted. Mastodon and its forks don\'t display the content of these posts if the post is created in the correct (default) way.'] = 'Kontrolliert wie Beiträge mit Titeln übermittel werden. Mastodon und dessen Forks stellen den Inhalt dieser Beiträge nicht dar, wenn sie an sich korrekt in den Grundeinstellungen übertragen werden.';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Dein alter ActivityPub/GNU Social-Account';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Wenn du deinen alten ActivityPub oder GNU Social/Statusnet-Account-Namen hier angibst (Format name@domain.tld), werden deine Kontakte automatisch hinzugefügt. Dieses Feld wird geleert, wenn die Kontakte hinzugefügt wurden.';
|
||||
$a->strings['Email/Mailbox Setup'] = 'E-Mail/Postfach-Einstellungen';
|
||||
$a->strings['If you wish to communicate with email contacts using this service (optional), please specify how to connect to your mailbox.'] = 'Wenn du mit E-Mail-Kontakten über diesen Service kommunizieren möchtest (optional), gib bitte die Einstellungen für dein Postfach an.';
|
||||
$a->strings['Last successful email check:'] = 'Letzter erfolgreicher E-Mail-Check';
|
||||
|
@ -2621,6 +2615,12 @@ $a->strings['Send public posts to all email contacts:'] = 'Sende öffentliche Be
|
|||
$a->strings['Action after import:'] = 'Aktion nach Import:';
|
||||
$a->strings['Move to folder'] = 'In einen Ordner verschieben';
|
||||
$a->strings['Move to folder:'] = 'In diesen Ordner verschieben:';
|
||||
$a->strings['Contact CSV file upload error'] = 'Fehler beim Hochladen der Kontakt CSV Datei';
|
||||
$a->strings['Importing Contacts done'] = 'Kontakte wurden importiert.';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Lade eine CSV Datei hoch, die das Handle der Kontakte deines alten Nutzerkontos in der ersten Spalte enthält.';
|
||||
$a->strings['Upload File'] = 'Datei hochladen';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Dein alter ActivityPub/GNU Social-Account';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Wenn du deinen alten ActivityPub oder GNU Social/Statusnet-Account-Namen hier angibst (Format name@domain.tld), werden deine Kontakte automatisch hinzugefügt. Dieses Feld wird geleert, wenn die Kontakte hinzugefügt wurden.';
|
||||
$a->strings['Delegation successfully granted.'] = 'Delegierung erfolgreich eingerichtet.';
|
||||
$a->strings['Parent user not found, unavailable or password doesn\'t match.'] = 'Der angegebene Nutzer konnte nicht gefunden werden, ist nicht verfügbar oder das angegebene Passwort ist nicht richtig.';
|
||||
$a->strings['Delegation successfully revoked.'] = 'Delegation erfolgreich aufgehoben.';
|
||||
|
@ -3037,7 +3037,7 @@ $a->strings['Delete globally'] = 'Global löschen';
|
|||
$a->strings['Remove locally'] = 'Lokal entfernen';
|
||||
$a->strings['Block %s'] = 'Blockiere %s';
|
||||
$a->strings['Ignore %s'] = 'Ignoriere %s';
|
||||
$a->strings['Collapse %s'] = 'Verberge %s';
|
||||
$a->strings['Collapse %s'] = 'Klappe %s zu';
|
||||
$a->strings['Report post'] = 'Beitrag melden';
|
||||
$a->strings['Save to folder'] = 'In Ordner speichern';
|
||||
$a->strings['I will attend'] = 'Ich werde teilnehmen';
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1073,6 +1073,7 @@ $a->strings['Display'] = 'Interfaz del usuario';
|
|||
$a->strings['Social Networks'] = 'Redes sociales';
|
||||
$a->strings['Manage Accounts'] = 'Gestionar cuentas';
|
||||
$a->strings['Connected apps'] = 'Aplicaciones conectadas';
|
||||
$a->strings['Import Contacts'] = 'Importar contactos';
|
||||
$a->strings['Export personal data'] = 'Exportación de datos personales';
|
||||
$a->strings['Remove account'] = 'Eliminar cuenta';
|
||||
$a->strings['This page is missing a url parameter.'] = 'A la página le falta URL.';
|
||||
|
@ -1686,8 +1687,6 @@ $a->strings['Wrong Password.'] = 'Password erróneo';
|
|||
$a->strings['Invalid email.'] = 'Corréo no válido.';
|
||||
$a->strings['Cannot change to that email.'] = 'Imposible cambiar a ese correo.';
|
||||
$a->strings['Settings were not updated.'] = 'Configuración no actualizada';
|
||||
$a->strings['Contact CSV file upload error'] = 'Error al subir archivo CSV';
|
||||
$a->strings['Importing Contacts done'] = 'Importación del contacto completada';
|
||||
$a->strings['Relocate message has been send to your contacts'] = 'Mensaje de reubicación enviado a sus contactos.';
|
||||
$a->strings['Unable to find your profile. Please contact your admin.'] = 'No se encontró tu perfil. Contacta al Administrador.';
|
||||
$a->strings['Personal Page Subtypes'] = 'Subtipos de Página Personal';
|
||||
|
@ -1766,9 +1765,6 @@ $a->strings['Show notifications of ignored contacts'] = 'Mostrar notificación d
|
|||
$a->strings['You don\'t see posts from ignored contacts. But you still see their comments. This setting controls if you want to still receive regular notifications that are caused by ignored contacts or not.'] = 'No ves publicaciones de contactos ignorados. Pero todavía ves sus comentarios. Esta configuración controla si aún desea recibir notificaciones regulares causadas por contactos ignorados o no.';
|
||||
$a->strings['Advanced Account/Page Type Settings'] = 'Configuración avanzada de tipo de Cuenta/Página';
|
||||
$a->strings['Change the behaviour of this account for special situations'] = 'Cambiar el comportamiento de esta cuenta para situaciones especiales';
|
||||
$a->strings['Import Contacts'] = 'Importar contactos';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Subir archivo CSV que contenga identificador de tus cuentas seguidas en elprimera columna que exportó desde la cuenta anterior.';
|
||||
$a->strings['Upload File'] = 'Subir archivo';
|
||||
$a->strings['Relocate'] = 'Relocalizar';
|
||||
$a->strings['If you have moved this profile from another server, and some of your contacts don\'t receive your updates, try pushing this button.'] = 'Si migró este perfil desde otro servidor y algunos contactos no reciben sus publicaciones intente oprimiendo esta opción.';
|
||||
$a->strings['Resend relocate message to contacts'] = 'Reenviar mensaje de relocalización a contactos';
|
||||
|
@ -1796,6 +1792,10 @@ $a->strings['Send public posts to all email contacts:'] = 'Enviar notificaciones
|
|||
$a->strings['Action after import:'] = 'Acción al importar:';
|
||||
$a->strings['Move to folder'] = 'Mover a un folder';
|
||||
$a->strings['Move to folder:'] = 'Mover al folder:';
|
||||
$a->strings['Contact CSV file upload error'] = 'Error al subir archivo CSV';
|
||||
$a->strings['Importing Contacts done'] = 'Importación del contacto completada';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Subir archivo CSV que contenga identificador de tus cuentas seguidas en elprimera columna que exportó desde la cuenta anterior.';
|
||||
$a->strings['Upload File'] = 'Subir archivo';
|
||||
$a->strings['Delegation successfully granted.'] = 'Se autorizó delegación.';
|
||||
$a->strings['Parent user not found, unavailable or password doesn\'t match.'] = 'Usuario padre no encontrado, o no coincide contraseña.';
|
||||
$a->strings['Delegation successfully revoked.'] = 'Delegación revocada con éxito.';
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -729,7 +729,6 @@ $a->strings['Text-only notification emails'] = 'Ainult tekstipõhised teavitusme
|
|||
$a->strings['Send text only notification emails, without the html part'] = 'Saada ainut tekstimeile ilma html-ita';
|
||||
$a->strings['Show detailled notifications'] = 'Kuva detailseid teavitusi';
|
||||
$a->strings['Advanced Account/Page Type Settings'] = 'Konto/Lehetüübi sätted edasijõudnutele';
|
||||
$a->strings['Upload File'] = 'Lae fail üles';
|
||||
$a->strings['Addon Settings'] = 'Lisade sätted';
|
||||
$a->strings['Add'] = 'Lisa';
|
||||
$a->strings['Failed to connect with email account using the settings provided.'] = 'E-posti kontoga ei õnnestu antud sätetega ühendust saada.';
|
||||
|
@ -740,6 +739,7 @@ $a->strings['Last successful email check:'] = 'Viimane õnnestunud meilikontroll
|
|||
$a->strings['Action after import:'] = 'Tegevus peale inporti:';
|
||||
$a->strings['Move to folder'] = 'Liiguta kausta';
|
||||
$a->strings['Move to folder:'] = 'Liiguta kausta:';
|
||||
$a->strings['Upload File'] = 'Lae fail üles';
|
||||
$a->strings['No parent user'] = 'Vanemkasutajad ei ole';
|
||||
$a->strings['Parent User'] = 'Vanemkasutaja';
|
||||
$a->strings['Additional Accounts'] = 'Lisakontod';
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1639,6 +1639,7 @@ $a->strings['Social Networks'] = 'Réseaux sociaux';
|
|||
$a->strings['Manage Accounts'] = 'Gérer vos comptes';
|
||||
$a->strings['Connected apps'] = 'Applications connectées';
|
||||
$a->strings['Remote servers'] = 'Serveurs distants';
|
||||
$a->strings['Import Contacts'] = 'Importer des contacts';
|
||||
$a->strings['Export personal data'] = 'Exporter';
|
||||
$a->strings['Remove account'] = 'Supprimer le compte';
|
||||
$a->strings['This page is missing a url parameter.'] = 'Il manque un paramètre d\'URL à cette adresse.';
|
||||
|
@ -2489,8 +2490,6 @@ $a->strings['Wrong Password.'] = 'Mot de passe erroné.';
|
|||
$a->strings['Invalid email.'] = 'Courriel invalide.';
|
||||
$a->strings['Cannot change to that email.'] = 'Ne peut pas changer vers ce courriel.';
|
||||
$a->strings['Settings were not updated.'] = 'Les paramètres n\'ont pas été mis à jour.';
|
||||
$a->strings['Contact CSV file upload error'] = 'Erreur de téléversement du fichier de contact CSV';
|
||||
$a->strings['Importing Contacts done'] = 'Import des contacts effectué';
|
||||
$a->strings['Relocate message has been send to your contacts'] = 'Un message de relocalisation a été envoyé à vos contacts.';
|
||||
$a->strings['Unable to find your profile. Please contact your admin.'] = 'Impossible de trouver votre profile. Merci de contacter votre administrateur.';
|
||||
$a->strings['Account for a service that automatically shares content based on user defined channels.'] = 'Compte de service qui partage automatiquement du contenu basés les chaînes de l\'utilisateur';
|
||||
|
@ -2586,9 +2585,6 @@ $a->strings['Show notifications of ignored contacts'] = 'Montrer les notificatio
|
|||
$a->strings['You don\'t see posts from ignored contacts. But you still see their comments. This setting controls if you want to still receive regular notifications that are caused by ignored contacts or not.'] = 'Par défaut les notifications de vos contacts ignorés sont également ignorées.';
|
||||
$a->strings['Advanced Account/Page Type Settings'] = 'Paramètres avancés de compte/page';
|
||||
$a->strings['Change the behaviour of this account for special situations'] = 'Modifier le comportement de ce compte dans certaines situations';
|
||||
$a->strings['Import Contacts'] = 'Importer des contacts';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Téléversez un fichier CSV contenant des identifiants de contacts dans la première colonne.';
|
||||
$a->strings['Upload File'] = 'Téléverser le fichier';
|
||||
$a->strings['Relocate'] = 'Relocaliser';
|
||||
$a->strings['If you have moved this profile from another server, and some of your contacts don\'t receive your updates, try pushing this button.'] = 'Si vous avez migré ce profil depuis un autre serveur et que vos contacts ne reçoivent plus vos mises à jour, essayez ce bouton.';
|
||||
$a->strings['Resend relocate message to contacts'] = 'Renvoyer un message de relocalisation aux contacts.';
|
||||
|
@ -2656,8 +2652,6 @@ $a->strings['API: Automatically links at the end of the post as attached posts']
|
|||
$a->strings['When activated, added links at the end of the post react the same way as added links in the web interface.'] = 'Quand activé, les liens ajoutés à la fin d\'une publication fonctionnent de la même manière que les liens ajoutés dans l\'interface web.';
|
||||
$a->strings['Article Mode'] = 'Mode Article';
|
||||
$a->strings['Controls how posts with titles are transmitted. Mastodon and its forks don\'t display the content of these posts if the post is created in the correct (default) way.'] = 'Contrôle la façon dont les publications avec des titres sont transmises. Mastodon et ses forks n\'affichent pas le contenu de ces publications si la publication est créée de la bonne manière (par défaut).';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Votre ancient compte ActivityPub/GNU Social';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Si vous saisissez votre adresse de compte précédente d\'un réseau basé sur ActivityPub ou GNU Social/Statusnet (au format utilisateur@domaine.tld), vos contacts seront ajoutés autoamtiquement. Le champ sera vidé quand l\'opération sera terminé.';
|
||||
$a->strings['Email/Mailbox Setup'] = 'Réglages de courriel/boîte à lettre';
|
||||
$a->strings['If you wish to communicate with email contacts using this service (optional), please specify how to connect to your mailbox.'] = 'Si vous souhaitez communiquer avec vos contacts "courriel" (facultatif), merci de nous indiquer comment vous connecter à votre boîte.';
|
||||
$a->strings['Last successful email check:'] = 'Dernière vérification réussie des courriels :';
|
||||
|
@ -2671,6 +2665,12 @@ $a->strings['Send public posts to all email contacts:'] = 'Envoyer les publicati
|
|||
$a->strings['Action after import:'] = 'Action après import :';
|
||||
$a->strings['Move to folder'] = 'Déplacer vers';
|
||||
$a->strings['Move to folder:'] = 'Déplacer vers :';
|
||||
$a->strings['Contact CSV file upload error'] = 'Erreur de téléversement du fichier de contact CSV';
|
||||
$a->strings['Importing Contacts done'] = 'Import des contacts effectué';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Téléversez un fichier CSV contenant des identifiants de contacts dans la première colonne.';
|
||||
$a->strings['Upload File'] = 'Téléverser le fichier';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Votre ancient compte ActivityPub/GNU Social';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Si vous saisissez votre adresse de compte précédente d\'un réseau basé sur ActivityPub ou GNU Social/Statusnet (au format utilisateur@domaine.tld), vos contacts seront ajoutés autoamtiquement. Le champ sera vidé quand l\'opération sera terminé.';
|
||||
$a->strings['Delegation successfully granted.'] = 'Délégation accordée avec succès.';
|
||||
$a->strings['Parent user not found, unavailable or password doesn\'t match.'] = 'Utilisateur parent introuvable, indisponible ou mot de passe incorrect.';
|
||||
$a->strings['Delegation successfully revoked.'] = 'Délégation retirée avec succès.';
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2208,8 +2208,6 @@ $a->strings['Normally the system shortens posts at the next line feed. If this o
|
|||
$a->strings['Attach the link title'] = 'Cuir tiotal a’ cheangail ris';
|
||||
$a->strings['When activated, the title of the attached link will be added as a title on posts to Diaspora. This is mostly helpful with "remote-self" contacts that share feed content.'] = 'Nuair a bhios seo an gnìomh, thèid tiotal a’ cheangail a chur ris mar tiotal air postaichean gu diaspora*. Tha seo as fheumaile dhan luchd-aithne “remote-self” a cho-roinneas susbaint inbhir.';
|
||||
$a->strings['When activated, added links at the end of the post react the same way as added links in the web interface.'] = 'Nuair a bhios seo an gnìomh, bidh an t-aon ghiùlan aig ceanglaichean a thèid a chur ri bonn puist ’s a tha aig ceanglaichean a thèid a chur ris san eadar-aghaidh-lìn.';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'An cunntas ActivityPub/GNU Social dìleabach agad';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Ma chuireas tu ainm seann-chunntais ris o shiostam stèidhichte air ActivityPub no ainm do chunntais GNU Social/Statusnet an-seo (san fhòrmat cleachdaiche@àrainn.tld), thèid an luchd-aithne agad a chur ris gu fèin-obrachail. Thèid an raon fhalamhachadh nuair a bhios sin deiseil.';
|
||||
$a->strings['Email/Mailbox Setup'] = 'Suidheachadh a’ phuist-d/a’ bhogsa-phuist';
|
||||
$a->strings['If you wish to communicate with email contacts using this service (optional), please specify how to connect to your mailbox.'] = 'Ma tha thu airson an t-seirbheis seo a chleachdadh airson conaltradh le luchd-aithne air a’ post-d (gu roghainneil), sònraich an dòigh air a nì thu ceangal leis a’ bhogsa-phuist agad.';
|
||||
$a->strings['Last successful email check:'] = 'An turas mu dheireadh a chaidh leinn sùil a thoirt air a’ phost-d:';
|
||||
|
@ -2223,6 +2221,8 @@ $a->strings['Send public posts to all email contacts:'] = 'Cuir postaichean pobl
|
|||
$a->strings['Action after import:'] = 'Gnìomh às dèid an ion-phortaidh:';
|
||||
$a->strings['Move to folder'] = 'Gluais gu pasgan';
|
||||
$a->strings['Move to folder:'] = 'Gluais gu pasgan:';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'An cunntas ActivityPub/GNU Social dìleabach agad';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Ma chuireas tu ainm seann-chunntais ris o shiostam stèidhichte air ActivityPub no ainm do chunntais GNU Social/Statusnet an-seo (san fhòrmat cleachdaiche@àrainn.tld), thèid an luchd-aithne agad a chur ris gu fèin-obrachail. Thèid an raon fhalamhachadh nuair a bhios sin deiseil.';
|
||||
$a->strings['Delegation successfully granted.'] = 'Chaidh neach-ionaid a dhèanamh dheth.';
|
||||
$a->strings['Delegation successfully revoked.'] = 'Chaidh ceadan neach-ionaid a thoirt air falbh.';
|
||||
$a->strings['Delegated administrators can view but not change delegation permissions.'] = 'Chì rianairean a tha ’nan luchd-ionaid na ceadan ach chan urrainn dhaibh an atharrachadh.';
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1618,6 +1618,7 @@ $a->strings['Social Networks'] = 'Közösségi hálózatok';
|
|||
$a->strings['Manage Accounts'] = 'Fiókok kezelése';
|
||||
$a->strings['Connected apps'] = 'Kapcsolt alkalmazások';
|
||||
$a->strings['Remote servers'] = 'Távoli kiszolgálók';
|
||||
$a->strings['Import Contacts'] = 'Partnerek importálása';
|
||||
$a->strings['Export personal data'] = 'Személyes adatok exportálása';
|
||||
$a->strings['Remove account'] = 'Fiók eltávolítása';
|
||||
$a->strings['This page is missing a url parameter.'] = 'Erről az oldalról hiányzik egy URL paraméter.';
|
||||
|
@ -2446,8 +2447,6 @@ $a->strings['Wrong Password.'] = 'Hibás jelszó.';
|
|||
$a->strings['Invalid email.'] = 'Érvénytelen e-mail-cím.';
|
||||
$a->strings['Cannot change to that email.'] = 'Nem lehet megváltoztatni arra az e-mail-címre.';
|
||||
$a->strings['Settings were not updated.'] = 'A beállítások nem lettek frissítve.';
|
||||
$a->strings['Contact CSV file upload error'] = 'Partner CSV-fájl feltöltési hiba';
|
||||
$a->strings['Importing Contacts done'] = 'A partnerek importálása kész';
|
||||
$a->strings['Relocate message has been send to your contacts'] = 'Az áthelyezési üzenet el lett küldve a partnereknek';
|
||||
$a->strings['Unable to find your profile. Please contact your admin.'] = 'Nem található a profilja. Vegye fel a kapcsolatot a rendszergazdával.';
|
||||
$a->strings['Account for a service that automatically shares content based on user defined channels.'] = 'Fiók egy olyan szolgáltatáshoz, amely automatikusan megosztja a tartalmat a felhasználó által meghatározott csatornák alapján.';
|
||||
|
@ -2543,9 +2542,6 @@ $a->strings['Show notifications of ignored contacts'] = 'Mellőzött partnerek
|
|||
$a->strings['You don\'t see posts from ignored contacts. But you still see their comments. This setting controls if you want to still receive regular notifications that are caused by ignored contacts or not.'] = 'Nem látja a mellőzött partnerektől érkező bejegyzéseket. Viszont továbbra is látja a hozzászólásaikat. Ez a beállítás azt vezérli, hogy továbbra is szeretne-e olyan normál értesítéseket kapni vagy sem, amelyeket mellőzött partnerek okoznak.';
|
||||
$a->strings['Advanced Account/Page Type Settings'] = 'Speciális fióktípus vagy oldaltípus beállítások';
|
||||
$a->strings['Change the behaviour of this account for special situations'] = 'A fiók viselkedésének megváltoztatása bizonyos helyzetekre.';
|
||||
$a->strings['Import Contacts'] = 'Partnerek importálása';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Töltsön fel egy olyan CSV-fájlt, amely a követett fiókok kezelőjét tartalmazza az első oszlopban, ahogy a régi fiókból exportálta.';
|
||||
$a->strings['Upload File'] = 'Fájl feltöltése';
|
||||
$a->strings['Relocate'] = 'Áthelyezés';
|
||||
$a->strings['If you have moved this profile from another server, and some of your contacts don\'t receive your updates, try pushing this button.'] = 'Ha áthelyezte ezt a profilt egy másik kiszolgálóról, és néhány partnere nem kapta meg a frissítéseket, akkor próbálja meg megnyomni ezt a gombot.';
|
||||
$a->strings['Resend relocate message to contacts'] = 'Áthelyezési üzenet küldése a partnereknek';
|
||||
|
@ -2613,8 +2609,6 @@ $a->strings['API: Automatically links at the end of the post as attached posts']
|
|||
$a->strings['When activated, added links at the end of the post react the same way as added links in the web interface.'] = 'Ha aktiválva van, akkor a bejegyzés végéhez hozzáadott hivatkozások ugyanúgy reagálnak, mint a webes felületen hozzáadott hivatkozások.';
|
||||
$a->strings['Article Mode'] = 'Cikk mód';
|
||||
$a->strings['Controls how posts with titles are transmitted. Mastodon and its forks don\'t display the content of these posts if the post is created in the correct (default) way.'] = 'Azt vezérli, hogy a címekkel rendelkező bejegyzések hogyan kerülnek továbbításra. A Mastodon és elágaztatásai nem jelenítik meg ezeknek a bejegyzéseknek a tartalmát, ha a bejegyzést a megfelelő (alapértelmezett) módon hozták létre.';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Az örökölt ActivityPub/GNU Social fiókja';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Ha megadja itt a régi, egy ActivityPub alapú rendszerből származó fiókja nevét, illetve a GNU Social vagy Statusnet fiókja nevét (felhasználó@tartomány.tld formátumban), akkor a partnerei automatikusan hozzá lesznek adva. A mező ki lesz ürítve, ha elkészült.';
|
||||
$a->strings['Email/Mailbox Setup'] = 'E-mail vagy postafiók-beállítások';
|
||||
$a->strings['If you wish to communicate with email contacts using this service (optional), please specify how to connect to your mailbox.'] = 'Ha e-mailes partnerekkel szeretne kommunikálni ezen szolgáltatás használatával (opcionális), akkor adja meg, hogy hogyan kell kapcsolódni a postafiókjához.';
|
||||
$a->strings['Last successful email check:'] = 'Legutóbbi sikeres e-mail-ellenőrzés:';
|
||||
|
@ -2628,6 +2622,12 @@ $a->strings['Send public posts to all email contacts:'] = 'Nyilvános bejegyzés
|
|||
$a->strings['Action after import:'] = 'Importálás utáni művelet:';
|
||||
$a->strings['Move to folder'] = 'Áthelyezés mappába';
|
||||
$a->strings['Move to folder:'] = 'Áthelyezés mappába:';
|
||||
$a->strings['Contact CSV file upload error'] = 'Partner CSV-fájl feltöltési hiba';
|
||||
$a->strings['Importing Contacts done'] = 'A partnerek importálása kész';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Töltsön fel egy olyan CSV-fájlt, amely a követett fiókok kezelőjét tartalmazza az első oszlopban, ahogy a régi fiókból exportálta.';
|
||||
$a->strings['Upload File'] = 'Fájl feltöltése';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Az örökölt ActivityPub/GNU Social fiókja';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Ha megadja itt a régi, egy ActivityPub alapú rendszerből származó fiókja nevét, illetve a GNU Social vagy Statusnet fiókja nevét (felhasználó@tartomány.tld formátumban), akkor a partnerei automatikusan hozzá lesznek adva. A mező ki lesz ürítve, ha elkészült.';
|
||||
$a->strings['Delegation successfully granted.'] = 'A meghatalmazás sikeresen megadva.';
|
||||
$a->strings['Parent user not found, unavailable or password doesn\'t match.'] = 'A fölérendelt felhasználó nem található, nem érhető el vagy a jelszó nem egyezik.';
|
||||
$a->strings['Delegation successfully revoked.'] = 'A meghatalmazás sikeresen visszavonva.';
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1121,6 +1121,7 @@ $a->strings['Display'] = 'Visualizzazione';
|
|||
$a->strings['Social Networks'] = 'Social Networks';
|
||||
$a->strings['Manage Accounts'] = 'Gestisci Account';
|
||||
$a->strings['Connected apps'] = 'Applicazioni collegate';
|
||||
$a->strings['Import Contacts'] = 'Importa Contatti';
|
||||
$a->strings['Export personal data'] = 'Esporta dati personali';
|
||||
$a->strings['Remove account'] = 'Rimuovi account';
|
||||
$a->strings['This page is missing a url parameter.'] = 'A questa pagina manca il parametro url.';
|
||||
|
@ -1785,8 +1786,6 @@ $a->strings['Wrong Password.'] = 'Password Sbagliata.';
|
|||
$a->strings['Invalid email.'] = 'Email non valida.';
|
||||
$a->strings['Cannot change to that email.'] = 'Non puoi usare quella email.';
|
||||
$a->strings['Settings were not updated.'] = 'Le impostazioni non sono state aggiornate.';
|
||||
$a->strings['Contact CSV file upload error'] = 'Errore nel caricamento del file CSV dei contatti';
|
||||
$a->strings['Importing Contacts done'] = 'Importazione dei Contatti riuscita';
|
||||
$a->strings['Relocate message has been send to your contacts'] = 'Il messaggio di trasloco è stato inviato ai tuoi contatti';
|
||||
$a->strings['Unable to find your profile. Please contact your admin.'] = 'Impossibile trovare il tuo profilo. Contatta il tuo amministratore.';
|
||||
$a->strings['Personal Page Subtypes'] = 'Sottotipi di Pagine Personali';
|
||||
|
@ -1868,9 +1867,6 @@ $a->strings['Show notifications of ignored contacts'] = 'Mostra notifiche dai co
|
|||
$a->strings['You don\'t see posts from ignored contacts. But you still see their comments. This setting controls if you want to still receive regular notifications that are caused by ignored contacts or not.'] = 'Non vedi i messaggi da contatti ignorati. Ma puoi ancora vedere i loro commenti. Questa impostazione controlla se vuoi o meno continuare a ricevere notifiche regolari che sono causate dai contatti ignorati.';
|
||||
$a->strings['Advanced Account/Page Type Settings'] = 'Impostazioni avanzate Account/Tipo di pagina';
|
||||
$a->strings['Change the behaviour of this account for special situations'] = 'Modifica il comportamento di questo account in situazioni speciali';
|
||||
$a->strings['Import Contacts'] = 'Importa Contatti';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Carica un file CSV che contiene gli indirizzi dei tuoi account seguiti nella prima colonna che hai esportato dal vecchio account.';
|
||||
$a->strings['Upload File'] = 'Carica File';
|
||||
$a->strings['Relocate'] = 'Trasloca';
|
||||
$a->strings['If you have moved this profile from another server, and some of your contacts don\'t receive your updates, try pushing this button.'] = 'Se hai spostato questo profilo da un\'altro server, e alcuni dei tuoi contatti non ricevono i tuoi aggiornamenti, prova a premere questo bottone.';
|
||||
$a->strings['Resend relocate message to contacts'] = 'Invia nuovamente il messaggio di trasloco ai contatti';
|
||||
|
@ -1889,8 +1885,6 @@ $a->strings['Enable simple text shortening'] = 'Abilita accorciamento semplice d
|
|||
$a->strings['Normally the system shortens posts at the next line feed. If this option is enabled then the system will shorten the text at the maximum character limit.'] = 'Normalmente il sistema accorcia i messaggi alla successiva interruzione di linea. Se questa opzione è abilitata il sistema accorcerà il testo al raggiungimento del limite massimo di caratteri.';
|
||||
$a->strings['Attach the link title'] = 'Allega il titolo del collegamento';
|
||||
$a->strings['When activated, the title of the attached link will be added as a title on posts to Diaspora. This is mostly helpful with "remote-self" contacts that share feed content.'] = 'Quando attivato, il titolo del collegamento allegato sarà aggiunto come titolo dei messaggi su Diaspora. Questo è più che altro utile con i contatti "remoti di sè stessi" che condividono il contenuto del flusso.';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Il tuo vecchio account ActivityPub/GNU Social ';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Se inserisci il nome del tuo vecchio account su un sistema basato su ActivityPub o del tuo vecchio account GNU Social/Statusnet (nel formato utente@dominio.tld), i tuoi contatti verranno importati automaticamente. Il campo verrà svuotato una volta terminata l\'operazione.';
|
||||
$a->strings['Email/Mailbox Setup'] = 'Impostazioni email';
|
||||
$a->strings['If you wish to communicate with email contacts using this service (optional), please specify how to connect to your mailbox.'] = 'Se vuoi comunicare con i contatti email usando questo servizio, specifica come collegarti alla tua casella di posta. (opzionale)';
|
||||
$a->strings['Last successful email check:'] = 'Ultimo controllo email eseguito con successo:';
|
||||
|
@ -1904,6 +1898,12 @@ $a->strings['Send public posts to all email contacts:'] = 'Invia i messaggi pubb
|
|||
$a->strings['Action after import:'] = 'Azione dopo importazione:';
|
||||
$a->strings['Move to folder'] = 'Sposta nella cartella';
|
||||
$a->strings['Move to folder:'] = 'Sposta nella cartella:';
|
||||
$a->strings['Contact CSV file upload error'] = 'Errore nel caricamento del file CSV dei contatti';
|
||||
$a->strings['Importing Contacts done'] = 'Importazione dei Contatti riuscita';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Carica un file CSV che contiene gli indirizzi dei tuoi account seguiti nella prima colonna che hai esportato dal vecchio account.';
|
||||
$a->strings['Upload File'] = 'Carica File';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Il tuo vecchio account ActivityPub/GNU Social ';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Se inserisci il nome del tuo vecchio account su un sistema basato su ActivityPub o del tuo vecchio account GNU Social/Statusnet (nel formato utente@dominio.tld), i tuoi contatti verranno importati automaticamente. Il campo verrà svuotato una volta terminata l\'operazione.';
|
||||
$a->strings['Delegation successfully granted.'] = 'Delega concessa con successo.';
|
||||
$a->strings['Parent user not found, unavailable or password doesn\'t match.'] = 'Utente principale non trovato, non disponibile o la password non corrisponde.';
|
||||
$a->strings['Delegation successfully revoked.'] = 'Delega revocata con successo.';
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -969,6 +969,7 @@ $a->strings['Display'] = '表示';
|
|||
$a->strings['Social Networks'] = 'ソーシャルネットワーク';
|
||||
$a->strings['Manage Accounts'] = 'アカウントの管理';
|
||||
$a->strings['Connected apps'] = '接続されたアプリ';
|
||||
$a->strings['Import Contacts'] = 'コンタクトをインポートする';
|
||||
$a->strings['Export personal data'] = '個人データのエクスポート';
|
||||
$a->strings['Remove account'] = 'アカウントを削除';
|
||||
$a->strings['This page is missing a url parameter.'] = 'このページにはurlパラメーターがありません。';
|
||||
|
@ -1482,8 +1483,6 @@ $a->strings['Wrong Password.'] = 'パスワードが間違っています。';
|
|||
$a->strings['Invalid email.'] = '無効なメール。';
|
||||
$a->strings['Cannot change to that email.'] = 'そのメールに変更できません。';
|
||||
$a->strings['Settings were not updated.'] = '設定が更新されませんでした。';
|
||||
$a->strings['Contact CSV file upload error'] = 'アップロードエラー:コンタクトCSVファイル';
|
||||
$a->strings['Importing Contacts done'] = 'コンタクトのインポートが完了しました';
|
||||
$a->strings['Relocate message has been send to your contacts'] = '再配置メッセージがコンタクトに送信されました';
|
||||
$a->strings['Unable to find your profile. Please contact your admin.'] = 'プロフィールが見つかりません。管理者に連絡してください。';
|
||||
$a->strings['Personal Page Subtypes'] = '個人ページのサブタイプ';
|
||||
|
@ -1562,9 +1561,6 @@ $a->strings['Show notifications of ignored contacts'] = '無視されたコン
|
|||
$a->strings['You don\'t see posts from ignored contacts. But you still see their comments. This setting controls if you want to still receive regular notifications that are caused by ignored contacts or not.'] = '無視されたコンタクトからの投稿は表示されません。しかし、相手のコメントは表示されます。この設定では、無視されたコンタクトからの通知を定期的に受け取るかどうかを設定します。';
|
||||
$a->strings['Advanced Account/Page Type Settings'] = 'アカウント/ページタイプの詳細設定';
|
||||
$a->strings['Change the behaviour of this account for special situations'] = '特別な状況でこのアカウントの動作を変更する';
|
||||
$a->strings['Import Contacts'] = 'コンタクトをインポートする';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = '古いアカウントからエクスポートしたCSVファイルをアップロードします。これは最初の列に、フォローしているアカウントのハンドルを含みます。';
|
||||
$a->strings['Upload File'] = 'ファイルをアップロード';
|
||||
$a->strings['Relocate'] = '再配置';
|
||||
$a->strings['If you have moved this profile from another server, and some of your contacts don\'t receive your updates, try pushing this button.'] = 'このプロフィールを別のサーバーから移動し、コンタクトの一部が更新を受信しない場合は、このボタンを押してみてください。';
|
||||
$a->strings['Resend relocate message to contacts'] = '再配置メッセージをコンタクトに再送信する';
|
||||
|
@ -1592,6 +1588,10 @@ $a->strings['Send public posts to all email contacts:'] = 'すべてのメール
|
|||
$a->strings['Action after import:'] = 'インポート後のアクション:';
|
||||
$a->strings['Move to folder'] = 'フォルダへ移動';
|
||||
$a->strings['Move to folder:'] = 'フォルダへ移動:';
|
||||
$a->strings['Contact CSV file upload error'] = 'アップロードエラー:コンタクトCSVファイル';
|
||||
$a->strings['Importing Contacts done'] = 'コンタクトのインポートが完了しました';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = '古いアカウントからエクスポートしたCSVファイルをアップロードします。これは最初の列に、フォローしているアカウントのハンドルを含みます。';
|
||||
$a->strings['Upload File'] = 'ファイルをアップロード';
|
||||
$a->strings['Delegation successfully granted.'] = '委任が正常に許可されました。';
|
||||
$a->strings['Parent user not found, unavailable or password doesn\'t match.'] = '親ユーザーが見つからないか、利用できないか、パスワードが一致しません。';
|
||||
$a->strings['Delegation successfully revoked.'] = '委任が正常に取り消されました。';
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -966,6 +966,7 @@ $a->strings['Display'] = 'Weergave';
|
|||
$a->strings['Social Networks'] = 'Sociale netwerken';
|
||||
$a->strings['Manage Accounts'] = 'Beheer Gebruikers';
|
||||
$a->strings['Connected apps'] = 'Verbonden applicaties';
|
||||
$a->strings['Import Contacts'] = 'Importeer contacten';
|
||||
$a->strings['Export personal data'] = 'Persoonlijke gegevens exporteren';
|
||||
$a->strings['Remove account'] = 'Account verwijderen';
|
||||
$a->strings['This page is missing a url parameter.'] = 'Deze pagina mist een url-parameter.';
|
||||
|
@ -1462,7 +1463,6 @@ $a->strings['Wrong Password.'] = 'Verkeerd wachtwoord.';
|
|||
$a->strings['Invalid email.'] = 'Ongeldig email adres.';
|
||||
$a->strings['Cannot change to that email.'] = 'Kan niet naar dat email adres veranderen.';
|
||||
$a->strings['Settings were not updated.'] = 'Wijziging instellingen is niet opgeslagen.';
|
||||
$a->strings['Importing Contacts done'] = 'Importeren Contacten voltooid';
|
||||
$a->strings['Relocate message has been send to your contacts'] = 'Verhuis boodschap is verzonden naar je contacten';
|
||||
$a->strings['Unable to find your profile. Please contact your admin.'] = 'Kan je profiel niet vinden. Contacteer alsjeblieft je beheerder.';
|
||||
$a->strings['Personal Page Subtypes'] = 'Persoonlijke Pagina Subtypes';
|
||||
|
@ -1539,9 +1539,6 @@ $a->strings['Show detailled notifications'] = 'Toon gedetailleerde notificaties'
|
|||
$a->strings['Per default, notifications are condensed to a single notification per item. When enabled every notification is displayed.'] = 'Standaard worden notificaties samengevoegd in een enkele notificatie per item. Als je deze parameter activeert wordt elke notificatie getoond.';
|
||||
$a->strings['Advanced Account/Page Type Settings'] = 'Geavanceerde Account/Pagina Type Instellingen';
|
||||
$a->strings['Change the behaviour of this account for special situations'] = 'Pas het gedrag van dit account aan voor speciale situaties';
|
||||
$a->strings['Import Contacts'] = 'Importeer contacten';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Upload een CSV-bestand met de handle van uw gevolgde gebruikers in de eerste kolom die u uit de oude gebruiker hebt geëxporteerd.';
|
||||
$a->strings['Upload File'] = 'Upload bestand';
|
||||
$a->strings['Relocate'] = 'Verhuis';
|
||||
$a->strings['If you have moved this profile from another server, and some of your contacts don\'t receive your updates, try pushing this button.'] = 'Als je je profiel van een andere server hebt verhuisd, en er zijn contacten die geen updates van je ontvangen, probeer dan eens deze knop.';
|
||||
$a->strings['Resend relocate message to contacts'] = 'Stuur verhuis boodschap naar contacten';
|
||||
|
@ -1569,6 +1566,9 @@ $a->strings['Send public posts to all email contacts:'] = 'Openbare posts naar a
|
|||
$a->strings['Action after import:'] = 'Actie na importeren:';
|
||||
$a->strings['Move to folder'] = 'Naar map verplaatsen';
|
||||
$a->strings['Move to folder:'] = 'Verplaatsen naar map:';
|
||||
$a->strings['Importing Contacts done'] = 'Importeren Contacten voltooid';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Upload een CSV-bestand met de handle van uw gevolgde gebruikers in de eerste kolom die u uit de oude gebruiker hebt geëxporteerd.';
|
||||
$a->strings['Upload File'] = 'Upload bestand';
|
||||
$a->strings['Delegation successfully granted.'] = 'Delegatie met succes verleend.';
|
||||
$a->strings['Parent user not found, unavailable or password doesn\'t match.'] = 'Brongebruiker niet gevonden, niet beschikbaar of wachtwoord komt niet overeen.';
|
||||
$a->strings['Delegation successfully revoked.'] = 'Delegatie is ingetrokken.';
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1209,6 +1209,7 @@ $a->strings['Display'] = 'Wygląd';
|
|||
$a->strings['Social Networks'] = 'Portale społecznościowe';
|
||||
$a->strings['Manage Accounts'] = 'Zarządzanie kontami';
|
||||
$a->strings['Connected apps'] = 'Powiązane aplikacje';
|
||||
$a->strings['Import Contacts'] = 'Import kontaktów';
|
||||
$a->strings['Export personal data'] = 'Eksportuj dane osobiste';
|
||||
$a->strings['Remove account'] = 'Usuń konto';
|
||||
$a->strings['This page is missing a url parameter.'] = 'Na tej stronie brakuje parametru url.';
|
||||
|
@ -1917,8 +1918,6 @@ $a->strings['Wrong Password.'] = 'Nieprawidłowe hasło.';
|
|||
$a->strings['Invalid email.'] = 'Niepoprawny e-mail.';
|
||||
$a->strings['Cannot change to that email.'] = 'Nie można zmienić tego e-maila.';
|
||||
$a->strings['Settings were not updated.'] = 'Ustawienia nie zostały zaktualizowane.';
|
||||
$a->strings['Contact CSV file upload error'] = 'Kontakt z plikiem CSV błąd przekazywania plików';
|
||||
$a->strings['Importing Contacts done'] = 'Importowanie kontaktów zakończone';
|
||||
$a->strings['Relocate message has been send to your contacts'] = 'Przeniesienie wiadomości zostało wysłane do Twoich kontaktów';
|
||||
$a->strings['Unable to find your profile. Please contact your admin.'] = 'Nie można znaleźć Twojego profilu. Skontaktuj się z administratorem.';
|
||||
$a->strings['Personal Page Subtypes'] = 'Podtypy osobistych stron';
|
||||
|
@ -2006,9 +2005,6 @@ $a->strings['Show notifications of ignored contacts'] = 'Pokaż powiadomienia o
|
|||
$a->strings['You don\'t see posts from ignored contacts. But you still see their comments. This setting controls if you want to still receive regular notifications that are caused by ignored contacts or not.'] = 'Nie widzisz wpisów od ignorowanych kontaktów. Ale nadal widzisz ich komentarze. To ustawienie określa, czy chcesz nadal otrzymywać regularne powiadomienia, które są powodowane przez ignorowane kontakty, czy nie.';
|
||||
$a->strings['Advanced Account/Page Type Settings'] = 'Zaawansowane ustawienia konta/rodzaju strony';
|
||||
$a->strings['Change the behaviour of this account for special situations'] = 'Zmień zachowanie tego konta w sytuacjach specjalnych';
|
||||
$a->strings['Import Contacts'] = 'Import kontaktów';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Prześlij plik CSV zawierający obsługę obserwowanych kont w pierwszej kolumnie wyeksportowanej ze starego konta.';
|
||||
$a->strings['Upload File'] = 'Prześlij plik';
|
||||
$a->strings['Relocate'] = 'Przeniesienie';
|
||||
$a->strings['If you have moved this profile from another server, and some of your contacts don\'t receive your updates, try pushing this button.'] = 'Jeśli ten profil został przeniesiony z innego serwera, a niektóre z Twoich kontaktów nie otrzymają aktualizacji, spróbuj nacisnąć ten przycisk.';
|
||||
$a->strings['Resend relocate message to contacts'] = 'Wyślij ponownie przenieść wiadomości do kontaktów';
|
||||
|
@ -2032,8 +2028,6 @@ $a->strings['Enable simple text shortening'] = 'Włącz proste skracanie tekstu'
|
|||
$a->strings['Normally the system shortens posts at the next line feed. If this option is enabled then the system will shorten the text at the maximum character limit.'] = 'Zwykle system skraca wpisy przy następnym wysunięciu wiersza. Jeśli ta opcja jest włączona, system skróci tekst do maksymalnego limitu znaków.';
|
||||
$a->strings['Attach the link title'] = 'Dołącz tytuł linku';
|
||||
$a->strings['When activated, the title of the attached link will be added as a title on posts to Diaspora. This is mostly helpful with "remote-self" contacts that share feed content.'] = 'Po aktywacji tytuł dołączonego linku zostanie dodany jako tytuł postów do Diaspory. Jest to szczególnie pomocne w przypadku kontaktów „zdalnych”, które udostępniają treść kanału.';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Twoje stare konto ActivityPub/GNU Social';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Jeśli wprowadzisz tutaj swoją starą nazwę konta z systemu opartego na ActivityPub lub nazwę konta GNU Social/Statusnet (w formacie użytkownik@domena.tld), Twoje kontakty zostaną dodane automatycznie. Po zakończeniu pole zostanie opróżnione.';
|
||||
$a->strings['Email/Mailbox Setup'] = 'Ustawienia emaila/skrzynki mailowej';
|
||||
$a->strings['If you wish to communicate with email contacts using this service (optional), please specify how to connect to your mailbox.'] = 'Jeśli chcesz komunikować się z kontaktami e-mail za pomocą tej usługi (opcjonalnie), określ sposób łączenia się ze skrzynką pocztową.';
|
||||
$a->strings['Last successful email check:'] = 'Ostatni sprawdzony e-mail:';
|
||||
|
@ -2047,6 +2041,12 @@ $a->strings['Send public posts to all email contacts:'] = 'Wyślij publiczny wpi
|
|||
$a->strings['Action after import:'] = 'Akcja po zaimportowaniu:';
|
||||
$a->strings['Move to folder'] = 'Przenieś do katalogu';
|
||||
$a->strings['Move to folder:'] = 'Przenieś do katalogu:';
|
||||
$a->strings['Contact CSV file upload error'] = 'Kontakt z plikiem CSV błąd przekazywania plików';
|
||||
$a->strings['Importing Contacts done'] = 'Importowanie kontaktów zakończone';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Prześlij plik CSV zawierający obsługę obserwowanych kont w pierwszej kolumnie wyeksportowanej ze starego konta.';
|
||||
$a->strings['Upload File'] = 'Prześlij plik';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Twoje stare konto ActivityPub/GNU Social';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Jeśli wprowadzisz tutaj swoją starą nazwę konta z systemu opartego na ActivityPub lub nazwę konta GNU Social/Statusnet (w formacie użytkownik@domena.tld), Twoje kontakty zostaną dodane automatycznie. Po zakończeniu pole zostanie opróżnione.';
|
||||
$a->strings['Delegation successfully granted.'] = 'Delegacja została pomyślnie przyznana.';
|
||||
$a->strings['Parent user not found, unavailable or password doesn\'t match.'] = 'Nie znaleziono użytkownika nadrzędnego, jest on niedostępny lub hasło nie pasuje.';
|
||||
$a->strings['Delegation successfully revoked.'] = 'Delegacja została pomyślnie odwołana.';
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1444,6 +1444,7 @@ $a->strings['Social Networks'] = 'Социальные сети';
|
|||
$a->strings['Manage Accounts'] = 'Управление учётными записями';
|
||||
$a->strings['Connected apps'] = 'Подключенные приложения';
|
||||
$a->strings['Remote servers'] = 'Другие серверы';
|
||||
$a->strings['Import Contacts'] = 'Импорт контактов';
|
||||
$a->strings['Export personal data'] = 'Экспорт личных данных';
|
||||
$a->strings['Remove account'] = 'Удалить аккаунт';
|
||||
$a->strings['The post was created'] = 'Запись создана';
|
||||
|
@ -2250,8 +2251,6 @@ $a->strings['Wrong Password.'] = 'Неправильный пароль';
|
|||
$a->strings['Invalid email.'] = 'Неправильный адрес почты';
|
||||
$a->strings['Cannot change to that email.'] = 'Нельзя установить этот адрес почты';
|
||||
$a->strings['Settings were not updated.'] = 'Настройки не были изменены.';
|
||||
$a->strings['Contact CSV file upload error'] = 'Ошибка загрузки CSV с контактами';
|
||||
$a->strings['Importing Contacts done'] = 'Импорт контактов завершён';
|
||||
$a->strings['Relocate message has been send to your contacts'] = 'Перемещённое сообщение было отправлено списку контактов';
|
||||
$a->strings['Unable to find your profile. Please contact your admin.'] = 'Не получается найти ваш профиль. Пожалуйста свяжитесь с администратором.';
|
||||
$a->strings['Account for a service that automatically shares content based on user defined channels.'] = 'Учётная запись, которая автоматически публикует контент из каналов, созданных пользователем.';
|
||||
|
@ -2347,9 +2346,6 @@ $a->strings['Show notifications of ignored contacts'] = 'Показывать у
|
|||
$a->strings['You don\'t see posts from ignored contacts. But you still see their comments. This setting controls if you want to still receive regular notifications that are caused by ignored contacts or not.'] = 'Вы не видите записи от игнорируемых контактов, но вы видите их комментарии. Эта настройка определяет, хотите ли вы получать уведомления от действий игнорируемых контактов или нет.';
|
||||
$a->strings['Advanced Account/Page Type Settings'] = 'Расширенные настройки учётной записи';
|
||||
$a->strings['Change the behaviour of this account for special situations'] = 'Измените поведение этого аккаунта в специальных ситуациях';
|
||||
$a->strings['Import Contacts'] = 'Импорт контактов';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Загрузите файл CSV, который содержит адреса ваших контактов в первой колонке. Вы можете экспортировать его из вашей старой учётной записи.';
|
||||
$a->strings['Upload File'] = 'Загрузить файл';
|
||||
$a->strings['Relocate'] = 'Перемещение';
|
||||
$a->strings['If you have moved this profile from another server, and some of your contacts don\'t receive your updates, try pushing this button.'] = 'Если вы переместили эту анкету с другого сервера, и некоторые из ваших контактов не получили ваши обновления, попробуйте нажать эту кнопку.';
|
||||
$a->strings['Resend relocate message to contacts'] = 'Отправить перемещённые сообщения контактам';
|
||||
|
@ -2417,8 +2413,6 @@ $a->strings['API: Automatically links at the end of the post as attached posts']
|
|||
$a->strings['When activated, added links at the end of the post react the same way as added links in the web interface.'] = 'Если включено, ссылки в конце записей будут обрабатываться так же, как ссылки, добавленные через веб-интерфейс.';
|
||||
$a->strings['Article Mode'] = 'Режим статей';
|
||||
$a->strings['Controls how posts with titles are transmitted. Mastodon and its forks don\'t display the content of these posts if the post is created in the correct (default) way.'] = 'Как будут передаваться записи, у которых указан заголовок. Mastodon и похожие платформы не показывают содержимое таких записей, если они созданы в обычном формате (по-умолчанию), оставляя лишь ссылку на них.';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Ваша старая учётная запись ActivityPub/GNU Social';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Если вы введете тут вашу старую учетную запись от платформы совместимой с ActivityPub или GNU Social/Statusnet (в виде пользователь@домен), ваши контакты оттуда будут автоматически добавлены. Поле будет очищено когда все контакты будут добавлены.';
|
||||
$a->strings['Email/Mailbox Setup'] = 'Настройка эл. почты / почтового ящика';
|
||||
$a->strings['If you wish to communicate with email contacts using this service (optional), please specify how to connect to your mailbox.'] = 'Если вы хотите общаться с Email контактами, используя этот сервис (по желанию), пожалуйста, уточните, как подключиться к вашему почтовому ящику.';
|
||||
$a->strings['Last successful email check:'] = 'Последняя успешная проверка электронной почты:';
|
||||
|
@ -2432,6 +2426,12 @@ $a->strings['Send public posts to all email contacts:'] = 'Отправлять
|
|||
$a->strings['Action after import:'] = 'Действие после импорта:';
|
||||
$a->strings['Move to folder'] = 'Переместить в папку';
|
||||
$a->strings['Move to folder:'] = 'Переместить в папку:';
|
||||
$a->strings['Contact CSV file upload error'] = 'Ошибка загрузки CSV с контактами';
|
||||
$a->strings['Importing Contacts done'] = 'Импорт контактов завершён';
|
||||
$a->strings['Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'] = 'Загрузите файл CSV, который содержит адреса ваших контактов в первой колонке. Вы можете экспортировать его из вашей старой учётной записи.';
|
||||
$a->strings['Upload File'] = 'Загрузить файл';
|
||||
$a->strings['Your legacy ActivityPub/GNU Social account'] = 'Ваша старая учётная запись ActivityPub/GNU Social';
|
||||
$a->strings['If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.'] = 'Если вы введете тут вашу старую учетную запись от платформы совместимой с ActivityPub или GNU Social/Statusnet (в виде пользователь@домен), ваши контакты оттуда будут автоматически добавлены. Поле будет очищено когда все контакты будут добавлены.';
|
||||
$a->strings['Delegation successfully granted.'] = 'Делегирование успешно предоставлено.';
|
||||
$a->strings['Parent user not found, unavailable or password doesn\'t match.'] = 'Родительский пользователь не найден, недоступен или пароль не совпадает.';
|
||||
$a->strings['Delegation successfully revoked.'] = 'Делегирование успешно отменено.';
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -730,6 +730,7 @@ $a->strings['Account'] = 'Konto';
|
|||
$a->strings['Display'] = 'Skärm';
|
||||
$a->strings['Social Networks'] = 'Sociala nätverk';
|
||||
$a->strings['Connected apps'] = 'Anslutna appar';
|
||||
$a->strings['Import Contacts'] = 'Importera kontakter';
|
||||
$a->strings['Export personal data'] = 'Exporter personlig data';
|
||||
$a->strings['Remove account'] = 'Ta bort konto';
|
||||
$a->strings['The post was created'] = 'Inlägget skapades';
|
||||
|
@ -1079,8 +1080,6 @@ $a->strings['Someone liked your content'] = 'Någon gillade ditt innehåll';
|
|||
$a->strings['Someone shared your content'] = 'Någon delade ditt innehåll';
|
||||
$a->strings['Activate desktop notifications'] = 'Aktivera skrivbordsaviseringar';
|
||||
$a->strings['Show detailled notifications'] = 'Visa detaljerade aviseringar';
|
||||
$a->strings['Import Contacts'] = 'Importera kontakter';
|
||||
$a->strings['Upload File'] = 'Ladda upp fil';
|
||||
$a->strings['Relocate'] = 'Omlokalisera';
|
||||
$a->strings['If you have moved this profile from another server, and some of your contacts don\'t receive your updates, try pushing this button.'] = 'Om du har flyttat den här profilen från en annan server och några av dina kontakter inte får dina uppdateringar, försök att trycka på den här knappen.';
|
||||
$a->strings['Addon Settings'] = 'Inställningar för Tillägg';
|
||||
|
@ -1103,6 +1102,7 @@ $a->strings['Send public posts to all email contacts:'] = 'Skicka publika inläg
|
|||
$a->strings['Action after import:'] = 'Åtgärd efter importering:';
|
||||
$a->strings['Move to folder'] = 'Flytta till mapp';
|
||||
$a->strings['Move to folder:'] = 'Flytta till mapp:';
|
||||
$a->strings['Upload File'] = 'Ladda upp fil';
|
||||
$a->strings['Potential Delegates'] = 'Potentiella delegater';
|
||||
$a->strings['No entries.'] = 'Inga poster.';
|
||||
$a->strings['Display Settings'] = 'Skärm-inställningar';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue