parent
ba959d2c43
commit
71ef41f763
|
@ -34,6 +34,7 @@ $config
|
|||
'@PhpCsFixer:risky' => true,
|
||||
'general_phpdoc_annotation_remove' => ['annotations' => ['expectedDeprecation']], // one should use PHPUnit built-in method instead
|
||||
'modernize_strpos' => false, // needs PHP 8+ or polyfill
|
||||
'phpdoc_to_comment' => ['ignored_tags' => ['psalm-suppress', 'template-implements']],
|
||||
])
|
||||
->setFinder($finder)
|
||||
;
|
||||
|
|
15
Makefile
15
Makefile
|
@ -1,17 +1,20 @@
|
|||
all: dev-setup lint build-js-production test
|
||||
|
||||
# Dev env management
|
||||
dev-setup: clean clean-dev npm-init exiftool php-cs-fixer
|
||||
dev-setup: clean clean-dev npm-init exiftool install-tools
|
||||
|
||||
exiftool:
|
||||
sh scripts/get-exiftool.sh
|
||||
|
||||
php-cs-fixer:
|
||||
mkdir -p tools/php-cs-fixer
|
||||
composer require --dev --working-dir=tools/php-cs-fixer friendsofphp/php-cs-fixer
|
||||
install-tools:
|
||||
mkdir -p tools
|
||||
composer require --dev --working-dir=tools friendsofphp/php-cs-fixer vimeo/psalm
|
||||
|
||||
php-lint:
|
||||
tools/php-cs-fixer/vendor/bin/php-cs-fixer fix lib
|
||||
tools/vendor/bin/php-cs-fixer fix lib
|
||||
|
||||
psalm:
|
||||
tools/vendor/bin/psalm
|
||||
|
||||
npm-init:
|
||||
npm ci
|
||||
|
@ -19,7 +22,7 @@ npm-init:
|
|||
npm-update:
|
||||
npm update
|
||||
|
||||
.PHONY: dev-setup exiftool php-cs-fixer php-lint npm-init npm-update
|
||||
.PHONY: dev-setup exiftool install-tools php-lint psalm npm-init npm-update
|
||||
|
||||
# Building
|
||||
build-js:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Memories",
|
||||
"description": "p",
|
||||
"name": "radialapps/memories",
|
||||
"description": "Fast and advanced photo management for Nextcloud",
|
||||
"type": "project",
|
||||
"license": "AGPL",
|
||||
"authors": [
|
||||
|
@ -9,7 +9,5 @@
|
|||
}
|
||||
],
|
||||
"require": {},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.4"
|
||||
}
|
||||
"require-dev": {}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ class AlbumsBackend extends Backend
|
|||
return Util::albumsIsEnabled();
|
||||
}
|
||||
|
||||
public function clusterName(string $name)
|
||||
public function clusterName(string $name): string
|
||||
{
|
||||
return explode('/', $name)[1];
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ class AlbumsBackend extends Backend
|
|||
return $this->albumsQuery->getAlbumPhotos($id, $limit) ?? [];
|
||||
}
|
||||
|
||||
public function sortPhotosForPreview(array &$photos)
|
||||
public function sortPhotosForPreview(array &$photos): void
|
||||
{
|
||||
// Do nothing, the photos are already sorted by added date desc
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ abstract class Backend
|
|||
/**
|
||||
* Human readable name for the cluster.
|
||||
*/
|
||||
public function clusterName(string $name)
|
||||
public function clusterName(string $name): string
|
||||
{
|
||||
return $name;
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ abstract class Backend
|
|||
* Put the photo objects in priority list.
|
||||
* Works on the array in place.
|
||||
*/
|
||||
public function sortPhotosForPreview(array &$photos)
|
||||
public function sortPhotosForPreview(array &$photos): void
|
||||
{
|
||||
shuffle($photos);
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ class FaceRecognitionBackend extends Backend
|
|||
return $this->tq->executeQueryWithCTEs($query)->fetchAll() ?: [];
|
||||
}
|
||||
|
||||
public function sortPhotosForPreview(array &$photos)
|
||||
public function sortPhotosForPreview(array &$photos): void
|
||||
{
|
||||
// Convert to recognize format (percentage position-size)
|
||||
foreach ($photos as &$p) {
|
||||
|
@ -216,15 +216,15 @@ class FaceRecognitionBackend extends Backend
|
|||
|
||||
private function model(): int
|
||||
{
|
||||
return (int) $this->config->getAppValue('facerecognition', 'model', -1);
|
||||
return (int) $this->config->getAppValue('facerecognition', 'model', (string) -1);
|
||||
}
|
||||
|
||||
private function minFaceInClusters(): int
|
||||
{
|
||||
return (int) $this->config->getAppValue('facerecognition', 'min_faces_in_cluster', 5);
|
||||
return (int) $this->config->getAppValue('facerecognition', 'min_faces_in_cluster', (string) 5);
|
||||
}
|
||||
|
||||
private function getFaceRecognitionClusters(int $fileid = 0)
|
||||
private function getFaceRecognitionClusters(int $fileid = 0): array
|
||||
{
|
||||
$query = $this->tq->getBuilder();
|
||||
|
||||
|
@ -276,7 +276,7 @@ class FaceRecognitionBackend extends Backend
|
|||
return $this->tq->executeQueryWithCTEs($query)->fetchAll() ?: [];
|
||||
}
|
||||
|
||||
private function getFaceRecognitionPersons(int $fileid = 0)
|
||||
private function getFaceRecognitionPersons(int $fileid = 0): array
|
||||
{
|
||||
$query = $this->tq->getBuilder();
|
||||
|
||||
|
|
|
@ -87,11 +87,13 @@ trait PeopleBackendUtils
|
|||
* @param array $photo The face object
|
||||
* @param float $padding The padding to add around the face
|
||||
*
|
||||
* @return [Blob, mimetype] of resulting image
|
||||
* @return string[] [Blob, mimetype] of resulting image
|
||||
*
|
||||
* @throws \Exception if file could not be used
|
||||
*
|
||||
* @psalm-return list{string, string}
|
||||
*/
|
||||
private function cropFace($file, array $photo, float $padding)
|
||||
private function cropFace($file, array $photo, float $padding): array
|
||||
{
|
||||
$img = new \OCP\Image();
|
||||
$img->loadFromData($file->getContent());
|
||||
|
@ -121,6 +123,13 @@ trait PeopleBackendUtils
|
|||
// Max 512x512
|
||||
$img->scaleDownToFit(512, 512);
|
||||
|
||||
return [$img->data(), $img->mimeType()];
|
||||
// Get blob and mimetype
|
||||
$data = $img->data();
|
||||
$mime = $img->mimeType();
|
||||
if (null === $data || null === $mime) {
|
||||
throw new \Exception('Could not get image data');
|
||||
}
|
||||
|
||||
return [$data, $mime];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -231,7 +231,7 @@ class RecognizeBackend extends Backend
|
|||
return $this->tq->executeQueryWithCTEs($query)->fetchAll() ?: [];
|
||||
}
|
||||
|
||||
public function sortPhotosForPreview(array &$photos)
|
||||
public function sortPhotosForPreview(array &$photos): void
|
||||
{
|
||||
$this->sortByScores($photos);
|
||||
}
|
||||
|
|
|
@ -203,7 +203,7 @@ class Index extends Command
|
|||
*
|
||||
* @param mixed $closure
|
||||
*/
|
||||
private function runForUsers($closure)
|
||||
private function runForUsers($closure): void
|
||||
{
|
||||
if ($uid = $this->opts->user) {
|
||||
if ($user = $this->userManager->get($uid)) {
|
||||
|
|
|
@ -296,7 +296,12 @@ class MigrateGoogleTakeout extends Command
|
|||
++$this->nProcessed;
|
||||
}
|
||||
|
||||
protected function takeoutToExiftoolJson(array $json)
|
||||
/**
|
||||
* @return (float|mixed|string)[]
|
||||
*
|
||||
* @psalm-return array<string, float|mixed|string>
|
||||
*/
|
||||
protected function takeoutToExiftoolJson(array $json): array
|
||||
{
|
||||
// Helper to get a value from nested JSON
|
||||
$get = static function (string $source) use ($json) {
|
||||
|
|
|
@ -109,9 +109,9 @@ class AdminController extends GenericApiController
|
|||
$status['indexed_count'] = $index->getIndexedCount();
|
||||
|
||||
// Automatic indexing stats
|
||||
$jobStart = $config->getAppValue(Application::APPNAME, 'last_index_job_start', 0);
|
||||
$jobStart = (int) $config->getAppValue(Application::APPNAME, 'last_index_job_start', (string) 0);
|
||||
$status['last_index_job_start'] = $jobStart ? time() - $jobStart : 0; // Seconds ago
|
||||
$status['last_index_job_duration'] = $config->getAppValue(Application::APPNAME, 'last_index_job_duration', 0);
|
||||
$status['last_index_job_duration'] = (float) $config->getAppValue(Application::APPNAME, 'last_index_job_duration', (string) 0);
|
||||
$status['last_index_job_status'] = $config->getAppValue(Application::APPNAME, 'last_index_job_status', 'Indexing has not been run yet');
|
||||
$status['last_index_job_status_type'] = $config->getAppValue(Application::APPNAME, 'last_index_job_status_type', 'warning');
|
||||
|
||||
|
|
|
@ -134,7 +134,7 @@ class DaysController extends GenericApiController
|
|||
/**
|
||||
* Get transformations depending on the request.
|
||||
*/
|
||||
private function getTransformations()
|
||||
private function getTransformations(): array
|
||||
{
|
||||
$transforms = [];
|
||||
|
||||
|
@ -180,7 +180,7 @@ class DaysController extends GenericApiController
|
|||
*
|
||||
* @param array $days the days array
|
||||
*/
|
||||
private function preloadDays(array &$days)
|
||||
private function preloadDays(array &$days): void
|
||||
{
|
||||
// Do not preload anything for native clients.
|
||||
// Since the contents of preloads are trusted, clients will not load locals.
|
||||
|
@ -234,7 +234,7 @@ class DaysController extends GenericApiController
|
|||
* Convert days response to months response.
|
||||
* The dayId is used to group the days into months.
|
||||
*/
|
||||
private function daysToMonths(array $days)
|
||||
private function daysToMonths(array $days): array
|
||||
{
|
||||
$months = [];
|
||||
foreach ($days as $day) {
|
||||
|
@ -255,45 +255,49 @@ class DaysController extends GenericApiController
|
|||
return $months;
|
||||
}
|
||||
|
||||
/** Convert list of month IDs to list of dayIds */
|
||||
private function monthIdToDayIds(int $monthId)
|
||||
/**
|
||||
* Convert list of month IDs to list of dayIds.
|
||||
*
|
||||
* @return int[] The list of dayIds
|
||||
*/
|
||||
private function monthIdToDayIds(int $monthId): array
|
||||
{
|
||||
$dayIds = [];
|
||||
$firstDay = (int) $monthId;
|
||||
$lastDay = strtotime(date('Ymt', $firstDay * 86400)) / 86400;
|
||||
for ($i = $firstDay; $i <= $lastDay; ++$i) {
|
||||
$dayIds[] = (string) $i;
|
||||
$dayIds[] = $i;
|
||||
}
|
||||
|
||||
return $dayIds;
|
||||
}
|
||||
|
||||
private function isRecursive()
|
||||
private function isRecursive(): bool
|
||||
{
|
||||
return null === $this->request->getParam('folder') || $this->request->getParam('recursive');
|
||||
}
|
||||
|
||||
private function isArchive()
|
||||
private function isArchive(): bool
|
||||
{
|
||||
return null !== $this->request->getParam('archive');
|
||||
}
|
||||
|
||||
private function isHidden()
|
||||
private function isHidden(): bool
|
||||
{
|
||||
return null !== $this->request->getParam('hidden');
|
||||
}
|
||||
|
||||
private function noPreload()
|
||||
private function noPreload(): bool
|
||||
{
|
||||
return null !== $this->request->getParam('nopreload');
|
||||
}
|
||||
|
||||
private function isMonthView()
|
||||
private function isMonthView(): bool
|
||||
{
|
||||
return null !== $this->request->getParam('monthView');
|
||||
}
|
||||
|
||||
private function isReverse()
|
||||
private function isReverse(): bool
|
||||
{
|
||||
return null !== $this->request->getParam('reverse');
|
||||
}
|
||||
|
|
|
@ -282,7 +282,7 @@ class DownloadController extends GenericApiController
|
|||
/** @var bool|resource */
|
||||
$handle = false;
|
||||
|
||||
/** @var ?File */
|
||||
/** @var ?\OCP\Files\File */
|
||||
$file = null;
|
||||
|
||||
/** @var ?string */
|
||||
|
|
|
@ -50,7 +50,7 @@ class ImageController extends GenericApiController
|
|||
int $y = 32,
|
||||
bool $a = false,
|
||||
string $mode = 'fill'
|
||||
) {
|
||||
): Http\Response {
|
||||
return Util::guardEx(function () use ($id, $x, $y, $a, $mode) {
|
||||
if (-1 === $id || 0 === $x || 0 === $y) {
|
||||
throw Exceptions::MissingParameter('id, x, y');
|
||||
|
|
|
@ -77,7 +77,7 @@ class OtherController extends GenericApiController
|
|||
}
|
||||
|
||||
// helper function to get user config values
|
||||
$getAppConfig = function ($key, $default) use ($uid) {
|
||||
$getAppConfig = function ($key, $default) use ($uid): string {
|
||||
return $this->config->getUserValue($uid, Application::APPNAME, $key, $default);
|
||||
};
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ class PageController extends Controller
|
|||
{
|
||||
// Image domains MUST be added to the connect domain list
|
||||
// because of the service worker fetch() call
|
||||
$addImageDomain = static function ($url) use (&$policy) {
|
||||
$addImageDomain = static function ($url) use (&$policy): void {
|
||||
$policy->addAllowedImageDomain($url);
|
||||
$policy->addAllowedConnectDomain($url);
|
||||
};
|
||||
|
@ -108,7 +108,7 @@ class PageController extends Controller
|
|||
/**
|
||||
* Get params for main.php template.
|
||||
*/
|
||||
public static function getMainParams()
|
||||
public static function getMainParams(): array
|
||||
{
|
||||
return [
|
||||
'native' => Util::callerIsNative(),
|
||||
|
|
|
@ -139,7 +139,7 @@ class PublicAlbumController extends Controller
|
|||
return $downloadController->file($handle);
|
||||
}
|
||||
|
||||
private function addOgMetadata(array $album, string $token)
|
||||
private function addOgMetadata(array $album, string $token): void
|
||||
{
|
||||
$fileId = (int) $album['last_added_photo'];
|
||||
$albumId = (int) $album['album_id'];
|
||||
|
|
|
@ -111,7 +111,7 @@ class ShareController extends GenericApiController
|
|||
});
|
||||
}
|
||||
|
||||
private function getNodeByIdOrPath($id, $path)
|
||||
private function getNodeByIdOrPath($id, $path): \OCP\Files\Node
|
||||
{
|
||||
$uid = Util::getUID();
|
||||
|
||||
|
@ -133,7 +133,7 @@ class ShareController extends GenericApiController
|
|||
return $file;
|
||||
}
|
||||
|
||||
private function makeShareResponse(\OCP\Share\IShare $share)
|
||||
private function makeShareResponse(\OCP\Share\IShare $share): array
|
||||
{
|
||||
/** @var \OCP\IURLGenerator $urlGenerator */
|
||||
$urlGenerator = \OC::$server->get(\OCP\IURLGenerator::class);
|
||||
|
|
|
@ -118,7 +118,7 @@ class VideoController extends GenericApiController
|
|||
string $liveid = '',
|
||||
string $format = '',
|
||||
string $transcode = ''
|
||||
) {
|
||||
): Http\Response {
|
||||
return Util::guardEx(function () use ($fileid, $liveid, $format, $transcode) {
|
||||
$file = $this->fs->getUserFile($fileid);
|
||||
|
||||
|
@ -230,7 +230,7 @@ class VideoController extends GenericApiController
|
|||
});
|
||||
}
|
||||
|
||||
private function getUpstream(string $client, string $path, string $profile)
|
||||
private function getUpstream(string $client, string $path, string $profile): int
|
||||
{
|
||||
$returnCode = $this->getUpstreamInternal($client, $path, $profile);
|
||||
|
||||
|
@ -251,7 +251,7 @@ class VideoController extends GenericApiController
|
|||
return $returnCode;
|
||||
}
|
||||
|
||||
private function getUpstreamInternal(string $client, string $path, string $profile)
|
||||
private function getUpstreamInternal(string $client, string $path, string $profile): int
|
||||
{
|
||||
// Make sure query params are repeated
|
||||
// For example, in folder sharing, we need the params on every request
|
||||
|
|
|
@ -39,7 +39,7 @@ class IndexJob extends TimedJob
|
|||
$this->setInterval(INTERVAL);
|
||||
}
|
||||
|
||||
protected function run($arguments)
|
||||
protected function run($argument)
|
||||
{
|
||||
// Check if indexing is enabled
|
||||
if ('0' === Util::getSystemConfig('memories.index.mode')) {
|
||||
|
@ -47,12 +47,12 @@ class IndexJob extends TimedJob
|
|||
}
|
||||
|
||||
// Store the last run time
|
||||
$this->config->setAppValue(Application::APPNAME, 'last_index_job_start', time());
|
||||
$this->config->setAppValue(Application::APPNAME, 'last_index_job_duration', 0);
|
||||
$this->config->setAppValue(Application::APPNAME, 'last_index_job_start', (string) time());
|
||||
$this->config->setAppValue(Application::APPNAME, 'last_index_job_duration', (string) 0);
|
||||
|
||||
// Run for a maximum of 5 minutes
|
||||
$startTime = microtime(true);
|
||||
$this->service->continueCheck = static function () use ($startTime) {
|
||||
$this->service->continueCheck = static function () use ($startTime): bool {
|
||||
return (microtime(true) - $startTime) < MAX_RUN_TIME;
|
||||
};
|
||||
|
||||
|
@ -81,7 +81,7 @@ class IndexJob extends TimedJob
|
|||
|
||||
// Store the last run duration
|
||||
$duration = round(microtime(true) - $startTime, 2);
|
||||
$this->config->setAppValue(Application::APPNAME, 'last_index_job_duration', $duration);
|
||||
$this->config->setAppValue(Application::APPNAME, 'last_index_job_duration', (string) $duration);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,7 @@ class AddMissingIndices
|
|||
/**
|
||||
* Add missing indices to the database schema.
|
||||
*/
|
||||
public static function run(IOutput $output)
|
||||
public static function run(IOutput $output): SchemaWrapper
|
||||
{
|
||||
$connection = \OC::$server->get(\OC\DB\Connection::class);
|
||||
$schema = new SchemaWrapper($connection);
|
||||
|
|
|
@ -22,7 +22,7 @@ class AlbumsQuery
|
|||
* @param bool $shared Whether to get shared albums
|
||||
* @param int $fileid File to filter by
|
||||
*/
|
||||
public function getList(string $uid, bool $shared = false, int $fileid = 0)
|
||||
public function getList(string $uid, bool $shared = false, int $fileid = 0): array
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
|
@ -234,7 +234,7 @@ class AlbumsQuery
|
|||
/**
|
||||
* Get list of photos in album.
|
||||
*/
|
||||
public function getAlbumPhotos(int $albumId, ?int $limit)
|
||||
public function getAlbumPhotos(int $albumId, ?int $limit): array
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
|
@ -283,8 +283,10 @@ class AlbumsQuery
|
|||
return $groups;
|
||||
}
|
||||
|
||||
/** Get the name of the collaborators table */
|
||||
private function collaboratorsTable()
|
||||
/**
|
||||
* Get the name of the collaborators table.
|
||||
*/
|
||||
private function collaboratorsTable(): string
|
||||
{
|
||||
// https://github.com/nextcloud/photos/commit/20e3e61ad577014e5f092a292c90a8476f630355
|
||||
$appManager = \OC::$server->get(\OCP\App\IAppManager::class);
|
||||
|
|
|
@ -74,7 +74,7 @@ class FsManager
|
|||
* @param TimelineRoot $root Root object to populate (by reference)
|
||||
* @param bool $recursive Whether to get the folders recursively
|
||||
*/
|
||||
public function populateRoot(TimelineRoot &$root, bool $recursive = true)
|
||||
public function populateRoot(TimelineRoot &$root, bool $recursive = true): TimelineRoot
|
||||
{
|
||||
$user = $this->userSession->getUser();
|
||||
|
||||
|
@ -183,7 +183,7 @@ class FsManager
|
|||
/**
|
||||
* Get a file with ID for the current user.
|
||||
*
|
||||
* @throws Exceptions\NotFoundFile
|
||||
* @throws \OCA\Memories\HttpResponseException
|
||||
*/
|
||||
public function getUserFile(int $fileId): File
|
||||
{
|
||||
|
@ -298,7 +298,7 @@ class FsManager
|
|||
return null;
|
||||
}
|
||||
|
||||
public function getShareObject()
|
||||
public function getShareObject(): ?IShare
|
||||
{
|
||||
// Get token from request
|
||||
$token = $this->getShareToken();
|
||||
|
|
|
@ -18,8 +18,10 @@ class LivePhoto
|
|||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/** Check if a given Exif data is the video part of a Live Photo */
|
||||
public function isVideoPart(array $exif)
|
||||
/**
|
||||
* Check if a given Exif data is the video part of a Live Photo.
|
||||
*/
|
||||
public function isVideoPart(array $exif): bool
|
||||
{
|
||||
return \array_key_exists('MIMEType', $exif)
|
||||
&& 'video/quicktime' === $exif['MIMEType']
|
||||
|
|
|
@ -37,16 +37,19 @@ class TimelineQuery
|
|||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function allowEmptyRoot(bool $value = true)
|
||||
public function allowEmptyRoot(bool $value = true): void
|
||||
{
|
||||
$this->_rootEmptyAllowed = $value;
|
||||
}
|
||||
|
||||
public function getBuilder()
|
||||
public function getBuilder(): IQueryBuilder
|
||||
{
|
||||
return $this->connection->getQueryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return never
|
||||
*/
|
||||
public static function debugQuery(IQueryBuilder &$query, string $sql = '')
|
||||
{
|
||||
// Print the query and exit
|
||||
|
@ -58,7 +61,7 @@ class TimelineQuery
|
|||
exit; // only for debugging, so this is okay
|
||||
}
|
||||
|
||||
public static function replaceQueryParams(IQueryBuilder &$query, string $sql)
|
||||
public static function replaceQueryParams(IQueryBuilder &$query, string $sql): string
|
||||
{
|
||||
$params = $query->getParameters();
|
||||
$platform = $query->getConnection()->getDatabasePlatform();
|
||||
|
|
|
@ -57,11 +57,11 @@ trait TimelineQueryDays
|
|||
/**
|
||||
* Get the day response from the database for the timeline.
|
||||
*
|
||||
* @param int[] $day_ids The day ids to fetch
|
||||
* @param bool $recursive If the query should be recursive
|
||||
* @param bool $archive If the query should include only the archive folder
|
||||
* @param bool $hidden If the query should include hidden files
|
||||
* @param array $queryTransforms The query transformations to apply
|
||||
* @param ?int[] $day_ids The day ids to fetch
|
||||
* @param bool $recursive If the query should be recursive
|
||||
* @param bool $archive If the query should include only the archive folder
|
||||
* @param bool $hidden If the query should include hidden files
|
||||
* @param array $queryTransforms The query transformations to apply
|
||||
*
|
||||
* @return array An array of day responses
|
||||
*/
|
||||
|
@ -124,7 +124,7 @@ trait TimelineQueryDays
|
|||
return $day;
|
||||
}
|
||||
|
||||
public function executeQueryWithCTEs(IQueryBuilder $query, string $psql = '')
|
||||
public function executeQueryWithCTEs(IQueryBuilder $query, string $psql = ''): \OCP\DB\IResult
|
||||
{
|
||||
$sql = empty($psql) ? $query->getSQL() : $psql;
|
||||
$params = $query->getParameters();
|
||||
|
@ -211,7 +211,7 @@ trait TimelineQueryDays
|
|||
*
|
||||
* @param array $days
|
||||
*/
|
||||
private function processDays($days)
|
||||
private function processDays($days): array
|
||||
{
|
||||
foreach ($days as &$row) {
|
||||
$row['dayid'] = (int) $row['dayid'];
|
||||
|
@ -224,7 +224,7 @@ trait TimelineQueryDays
|
|||
/**
|
||||
* Process the single day response.
|
||||
*/
|
||||
private function processDayPhoto(array &$row)
|
||||
private function processDayPhoto(array &$row): void
|
||||
{
|
||||
// Convert field types
|
||||
$row['fileid'] = (int) $row['fileid'];
|
||||
|
@ -278,7 +278,7 @@ trait TimelineQueryDays
|
|||
TimelineRoot &$root,
|
||||
bool $archive,
|
||||
bool $hidden
|
||||
) {
|
||||
): void {
|
||||
// Add query parameters
|
||||
$query->setParameter('topFolderIds', $root->getIds(), IQueryBuilder::PARAM_INT_ARRAY);
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use OCP\ITags;
|
|||
|
||||
trait TimelineQueryFilters
|
||||
{
|
||||
public function transformFavoriteFilter(IQueryBuilder &$query, bool $aggregate)
|
||||
public function transformFavoriteFilter(IQueryBuilder &$query, bool $aggregate): void
|
||||
{
|
||||
if (Util::isLoggedIn()) {
|
||||
$query->innerJoin('m', 'vcategory_to_object', 'vcoi', $query->expr()->andX(
|
||||
|
@ -20,7 +20,7 @@ trait TimelineQueryFilters
|
|||
}
|
||||
}
|
||||
|
||||
public function addFavoriteTag(IQueryBuilder &$query)
|
||||
public function addFavoriteTag(IQueryBuilder &$query): void
|
||||
{
|
||||
if (Util::isLoggedIn()) {
|
||||
$query->leftJoin('m', 'vcategory_to_object', 'vco', $query->expr()->andX(
|
||||
|
@ -31,12 +31,12 @@ trait TimelineQueryFilters
|
|||
}
|
||||
}
|
||||
|
||||
public function transformVideoFilter(IQueryBuilder &$query, bool $aggregate)
|
||||
public function transformVideoFilter(IQueryBuilder &$query, bool $aggregate): void
|
||||
{
|
||||
$query->andWhere($query->expr()->eq('m.isvideo', $query->expr()->literal(1)));
|
||||
}
|
||||
|
||||
public function transformLimit(IQueryBuilder &$query, bool $aggregate, int $limit)
|
||||
public function transformLimit(IQueryBuilder &$query, bool $aggregate, int $limit): void
|
||||
{
|
||||
if ($limit >= 1 || $limit <= 100) {
|
||||
$query->setMaxResults($limit);
|
||||
|
|
|
@ -17,7 +17,7 @@ trait TimelineQueryFolders
|
|||
*
|
||||
* @param TimelineRoot $root The root to use for the query
|
||||
*/
|
||||
public function getRootPreviews(TimelineRoot $root)
|
||||
public function getRootPreviews(TimelineRoot $root): array
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace OCA\Memories\Db;
|
|||
|
||||
trait TimelineQueryLivePhoto
|
||||
{
|
||||
public function getLivePhotos(int $fileid)
|
||||
public function getLivePhotos(int $fileid): array
|
||||
{
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->select('lp.fileid', 'lp.liveid')
|
||||
|
|
|
@ -13,7 +13,7 @@ trait TimelineQueryMap
|
|||
|
||||
protected IDBConnection $connection;
|
||||
|
||||
public function transformMapBoundsFilter(IQueryBuilder &$query, bool $aggregate, string $bounds, string $table = 'm')
|
||||
public function transformMapBoundsFilter(IQueryBuilder &$query, bool $aggregate, string $bounds, string $table = 'm'): void
|
||||
{
|
||||
$bounds = explode(',', $bounds);
|
||||
$bounds = array_map('floatval', $bounds);
|
||||
|
@ -84,7 +84,7 @@ trait TimelineQueryMap
|
|||
return $clusters;
|
||||
}
|
||||
|
||||
public function getMapClusterPreviews(array $clusterIds)
|
||||
public function getMapClusterPreviews(array $clusterIds): array
|
||||
{
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
|
|||
|
||||
trait TimelineQueryNativeX
|
||||
{
|
||||
public function transformNativeQuery(IQueryBuilder &$query, bool $aggregate)
|
||||
public function transformNativeQuery(IQueryBuilder &$query, bool $aggregate): void
|
||||
{
|
||||
if (!$aggregate) {
|
||||
$query->addSelect('m.epoch', 'f.size', 'm.buid');
|
||||
|
|
|
@ -23,7 +23,7 @@ class TimelineRoot
|
|||
*
|
||||
* @throws \Exception if node is not valid readable folder
|
||||
*/
|
||||
public function addFolder(FileInfo $info)
|
||||
public function addFolder(FileInfo $info): void
|
||||
{
|
||||
$path = $info->getPath();
|
||||
|
||||
|
@ -42,7 +42,7 @@ class TimelineRoot
|
|||
/**
|
||||
* Add mountpoints recursively.
|
||||
*/
|
||||
public function addMountPoints()
|
||||
public function addMountPoints(): void
|
||||
{
|
||||
$manager = \OC\Files\Filesystem::getMountManager();
|
||||
foreach ($this->folderPaths as $id => $folderPath) {
|
||||
|
@ -67,7 +67,7 @@ class TimelineRoot
|
|||
*
|
||||
* @param string[] $paths The paths to exclude
|
||||
*/
|
||||
public function excludePaths(array $paths)
|
||||
public function excludePaths(array $paths): void
|
||||
{
|
||||
foreach ($paths as $path) {
|
||||
foreach ($this->folderPaths as $id => $folderPath) {
|
||||
|
@ -87,7 +87,7 @@ class TimelineRoot
|
|||
*
|
||||
* @param string $path The new base path
|
||||
*/
|
||||
public function baseChange(string $path)
|
||||
public function baseChange(string $path): void
|
||||
{
|
||||
foreach ($this->folderPaths as $id => $folderPath) {
|
||||
if (!str_starts_with($folderPath.'/', $path.'/')) {
|
||||
|
@ -101,11 +101,17 @@ class TimelineRoot
|
|||
return $this->folderPaths[$id];
|
||||
}
|
||||
|
||||
public function getIds()
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getIds(): array
|
||||
{
|
||||
return array_keys($this->folderPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|int
|
||||
*/
|
||||
public function getOneId()
|
||||
{
|
||||
return array_key_first($this->folders);
|
||||
|
@ -116,12 +122,12 @@ class TimelineRoot
|
|||
return $this->folders[$id];
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty($this->folderPaths);
|
||||
}
|
||||
|
||||
private function setFolder(int $id, ?FileInfo $fileInfo, ?string $path)
|
||||
private function setFolder(int $id, ?FileInfo $fileInfo, ?string $path): void
|
||||
{
|
||||
if (null !== $path) {
|
||||
$this->folderPaths[$id] = $path;
|
||||
|
|
|
@ -192,7 +192,7 @@ class TimelineWrite
|
|||
/**
|
||||
* Remove a file from the exif database.
|
||||
*/
|
||||
public function deleteFile(File &$file)
|
||||
public function deleteFile(File &$file): void
|
||||
{
|
||||
// Get full record
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
|
@ -255,7 +255,7 @@ class TimelineWrite
|
|||
/**
|
||||
* Clear the entire index. Does not need confirmation!
|
||||
*/
|
||||
public function clear()
|
||||
public function clear(): void
|
||||
{
|
||||
$p = $this->connection->getDatabasePlatform();
|
||||
foreach (array_merge(DELETE_TABLES, TRUNCATE_TABLES) as $table) {
|
||||
|
|
|
@ -22,7 +22,7 @@ trait TimelineWriteOrphans
|
|||
*/
|
||||
public function orphanAll(bool $value = true, ?array $fileIds = null, bool $onlyMain = false): int
|
||||
{
|
||||
$do = function (string $table) use ($value, $fileIds) {
|
||||
$do = function (string $table) use ($value, $fileIds): int {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->update($table)
|
||||
->set('orphan', $query->createNamedParameter($value, IQueryBuilder::PARAM_BOOL))
|
||||
|
@ -51,7 +51,7 @@ trait TimelineWriteOrphans
|
|||
* @param int $txnSize number of rows to process in a single transaction
|
||||
* @param \Closure $callback will be passed each row
|
||||
*/
|
||||
public function orphanAndRun(array $fields, int $txnSize, \Closure $callback)
|
||||
public function orphanAndRun(array $fields, int $txnSize, \Closure $callback): void
|
||||
{
|
||||
// Orphan all files. This means if we are interrupted,
|
||||
// it will lead to a re-index of the whole library!
|
||||
|
|
|
@ -156,8 +156,12 @@ trait TimelineWritePlaces
|
|||
* Read coordinates from array and round to 6 decimal places.
|
||||
*
|
||||
* Modifies the array to remove invalid coordinates.
|
||||
*
|
||||
* @return (null|float)[]
|
||||
*
|
||||
* @psalm-return list{float|null, float|null}
|
||||
*/
|
||||
private static function readCoord(array &$exif)
|
||||
private static function readCoord(array &$exif): array
|
||||
{
|
||||
$lat = \array_key_exists(LAT_KEY, $exif) ? round((float) $exif[LAT_KEY], 6) : null;
|
||||
$lon = \array_key_exists(LON_KEY, $exif) ? round((float) $exif[LON_KEY], 6) : null;
|
||||
|
|
36
lib/Exif.php
36
lib/Exif.php
|
@ -19,7 +19,7 @@ class Exif
|
|||
private static $staticPipes;
|
||||
private static $noStaticProc = false;
|
||||
|
||||
public static function closeStaticExiftoolProc()
|
||||
public static function closeStaticExiftoolProc(): void
|
||||
{
|
||||
try {
|
||||
if (self::$staticProc) {
|
||||
|
@ -35,13 +35,13 @@ class Exif
|
|||
}
|
||||
}
|
||||
|
||||
public static function restartStaticExiftoolProc()
|
||||
public static function restartStaticExiftoolProc(): void
|
||||
{
|
||||
self::closeStaticExiftoolProc();
|
||||
self::ensureStaticExiftoolProc();
|
||||
}
|
||||
|
||||
public static function ensureStaticExiftoolProc()
|
||||
public static function ensureStaticExiftoolProc(): void
|
||||
{
|
||||
if (self::$noStaticProc) {
|
||||
return;
|
||||
|
@ -68,7 +68,7 @@ class Exif
|
|||
/**
|
||||
* Get exif data as a JSON object from a Nextcloud file.
|
||||
*/
|
||||
public static function getExifFromFile(File $file)
|
||||
public static function getExifFromFile(File $file): array
|
||||
{
|
||||
try {
|
||||
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
|
||||
|
@ -106,8 +106,10 @@ class Exif
|
|||
return $exif;
|
||||
}
|
||||
|
||||
/** Get exif data as a JSON object from a local file path */
|
||||
public static function getExifFromLocalPath(string $path)
|
||||
/**
|
||||
* Get exif data as a JSON object from a local file path.
|
||||
*/
|
||||
public static function getExifFromLocalPath(string $path): array
|
||||
{
|
||||
if (null !== self::$staticProc) {
|
||||
self::ensureStaticExiftoolProc();
|
||||
|
@ -298,7 +300,7 @@ class Exif
|
|||
*
|
||||
* @throws \Exception on failure
|
||||
*/
|
||||
public static function setExif(string $path, array $data)
|
||||
public static function setExif(string $path, array $data): void
|
||||
{
|
||||
$data['SourceFile'] = $path;
|
||||
$raw = json_encode([$data], JSON_UNESCAPED_UNICODE);
|
||||
|
@ -328,7 +330,7 @@ class Exif
|
|||
}
|
||||
}
|
||||
|
||||
public static function setFileExif(File $file, array $data)
|
||||
public static function setFileExif(File $file, array $data): void
|
||||
{
|
||||
// Get path to local file so we can skip reading
|
||||
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
|
||||
|
@ -345,7 +347,7 @@ class Exif
|
|||
$file->touch();
|
||||
}
|
||||
|
||||
public static function getBinaryExifProp(string $path, string $prop)
|
||||
public static function getBinaryExifProp(string $path, string $prop): string
|
||||
{
|
||||
$pipes = [];
|
||||
$proc = proc_open(array_merge(self::getExiftool(), [$prop, '-n', '-b', $path]), [
|
||||
|
@ -368,7 +370,7 @@ class Exif
|
|||
}
|
||||
}
|
||||
|
||||
public static function getExifWithDuplicates(string $path)
|
||||
public static function getExifWithDuplicates(string $path): array
|
||||
{
|
||||
return self::getExifFromLocalPathWithSeparateProc($path, ['-U', '-G4']);
|
||||
}
|
||||
|
@ -378,8 +380,10 @@ class Exif
|
|||
return BinExt::getExiftool();
|
||||
}
|
||||
|
||||
/** Initialize static exiftool process for local reads */
|
||||
private static function initializeStaticExiftoolProc()
|
||||
/**
|
||||
* Initialize static exiftool process for local reads.
|
||||
*/
|
||||
private static function initializeStaticExiftoolProc(): void
|
||||
{
|
||||
self::closeStaticExiftoolProc();
|
||||
self::$staticProc = proc_open(array_merge(self::getExiftool(), ['-stay_open', 'true', '-@', '-']), [
|
||||
|
@ -397,7 +401,7 @@ class Exif
|
|||
* @param int $timeout milliseconds
|
||||
* @param string $delimiter null for eof
|
||||
*/
|
||||
private static function readOrTimeout($handle, int $timeout, ?string $delimiter = null)
|
||||
private static function readOrTimeout($handle, int $timeout, ?string $delimiter = null): string
|
||||
{
|
||||
$buf = '';
|
||||
$waitedMs = 0;
|
||||
|
@ -420,7 +424,7 @@ class Exif
|
|||
return $buf;
|
||||
}
|
||||
|
||||
private static function getExifFromLocalPathWithStaticProc(string $path)
|
||||
private static function getExifFromLocalPathWithStaticProc(string $path): array
|
||||
{
|
||||
$args = implode("\n", self::EXIFTOOL_ARGS);
|
||||
fwrite(self::$staticPipes[0], "{$path}\n{$args}\n-execute\n");
|
||||
|
@ -442,7 +446,7 @@ class Exif
|
|||
}
|
||||
}
|
||||
|
||||
private static function getExifFromLocalPathWithSeparateProc(string $path, array $extraArgs = [])
|
||||
private static function getExifFromLocalPathWithSeparateProc(string $path, array $extraArgs = []): array
|
||||
{
|
||||
$pipes = [];
|
||||
$proc = proc_open(array_merge(self::getExiftool(), self::EXIFTOOL_ARGS, $extraArgs, [$path]), [
|
||||
|
@ -468,7 +472,7 @@ class Exif
|
|||
}
|
||||
|
||||
/** Get json array from stdout of exiftool */
|
||||
private static function processStdout(string $stdout)
|
||||
private static function processStdout(string $stdout): array
|
||||
{
|
||||
$json = json_decode($stdout, true);
|
||||
if (!$json) {
|
||||
|
|
|
@ -26,6 +26,9 @@ use OCP\EventDispatcher\Event;
|
|||
use OCP\EventDispatcher\IEventListener;
|
||||
use OCP\ISession;
|
||||
|
||||
/**
|
||||
* @template-implements IEventListener<Event>
|
||||
*/
|
||||
class BeforeTemplateListener implements IEventListener
|
||||
{
|
||||
private ISession $session;
|
||||
|
|
|
@ -27,6 +27,9 @@ use OCP\EventDispatcher\IEventListener;
|
|||
use OCP\Files\Events\Node\NodeDeletedEvent;
|
||||
use OCP\Files\Folder;
|
||||
|
||||
/**
|
||||
* @template-implements IEventListener<Event>
|
||||
*/
|
||||
class PostDeleteListener implements IEventListener
|
||||
{
|
||||
private TimelineWrite $util;
|
||||
|
|
|
@ -25,6 +25,9 @@ use OCP\EventDispatcher\Event;
|
|||
use OCP\EventDispatcher\IEventListener;
|
||||
use OCP\ISession;
|
||||
|
||||
/**
|
||||
* @template-implements IEventListener<Event>
|
||||
*/
|
||||
class PostLogoutListener implements IEventListener
|
||||
{
|
||||
public const CLEAR_CACHE_KEY = 'memories_clear_cache';
|
||||
|
|
|
@ -29,6 +29,9 @@ use OCP\Files\Events\Node\NodeTouchedEvent;
|
|||
use OCP\Files\Events\Node\NodeWrittenEvent;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @template-implements IEventListener<Event>
|
||||
*/
|
||||
class PostWriteListener implements IEventListener
|
||||
{
|
||||
private TimelineWrite $timelineWrite;
|
||||
|
|
|
@ -164,7 +164,7 @@ class BinExt
|
|||
return "http://{$connect}/{$client}{$path}/{$profile}";
|
||||
}
|
||||
|
||||
public static function getGoVodConfig($local = false)
|
||||
public static function getGoVodConfig(bool $local = false): array
|
||||
{
|
||||
// Get config from system values
|
||||
$env = [
|
||||
|
@ -205,7 +205,7 @@ class BinExt
|
|||
/**
|
||||
* Get temp binary for go-vod.
|
||||
*/
|
||||
public static function getGoVodBin()
|
||||
public static function getGoVodBin(): string
|
||||
{
|
||||
$path = Util::getSystemConfig('memories.vod.path');
|
||||
|
||||
|
@ -332,8 +332,10 @@ class BinExt
|
|||
return $version;
|
||||
}
|
||||
|
||||
/** POST a new configuration to go-vod */
|
||||
public static function configureGoVod()
|
||||
/**
|
||||
* POST a new configuration to go-vod.
|
||||
*/
|
||||
public static function configureGoVod(): bool
|
||||
{
|
||||
// Get config
|
||||
$config = self::getGoVodConfig();
|
||||
|
@ -412,7 +414,7 @@ class BinExt
|
|||
return $ffmpegPath;
|
||||
}
|
||||
|
||||
public static function testFFmpeg(string $path, string $name)
|
||||
public static function testFFmpeg(string $path, string $name): string
|
||||
{
|
||||
$version = shell_exec("{$path} -version");
|
||||
if (!preg_match("/{$name} version \\S*/", $version, $matches)) {
|
||||
|
@ -422,12 +424,12 @@ class BinExt
|
|||
return explode(' ', $matches[0])[2];
|
||||
}
|
||||
|
||||
public static function testSystemPerl(string $path): string
|
||||
public static function testSystemPerl(string $path): ?string
|
||||
{
|
||||
if (($out = shell_exec("{$path} -e 'print \"OK\";'")) !== 'OK') {
|
||||
throw new \Exception('Failed to run test perl script: '.$out);
|
||||
}
|
||||
|
||||
return shell_exec("{$path} -e 'print $^V;'");
|
||||
return shell_exec("{$path} -e 'print $^V;'") ?: null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ class FileRobotImageState
|
|||
}
|
||||
}
|
||||
|
||||
private function _set(array $parent, string $key, string $ckey = null)
|
||||
private function _set(array $parent, string $key, string $ckey = null): void
|
||||
{
|
||||
$ckey ??= $key;
|
||||
if (\array_key_exists($key, $parent)) {
|
||||
|
@ -146,7 +146,7 @@ class FileRobotMagick
|
|||
$this->state = new FileRobotImageState($state);
|
||||
}
|
||||
|
||||
public function apply()
|
||||
public function apply(): \Imagick
|
||||
{
|
||||
// Ensure the image is in the correct colorspace
|
||||
if (\Imagick::COLORSPACE_SRGB !== $this->image->getColorspace()) {
|
||||
|
@ -179,7 +179,7 @@ class FileRobotMagick
|
|||
return $this->image;
|
||||
}
|
||||
|
||||
protected function applyCrop()
|
||||
protected function applyCrop(): void
|
||||
{
|
||||
if ($this->state->cropX || $this->state->cropY || $this->state->cropWidth || $this->state->cropHeight) {
|
||||
$iw = $this->image->getImageWidth();
|
||||
|
@ -193,7 +193,7 @@ class FileRobotMagick
|
|||
}
|
||||
}
|
||||
|
||||
protected function applyFlipRotation()
|
||||
protected function applyFlipRotation(): void
|
||||
{
|
||||
if ($this->state->isFlippedX) {
|
||||
$this->image->flopImage();
|
||||
|
@ -206,7 +206,7 @@ class FileRobotMagick
|
|||
}
|
||||
}
|
||||
|
||||
protected function applyResize()
|
||||
protected function applyResize(): void
|
||||
{
|
||||
if ($this->state->resizeWidth || $this->state->resizeHeight) {
|
||||
$this->image->resizeImage(
|
||||
|
@ -218,7 +218,7 @@ class FileRobotMagick
|
|||
}
|
||||
}
|
||||
|
||||
protected function applyBrighten(?float $value = null)
|
||||
protected function applyBrighten(?float $value = null): void
|
||||
{
|
||||
$brightness = $value ?? $this->state->brightness ?? 0;
|
||||
if (0 === $brightness) {
|
||||
|
@ -229,7 +229,7 @@ class FileRobotMagick
|
|||
$this->image->evaluateImage(\Imagick::EVALUATE_ADD, $brightness * 255 * 255, \Imagick::CHANNEL_ALL);
|
||||
}
|
||||
|
||||
protected function applyContrast(?float $value = null)
|
||||
protected function applyContrast(?float $value = null): void
|
||||
{
|
||||
$contrast = $value ?? $this->state->contrast ?? 0;
|
||||
if (0 === $contrast) {
|
||||
|
@ -246,7 +246,7 @@ class FileRobotMagick
|
|||
$this->image->functionImage(\Imagick::FUNCTION_POLYNOMIAL, [$m, $c], \Imagick::CHANNEL_ALL);
|
||||
}
|
||||
|
||||
protected function applyHSV(?float $hue = null, ?float $saturation = null, ?float $value = null)
|
||||
protected function applyHSV(?float $hue = null, ?float $saturation = null, ?float $value = null): void
|
||||
{
|
||||
$hue ??= $this->state->hue ?? 0;
|
||||
$saturation ??= $this->state->saturation ?? 0;
|
||||
|
@ -274,18 +274,17 @@ class FileRobotMagick
|
|||
$bg = 0.587 * $v - 0.586 * $vsu - 1.05 * $vsw;
|
||||
$bb = 0.114 * $v + 0.886 * $vsu - 0.2 * $vsw;
|
||||
|
||||
$colorMatrix = [
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
$this->image->colorMatrixImage([
|
||||
$rr, $rg, $rb, 0, 0,
|
||||
$gr, $gg, $gb, 0, 0,
|
||||
$br, $bg, $bb, 0, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 1,
|
||||
];
|
||||
|
||||
$this->image->colorMatrixImage($colorMatrix);
|
||||
]);
|
||||
}
|
||||
|
||||
protected function applyBlur()
|
||||
protected function applyBlur(): void
|
||||
{
|
||||
if ($this->state->blurRadius <= 0) {
|
||||
return;
|
||||
|
@ -296,7 +295,7 @@ class FileRobotMagick
|
|||
$this->image->blurImage(0, $sigma);
|
||||
}
|
||||
|
||||
protected function applyWarmth()
|
||||
protected function applyWarmth(): void
|
||||
{
|
||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/finetunes/Warmth.js#L17-L28
|
||||
$warmth = ($this->state->warmth ?? 0);
|
||||
|
@ -310,20 +309,21 @@ class FileRobotMagick
|
|||
}
|
||||
|
||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/components/tools/Filters/Filters.constants.js#L8
|
||||
protected function applyFilterInvert()
|
||||
protected function applyFilterInvert(): void
|
||||
{
|
||||
$this->image->negateImage(false);
|
||||
}
|
||||
|
||||
protected function applyFilterBlackAndWhite()
|
||||
protected function applyFilterBlackAndWhite(): void
|
||||
{
|
||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BlackAndWhite.js
|
||||
$this->image->thresholdImage(100 * 255);
|
||||
}
|
||||
|
||||
protected function applyFilterSepia()
|
||||
protected function applyFilterSepia(): void
|
||||
{
|
||||
// https://github.com/konvajs/konva/blob/master/src/filters/Sepia.ts
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
$this->image->colorMatrixImage([
|
||||
0.393, 0.769, 0.189, 0, 0,
|
||||
0.349, 0.686, 0.168, 0, 0,
|
||||
|
@ -333,267 +333,267 @@ class FileRobotMagick
|
|||
]);
|
||||
}
|
||||
|
||||
protected function applyFilterSolarize()
|
||||
protected function applyFilterSolarize(): void
|
||||
{
|
||||
// https://github.com/konvajs/konva/blob/master/src/filters/Solarize.ts
|
||||
$this->image->solarizeImage(128 * 255);
|
||||
}
|
||||
|
||||
protected function applyFilterClarendon()
|
||||
protected function applyFilterClarendon(): void
|
||||
{
|
||||
$this->applyBaseFilterBrightness(0.1);
|
||||
$this->applyBaseFilterContrast(0.1);
|
||||
$this->applyBaseFilterSaturation(0.15);
|
||||
}
|
||||
|
||||
protected function applyFilterGingham()
|
||||
protected function applyFilterGingham(): void
|
||||
{
|
||||
$this->applyBaseFilterSepia(0.04);
|
||||
$this->applyBaseFilterContrast(-0.15);
|
||||
}
|
||||
|
||||
protected function applyFilterMoon()
|
||||
protected function applyFilterMoon(): void
|
||||
{
|
||||
$this->applyBaseFilterGrayscale();
|
||||
$this->applyBaseFilterBrightness(0.1);
|
||||
}
|
||||
|
||||
protected function applyFilterLark()
|
||||
protected function applyFilterLark(): void
|
||||
{
|
||||
$this->applyBaseFilterBrightness(0.08);
|
||||
$this->applyBaseFilterAdjustRGB(1, 1.03, 1.05);
|
||||
$this->applyBaseFilterSaturation(0.12);
|
||||
}
|
||||
|
||||
protected function applyFilterReyes()
|
||||
protected function applyFilterReyes(): void
|
||||
{
|
||||
$this->applyBaseFilterSepia(0.4);
|
||||
$this->applyBaseFilterBrightness(0.13);
|
||||
$this->applyBaseFilterContrast(-0.05);
|
||||
}
|
||||
|
||||
protected function applyFilterJuno()
|
||||
protected function applyFilterJuno(): void
|
||||
{
|
||||
$this->applyBaseFilterAdjustRGB(1.01, 1.04, 1);
|
||||
$this->applyBaseFilterSaturation(0.3);
|
||||
}
|
||||
|
||||
protected function applyFilterSlumber()
|
||||
protected function applyFilterSlumber(): void
|
||||
{
|
||||
$this->applyBaseFilterBrightness(0.1);
|
||||
$this->applyBaseFilterSaturation(-0.5);
|
||||
}
|
||||
|
||||
protected function applyFilterCrema()
|
||||
protected function applyFilterCrema(): void
|
||||
{
|
||||
$this->applyBaseFilterAdjustRGB(1.04, 1, 1.02);
|
||||
$this->applyBaseFilterSaturation(-0.05);
|
||||
}
|
||||
|
||||
protected function applyFilterLudwig()
|
||||
protected function applyFilterLudwig(): void
|
||||
{
|
||||
$this->applyBaseFilterBrightness(0.05);
|
||||
$this->applyBaseFilterSaturation(-0.03);
|
||||
}
|
||||
|
||||
protected function applyFilterAden()
|
||||
protected function applyFilterAden(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(228, 130, 225, 0.13);
|
||||
$this->applyBaseFilterSaturation(-0.2);
|
||||
}
|
||||
|
||||
protected function applyFilterPerpetua()
|
||||
protected function applyFilterPerpetua(): void
|
||||
{
|
||||
$this->applyBaseFilterAdjustRGB(1.05, 1.1, 1);
|
||||
}
|
||||
|
||||
protected function applyFilterAmaro()
|
||||
protected function applyFilterAmaro(): void
|
||||
{
|
||||
$this->applyBaseFilterSaturation(0.3);
|
||||
$this->applyBaseFilterBrightness(0.15);
|
||||
}
|
||||
|
||||
protected function applyFilterMayfair()
|
||||
protected function applyFilterMayfair(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(230, 115, 108, 0.05);
|
||||
$this->applyBaseFilterSaturation(0.15);
|
||||
}
|
||||
|
||||
protected function applyFilterRise()
|
||||
protected function applyFilterRise(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(255, 170, 0, 0.1);
|
||||
$this->applyBaseFilterBrightness(0.09);
|
||||
$this->applyBaseFilterSaturation(0.1);
|
||||
}
|
||||
|
||||
protected function applyFilterHudson()
|
||||
protected function applyFilterHudson(): void
|
||||
{
|
||||
$this->applyBaseFilterAdjustRGB(1, 1, 1.25);
|
||||
$this->applyBaseFilterContrast(0.1);
|
||||
$this->applyBaseFilterBrightness(0.15);
|
||||
}
|
||||
|
||||
protected function applyFilterValencia()
|
||||
protected function applyFilterValencia(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(255, 225, 80, 0.08);
|
||||
$this->applyBaseFilterSaturation(0.1);
|
||||
$this->applyBaseFilterContrast(0.05);
|
||||
}
|
||||
|
||||
protected function applyFilterXpro2()
|
||||
protected function applyFilterXpro2(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(255, 255, 0, 0.07);
|
||||
$this->applyBaseFilterSaturation(0.2);
|
||||
$this->applyBaseFilterContrast(0.15);
|
||||
}
|
||||
|
||||
protected function applyFilterSierra()
|
||||
protected function applyFilterSierra(): void
|
||||
{
|
||||
$this->applyBaseFilterContrast(-0.15);
|
||||
$this->applyBaseFilterSaturation(0.1);
|
||||
}
|
||||
|
||||
protected function applyFilterWillow()
|
||||
protected function applyFilterWillow(): void
|
||||
{
|
||||
$this->applyBaseFilterGrayscale();
|
||||
$this->applyBaseFilterColorFilter(100, 28, 210, 0.03);
|
||||
$this->applyBaseFilterBrightness(0.1);
|
||||
}
|
||||
|
||||
protected function applyFilterLoFi()
|
||||
protected function applyFilterLoFi(): void
|
||||
{
|
||||
$this->applyBaseFilterContrast(0.15);
|
||||
$this->applyBaseFilterSaturation(0.2);
|
||||
}
|
||||
|
||||
protected function applyFilterInkwell()
|
||||
protected function applyFilterInkwell(): void
|
||||
{
|
||||
$this->applyBaseFilterGrayscale();
|
||||
}
|
||||
|
||||
protected function applyFilterHefe()
|
||||
protected function applyFilterHefe(): void
|
||||
{
|
||||
$this->applyBaseFilterContrast(0.1);
|
||||
$this->applyBaseFilterSaturation(0.15);
|
||||
}
|
||||
|
||||
protected function applyFilterNashville()
|
||||
protected function applyFilterNashville(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(220, 115, 188, 0.12);
|
||||
$this->applyBaseFilterContrast(-0.05);
|
||||
}
|
||||
|
||||
protected function applyFilterStinson()
|
||||
protected function applyFilterStinson(): void
|
||||
{
|
||||
$this->applyBaseFilterBrightness(0.1);
|
||||
$this->applyBaseFilterSepia(0.3);
|
||||
}
|
||||
|
||||
protected function applyFilterVesper()
|
||||
protected function applyFilterVesper(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(255, 225, 0, 0.05);
|
||||
$this->applyBaseFilterBrightness(0.06);
|
||||
$this->applyBaseFilterContrast(0.06);
|
||||
}
|
||||
|
||||
protected function applyFilterEarlybird()
|
||||
protected function applyFilterEarlybird(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(255, 165, 40, 0.2);
|
||||
}
|
||||
|
||||
protected function applyFilterBrannan()
|
||||
protected function applyFilterBrannan(): void
|
||||
{
|
||||
$this->applyBaseFilterContrast(0.2);
|
||||
$this->applyBaseFilterColorFilter(140, 10, 185, 0.1);
|
||||
}
|
||||
|
||||
protected function applyFilterSutro()
|
||||
protected function applyFilterSutro(): void
|
||||
{
|
||||
$this->applyBaseFilterBrightness(-0.1);
|
||||
$this->applyBaseFilterContrast(-0.1);
|
||||
}
|
||||
|
||||
protected function applyFilterToaster()
|
||||
protected function applyFilterToaster(): void
|
||||
{
|
||||
$this->applyBaseFilterSepia(0.1);
|
||||
$this->applyBaseFilterColorFilter(255, 145, 0, 0.2);
|
||||
}
|
||||
|
||||
protected function applyFilterWalden()
|
||||
protected function applyFilterWalden(): void
|
||||
{
|
||||
$this->applyBaseFilterBrightness(0.1);
|
||||
$this->applyBaseFilterColorFilter(255, 255, 0, 0.2);
|
||||
}
|
||||
|
||||
protected function applyFilterNinteenSeventySeven()
|
||||
protected function applyFilterNinteenSeventySeven(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(255, 25, 0, 0.15);
|
||||
$this->applyBaseFilterBrightness(0.1);
|
||||
}
|
||||
|
||||
protected function applyFilterKelvin()
|
||||
protected function applyFilterKelvin(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(255, 140, 0, 0.1);
|
||||
$this->applyBaseFilterAdjustRGB(1.15, 1.05, 1);
|
||||
$this->applyBaseFilterSaturation(0.35);
|
||||
}
|
||||
|
||||
protected function applyFilterMaven()
|
||||
protected function applyFilterMaven(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(225, 240, 0, 0.1);
|
||||
$this->applyBaseFilterSaturation(0.25);
|
||||
$this->applyBaseFilterContrast(0.05);
|
||||
}
|
||||
|
||||
protected function applyFilterGinza()
|
||||
protected function applyFilterGinza(): void
|
||||
{
|
||||
$this->applyBaseFilterSepia(0.06);
|
||||
$this->applyBaseFilterBrightness(0.1);
|
||||
}
|
||||
|
||||
protected function applyFilterSkyline()
|
||||
protected function applyFilterSkyline(): void
|
||||
{
|
||||
$this->applyBaseFilterSaturation(0.35);
|
||||
$this->applyBaseFilterBrightness(0.1);
|
||||
}
|
||||
|
||||
protected function applyFilterDogpatch()
|
||||
protected function applyFilterDogpatch(): void
|
||||
{
|
||||
$this->applyBaseFilterContrast(0.15);
|
||||
$this->applyBaseFilterBrightness(0.1);
|
||||
}
|
||||
|
||||
protected function applyFilterBrooklyn()
|
||||
protected function applyFilterBrooklyn(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(25, 240, 252, 0.05);
|
||||
$this->applyBaseFilterSepia(0.3);
|
||||
}
|
||||
|
||||
protected function applyFilterHelena()
|
||||
protected function applyFilterHelena(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(208, 208, 86, 0.2);
|
||||
$this->applyBaseFilterContrast(0.15);
|
||||
}
|
||||
|
||||
protected function applyFilterAshby()
|
||||
protected function applyFilterAshby(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(255, 160, 25, 0.1);
|
||||
$this->applyBaseFilterBrightness(0.1);
|
||||
}
|
||||
|
||||
protected function applyFilterCharmes()
|
||||
protected function applyFilterCharmes(): void
|
||||
{
|
||||
$this->applyBaseFilterColorFilter(255, 50, 80, 0.12);
|
||||
$this->applyBaseFilterContrast(0.05);
|
||||
}
|
||||
|
||||
protected function applyBaseFilterBrightness(float $value)
|
||||
protected function applyBaseFilterBrightness(float $value): void
|
||||
{
|
||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L2
|
||||
$this->applyBrighten($value);
|
||||
}
|
||||
|
||||
protected function applyBaseFilterContrast(float $value)
|
||||
protected function applyBaseFilterContrast(float $value): void
|
||||
{
|
||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L14
|
||||
$value *= 255;
|
||||
|
@ -606,16 +606,17 @@ class FileRobotMagick
|
|||
$this->image->functionImage(\Imagick::FUNCTION_POLYNOMIAL, [$m, $c], \Imagick::CHANNEL_ALL);
|
||||
}
|
||||
|
||||
protected function applyBaseFilterSaturation(float $value)
|
||||
protected function applyBaseFilterSaturation(float $value): void
|
||||
{
|
||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L24
|
||||
$this->applyHSV(0, $value, 0); // lazy
|
||||
}
|
||||
|
||||
protected function applyBaseFilterGrayscale()
|
||||
protected function applyBaseFilterGrayscale(): void
|
||||
{
|
||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L38
|
||||
// y = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
$this->image->colorMatrixImage([
|
||||
0.2126, 0.7152, 0.0722, 0, 0,
|
||||
0.2126, 0.7152, 0.0722, 0, 0,
|
||||
|
@ -625,9 +626,10 @@ class FileRobotMagick
|
|||
]);
|
||||
}
|
||||
|
||||
protected function applyBaseFilterSepia(float $value)
|
||||
protected function applyBaseFilterSepia(float $value): void
|
||||
{
|
||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L46
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
$this->image->colorMatrixImage([
|
||||
1 - 0.607 * $value, 0.769 * $value, 0.189 * $value, 0, 0,
|
||||
0.349 * $value, 1 - 0.314 * $value, 0.168 * $value, 0, 0,
|
||||
|
@ -637,9 +639,10 @@ class FileRobotMagick
|
|||
]);
|
||||
}
|
||||
|
||||
protected function applyBaseFilterAdjustRGB(float $r, float $g, float $b)
|
||||
protected function applyBaseFilterAdjustRGB(float $r, float $g, float $b): void
|
||||
{
|
||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L57
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
$this->image->colorMatrixImage([
|
||||
$r, 0, 0, 0, 0,
|
||||
0, $g, 0, 0, 0,
|
||||
|
@ -649,7 +652,7 @@ class FileRobotMagick
|
|||
]);
|
||||
}
|
||||
|
||||
protected function applyBaseFilterColorFilter(float $r, float $g, float $b, float $v)
|
||||
protected function applyBaseFilterColorFilter(float $r, float $g, float $b, float $v): void
|
||||
{
|
||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L63
|
||||
// y = x - (x - k) * v = (1 - v) * x + k * v
|
||||
|
|
|
@ -153,7 +153,7 @@ class Index
|
|||
;
|
||||
|
||||
// Filter out files that are already indexed
|
||||
$addFilter = static function (string $table, string $alias) use (&$query) {
|
||||
$addFilter = static function (string $table, string $alias) use (&$query): void {
|
||||
$query->leftJoin('f', $table, $alias, $query->expr()->andX(
|
||||
$query->expr()->eq('f.fileid', "{$alias}.fileid"),
|
||||
$query->expr()->eq('f.mtime', "{$alias}.mtime"),
|
||||
|
@ -224,7 +224,7 @@ class Index
|
|||
/**
|
||||
* Get total number of files that are indexed.
|
||||
*/
|
||||
public function getIndexedCount()
|
||||
public function getIndexedCount(): int
|
||||
{
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select($query->createFunction('COUNT(DISTINCT fileid)'))
|
||||
|
@ -282,8 +282,10 @@ class Index
|
|||
return \in_array($file->getMimeType(), Application::VIDEO_MIMES, true);
|
||||
}
|
||||
|
||||
/** Log to console if CLI or logger */
|
||||
private function error(string $message)
|
||||
/**
|
||||
* Log error to console if CLI or logger.
|
||||
*/
|
||||
private function error(string $message): void
|
||||
{
|
||||
$this->logger->error($message);
|
||||
|
||||
|
@ -292,8 +294,10 @@ class Index
|
|||
}
|
||||
}
|
||||
|
||||
/** Log to console if CLI */
|
||||
private function log(string $message, bool $overwrite = false)
|
||||
/**
|
||||
* Log to console if CLI.
|
||||
*/
|
||||
private function log(string $message, bool $overwrite = false): void
|
||||
{
|
||||
if ($this->output) {
|
||||
if ($overwrite) {
|
||||
|
|
|
@ -34,8 +34,10 @@ class Places
|
|||
|
||||
/**
|
||||
* Make SQL query to detect GIS type.
|
||||
*
|
||||
* @psalm-return 0|1|2
|
||||
*/
|
||||
public function detectGisType()
|
||||
public function detectGisType(): int
|
||||
{
|
||||
// Make sure database prefix is set
|
||||
$prefix = $this->config->getSystemValue('dbtableprefix', '') ?: '';
|
||||
|
@ -83,7 +85,7 @@ class Places
|
|||
public function geomCount(): int
|
||||
{
|
||||
try {
|
||||
return $this->connection->executeQuery('SELECT COUNT(osm_id) as c FROM memories_planet_geometry')->fetchOne();
|
||||
return (int) $this->connection->executeQuery('SELECT COUNT(osm_id) as c FROM memories_planet_geometry')->fetchOne();
|
||||
} catch (\Exception $e) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -244,7 +246,7 @@ class Places
|
|||
$txnCount = 0;
|
||||
|
||||
// Function to commit the current transaction
|
||||
$transact = function () use (&$txnCount) {
|
||||
$transact = function () use (&$txnCount): void {
|
||||
if (++$txnCount >= DB_TRANSACTION_SIZE) {
|
||||
$this->connection->commit();
|
||||
$this->connection->beginTransaction();
|
||||
|
@ -379,7 +381,7 @@ class Places
|
|||
/**
|
||||
* Recalculate all places for all users.
|
||||
*/
|
||||
public function recalculateAll()
|
||||
public function recalculateAll(): void
|
||||
{
|
||||
echo "Recalculating places for all files (do not interrupt this process)...\n";
|
||||
flush();
|
||||
|
|
25
lib/Util.php
25
lib/Util.php
|
@ -12,7 +12,6 @@ use OCP\App\IAppManager;
|
|||
use OCP\Files\Node;
|
||||
use OCP\Files\Search\ISearchBinaryOperator;
|
||||
use OCP\Files\Search\ISearchComparison;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IConfig;
|
||||
|
||||
class Util
|
||||
|
@ -23,8 +22,10 @@ class Util
|
|||
|
||||
/**
|
||||
* Get host CPU architecture (amd64 or aarch64).
|
||||
*
|
||||
* @psalm-return 'aarch64'|'amd64'|null
|
||||
*/
|
||||
public static function getArch()
|
||||
public static function getArch(): ?string
|
||||
{
|
||||
$uname = php_uname('m');
|
||||
if (false !== stripos($uname, 'aarch64') || false !== stripos($uname, 'arm64')) {
|
||||
|
@ -39,8 +40,10 @@ class Util
|
|||
|
||||
/**
|
||||
* Get the libc type for host (glibc or musl).
|
||||
*
|
||||
* @psalm-return 'glibc'|'musl'|null
|
||||
*/
|
||||
public static function getLibc()
|
||||
public static function getLibc(): ?string
|
||||
{
|
||||
if ($ldd = shell_exec('ldd --version 2>&1')) {
|
||||
if (false !== stripos($ldd, 'musl')) {
|
||||
|
@ -89,8 +92,8 @@ class Util
|
|||
return false;
|
||||
}
|
||||
|
||||
$config = \OC::$server->get(IAppConfig::class);
|
||||
if ('true' !== $config->getValue('recognize', 'faces.enabled', 'false')) {
|
||||
$config = \OC::$server->get(IConfig::class);
|
||||
if ('true' !== $config->getAppValue('recognize', 'faces.enabled', 'false')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -192,10 +195,12 @@ class Util
|
|||
* @param mixed $key Key to set
|
||||
* @param mixed $value Value to set
|
||||
*/
|
||||
public static function forceFileInfo(Node &$node, $key, $value)
|
||||
public static function forceFileInfo(Node &$node, $key, $value): void
|
||||
{
|
||||
/** @var \OC\Files\Node\Node */
|
||||
$node = $node;
|
||||
|
||||
/** @psalm-suppress UndefinedInterfaceMethod */
|
||||
$node->getFileInfo()[$key] = $value;
|
||||
}
|
||||
|
||||
|
@ -205,7 +210,7 @@ class Util
|
|||
* @param mixed $node File to patch
|
||||
* @param mixed $permissions Permissions to set
|
||||
*/
|
||||
public static function forcePermissions(Node &$node, int $permissions)
|
||||
public static function forcePermissions(Node &$node, int $permissions): void
|
||||
{
|
||||
self::forceFileInfo($node, 'permissions', $permissions);
|
||||
}
|
||||
|
@ -284,7 +289,7 @@ class Util
|
|||
*
|
||||
* @param $folder Folder to search
|
||||
*/
|
||||
public static function getAnyMedia(\OCP\Files\Folder $folder): Node
|
||||
public static function getAnyMedia(\OCP\Files\Folder $folder): ?Node
|
||||
{
|
||||
$query = new SearchQuery(new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
|
||||
new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', 'image/%'),
|
||||
|
@ -338,11 +343,11 @@ class Util
|
|||
/**
|
||||
* Sanitize a path to keep only ASCII characters and special characters.
|
||||
*/
|
||||
public static function sanitizePath(string $path): string
|
||||
public static function sanitizePath(string $path): ?string
|
||||
{
|
||||
$path = str_replace("\0", '', $path); // remove null characters
|
||||
|
||||
return mb_ereg_replace('\/\/+', '/', $path); // remove extra slashes
|
||||
return mb_ereg_replace('\/\/+', '/', $path) ?: null; // remove extra slashes
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,6 +32,7 @@ trait UtilController
|
|||
*/
|
||||
public static function guardExDirect(\Closure $closure): Http\Response
|
||||
{
|
||||
/** @psalm-suppress MissingTemplateParam */
|
||||
return new class($closure) extends Http\Response implements Http\ICallbackResponse {
|
||||
private \Closure $_closure;
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
totallyTyped="true"
|
||||
errorLevel="5"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="lib" />
|
||||
<ignoreFiles>
|
||||
<directory name="tools" />
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
<extraFiles>
|
||||
<directory name="../../lib" />
|
||||
|
||||
<directory name="../../3rdparty/doctrine" />
|
||||
<directory name="../../3rdparty/psr" />
|
||||
<directory name="../../3rdparty/guzzlehttp" />
|
||||
<directory name="../../3rdparty/doctrine" />
|
||||
|
||||
<directory name="../../apps/files/lib/Event" />
|
||||
</extraFiles>
|
||||
<issueHandlers>
|
||||
<UndefinedDocblockClass>
|
||||
<errorLevel type="suppress">
|
||||
<referencedClass name="Doctrine\DBAL\Schema\Schema" />
|
||||
<referencedClass name="Doctrine\DBAL\Schema\SchemaException" />
|
||||
<referencedClass name="Doctrine\DBAL\Driver\Statement" />
|
||||
<referencedClass name="Doctrine\DBAL\Schema\Table" />
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\AbstractPlatform" />
|
||||
</errorLevel>
|
||||
</UndefinedDocblockClass>
|
||||
</issueHandlers>
|
||||
</psalm>
|
Loading…
Reference in New Issue