Compare commits

..

No commits in common. "548c0b9602b4e26a18f30c1ba7a40ff4dd850248" and "04cb22a134421bf2ba90e2cae3953a08e3aaf9c7" have entirely different histories.

29 changed files with 156 additions and 442 deletions

View file

@ -2,9 +2,6 @@
# #
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon
parameters: parameters:
level: 3 level: 3
@ -26,10 +23,6 @@ parameters:
dynamicConstantNames: dynamicConstantNames:
- DB_UPDATE_VERSION - DB_UPDATE_VERSION
# See all rules at https://github.com/phpstan/phpstan-strict-rules/blob/2.0.x/rules.neon
strictRules:
allRules: false
ignoreErrors: ignoreErrors:
- -
# Ignore missing GdImage class in PHP <= 7.4 # Ignore missing GdImage class in PHP <= 7.4
@ -45,17 +38,3 @@ parameters:
# Ignore missing IMAP\Connection class in PHP <= 8.0 # Ignore missing IMAP\Connection class in PHP <= 8.0
message: '(^Parameter .+ has invalid type IMAP\\Connection\.$)' message: '(^Parameter .+ has invalid type IMAP\\Connection\.$)'
path: src path: src
-
# #Fixme: Ignore type mismatch of BaseRepository::$factory in child classes
message: '#^PHPDoc type Friendica\\.+ of property Friendica\\.+\:\:\$factory is not the same as PHPDoc type Friendica\\Capabilities\\ICanCreateFromTableRow of overridden property Friendica\\BaseRepository\:\:\$factory\.$#'
identifier: property.phpDocType
count: 13
path: src
-
# #Fixme: Ignore type mismatch of BaseModule::$response in BaseApi module
message: '#^PHPDoc type Friendica\\Module\\Api\\ApiResponse of property Friendica\\Module\\BaseApi\:\:\$response is not the same as PHPDoc type Friendica\\Capabilities\\ICanCreateResponses of overridden property Friendica\\BaseModule\:\:\$response\.$#'
identifier: property.phpDocType
count: 1
path: src/Module/BaseApi.php

View file

@ -157,7 +157,6 @@
"php-mock/php-mock-phpunit": "^2.10", "php-mock/php-mock-phpunit": "^2.10",
"phpmd/phpmd": "^2.15", "phpmd/phpmd": "^2.15",
"phpstan/phpstan": "^2.0", "phpstan/phpstan": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9" "phpunit/phpunit": "^9"
}, },
"scripts": { "scripts": {

50
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "e93a8ac7e31cf3e5e0ca76134e5ffa0b", "content-hash": "897b878d6db24b9a6437bd9f971478be",
"packages": [ "packages": [
{ {
"name": "asika/simple-console", "name": "asika/simple-console",
@ -5849,54 +5849,6 @@
], ],
"time": "2024-11-11T15:43:04+00:00" "time": "2024-11-11T15:43:04+00:00"
}, },
{
"name": "phpstan/phpstan-strict-rules",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "a4a6a08bd4a461e516b9c3b8fdbf0f1883b34158"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/a4a6a08bd4a461e516b9c3b8fdbf0f1883b34158",
"reference": "a4a6a08bd4a461e516b9c3b8fdbf0f1883b34158",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.0"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^9.6"
},
"type": "phpstan-extension",
"extra": {
"phpstan": {
"includes": [
"rules.neon"
]
}
},
"autoload": {
"psr-4": {
"PHPStan\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.0"
},
"time": "2024-10-26T16:04:33+00:00"
},
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "9.2.31", "version": "9.2.31",

View file

@ -18,6 +18,7 @@ use Friendica\Capabilities\ICanCreateResponses;
use Friendica\Capabilities\ICanHandleRequests; use Friendica\Capabilities\ICanHandleRequests;
use Friendica\Content\Nav; use Friendica\Content\Nav;
use Friendica\Core\Addon\AddonHelper; use Friendica\Core\Addon\AddonHelper;
use Friendica\Core\Addon\Capability\ICanLoadAddons;
use Friendica\Core\Config\Factory\Config; use Friendica\Core\Config\Factory\Config;
use Friendica\Core\Container; use Friendica\Core\Container;
use Friendica\Core\Hooks\HookEventBridge; use Friendica\Core\Hooks\HookEventBridge;
@ -277,15 +278,11 @@ class App
private function setupContainerForAddons(): void private function setupContainerForAddons(): void
{ {
/** @var AddonHelper $addonHelper */ /** @var ICanLoadAddons $addonLoader */
$addonHelper = $this->container->create(AddonHelper::class); $addonLoader = $this->container->create(ICanLoadAddons::class);
$addonHelper->loadAddons(); foreach ($addonLoader->getActiveAddonConfig('dependencies') as $name => $rule) {
$this->container->addRule($name, $rule);
foreach ($addonHelper->getEnabledAddons() as $addonId) {
foreach ($addonHelper->getAddonDependencyConfig($addonId) as $name => $rule) {
$this->container->addRule($name, $rule);
}
} }
} }

View file

@ -92,7 +92,5 @@ HELP;
CoreWorker::unclaimProcess($process); CoreWorker::unclaimProcess($process);
$this->processRepo->delete($process); $this->processRepo->delete($process);
return;
} }
} }

View file

