diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/l10n/bg.js | 1 | ||||
-rw-r--r-- | lib/l10n/bg.json | 1 | ||||
-rw-r--r-- | lib/l10n/lt_LT.js | 1 | ||||
-rw-r--r-- | lib/l10n/lt_LT.json | 1 | ||||
-rw-r--r-- | lib/l10n/uk.js | 9 | ||||
-rw-r--r-- | lib/l10n/uk.json | 9 | ||||
-rw-r--r-- | lib/l10n/uz.js | 33 | ||||
-rw-r--r-- | lib/l10n/uz.json | 33 | ||||
-rw-r--r-- | lib/private/Avatar/Avatar.php | 37 | ||||
-rw-r--r-- | lib/private/Avatar/GuestAvatar.php | 4 | ||||
-rw-r--r-- | lib/private/Avatar/PlaceholderAvatar.php | 10 | ||||
-rw-r--r-- | lib/private/Avatar/UserAvatar.php | 46 | ||||
-rw-r--r-- | lib/private/Calendar/Manager.php | 17 | ||||
-rw-r--r-- | lib/private/Files/Node/Folder.php | 82 | ||||
-rw-r--r-- | lib/private/Files/SetupManager.php | 8 | ||||
-rw-r--r-- | lib/private/Files/Storage/Wrapper/Quota.php | 71 | ||||
-rw-r--r-- | lib/private/Repair/ClearGeneratedAvatarCache.php | 20 | ||||
-rw-r--r-- | lib/private/Settings/Manager.php | 7 | ||||
-rw-r--r-- | lib/private/User/User.php | 2 | ||||
-rw-r--r-- | lib/public/Color.php | 14 | ||||
-rw-r--r-- | lib/public/IAvatar.php | 6 | ||||
-rw-r--r-- | lib/public/Settings/IIconSection.php | 4 | ||||
-rw-r--r-- | lib/public/Settings/IManager.php | 6 |
23 files changed, 289 insertions, 133 deletions
diff --git a/lib/l10n/bg.js b/lib/l10n/bg.js index a0be77e48fa..4081facf7e8 100644 --- a/lib/l10n/bg.js +++ b/lib/l10n/bg.js @@ -92,6 +92,7 @@ OC.L10N.register( "Help" : "Помощ", "Apps" : "Приложения", "Personal settings" : "Лични настройки", + "Administration settings" : "Административни настройки", "Settings" : "Настройки", "Log out" : "Отписване", "Users" : "Потребители", diff --git a/lib/l10n/bg.json b/lib/l10n/bg.json index 52faf059f8c..35a4bf1a7a9 100644 --- a/lib/l10n/bg.json +++ b/lib/l10n/bg.json @@ -90,6 +90,7 @@ "Help" : "Помощ", "Apps" : "Приложения", "Personal settings" : "Лични настройки", + "Administration settings" : "Административни настройки", "Settings" : "Настройки", "Log out" : "Отписване", "Users" : "Потребители", diff --git a/lib/l10n/lt_LT.js b/lib/l10n/lt_LT.js index c029d11b399..93707397511 100644 --- a/lib/l10n/lt_LT.js +++ b/lib/l10n/lt_LT.js @@ -68,6 +68,7 @@ OC.L10N.register( "This is an automatically sent email, please do not reply." : "Tai yra automatinis pranešimas, prašome neatsakyti.", "Help" : "Pagalba", "Apps" : "Programėlės", + "Personal settings" : "Asmeniniai nustatymai", "Settings" : "Nustatymai", "Log out" : "Atsijungti", "Users" : "Naudotojai", diff --git a/lib/l10n/lt_LT.json b/lib/l10n/lt_LT.json index e4be438cd11..783eac7f717 100644 --- a/lib/l10n/lt_LT.json +++ b/lib/l10n/lt_LT.json @@ -66,6 +66,7 @@ "This is an automatically sent email, please do not reply." : "Tai yra automatinis pranešimas, prašome neatsakyti.", "Help" : "Pagalba", "Apps" : "Programėlės", + "Personal settings" : "Asmeniniai nustatymai", "Settings" : "Nustatymai", "Log out" : "Atsijungti", "Users" : "Naudotojai", diff --git a/lib/l10n/uk.js b/lib/l10n/uk.js index 8f204020c65..9705debbece 100644 --- a/lib/l10n/uk.js +++ b/lib/l10n/uk.js @@ -16,6 +16,7 @@ OC.L10N.register( "The command line tool %s could not be found" : "Утиліту командного рядка %s не знайдено", "The library %s is not available." : "Бібліотека %s недоступна.", "Server version %s or higher is required." : "Потрібна версія сервера %s або вище.", + "Server version %s or lower is required." : "Потрібна версія сервера %s або нижча.", "Remote wipe started" : "Розпочато віддалене стирання", "Remote wipe finished" : "Віддалене стирання завершено", "Authentication" : "Автентифікація", @@ -34,19 +35,24 @@ OC.L10N.register( "last year" : "минулого року", "_%n year ago_::_%n years ago_" : ["%n рік тому","%n років тому","%n років тому","%n років тому"], "_%n hour ago_::_%n hours ago_" : ["%nгодину тому","%n годин тому","%n годин тому","%n годин тому"], + "_%n minute ago_::_%n minutes ago_" : ["%n хвилину тому","%n хвилин тому","%n хвилин тому","%n хвилин тому"], "in a few seconds" : "через кілька секунд", "seconds ago" : "секунди тому", "Empty file" : "Порожній файл", "File already exists" : "Файл вже існує", + "Templates" : "Шаблони", "File name is a reserved word" : "Ім’я файлу є зарезервованим словом", "File name contains at least one invalid character" : "Ім’я файлу містить принаймні один некоректний символ", "File name is too long" : "Ім’я файлу занадто довге", "Dot files are not allowed" : "Файли які починаються з крапки не допустимі", "Empty filename is not allowed" : "Порожні імена файлів не допускаються", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Застосунок \"%s\" не може бути встановлений через те, що файл appinfo не може бути прочитано.", + "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Програму \"%s\" неможливо встановити, оскільки вона не сумісна з цією версією сервера.", "__language_name__" : "Українська", "Help" : "Допомога", "Apps" : "Застосунки", + "Personal settings" : "Персональні налаштування", + "Administration settings" : "Налаштування адміністрування", "Settings" : "Налаштування", "Log out" : "Вихід", "Users" : "Користувачі", @@ -58,6 +64,9 @@ OC.L10N.register( "Profile picture" : "Зображення облікового запису", "About" : "Про систему", "Full name" : "Повна назва", + "Headline" : "Заголовок", + "Organisation" : "Організація", + "Role" : "Роль", "Unknown user" : "Невідомий користувач", "Additional settings" : "Додаткові налаштування", "%s enter the database username and name." : "%s введіть назву бази даних та ім'я користувача.", diff --git a/lib/l10n/uk.json b/lib/l10n/uk.json index 24ae8d496ba..3d478f62337 100644 --- a/lib/l10n/uk.json +++ b/lib/l10n/uk.json @@ -14,6 +14,7 @@ "The command line tool %s could not be found" : "Утиліту командного рядка %s не знайдено", "The library %s is not available." : "Бібліотека %s недоступна.", "Server version %s or higher is required." : "Потрібна версія сервера %s або вище.", + "Server version %s or lower is required." : "Потрібна версія сервера %s або нижча.", "Remote wipe started" : "Розпочато віддалене стирання", "Remote wipe finished" : "Віддалене стирання завершено", "Authentication" : "Автентифікація", @@ -32,19 +33,24 @@ "last year" : "минулого року", "_%n year ago_::_%n years ago_" : ["%n рік тому","%n років тому","%n років тому","%n років тому"], "_%n hour ago_::_%n hours ago_" : ["%nгодину тому","%n годин тому","%n годин тому","%n годин тому"], + "_%n minute ago_::_%n minutes ago_" : ["%n хвилину тому","%n хвилин тому","%n хвилин тому","%n хвилин тому"], "in a few seconds" : "через кілька секунд", "seconds ago" : "секунди тому", "Empty file" : "Порожній файл", "File already exists" : "Файл вже існує", + "Templates" : "Шаблони", "File name is a reserved word" : "Ім’я файлу є зарезервованим словом", "File name contains at least one invalid character" : "Ім’я файлу містить принаймні один некоректний символ", "File name is too long" : "Ім’я файлу занадто довге", "Dot files are not allowed" : "Файли які починаються з крапки не допустимі", "Empty filename is not allowed" : "Порожні імена файлів не допускаються", "App \"%s\" cannot be installed because appinfo file cannot be read." : "Застосунок \"%s\" не може бути встановлений через те, що файл appinfo не може бути прочитано.", + "App \"%s\" cannot be installed because it is not compatible with this version of the server." : "Програму \"%s\" неможливо встановити, оскільки вона не сумісна з цією версією сервера.", "__language_name__" : "Українська", "Help" : "Допомога", "Apps" : "Застосунки", + "Personal settings" : "Персональні налаштування", + "Administration settings" : "Налаштування адміністрування", "Settings" : "Налаштування", "Log out" : "Вихід", "Users" : "Користувачі", @@ -56,6 +62,9 @@ "Profile picture" : "Зображення облікового запису", "About" : "Про систему", "Full name" : "Повна назва", + "Headline" : "Заголовок", + "Organisation" : "Організація", + "Role" : "Роль", "Unknown user" : "Невідомий користувач", "Additional settings" : "Додаткові налаштування", "%s enter the database username and name." : "%s введіть назву бази даних та ім'я користувача.", diff --git a/lib/l10n/uz.js b/lib/l10n/uz.js index cea4453992f..41cb4509e83 100644 --- a/lib/l10n/uz.js +++ b/lib/l10n/uz.js @@ -1,18 +1,25 @@ OC.L10N.register( "lib", { - "Cannot write into \"config\" directory!" : "\"Config\" katalogiga yozish mumkin emas!", - "Unknown filetype" : "Noma'lum filetype", - "Invalid image" : "Tasdiqlanmagan tasvir", - "seconds ago" : "soniya oldin", - "Help" : "Yordam", - "Apps" : "Ilovalar", - "Settings" : "Sozlamalar", - "Users" : "Foydalanuvchilar", - "Address" : "manzil", - "About" : "Biz haqimizda", - "Unknown user" : "Noma'lum foydalanuvchi", - "January" : "Yanvar", - "Storage is temporarily not available" : "Saqlash vaqti-vaqti bilan mavjud emas" + "Cannot write into \"config\" directory!" : "Cannot write into \"config\" directory!", + "Unknown filetype" : "Unknown filetype", + "Invalid image" : "Invalid image", + "View profile" : "View profile", + "last month" : "last month", + "seconds ago" : "seconds ago", + "File name is too long" : "File name is too long", + "Help" : "Help", + "Apps" : "Apps", + "Settings" : "Settings", + "Users" : "Users", + "Address" : "Address", + "Profile picture" : "Profil rasmi", + "About" : "About", + "Full name" : "Full name", + "Unknown user" : "Unknown user", + "Set an admin password." : "Set an admin password.", + "January" : "January", + "Authentication error" : "Authentication error", + "Storage is temporarily not available" : "Storage is temporarily not available" }, "nplurals=1; plural=0;"); diff --git a/lib/l10n/uz.json b/lib/l10n/uz.json index 3ad6192351f..bbddc8953c7 100644 --- a/lib/l10n/uz.json +++ b/lib/l10n/uz.json @@ -1,16 +1,23 @@ { "translations": { - "Cannot write into \"config\" directory!" : "\"Config\" katalogiga yozish mumkin emas!", - "Unknown filetype" : "Noma'lum filetype", - "Invalid image" : "Tasdiqlanmagan tasvir", - "seconds ago" : "soniya oldin", - "Help" : "Yordam", - "Apps" : "Ilovalar", - "Settings" : "Sozlamalar", - "Users" : "Foydalanuvchilar", - "Address" : "manzil", - "About" : "Biz haqimizda", - "Unknown user" : "Noma'lum foydalanuvchi", - "January" : "Yanvar", - "Storage is temporarily not available" : "Saqlash vaqti-vaqti bilan mavjud emas" + "Cannot write into \"config\" directory!" : "Cannot write into \"config\" directory!", + "Unknown filetype" : "Unknown filetype", + "Invalid image" : "Invalid image", + "View profile" : "View profile", + "last month" : "last month", + "seconds ago" : "seconds ago", + "File name is too long" : "File name is too long", + "Help" : "Help", + "Apps" : "Apps", + "Settings" : "Settings", + "Users" : "Users", + "Address" : "Address", + "Profile picture" : "Profil rasmi", + "About" : "About", + "Full name" : "Full name", + "Unknown user" : "Unknown user", + "Set an admin password." : "Set an admin password.", + "January" : "January", + "Authentication error" : "Authentication error", + "Storage is temporarily not available" : "Storage is temporarily not available" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/lib/private/Avatar/Avatar.php b/lib/private/Avatar/Avatar.php index 0eb8f8816d8..9b9220936eb 100644 --- a/lib/private/Avatar/Avatar.php +++ b/lib/private/Avatar/Avatar.php @@ -59,7 +59,7 @@ abstract class Avatar implements IAvatar { private string $svgTemplate = '<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="{size}" height="{size}" version="1.1" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg"> <rect width="100%" height="100%" fill="#{fill}"></rect> - <text x="50%" y="350" style="font-weight:normal;font-size:280px;font-family:\'Noto Sans\';text-anchor:middle;fill:#fff">{letter}</text> + <text x="50%" y="350" style="font-weight:normal;font-size:280px;font-family:\'Noto Sans\';text-anchor:middle;fill:#{fgFill}">{letter}</text> </svg>'; public function __construct(LoggerInterface $logger) { @@ -88,9 +88,9 @@ abstract class Avatar implements IAvatar { /** * @inheritdoc */ - public function get(int $size = 64) { + public function get(int $size = 64, bool $darkTheme = false) { try { - $file = $this->getFile($size); + $file = $this->getFile($size, $darkTheme); } catch (NotFoundException $e) { return false; } @@ -111,25 +111,27 @@ abstract class Avatar implements IAvatar { * @return string * */ - protected function getAvatarVector(int $size): string { + protected function getAvatarVector(int $size, bool $darkTheme): string { $userDisplayName = $this->getDisplayName(); - $bgRGB = $this->avatarBackgroundColor($userDisplayName); - $bgHEX = sprintf("%02x%02x%02x", $bgRGB->red(), $bgRGB->green(), $bgRGB->blue()); + $fgRGB = $this->avatarBackgroundColor($userDisplayName); + $bgRGB = $fgRGB->alphaBlending(0.1, $darkTheme ? new Color(0, 0, 0) : new Color(255, 255, 255)); + $fill = sprintf("%02x%02x%02x", $bgRGB->red(), $bgRGB->green(), $bgRGB->blue()); + $fgFill = sprintf("%02x%02x%02x", $fgRGB->red(), $fgRGB->green(), $fgRGB->blue()); $text = $this->getAvatarText(); - $toReplace = ['{size}', '{fill}', '{letter}']; - return str_replace($toReplace, [$size, $bgHEX, $text], $this->svgTemplate); + $toReplace = ['{size}', '{fill}', '{fgFill}', '{letter}']; + return str_replace($toReplace, [$size, $fill, $fgFill, $text], $this->svgTemplate); } /** * Generate png avatar from svg with Imagick */ - protected function generateAvatarFromSvg(int $size): ?string { + protected function generateAvatarFromSvg(int $size, bool $darkTheme): ?string { if (!extension_loaded('imagick')) { return null; } try { - $font = __DIR__ . '/../../core/fonts/NotoSans-Regular.ttf'; - $svg = $this->getAvatarVector($size); + $font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf'; + $svg = $this->getAvatarVector($size, $darkTheme); $avatar = new Imagick(); $avatar->setFont($font); $avatar->readImageBlob($svg); @@ -145,9 +147,10 @@ abstract class Avatar implements IAvatar { /** * Generate png avatar with GD */ - protected function generateAvatar(string $userDisplayName, int $size): string { + protected function generateAvatar(string $userDisplayName, int $size, bool $darkTheme): string { $text = $this->getAvatarText(); - $backgroundColor = $this->avatarBackgroundColor($userDisplayName); + $textColor = $this->avatarBackgroundColor($userDisplayName); + $backgroundColor = $textColor->alphaBlending(0.1, $darkTheme ? new Color(0, 0, 0) : new Color(255, 255, 255)); $im = imagecreatetruecolor($size, $size); $background = imagecolorallocate( @@ -156,7 +159,11 @@ abstract class Avatar implements IAvatar { $backgroundColor->green(), $backgroundColor->blue() ); - $white = imagecolorallocate($im, 255, 255, 255); + $textColor = imagecolorallocate($im, + $textColor->red(), + $textColor->green(), + $textColor->blue() + ); imagefilledrectangle($im, 0, 0, $size, $size, $background); $font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf'; @@ -166,7 +173,7 @@ abstract class Avatar implements IAvatar { $im, $text, $font, (int)$fontSize ); - imagettftext($im, $fontSize, 0, $x, $y, $white, $font, $text); + imagettftext($im, $fontSize, 0, $x, $y, $textColor, $font, $text); ob_start(); imagepng($im); diff --git a/lib/private/Avatar/GuestAvatar.php b/lib/private/Avatar/GuestAvatar.php index 79d7e6ee094..083deb4108f 100644 --- a/lib/private/Avatar/GuestAvatar.php +++ b/lib/private/Avatar/GuestAvatar.php @@ -84,8 +84,8 @@ class GuestAvatar extends Avatar { /** * Generates an avatar for the guest. */ - public function getFile(int $size): ISimpleFile { - $avatar = $this->generateAvatar($this->userDisplayName, $size); + public function getFile(int $size, bool $darkTheme = false): ISimpleFile { + $avatar = $this->generateAvatar($this->userDisplayName, $size, $darkTheme); return new InMemoryFile('avatar.png', $avatar); } diff --git a/lib/private/Avatar/PlaceholderAvatar.php b/lib/private/Avatar/PlaceholderAvatar.php index 504e5c1457d..e7ca89f4d30 100644 --- a/lib/private/Avatar/PlaceholderAvatar.php +++ b/lib/private/Avatar/PlaceholderAvatar.php @@ -108,13 +108,13 @@ class PlaceholderAvatar extends Avatar { * @throws \OCP\Files\NotPermittedException * @throws \OCP\PreConditionNotMetException */ - public function getFile(int $size): ISimpleFile { + public function getFile(int $size, bool $darkTheme = false): ISimpleFile { $ext = 'png'; if ($size === -1) { - $path = 'avatar-placeholder.' . $ext; + $path = 'avatar-placeholder' . ($darkTheme ? '-dark' : '') . '.' . $ext; } else { - $path = 'avatar-placeholder.' . $size . '.' . $ext; + $path = 'avatar-placeholder' . ($darkTheme ? '-dark' : '') . '.' . $size . '.' . $ext; } try { @@ -124,8 +124,8 @@ class PlaceholderAvatar extends Avatar { throw new NotFoundException; } - if (!$data = $this->generateAvatarFromSvg($size)) { - $data = $this->generateAvatar($this->getDisplayName(), $size); + if (!$data = $this->generateAvatarFromSvg($size, $darkTheme)) { + $data = $this->generateAvatar($this->getDisplayName(), $size, $darkTheme); } try { diff --git a/lib/private/Avatar/UserAvatar.php b/lib/private/Avatar/UserAvatar.php index f5a1d7e77b1..02fcfcb0fc8 100644 --- a/lib/private/Avatar/UserAvatar.php +++ b/lib/private/Avatar/UserAvatar.php @@ -208,7 +208,14 @@ class UserAvatar extends Avatar { * * @throws NotFoundException */ - private function getExtension(): string { + private function getExtension(bool $generated, bool $darkTheme): string { + if ($darkTheme && !$generated) { + if ($this->folder->fileExists('avatar-dark.jpg')) { + return 'jpg'; + } elseif ($this->folder->fileExists('avatar-dark.png')) { + return 'png'; + } + } if ($this->folder->fileExists('avatar.jpg')) { return 'jpg'; } elseif ($this->folder->fileExists('avatar.png')) { @@ -228,25 +235,36 @@ class UserAvatar extends Avatar { * @throws \OCP\Files\NotPermittedException * @throws \OCP\PreConditionNotMetException */ - public function getFile(int $size): ISimpleFile { + public function getFile(int $size, bool $darkTheme = false): ISimpleFile { + $generated = $this->folder->fileExists('generated'); + try { - $ext = $this->getExtension(); + $ext = $this->getExtension($generated, $darkTheme); } catch (NotFoundException $e) { - if (!$data = $this->generateAvatarFromSvg(1024)) { - $data = $this->generateAvatar($this->getDisplayName(), 1024); + if (!$data = $this->generateAvatarFromSvg(1024, $darkTheme)) { + $data = $this->generateAvatar($this->getDisplayName(), 1024, $darkTheme); } - $avatar = $this->folder->newFile('avatar.png'); + $avatar = $this->folder->newFile($darkTheme ? 'avatar-dark.png' : 'avatar.png'); $avatar->putContent($data); $ext = 'png'; $this->folder->newFile('generated', ''); $this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true'); + $generated = true; } - if ($size === -1) { - $path = 'avatar.' . $ext; + if ($generated) { + if ($size === -1) { + $path = 'avatar' . ($darkTheme ? '-dark' : '') . '.' . $ext; + } else { + $path = 'avatar' . ($darkTheme ? '-dark' : '') . '.' . $size . '.' . $ext; + } } else { - $path = 'avatar.' . $size . '.' . $ext; + if ($size === -1) { + $path = 'avatar.' . $ext; + } else { + $path = 'avatar.' . $size . '.' . $ext; + } } try { @@ -255,11 +273,9 @@ class UserAvatar extends Avatar { if ($size <= 0) { throw new NotFoundException; } - - // TODO: rework to integrate with the PlaceholderAvatar in a compatible way - if ($this->folder->fileExists('generated')) { - if (!$data = $this->generateAvatarFromSvg($size)) { - $data = $this->generateAvatar($this->getDisplayName(), $size); + if ($generated) { + if (!$data = $this->generateAvatarFromSvg($size, $darkTheme)) { + $data = $this->generateAvatar($this->getDisplayName(), $size, $darkTheme); } } else { $avatar = new \OCP\Image(); @@ -279,7 +295,7 @@ class UserAvatar extends Avatar { } if ($this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', null) === null) { - $generated = $this->folder->fileExists('generated') ? 'true' : 'false'; + $generated = $generated ? 'true' : 'false'; $this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', $generated); } diff --git a/lib/private/Calendar/Manager.php b/lib/private/Calendar/Manager.php index f0b8e9fd50d..550ba36dd6b 100644 --- a/lib/private/Calendar/Manager.php +++ b/lib/private/Calendar/Manager.php @@ -330,12 +330,27 @@ class Manager implements IManager { // to the email address in the ORGANIZER. // We don't want to accept a CANCEL request from just anyone $organizer = substr($vEvent->{'ORGANIZER'}->getValue(), 7); - if (strcasecmp($sender, $organizer) !== 0 && strcasecmp($replyTo, $organizer) !== 0) { + $isNotOrganizer = ($replyTo !== null) ? (strcasecmp($sender, $organizer) !== 0 && strcasecmp($replyTo, $organizer) !== 0) : (strcasecmp($sender, $organizer) !== 0); + if ($isNotOrganizer) { $this->logger->warning('Sender must be the ORGANIZER of this event'); return false; } + //check if the event is in the future + /** @var DateTime $eventTime */ + $eventTime = $vEvent->{'DTSTART'}; + if ($eventTime->getDateTime()->getTimeStamp() < $this->timeFactory->getTime()) { // this might cause issues with recurrences + $this->logger->warning('Only events in the future are processed'); + return false; + } + + // Check if we have a calendar to work with $calendars = $this->getCalendarsForPrincipal($principalUri); + if (empty($calendars)) { + $this->logger->warning('Could not find any calendars for principal ' . $principalUri); + return false; + } + $found = null; // if the attendee has been found in at least one calendar event with the UID of the iMIP event // we process it. diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index fb3c78bb801..268c1d8dd06 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -413,37 +413,65 @@ class Folder extends Node implements \OCP\Files\Folder { * @return \OCP\Files\Node[] */ public function getRecent($limit, $offset = 0) { - $query = new SearchQuery( - new SearchBinaryOperator( - // filter out non empty folders - ISearchBinaryOperator::OPERATOR_OR, - [ - new SearchBinaryOperator( - ISearchBinaryOperator::OPERATOR_NOT, - [ - new SearchComparison( - ISearchComparison::COMPARE_EQUAL, - 'mimetype', - FileInfo::MIMETYPE_FOLDER - ), - ] - ), - new SearchComparison( - ISearchComparison::COMPARE_EQUAL, - 'size', - 0 - ), - ] - ), - $limit, - $offset, + $filterOutNonEmptyFolder = new SearchBinaryOperator( + // filter out non empty folders + ISearchBinaryOperator::OPERATOR_OR, [ - new SearchOrder( - ISearchOrder::DIRECTION_DESCENDING, - 'mtime' + new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_NOT, + [ + new SearchComparison( + ISearchComparison::COMPARE_EQUAL, + 'mimetype', + FileInfo::MIMETYPE_FOLDER + ), + ] + ), + new SearchComparison( + ISearchComparison::COMPARE_EQUAL, + 'size', + 0 ), ] ); + + $filterNonRecentFiles = new SearchComparison( + ISearchComparison::COMPARE_GREATER_THAN, + 'mtime', + strtotime("-2 week") + ); + if ($offset === 0 && $limit <= 100) { + $query = new SearchQuery( + new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_AND, + [ + $filterOutNonEmptyFolder, + $filterNonRecentFiles, + ], + ), + $limit, + $offset, + [ + new SearchOrder( + ISearchOrder::DIRECTION_DESCENDING, + 'mtime' + ), + ] + ); + } else { + $query = new SearchQuery( + $filterOutNonEmptyFolder, + $limit, + $offset, + [ + new SearchOrder( + ISearchOrder::DIRECTION_DESCENDING, + 'mtime' + ), + ] + ); + } + return $this->search($query); } } diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php index 5782a5a72a6..d52a291cd99 100644 --- a/lib/private/Files/SetupManager.php +++ b/lib/private/Files/SetupManager.php @@ -172,10 +172,10 @@ class SetupManager { */ if ($storage->instanceOfStorage(HomeObjectStoreStorage::class) || $storage->instanceOfStorage(Home::class)) { if (is_object($storage->getUser())) { - $quota = OC_Util::getUserQuota($storage->getUser()); - if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) { - return new Quota(['storage' => $storage, 'quota' => $quota, 'root' => 'files']); - } + $user = $storage->getUser(); + return new Quota(['storage' => $storage, 'quotaCallback' => function () use ($user) { + return OC_Util::getUserQuota($user); + }, 'root' => 'files']); } } diff --git a/lib/private/Files/Storage/Wrapper/Quota.php b/lib/private/Files/Storage/Wrapper/Quota.php index 4cd0a5e0b4a..8b129472eb0 100644 --- a/lib/private/Files/Storage/Wrapper/Quota.php +++ b/lib/private/Files/Storage/Wrapper/Quota.php @@ -33,40 +33,47 @@ namespace OC\Files\Storage\Wrapper; use OC\Files\Filesystem; +use OC\SystemConfig; use OCP\Files\Cache\ICacheEntry; +use OCP\Files\FileInfo; use OCP\Files\Storage\IStorage; class Quota extends Wrapper { - - /** - * @var int $quota - */ - protected $quota; - - /** - * @var string $sizeRoot - */ - protected $sizeRoot; - - private $config; + /** @var callable|null */ + protected $quotaCallback; + protected ?int $quota; + protected string $sizeRoot; + private SystemConfig $config; /** * @param array $parameters */ public function __construct($parameters) { parent::__construct($parameters); - $this->quota = $parameters['quota']; - $this->sizeRoot = isset($parameters['root']) ? $parameters['root'] : ''; - $this->config = \OC::$server->getSystemConfig(); + $this->quota = $parameters['quota'] ?? null; + $this->quotaCallback = $parameters['quotaCallback'] ?? null; + $this->sizeRoot = $parameters['root'] ?? ''; + $this->config = \OC::$server->get(SystemConfig::class); } /** * @return int quota value */ - public function getQuota() { + public function getQuota(): int { + if ($this->quota === null) { + $quotaCallback = $this->quotaCallback; + if ($quotaCallback === null) { + throw new \Exception("No quota or quota callback provider"); + } + $this->quota = $quotaCallback(); + } return $this->quota; } + private function hasQuota(): bool { + return $this->getQuota() !== FileInfo::SPACE_UNLIMITED; + } + /** * @param string $path * @param \OC\Files\Storage\Storage $storage @@ -100,7 +107,10 @@ class Quota extends Wrapper { * @return int|bool */ public function free_space($path) { - if ($this->quota < 0 || strpos($path, 'cache') === 0 || strpos($path, 'uploads') === 0) { + if (!$this->hasQuota()) { + return $this->storage->free_space($path); + } + if ($this->getQuota() < 0 || strpos($path, 'cache') === 0 || strpos($path, 'uploads') === 0) { return $this->storage->free_space($path); } else { $used = $this->getSize($this->sizeRoot); @@ -108,7 +118,7 @@ class Quota extends Wrapper { return \OCP\Files\FileInfo::SPACE_NOT_COMPUTED; } else { $free = $this->storage->free_space($path); - $quotaFree = max($this->quota - $used, 0); + $quotaFree = max($this->getQuota() - $used, 0); // if free space is known if ($free >= 0) { $free = min($free, $quotaFree); @@ -128,6 +138,9 @@ class Quota extends Wrapper { * @return int|false */ public function file_put_contents($path, $data) { + if (!$this->hasQuota()) { + return $this->storage->file_put_contents($path, $data); + } $free = $this->free_space($path); if ($free < 0 or strlen($data) < $free) { return $this->storage->file_put_contents($path, $data); @@ -144,6 +157,9 @@ class Quota extends Wrapper { * @return bool */ public function copy($source, $target) { + if (!$this->hasQuota()) { + return $this->storage->copy($source, $target); + } $free = $this->free_space($target); if ($free < 0 or $this->getSize($source) < $free) { return $this->storage->copy($source, $target); @@ -160,6 +176,9 @@ class Quota extends Wrapper { * @return resource|bool */ public function fopen($path, $mode) { + if (!$this->hasQuota()) { + return $this->storage->fopen($path, $mode); + } $source = $this->storage->fopen($path, $mode); // don't apply quota for part files @@ -202,6 +221,9 @@ class Quota extends Wrapper { * @return bool */ public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + if (!$this->hasQuota()) { + return $this->storage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); + } $free = $this->free_space($targetInternalPath); if ($free < 0 or $this->getSize($sourceInternalPath, $sourceStorage) < $free) { return $this->storage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); @@ -217,6 +239,9 @@ class Quota extends Wrapper { * @return bool */ public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + if (!$this->hasQuota()) { + return $this->storage->moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); + } $free = $this->free_space($targetInternalPath); if ($free < 0 or $this->getSize($sourceInternalPath, $sourceStorage) < $free) { return $this->storage->moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); @@ -226,8 +251,11 @@ class Quota extends Wrapper { } public function mkdir($path) { + if (!$this->hasQuota()) { + return $this->storage->mkdir($path); + } $free = $this->free_space($path); - if ($this->shouldApplyQuota($path) && $free === 0.0) { + if ($this->shouldApplyQuota($path) && $free == 0) { return false; } @@ -235,8 +263,11 @@ class Quota extends Wrapper { } public function touch($path, $mtime = null) { + if (!$this->hasQuota()) { + return $this->storage->touch($path, $mtime); + } $free = $this->free_space($path); - if ($free === 0.0) { + if ($free == 0) { return false; } diff --git a/lib/private/Repair/ClearGeneratedAvatarCache.php b/lib/private/Repair/ClearGeneratedAvatarCache.php index 314299a0528..1c1be4f7893 100644 --- a/lib/private/Repair/ClearGeneratedAvatarCache.php +++ b/lib/private/Repair/ClearGeneratedAvatarCache.php @@ -30,35 +30,29 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class ClearGeneratedAvatarCache implements IRepairStep { - - /** @var AvatarManager */ - protected $avatarManager; - - /** @var IConfig */ - private $config; + protected AvatarManager $avatarManager; + private IConfig $config; public function __construct(IConfig $config, AvatarManager $avatarManager) { $this->config = $config; $this->avatarManager = $avatarManager; } - public function getName() { + public function getName(): string { return 'Clear every generated avatar on major updates'; } /** * Check if this repair step should run - * - * @return boolean */ - private function shouldRun() { + private function shouldRun(): bool { $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); - // was added to 15.0.0.4 - return version_compare($versionFromBeforeUpdate, '15.0.0.4', '<='); + // was added to 25.0.0.10 + return version_compare($versionFromBeforeUpdate, '25.0.0.10', '<='); } - public function run(IOutput $output) { + public function run(IOutput $output): void { if ($this->shouldRun()) { try { $this->avatarManager->clearCachedAvatars(); diff --git a/lib/private/Settings/Manager.php b/lib/private/Settings/Manager.php index 05a286e4758..44f1df09c15 100644 --- a/lib/private/Settings/Manager.php +++ b/lib/private/Settings/Manager.php @@ -150,6 +150,13 @@ class Manager implements IManager { return $this->sections[$type]; } + public function getSection(string $type, string $sectionId): ?IIconSection { + if (isset($this->sections[$type]) && isset($this->sections[$type][$sectionId])) { + return $this->sections[$type][$sectionId]; + } + return null; + } + protected function isKnownDuplicateSectionId(string $sectionID): bool { return in_array($sectionID, [ 'connected-accounts', diff --git a/lib/private/User/User.php b/lib/private/User/User.php index 72c0d5c1a88..f5d93dcd680 100644 --- a/lib/private/User/User.php +++ b/lib/private/User/User.php @@ -199,7 +199,7 @@ class User implements IUser { $this->setPrimaryEMailAddress(''); } - if ($oldMailAddress !== $mailAddress) { + if ($oldMailAddress !== strtolower($mailAddress)) { $this->triggerChange('eMailAddress', $mailAddress, $oldMailAddress); } } diff --git a/lib/public/Color.php b/lib/public/Color.php index e2cabd9c2fc..d5b2a92a6ac 100644 --- a/lib/public/Color.php +++ b/lib/public/Color.php @@ -126,6 +126,20 @@ class Color { } /** + * Alpha blend another color with a given opacity to this color + * + * @return Color The new color + * @since 25.0.0 + */ + public function alphaBlending(float $opacity, Color $source): Color { + return new Color( + (int)((1 - $opacity) * $source->red() + $opacity * $this->red()), + (int)((1 - $opacity) * $source->green() + $opacity * $this->green()), + (int)((1 - $opacity) * $source->blue() + $opacity * $this->blue()) + ); + } + + /** * Calculate steps between two Colors * @param int $steps start color * @param Color[] $ends end color diff --git a/lib/public/IAvatar.php b/lib/public/IAvatar.php index d05a12e1dbf..f9fe9a645e6 100644 --- a/lib/public/IAvatar.php +++ b/lib/public/IAvatar.php @@ -39,10 +39,11 @@ interface IAvatar { * Get the users avatar * * @param int $size size in px of the avatar, avatars are square, defaults to 64, -1 can be used to not scale the image + * @param bool $darkTheme Should the generated avatar be dark themed * @return false|\OCP\IImage containing the avatar or false if there's no image * @since 6.0.0 - size of -1 was added in 9.0.0 */ - public function get(int $size = 64); + public function get(int $size = 64, bool $darkTheme = false); /** * Check if an avatar exists for the user @@ -81,10 +82,11 @@ interface IAvatar { * Get the file of the avatar * * @param int $size The desired image size. -1 can be used to not scale the image + * @param bool $darkTheme Should the generated avatar be dark themed * @throws NotFoundException * @since 9.0.0 */ - public function getFile(int $size): ISimpleFile; + public function getFile(int $size, bool $darkTheme = false): ISimpleFile; /** * Get the avatar background color diff --git a/lib/public/Settings/IIconSection.php b/lib/public/Settings/IIconSection.php index c56565fbf85..bb9b2e94b0d 100644 --- a/lib/public/Settings/IIconSection.php +++ b/lib/public/Settings/IIconSection.php @@ -31,7 +31,7 @@ interface IIconSection { * returns the ID of the section. It is supposed to be a lower case string, * e.g. 'ldap' * - * @returns string + * @return string * @since 9.1 */ public function getID(); @@ -59,7 +59,7 @@ interface IIconSection { * returns the relative path to an 16*16 icon describing the section. * e.g. '/core/img/places/files.svg' * - * @returns string + * @return string * @since 12 */ public function getIcon(); diff --git a/lib/public/Settings/IManager.php b/lib/public/Settings/IManager.php index 2ec3fb0fd21..10de596dbea 100644 --- a/lib/public/Settings/IManager.php +++ b/lib/public/Settings/IManager.php @@ -116,4 +116,10 @@ interface IManager { * @since 13.0.0 */ public function getPersonalSettings($section): array; + + /** + * Get a specific section by type and id + * @since 25.0.0 + */ + public function getSection(string $type, string $sectionId): ?IIconSection; } |