aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/composer/composer/autoload_classmap.php2
-rw-r--r--lib/composer/composer/autoload_static.php2
-rw-r--r--lib/l10n/mk.js3
-rw-r--r--lib/l10n/mk.json3
-rw-r--r--lib/l10n/sw.js2
-rw-r--r--lib/l10n/sw.json2
-rw-r--r--lib/private/Avatar/Avatar.php51
-rw-r--r--lib/private/Avatar/AvatarManager.php6
-rw-r--r--lib/private/Avatar/GuestAvatar.php4
-rw-r--r--lib/private/Avatar/PlaceholderAvatar.php9
-rw-r--r--lib/private/Avatar/UserAvatar.php21
-rw-r--r--lib/private/Mail/EMailTemplate.php42
-rw-r--r--lib/private/Notification/Manager.php12
-rw-r--r--lib/private/Setup/MySQL.php27
-rw-r--r--lib/private/SystemConfig.php6
-rw-r--r--lib/private/Tags.php1
-rw-r--r--lib/public/Notification/IManager.php2
-rw-r--r--lib/public/Notification/INotifier.php5
-rw-r--r--lib/public/Notification/IPreloadableNotifier.php31
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;
+}