@ -157,10 +157,10 @@ class Pager
'text' => $this->l10n->t('newer'), 'text' => $this->l10n->t('newer'),
'class' => 'previous' . ($this->getPage() == 1 ? ' disabled' : '') 'class' => 'previous' . ($this->getPage() == 1 ? ' disabled' : '')
], ],
'next' => [ 'next' => [
'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() + 1)), 'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() + 1)),
'text' => $this->l10n->t('older'), 'text' => $this->l10n->t('older'),
'class' => 'next' . ($displayedItemCount < $this->getItemsPerPage() ? ' disabled' : '') 'class' => 'next' . ($displayedItemCount < $this->getItemsPerPage() ? ' disabled' : '')
] ]
]; ];
@ -208,15 +208,15 @@ class Pager
'class' => $this->getPage() == 1 ? 'disabled' : '' 'class' => $this->getPage() == 1 ? 'disabled' : ''
]; ];
$numpages = (int) ceil($totalItemCount / $this->getItemsPerPage()); $numpages = $totalItemCount / $this->getItemsPerPage();
$numstart = 1; $numstart = 1;
$numstop = $numpages; $numstop = $numpages;
// Limit the number of displayed page number buttons. // Limit the number of displayed page number buttons.
if ($numpages > 8) { if ($numpages > 8) {
$numstart = ($this->getPage() > 4) ? ($this->getPage() - 4) : 1; $numstart = (($this->getPage() > 4) ? ($this->getPage() - 4) : 1);
$numstop = ($this->getPage() > ($numpages - 7)) ? $numpages : ($numstart + 8); $numstop = (($this->getPage() > ($numpages - 7)) ? $numpages : ($numstart + 8));
} }
$pages = []; $pages = [];
@ -237,9 +237,25 @@ class Pager
} }
} }
if (($totalItemCount % $this->getItemsPerPage()) != 0) {
if ($i == $this->getPage()) {
$pages[$i] = [
'url' => '#',
'text' => $i,
'class' => 'current active'
];
} else {
$pages[$i] = [
'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . $i),
'text' => $i,
'class' => 'n'
];
}
}
$data['pages'] = $pages; $data['pages'] = $pages;
$lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages); $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages);
$data['next'] = [ $data['next'] = [
'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() + 1)), 'url' => Strings::ensureQueryParameter($this->baseQueryString . '&page=' . ($this->getPage() + 1)),

View file

@ -55,7 +55,7 @@ class HTML
$xpath = new DOMXPath($doc); $xpath = new DOMXPath($doc);
/** @var \DOMNodeList<\DOMNode>|false $list */ /** @var \DOMNode[] $list */
$list = $xpath->query("//" . $tag); $list = $xpath->query("//" . $tag);
foreach ($list as $node) { foreach ($list as $node) {
$attr = []; $attr = [];
@ -1018,7 +1018,7 @@ class HTML
*/ */
public static function checkRelMeLink(DOMDocument $doc, UriInterface $meUrl): bool public static function checkRelMeLink(DOMDocument $doc, UriInterface $meUrl): bool
{ {
$xpath = new \DOMXPath($doc); $xpath = new \DOMXpath($doc);
// This expression checks that "me" is among the space-delimited values of the "rel" attribute. // This expression checks that "me" is among the space-delimited values of the "rel" attribute.
// And that the href attribute contains exactly the provided URL // And that the href attribute contains exactly the provided URL

View file

@ -9,8 +9,6 @@ declare(strict_types=1);
namespace Friendica\Core\Addon; namespace Friendica\Core\Addon;
use Friendica\Core\Addon\Exception\AddonInvalidConfigFileException;
/** /**
* Some functions to handle addons * Some functions to handle addons
*/ */
@ -68,17 +66,6 @@ interface AddonHelper
*/ */
public function getAddonInfo(string $addonId): AddonInfo; public function getAddonInfo(string $addonId): AddonInfo;
/**
* Returns a dependency config array for a given addon
*
* This will load a potential config-file from the static directory, like `addon/{addonId}/static/dependencies.config.php`
*
* @throws AddonInvalidConfigFileException If the config file doesn't return an array
*
* @return array the config as array or empty array if no config file was found
*/
public function getAddonDependencyConfig(string $addonId): array;
/** /**
* Checks if the provided addon is enabled * Checks if the provided addon is enabled
*/ */

View file

@ -9,7 +9,6 @@ declare(strict_types=1);
namespace Friendica\Core\Addon; namespace Friendica\Core\Addon;
use Friendica\Core\Addon\Exception\AddonInvalidConfigFileException;
use Friendica\Core\Addon\Exception\InvalidAddonException; use Friendica\Core\Addon\Exception\InvalidAddonException;
use Friendica\Core\Cache\Capability\ICanCache; use Friendica\Core\Cache\Capability\ICanCache;
use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Config\Capability\IManageConfigValues;
@ -271,34 +270,6 @@ final class AddonManagerHelper implements AddonHelper
return AddonInfo::fromString($addonId, $matches[0]); return AddonInfo::fromString($addonId, $matches[0]);
} }
/**
* Returns a dependency config array for a given addon
*
* This will load a potential config-file from the static directory, like `addon/{addonId}/static/dependencies.config.php`
*
* @throws AddonInvalidConfigFileException If the config file doesn't return an array
*
* @return array the config as array or empty array if no config file was found
*/
public function getAddonDependencyConfig(string $addonId): array
{
$addonId = Strings::sanitizeFilePathItem(trim($addonId));
$configFile = $this->getAddonPath() . '/' . $addonId . '/static/dependencies.config.php';
if (!file_exists($configFile)) {
return [];
}
$config = include($configFile);
if (!is_array($config)) {
throw new AddonInvalidConfigFileException('Error loading config file ' . $configFile);
}
return $config;
}
/** /**
* Checks if the provided addon is enabled * Checks if the provided addon is enabled
*/ */

View file

@ -9,16 +9,12 @@ namespace Friendica\Core\Addon\Capability;
/** /**
* Interface for loading Addons specific content * Interface for loading Addons specific content
*
* @deprecated 2025.02 Use implementation of `\Friendica\Core\Addon\AddonHelper` instead.
*/ */
interface ICanLoadAddons interface ICanLoadAddons
{ {
/** /**
* Returns a merged config array of all active addons for a given config-name * Returns a merged config array of all active addons for a given config-name
* *
* @deprecated 2025.02 Use `\Friendica\Core\Addon\AddonHelper::getAddonDependencyConfig()` instead.
*
* @param string $configName The config-name (config-file at the static directory, like 'hooks' => '{addon}/static/hooks.config.php) * @param string $configName The config-name (config-file at the static directory, like 'hooks' => '{addon}/static/hooks.config.php)
* *
* @return array the merged array * @return array the merged array

View file

@ -14,9 +14,6 @@ use Friendica\Core\Logger\Factory\LoggerFactory;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
/**
* @deprecated 2025.02 Use implementation of `\Friendica\Core\Addon\AddonHelper` instead.
*/
class AddonLoader implements ICanLoadAddons class AddonLoader implements ICanLoadAddons
{ {
const STATIC_PATH = 'static'; const STATIC_PATH = 'static';
@ -27,19 +24,13 @@ class AddonLoader implements ICanLoadAddons
public function __construct(string $basePath, IManageConfigValues $config) public function __construct(string $basePath, IManageConfigValues $config)
{ {
@trigger_error('Class `' . __CLASS__ . '` is deprecated since 2025.02 and will be removed after 5 months, use implementation of `Friendica\Core\Addon\AddonHelper` instead.', E_USER_DEPRECATED);
$this->basePath = $basePath; $this->basePath = $basePath;
$this->config = $config; $this->config = $config;
} }
/** /** {@inheritDoc} */
* @deprecated 2025.02 Use `\Friendica\Core\Addon\AddonHelper::getAddonDependencyConfig()` instead.
*/
public function getActiveAddonConfig(string $configName): array public function getActiveAddonConfig(string $configName): array
{ {
@trigger_error('Class `' . __CLASS__ . '` is deprecated since 2025.02 and will be removed after 5 months, use `\Friendica\Core\Addon\AddonHelper::getAddonDependencyConfig()` instead.', E_USER_DEPRECATED);
$addons = array_keys(array_filter($this->config->get('addons') ?? [])); $addons = array_keys(array_filter($this->config->get('addons') ?? []));
$returnConfig = []; $returnConfig = [];

View file

@ -7,13 +7,9 @@
namespace Friendica\Core\Hooks\Util; namespace Friendica\Core\Hooks\Util;
use Friendica\Core\Addon\Exception\AddonInvalidConfigFileException; use Friendica\Core\Addon\Capability\ICanLoadAddons;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\Hooks\Capability\ICanRegisterStrategies; use Friendica\Core\Hooks\Capability\ICanRegisterStrategies;
use Friendica\Core\Hooks\Exceptions\HookConfigException; use Friendica\Core\Hooks\Exceptions\HookConfigException;
use Friendica\Core\Logger\Factory\LoggerFactory;
use Friendica\Util\Strings;
use Psr\Log\LoggerInterface;
/** /**
* Manage all strategies.config.php files * Manage all strategies.config.php files
@ -28,15 +24,17 @@ class StrategiesFileManager
const STATIC_DIR = 'static'; const STATIC_DIR = 'static';
const CONFIG_NAME = 'strategies'; const CONFIG_NAME = 'strategies';
private IManageConfigValues $configuration; /** @var ICanLoadAddons */
protected array $config = []; protected $addonLoader;
/** @var array */
protected $config = [];
/** @var string */ /** @var string */
protected $basePath; protected $basePath;
public function __construct(string $basePath, IManageConfigValues $configuration) public function __construct(string $basePath, ICanLoadAddons $addonLoader)
{ {
$this->basePath = $basePath; $this->basePath = $basePath;
$this->configuration = $configuration; $this->addonLoader = $addonLoader;
} }
/** /**
@ -86,50 +84,6 @@ class StrategiesFileManager
/** /**
* @deprecated 2025.02 Providing strategies via addons is deprecated and will be removed in 5 months. * @deprecated 2025.02 Providing strategies via addons is deprecated and will be removed in 5 months.
*/ */
$this->config = array_merge_recursive($config, $this->getActiveAddonConfig()); $this->config = array_merge_recursive($config, $this->addonLoader->getActiveAddonConfig(static::CONFIG_NAME));
}
private function getActiveAddonConfig(): array
{
$addons = array_keys(array_filter($this->configuration->get('addons') ?? []));
$returnConfig = [];
foreach ($addons as $addon) {
$addonName = Strings::sanitizeFilePathItem(trim($addon));
$configFile = $this->basePath . '/addon/' . $addonName . '/' . static::STATIC_DIR . '/strategies.config.php';
if (!file_exists($configFile)) {
// Addon unmodified, skipping
continue;
}
$config = include $configFile;
if (!is_array($config)) {
throw new AddonInvalidConfigFileException('Error loading config file ' . $configFile);
}
foreach ($config as $classname => $rule) {
if ($classname === LoggerInterface::class) {
@trigger_error(sprintf(
'Providing a strategy for `%s` is deprecated since 2025.02 and will stop working in 5 months, please provide an implementation for `%s` via `dependency.config.php` and remove the `strategies.config.php` file in the `%s` addon.',
$classname,
LoggerFactory::class,
$addonName,
), \E_USER_DEPRECATED);
} else {
@trigger_error(sprintf(
'Providing strategies for `%s` via addons is deprecated since 2025.02 and will stop working in 5 months, please stop using this and remove the `strategies.config.php` file in the `%s` addon.',
$classname,
$addonName,
), \E_USER_DEPRECATED);
}
}
$returnConfig = array_merge_recursive($returnConfig, $config);
}
return $returnConfig;
} }
} }

View file

@ -28,7 +28,7 @@ abstract class AbstractLock implements ICanLock
*/ */
protected function hasAcquiredLock(string $key): bool protected function hasAcquiredLock(string $key): bool
{ {
return isset($this->acquiredLocks[$key]) && $this->acquiredLocks[$key] === true; return isset($this->acquireLock[$key]) && $this->acquiredLocks[$key] === true;
} }
/** /**

View file

@ -99,11 +99,8 @@ class Cache extends AbstractSessionHandler
} }
#[\ReturnTypeWillChange] #[\ReturnTypeWillChange]
/** public function gc($max_lifetime): bool
* @return int|false
*/
public function gc($max_lifetime)
{ {
return 0; // Cache does not support garbage collection, so we return 0 to indicate no action taken return true;
} }
} }

View file

@ -124,23 +124,13 @@ class Database extends AbstractSessionHandler
} }
#[\ReturnTypeWillChange] #[\ReturnTypeWillChange]
/** public function gc($max_lifetime): bool
* @return int|false
*/
public function gc($max_lifetime)
{ {
try { try {
$result = $this->dba->delete('session', ["`expire` < ?", time()]); return $this->dba->delete('session', ["`expire` < ?", time()]);
} catch (\Exception $exception) { } catch (\Exception $exception) {
$this->logger->warning('Cannot use garbage collector.', ['exception' => $exception]); $this->logger->warning('Cannot use garbage collector.', ['exception' => $exception]);
return false; return false;
} }
if ($result !== false) {
// TODO: DBA::delete() returns true, but we need to return the number of deleted rows as interger
$result = 0;
}
return $result;
} }
} }

