Compare commits
32 commits
04cb22a134
...
548c0b9602
Author | SHA1 | Date | |
---|---|---|---|
|
548c0b9602 | ||
|
9c32e22530 |
||
|
67330cb497 |
||
|
5d481426cd | ||
|
e214aabf31 | ||
|
5805d21688 |
||
|
f97abc11f0 | ||
|
14e4fb8334 |
||
|
ccd3ebbd11 |
||
|
e64bd7178c |
||
|
7fd6272a32 | ||
|
a06a816c08 | ||
|
6633d1b491 | ||
|
326f7d677b | ||
|
696972629d | ||
|
df7c3a6566 | ||
|
95b3322731 | ||
|
07db85b99d | ||
|
4bac217806 | ||
|
4f4f7f8a64 | ||
|
53191caeac | ||
|
603f96b403 | ||
|
b8b7d3cd15 | ||
|
f8b1770efa | ||
|
3f1082400a | ||
|
f9d695d80d | ||
|
d00fc89a59 | ||
|
59d22b87af | ||
|
9870fbf84f | ||
|
1428085c4b | ||
|
203e9642d5 | ||
|
694893e2bb |
29 changed files with 442 additions and 156 deletions
|
@ -2,6 +2,9 @@
|
||||||
#
|
#
|
||||||
# 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
|
||||||
|
|
||||||
|
@ -23,6 +26,10 @@ 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
|
||||||
|
@ -38,3 +45,17 @@ 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
|
||||||
|
|
|
@ -157,6 +157,7 @@
|
||||||
"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
50
composer.lock
generated
|
@ -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": "897b878d6db24b9a6437bd9f971478be",
|
"content-hash": "e93a8ac7e31cf3e5e0ca76134e5ffa0b",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "asika/simple-console",
|
"name": "asika/simple-console",
|
||||||
|
@ -5849,6 +5849,54 @@
|
||||||
],
|
],
|
||||||
"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",
|
||||||
|
|
13
src/App.php
13
src/App.php
|
@ -18,7 +18,6 @@ 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;
|
||||||
|
@ -278,11 +277,15 @@ class App
|
||||||
|
|
||||||
private function setupContainerForAddons(): void
|
private function setupContainerForAddons(): void
|
||||||
{
|
{
|
||||||
/** @var ICanLoadAddons $addonLoader */
|
/** @var AddonHelper $addonHelper */
|
||||||
$addonLoader = $this->container->create(ICanLoadAddons::class);
|
$addonHelper = $this->container->create(AddonHelper::class);
|
||||||
|
|
||||||
foreach ($addonLoader->getActiveAddonConfig('dependencies') as $name => $rule) {
|
$addonHelper->loadAddons();
|
||||||
$this->container->addRule($name, $rule);
|
|
||||||
|
foreach ($addonHelper->getEnabledAddons() as $addonId) {
|
||||||
|
foreach ($addonHelper->getAddonDependencyConfig($addonId) as $name => $rule) {
|
||||||
|
$this->container->addRule($name, $rule);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,5 +92,7 @@ HELP;
|
||||||
CoreWorker::unclaimProcess($process);
|
CoreWorker::unclaimProcess($process);
|
||||||
|
|
||||||
$this->processRepo->delete($process);
|
$this->processRepo->delete($process);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = $totalItemCount / $this->getItemsPerPage();
|
$numpages = (int) ceil($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,25 +237,9 @@ 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)),
|
||||||
|
|
|
@ -55,7 +55,7 @@ class HTML
|
||||||
|
|
||||||
$xpath = new DOMXPath($doc);
|
$xpath = new DOMXPath($doc);
|
||||||
|
|
||||||
/** @var \DOMNode[] $list */
|
/** @var \DOMNodeList<\DOMNode>|false $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
|
||||||
|
|
|
@ -9,6 +9,8 @@ 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
|
||||||
*/
|
*/
|
||||||
|
@ -66,6 +68,17 @@ 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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,6 +9,7 @@ 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;
|
||||||
|
@ -270,6 +271,34 @@ 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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,12 +9,16 @@ 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
|
||||||
|
|
|
@ -14,6 +14,9 @@ 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';
|
||||||
|
@ -24,13 +27,19 @@ 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 = [];
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,13 @@
|
||||||
|
|
||||||
namespace Friendica\Core\Hooks\Util;
|
namespace Friendica\Core\Hooks\Util;
|
||||||
|
|
||||||
use Friendica\Core\Addon\Capability\ICanLoadAddons;
|
use Friendica\Core\Addon\Exception\AddonInvalidConfigFileException;
|
||||||
|
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
|
||||||
|
@ -24,17 +28,15 @@ class StrategiesFileManager
|
||||||
const STATIC_DIR = 'static';
|
const STATIC_DIR = 'static';
|
||||||
const CONFIG_NAME = 'strategies';
|
const CONFIG_NAME = 'strategies';
|
||||||
|
|
||||||
/** @var ICanLoadAddons */
|
private IManageConfigValues $configuration;
|
||||||
protected $addonLoader;
|
protected array $config = [];
|
||||||
/** @var array */
|
|
||||||
protected $config = [];
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $basePath;
|
protected $basePath;
|
||||||
|
|
||||||
public function __construct(string $basePath, ICanLoadAddons $addonLoader)
|
public function __construct(string $basePath, IManageConfigValues $configuration)
|
||||||
{
|
{
|
||||||
$this->basePath = $basePath;
|
$this->basePath = $basePath;
|
||||||
$this->addonLoader = $addonLoader;
|
$this->configuration = $configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,6 +86,50 @@ 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->addonLoader->getActiveAddonConfig(static::CONFIG_NAME));
|
$this->config = array_merge_recursive($config, $this->getActiveAddonConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ abstract class AbstractLock implements ICanLock
|
||||||
*/
|
*/
|
||||||
protected function hasAcquiredLock(string $key): bool
|
protected function hasAcquiredLock(string $key): bool
|
||||||
{
|
{
|
||||||
return isset($this->acquireLock[$key]) && $this->acquiredLocks[$key] === true;
|
return isset($this->acquiredLocks[$key]) && $this->acquiredLocks[$key] === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -99,8 +99,11 @@ class Cache extends AbstractSessionHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function gc($max_lifetime): bool
|
/**
|
||||||
|
* @return int|false
|
||||||
|
*/
|
||||||
|
public function gc($max_lifetime)
|
||||||
{
|
{
|
||||||
return true;
|
return 0; // Cache does not support garbage collection, so we return 0 to indicate no action taken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,13 +124,23 @@ class Database extends AbstractSessionHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
#[\ReturnTypeWillChange]
|
#[\ReturnTypeWillChange]
|
||||||
public function gc($max_lifetime): bool
|
/**
|
||||||
|
* @return int|false
|
||||||
|
*/
|
||||||
|
public function gc($max_lifetime)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return $this->dba->delete('session', ["`expire` < ?", time()]);
|
$result = $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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -193,7 +193,7 @@ class PostUpdate
|
||||||
Contact::removeDuplicates($contact['nurl'], $contact['uid']);
|
Contact::removeDuplicates($contact['nurl'], $contact['uid']);
|
||||||
}
|
}
|
||||||
|
|
||||||
DBA::close($contact);
|
DBA::close($contacts);
|
||||||
DI::keyValue()->set('post_update_version', 1322);
|
DI::keyValue()->set('post_update_version', 1322);
|
||||||
|
|
||||||
DI::logger()->info('Done');
|
DI::logger()->info('Done');
|
||||||
|
|
|
@ -136,7 +136,9 @@ 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]);
|
||||||
DI::sysmsg()->addNotice(DI::l10n()->t('No results.'));
|
$o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), [
|
||||||
|
'$title' => DI::l10n()->t('No results.')
|
||||||
|
]);
|
||||||
return $o;
|
return $o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +188,9 @@ class Index extends BaseSearch
|
||||||
|
|
||||||
if (empty($items)) {
|
if (empty($items)) {
|
||||||
if (empty($last_uriid)) {
|
if (empty($last_uriid)) {
|
||||||
DI::sysmsg()->addNotice(DI::l10n()->t('No results.'));
|
$o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), [
|
||||||
|
'$title' => DI::l10n()->t('No results.')
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
return $o;
|
return $o;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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', false);
|
$infinite_scroll = $this->pConfig->get($uid, 'system', 'infinite_scroll', true);
|
||||||
$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', false);
|
$stay_local = $this->pConfig->get($uid, 'system', 'stay_local', true);
|
||||||
$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);
|
||||||
|
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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,12 +363,11 @@ 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)
|
public function rotate(int $degrees): void
|
||||||
{
|
{
|
||||||
if (!$this->isValid()) {
|
if (!$this->isValid()) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isImagick()) {
|
if ($this->isImagick()) {
|
||||||
|
@ -393,12 +392,11 @@ 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)
|
public function flip(bool $horiz = true, bool $vert = false): void
|
||||||
{
|
{
|
||||||
if (!$this->isValid()) {
|
if (!$this->isValid()) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isImagick()) {
|
if ($this->isImagick()) {
|
||||||
|
@ -414,8 +412,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++) {
|
||||||
|
@ -523,7 +521,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)) {
|
||||||
|
@ -532,22 +530,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -622,7 +620,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);
|
||||||
}
|
}
|
||||||
|
@ -799,9 +797,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;
|
||||||
|
@ -830,7 +828,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']);
|
||||||
|
@ -838,7 +836,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) {
|
||||||
[$r, $g, $b] = $pixels[$y][$x];
|
list($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);
|
||||||
|
|
|
@ -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)'])) {
|
||||||
|
|
|
@ -39,7 +39,6 @@ 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 => [
|
||||||
|
|
|
@ -12,6 +12,7 @@ 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;
|
||||||
|
@ -81,6 +82,76 @@ 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);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
namespace Friendica\Test\src\Core\Hooks\Util;
|
namespace Friendica\Test\src\Core\Hooks\Util;
|
||||||
|
|
||||||
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\Hooks\Util\StrategiesFileManager;
|
use Friendica\Core\Hooks\Util\StrategiesFileManager;
|
||||||
|
@ -33,49 +33,61 @@ 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,
|
||||||
'addonsArray' => [],
|
'addonContent' => <<<EOF
|
||||||
|
<?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,
|
||||||
'addonsArray' => [],
|
'addonContent' => <<<EOF
|
||||||
|
<?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,
|
||||||
'addonsArray' => [
|
'addonContent' => <<<EOF
|
||||||
\Psr\Log\LoggerInterface::class => [
|
<?php
|
||||||
\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'],
|
||||||
|
@ -83,19 +95,23 @@ EOF,
|
||||||
],
|
],
|
||||||
'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,
|
||||||
'addonsArray' => [
|
'addonContent' => <<<EOF
|
||||||
\Psr\Log\LoggerInterface::class => [
|
<?php
|
||||||
\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'],
|
||||||
|
@ -104,19 +120,23 @@ EOF,
|
||||||
// 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,
|
||||||
'addonsArray' => [
|
'addonContent' => <<<EOF
|
||||||
\Psr\Log\LoggerInterface::class => [
|
<?php
|
||||||
\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, ''],
|
||||||
|
@ -128,16 +148,20 @@ EOF,
|
||||||
/**
|
/**
|
||||||
* @dataProvider dataHooks
|
* @dataProvider dataHooks
|
||||||
*/
|
*/
|
||||||
public function testSetupHooks(string $content, array $addonsArray, array $assertStrategies)
|
public function testSetupHooks(string $content, string $addonContent, 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);
|
||||||
|
|
||||||
$addonLoader = \Mockery::mock(ICanLoadAddons::class);
|
vfsStream::newFile('addon/testaddon/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php')
|
||||||
$addonLoader->shouldReceive('getActiveAddonConfig')->andReturn($addonsArray)->once();
|
->withContent($addonContent)
|
||||||
|
->at($this->root);
|
||||||
|
|
||||||
$hookFileManager = new StrategiesFileManager($this->root->url(), $addonLoader);
|
$config = \Mockery::mock(IManageConfigValues::class);
|
||||||
|
$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) {
|
||||||
|
@ -155,13 +179,15 @@ EOF,
|
||||||
*/
|
*/
|
||||||
public function testMissingStrategiesFile()
|
public function testMissingStrategiesFile()
|
||||||
{
|
{
|
||||||
$addonLoader = \Mockery::mock(ICanLoadAddons::class);
|
$config = \Mockery::mock(IManageConfigValues::class);
|
||||||
$instanceManager = \Mockery::mock(ICanRegisterStrategies::class);
|
$instanceManager = \Mockery::mock(ICanRegisterStrategies::class);
|
||||||
$hookFileManager = new StrategiesFileManager($this->root->url(), $addonLoader);
|
$hookFileManager = new StrategiesFileManager($this->root->url(), $config);
|
||||||
|
|
||||||
self::expectException(HookConfigException::class);
|
self::expectException(HookConfigException::class);
|
||||||
self::expectExceptionMessage(sprintf('config file %s does not exist.',
|
self::expectExceptionMessage(sprintf(
|
||||||
$this->root->url() . '/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php'));
|
'config file %s does not exist.',
|
||||||
|
$this->root->url() . '/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php'
|
||||||
|
));
|
||||||
|
|
||||||
$hookFileManager->loadConfig();
|
$hookFileManager->loadConfig();
|
||||||
}
|
}
|
||||||
|
@ -171,17 +197,19 @@ EOF,
|
||||||
*/
|
*/
|
||||||
public function testWrongStrategiesFile()
|
public function testWrongStrategiesFile()
|
||||||
{
|
{
|
||||||
$addonLoader = \Mockery::mock(ICanLoadAddons::class);
|
$config = \Mockery::mock(IManageConfigValues::class);
|
||||||
$instanceManager = \Mockery::mock(ICanRegisterStrategies::class);
|
$instanceManager = \Mockery::mock(ICanRegisterStrategies::class);
|
||||||
$hookFileManager = new StrategiesFileManager($this->root->url(), $addonLoader);
|
$hookFileManager = new StrategiesFileManager($this->root->url(), $config);
|
||||||
|
|
||||||
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('Error loading config file %s.',
|
self::expectExceptionMessage(sprintf(
|
||||||
$this->root->url() . '/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php'));
|
'Error loading config file %s.',
|
||||||
|
$this->root->url() . '/' . StrategiesFileManager::STATIC_DIR . '/' . StrategiesFileManager::CONFIG_NAME . '.config.php'
|
||||||
|
));
|
||||||
|
|
||||||
$hookFileManager->loadConfig();
|
$hookFileManager->loadConfig();
|
||||||
}
|
}
|
||||||
|
|
|
@ -806,6 +806,7 @@ 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 {
|
||||||
|
@ -813,3 +814,16 @@ 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;
|
||||||
|
}
|
||||||
|
|
|
@ -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-14 15:06+0200\n"
|
"POT-Creation-Date: 2025-06-21 13:03+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:451
|
#: src/App.php:454
|
||||||
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:262 src/Module/Calendar/Show.php:122
|
#: src/Content/Pager.php:246 src/Module/Calendar/Show.php:122
|
||||||
msgid "next"
|
msgid "next"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/Content/Pager.php:267
|
#: src/Content/Pager.php:251
|
||||||
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:202
|
#: src/Module/Contact.php:441 src/Module/Search/Index.php:206
|
||||||
#, 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:139
|
#: src/Module/Conversation/Community.php:114 src/Module/Search/Index.php:140
|
||||||
#: src/Module/Search/Index.php:189
|
#: src/Module/Search/Index.php:192
|
||||||
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:200
|
#: src/Module/Search/Index.php:204
|
||||||
#, php-format
|
#, php-format
|
||||||
msgid "Items tagged with: %s"
|
msgid "Items tagged with: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -174,9 +174,8 @@ $(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 = "No results";
|
searchText = "";
|
||||||
}
|
}
|
||||||
// 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>";
|
||||||
|
|
|
@ -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" title="{{$search_hint}}"
|
type="text" name="q" data-toggle="tooltip" data-viewport="#topbar-first" 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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue