Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
21 changes: 13 additions & 8 deletions Core/Resgrid.Chatbot/Handlers/CallsActionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,25 @@ public async Task<ChatbotResponse> HandleAsync(ChatbotMessage message, ChatbotIn
var culture = session.Culture;
try
{
// Layer 2 for a list: every active member may list their department's calls, so the
// permission is decided once per request against session.DepartmentId. A per-row
// CanUserViewCallAsync answers the same membership question with two lookups per call.
if (!await _authorizationService.IsUserValidWithinLimitsAsync(session.UserId, session.DepartmentId))
return new ChatbotResponse { Text = ChatbotResources.Get("Calls_NoPermission", culture), Processed = false };

var department = await _departmentsService.GetDepartmentByIdAsync(session.DepartmentId);
var departmentName = department?.Name ?? ChatbotResources.Get("Common_YourDepartment", culture);
var activeCalls = await _callsService.GetActiveCallsByDepartmentAsync(session.DepartmentId);

if (activeCalls == null || !activeCalls.Any())
// The department boundary is the per-row rule (the unread-messages list applies the same one).
var callList = (activeCalls ?? Enumerable.Empty<Resgrid.Model.Call>())
.Where(call => call != null && call.DepartmentId == session.DepartmentId)
.Take(10)
.ToList();

if (callList.Count == 0)
return new ChatbotResponse { Text = ChatbotResources.Get("Calls_NoActive", culture, departmentName), Processed = true };

var callList = new System.Collections.Generic.List<Resgrid.Model.Call>();
foreach (var call in activeCalls)
{
if (call.DepartmentId == session.DepartmentId && await _authorizationService.CanUserViewCallAsync(session.UserId, call.CallId))
callList.Add(call);
if (callList.Count == 10) break;
}
var sb = new StringBuilder();
sb.AppendLine(ChatbotResources.Get("Calls_Header", culture, departmentName));
sb.AppendLine("----------------------");
Expand Down
10 changes: 8 additions & 2 deletions Core/Resgrid.Chatbot/Handlers/UnitsActionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ public async Task<ChatbotResponse> HandleAsync(ChatbotMessage message, ChatbotIn
var culture = session.Culture;
try
{
// Layer 2 for a list: every active member may list their department's units, so the
// permission is decided once per request against session.DepartmentId. A per-row
// CanUserViewUnitAsync answers the same membership question with two lookups per unit.
if (!await _authorizationService.IsUserValidWithinLimitsAsync(session.UserId, session.DepartmentId))
return new ChatbotResponse { Text = ChatbotResources.Get("Units_NoPermission", culture), Processed = false };

var department = await _departmentsService.GetDepartmentByIdAsync(session.DepartmentId);
var departmentName = department?.Name ?? ChatbotResources.Get("Common_YourDepartment", culture);
var unitStatuses = await _unitsService.GetAllLatestStatusForUnitsByDepartmentIdAsync(session.DepartmentId);
Expand All @@ -47,9 +53,9 @@ public async Task<ChatbotResponse> HandleAsync(ChatbotMessage message, ChatbotIn
var listedCount = 0;
foreach (var unitState in unitStatuses)
{
// The department boundary is the per-row rule.
var unit = unitState?.Unit;
if (unit == null || unit.DepartmentId != session.DepartmentId
|| !await _authorizationService.CanUserViewUnitAsync(session.UserId, unit.UnitId))
if (unit == null || unit.DepartmentId != session.DepartmentId)
continue;

var status = await _customStateService.GetCustomUnitStateAsync(unitState);
Expand Down
11 changes: 8 additions & 3 deletions Core/Resgrid.Chatbot/Handlers/UnitsAvailableActionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ public async Task<ChatbotResponse> HandleAsync(ChatbotMessage message, ChatbotIn
var culture = session.Culture;
try
{
// Layer 2 for a list: every active member may list their department's units, so the
// permission is decided once per request against session.DepartmentId. A per-row
// CanUserViewUnitAsync answers the same membership question with two lookups per unit —
// over the whole department here, since availability is classified before the cap.
if (!await _authorizationService.IsUserValidWithinLimitsAsync(session.UserId, session.DepartmentId))
return new ChatbotResponse { Text = ChatbotResources.Get("Units_NoPermission", culture), Processed = false };

var unitStatuses = await _unitsService.GetAllLatestStatusForUnitsByDepartmentIdAsync(session.DepartmentId);

if (unitStatuses == null || !unitStatuses.Any())
Expand All @@ -51,12 +58,10 @@ public async Task<ChatbotResponse> HandleAsync(ChatbotMessage message, ChatbotIn
var lines = new StringBuilder();
var availableCount = 0;

// The department boundary is the per-row rule.
foreach (var unitState in unitStatuses.Where(u => u?.Unit != null && u.Unit.DepartmentId == session.DepartmentId)
.OrderBy(u => u.Unit.Name))
{
if (!await _authorizationService.CanUserViewUnitAsync(session.UserId, unitState.Unit.UnitId))
continue;

var availability = await _platformReportingService.ClassifyUnitAvailabilityAsync(session.DepartmentId, unitState.State);
if (availability != AvailabilityClass.Available)
continue;
Expand Down
22 changes: 22 additions & 0 deletions Core/Resgrid.Chatbot/Localization/ChatbotResources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,17 @@ private static Dictionary<string, string> EnOnly(string en)
"Немає активних викликів для {0}.",
"لا توجد بلاغات نشطة لـ {0}."),

["Calls_NoPermission"] = L(
"You don't have permission to view calls for your department.",
"No tienes permiso para ver las llamadas de tu departamento.",
"Du har inte behörighet att se larm för din avdelning.",
"Sie haben keine Berechtigung, die Einsätze Ihrer Abteilung anzuzeigen.",
"Vous n'avez pas la permission de voir les appels de votre département.",
"Non hai il permesso di visualizzare le chiamate del tuo dipartimento.",
"Nie masz uprawnień do wyświetlania zgłoszeń swojego działu.",
"У вас немає дозволу переглядати виклики вашого підрозділу.",
"ليس لديك إذن لعرض بلاغات قسمك."),

["Calls_Header"] = L(
"Active Calls for {0}:",
"Llamadas activas para {0}:",
Expand Down Expand Up @@ -1231,6 +1242,17 @@ private static Dictionary<string, string> EnOnly(string en)
"Підрозділів для вашого відділу не знайдено.",
"لم يتم العثور على وحدات لقسمك."),

["Units_NoPermission"] = L(
"You don't have permission to view units for your department.",
"No tienes permiso para ver las unidades de tu departamento.",
"Du har inte behörighet att se enheter för din avdelning.",
"Sie haben keine Berechtigung, die Einheiten Ihrer Abteilung anzuzeigen.",
"Vous n'avez pas la permission de voir les unités de votre département.",
"Non hai il permesso di visualizzare le unità del tuo dipartimento.",
"Nie masz uprawnień do wyświetlania jednostek swojego działu.",
"У вас немає дозволу переглядати підрозділи вашого відділу.",
"ليس لديك إذن لعرض وحدات قسمك."),

["Units_Header"] = L(
"Unit Statuses for {0}:",
"Estados de unidades para {0}:",
Expand Down
19 changes: 19 additions & 0 deletions Core/Resgrid.Framework/DateTimeHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,25 @@
return resultedDay;
}

/// <summary>
/// True when <paramref name="timeZone"/> is a Windows zone id that <see cref="ConvertToUtc"/> and
/// <see cref="GetLocalDateTime"/> can resolve (blank counts as "no zone", which both accept).
/// </summary>
public static bool IsKnownTimeZone(string timeZone)
{
if (String.IsNullOrWhiteSpace(timeZone))
return true;

try
{
return DateTimeZoneProviders.Tzdb.GetZoneOrNull(TZConvert.WindowsToIana(timeZone)) != null;
}
catch (Exception)
{
return false;
}
}

/// <summary>
/// Converts a local (department) time into UTC. When lenient is set, local times that are
/// ambiguous or skipped by a DST transition are resolved instead of throwing, which matters
Expand Down Expand Up @@ -374,7 +393,7 @@
}

//http://stackoverflow.com/questions/381401/how-do-you-compare-datetime-objects-using-a-specified-tolerance-in-c
public class DateTimeWithin

Check warning on line 396 in Core/Resgrid.Framework/DateTimeHelpers.cs

View workflow job for this annotation

GitHub Actions / build-and-test

'DateTimeWithin' defines operator == or operator != but does not override Object.GetHashCode()

Check warning on line 396 in Core/Resgrid.Framework/DateTimeHelpers.cs

View workflow job for this annotation

GitHub Actions / build-and-test

'DateTimeWithin' defines operator == or operator != but does not override Object.Equals(object o)
{
public DateTimeWithin(DateTime dateTime, TimeSpan tolerance)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@
<data name="DispatchedUnits" xml:space="preserve"><value>الوحدات المرسلة</value></data>
<data name="IssuedEquipment" xml:space="preserve"><value>المعدات المصروفة وقت البلاغ</value></data>
<data name="ScheduledReportProtected" xml:space="preserve"><value>تحتوي الرسالة المجدولة على إشعار PDF ورابط آمن للتقرير. افتح التقرير للتحقق من صلاحياتك الحالية وفتح البيانات المحمية؛ لا تُرسل نتائج الفحوص أو البيانات الشخصية بالبريد الإلكتروني.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>حدد فترة زمنية مرتبة بتوقيت UTC لا تتجاوز 93 يوماً.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>حدد فترة زمنية مرتبة بتوقيت لا تتجاوز 93 يوماً.</value></data>
<data name="ReportTooLarge" xml:space="preserve"><value>هذا التقرير كبير جداً. حدد فترة أقصر.</value></data>
<data name="ReadinessCallUnavailable" xml:space="preserve"><value>البلاغ غير متاح أو تم رفض الوصول.</value></data>
<data name="HistoricalAssetInvalid" xml:space="preserve"><value>بيانات المصدر التاريخي للمعدات ناقصة أو غير متسقة.</value></data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@
<data name="DispatchedUnits" xml:space="preserve"><value>Alarmierte Einheiten</value></data>
<data name="IssuedEquipment" xml:space="preserve"><value>Zum Einsatzzeitpunkt ausgegebene Ausrüstung</value></data>
<data name="ScheduledReportProtected" xml:space="preserve"><value>Der geplante Versand enthält einen PDF-Hinweis und einen sicheren Berichtslink. Öffnen Sie den Bericht, um Ihre aktuelle Berechtigung zu prüfen und geschützte Daten freizugeben. Prüfergebnisse und personenbezogene Daten werden nicht per E-Mail versendet.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Wählen Sie einen chronologischen UTC-Zeitraum von höchstens 93 Tagen.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Wählen Sie einen chronologischen Zeitraum von höchstens 93 Tagen.</value></data>
<data name="ReportTooLarge" xml:space="preserve"><value>Dieser Bericht ist zu groß. Wählen Sie einen kürzeren Zeitraum.</value></data>
<data name="ReadinessCallUnavailable" xml:space="preserve"><value>Der Einsatz ist nicht verfügbar oder der Zugriff wurde verweigert.</value></data>
<data name="HistoricalAssetInvalid" xml:space="preserve"><value>Die Herkunftsnachweise der früheren Ausrüstung sind unvollständig oder widersprüchlich.</value></data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@
<data name="DispatchedUnits" xml:space="preserve"><value>Μονάδες που κινητοποιήθηκαν</value></data>
<data name="IssuedEquipment" xml:space="preserve"><value>Εξοπλισμός που είχε χορηγηθεί κατά το συμβάν</value></data>
<data name="ScheduledReportProtected" xml:space="preserve"><value>Η προγραμματισμένη αποστολή περιέχει ειδοποίηση PDF και ασφαλή σύνδεσμο αναφοράς. Ανοίξτε την αναφορά για επαλήθευση της τρέχουσας πρόσβασης και ξεκλείδωμα προστατευμένων δεδομένων· αποτελέσματα ελέγχων και προσωπικά δεδομένα δεν αποστέλλονται με ηλεκτρονικό ταχυδρομείο.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Επιλέξτε χρονολογική περίοδο UTC έως 93 ημερών.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Επιλέξτε χρονολογική περίοδο έως 93 ημερών.</value></data>
<data name="ReportTooLarge" xml:space="preserve"><value>Η αναφορά είναι πολύ μεγάλη. Επιλέξτε μικρότερη περίοδο.</value></data>
<data name="ReadinessCallUnavailable" xml:space="preserve"><value>Το συμβάν δεν είναι διαθέσιμο ή δεν επιτρέπεται η πρόσβαση.</value></data>
<data name="HistoricalAssetInvalid" xml:space="preserve"><value>Η προέλευση του παλαιότερου εξοπλισμού είναι ελλιπής ή ασυνεπής.</value></data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@
<data name="DispatchedUnits" xml:space="preserve"><value>Dispatched units</value></data>
<data name="IssuedEquipment" xml:space="preserve"><value>Equipment issued at call time</value></data>
<data name="ScheduledReportProtected" xml:space="preserve"><value>Scheduled delivery contains a PDF notice and a secure report link. Open the report to verify your current access and unlock protected data; checklist results and personal data are not emailed.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Select a chronological UTC period of at most 93 days.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Select a chronological period of at most 93 days.</value></data>
<data name="ReportTooLarge" xml:space="preserve"><value>This report is too large. Select a shorter period.</value></data>
<data name="ReadinessCallUnavailable" xml:space="preserve"><value>The call is unavailable or access is denied.</value></data>
<data name="HistoricalAssetInvalid" xml:space="preserve"><value>Historical equipment provenance is incomplete or inconsistent.</value></data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@
<data name="DispatchedUnits" xml:space="preserve"><value>Unidades movilizadas</value></data>
<data name="IssuedEquipment" xml:space="preserve"><value>Equipo entregado en el momento del incidente</value></data>
<data name="ScheduledReportProtected" xml:space="preserve"><value>El envío programado contiene un aviso PDF y un enlace seguro al informe. Abra el informe para verificar su acceso actual y desbloquear datos protegidos; no se envían por correo resultados de verificaciones ni datos personales.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Seleccione un período UTC cronológico de hasta 93 días.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Seleccione un período cronológico de hasta 93 días.</value></data>
<data name="ReportTooLarge" xml:space="preserve"><value>Este informe es demasiado grande. Seleccione un período más corto.</value></data>
<data name="ReadinessCallUnavailable" xml:space="preserve"><value>El incidente no está disponible o se ha denegado el acceso.</value></data>
<data name="HistoricalAssetInvalid" xml:space="preserve"><value>La procedencia histórica del equipo está incompleta o es incoherente.</value></data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@
<data name="DispatchedUnits" xml:space="preserve"><value>Unités mobilisées</value></data>
<data name="IssuedEquipment" xml:space="preserve"><value>Équipement attribué au moment de l’intervention</value></data>
<data name="ScheduledReportProtected" xml:space="preserve"><value>L’envoi programmé contient un avis PDF et un lien sécurisé vers le rapport. Ouvrez le rapport pour vérifier vos droits actuels et déverrouiller les données protégées ; les résultats de contrôle et les données personnelles ne sont pas envoyés par courriel.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Sélectionnez une période UTC chronologique de 93 jours au maximum.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Sélectionnez une période chronologique de 93 jours au maximum.</value></data>
<data name="ReportTooLarge" xml:space="preserve"><value>Ce rapport est trop volumineux. Sélectionnez une période plus courte.</value></data>
<data name="ReadinessCallUnavailable" xml:space="preserve"><value>L’intervention est indisponible ou l’accès est refusé.</value></data>
<data name="HistoricalAssetInvalid" xml:space="preserve"><value>La provenance historique de l’équipement est incomplète ou incohérente.</value></data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@
<data name="DispatchedUnits" xml:space="preserve"><value>Unità mobilitate</value></data>
<data name="IssuedEquipment" xml:space="preserve"><value>Attrezzatura assegnata al momento dell’intervento</value></data>
<data name="ScheduledReportProtected" xml:space="preserve"><value>L’invio programmato contiene un avviso PDF e un collegamento sicuro al rapporto. Apri il rapporto per verificare l’accesso attuale e sbloccare i dati protetti; i risultati dei controlli e i dati personali non vengono inviati per e-mail.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Seleziona un periodo UTC cronologico di massimo 93 giorni.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Seleziona un periodo cronologico di massimo 93 giorni.</value></data>
<data name="ReportTooLarge" xml:space="preserve"><value>Questo rapporto è troppo grande. Seleziona un periodo più breve.</value></data>
<data name="ReadinessCallUnavailable" xml:space="preserve"><value>L’intervento non è disponibile o l’accesso è negato.</value></data>
<data name="HistoricalAssetInvalid" xml:space="preserve"><value>La provenienza storica dell’attrezzatura è incompleta o incoerente.</value></data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@
<data name="DispatchedUnits" xml:space="preserve"><value>Zadysponowane jednostki</value></data>
<data name="IssuedEquipment" xml:space="preserve"><value>Sprzęt wydany w chwili zdarzenia</value></data>
<data name="ScheduledReportProtected" xml:space="preserve"><value>Zaplanowana wysyłka zawiera powiadomienie PDF i bezpieczny odnośnik do raportu. Otwórz raport, aby zweryfikować bieżący dostęp i odblokować chronione dane; wyniki kontroli i dane osobowe nie są wysyłane pocztą elektroniczną.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Wybierz chronologiczny okres UTC nie dłuższy niż 93 dni.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Wybierz chronologiczny okres nie dłuższy niż 93 dni.</value></data>
<data name="ReportTooLarge" xml:space="preserve"><value>Ten raport jest zbyt duży. Wybierz krótszy okres.</value></data>
<data name="ReadinessCallUnavailable" xml:space="preserve"><value>Zdarzenie jest niedostępne lub odmówiono dostępu.</value></data>
<data name="HistoricalAssetInvalid" xml:space="preserve"><value>Historyczne pochodzenie sprzętu jest niepełne lub niespójne.</value></data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@
<data name="DispatchedUnits" xml:space="preserve"><value>Dispatched units</value></data>
<data name="IssuedEquipment" xml:space="preserve"><value>Equipment issued at call time</value></data>
<data name="ScheduledReportProtected" xml:space="preserve"><value>Scheduled delivery contains a PDF notice and a secure report link. Open the report to verify your current access and unlock protected data; checklist results and personal data are not emailed.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Select a chronological UTC period of at most 93 days.</value></data>
<data name="ReportRangeInvalid" xml:space="preserve"><value>Select a chronological period of at most 93 days.</value></data>
<data name="ReportTooLarge" xml:space="preserve"><value>This report is too large. Select a shorter period.</value></data>
<data name="ReadinessCallUnavailable" xml:space="preserve"><value>The call is unavailable or access is denied.</value></data>
<data name="HistoricalAssetInvalid" xml:space="preserve"><value>Historical equipment provenance is incomplete or inconsistent.</value></data>
Expand Down
Loading
Loading