View file

@ -521,7 +521,7 @@ class Worker
} }
if ($sleeping) { if ($sleeping) {
DI::logger()->info('Cooldown ended.', ['max-load' => $load_cooldown, 'max-processes' => $processes_cooldown, 'load' => $load ?? [], 'called-by' => System::callstack(1)]); DI::logger()->info('Cooldown ended.', ['max-load' => $load_cooldown, 'max-processes' => $processes_cooldown, 'load' => $load, 'called-by' => System::callstack(1)]);
} }
} }

View file

@ -193,7 +193,7 @@ class PostUpdate
Contact::removeDuplicates($contact['nurl'], $contact['uid']); Contact::removeDuplicates($contact['nurl'], $contact['uid']);
} }
DBA::close($contacts); DBA::close($contact);
DI::keyValue()->set('post_update_version', 1322); DI::keyValue()->set('post_update_version', 1322);
DI::logger()->info('Done'); DI::logger()->info('Done');

View file

@ -136,9 +136,7 @@ class Index extends BaseSearch
// Tags don't look like an URL and the fulltext search does only work with natural words // Tags don't look like an URL and the fulltext search does only work with natural words
if (parse_url($search, PHP_URL_SCHEME) && parse_url($search, PHP_URL_HOST)) { if (parse_url($search, PHP_URL_SCHEME) && parse_url($search, PHP_URL_HOST)) {
$this->logger->info('Skipping tag and fulltext search since the search looks like a URL.', ['q' => $search]); $this->logger->info('Skipping tag and fulltext search since the search looks like a URL.', ['q' => $search]);
$o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), [ DI::sysmsg()->addNotice(DI::l10n()->t('No results.'));
'$title' => DI::l10n()->t('No results.')
]);
return $o; return $o;
} }
@ -188,9 +186,7 @@ class Index extends BaseSearch
if (empty($items)) { if (empty($items)) {
if (empty($last_uriid)) { if (empty($last_uriid)) {
$o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), [ DI::sysmsg()->addNotice(DI::l10n()->t('No results.'));
'$title' => DI::l10n()->t('No results.')
]);
} }
return $o; return $o;
} }

