Compare commits
13 Commits
master
...
fix/remove
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
139ae8a88c | ||
|
|
c6b5d3e2d3 | ||
|
|
bbadda93bb | ||
|
|
b47c09be9c | ||
|
|
11c9e5a83b | ||
|
|
8c0a7fdf80 | ||
|
|
4db0114b2d | ||
|
|
fdee34c8cb | ||
|
|
91ba91a9d2 | ||
|
|
aa7461afb6 | ||
|
|
d6c8333f78 | ||
|
|
83cb75e3a4 | ||
|
|
0295da5b42 |
@@ -54,7 +54,8 @@ class BackendService {
|
||||
/** @var callable[] */
|
||||
private $configHandlerLoaders = [];
|
||||
|
||||
private $configHandlers = [];
|
||||
private array $configHandlers = [];
|
||||
private bool $eventSent = false;
|
||||
|
||||
public function __construct(
|
||||
protected readonly IAppConfig $appConfig,
|
||||
@@ -71,14 +72,14 @@ class BackendService {
|
||||
$this->backendProviders[] = $provider;
|
||||
}
|
||||
|
||||
private function callForRegistrations() {
|
||||
static $eventSent = false;
|
||||
if (!$eventSent) {
|
||||
private function callForRegistrations(): void {
|
||||
$instance = Server::get(self::class);
|
||||
if (!$instance->eventSent) {
|
||||
Server::get(IEventDispatcher::class)->dispatch(
|
||||
'OCA\\Files_External::loadAdditionalBackends',
|
||||
new GenericEvent()
|
||||
);
|
||||
$eventSent = true;
|
||||
$instance->eventSent = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class RestoreAllFiles extends Base {
|
||||
private const SCOPE_USER = 1;
|
||||
private const SCOPE_GROUPFOLDERS = 2;
|
||||
|
||||
private static array $SCOPE_MAP = [
|
||||
private const SCOPE_MAP = [
|
||||
'user' => self::SCOPE_USER,
|
||||
'groupfolders' => self::SCOPE_GROUPFOLDERS,
|
||||
'all' => self::SCOPE_ALL
|
||||
@@ -218,8 +218,8 @@ class RestoreAllFiles extends Base {
|
||||
}
|
||||
|
||||
protected function parseScope(string $scope): int {
|
||||
if (isset(self::$SCOPE_MAP[$scope])) {
|
||||
return self::$SCOPE_MAP[$scope];
|
||||
if (isset(self::SCOPE_MAP[$scope])) {
|
||||
return self::SCOPE_MAP[$scope];
|
||||
}
|
||||
|
||||
throw new InvalidOptionException("Invalid scope '$scope'");
|
||||
|
||||
@@ -60,7 +60,7 @@ class Storage {
|
||||
|
||||
private static $sourcePathAndUser = [];
|
||||
|
||||
private static $max_versions_per_interval = [
|
||||
private const MAX_VERSIONS_PER_INTERVAL = [
|
||||
//first 10sec, one version every 2sec
|
||||
1 => ['intervalEndsAfter' => 10, 'step' => 2],
|
||||
//next minute, one version every 10sec
|
||||
@@ -763,11 +763,11 @@ class Storage {
|
||||
$toDelete = []; // versions we want to delete
|
||||
|
||||
$interval = 1;
|
||||
$step = Storage::$max_versions_per_interval[$interval]['step'];
|
||||
if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] === -1) {
|
||||
$step = Storage::MAX_VERSIONS_PER_INTERVAL[$interval]['step'];
|
||||
if (Storage::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'] === -1) {
|
||||
$nextInterval = -1;
|
||||
} else {
|
||||
$nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
|
||||
$nextInterval = $time - Storage::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'];
|
||||
}
|
||||
|
||||
$firstVersion = reset($versions);
|
||||
@@ -797,12 +797,12 @@ class Storage {
|
||||
$newInterval = false; // version checked so we can move to the next one
|
||||
} else { // time to move on to the next interval
|
||||
$interval++;
|
||||
$step = Storage::$max_versions_per_interval[$interval]['step'];
|
||||
$step = Storage::MAX_VERSIONS_PER_INTERVAL[$interval]['step'];
|
||||
$nextVersion = $prevTimestamp - $step;
|
||||
if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] === -1) {
|
||||
if (Storage::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'] === -1) {
|
||||
$nextInterval = -1;
|
||||
} else {
|
||||
$nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
|
||||
$nextInterval = $time - Storage::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'];
|
||||
}
|
||||
$newInterval = true; // we changed the interval -> check same version with new interval
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use OCA\User_LDAP\Exceptions\NoMoreResults;
|
||||
use OCA\User_LDAP\Mapping\AbstractMapping;
|
||||
use OCA\User_LDAP\User\Manager;
|
||||
use OCA\User_LDAP\User\OfflineUser;
|
||||
use OCP\Cache\CappedMemoryCache;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\HintException;
|
||||
use OCP\IAppConfig;
|
||||
@@ -48,6 +49,7 @@ class Access extends LDAPUtility {
|
||||
protected $groupMapper;
|
||||
|
||||
private string $lastCookie = '';
|
||||
private CappedMemoryCache $intermediates;
|
||||
|
||||
public function __construct(
|
||||
ILDAPWrapper $ldap,
|
||||
@@ -61,6 +63,7 @@ class Access extends LDAPUtility {
|
||||
) {
|
||||
parent::__construct($ldap);
|
||||
$this->userManager->setLdapAccess($this);
|
||||
$this->intermediates = new CappedMemoryCache();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -483,8 +486,7 @@ class Access extends LDAPUtility {
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped = null, ?array $record = null, bool $autoMapping = true) {
|
||||
static $intermediates = [];
|
||||
if (isset($intermediates[($isUser ? 'user-' : 'group-') . $fdn])) {
|
||||
if (isset($this->intermediates[($isUser ? 'user-' : 'group-') . $fdn])) {
|
||||
return false; // is a known intermediate
|
||||
}
|
||||
|
||||
@@ -531,7 +533,7 @@ class Access extends LDAPUtility {
|
||||
$record = $this->readAttributes($fdn, $attributesToRead, $filter);
|
||||
if ($record === false) {
|
||||
$this->logger->debug('Cannot read attributes for ' . $fdn . '. Skipping.', ['filter' => $filter]);
|
||||
$intermediates[($isUser ? 'user-' : 'group-') . $fdn] = true;
|
||||
$this->intermediates[($isUser ? 'user-' : 'group-') . $fdn] = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -578,7 +580,7 @@ class Access extends LDAPUtility {
|
||||
$ldapName = $record[$nameAttribute];
|
||||
if (!isset($ldapName[0]) || empty($ldapName[0])) {
|
||||
$this->logger->debug('No or empty name for ' . $fdn . ' with filter ' . $filter . '.', ['app' => 'user_ldap']);
|
||||
$intermediates['group-' . $fdn] = true;
|
||||
$this->intermediates['group-' . $fdn] = true;
|
||||
return false;
|
||||
}
|
||||
$ldapName = $ldapName[0];
|
||||
|
||||
54
build/psalm/StaticVarsChecker.php
Normal file
54
build/psalm/StaticVarsChecker.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Plugin\EventHandler\AfterClassLikeVisitInterface;
|
||||
use Psalm\Plugin\EventHandler\AfterStatementAnalysisInterface;
|
||||
use Psalm\Plugin\EventHandler\Event\AfterClassLikeVisitEvent;
|
||||
use Psalm\Plugin\EventHandler\Event\AfterStatementAnalysisEvent;
|
||||
|
||||
/**
|
||||
* Complains about static property in classes and static vars in methods
|
||||
*/
|
||||
class StaticVarsChecker implements AfterClassLikeVisitInterface, AfterStatementAnalysisInterface {
|
||||
public static function afterClassLikeVisit(AfterClassLikeVisitEvent $event): void {
|
||||
$classLike = $event->getStmt();
|
||||
$statementsSource = $event->getStatementsSource();
|
||||
|
||||
foreach ($classLike->stmts as $stmt) {
|
||||
if ($stmt instanceof Property) {
|
||||
if ($stmt->isStatic()) {
|
||||
IssueBuffer::maybeAdd(
|
||||
// ImpureStaticProperty is close enough, all static properties are impure to my eyes
|
||||
new \Psalm\Issue\ImpureStaticProperty(
|
||||
'Static property should not be used as they do not follow requests lifecycle',
|
||||
new CodeLocation($statementsSource, $stmt),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function afterStatementAnalysis(AfterStatementAnalysisEvent $event): ?bool {
|
||||
$stmt = $event->getStmt();
|
||||
if ($stmt instanceof PhpParser\Node\Stmt\Static_) {
|
||||
IssueBuffer::maybeAdd(
|
||||
// Same logic
|
||||
new \Psalm\Issue\ImpureStaticVariable(
|
||||
'Static var should not be used as they do not follow requests lifecycle and are hard to reset',
|
||||
new CodeLocation($event->getStatementsSource(), $stmt),
|
||||
)
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
23
build/psalm/StaticVarsTest.php
Normal file
23
build/psalm/StaticVarsTest.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
class StaticVarsTest {
|
||||
public static $forbiddenStaticProperty;
|
||||
protected static $forbiddenProtectedStaticProperty;
|
||||
private static $forbiddenPrivateStaticProperty;
|
||||
private static $forbiddenPrivateStaticPropertyWithValue = [];
|
||||
|
||||
public function normalFunction(): void {
|
||||
static $forbiddenStaticVar = false;
|
||||
}
|
||||
|
||||
public static function staticFunction(): void {
|
||||
static $forbiddenStaticVar = false;
|
||||
}
|
||||
}
|
||||
@@ -277,7 +277,7 @@ class InstalledVersions
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C' && is_file(__DIR__ . '/installed.php')) {
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = include __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
@@ -378,7 +378,7 @@ class InstalledVersions
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C' && is_file(__DIR__ . '/installed.php')) {
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require __DIR__ . '/installed.php';
|
||||
self::$installed = $required;
|
||||
|
||||
@@ -1,5 +1,68 @@
|
||||
{
|
||||
"packages": [],
|
||||
"dev": false,
|
||||
"dev-package-names": []
|
||||
"packages": [
|
||||
{
|
||||
"name": "bamarni/composer-bin-plugin",
|
||||
"version": "1.9.1",
|
||||
"version_normalized": "1.9.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bamarni/composer-bin-plugin.git",
|
||||
"reference": "641d0663f5ac270b1aeec4337b7856f76204df47"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/641d0663f5ac270b1aeec4337b7856f76204df47",
|
||||
"reference": "641d0663f5ac270b1aeec4337b7856f76204df47",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer-plugin-api": "^2.0",
|
||||
"php": "^7.2.5 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "^2.2.26",
|
||||
"ext-json": "*",
|
||||
"phpstan/extension-installer": "^1.1",
|
||||
"phpstan/phpstan": "^1.8 || ^2.0",
|
||||
"phpstan/phpstan-phpunit": "^1.1 || ^2.0",
|
||||
"phpunit/phpunit": "^8.5 || ^9.6 || ^10.0",
|
||||
"symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
|
||||
"symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
|
||||
"symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0"
|
||||
},
|
||||
"time": "2026-02-04T10:18:12+00:00",
|
||||
"type": "composer-plugin",
|
||||
"extra": {
|
||||
"class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin"
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Bamarni\\Composer\\Bin\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "No conflicts for your bin dependencies",
|
||||
"keywords": [
|
||||
"composer",
|
||||
"conflict",
|
||||
"dependency",
|
||||
"executable",
|
||||
"isolation",
|
||||
"tool"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/bamarni/composer-bin-plugin/issues",
|
||||
"source": "https://github.com/bamarni/composer-bin-plugin/tree/1.9.1"
|
||||
},
|
||||
"install-path": "../bamarni/composer-bin-plugin"
|
||||
}
|
||||
],
|
||||
"dev": true,
|
||||
"dev-package-names": [
|
||||
"bamarni/composer-bin-plugin"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,21 +3,30 @@
|
||||
'name' => '__root__',
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '671cec33f134e670bb21c5e3c49c685bd78fc339',
|
||||
'reference' => '91ba91a9d21106ee519f065d6feea2f8f757651c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../../',
|
||||
'aliases' => array(),
|
||||
'dev' => false,
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '671cec33f134e670bb21c5e3c49c685bd78fc339',
|
||||
'reference' => '91ba91a9d21106ee519f065d6feea2f8f757651c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'bamarni/composer-bin-plugin' => array(
|
||||
'pretty_version' => '1.9.1',
|
||||
'version' => '1.9.1.0',
|
||||
'reference' => '641d0663f5ac270b1aeec4337b7856f76204df47',
|
||||
'type' => 'composer-plugin',
|
||||
'install_path' => __DIR__ . '/../bamarni/composer-bin-plugin',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@ use OCP\FilesMetadata\IMetadataQuery;
|
||||
*/
|
||||
class SearchBuilder {
|
||||
/** @var array<string, string> */
|
||||
protected static $searchOperatorMap = [
|
||||
private const SEARCH_OPERATOR_MAP = [
|
||||
ISearchComparison::COMPARE_LIKE => 'iLike',
|
||||
ISearchComparison::COMPARE_LIKE_CASE_SENSITIVE => 'like',
|
||||
ISearchComparison::COMPARE_EQUAL => 'eq',
|
||||
@@ -37,7 +37,7 @@ class SearchBuilder {
|
||||
];
|
||||
|
||||
/** @var array<string, string> */
|
||||
protected static $searchOperatorNegativeMap = [
|
||||
private const SEARCH_OPERATOR_NEGATIVE_MAP = [
|
||||
ISearchComparison::COMPARE_LIKE => 'notLike',
|
||||
ISearchComparison::COMPARE_LIKE_CASE_SENSITIVE => 'notLike',
|
||||
ISearchComparison::COMPARE_EQUAL => 'neq',
|
||||
@@ -50,7 +50,7 @@ class SearchBuilder {
|
||||
];
|
||||
|
||||
/** @var array<string, string> */
|
||||
protected static $fieldTypes = [
|
||||
private const FIELD_TYPES = [
|
||||
'mimetype' => 'string',
|
||||
'mtime' => 'integer',
|
||||
'name' => 'string',
|
||||
@@ -69,14 +69,14 @@ class SearchBuilder {
|
||||
];
|
||||
|
||||
/** @var array<string, int|string> */
|
||||
protected static $paramTypeMap = [
|
||||
private const PARAM_TYPE_MAP = [
|
||||
'string' => IQueryBuilder::PARAM_STR,
|
||||
'integer' => IQueryBuilder::PARAM_INT,
|
||||
'boolean' => IQueryBuilder::PARAM_BOOL,
|
||||
];
|
||||
|
||||
/** @var array<string, int> */
|
||||
protected static $paramArrayTypeMap = [
|
||||
private const PARAM_ARRAY_TYPE_MAP = [
|
||||
'string' => IQueryBuilder::PARAM_STR_ARRAY,
|
||||
'integer' => IQueryBuilder::PARAM_INT_ARRAY,
|
||||
'boolean' => IQueryBuilder::PARAM_INT_ARRAY,
|
||||
@@ -134,7 +134,7 @@ class SearchBuilder {
|
||||
case ISearchBinaryOperator::OPERATOR_NOT:
|
||||
$negativeOperator = $operator->getArguments()[0];
|
||||
if ($negativeOperator instanceof ISearchComparison) {
|
||||
return $this->searchComparisonToDBExpr($builder, $negativeOperator, self::$searchOperatorNegativeMap, $metadataQuery);
|
||||
return $this->searchComparisonToDBExpr($builder, $negativeOperator, self::SEARCH_OPERATOR_NEGATIVE_MAP, $metadataQuery);
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Binary operators inside "not" is not supported');
|
||||
}
|
||||
@@ -147,7 +147,7 @@ class SearchBuilder {
|
||||
throw new \InvalidArgumentException('Invalid operator type: ' . $operator->getType());
|
||||
}
|
||||
} elseif ($operator instanceof ISearchComparison) {
|
||||
return $this->searchComparisonToDBExpr($builder, $operator, self::$searchOperatorMap, $metadataQuery);
|
||||
return $this->searchComparisonToDBExpr($builder, $operator, self::SEARCH_OPERATOR_MAP, $metadataQuery);
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Invalid operator type: ' . get_class($operator));
|
||||
}
|
||||
@@ -193,7 +193,7 @@ class SearchBuilder {
|
||||
* @return list{string, ParamValue, string, string}
|
||||
*/
|
||||
private function getOperatorFieldAndValueInner(string $field, mixed $value, string $type, bool $pathEqHash): array {
|
||||
$paramType = self::$fieldTypes[$field];
|
||||
$paramType = self::FIELD_TYPES[$field];
|
||||
if ($type === ISearchComparison::COMPARE_IN) {
|
||||
$resultField = $field;
|
||||
$values = [];
|
||||
@@ -263,10 +263,10 @@ class SearchBuilder {
|
||||
'upload_time' => ['eq', 'gt', 'lt', 'gte', 'lte'],
|
||||
];
|
||||
|
||||
if (!isset(self::$fieldTypes[$operator->getField()])) {
|
||||
if (!isset(self::FIELD_TYPES[$operator->getField()])) {
|
||||
throw new \InvalidArgumentException('Unsupported comparison field ' . $operator->getField());
|
||||
}
|
||||
$type = self::$fieldTypes[$operator->getField()];
|
||||
$type = self::FIELD_TYPES[$operator->getField()];
|
||||
if ($operator->getType() === ISearchComparison::COMPARE_IN) {
|
||||
if (!is_array($operator->getValue())) {
|
||||
throw new \InvalidArgumentException('Invalid type for field ' . $operator->getField());
|
||||
@@ -317,9 +317,9 @@ class SearchBuilder {
|
||||
$value = $value->getTimestamp();
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$type = self::$paramArrayTypeMap[$paramType];
|
||||
$type = self::PARAM_ARRAY_TYPE_MAP[$paramType];
|
||||
} else {
|
||||
$type = self::$paramTypeMap[$paramType];
|
||||
$type = self::PARAM_TYPE_MAP[$paramType];
|
||||
}
|
||||
return $builder->createNamedParameter($value, $type);
|
||||
}
|
||||
|
||||
@@ -7,73 +7,18 @@
|
||||
*/
|
||||
namespace OC\Memcache;
|
||||
|
||||
use OC\SystemConfig;
|
||||
use OCP\HintException;
|
||||
use OCP\IConfig;
|
||||
use OCP\IMemcache;
|
||||
use OCP\Server;
|
||||
|
||||
class Memcached extends Cache implements IMemcache {
|
||||
use CASTrait;
|
||||
|
||||
/**
|
||||
* @var \Memcached $cache
|
||||
*/
|
||||
private static $cache = null;
|
||||
private \Memcached $cache;
|
||||
|
||||
use CADTrait;
|
||||
|
||||
public function __construct($prefix = '') {
|
||||
parent::__construct($prefix);
|
||||
if (is_null(self::$cache)) {
|
||||
self::$cache = new \Memcached();
|
||||
|
||||
$defaultOptions = [
|
||||
\Memcached::OPT_CONNECT_TIMEOUT => 50,
|
||||
\Memcached::OPT_RETRY_TIMEOUT => 50,
|
||||
\Memcached::OPT_SEND_TIMEOUT => 50,
|
||||
\Memcached::OPT_RECV_TIMEOUT => 50,
|
||||
\Memcached::OPT_POLL_TIMEOUT => 50,
|
||||
|
||||
// Enable compression
|
||||
\Memcached::OPT_COMPRESSION => true,
|
||||
|
||||
// Turn on consistent hashing
|
||||
\Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
|
||||
|
||||
// Enable Binary Protocol
|
||||
\Memcached::OPT_BINARY_PROTOCOL => true,
|
||||
];
|
||||
/**
|
||||
* By default enable igbinary serializer if available
|
||||
*
|
||||
* Psalm checks depend on if igbinary is installed or not with memcached
|
||||
* @psalm-suppress RedundantCondition
|
||||
* @psalm-suppress TypeDoesNotContainType
|
||||
*/
|
||||
if (\Memcached::HAVE_IGBINARY) {
|
||||
$defaultOptions[\Memcached::OPT_SERIALIZER]
|
||||
= \Memcached::SERIALIZER_IGBINARY;
|
||||
}
|
||||
$options = Server::get(IConfig::class)->getSystemValue('memcached_options', []);
|
||||
if (is_array($options)) {
|
||||
$options = $options + $defaultOptions;
|
||||
self::$cache->setOptions($options);
|
||||
} else {
|
||||
throw new HintException("Expected 'memcached_options' config to be an array, got $options");
|
||||
}
|
||||
|
||||
$servers = Server::get(SystemConfig::class)->getValue('memcached_servers');
|
||||
if (!$servers) {
|
||||
$server = Server::get(SystemConfig::class)->getValue('memcached_server');
|
||||
if ($server) {
|
||||
$servers = [$server];
|
||||
} else {
|
||||
$servers = [['localhost', 11211]];
|
||||
}
|
||||
}
|
||||
self::$cache->addServers($servers);
|
||||
}
|
||||
$this->cache = \OCP\Server::get(MemcachedFactory::class)->getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,8 +29,8 @@ class Memcached extends Cache implements IMemcache {
|
||||
}
|
||||
|
||||
public function get($key) {
|
||||
$result = self::$cache->get($this->getNameSpace() . $key);
|
||||
if ($result === false && self::$cache->getResultCode() === \Memcached::RES_NOTFOUND) {
|
||||
$result = $this->cache->get($this->getNameSpace() . $key);
|
||||
if ($result === false && $this->cache->getResultCode() === \Memcached::RES_NOTFOUND) {
|
||||
return null;
|
||||
} else {
|
||||
return $result;
|
||||
@@ -94,26 +39,26 @@ class Memcached extends Cache implements IMemcache {
|
||||
|
||||
public function set($key, $value, $ttl = 0) {
|
||||
if ($ttl > 0) {
|
||||
$result = self::$cache->set($this->getNameSpace() . $key, $value, $ttl);
|
||||
$result = $this->cache->set($this->getNameSpace() . $key, $value, $ttl);
|
||||
} else {
|
||||
$result = self::$cache->set($this->getNameSpace() . $key, $value);
|
||||
$result = $this->cache->set($this->getNameSpace() . $key, $value);
|
||||
}
|
||||
return $result || $this->isSuccess();
|
||||
}
|
||||
|
||||
public function hasKey($key) {
|
||||
self::$cache->get($this->getNameSpace() . $key);
|
||||
return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
|
||||
$this->cache->get($this->getNameSpace() . $key);
|
||||
return $this->cache->getResultCode() === \Memcached::RES_SUCCESS;
|
||||
}
|
||||
|
||||
public function remove($key) {
|
||||
$result = self::$cache->delete($this->getNameSpace() . $key);
|
||||
return $result || $this->isSuccess() || self::$cache->getResultCode() === \Memcached::RES_NOTFOUND;
|
||||
$result = $this->cache->delete($this->getNameSpace() . $key);
|
||||
return $result || $this->isSuccess() || $this->cache->getResultCode() === \Memcached::RES_NOTFOUND;
|
||||
}
|
||||
|
||||
public function clear($prefix = '') {
|
||||
// Newer Memcached doesn't like getAllKeys(), flush everything
|
||||
self::$cache->flush();
|
||||
$this->cache->flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -126,7 +71,7 @@ class Memcached extends Cache implements IMemcache {
|
||||
* @return bool
|
||||
*/
|
||||
public function add($key, $value, $ttl = 0) {
|
||||
$result = self::$cache->add($this->getPrefix() . $key, $value, $ttl);
|
||||
$result = $this->cache->add($this->getPrefix() . $key, $value, $ttl);
|
||||
return $result || $this->isSuccess();
|
||||
}
|
||||
|
||||
@@ -139,9 +84,9 @@ class Memcached extends Cache implements IMemcache {
|
||||
*/
|
||||
public function inc($key, $step = 1) {
|
||||
$this->add($key, 0);
|
||||
$result = self::$cache->increment($this->getPrefix() . $key, $step);
|
||||
$result = $this->cache->increment($this->getPrefix() . $key, $step);
|
||||
|
||||
if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
|
||||
if ($this->cache->getResultCode() !== \Memcached::RES_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -156,9 +101,9 @@ class Memcached extends Cache implements IMemcache {
|
||||
* @return int | bool
|
||||
*/
|
||||
public function dec($key, $step = 1) {
|
||||
$result = self::$cache->decrement($this->getPrefix() . $key, $step);
|
||||
$result = $this->cache->decrement($this->getPrefix() . $key, $step);
|
||||
|
||||
if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
|
||||
if ($this->cache->getResultCode() !== \Memcached::RES_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -170,6 +115,6 @@ class Memcached extends Cache implements IMemcache {
|
||||
}
|
||||
|
||||
private function isSuccess(): bool {
|
||||
return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
|
||||
return $this->cache->getResultCode() === \Memcached::RES_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
81
lib/private/Memcache/MemcachedFactory.php
Normal file
81
lib/private/Memcache/MemcachedFactory.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
namespace OC\Memcache;
|
||||
|
||||
use OC\SystemConfig;
|
||||
use OCP\HintException;
|
||||
use OCP\IConfig;
|
||||
use OCP\Server;
|
||||
|
||||
/**
|
||||
* Factory for \Memcached instance, to create a singleton
|
||||
*/
|
||||
class MemcachedFactory {
|
||||
private ?\Memcached $instance = null;
|
||||
|
||||
public function getInstance(): \Memcached {
|
||||
if ($this->instance === null) {
|
||||
$this->init();
|
||||
}
|
||||
return $this->instance;
|
||||
}
|
||||
|
||||
/** @psalm-assert \Memcached $this->instance */
|
||||
public function init(): void {
|
||||
$this->instance = new \Memcached();
|
||||
|
||||
$defaultOptions = [
|
||||
\Memcached::OPT_CONNECT_TIMEOUT => 50,
|
||||
\Memcached::OPT_RETRY_TIMEOUT => 50,
|
||||
\Memcached::OPT_SEND_TIMEOUT => 50,
|
||||
\Memcached::OPT_RECV_TIMEOUT => 50,
|
||||
\Memcached::OPT_POLL_TIMEOUT => 50,
|
||||
|
||||
// Enable compression
|
||||
\Memcached::OPT_COMPRESSION => true,
|
||||
|
||||
// Turn on consistent hashing
|
||||
\Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
|
||||
|
||||
// Enable Binary Protocol
|
||||
\Memcached::OPT_BINARY_PROTOCOL => true,
|
||||
];
|
||||
/**
|
||||
* By default enable igbinary serializer if available
|
||||
*
|
||||
* Psalm checks depend on if igbinary is installed or not with memcached
|
||||
* @psalm-suppress RedundantCondition
|
||||
* @psalm-suppress TypeDoesNotContainType
|
||||
*/
|
||||
if (\Memcached::HAVE_IGBINARY) {
|
||||
$defaultOptions[\Memcached::OPT_SERIALIZER]
|
||||
= \Memcached::SERIALIZER_IGBINARY;
|
||||
}
|
||||
$options = Server::get(IConfig::class)->getSystemValue('memcached_options', []);
|
||||
if (is_array($options)) {
|
||||
$options = $options + $defaultOptions;
|
||||
$this->instance->setOptions($options);
|
||||
} else {
|
||||
throw new HintException("Expected 'memcached_options' config to be an array, got $options");
|
||||
}
|
||||
|
||||
$servers = Server::get(SystemConfig::class)->getValue('memcached_servers');
|
||||
if (!$servers) {
|
||||
$server = Server::get(SystemConfig::class)->getValue('memcached_server');
|
||||
if ($server) {
|
||||
$servers = [$server];
|
||||
} else {
|
||||
$servers = [['localhost', 11211]];
|
||||
}
|
||||
}
|
||||
$this->instance->addServers($servers);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
namespace OC\Memcache;
|
||||
|
||||
use OC\RedisFactory;
|
||||
use OCP\IMemcacheTTL;
|
||||
use OCP\Server;
|
||||
|
||||
@@ -37,24 +38,18 @@ class Redis extends Cache implements IMemcacheTTL {
|
||||
|
||||
private const MAX_TTL = 30 * 24 * 60 * 60; // 1 month
|
||||
|
||||
/**
|
||||
* @var \Redis|\RedisCluster $cache
|
||||
*/
|
||||
private static $cache = null;
|
||||
private \Redis|\RedisCluster $cache;
|
||||
|
||||
public function __construct($prefix = '', string $logFile = '') {
|
||||
parent::__construct($prefix);
|
||||
$this->cache = \OCP\Server::get(RedisFactory::class)->getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Redis|\RedisCluster|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getCache() {
|
||||
if (is_null(self::$cache)) {
|
||||
self::$cache = Server::get('RedisFactory')->getInstance();
|
||||
}
|
||||
return self::$cache;
|
||||
public function getCache(): \Redis|\RedisCluster {
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
public function get($key) {
|
||||
|
||||
@@ -33,6 +33,8 @@ class Generator {
|
||||
public const SEMAPHORE_ID_ALL = 0x0a11;
|
||||
public const SEMAPHORE_ID_NEW = 0x07ea;
|
||||
|
||||
private array $cachedNumConcurrentPreviews = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly IConfig $config,
|
||||
private readonly IAppConfig $appConfig,
|
||||
@@ -259,22 +261,14 @@ class Generator {
|
||||
*
|
||||
* @return int number of concurrent threads, or 0 if it cannot be determined
|
||||
*/
|
||||
public static function getHardwareConcurrency(): int {
|
||||
static $width;
|
||||
|
||||
if (!isset($width)) {
|
||||
if (function_exists('ini_get')) {
|
||||
$openBasedir = ini_get('open_basedir');
|
||||
if (empty($openBasedir) || strpos($openBasedir, '/proc/cpuinfo') !== false) {
|
||||
$width = is_readable('/proc/cpuinfo') ? substr_count(file_get_contents('/proc/cpuinfo'), 'processor') : 0;
|
||||
} else {
|
||||
$width = 0;
|
||||
}
|
||||
} else {
|
||||
$width = 0;
|
||||
private static function getHardwareConcurrency(): int {
|
||||
if (function_exists('ini_get')) {
|
||||
$openBasedir = ini_get('open_basedir');
|
||||
if (empty($openBasedir) || strpos($openBasedir, '/proc/cpuinfo') !== false) {
|
||||
return is_readable('/proc/cpuinfo') ? substr_count(file_get_contents('/proc/cpuinfo'), 'processor') : 0;
|
||||
}
|
||||
}
|
||||
return $width;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,9 +287,8 @@ class Generator {
|
||||
* @return int number of concurrent preview generations, or -1 if $type is invalid
|
||||
*/
|
||||
public function getNumConcurrentPreviews(string $type): int {
|
||||
static $cached = [];
|
||||
if (array_key_exists($type, $cached)) {
|
||||
return $cached[$type];
|
||||
if (array_key_exists($type, $this->cachedNumConcurrentPreviews)) {
|
||||
return $this->cachedNumConcurrentPreviews[$type];
|
||||
}
|
||||
|
||||
$hardwareConcurrency = self::getHardwareConcurrency();
|
||||
@@ -304,16 +297,16 @@ class Generator {
|
||||
$fallback = $hardwareConcurrency > 0 ? $hardwareConcurrency * 2 : 8;
|
||||
$concurrency_all = $this->config->getSystemValueInt($type, $fallback);
|
||||
$concurrency_new = $this->getNumConcurrentPreviews('preview_concurrency_new');
|
||||
$cached[$type] = max($concurrency_all, $concurrency_new);
|
||||
$this->cachedNumConcurrentPreviews[$type] = max($concurrency_all, $concurrency_new);
|
||||
break;
|
||||
case 'preview_concurrency_new':
|
||||
$fallback = $hardwareConcurrency > 0 ? $hardwareConcurrency : 4;
|
||||
$cached[$type] = $this->config->getSystemValueInt($type, $fallback);
|
||||
$this->cachedNumConcurrentPreviews[$type] = $this->config->getSystemValueInt($type, $fallback);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return $cached[$type];
|
||||
return $this->cachedNumConcurrentPreviews[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -131,15 +131,14 @@ class RedisFactory {
|
||||
}
|
||||
|
||||
public function getInstance(): \Redis|\RedisCluster {
|
||||
if (!$this->isAvailable()) {
|
||||
throw new \Exception('Redis support is not available');
|
||||
}
|
||||
if ($this->instance === null) {
|
||||
if (!$this->isAvailable()) {
|
||||
throw new \Exception('Redis support is not available');
|
||||
}
|
||||
$this->create();
|
||||
}
|
||||
|
||||
if ($this->instance === null) {
|
||||
throw new \Exception('Redis support is not available');
|
||||
if ($this->instance === null) {
|
||||
throw new \Exception('Redis support is not available');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->instance;
|
||||
|
||||
@@ -669,10 +669,7 @@ class Server extends ServerContainer implements IServerContainer {
|
||||
});
|
||||
$this->registerAlias(ICacheFactory::class, Factory::class);
|
||||
|
||||
$this->registerService('RedisFactory', function (Server $c) {
|
||||
$systemConfig = $c->get(SystemConfig::class);
|
||||
return new RedisFactory($systemConfig, $c->get(IEventLogger::class));
|
||||
});
|
||||
$this->registerDeprecatedAlias('RedisFactory', RedisFactory::class);
|
||||
|
||||
$this->registerService(\OCP\Activity\IManager::class, function (Server $c) {
|
||||
$l10n = $this->get(IFactory::class)->get('lib');
|
||||
|
||||
@@ -76,12 +76,12 @@ class Task extends Entity {
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'custom_id', 'completion_expected_at', 'error_message', 'progress', 'webhook_uri', 'webhook_method', 'scheduled_at', 'started_at', 'ended_at', 'allow_cleanup', 'user_facing_error_message', 'include_watermark'];
|
||||
public const COLUMNS = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'custom_id', 'completion_expected_at', 'error_message', 'progress', 'webhook_uri', 'webhook_method', 'scheduled_at', 'started_at', 'ended_at', 'allow_cleanup', 'user_facing_error_message', 'include_watermark'];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'customId', 'completionExpectedAt', 'errorMessage', 'progress', 'webhookUri', 'webhookMethod', 'scheduledAt', 'startedAt', 'endedAt', 'allowCleanup', 'userFacingErrorMessage', 'includeWatermark'];
|
||||
public const FIELDS = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'customId', 'completionExpectedAt', 'errorMessage', 'progress', 'webhookUri', 'webhookMethod', 'scheduledAt', 'startedAt', 'endedAt', 'allowCleanup', 'userFacingErrorMessage', 'includeWatermark'];
|
||||
|
||||
|
||||
public function __construct() {
|
||||
@@ -109,9 +109,9 @@ class Task extends Entity {
|
||||
}
|
||||
|
||||
public function toRow(): array {
|
||||
return array_combine(self::$columns, array_map(function ($field) {
|
||||
return array_combine(self::COLUMNS, array_map(function ($field) {
|
||||
return $this->{'get' . ucfirst($field)}();
|
||||
}, self::$fields));
|
||||
}, self::FIELDS));
|
||||
}
|
||||
|
||||
public static function fromPublicTask(OCPTask $task): self {
|
||||
|
||||
@@ -38,7 +38,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function find(int $id): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
return $this->findEntity($qb);
|
||||
@@ -53,7 +53,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function findOldestScheduledByType(array $taskTypes, array $taskIdsToIgnore): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('status', $qb->createPositionalParameter(\OCP\TaskProcessing\Task::STATUS_SCHEDULED, IQueryBuilder::PARAM_INT)))
|
||||
->setMaxResults(1)
|
||||
@@ -85,7 +85,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function findByIdAndUser(int $id, ?string $userId): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
if ($userId === null) {
|
||||
@@ -105,7 +105,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function findByUserAndTaskType(?string $userId, ?string $taskType = null, ?string $customId = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)));
|
||||
if ($taskType !== null) {
|
||||
@@ -126,7 +126,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function findUserTasksByApp(?string $userId, string $appId, ?string $customId = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)))
|
||||
->andWhere($qb->expr()->eq('app_id', $qb->createPositionalParameter($appId)));
|
||||
@@ -151,7 +151,7 @@ class TaskMapper extends QBMapper {
|
||||
?string $userId, ?string $taskType = null, ?string $appId = null, ?string $customId = null,
|
||||
?int $status = null, ?int $scheduleAfter = null, ?int $endedBefore = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName);
|
||||
|
||||
// empty string: no userId filter
|
||||
@@ -205,7 +205,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function getTasksToCleanup(int $timeout, bool $force = false): \Generator {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter($this->timeFactory->getDateTime()->getTimestamp() - $timeout)));
|
||||
if (!$force) {
|
||||
@@ -243,7 +243,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function findNOldestScheduledByType(array $taskTypes, array $taskIdsToIgnore, int $numberOfTasks) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('status', $qb->createPositionalParameter(\OCP\TaskProcessing\Task::STATUS_SCHEDULED, IQueryBuilder::PARAM_INT)))
|
||||
->setMaxResults($numberOfTasks)
|
||||
|
||||
@@ -43,9 +43,9 @@ use OCP\Template\ITemplateManager;
|
||||
use OCP\Util;
|
||||
|
||||
class TemplateLayout {
|
||||
private static string $versionHash = '';
|
||||
private string $versionHash = '';
|
||||
/** @var string[] */
|
||||
private static array $cacheBusterCache = [];
|
||||
private array $cacheBusterCache = [];
|
||||
|
||||
public ?CSSResourceLocator $cssLocator = null;
|
||||
public ?JSResourceLocator $jsLocator = null;
|
||||
@@ -211,13 +211,13 @@ class TemplateLayout {
|
||||
$page->assign('enabledThemes', $themesService?->getEnabledThemes() ?? []);
|
||||
|
||||
if ($this->config->getSystemValueBool('installed', false)) {
|
||||
if (empty(self::$versionHash)) {
|
||||
if (empty($this->versionHash)) {
|
||||
$v = $this->appManager->getAppInstalledVersions(true);
|
||||
$v['core'] = implode('.', $this->serverVersion->getVersion());
|
||||
self::$versionHash = substr(md5(implode(',', $v)), 0, 8);
|
||||
$this->versionHash = substr(md5(implode(',', $v)), 0, 8);
|
||||
}
|
||||
} else {
|
||||
self::$versionHash = md5('not installed');
|
||||
$this->versionHash = md5('not installed');
|
||||
}
|
||||
|
||||
// Add the js files
|
||||
@@ -247,7 +247,7 @@ class TemplateLayout {
|
||||
if (Server::get(ContentSecurityPolicyNonceManager::class)->browserSupportsCspV3()) {
|
||||
$page->assign('inline_ocjs', $config);
|
||||
} else {
|
||||
$page->append('jsfiles', Server::get(IURLGenerator::class)->linkToRoute('core.OCJS.getConfig', ['v' => self::$versionHash]));
|
||||
$page->append('jsfiles', Server::get(IURLGenerator::class)->linkToRoute('core.OCJS.getConfig', ['v' => $this->versionHash]));
|
||||
}
|
||||
}
|
||||
foreach ($jsFiles as $info) {
|
||||
@@ -280,7 +280,7 @@ class TemplateLayout {
|
||||
|
||||
$page->assign('cssfiles', []);
|
||||
$page->assign('printcssfiles', []);
|
||||
$this->initialState->provideInitialState('core', 'versionHash', self::$versionHash);
|
||||
$this->initialState->provideInitialState('core', 'versionHash', $this->versionHash);
|
||||
foreach ($cssFiles as $info) {
|
||||
$web = $info[1];
|
||||
$file = $info[2];
|
||||
@@ -320,7 +320,7 @@ class TemplateLayout {
|
||||
|
||||
if ($this->config->getSystemValueBool('installed', false) === false) {
|
||||
// if not installed just return the version hash
|
||||
return '?v=' . self::$versionHash;
|
||||
return '?v=' . $this->versionHash;
|
||||
}
|
||||
|
||||
$hash = false;
|
||||
@@ -334,7 +334,7 @@ class TemplateLayout {
|
||||
}
|
||||
// As a last resort we use the server version hash
|
||||
if ($hash === false) {
|
||||
$hash = self::$versionHash;
|
||||
$hash = $this->versionHash;
|
||||
}
|
||||
|
||||
// The theming app is force-enabled thus the cache buster is always available
|
||||
@@ -344,7 +344,7 @@ class TemplateLayout {
|
||||
}
|
||||
|
||||
private function getVersionHashByPath(string $path): string|false {
|
||||
if (array_key_exists($path, self::$cacheBusterCache) === false) {
|
||||
if (array_key_exists($path, $this->cacheBusterCache) === false) {
|
||||
// Not yet cached, so lets find the cache buster string
|
||||
$appId = $this->getAppNamefromPath($path);
|
||||
if ($appId === false) {
|
||||
@@ -354,20 +354,20 @@ class TemplateLayout {
|
||||
|
||||
if ($appId === 'core') {
|
||||
// core is not a real app but the server itself
|
||||
$hash = self::$versionHash;
|
||||
$hash = $this->versionHash;
|
||||
} else {
|
||||
$appVersion = $this->appManager->getAppVersion($appId);
|
||||
// For shipped apps the app version is not a single source of truth, we rather also need to consider the Nextcloud version
|
||||
if ($this->appManager->isShipped($appId)) {
|
||||
$appVersion .= '-' . self::$versionHash;
|
||||
$appVersion .= '-' . $this->versionHash;
|
||||
}
|
||||
|
||||
$hash = substr(md5($appVersion), 0, 8);
|
||||
}
|
||||
self::$cacheBusterCache[$path] = $hash;
|
||||
$this->cacheBusterCache[$path] = $hash;
|
||||
}
|
||||
|
||||
return self::$cacheBusterCache[$path];
|
||||
return $this->cacheBusterCache[$path];
|
||||
}
|
||||
|
||||
private function findStylesheetFiles(array $styles): array {
|
||||
|
||||
@@ -46,13 +46,12 @@ class Task extends Entity {
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'identifier', 'completion_expected_at'];
|
||||
public const COLUMNS = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'identifier', 'completion_expected_at'];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'identifier', 'completionExpectedAt'];
|
||||
|
||||
public const FIELDS = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'identifier', 'completionExpectedAt'];
|
||||
|
||||
public function __construct() {
|
||||
// add types in constructor
|
||||
@@ -69,9 +68,9 @@ class Task extends Entity {
|
||||
}
|
||||
|
||||
public function toRow(): array {
|
||||
return array_combine(self::$columns, array_map(function ($field) {
|
||||
return array_combine(self::COLUMNS, array_map(function ($field) {
|
||||
return $this->{'get' . ucfirst($field)}();
|
||||
}, self::$fields));
|
||||
}, self::FIELDS));
|
||||
}
|
||||
|
||||
public static function fromPublicTask(OCPTask $task): Task {
|
||||
|
||||
@@ -37,7 +37,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function find(int $id): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
return $this->findEntity($qb);
|
||||
@@ -53,7 +53,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function findByIdAndUser(int $id, ?string $userId): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
if ($userId === null) {
|
||||
@@ -73,7 +73,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function findUserTasksByApp(string $userId, string $appId, ?string $identifier = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)))
|
||||
->andWhere($qb->expr()->eq('app_id', $qb->createPositionalParameter($appId)));
|
||||
|
||||
@@ -44,7 +44,10 @@ class Manager implements IManager {
|
||||
/** @var ?IProvider[] */
|
||||
private ?array $providers = null;
|
||||
|
||||
private static array $taskProcessingCompatibleTaskTypes = [
|
||||
/**
|
||||
* @var array<class-string, string>
|
||||
*/
|
||||
private const COMPATIBLE_TASK_TYPES = [
|
||||
FreePromptTaskType::class => TextToText::ID,
|
||||
HeadlineTaskType::class => TextToTextHeadline::ID,
|
||||
SummaryTaskType::class => TextToTextSummary::ID,
|
||||
@@ -91,7 +94,7 @@ class Manager implements IManager {
|
||||
public function hasProviders(): bool {
|
||||
// check if task processing equivalent types are available
|
||||
$taskTaskTypes = $this->taskProcessingManager->getAvailableTaskTypes();
|
||||
foreach (self::$taskProcessingCompatibleTaskTypes as $textTaskTypeClass => $taskTaskTypeId) {
|
||||
foreach (self::COMPATIBLE_TASK_TYPES as $textTaskTypeClass => $taskTaskTypeId) {
|
||||
if (isset($taskTaskTypes[$taskTaskTypeId])) {
|
||||
return true;
|
||||
}
|
||||
@@ -115,7 +118,7 @@ class Manager implements IManager {
|
||||
|
||||
// check if task processing equivalent types are available
|
||||
$taskTaskTypes = $this->taskProcessingManager->getAvailableTaskTypes();
|
||||
foreach (self::$taskProcessingCompatibleTaskTypes as $textTaskTypeClass => $taskTaskTypeId) {
|
||||
foreach (self::COMPATIBLE_TASK_TYPES as $textTaskTypeClass => $taskTaskTypeId) {
|
||||
if (isset($taskTaskTypes[$taskTaskTypeId])) {
|
||||
$tasks[$textTaskTypeClass] = true;
|
||||
}
|
||||
@@ -134,9 +137,9 @@ class Manager implements IManager {
|
||||
public function runTask(OCPTask $task): string {
|
||||
// try to run a task processing task if possible
|
||||
$taskTypeClass = $task->getType();
|
||||
if (isset(self::$taskProcessingCompatibleTaskTypes[$taskTypeClass]) && isset($this->taskProcessingManager->getAvailableTaskTypes()[self::$taskProcessingCompatibleTaskTypes[$taskTypeClass]])) {
|
||||
if (isset(self::COMPATIBLE_TASK_TYPES[$taskTypeClass]) && isset($this->taskProcessingManager->getAvailableTaskTypes()[self::COMPATIBLE_TASK_TYPES[$taskTypeClass]])) {
|
||||
try {
|
||||
$taskProcessingTaskTypeId = self::$taskProcessingCompatibleTaskTypes[$taskTypeClass];
|
||||
$taskProcessingTaskTypeId = self::COMPATIBLE_TASK_TYPES[$taskTypeClass];
|
||||
$taskProcessingTask = new \OCP\TaskProcessing\Task(
|
||||
$taskProcessingTaskTypeId,
|
||||
['input' => $task->getInput()],
|
||||
@@ -222,8 +225,8 @@ class Manager implements IManager {
|
||||
$task->setStatus(OCPTask::STATUS_SCHEDULED);
|
||||
$providers = $this->getPreferredProviders($task);
|
||||
$equivalentTaskProcessingTypeAvailable = (
|
||||
isset(self::$taskProcessingCompatibleTaskTypes[$task->getType()])
|
||||
&& isset($this->taskProcessingManager->getAvailableTaskTypes()[self::$taskProcessingCompatibleTaskTypes[$task->getType()]])
|
||||
isset(self::COMPATIBLE_TASK_TYPES[$task->getType()])
|
||||
&& isset($this->taskProcessingManager->getAvailableTaskTypes()[self::COMPATIBLE_TASK_TYPES[$task->getType()]])
|
||||
);
|
||||
if (count($providers) === 0 && !$equivalentTaskProcessingTypeAvailable) {
|
||||
throw new PreConditionNotMetException('No LanguageModel provider is installed that can handle this task');
|
||||
|
||||
@@ -49,12 +49,12 @@ class Task extends Entity {
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $columns = ['id', 'last_updated', 'input', 'status', 'user_id', 'app_id', 'identifier', 'number_of_images', 'completion_expected_at'];
|
||||
public const COLUMNS = ['id', 'last_updated', 'input', 'status', 'user_id', 'app_id', 'identifier', 'number_of_images', 'completion_expected_at'];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $fields = ['id', 'lastUpdated', 'input', 'status', 'userId', 'appId', 'identifier', 'numberOfImages', 'completionExpectedAt'];
|
||||
public const FIELDS = ['id', 'lastUpdated', 'input', 'status', 'userId', 'appId', 'identifier', 'numberOfImages', 'completionExpectedAt'];
|
||||
|
||||
|
||||
public function __construct() {
|
||||
@@ -71,9 +71,9 @@ class Task extends Entity {
|
||||
}
|
||||
|
||||
public function toRow(): array {
|
||||
return array_combine(self::$columns, array_map(function ($field) {
|
||||
return array_combine(self::COLUMNS, array_map(function ($field) {
|
||||
return $this->{'get' . ucfirst($field)}();
|
||||
}, self::$fields));
|
||||
}, self::FIELDS));
|
||||
}
|
||||
|
||||
public static function fromPublicTask(OCPTask $task): Task {
|
||||
|
||||
@@ -38,7 +38,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function find(int $id): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
return $this->findEntity($qb);
|
||||
@@ -54,7 +54,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function findByIdAndUser(int $id, ?string $userId): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
if ($userId === null) {
|
||||
@@ -74,7 +74,7 @@ class TaskMapper extends QBMapper {
|
||||
*/
|
||||
public function findUserTasksByApp(?string $userId, string $appId, ?string $identifier = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)))
|
||||
->andWhere($qb->expr()->eq('app_id', $qb->createPositionalParameter($appId)));
|
||||
|
||||
@@ -27,6 +27,7 @@ class Util {
|
||||
private static array $scriptsInit = [];
|
||||
private static array $scripts = [];
|
||||
private static array $scriptDeps = [];
|
||||
private static ?bool $needUpgradeCache = null;
|
||||
|
||||
/**
|
||||
* get the current installed version of Nextcloud
|
||||
@@ -575,8 +576,6 @@ class Util {
|
||||
return \OC_Util::isDefaultExpireDateEnforced();
|
||||
}
|
||||
|
||||
protected static $needUpgradeCache = null;
|
||||
|
||||
/**
|
||||
* Checks whether the current version needs upgrade.
|
||||
*
|
||||
@@ -584,7 +583,7 @@ class Util {
|
||||
* @since 7.0.0
|
||||
*/
|
||||
public static function needUpgrade() {
|
||||
if (!isset(self::$needUpgradeCache)) {
|
||||
if (self::$needUpgradeCache === null) {
|
||||
self::$needUpgradeCache = \OC_Util::needUpgrade(\OCP\Server::get(\OC\SystemConfig::class));
|
||||
}
|
||||
return self::$needUpgradeCache;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<plugin filename="build/psalm/AppFrameworkTainter.php" />
|
||||
<plugin filename="build/psalm/AttributeNamedParameters.php" />
|
||||
<plugin filename="build/psalm/LogicalOperatorChecker.php" />
|
||||
<plugin filename="build/psalm/StaticVarsChecker.php" />
|
||||
</plugins>
|
||||
<projectFiles>
|
||||
<directory name="apps/admin_audit"/>
|
||||
|
||||
@@ -48,8 +48,8 @@ class TemplateLayoutTest extends \Test\TestCase {
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('dataVersionHash')]
|
||||
public function testVersionHash(
|
||||
string|false $path,
|
||||
string|false $file,
|
||||
string $path,
|
||||
string $file,
|
||||
bool $installed,
|
||||
bool $debug,
|
||||
string $expected,
|
||||
@@ -57,13 +57,13 @@ class TemplateLayoutTest extends \Test\TestCase {
|
||||
$this->appManager->expects(self::any())
|
||||
->method('getAppVersion')
|
||||
->willReturnCallback(fn ($appId) => match ($appId) {
|
||||
'shippedApp' => 'shipped_1',
|
||||
'otherApp' => 'other_2',
|
||||
'shipped' => 'shipped_1',
|
||||
'other' => 'other_2',
|
||||
default => "$appId",
|
||||
});
|
||||
$this->appManager->expects(self::any())
|
||||
->method('isShipped')
|
||||
->willReturnCallback(fn (string $app) => $app === 'shippedApp');
|
||||
->willReturnCallback(fn (string $app) => $app === 'shipped');
|
||||
|
||||
$this->config->expects(self::atLeastOnce())
|
||||
->method('getSystemValueBool')
|
||||
@@ -79,31 +79,20 @@ class TemplateLayoutTest extends \Test\TestCase {
|
||||
$this->request->method('getPathInfo')
|
||||
->willReturn('/' . $path);
|
||||
|
||||
$this->templateLayout = $this->getMockBuilder(TemplateLayout::class)
|
||||
->onlyMethods(['getAppNamefromPath'])
|
||||
->setConstructorArgs([
|
||||
$this->config,
|
||||
$this->appConfig,
|
||||
$this->appManager,
|
||||
$this->initialState,
|
||||
$this->navigationManager,
|
||||
$this->templateManager,
|
||||
$this->serverVersion,
|
||||
$this->request,
|
||||
])
|
||||
->getMock();
|
||||
$this->templateLayout = new TemplateLayout(
|
||||
$this->config,
|
||||
$this->appConfig,
|
||||
$this->appManager,
|
||||
$this->initialState,
|
||||
$this->navigationManager,
|
||||
$this->templateManager,
|
||||
$this->serverVersion,
|
||||
$this->request,
|
||||
);
|
||||
|
||||
$layout = $this->templateLayout->getPageTemplate(TemplateResponse::RENDER_AS_ERROR, '');
|
||||
|
||||
self::invokePrivate(TemplateLayout::class, 'versionHash', ['version_hash']);
|
||||
|
||||
$this->templateLayout->expects(self::any())
|
||||
->method('getAppNamefromPath')
|
||||
->willReturnCallback(fn ($appName) => match($appName) {
|
||||
'apps/shipped' => 'shippedApp',
|
||||
'other/app.css' => 'otherApp',
|
||||
default => false,
|
||||
});
|
||||
self::invokePrivate($this->templateLayout, 'versionHash', ['version_hash']);
|
||||
|
||||
$hash = self::invokePrivate($this->templateLayout, 'getVersionHashSuffix', [$path, $file]);
|
||||
self::assertEquals($expected, $hash);
|
||||
@@ -113,9 +102,8 @@ class TemplateLayoutTest extends \Test\TestCase {
|
||||
return [
|
||||
'no hash if in debug mode' => ['apps/shipped', 'style.css', true, true, ''],
|
||||
'only version hash if not installed' => ['apps/shipped', 'style.css', false, false, '?v=version_hash'],
|
||||
'version hash with cache buster if app not found' => ['unknown/path', '', true, false, '?v=version_hash-42'],
|
||||
'version hash with cache buster if neither path nor file provided' => [false, false, true, false, '?v=version_hash-42'],
|
||||
'app version hash if external app' => ['', 'other/app.css', true, false, '?v=' . substr(md5('other_2'), 0, 8) . '-42'],
|
||||
'version hash with cache buster if neither path nor file provided' => ['', '', true, false, '?v=version_hash-42'],
|
||||
'app version hash if external app' => ['apps/other', 'app.css', true, false, '?v=' . substr(md5('other_2'), 0, 8) . '-42'],
|
||||
'app version and version hash if shipped app' => ['apps/shipped', 'style.css', true, false, '?v=' . substr(md5('shipped_1-version_hash'), 0, 8) . '-42'],
|
||||
'prefer path over file' => ['apps/shipped', 'other/app.css', true, false, '?v=' . substr(md5('shipped_1-version_hash'), 0, 8) . '-42'],
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user