diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 2 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 2 | ||||
-rw-r--r-- | lib/l10n/mk.js | 3 | ||||
-rw-r--r-- | lib/l10n/mk.json | 3 | ||||
-rw-r--r-- | lib/l10n/sw.js | 2 | ||||
-rw-r--r-- | lib/l10n/sw.json | 2 | ||||
-rw-r--r-- | lib/private/Avatar/Avatar.php | 51 | ||||
-rw-r--r-- | lib/private/Avatar/AvatarManager.php | 6 | ||||
-rw-r--r-- | lib/private/Avatar/GuestAvatar.php | 4 | ||||
-rw-r--r-- | lib/private/Avatar/PlaceholderAvatar.php | 9 | ||||
-rw-r--r-- | lib/private/Avatar/UserAvatar.php | 21 | ||||
-rw-r--r-- | lib/private/Mail/EMailTemplate.php | 42 | ||||
-rw-r--r-- | lib/private/Notification/Manager.php | 12 | ||||
-rw-r--r-- | lib/private/Setup/MySQL.php | 27 | ||||
-rw-r--r-- | lib/private/SystemConfig.php | 6 | ||||
-rw-r--r-- | lib/private/Tags.php | 1 | ||||
-rw-r--r-- | lib/public/Notification/IManager.php | 2 | ||||
-rw-r--r-- | lib/public/Notification/INotifier.php | 5 | ||||
-rw-r--r-- | lib/public/Notification/IPreloadableNotifier.php | 31 |
19 files changed, 182 insertions, 49 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 3f2dbe5edf9..fda8798fe43 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -696,6 +696,7 @@ return array( 'OCP\\Notification\\IManager' => $baseDir . '/lib/public/Notification/IManager.php', 'OCP\\Notification\\INotification' => $baseDir . '/lib/public/Notification/INotification.php', 'OCP\\Notification\\INotifier' => $baseDir . '/lib/public/Notification/INotifier.php', + 'OCP\\Notification\\IPreloadableNotifier' => $baseDir . '/lib/public/Notification/IPreloadableNotifier.php', 'OCP\\Notification\\IncompleteNotificationException' => $baseDir . '/lib/public/Notification/IncompleteNotificationException.php', 'OCP\\Notification\\IncompleteParsedNotificationException' => $baseDir . '/lib/public/Notification/IncompleteParsedNotificationException.php', 'OCP\\Notification\\InvalidValueException' => $baseDir . '/lib/public/Notification/InvalidValueException.php', @@ -1510,6 +1511,7 @@ return array( 'OC\\Core\\Migrations\\Version31000Date20240814184402' => $baseDir . '/core/Migrations/Version31000Date20240814184402.php', 'OC\\Core\\Migrations\\Version31000Date20250213102442' => $baseDir . '/core/Migrations/Version31000Date20250213102442.php', 'OC\\Core\\Migrations\\Version32000Date20250620081925' => $baseDir . '/core/Migrations/Version32000Date20250620081925.php', + 'OC\\Core\\Migrations\\Version32000Date20250731062008' => $baseDir . '/core/Migrations/Version32000Date20250731062008.php', 'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php', 'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 233b667add9..69e3c41284b 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -737,6 +737,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Notification\\IManager' => __DIR__ . '/../../..' . '/lib/public/Notification/IManager.php', 'OCP\\Notification\\INotification' => __DIR__ . '/../../..' . '/lib/public/Notification/INotification.php', 'OCP\\Notification\\INotifier' => __DIR__ . '/../../..' . '/lib/public/Notification/INotifier.php', + 'OCP\\Notification\\IPreloadableNotifier' => __DIR__ . '/../../..' . '/lib/public/Notification/IPreloadableNotifier.php', 'OCP\\Notification\\IncompleteNotificationException' => __DIR__ . '/../../..' . '/lib/public/Notification/IncompleteNotificationException.php', 'OCP\\Notification\\IncompleteParsedNotificationException' => __DIR__ . '/../../..' . '/lib/public/Notification/IncompleteParsedNotificationException.php', 'OCP\\Notification\\InvalidValueException' => __DIR__ . '/../../..' . '/lib/public/Notification/InvalidValueException.php', @@ -1551,6 +1552,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Migrations\\Version31000Date20240814184402' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20240814184402.php', 'OC\\Core\\Migrations\\Version31000Date20250213102442' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20250213102442.php', 'OC\\Core\\Migrations\\Version32000Date20250620081925' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250620081925.php', + 'OC\\Core\\Migrations\\Version32000Date20250731062008' => __DIR__ . '/../../..' . '/core/Migrations/Version32000Date20250731062008.php', 'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php', 'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', diff --git a/lib/l10n/mk.js b/lib/l10n/mk.js index 8b49f0914e5..102926f9e1d 100644 --- a/lib/l10n/mk.js +++ b/lib/l10n/mk.js @@ -77,6 +77,9 @@ OC.L10N.register( "Empty file" : "Празна датотека", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модул со ИД: %s не постои. Овозможете го во параметрите на апликациите или контактирајте администратор.", "Dot files are not allowed" : "Датотеки само со точки не се дозволени", + "%1$s (renamed)" : "%1$s (преименувано)", + "renamed file" : "преименувана датотека", + "Filenames must not end with \"%1$s\"." : "Името неможе да завршува со \"%1$s\".", "File already exists" : "Датотека веќе постои", "Invalid path" : "Невалидна патека", "Failed to create file from template" : "Неуспешно креирање на датотека од шаблон", diff --git a/lib/l10n/mk.json b/lib/l10n/mk.json index db83bccd774..d9de1976caf 100644 --- a/lib/l10n/mk.json +++ b/lib/l10n/mk.json @@ -75,6 +75,9 @@ "Empty file" : "Празна датотека", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Модул со ИД: %s не постои. Овозможете го во параметрите на апликациите или контактирајте администратор.", "Dot files are not allowed" : "Датотеки само со точки не се дозволени", + "%1$s (renamed)" : "%1$s (преименувано)", + "renamed file" : "преименувана датотека", + "Filenames must not end with \"%1$s\"." : "Името неможе да завршува со \"%1$s\".", "File already exists" : "Датотека веќе постои", "Invalid path" : "Невалидна патека", "Failed to create file from template" : "Неуспешно креирање на датотека од шаблон", diff --git a/lib/l10n/sw.js b/lib/l10n/sw.js index 0eca41da17e..84ddd8abf9b 100644 --- a/lib/l10n/sw.js +++ b/lib/l10n/sw.js @@ -59,7 +59,9 @@ OC.L10N.register( "Transcribe audio" : "Transcribe audio", "Audio input" : "Audio input", "Confirmation" : "Uthibitisho", + "Generate image" : "Generate image", "Prompt" : "Prompt", + "Describe the image you want to generate" : "Describe the image you want to generate", "Describe a task that you want the assistant to do or ask a question" : "Describe a task that you want the assistant to do or ask a question", "The history of chat messages before the current message, starting with a message by the user" : "The history of chat messages before the current message, starting with a message by the user", "Text" : "Maandishi", diff --git a/lib/l10n/sw.json b/lib/l10n/sw.json index 5759a8f07f4..6adf1f1d8ee 100644 --- a/lib/l10n/sw.json +++ b/lib/l10n/sw.json @@ -57,7 +57,9 @@ "Transcribe audio" : "Transcribe audio", "Audio input" : "Audio input", "Confirmation" : "Uthibitisho", + "Generate image" : "Generate image", "Prompt" : "Prompt", + "Describe the image you want to generate" : "Describe the image you want to generate", "Describe a task that you want the assistant to do or ask a question" : "Describe a task that you want the assistant to do or ask a question", "The history of chat messages before the current message, starting with a message by the user" : "The history of chat messages before the current message, starting with a message by the user", "Text" : "Maandishi", diff --git a/lib/private/Avatar/Avatar.php b/lib/private/Avatar/Avatar.php index 7aa2d220b88..dc65c9d5743 100644 --- a/lib/private/Avatar/Avatar.php +++ b/lib/private/Avatar/Avatar.php @@ -10,17 +10,17 @@ declare(strict_types=1); namespace OC\Avatar; use Imagick; +use OC\User\User; use OCP\Color; use OCP\Files\NotFoundException; use OCP\IAvatar; +use OCP\IConfig; use Psr\Log\LoggerInterface; /** * This class gets and sets users avatars. */ abstract class Avatar implements IAvatar { - protected LoggerInterface $logger; - /** * https://github.com/sebdesign/cap-height -- for 500px height * Automated check: https://codepen.io/skjnldsv/pen/PydLBK/ @@ -35,8 +35,10 @@ abstract class Avatar implements IAvatar { <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) { - $this->logger = $logger; + public function __construct( + protected IConfig $config, + protected LoggerInterface $logger, + ) { } /** @@ -84,8 +86,7 @@ abstract class Avatar implements IAvatar { * @return string * */ - protected function getAvatarVector(int $size, bool $darkTheme): string { - $userDisplayName = $this->getDisplayName(); + protected function getAvatarVector(string $userDisplayName, int $size, bool $darkTheme): string { $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()); @@ -96,9 +97,30 @@ abstract class Avatar implements IAvatar { } /** + * Select the rendering font based on the user's display name and language + */ + private function getFont(string $userDisplayName): string { + if (preg_match('/\p{Han}/u', $userDisplayName) === 1) { + switch ($this->getAvatarLanguage()) { + case 'zh_TW': + return __DIR__ . '/../../../core/fonts/NotoSansTC-Regular.ttf'; + case 'zh_HK': + return __DIR__ . '/../../../core/fonts/NotoSansHK-Regular.ttf'; + case 'ja': + return __DIR__ . '/../../../core/fonts/NotoSansJP-Regular.ttf'; + case 'ko': + return __DIR__ . '/../../../core/fonts/NotoSansKR-Regular.ttf'; + default: + return __DIR__ . '/../../../core/fonts/NotoSansSC-Regular.ttf'; + } + } + return __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf'; + } + + /** * Generate png avatar from svg with Imagick */ - protected function generateAvatarFromSvg(int $size, bool $darkTheme): ?string { + protected function generateAvatarFromSvg(string $userDisplayName, int $size, bool $darkTheme): ?string { if (!extension_loaded('imagick')) { return null; } @@ -107,9 +129,10 @@ abstract class Avatar implements IAvatar { if (in_array('RSVG', $formats, true)) { return null; } + $text = $this->getAvatarText(); try { - $font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf'; - $svg = $this->getAvatarVector($size, $darkTheme); + $font = $this->getFont($text); + $svg = $this->getAvatarVector($userDisplayName, $size, $darkTheme); $avatar = new Imagick(); $avatar->setFont($font); $avatar->readImageBlob($svg); @@ -151,7 +174,7 @@ abstract class Avatar implements IAvatar { } imagefilledrectangle($im, 0, 0, $size, $size, $background); - $font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf'; + $font = $this->getFont($text); $fontSize = $size * 0.4; [$x, $y] = $this->imageTTFCenter( @@ -258,4 +281,12 @@ abstract class Avatar implements IAvatar { return $finalPalette[$this->hashToInt($hash, $steps * 3)]; } + + /** + * Get the language to be used for avatar generation. + * This is used to determine the font to use for the avatar text (e.g. CJK characters). + */ + protected function getAvatarLanguage(): string { + return $this->config->getSystemValueString('default_language', 'en'); + } } diff --git a/lib/private/Avatar/AvatarManager.php b/lib/private/Avatar/AvatarManager.php index 60a3d358bf4..c68467085f0 100644 --- a/lib/private/Avatar/AvatarManager.php +++ b/lib/private/Avatar/AvatarManager.php @@ -92,10 +92,10 @@ class AvatarManager implements IAvatarManager { return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config); default: // use a placeholder avatar which caches the generated images - return new PlaceholderAvatar($folder, $user, $this->logger); + return new PlaceholderAvatar($folder, $user, $this->config, $this->logger); } - return new PlaceholderAvatar($folder, $user, $this->logger); + return new PlaceholderAvatar($folder, $user, $this->config, $this->logger); } /** @@ -129,6 +129,6 @@ class AvatarManager implements IAvatarManager { * @param string $name The guest name, e.g. "Albert". */ public function getGuestAvatar(string $name): IAvatar { - return new GuestAvatar($name, $this->logger); + return new GuestAvatar($name, $this->config, $this->logger); } } diff --git a/lib/private/Avatar/GuestAvatar.php b/lib/private/Avatar/GuestAvatar.php index 7ae633f1260..c0c7de0c078 100644 --- a/lib/private/Avatar/GuestAvatar.php +++ b/lib/private/Avatar/GuestAvatar.php @@ -10,6 +10,7 @@ namespace OC\Avatar; use OCP\Files\SimpleFS\InMemoryFile; use OCP\Files\SimpleFS\ISimpleFile; +use OCP\IConfig; use Psr\Log\LoggerInterface; /** @@ -23,9 +24,10 @@ class GuestAvatar extends Avatar { */ public function __construct( private string $userDisplayName, + IConfig $config, LoggerInterface $logger, ) { - parent::__construct($logger); + parent::__construct($config, $logger); } /** diff --git a/lib/private/Avatar/PlaceholderAvatar.php b/lib/private/Avatar/PlaceholderAvatar.php index 07c54f62713..f5f49fb7cb2 100644 --- a/lib/private/Avatar/PlaceholderAvatar.php +++ b/lib/private/Avatar/PlaceholderAvatar.php @@ -14,6 +14,7 @@ use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFolder; +use OCP\IConfig; use OCP\IImage; use Psr\Log\LoggerInterface; @@ -27,9 +28,10 @@ class PlaceholderAvatar extends Avatar { public function __construct( private ISimpleFolder $folder, private User $user, + IConfig $config, LoggerInterface $logger, ) { - parent::__construct($logger); + parent::__construct($config, $logger); } /** @@ -87,8 +89,9 @@ class PlaceholderAvatar extends Avatar { throw new NotFoundException; } - if (!$data = $this->generateAvatarFromSvg($size, $darkTheme)) { - $data = $this->generateAvatar($this->getDisplayName(), $size, $darkTheme); + $userDisplayName = $this->getDisplayName(); + if (!$data = $this->generateAvatarFromSvg($userDisplayName, $size, $darkTheme)) { + $data = $this->generateAvatar($userDisplayName, $size, $darkTheme); } try { diff --git a/lib/private/Avatar/UserAvatar.php b/lib/private/Avatar/UserAvatar.php index bef0a20e7b8..aca2aa574bc 100644 --- a/lib/private/Avatar/UserAvatar.php +++ b/lib/private/Avatar/UserAvatar.php @@ -26,11 +26,11 @@ class UserAvatar extends Avatar { public function __construct( private ISimpleFolder $folder, private IL10N $l, - private User $user, + protected User $user, LoggerInterface $logger, - private IConfig $config, + IConfig $config, ) { - parent::__construct($logger); + parent::__construct($config, $logger); } /** @@ -201,8 +201,9 @@ class UserAvatar extends Avatar { try { $ext = $this->getExtension($generated, $darkTheme); } catch (NotFoundException $e) { - if (!$data = $this->generateAvatarFromSvg(1024, $darkTheme)) { - $data = $this->generateAvatar($this->getDisplayName(), 1024, $darkTheme); + $userDisplayName = $this->getDisplayName(); + if (!$data = $this->generateAvatarFromSvg($userDisplayName, 1024, $darkTheme)) { + $data = $this->generateAvatar($userDisplayName, 1024, $darkTheme); } $avatar = $this->folder->newFile($darkTheme ? 'avatar-dark.png' : 'avatar.png'); $avatar->putContent($data); @@ -234,8 +235,9 @@ class UserAvatar extends Avatar { throw new NotFoundException; } if ($generated) { - if (!$data = $this->generateAvatarFromSvg($size, $darkTheme)) { - $data = $this->generateAvatar($this->getDisplayName(), $size, $darkTheme); + $userDisplayName = $this->getDisplayName(); + if (!$data = $this->generateAvatarFromSvg($userDisplayName, $size, $darkTheme)) { + $data = $this->generateAvatar($userDisplayName, $size, $darkTheme); } } else { $avatar = new \OCP\Image(); @@ -293,4 +295,9 @@ class UserAvatar extends Avatar { public function isCustomAvatar(): bool { return $this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', 'false') !== 'true'; } + + #[\Override] + protected function getAvatarLanguage(): string { + return $this->config->getUserValue($this->user->getUID(), 'core', 'lang', parent::getAvatarLanguage()); + } } diff --git a/lib/private/Mail/EMailTemplate.php b/lib/private/Mail/EMailTemplate.php index 1d19f00b0a1..a327109cc12 100644 --- a/lib/private/Mail/EMailTemplate.php +++ b/lib/private/Mail/EMailTemplate.php @@ -190,32 +190,46 @@ EOF; <tr style="padding:0;text-align:left;vertical-align:top"> <th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left"> <center data-parsed="" style="min-width:490px;width:100%%"> - <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:%1\$s;background-color:%1\$s;color:#fefefe;"> - <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> + <!--[if (gte mso 9)|(IE)]> + <table> + <tr> + <td> + <![endif]--> + <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:%1\$s;background-color:%1\$s;color:#fefefe;"> <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid %2\$s;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <a href="%3\$s" style="Margin:0;border:0 solid %4\$s;color:%5\$s;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid %6\$s;text-decoration:none">%7\$s</a> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> + <tr style="padding:0;text-align:left;vertical-align:top"> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid %2\$s;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <a href="%3\$s" style="Margin:0;border:0 solid %4\$s;color:%5\$s;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid %6\$s;text-decoration:none">%7\$s</a> + </td> + </tr> + </table> </td> </tr> </table> + <!--[if (gte mso 9)|(IE)]> </td> - </tr> - </table> - <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto"> - <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> + <td> + <![endif]--> + <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto"> <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <a href="%8\$s" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">%9\$s</a> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%"> + <tr style="padding:0;text-align:left;vertical-align:top"> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <a href="%8\$s" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">%9\$s</a> + </td> + </tr> + </table> </td> </tr> </table> + <!--[if (gte mso 9)|(IE)]> </td> </tr> </table> + <![endif]--> </center> </th> <th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th> diff --git a/lib/private/Notification/Manager.php b/lib/private/Notification/Manager.php index 8c457db8beb..0cbda651a8b 100644 --- a/lib/private/Notification/Manager.php +++ b/lib/private/Notification/Manager.php @@ -21,6 +21,7 @@ use OCP\Notification\IncompleteNotificationException; use OCP\Notification\IncompleteParsedNotificationException; use OCP\Notification\INotification; use OCP\Notification\INotifier; +use OCP\Notification\IPreloadableNotifier; use OCP\Notification\UnknownNotificationException; use OCP\RichObjectStrings\IRichTextFormatter; use OCP\RichObjectStrings\IValidator; @@ -390,6 +391,17 @@ class Manager implements IManager { return $notification; } + public function preloadDataForParsing(array $notifications, string $languageCode): void { + $notifiers = $this->getNotifiers(); + foreach ($notifiers as $notifier) { + if (!($notifier instanceof IPreloadableNotifier)) { + continue; + } + + $notifier->preloadDataForParsing($notifications, $languageCode); + } + } + /** * @param INotification $notification */ diff --git a/lib/private/Setup/MySQL.php b/lib/private/Setup/MySQL.php index 1e2dda4c609..c4794a86743 100644 --- a/lib/private/Setup/MySQL.php +++ b/lib/private/Setup/MySQL.php @@ -8,6 +8,7 @@ namespace OC\Setup; use Doctrine\DBAL\Platforms\MySQL80Platform; +use Doctrine\DBAL\Platforms\MySQL84Platform; use OC\DB\ConnectionAdapter; use OC\DB\MySqlTools; use OCP\IDBConnection; @@ -92,22 +93,29 @@ class MySQL extends AbstractDatabase { * @throws \OC\DatabaseSetupException */ private function createDBUser($connection): void { + $name = $this->dbUser; + $password = $this->dbPassword; + try { - $name = $this->dbUser; - $password = $this->dbPassword; // we need to create 2 accounts, one for global use and one for local user. if we don't specify the local one, // the anonymous user would take precedence when there is one. - if ($connection->getDatabasePlatform() instanceof Mysql80Platform) { + if ($connection->getDatabasePlatform() instanceof MySQL84Platform) { + $query = "CREATE USER ?@'localhost' IDENTIFIED WITH caching_sha2_password BY ?"; + $connection->executeStatement($query, [$name,$password]); + $query = "CREATE USER ?@'%' IDENTIFIED WITH caching_sha2_password BY ?"; + $connection->executeStatement($query, [$name,$password]); + } elseif ($connection->getDatabasePlatform() instanceof Mysql80Platform) { + // TODO: Remove this elseif section as soon as MySQL 8.0 is out-of-support (after April 2026) $query = "CREATE USER ?@'localhost' IDENTIFIED WITH mysql_native_password BY ?"; - $connection->executeUpdate($query, [$name,$password]); + $connection->executeStatement($query, [$name,$password]); $query = "CREATE USER ?@'%' IDENTIFIED WITH mysql_native_password BY ?"; - $connection->executeUpdate($query, [$name,$password]); + $connection->executeStatement($query, [$name,$password]); } else { $query = "CREATE USER ?@'localhost' IDENTIFIED BY ?"; - $connection->executeUpdate($query, [$name,$password]); + $connection->executeStatement($query, [$name,$password]); $query = "CREATE USER ?@'%' IDENTIFIED BY ?"; - $connection->executeUpdate($query, [$name,$password]); + $connection->executeStatement($query, [$name,$password]); } } catch (\Exception $ex) { $this->logger->error('Database user creation failed.', [ @@ -158,6 +166,11 @@ class MySQL extends AbstractDatabase { //use the admin login data for the new database user $this->dbUser = $adminUser; $this->createDBUser($connection); + // if sharding is used we need to manually call this for every shard as those also need the user setup! + /** @var ConnectionAdapter $connection */ + foreach ($connection->getInner()->getShardConnections() as $shard) { + $this->createDBUser($shard); + } break; } else { diff --git a/lib/private/SystemConfig.php b/lib/private/SystemConfig.php index 57777b06ed6..7e8946f4d05 100644 --- a/lib/private/SystemConfig.php +++ b/lib/private/SystemConfig.php @@ -15,8 +15,9 @@ use OCP\IConfig; * fixes cyclic DI: AllConfig needs AppConfig needs Database needs AllConfig */ class SystemConfig { - /** @var array */ - protected $sensitiveValues = [ + protected array $sensitiveValues; + + protected const DEFAULT_SENSITIVE_VALUES = [ 'instanceid' => true, 'datadirectory' => true, 'dbname' => true, @@ -114,6 +115,7 @@ class SystemConfig { public function __construct( private Config $config, ) { + $this->sensitiveValues = array_merge(self::DEFAULT_SENSITIVE_VALUES, $this->config->getValue('config_extra_sensitive_values', [])); } /** diff --git a/lib/private/Tags.php b/lib/private/Tags.php index 0a37f4c9f4e..fe4a4137e10 100644 --- a/lib/private/Tags.php +++ b/lib/private/Tags.php @@ -273,7 +273,6 @@ class Tags implements ITags { return false; } if ($this->userHasTag($name, $this->user)) { - // TODO use unique db properties instead of an additional check $this->logger->debug(__METHOD__ . ' Tag with name already exists', ['app' => 'core']); return false; } diff --git a/lib/public/Notification/IManager.php b/lib/public/Notification/IManager.php index 23664af17cd..207a89344b0 100644 --- a/lib/public/Notification/IManager.php +++ b/lib/public/Notification/IManager.php @@ -11,7 +11,7 @@ namespace OCP\Notification; use OCP\AppFramework\Attribute\Consumable; #[Consumable(since: '9.0.0')] -interface IManager extends IApp, INotifier { +interface IManager extends IApp, IPreloadableNotifier { /** * @param string $appClass The service must implement IApp, otherwise a * \InvalidArgumentException is thrown later diff --git a/lib/public/Notification/INotifier.php b/lib/public/Notification/INotifier.php index bdc7207216f..b6851e3dfb3 100644 --- a/lib/public/Notification/INotifier.php +++ b/lib/public/Notification/INotifier.php @@ -10,6 +10,11 @@ namespace OCP\Notification; use OCP\AppFramework\Attribute\Implementable; +/** + * Please consider implementing {@see IPreloadableNotifier} to improve performance. It allows to + * preload and cache data for many notifications at once instead of loading the data for each + * prepared notification separately. + */ #[Implementable(since: '9.0.0')] interface INotifier { /** diff --git a/lib/public/Notification/IPreloadableNotifier.php b/lib/public/Notification/IPreloadableNotifier.php new file mode 100644 index 00000000000..2bdcd84d254 --- /dev/null +++ b/lib/public/Notification/IPreloadableNotifier.php @@ -0,0 +1,31 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCP\Notification; + +use OCP\AppFramework\Attribute\Implementable; + +/** + * Allow notifier implementations to preload and cache data for many notifications at once to + * improve performance by, for example, bundling SQL queries. + */ +#[Implementable(since: '32.0.0')] +interface IPreloadableNotifier extends INotifier { + /** + * This method provides a way for notifier implementations to preload and cache data for many + * notifications. The data is meant to be consumed later in the {@see INotifier::prepare()} + * method to improve performance. + * + * @since 32.0.0 + * + * @param INotification[] $notifications The notifications which are about to be prepared in the next step. + * @param string $languageCode The code of the language that should be used to prepare the notification. + */ + public function preloadDataForParsing(array $notifications, string $languageCode): void; +} |