View file

@ -234,11 +234,11 @@ class Display extends BaseSettings
$update_content = $this->pConfig->get($uid, 'system', 'update_content') ?? false; $update_content = $this->pConfig->get($uid, 'system', 'update_content') ?? false;
$enable_smile = !$this->pConfig->get($uid, 'system', 'no_smilies', false); $enable_smile = !$this->pConfig->get($uid, 'system', 'no_smilies', false);
$infinite_scroll = $this->pConfig->get($uid, 'system', 'infinite_scroll', true); $infinite_scroll = $this->pConfig->get($uid, 'system', 'infinite_scroll', false);
$enable_smart_threading = !$this->pConfig->get($uid, 'system', 'no_smart_threading', false); $enable_smart_threading = !$this->pConfig->get($uid, 'system', 'no_smart_threading', false);
$enable_dislike = !$this->pConfig->get($uid, 'system', 'hide_dislike', false); $enable_dislike = !$this->pConfig->get($uid, 'system', 'hide_dislike', false);
$display_resharer = $this->pConfig->get($uid, 'system', 'display_resharer', false); $display_resharer = $this->pConfig->get($uid, 'system', 'display_resharer', false);
$stay_local = $this->pConfig->get($uid, 'system', 'stay_local', true); $stay_local = $this->pConfig->get($uid, 'system', 'stay_local', false);
$show_page_drop = $this->pConfig->get($uid, 'system', 'show_page_drop', true); $show_page_drop = $this->pConfig->get($uid, 'system', 'show_page_drop', true);
$display_eventlist = $this->pConfig->get($uid, 'system', 'display_eventlist', true); $display_eventlist = $this->pConfig->get($uid, 'system', 'display_eventlist', true);

View file

