diff --git a/lib/Horde/Core/Mime/Viewer/Vcard.php b/lib/Horde/Core/Mime/Viewer/Vcard.php index 72a5b069..98966f4c 100644 --- a/lib/Horde/Core/Mime/Viewer/Vcard.php +++ b/lib/Horde/Core/Mime/Viewer/Vcard.php @@ -166,7 +166,7 @@ protected function _renderInline() $birthday = new Horde_Date($birthdays[0]); $html .= $this->_row( Horde_Core_Translation::t('Birthday'), - $birthday->strftime($prefs->getValue('date_format')) + $birthday->format($prefs->getValue('date_format'), new \Horde\Date\Formatter\IcuFormatter(), $GLOBALS['language'] ?? 'en_US') ); } catch (Horde_Icalendar_Exception $e) { } diff --git a/src/Factory/DateFormatPrefsFactory.php b/src/Factory/DateFormatPrefsFactory.php new file mode 100644 index 00000000..69e23d9d --- /dev/null +++ b/src/Factory/DateFormatPrefsFactory.php @@ -0,0 +1,49 @@ +getInstance(LoggerInterface::class); + } catch (\Throwable) { + // Logger unavailable — proceed without + } + + return new DateFormatPrefs( + prefs: $injector->getInstance('Horde_Prefs'), + locale: $GLOBALS['language'] ?? 'en_US', + logger: $logger, + ); + } +} diff --git a/src/Middleware/AuthHordeSession.php b/src/Middleware/AuthHordeSession.php index 8c49eeb0..8a80f2f6 100644 --- a/src/Middleware/AuthHordeSession.php +++ b/src/Middleware/AuthHordeSession.php @@ -37,7 +37,8 @@ public function __construct(Horde_Registry $registry) public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { - if ($this->registry->isAuthenticated()) { + $isAuth = $this->registry->isAuthenticated(); + if ($isAuth) { $request = $request->withAttribute('HORDE_AUTHENTICATED_USER', $this->registry->getAuth()); $request = $request->withoutAttribute('HORDE_GUEST'); } else { diff --git a/src/Middleware/RedirectToLogin.php b/src/Middleware/RedirectToLogin.php index 562dd88f..8e3911f4 100644 --- a/src/Middleware/RedirectToLogin.php +++ b/src/Middleware/RedirectToLogin.php @@ -50,15 +50,18 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $user = $request->getAttribute('HORDE_AUTHENTICATED_USER'); $app = $request->getAttribute('app'); + // Admins bypass all permission checks + if ($user && $this->registry->isAdmin(['user' => $user])) { + return $handler->handle($request); + } + // Check app-level read permission if PermissionService is available if ($this->permissionService !== null && $app) { if ($this->permissionService->exists($app)) { - // Pass empty string for guests — backend returns guest permissions $checkUser = $user ?: ''; if (!$this->permissionService->hasPermission($app, $checkUser, ['read'])) { return $this->redirectToLogin($request); } - // Permission granted — allow through even if not authenticated (guest read) return $handler->handle($request); } } diff --git a/src/Prefs/DateFormatPrefs.php b/src/Prefs/DateFormatPrefs.php new file mode 100644 index 00000000..00330464 --- /dev/null +++ b/src/Prefs/DateFormatPrefs.php @@ -0,0 +1,119 @@ +prefs->getValue($key); + if ($value === null) { + return ''; + } + + if (!Format::isStrftimeFormat($value)) { + return $value; + } + + $icu = Format::strftimeToIcu($value, $this->locale); + + $this->logger?->notice( + "DateFormatPrefs: converting legacy strftime pref '{$key}': '{$value}' → '{$icu}'" + ); + + if (!$this->prefs->isLocked($key)) { + try { + $this->prefs->setValue($key, $icu); + } catch (Throwable) { + // Noop — read-only backend or other error + } + } + + return $icu; + } + + /** + * Get the short time format ICU pattern based on twentyFour pref. + * + * Replaces the common pattern: + * $prefs->getValue('twentyFour') ? '%R' : '%I:%M%p' + */ + public function getTimeFormatShort(): string + { + return $this->prefs->getValue('twentyFour') ? 'HH:mm' : 'h:mm a'; + } + + /** + * Get the time format with seconds based on twentyFour pref. + * + * Replaces the common pattern: + * $prefs->getValue('twentyFour') ? '%H:%M:%S' : '%I:%M:%S %p' + */ + public function getTimeFormatFull(): string + { + return $this->prefs->getValue('twentyFour') ? 'HH:mm:ss' : 'h:mm:ss a'; + } + + /** + * Check if a given pref key is a date format key handled by this class. + */ + public function isDateFormatKey(string $key): bool + { + return in_array($key, self::DATE_FORMAT_KEYS, true); + } +} diff --git a/src/Prefs/StrftimeFinding.php b/src/Prefs/StrftimeFinding.php index fce104ea..4b16cecd 100644 --- a/src/Prefs/StrftimeFinding.php +++ b/src/Prefs/StrftimeFinding.php @@ -52,7 +52,7 @@ public function __construct( public readonly string $field, public readonly string $location, public readonly string $strftime, - public readonly string|array $icu, + public readonly string $icu, public readonly string $confidence, ) { if (!in_array($confidence, [self::CONFIDENCE_HIGH, self::CONFIDENCE_MEDIUM, self::CONFIDENCE_LOW], true)) { @@ -60,29 +60,13 @@ public function __construct( } } - /** - * Check if ICU pattern is locale-specific - * - * @return bool True if pattern depends on user locale - */ - public function isLocaleSpecific(): bool - { - return is_array($this->icu); - } - /** * Get ICU pattern as string * - * For locale-specific patterns, returns a description. - * For concrete patterns, returns the pattern itself. - * - * @return string ICU pattern or description + * @return string ICU pattern */ public function getIcuString(): string { - if (is_array($this->icu)) { - return '[locale-specific: ' . implode(', ', array_keys($this->icu)) . ']'; - } return $this->icu; }