diff --git a/lib/classes/sidebar/NavigationWidget.php b/lib/classes/sidebar/NavigationWidget.php index 651a1f54c6ce755c3b7de96cd5b3514e2d3b6187..32442f5bbefa1e3fab1a6777f8d8a5ab0f59a4b9 100644 --- a/lib/classes/sidebar/NavigationWidget.php +++ b/lib/classes/sidebar/NavigationWidget.php @@ -17,7 +17,7 @@ class NavigationWidget extends LinksWidget * @param Icon $icon (not used) * @param array $attributes Optional attributes fot the generated link * @param mixed $index Index to use for the element - * @return String + * @return LinkElement */ public function &addLink($label, $url, $icon = null, $attributes = [], $index = null) { diff --git a/lib/models/PersonalNotifications.php b/lib/models/PersonalNotifications.php index 8ce1dba044500617eb7457fcc938101656de2a93..1b1c886181b22a95a97936bf61ab77d3ec913a0b 100644 --- a/lib/models/PersonalNotifications.php +++ b/lib/models/PersonalNotifications.php @@ -132,14 +132,16 @@ class PersonalNotifications extends SimpleORMap * Returns all notifications fitting to the parameters. * @param boolean $only_unread : true for getting only unread notifications, false for all. * @param null|string $user_id : ID of special user the notification should belong to or (default:) null for current user - * @return array of \PersonalNotifications in ascending order of mkdate + * @return static[] array of \PersonalNotifications in ascending order of mkdate */ public static function getMyNotifications($only_unread = true, $user_id = null, $limit = 15) { - if (!$user_id) { - $user_id = $GLOBALS['user']->id; + if (!self::isActivated($user_id)) { + return []; } + $user_id ??= $GLOBALS['user']->id; + $cached = self::getCache($user_id); if ($cached === false) { $query = "SELECT pn.*, COUNT(DISTINCT personal_notification_id) - 1 AS unseen @@ -373,7 +375,7 @@ class PersonalNotifications extends SimpleORMap if (!$user_id) { $user_id = $GLOBALS['user']->id; } - return (new UserConfig($user_id))->getValue('PERSONAL_NOTIFICATIONS_DEACTIVATED') ? false : true; + return !UserConfig::get($user_id)->getValue('PERSONAL_NOTIFICATIONS_DEACTIVATED'); } /** @@ -385,13 +387,32 @@ class PersonalNotifications extends SimpleORMap */ public static function isAudioActivated($user_id = null) { - if (!PersonalNotifications::isGloballyActivated()) { + if (!PersonalNotifications::isGloballyActivated() || !self::isActivated($user_id)) { return false; } if (!$user_id) { $user_id = $GLOBALS['user']->id; } - return UserConfig::get($user_id)->getValue("PERSONAL_NOTIFICATIONS_AUDIO_DEACTIVATED") ? false : true; + return ! UserConfig::get($user_id)->getValue("PERSONAL_NOTIFICATIONS_AUDIO_DEACTIVATED"); + } + + /** + * Returns whether there are any new/unseen notifications for the given + * user id. + */ + public static function hasUnseenNotifications(?string $user_id = null): bool + { + if (!self::isActivated($user_id)) { + return false; + } + + $user_id ??= User::findCurrent()->id; + + $lastvisit = (int) UserConfig::get($user_id)->getValue('NOTIFICATIONS_SEEN_LAST_DATE'); + return array_any( + self::getMyNotifications(user_id: $user_id), + fn($notification) => $notification['mkdate'] > $lastvisit + ); } /** diff --git a/resources/assets/stylesheets/scss/header.scss b/resources/assets/stylesheets/scss/header.scss index 2e6ce53609e0a853650973478cd1fe66563f69c3..0a04d64456115adf2c63c98c99f512c75ad238c3 100644 --- a/resources/assets/stylesheets/scss/header.scss +++ b/resources/assets/stylesheets/scss/header.scss @@ -222,13 +222,9 @@ img { height: 26px; width: 26px; - border-top-right-radius: var(--border-radius-avatar-menu); - border-bottom-right-radius: var(--border-radius-avatar-menu); + border-radius: var(--border-radius-avatar-menu); } - #notification-container + & { - border-left: 0; - } } .action-menu-title { @@ -259,6 +255,13 @@ } } } + + #notification-container + #avatar-menu { + .action-menu-icon img { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + } } // Fix header covering relevant other areas diff --git a/templates/header.php b/templates/header.php index b4ced53f1599ffc0fc10484a36a349c740c63fea..fda7471171e18e90875948d937d56198c9c79256 100644 --- a/templates/header.php +++ b/templates/header.php @@ -118,38 +118,35 @@ if ($navigation) { <? endif; ?> <? endif; ?> - <? if (is_object($GLOBALS['perm']) && $GLOBALS['perm']->have_perm('user')): ?> - <? $active = Navigation::hasItem('/profile') - && Navigation::getItem('/profile')->isActive(); - ?> - - - - <? if (is_object($GLOBALS['perm']) && PersonalNotifications::isActivated() && $GLOBALS['perm']->have_perm('autor')) : ?> + <? if (is_object($GLOBALS['perm']) && $GLOBALS['perm']->have_perm('user')): ?> + <? $active = Navigation::getItem('/profile')?->isActive() ?? false; ?> + <? if ($GLOBALS['perm']->have_perm('autor')) : ?> + <li id="avatar-menu-container" + class="header_avatar_container <?= PersonalNotifications::hasUnseenNotifications() ? 'alert' : '' ?>" + > + <? if (PersonalNotifications::isActivated()): ?> <? $notifications = PersonalNotifications::getMyNotifications() ?> - <? $lastvisit = (int)UserConfig::get($GLOBALS['user']->id)->getValue('NOTIFICATIONS_SEEN_LAST_DATE') ?> - <? foreach ($notifications as $notification) { - if ($notification['mkdate'] > $lastvisit) { - $alert = true; - } - } ?> - <!-- User-Avatar --> - <li class="header_avatar_container <?= !empty($alert) ? 'alert' : '' ?> <? if ($active) echo 'active'; ?>" id="avatar-menu-container"> <div id="notification-container" <?= count($notifications) > 0 ? ' class="hoverable"' : '' ?>> - - <button id="notification_marker" data-toggles="#notification_checkbox" <?= !empty($alert) ? ' class="alert"' : "" ?> + <button id="notification_marker" + data-toggles="#notification_checkbox" title="<?= sprintf( ngettext('%u Benachrichtigung', '%u Benachrichtigungen', count($notifications)), count($notifications) - ) ?>" data-lastvisit="<?= $lastvisit ?>" - <?= count($notifications) == 0 ? 'disabled' : '' ?> aria-controls="notification-list" - aria-expanded="false"> + ) ?>" + aria-controls="notification-list" + data-lastvisit="<?= UserConfig::get($GLOBALS['user']->id)->getValue('NOTIFICATIONS_SEEN_LAST_DATE') ?>" + <? if (count($notifications) === 0) echo 'disabled'; ?> + <? if (PersonalNotifications::hasUnseenNotifications()) echo 'class="alert"'; ?> + aria-expanded="false" + > <span class="count" aria-hidden="true"><?= count($notifications) ?></span> </button> <input type="checkbox" id="notification_checkbox"> <div class="list below" id="notification_list"> - <a class="mark-all-as-read <? if (count($notifications) < 2) echo 'invisible'; ?>" href="<?= URLHelper::getLink('dispatch.php/jsupdater/mark_notification_read/all', ['return_to' => $_SERVER['REQUEST_URI']]) ?>"> + <a class="mark-all-as-read <? if (count($notifications) < 2) echo 'invisible'; ?>" + href="<?= URLHelper::getLink('dispatch.php/jsupdater/mark_notification_read/all', ['return_to' => $_SERVER['REQUEST_URI']]) ?>" + > <?= _('Alle Benachrichtigungen als gelesen markieren') ?> </a> <a class="enable-desktop-notifications" href="#" style="display: none;"> @@ -173,43 +170,44 @@ if ($navigation) { <? if (Navigation::hasItem('/avatar')): ?> <form id="avatar-menu" method="post"> <?php - $action_menu = ContentGroupMenu::get(); - $action_menu->addCSSClass('avatar-menu'); - $action_menu->addAttribute('data-action-menu-reposition', 'false'); - $action_menu->setLabel(User::findCurrent()->getFullName()); - $action_menu->setAriaLabel(_('Profilmenü')); - $action_menu->setIcon( - Avatar::getAvatar(User::findCurrent()->id)->getImageTag(Avatar::MEDIUM), - ['id' => 'header_avatar_image_link'] - ); + $action_menu = ContentGroupMenu::get(); + $action_menu->addCSSClass('avatar-menu'); + $action_menu->addAttribute('data-action-menu-reposition', 'false'); + $action_menu->setLabel(User::findCurrent()->getFullName()); + $action_menu->setAriaLabel(_('Profilmenü')); + $action_menu->setIcon( + Avatar::getAvatar(User::findCurrent()->id)->getImageTag(), + ['id' => 'header_avatar_image_link'] + ); - foreach (Navigation::getItem('/avatar') as $subnav) { - if ($subnav->getRenderAsButton()) { - $action_menu->addButton( - 'logout', - $subnav->getTitle(), - $subnav->getImage(), - array_merge( - $subnav->getLinkAttributes(), - ['formaction' => URLHelper::getURL($subnav->getURL(), [], true)] - ) - ); - } else { - $action_menu->addLink( - URLHelper::getURL($subnav->getURL(), [], true), - $subnav->getTitle(), - $subnav->getImage(), - $subnav->getLinkAttributes() - ); + foreach (Navigation::getItem('/avatar') as $subnav) { + if ($subnav->getRenderAsButton()) { + $action_menu->addButton( + 'logout', + $subnav->getTitle(), + $subnav->getImage(), + array_merge( + $subnav->getLinkAttributes(), + ['formaction' => URLHelper::getURL($subnav->getURL(), [], true)] + ) + ); + } else { + $action_menu->addLink( + URLHelper::getURL($subnav->getURL(), [], true), + $subnav->getTitle(), + $subnav->getImage(), + $subnav->getLinkAttributes() + ); + } } - } - SkipLinks::addIndex(_('Profilmenü'), 'header_avatar_image_link', 1, false); + SkipLinks::addIndex(_('Profilmenü'), 'header_avatar_image_link', 1, false); ?> <?= $action_menu->render(); ?> </form> <? endif; ?> </li> - <? else: ?> + <? endif; ?> + <? else: ?> <li> <form method="post" action="<?= URLHelper::getLink(Request::url(), ['cancel_login' => null]) ?>"> <? try {echo CSRFProtection::tokenTag();} catch (SessionRequiredException){}?> @@ -229,18 +227,18 @@ if ($navigation) { </form> </li> <li><?= $this->render_partial('login/_header_languages') ?></li> - <? endif; ?> + <? endif; ?> <li id="responsive-toggle-fullscreen"> <button class="styleless" id="fullscreen-off" title="<?= _('Kompakte Navigation ausschalten') ?>"> - <?= Icon::create('screen-standard', ICON::ROLE_INFO_ALT)->asImg(24) ?> + <?= Icon::create('screen-standard', Icon::ROLE_INFO_ALT)->asImg(24) ?> </button> </li> <li id="responsive-toggle-focusmode"> <button class="styleless consuming_mode_trigger" id="focusmode-on" title="<?= _('Vollbild aktivieren') ?>"> - <?= Icon::create('screen-full', ICON::ROLE_INFO_ALT)->asImg(24) ?> + <?= Icon::create('screen-full', Icon::ROLE_INFO_ALT)->asImg(24) ?> </button> </li> </ul>