@ -8,7 +8,7 @@
namespace Friendica\Network; namespace Friendica\Network;
use DOMDocument; use DOMDocument;
use DOMXPath; use DomXPath;
use Exception; use Exception;
use Friendica\Content\Text\HTML; use Friendica\Content\Text\HTML;
use Friendica\Core\Hook; use Friendica\Core\Hook;
@ -1273,7 +1273,7 @@ class Probe
return []; return [];
} }
$xpath = new DOMXPath($doc); $xpath = new DomXPath($doc);
$vcards = $xpath->query("//div[contains(concat(' ', @class, ' '), ' vcard ')]"); $vcards = $xpath->query("//div[contains(concat(' ', @class, ' '), ' vcard ')]");
if (!is_object($vcards)) { if (!is_object($vcards)) {

View file

@ -48,8 +48,8 @@ class Image
public function __construct(string $data, string $type = '', string $filename = '', bool $imagick = true) public function __construct(string $data, string $type = '', string $filename = '', bool $imagick = true)
{ {
$this->filename = $filename; $this->filename = $filename;
$type = Images::addMimeTypeByDataIfInvalid($type, $data); $type = Images::addMimeTypeByDataIfInvalid($type, $data);
$type = Images::addMimeTypeByExtensionIfInvalid($type, $filename); $type = Images::addMimeTypeByExtensionIfInvalid($type, $filename);
if (Images::isSupportedMimeType($type)) { if (Images::isSupportedMimeType($type)) {
$this->originType = $this->outputType = Images::getImageTypeByMimeType($type); $this->originType = $this->outputType = Images::getImageTypeByMimeType($type);
@ -108,7 +108,7 @@ class Image
private function isAnimatedWebP(string $data) private function isAnimatedWebP(string $data)
{ {
$header_format = 'A4Riff/I1Filesize/A4Webp/A4Vp/A74Chunk'; $header_format = 'A4Riff/I1Filesize/A4Webp/A4Vp/A74Chunk';
$header = @unpack($header_format, $data); $header = @unpack($header_format, $data);
if (!isset($header['Riff']) || strtoupper($header['Riff']) !== 'RIFF') { if (!isset($header['Riff']) || strtoupper($header['Riff']) !== 'RIFF') {
return false; return false;
@ -348,7 +348,7 @@ class Image
return false; return false;
} }
$width = $this->getWidth(); $width = $this->getWidth();
$height = $this->getHeight(); $height = $this->getHeight();
$scale = Images::getScalingDimensions($width, $height, $max); $scale = Images::getScalingDimensions($width, $height, $max);
@ -363,11 +363,12 @@ class Image
* Rotates image * Rotates image
* *
* @param integer $degrees degrees to rotate image * @param integer $degrees degrees to rotate image
* @return mixed
*/ */
public function rotate(int $degrees): void public function rotate(int $degrees)
{ {
if (!$this->isValid()) { if (!$this->isValid()) {
return; return false;
} }
if ($this->isImagick()) { if ($this->isImagick()) {
@ -392,11 +393,12 @@ class Image
* *
* @param boolean $horiz optional, default true * @param boolean $horiz optional, default true
* @param boolean $vert optional, default false * @param boolean $vert optional, default false
* @return mixed
*/ */
public function flip(bool $horiz = true, bool $vert = false): void public function flip(bool $horiz = true, bool $vert = false)
{ {
if (!$this->isValid()) { if (!$this->isValid()) {
return; return false;
} }
if ($this->isImagick()) { if ($this->isImagick()) {
@ -412,8 +414,8 @@ class Image
return; return;
} }
$w = imagesx($this->image); $w = imagesx($this->image);
$h = imagesy($this->image); $h = imagesy($this->image);
$flipped = imagecreate($w, $h); $flipped = imagecreate($w, $h);
if ($horiz) { if ($horiz) {
for ($x = 0; $x < $w; $x++) { for ($x = 0; $x < $w; $x++) {
@ -521,7 +523,7 @@ class Image
return false; return false;
} }
$width = $this->getWidth(); $width = $this->getWidth();
$height = $this->getHeight(); $height = $this->getHeight();
if ((!$width) || (!$height)) { if ((!$width) || (!$height)) {
@ -530,22 +532,22 @@ class Image
if ($width < $min && $height < $min) { if ($width < $min && $height < $min) {
if ($width > $height) { if ($width > $height) {
$dest_width = $min; $dest_width = $min;
$dest_height = intval(($height * $min) / $width); $dest_height = intval(($height * $min) / $width);
} else { } else {
$dest_width = intval(($width * $min) / $height); $dest_width = intval(($width * $min) / $height);
$dest_height = $min; $dest_height = $min;
} }
} else { } else {
if ($width < $min) { if ($width < $min) {
$dest_width = $min; $dest_width = $min;
$dest_height = intval(($height * $min) / $width); $dest_height = intval(($height * $min) / $width);
} else { } else {
if ($height < $min) { if ($height < $min) {
$dest_width = intval(($width * $min) / $height); $dest_width = intval(($width * $min) / $height);
$dest_height = $min; $dest_height = $min;
} else { } else {
$dest_width = $width; $dest_width = $width;
$dest_height = $height; $dest_height = $height;
} }
} }
@ -620,7 +622,7 @@ class Image
imagedestroy($this->image); imagedestroy($this->image);
} }
$this->image = $dest; $this->image = $dest;
$this->width = imagesx($this->image); $this->width = imagesx($this->image);
$this->height = imagesy($this->image); $this->height = imagesy($this->image);
} }
@ -797,9 +799,9 @@ class Image
} }
$row[] = [$colors['r'], $colors['g'], $colors['b']]; $row[] = [$colors['r'], $colors['g'], $colors['b']];
} else { } else {
$index = imagecolorat($image->image, $x, $y); $index = imagecolorat($image->image, $x, $y);
$colors = @imagecolorsforindex($image->image, $index); $colors = @imagecolorsforindex($image->image, $index);
$row[] = [$colors['red'], $colors['green'], $colors['blue']]; $row[] = [$colors['red'], $colors['green'], $colors['blue']];
} }
} }
$pixels[] = $row; $pixels[] = $row;
@ -828,7 +830,7 @@ class Image
if ($this->isImagick()) { if ($this->isImagick()) {
$this->image = new Imagick(); $this->image = new Imagick();
$draw = new ImagickDraw(); $draw = new ImagickDraw();
$this->image->newImage($scaled['width'], $scaled['height'], '', 'png'); $this->image->newImage($scaled['width'], $scaled['height'], '', 'png');
} else { } else {
$this->image = imagecreatetruecolor($scaled['width'], $scaled['height']); $this->image = imagecreatetruecolor($scaled['width'], $scaled['height']);
@ -836,7 +838,7 @@ class Image
for ($y = 0; $y < $scaled['height']; ++$y) { for ($y = 0; $y < $scaled['height']; ++$y) {
for ($x = 0; $x < $scaled['width']; ++$x) { for ($x = 0; $x < $scaled['width']; ++$x) {
list($r, $g, $b) = $pixels[$y][$x]; [$r, $g, $b] = $pixels[$y][$x];
if ($draw !== null) { if ($draw !== null) {
$draw->setFillColor("rgb($r, $g, $b)"); $draw->setFillColor("rgb($r, $g, $b)");
$draw->point($x, $y); $draw->point($x, $y);

View file

@ -592,7 +592,7 @@ class HTTPSignature
return []; return [];
} }
$sig_block = self::parseSigheader($http_headers['HTTP_SIGNATURE']); $sig_block = self::parseSigHeader($http_headers['HTTP_SIGNATURE']);
if (empty($sig_block['keyId'])) { if (empty($sig_block['keyId'])) {
DI::logger()->debug('No keyId', ['sig_block' => $sig_block]); DI::logger()->debug('No keyId', ['sig_block' => $sig_block]);
@ -652,7 +652,7 @@ class HTTPSignature
} }
} }
$sig_block = self::parseSigheader($http_headers['HTTP_SIGNATURE']); $sig_block = self::parseSigHeader($http_headers['HTTP_SIGNATURE']);
// Add fields from the signature block to the header. See issue 8845 // Add fields from the signature block to the header. See issue 8845
if (!empty($sig_block['created']) && empty($headers['(created)'])) { if (!empty($sig_block['created']) && empty($headers['(created)'])) {

View file

@ -39,6 +39,7 @@ return (function(string $basepath, array $getVars, array $serverVars, array $coo
'instanceOf' => \Friendica\Core\Addon\Model\AddonLoader::class, 'instanceOf' => \Friendica\Core\Addon\Model\AddonLoader::class,
'constructParams' => [ 'constructParams' => [
$basepath, $basepath,
[Dice::INSTANCE => Dice::SELF],
], ],
], ],
\Friendica\Core\Addon\AddonHelper::class => [ \Friendica\Core\Addon\AddonHelper::class => [

View file

@ -12,7 +12,6 @@ namespace Friendica\Test\Unit\Core\Addon;
use Exception; use Exception;
use Friendica\Core\Addon\AddonInfo; use Friendica\Core\Addon\AddonInfo;
use Friendica\Core\Addon\AddonManagerHelper; use Friendica\Core\Addon\AddonManagerHelper;
use Friendica\Core\Addon\Exception\AddonInvalidConfigFileException;
use Friendica\Core\Addon\Exception\InvalidAddonException; use Friendica\Core\Addon\Exception\InvalidAddonException;
use Friendica\Core\Cache\Capability\ICanCache; use Friendica\Core\Cache\Capability\ICanCache;
use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Config\Capability\IManageConfigValues;
@ -82,76 +81,6 @@ class AddonManagerHelperTest extends TestCase
$addonManagerHelper->getAddonInfo('helloaddon'); $addonManagerHelper->getAddonInfo('helloaddon');
} }
public function testGetAddonDependencyConfigReturnsArray(): void
{
$root = vfsStream::setup(__FUNCTION__ . '_addons', 0777, [
'helloaddon' => [
'static' => [
'dependencies.config.php' => <<<PHP
<?php
return [
'foo' => 'bar',
];
PHP,
],
]
]);
$addonManagerHelper = new AddonManagerHelper(
$root->url(),
$this->createStub(Database::class),
$this->createStub(IManageConfigValues::class),
$this->createStub(ICanCache::class),
$this->createStub(LoggerInterface::class),
$this->createStub(Profiler::class)
);
$this->assertSame(['foo' => 'bar'], $addonManagerHelper->getAddonDependencyConfig('helloaddon'));
}
public function testGetAddonDependencyConfigWithoutConfigFileReturnsEmptyArray(): void
{
$root = vfsStream::setup(__FUNCTION__ . '_addons', 0777, [
'helloaddon' => []
]);
$addonManagerHelper = new AddonManagerHelper(
$root->url(),
$this->createStub(Database::class),
$this->createStub(IManageConfigValues::class),
$this->createStub(ICanCache::class),
$this->createStub(LoggerInterface::class),
$this->createStub(Profiler::class)
);
$this->assertSame([], $addonManagerHelper->getAddonDependencyConfig('helloaddon'));
}
public function testGetAddonDependencyConfigWithoutReturningAnArrayThrowsException(): void
{
$root = vfsStream::setup(__FUNCTION__ . '_addons', 0777, [
'helloaddon' => [
'static' => [
'dependencies.config.php' => '<?php return null;',
],
]
]);
$addonManagerHelper = new AddonManagerHelper(
$root->url(),
$this->createStub(Database::class),
$this->createStub(IManageConfigValues::class),
$this->createStub(ICanCache::class),
$this->createStub(LoggerInterface::class),
$this->createStub(Profiler::class)
);
$this->expectException(AddonInvalidConfigFileException::class);
$this->expectExceptionMessageMatches('#Error loading config file .+/helloaddon/static/dependencies\.config\.php#');
$addonManagerHelper->getAddonDependencyConfig('helloaddon');
}
public function testEnabledAddons(): void public function testEnabledAddons(): void
{ {
$config = $this->createStub(IManageConfigValues::class); $config = $this->createStub(IManageConfigValues::class);

View file

@ -7,7 +7,7 @@
namespace Friendica\Test\src\Core\Hooks\Util; namespace Friendica\Test\src\Core\Hooks\Util;
use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Addon\Capability\ICanLoadAddons;
use Friendica\Core\Hooks\Capability\ICanRegisterStrategies; use Friendica\Core\Hooks\Capability\ICanRegisterStrategies;
use Friendica\Core\Hooks\Exceptions\HookConfigException; use Friendica\Core\Hooks\Exceptions\HookConfigException;
use Friendica\Core\Hooks\Util\StrategiesFileManager; use Friendica\Core\Hooks\Util\StrategiesFileManager;
@ -33,61 +33,49 @@ class StrategiesFileManagerTest extends MockedTestCase
return [ return [
'normal' => [ 'normal' => [
'content' => <<<EOF 'content' => <<<EOF
<?php <?php
return [ return [
\Psr\Log\LoggerInterface::class => [ \Psr\Log\LoggerInterface::class => [
\Psr\Log\NullLogger::class => [''], \Psr\Log\NullLogger::class => [''],
], ],
]; ];
EOF, EOF,
'addonContent' => <<<EOF 'addonsArray' => [],
<?php
return [];
EOF,
'assertStrategies' => [ 'assertStrategies' => [
[LoggerInterface::class, NullLogger::class, ''], [LoggerInterface::class, NullLogger::class, ''],
], ],
], ],
'normalWithString' => [ 'normalWithString' => [
'content' => <<<EOF 'content' => <<<EOF
<?php <?php
return [ return [
\Psr\Log\LoggerInterface::class => [ \Psr\Log\LoggerInterface::class => [
\Psr\Log\NullLogger::class => '', \Psr\Log\NullLogger::class => '',
], ],
]; ];
EOF, EOF,
'addonContent' => <<<EOF 'addonsArray' => [],
<?php
return [];
EOF,
'assertStrategies' => [ 'assertStrategies' => [
[LoggerInterface::class, NullLogger::class, ''], [LoggerInterface::class, NullLogger::class, ''],
], ],
], ],
'withAddons' => [ 'withAddons' => [
'content' => <<<EOF 'content' => <<<EOF
<?php <?php
return [ return [
\Psr\Log\LoggerInterface::class => [ \Psr\Log\LoggerInterface::class => [
\Psr\Log\NullLogger::class => [''], \Psr\Log\NullLogger::class => [''],
], ],
]; ];
EOF, EOF,
'addonContent' => <<<EOF 'addonsArray' => [
<?php \Psr\Log\LoggerInterface::class => [
\Psr\Log\NullLogger::class => ['null'],
return [ ],
\Psr\Log\LoggerInterface::class => [ ],
\Psr\Log\NullLogger::class => ['null'],
],
];
EOF,
'assertStrategies' => [ 'assertStrategies' => [
[LoggerInterface::class, NullLogger::class, ''], [LoggerInterface::class, NullLogger::class, ''],
[LoggerInterface::class, NullLogger::class, 'null'], [LoggerInterface::class, NullLogger::class, 'null'],
@ -95,23 +83,19 @@ class StrategiesFileManagerTest extends MockedTestCase
], ],
'withAddonsWithString' => [ 'withAddonsWithString' => [
'content' => <<<EOF 'content' => <<<EOF
<?php <?php
return [ return [
\Psr\Log\LoggerInterface::class => [ \Psr\Log\LoggerInterface::class => [
\Psr\Log\NullLogger::class => [''], \Psr\Log\NullLogger::class => [''],
], ],
]; ];
EOF, EOF,
'addonContent' => <<<EOF 'addonsArray' => [
<?php \Psr\Log\LoggerInterface::class => [
\Psr\Log\NullLogger::class => 'null',
return [ ],
\Psr\Log\LoggerInterface::class => [ ],
\Psr\Log\NullLogger::class => ['null'],
],
];
EOF,
'assertStrategies' => [ 'assertStrategies' => [
[LoggerInterface::class, NullLogger::class, ''], [LoggerInterface::class, NullLogger::class, ''],
[LoggerInterface::class, NullLogger::class, 'null'], [LoggerInterface::class, NullLogger::class, 'null'],
@ -120,23 +104,19 @@ class StrategiesFileManagerTest extends MockedTestCase
// This should work because unique name convention is part of the instance manager logic, not of the file-infrastructure layer // This should work because unique name convention is part of the instance manager logic, not of the file-infrastructure layer
'withAddonsDoubleNamed' => [ 'withAddonsDoubleNamed' => [
'content' => <<<EOF 'content' => <<<EOF
<?php <?php
return [ return [
\Psr\Log\LoggerInterface::class => [ \Psr\Log\LoggerInterface::class => [
\Psr\Log\NullLogger::class => [''], \Psr\Log\NullLogger::class => [''],
], ],
]; ];
EOF, EOF,
'addonContent' => <<<EOF 'addonsArray' => [
<?php \Psr\Log\LoggerInterface::class => [
\Psr\Log\NullLogger::class => [''],
return [ ],
\Psr\Log\LoggerInterface::class => [ ],
\Psr\Log\NullLogger::class => [''],
],
];
EOF,
'assertStrategies' => [ 'assertStrategies' => [
[LoggerInterface::class, NullLogger::class, ''], [LoggerInterface::class, NullLogger::class, ''],
[LoggerInterface::class, NullLogger::class, ''], [LoggerInterface::class, NullLogger::class, ''],
@ -148,20 +128,16 @@ class StrategiesFileManagerTest extends MockedTestCase
/** /**
* @dataProvider dataHooks * @dataProvider dataHooks
*/ */
public function testSetupHooks(string $content, string $addonContent, array $assertStrategies) public function testSetupHooks(string $content, array $addonsArray, array $assertStrategies)
{ {
vfsStream::newFile(StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php') vfsStream::newFile(StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php')
->withContent($content) ->withContent($content)
->at($this->root); ->at($this->root);
vfsStream::newFile('addon/testaddon/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php') $addonLoader = \Mockery::mock(ICanLoadAddons::class);
->withContent($addonContent) $addonLoader->shouldReceive('getActiveAddonConfig')->andReturn($addonsArray)->once();
->at($this->root);
$config = \Mockery::mock(IManageConfigValues::class); $hookFileManager = new StrategiesFileManager($this->root->url(), $addonLoader);
$config->shouldReceive('get')->andReturn(['testaddon' => ['admin' => false]])->once();
$hookFileManager = new StrategiesFileManager($this->root->url(), $config);
$instanceManager = \Mockery::mock(ICanRegisterStrategies::class); $instanceManager = \Mockery::mock(ICanRegisterStrategies::class);
foreach ($assertStrategies as $assertStrategy) { foreach ($assertStrategies as $assertStrategy) {
@ -179,15 +155,13 @@ class StrategiesFileManagerTest extends MockedTestCase
*/ */
public function testMissingStrategiesFile() public function testMissingStrategiesFile()
{ {
$config = \Mockery::mock(IManageConfigValues::class); $addonLoader = \Mockery::mock(ICanLoadAddons::class);
$instanceManager = \Mockery::mock(ICanRegisterStrategies::class); $instanceManager = \Mockery::mock(ICanRegisterStrategies::class);
$hookFileManager = new StrategiesFileManager($this->root->url(), $config); $hookFileManager = new StrategiesFileManager($this->root->url(), $addonLoader);
self::expectException(HookConfigException::class); self::expectException(HookConfigException::class);
self::expectExceptionMessage(sprintf( self::expectExceptionMessage(sprintf('config file %s does not exist.',
'config file %s does not exist.', $this->root->url() . '/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php'));
$this->root->url() . '/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php'
));
$hookFileManager->loadConfig(); $hookFileManager->loadConfig();
} }
@ -197,19 +171,17 @@ class StrategiesFileManagerTest extends MockedTestCase
*/ */
public function testWrongStrategiesFile() public function testWrongStrategiesFile()
{ {
$config = \Mockery::mock(IManageConfigValues::class); $addonLoader = \Mockery::mock(ICanLoadAddons::class);
$instanceManager = \Mockery::mock(ICanRegisterStrategies::class); $instanceManager = \Mockery::mock(ICanRegisterStrategies::class);
$hookFileManager = new StrategiesFileManager($this->root->url(), $config); $hookFileManager = new StrategiesFileManager($this->root->url(), $addonLoader);
vfsStream::newFile(StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php') vfsStream::newFile(StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php')
->withContent("<?php return 'WRONG_CONTENT';") ->withContent("<?php return 'WRONG_CONTENT';")
->at($this->root); ->at($this->root);
self::expectException(HookConfigException::class); self::expectException(HookConfigException::class);
self::expectExceptionMessage(sprintf( self::expectExceptionMessage(sprintf('Error loading config file %s.',
'Error loading config file %s.', $this->root->url() . '/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php'));
$this->root->url() . '/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php'
));
$hookFileManager->loadConfig(); $hookFileManager->loadConfig();
} }

View file

@ -806,7 +806,6 @@ summary.wall-item-summary {
/* Margin to create space between the icon and the text */ /* Margin to create space between the icon and the text */
.notif-image { .notif-image {
margin-right: 10px; margin-right: 10px;
}
/* Styles to ensure the text wraps properly after 70 characters */ /* Styles to ensure the text wraps properly after 70 characters */
.notif-text { .notif-text {
@ -814,16 +813,3 @@ summary.wall-item-summary {
max-width: 70ch; max-width: 70ch;
overflow-wrap: break-word; overflow-wrap: break-word;
} }
/* Add alt tag indicators to the relevant images */
a:has(img.has-alt-description)::after {
background: lightgray;
border-radius: 4px;
color: black;
content: "ALT";
font-weight: bold;
padding: 3px 8px;
position: absolute;
bottom: 10px;
right: 10px;
}

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 2025.02-dev\n" "Project-Id-Version: 2025.02-dev\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-06-21 13:03+0200\n" "POT-Creation-Date: 2025-06-14 15:06+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -646,7 +646,7 @@ msgstr ""
msgid "Map" msgid "Map"
msgstr "" msgstr ""
#: src/App.php:454 #: src/App.php:451
msgid "Apologies but the website is unavailable at the moment." msgid "Apologies but the website is unavailable at the moment."
msgstr "" msgstr ""
@ -2227,11 +2227,11 @@ msgstr ""
msgid "prev" msgid "prev"
msgstr "" msgstr ""
#: src/Content/Pager.php:246 src/Module/Calendar/Show.php:122 #: src/Content/Pager.php:262 src/Module/Calendar/Show.php:122
msgid "next" msgid "next"
msgstr "" msgstr ""
#: src/Content/Pager.php:251 #: src/Content/Pager.php:267
msgid "last" msgid "last"
msgstr "" msgstr ""
@ -6108,7 +6108,7 @@ msgstr ""
msgid "Search your contacts" msgid "Search your contacts"
msgstr "" msgstr ""
#: src/Module/Contact.php:441 src/Module/Search/Index.php:206 #: src/Module/Contact.php:441 src/Module/Search/Index.php:202
#, php-format #, php-format
msgid "Results for: %s" msgid "Results for: %s"
msgstr "" msgstr ""
@ -6698,8 +6698,8 @@ msgid "Unable to unfollow this contact, please contact your administrator"
msgstr "" msgstr ""
#: src/Module/Conversation/Channel.php:125 #: src/Module/Conversation/Channel.php:125
#: src/Module/Conversation/Community.php:114 src/Module/Search/Index.php:140 #: src/Module/Conversation/Community.php:114 src/Module/Search/Index.php:139
#: src/Module/Search/Index.php:192 #: src/Module/Search/Index.php:189
msgid "No results." msgid "No results."
msgstr "" msgstr ""
@ -8901,7 +8901,7 @@ msgstr ""
msgid "Only one search per minute is permitted for not logged in users." msgid "Only one search per minute is permitted for not logged in users."
msgstr "" msgstr ""
#: src/Module/Search/Index.php:204 #: src/Module/Search/Index.php:200
#, php-format #, php-format
msgid "Items tagged with: %s" msgid "Items tagged with: %s"
msgstr "" msgstr ""

View file

@ -174,8 +174,9 @@ $(document).ready(function () {
// temporary workaround to avoid 'undefined' being displayed (issue #9789) // temporary workaround to avoid 'undefined' being displayed (issue #9789)
// https://github.com/friendica/friendica/issues/9789 // https://github.com/friendica/friendica/issues/9789
// TODO: find a way to localize this string
if (typeof searchText === "undefined") { if (typeof searchText === "undefined") {
searchText = ""; searchText = "No results";
} }
// insert the plain text in a <h4> heading and give it a class // insert the plain text in a <h4> heading and give it a class
var newText = '<h4 class="search-heading">' + searchText + "</h4>"; var newText = '<h4 class="search-heading">' + searchText + "</h4>";

View file

@ -164,7 +164,7 @@
<form class="navbar-form" role="search" method="get" action="{{$nav.search.0}}"> <form class="navbar-form" role="search" method="get" action="{{$nav.search.0}}">
<div class="form-group form-group-search"> <div class="form-group form-group-search">
<input accesskey="s" id="nav-search-input-field" class="form-control form-search" <input accesskey="s" id="nav-search-input-field" class="form-control form-search"
type="text" name="q" data-toggle="tooltip" data-viewport="#topbar-first" title="{{$search_hint}}" type="text" name="q" data-toggle="tooltip" title="{{$search_hint}}"
placeholder="{{$nav.search.1}}"> placeholder="{{$nav.search.1}}">
<button class="btn btn-default btn-sm form-button-search" <button class="btn btn-default btn-sm form-button-search"
type="submit">{{$nav.search.1}}</button> type="submit">{{$nav.search.1}}</button>