From b61aca2692f04f9be376d001877e453cedf8423f Mon Sep 17 00:00:00 2001 From: zachmann <gabriel.zachmann@kit.edu> Date: Tue, 27 Feb 2024 09:37:42 +0100 Subject: [PATCH] [web] add notifications info to tokeninfo pane --- internal/server/handlers.go | 21 +++--- .../web/partials/calendar-listing.mustache | 27 ++++++++ .../web/partials/notifications-info.mustache | 31 +++++++++ internal/server/web/partials/scripts.mustache | 1 + .../settings-notifications-calendar.mustache | 21 ------ .../partials/settings-notifications.mustache | 2 +- .../server/web/partials/tokeninfo.mustache | 3 + .../server/web/static/js/notifications.js | 5 +- .../web/static/js/settings-notifications.js | 58 +++++++++------- internal/server/web/static/js/settings.js | 2 + .../server/web/static/js/tokeninfo-status.js | 69 +++++++++++++++++++ internal/utils/templating/keys.go | 1 + 12 files changed, 183 insertions(+), 58 deletions(-) create mode 100644 internal/server/web/partials/calendar-listing.mustache create mode 100644 internal/server/web/partials/notifications-info.mustache delete mode 100644 internal/server/web/partials/settings-notifications-calendar.mustache diff --git a/internal/server/handlers.go b/internal/server/handlers.go index cacee0cd..a0bca10f 100644 --- a/internal/server/handlers.go +++ b/internal/server/handlers.go @@ -47,8 +47,9 @@ func homeBindingData() map[string]interface{} { templating.MustacheKeyCollapse: templating.Collapsable{ CollapseRestr: true, }, - templating.MustacheKeyPrefix: "tokeninfo-", - templating.MustacheKeyReadOnly: true, + templating.MustacheKeyPrefix: "tokeninfo-", + templating.MustacheKeyReadOnly: true, + templating.MustacheKeyCalendarsEditable: false, }, templating.MustacheSubCreateMT: map[string]interface{}{ templating.MustacheKeyPrefix: "createMT-", @@ -196,13 +197,15 @@ func handleSettings(ctx *fiber.Ctx) error { g.EmbedBody = embed.String() } binding := map[string]interface{}{ - templating.MustacheKeyGrants: grants, - templating.MustacheKeyLoggedIn: true, - templating.MustacheKeySettings: true, - templating.MustacheKeySettingsSSH: true, - templating.MustacheKeyRestrictionsGUI: true, - templating.MustacheKeyRestrictions: webentities.WebRestrictions{}, - templating.MustacheKeyCapabilities: webentities.AllWebCapabilities(), + templating.MustacheKeyGrants: grants, + templating.MustacheKeyLoggedIn: true, + templating.MustacheKeySettings: true, + templating.MustacheKeySettingsSSH: true, + templating.MustacheKeyRestrictionsGUI: true, + templating.MustacheKeyCalendarsEditable: true, + templating.MustacheKeyRestrictions: webentities.WebRestrictions{}, + templating.MustacheKeyCapabilities: webentities.AllWebCapabilities(), + templating.MustacheKeyPrefix: "settings-", } return ctx.Render("sites/settings", binding, templating.LayoutMain) } diff --git a/internal/server/web/partials/calendar-listing.mustache b/internal/server/web/partials/calendar-listing.mustache new file mode 100644 index 00000000..ce158ba3 --- /dev/null +++ b/internal/server/web/partials/calendar-listing.mustache @@ -0,0 +1,27 @@ +<table class="table table-striped bg-my_grey"> + <thead> + <tr> + <th>Name</th> + <th></th> + <th>ICS Link</th> + {{#calendar-editable}} + <th></th> + {{/calendar-editable}} + </tr> + </thead> + <tbody id="{{prefix}}calendars"> + <tr id="{{prefix}}noCalendars"> + <td colspan="{{#calendar-editable}}{{/calendar-editable}}4{{^calendar-editable}}3{{/calendar-editable}}" + class="text-muted text-center">No calendar created. + </td> + </tr> + {{#calendar-editable}} + <tr> + <td colspan="4" class="text-center"> + <button class="btn btn-success new-calendar-btn" role="button" id="{{prefix}}new-calendar-btn"><i + class="fas fa-plus-circle"> New Calendar</i></button> + </td> + </tr> + {{/calendar-editable}} + </tbody> +</table> diff --git a/internal/server/web/partials/notifications-info.mustache b/internal/server/web/partials/notifications-info.mustache new file mode 100644 index 00000000..c3938d01 --- /dev/null +++ b/internal/server/web/partials/notifications-info.mustache @@ -0,0 +1,31 @@ +<h4>Notifications</h4> +{{#collapse.Notifications}} + <button class="btn btn-info my-expand" type="button" data-toggle="collapse" + data-target="#{{prefix}}not-info-body" + aria-expanded="false" aria-controls="{{prefix}}not-info-body" id="{{prefix}}not-info-expand">Expand + </button> +{{/collapse.Notifications}} + + +<div id="{{prefix}}not-info-body" class="{{#collapse.Notifications}}collapse{{/collapse.Notifications}}"> + + <div> + <p id="{{prefix}}tokeninfo-notifications-no" class="d-none"> + This mytoken does not have anay active notifications. + </p> + <div id="{{prefix}}tokeninfo-notifications-listing"> + <p>The following notifications are defined for this mytoken:</p> + <div id="{{prefix}}tokeninfo-notifications-listing-table-container"></div> + </div> + </div> + + + <h5>Calendars</h5> + <div id="{{prefix}}tokeninfo-calendar-listing"> + <p>This mytoken is included in the following calendars:</p> + {{>calendar-listing}} + </div> + <div id="{{prefix}}tokeninfo-calendar-no" class="d-none"> + <p>This mytoken is not included in any calendars</p> + </div> +</div> diff --git a/internal/server/web/partials/scripts.mustache b/internal/server/web/partials/scripts.mustache index ad12e1a2..04d0f2f8 100644 --- a/internal/server/web/partials/scripts.mustache +++ b/internal/server/web/partials/scripts.mustache @@ -33,6 +33,7 @@ <script src="{{instance-url}}/static/js/create-mt.js"></script> <script src="{{instance-url}}/static/js/capabilities.js"></script> <script src="{{instance-url}}/static/js/notifications.js"></script> + <script src="{{instance-url}}/static/js/settings-notifications.js"></script> <!-- TODO adapt--> {{/home}} {{#settings}} <script src="{{instance-url}}/static/js/tokeninfo.js"></script> diff --git a/internal/server/web/partials/settings-notifications-calendar.mustache b/internal/server/web/partials/settings-notifications-calendar.mustache deleted file mode 100644 index c71fe4db..00000000 --- a/internal/server/web/partials/settings-notifications-calendar.mustache +++ /dev/null @@ -1,21 +0,0 @@ -<table class="table table-striped bg-my_grey"> - <thead> - <tr> - <th>Name</th> - <th></th> - <th>ICS Link</th> - <th></th> - </tr> - </thead> - <tbody id="calendars"> - <tr id="noCalendars"> - <td colspan="4" class="text-muted text-center">No calendar created.</td> - </tr> - <tr> - <td colspan="4" class="text-center"> - <button class="btn btn-success" role="button" id="new-calendar-btn"><i class="fas fa-plus-circle"> New - Calendar</i></button> - </td> - </tr> - </tbody> -</table> diff --git a/internal/server/web/partials/settings-notifications.mustache b/internal/server/web/partials/settings-notifications.mustache index c1d730b9..3a47e7d0 100644 --- a/internal/server/web/partials/settings-notifications.mustache +++ b/internal/server/web/partials/settings-notifications.mustache @@ -34,7 +34,7 @@ <tr class="collapse no-top-border" id="settings-notifications-calendar-body"> <td class="no-top-border"></td> <td class="no-top-border" colspan="3"> - <div>{{> settings-notifications-calendar }}</div> + <div>{{> calendar-listing }}</div> </td> </tr> diff --git a/internal/server/web/partials/tokeninfo.mustache b/internal/server/web/partials/tokeninfo.mustache index 4825440a..9cfb5a41 100644 --- a/internal/server/web/partials/tokeninfo.mustache +++ b/internal/server/web/partials/tokeninfo.mustache @@ -134,6 +134,9 @@ <div class="alert border"> {{>rotation}} </div> + <div id="tokeninfo-notifications-display" class="alert border d-none"> + {{>notifications-info}} + </div> </div> </div> diff --git a/internal/server/web/static/js/notifications.js b/internal/server/web/static/js/notifications.js index b50b1760..0dfe67a7 100644 --- a/internal/server/web/static/js/notifications.js +++ b/internal/server/web/static/js/notifications.js @@ -50,7 +50,7 @@ function getNotificationClassIcon(notificationInfo, class_name) { } } -function notificationsToTable(notifications) { +function notificationsToTable(notifications, details = true) { let tableEntries = ""; const websocketIcon = `<i class="fas fa-rss"></i>`; const mailIcon = `<i class="fas fa-envelope"></i>`; @@ -82,7 +82,8 @@ function notificationsToTable(notifications) { let notification_classes_html = `<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="${notification_classes_str}">${notification_classes_icons}</span>` - tableEntries += `<tr management-code="${management_code}" role="button" onclick="toggleSubscriptionDetails('${management_code}')"><td>${typeIcon}</td><td>${notification_classes_html}</td><td>${tokens}</td></tr>`; + let details_html = details ? `role="button" onclick="toggleSubscriptionDetails('${management_code}')"` : ''; + tableEntries += `<tr management-code="${management_code}" ${details_html}><td>${typeIcon}</td><td>${notification_classes_html}</td><td>${tokens}</td></tr>`; tableEntries += `<tr><td colspan="4" management-code="${management_code}" class="notification-details-container d-none pl-5"></td></tr>` }); if (tableEntries === "") { diff --git a/internal/server/web/static/js/settings-notifications.js b/internal/server/web/static/js/settings-notifications.js index 2e9f0653..a50528f9 100644 --- a/internal/server/web/static/js/settings-notifications.js +++ b/internal/server/web/static/js/settings-notifications.js @@ -6,9 +6,14 @@ const $preferredMimeTypeText = $('#preferred_mimetype_txt'); const $editMailBtn = $('#edit-mail-btn'); const $saveMailBtn = $('#save-mail-btn'); -const $calendarTable = $('#calendars'); -const $noCalendarsEntry = $('#noCalendars'); -const $addCalendarButton = $('#new-calendar-btn'); + +function $calendarTable(prefix = "") { + return $(prefixId('calendars', prefix)); +} + +function $noCalendarsEntry(prefix = "") { + return $(prefixId('noCalendars', prefix)); +} let settingsStatus = {"email_data_obtained": false, "calendars_loaded": false}; @@ -108,22 +113,22 @@ $emailInput.on('keyup', function (e) { $('#calendar-trigger-btn').on('click', function () { if (!settingsStatus["calendars_loaded"]) { - loadCalendars(); + loadCalendars(settingsPrefix); } }) -function loadCalendars() { - clearCalendarTable(); +function loadCalendars(prefix = "") { + clearCalendarTable(prefix); $.ajax({ type: "GET", url: storageGet('notifications_endpoint') + "/calendars", success: function (res) { let cals = res["calendars"]; if (cals === undefined || cals === null) { - $noCalendarsEntry.showB(); + $noCalendarsEntry(prefix).showB(); } else { cals.forEach(function (cal) { - addCalendarToTable(cal); + addCalendarToTable(cal, prefix); }) settingsStatus["calendars_loaded"] = true; } @@ -135,22 +140,24 @@ function loadCalendars() { }); } -const deleteCalendarHtml = `<td><a href="#" role="button" onclick="deleteCalendar(this)"><i class="fas fa-trash-alt text-danger"></i></a></td>`; +function deleteCalendarHtml(prefix = "") { + return `<td><a href="#" role="button" onclick="deleteCalendar(this, '${prefix}')"><i class="fas fa-trash-alt text-danger"></i></a></td>`; +} -function clearCalendarTable() { - $calendarTable.find('tr.calendar-entry').remove(); +function clearCalendarTable(prefix = "") { + $calendarTable(prefix).find('tr.calendar-entry').remove(); } -function addCalendarToTable(cal) { - $noCalendarsEntry.hideB(); +function addCalendarToTable(cal, prefix = "", with_delete = true) { + $noCalendarsEntry(prefix).hideB(); let name = cal['name']; let ics_path = cal['ics_path']; let viewCalendarHtml = `<td><a href="${ics_path}/view"><i class="fas fa-calendar-alt"></i></a></td>`; - const html = `<tr class="calendar-entry"><td>${name}</td>${viewCalendarHtml}<td><a href="${ics_path}" target="_blank" rel="noopener noreferrer">${ics_path}</a></td>${deleteCalendarHtml}</tr>`; - $calendarTable.prepend(html); + const html = `<tr class="calendar-entry"><td>${name}</td>${viewCalendarHtml}<td><a href="${ics_path}" target="_blank" rel="noopener noreferrer">${ics_path}</a></td>${with_delete ? deleteCalendarHtml(prefix) : ""}</tr>`; + $calendarTable(prefix).prepend(html); } -function deleteCalendar(el) { +function deleteCalendar(el, prefix = "") { const name = $(el).parent().siblings()[0].innerHTML; $(` <div class="modal fade" tabindex="-1" role="dialog"> @@ -165,7 +172,7 @@ function deleteCalendar(el) { <div class="modal-body">Confirm to delete the calendar '${name}'.</div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> - <button type="button" class="btn btn-danger" data-dismiss="modal" onclick="sendDeleteCalendarRequest('${name}')">Delete</button> + <button type="button" class="btn btn-danger" data-dismiss="modal" onclick="sendDeleteCalendarRequest('${name}', '${prefix}')">Delete</button> </div> </div> </div> @@ -173,12 +180,12 @@ function deleteCalendar(el) { `).modal(); } -function sendDeleteCalendarRequest(name) { +function sendDeleteCalendarRequest(name, prefix = "") { $.ajax({ type: "DELETE", url: storageGet('notifications_endpoint') + "/calendars/" + name, success: function () { - loadCalendars(); + loadCalendars(prefix); }, error: function (errRes) { $settingsErrorModalMsg.text(getErrorMessage(errRes)); @@ -187,9 +194,11 @@ function sendDeleteCalendarRequest(name) { }); } -$addCalendarButton.on('click', addCalendar); +$('.new-calendar-btn').on('click', function () { + addCalendar(extractPrefix('new-calendar-btn', $(this).attr('id'))); +}); -function addCalendar() { +function addCalendar(prefix = "") { $(` <div class="modal fade" tabindex="-1" role="dialog"> <div class="modal-dialog modal-dialog-centered modal-lg" role="document"> @@ -206,7 +215,7 @@ function addCalendar() { </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> - <button type="button" class="btn btn-success" data-dismiss="modal" onclick="sendCreateCalendarRequest()">Create <i class="fas fa-calendar-alt"></i></button> + <button type="button" class="btn btn-success" data-dismiss="modal" onclick="sendCreateCalendarRequest('${prefix}')">Create <i class="fas fa-calendar-alt"></i></button> </div> </div> </div> @@ -214,9 +223,8 @@ function addCalendar() { `).modal(); } -function sendCreateCalendarRequest() { +function sendCreateCalendarRequest(prefix = "") { let data = JSON.stringify({"name": $('#calendar-name-input').val()}); - console.log(data); $.ajax({ type: "POST", data: data, @@ -224,7 +232,7 @@ function sendCreateCalendarRequest() { contentType: "application/json", url: storageGet('notifications_endpoint') + "/calendars", success: function () { - loadCalendars(); + loadCalendars(prefix); }, error: function (errRes) { $settingsErrorModalMsg.text(getErrorMessage(errRes)); diff --git a/internal/server/web/static/js/settings.js b/internal/server/web/static/js/settings.js index 1e6904dd..5250c5bd 100644 --- a/internal/server/web/static/js/settings.js +++ b/internal/server/web/static/js/settings.js @@ -7,6 +7,8 @@ $(function () { openCorrectTab(); }) +const settingsPrefix = "settings-"; + function initGrants(...next) { $.ajax({ type: "GET", diff --git a/internal/server/web/static/js/tokeninfo-status.js b/internal/server/web/static/js/tokeninfo-status.js index a8dcc9b0..959c520f 100644 --- a/internal/server/web/static/js/tokeninfo-status.js +++ b/internal/server/web/static/js/tokeninfo-status.js @@ -16,6 +16,27 @@ const $tokeninfoBadgeExpDate = $('#tokeninfo-token-exp-date'); const $tokeninfoTypeBadges = $('.tokeninfo-token-type'); const $tokeninfoTokenGoneWarningMsg = $('#token-gone-warning'); const $tokeninfoActionButtons = $('#token-action-buttons'); +const $tokeninfoNotificationsInfo = $('#tokeninfo-notifications-display'); + +function $tokeninfoCalendarListing(prefix = "") { + return $(prefixId('tokeninfo-calendar-listing', prefix)); +} + +function $tokeninfoNoCalendars(prefix = "") { + return $(prefixId('tokeninfo-calendar-no', prefix)); +} + +function $tokeninfoNotificationsListing(prefix = "") { + return $(prefixId('tokeninfo-notifications-listing', prefix)); +} + +function $tokeninfoNoNotifications(prefix = "") { + return $(prefixId('tokeninfo-notifications-no', prefix)); +} + +function $tokeninfoNotificationsListingTableContainer(prefix = "") { + return $(prefixId('tokeninfo-notifications-listing-table-container', prefix)); +} $('#tokeninfo-token-copy').on('click', function () { if (!$tokeninfoTokenGoneWarningMsg.hasClass('d-none')) { @@ -106,6 +127,7 @@ async function update_tokeninfo() { if (res['valid']) { $tokeninfoBadgeValid.showB(); $tokeninfoBadgeInvalid.hideB(); + notificationsInfo(token); } else { $tokeninfoBadgeValid.hideB(); $tokeninfoBadgeInvalid.showB(); @@ -149,6 +171,53 @@ async function update_tokeninfo() { $('#introspect-tab').tab('show'); } +function notificationsInfo(token) { + $.ajax({ + type: "POST", + url: tokeninfoEndpointToUse, + data: JSON.stringify({ + 'action': 'notifications', + 'mytoken': token, + }), + dataType: "json", + contentType: "application/json", + success: function (res) { + clearCalendarTable(tokeninfoPrefix); + let cals = res["calendars"]; + let notifications = res["notifications"]; + let calsSet = cals !== undefined && cals.length > 0; + let notificationsSet = notifications !== undefined && notifications.length > 0; + if (!calsSet && !notificationsSet) { + $tokeninfoNotificationsInfo.hideB(); + return; + } + $tokeninfoNotificationsInfo.showB(); + if (!calsSet) { + $tokeninfoCalendarListing(tokeninfoPrefix).hideB(); + $tokeninfoNoCalendars(tokeninfoPrefix).showB(); + } else { + $tokeninfoNoCalendars(tokeninfoPrefix).hideB(); + $tokeninfoCalendarListing(tokeninfoPrefix).showB(); + cals.forEach(function (cal) { + addCalendarToTable(cal, tokeninfoPrefix, false); + }) + } + if (!notificationsSet) { + $tokeninfoNotificationsListing(tokeninfoPrefix).hideB(); + $tokeninfoNoNotifications(tokeninfoPrefix).showB(); + } else { + $tokeninfoNoNotifications(tokeninfoPrefix).hideB(); + $tokeninfoNotificationsListing(tokeninfoPrefix).showB(); + $tokeninfoNotificationsListingTableContainer(tokeninfoPrefix).html(notificationsToTable(notifications, false)); + $('[data-toggle="tooltip"]').tooltip(); + } + }, + error: function (errRes) { + console.error(getErrorMessage(errRes)); + } + }); +} + $tokenInput.on('change', update_tokeninfo); $('#info-reload').on('click', update_tokeninfo); $('#info-tab').on('shown.bs.tab', update_tokeninfo); diff --git a/internal/utils/templating/keys.go b/internal/utils/templating/keys.go index a5fb67a3..3ab92ad5 100644 --- a/internal/utils/templating/keys.go +++ b/internal/utils/templating/keys.go @@ -38,6 +38,7 @@ const ( MustacheKeyProfilesRestrictions = "restrictions" MustacheKeyProfilesRotation = "rotation" MustacheKeyNotificationClasses = "notification-classes" + MustacheKeyCalendarsEditable = "calendar-editable" ) // Keys for sub configs -- GitLab