diff --git a/applications/Unity.GrantManager/.env.example b/applications/Unity.GrantManager/.env.example index c937e86596..42e07659d4 100644 --- a/applications/Unity.GrantManager/.env.example +++ b/applications/Unity.GrantManager/.env.example @@ -54,7 +54,7 @@ AuthServer__OidcSignoutCallback="http://localhost:44342/signout-callback-oidc" ##S3__SecretAccessKey="******************" ##S3__ApplicationS3Folder="Unity/Application" ##S3__AssessmentS3Folder="Unity/Adjudication" -##S3__DisallowedFileTypes="[ "exe" , "sh" , "ksh" , "bat" , "cmd" ]" +##S3__AllowedFileTypes=["pdf","doc","docx","xls","xlsx","ppt","pptx","jpg","jpeg","png","gif","txt","csv","zip","odt","ods","odp","rtf","bmp","tif","tiff","webp","heic","heif","eml","msg"] ##S3__MaxFileSize="25" ##S3__EmailAttachmentMaxFileSize="20" ##S3__EmailAttachmentsTotalMaxFileSize="25" diff --git a/applications/Unity.GrantManager/Directory.Build.props b/applications/Unity.GrantManager/Directory.Build.props index 4239024ce7..0e9f5007d6 100644 --- a/applications/Unity.GrantManager/Directory.Build.props +++ b/applications/Unity.GrantManager/Directory.Build.props @@ -10,7 +10,7 @@ - + diff --git a/applications/Unity.GrantManager/common.props b/applications/Unity.GrantManager/common.props index bdf9a7442f..e28f59fbc5 100644 --- a/applications/Unity.GrantManager/common.props +++ b/applications/Unity.GrantManager/common.props @@ -19,7 +19,7 @@ - + \ No newline at end of file diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/AI/Responses/FormWorksheetResponse.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/AI/Responses/FormWorksheetResponse.cs index 4fea2802d2..1ee1d837bc 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/AI/Responses/FormWorksheetResponse.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/AI/Responses/FormWorksheetResponse.cs @@ -1,69 +1,6 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - namespace Unity.AI.Responses; public class FormWorksheetResponse { public string Worksheet { get; set; } = string.Empty; - - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - [JsonPropertyName("title")] - public string Title { get; set; } = string.Empty; - - [JsonPropertyName("version")] - public uint Version { get; set; } = 1; - - [JsonPropertyName("published")] - public bool Published { get; set; } - - [JsonPropertyName("reportColumns")] - public string ReportColumns { get; set; } = string.Empty; - - [JsonPropertyName("reportKeys")] - public string ReportKeys { get; set; } = string.Empty; - - [JsonPropertyName("reportViewName")] - public string ReportViewName { get; set; } = string.Empty; - - [JsonPropertyName("sections")] - public List Sections { get; set; } = []; -} - -public class FormWorksheetSectionResponse -{ - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - [JsonPropertyName("order")] - public uint Order { get; set; } - - [JsonPropertyName("fields")] - public List Fields { get; set; } = []; -} - -public class FormWorksheetFieldResponse -{ - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; - - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; - - [JsonPropertyName("type")] - public int Type { get; set; } - - [JsonPropertyName("order")] - public uint Order { get; set; } - - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } = true; - - [JsonPropertyName("definition")] - public string? Definition { get; set; } } diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/Unity.AI.Application.Contracts.csproj b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/Unity.AI.Application.Contracts.csproj index d086c0cf1d..2222c49ef6 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/Unity.AI.Application.Contracts.csproj +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application.Contracts/Unity.AI.Application.Contracts.csproj @@ -6,15 +6,15 @@ Unity.AI - - - - + + + + - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/AIProviderPayloadValidator.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/AIProviderPayloadValidator.cs index 0f17bbd1cd..23d9ed10da 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/AIProviderPayloadValidator.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/AIProviderPayloadValidator.cs @@ -129,18 +129,10 @@ public static AIResponseValidationResult ValidateFormWorksheetJson(string respon return AIResponseValidationResult.Invalid("Form worksheet response was not valid JSON."); } - if (!root.TryGetProperty("title", out var title) - || title.ValueKind != JsonValueKind.String - || string.IsNullOrWhiteSpace(title.GetString())) + if (!root.TryGetProperty("fields", out var fields) + || fields.ValueKind != JsonValueKind.Array) { - return AIResponseValidationResult.Invalid("Form worksheet response is missing a non-empty 'title'."); - } - - if (!root.TryGetProperty("sections", out var sections) - || sections.ValueKind != JsonValueKind.Array - || sections.GetArrayLength() == 0) - { - return AIResponseValidationResult.Invalid("Form worksheet response must include at least one section."); + return AIResponseValidationResult.Invalid("Form worksheet response must include a 'fields' array."); } return AIResponseValidationResult.Success(); diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/DataSeed/AIPromptDataSeeder.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/DataSeed/AIPromptDataSeeder.cs index 044da04dc5..7df54a2a2a 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/DataSeed/AIPromptDataSeeder.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/DataSeed/AIPromptDataSeeder.cs @@ -848,8 +848,8 @@ Return only valid JSON in the exact mapping shape requested. // ── v2/form-worksheet.system.txt ─────────────────────────────────────── private const string FormWorksheetSystemV2 = """ - You are a worksheet definition generator for Unity Grant Manager. - Generate a recommended worksheet definition JSON that can be used to create a Flex worksheet. + You are a custom-field suggestion generator for Unity Grant Manager. + Recommend only the additional fields needed for a Flex worksheet. Return only valid JSON. """; @@ -860,47 +860,30 @@ Return only valid JSON. OUTPUT { - "Name": "", - "Title": "", - "Version": , - "Published": true, - "Sections": [ - { - "Name": "", - "Order": 1, - "Fields": [ - { - "Name": "", - "Key": "", - "Label": "", - "Type": , - "Definition": "" - } - ] - } - ], - "ReportColumns": "", - "ReportKeys": "", - "ReportViewName": "" + "fields": [ + { "key": "", "label": "", "type": "Text" } + ] } Rules: - - Return one worksheet definition JSON object only. + - Return one field-suggestion JSON object only. - chefsFields contains the available CHEFS source fields. - unityCoreFields contains existing Unity core fields. Do not create a custom field when one of these already fits. - - existingMapping contains any current confirmed Unity-to-CHEFS mappings. Do not duplicate those mappings with a custom field. - - existingWorksheets contains the previous AI worksheet definition, if one exists. Refine it rather than duplicating its custom fields. + - existingMapping contains the current saved Unity-to-CHEFS mappings. Do not duplicate those mappings with a custom field. + - existingCustomFields is a flattened list of fields from worksheets currently linked to this form version. Each entry includes its worksheet name, field name, label, and type. Do not create duplicate custom fields. - formSchema contains detailed CHEFS control configuration when labels and types need more context. - Use the provided form context to decide which custom fields are genuinely needed. - Prefer existing Unity core fields when they already satisfy the need. - Only create additional worksheet custom fields when the form genuinely needs them. - - Keep the worksheet structure valid for Flex. + - Do not include a worksheet title, sections, order, publish state, reporting fields, enabled flag, or field definition. + - Each key and label must be non-empty. Do not repeat a key. + - type must be one of: Text, TextArea, Numeric, Currency, Date, DateTime, Email, Phone, YesNo, Checkbox. Use the type name, never a number. - Return valid plain JSON only. """; private const string FormWorksheetMetadataV2 = """ { - "DATA": "Serialized JSON payload containing form metadata, CHEFS fields, Unity core fields, the current mapping, form schema, and the existing AI worksheet." + "DATA": "Serialized JSON payload containing form metadata, CHEFS fields, Unity core fields, the current mapping, form schema, and custom fields from worksheets currently linked to the form version." } """; diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/Unity.AI.Application.csproj b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/Unity.AI.Application.csproj index 2be3ad05e5..91c741e446 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/Unity.AI.Application.csproj +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/Unity.AI.Application.csproj @@ -6,22 +6,21 @@ Unity.AI - + - + - - - + + + - - - - - - - - + + + + + + + @@ -29,8 +28,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Domain.Shared/Unity.AI.Shared.csproj b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Domain.Shared/Unity.AI.Shared.csproj index f094575dec..30508215fd 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Domain.Shared/Unity.AI.Shared.csproj +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Domain.Shared/Unity.AI.Shared.csproj @@ -10,12 +10,12 @@ - - + + - + @@ -24,8 +24,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Web/Unity.AI.Web.csproj b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Web/Unity.AI.Web.csproj index b37df03bc2..4b5d57bd20 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Web/Unity.AI.Web.csproj +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Web/Unity.AI.Web.csproj @@ -12,16 +12,14 @@ - + - - - - + + - + @@ -40,8 +38,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application.Contracts/Unity.Flex.Application.Contracts.csproj b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application.Contracts/Unity.Flex.Application.Contracts.csproj index 259d86d1c9..0b0a20199a 100644 --- a/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application.Contracts/Unity.Flex.Application.Contracts.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application.Contracts/Unity.Flex.Application.Contracts.csproj @@ -9,13 +9,13 @@ - - - - - - - + + + + + + + @@ -23,8 +23,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application/Unity.Flex.Application.csproj b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application/Unity.Flex.Application.csproj index 331e21863e..43505d8f82 100644 --- a/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application/Unity.Flex.Application.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Application/Unity.Flex.Application.csproj @@ -9,24 +9,24 @@ - - - - - + + + + + - - - - - + + + + + - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Shared/Unity.Flex.Shared.csproj b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Shared/Unity.Flex.Shared.csproj index 9963d2d2bd..7da95766a1 100644 --- a/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Shared/Unity.Flex.Shared.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Shared/Unity.Flex.Shared.csproj @@ -10,15 +10,15 @@ - - - - - + + + + + - + @@ -30,8 +30,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Shared/Worksheets/Definitions/DefinitionResolver.cs b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Shared/Worksheets/Definitions/DefinitionResolver.cs index 3d184fac84..28ac11f104 100644 --- a/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Shared/Worksheets/Definitions/DefinitionResolver.cs +++ b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Shared/Worksheets/Definitions/DefinitionResolver.cs @@ -60,20 +60,20 @@ public static string Resolve(CustomFieldType type, object? definition) { CustomFieldType.Undefined => "{}", CustomFieldType.BCAddress => "{}", - CustomFieldType.Numeric => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.Text => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.Date => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.DateTime => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.Currency => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.YesNo => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.Email => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.Phone => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.Radio => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.Checkbox => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.CheckboxGroup => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.SelectList => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.TextArea => JsonSerializer.Serialize(element.ToString()), - CustomFieldType.DataGrid => JsonSerializer.Serialize(element.ToString()), + CustomFieldType.Numeric => element.GetRawText(), + CustomFieldType.Text => element.GetRawText(), + CustomFieldType.Date => element.GetRawText(), + CustomFieldType.DateTime => element.GetRawText(), + CustomFieldType.Currency => element.GetRawText(), + CustomFieldType.YesNo => element.GetRawText(), + CustomFieldType.Email => element.GetRawText(), + CustomFieldType.Phone => element.GetRawText(), + CustomFieldType.Radio => element.GetRawText(), + CustomFieldType.Checkbox => element.GetRawText(), + CustomFieldType.CheckboxGroup => element.GetRawText(), + CustomFieldType.SelectList => element.GetRawText(), + CustomFieldType.TextArea => element.GetRawText(), + CustomFieldType.DataGrid => element.GetRawText(), _ => throw new NotImplementedException(), }; } @@ -216,4 +216,4 @@ public static bool ResolveIsDynamic(CustomFieldDefinition field) }; } } -} \ No newline at end of file +} diff --git a/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Unity.Flex.Web.csproj b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Unity.Flex.Web.csproj index 1b9333d47f..f51c9ebdb9 100644 --- a/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Unity.Flex.Web.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Unity.Flex.Web.csproj @@ -12,13 +12,13 @@ - + - + - + @@ -55,8 +55,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.Application.Tests/Unity.Flex.Application.Tests.csproj b/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.Application.Tests/Unity.Flex.Application.Tests.csproj index fbe7add032..16ef810103 100644 --- a/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.Application.Tests/Unity.Flex.Application.Tests.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.Application.Tests/Unity.Flex.Application.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -12,20 +12,22 @@ - - - - - - - - - + + + + + + + + + + + - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.Application.Tests/Worksheets/DefinitionResolverTests.cs b/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.Application.Tests/Worksheets/DefinitionResolverTests.cs new file mode 100644 index 0000000000..6a065af65e --- /dev/null +++ b/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.Application.Tests/Worksheets/DefinitionResolverTests.cs @@ -0,0 +1,22 @@ +using System.Text.Json; +using Shouldly; +using Unity.Flex; +using Unity.Flex.Worksheets; +using Unity.Flex.Worksheets.Definitions; +using Xunit; + +namespace Unity.Flex.Application.Tests.Worksheets; + +public class DefinitionResolverTests +{ + [Fact] + public void Resolve_Should_Preserve_JsonObject_When_Definition_Is_JsonElement() + { + using var document = JsonDocument.Parse("""{"required":true,"maxLength":100}"""); + + var definition = DefinitionResolver.Resolve(CustomFieldType.Text, document.RootElement); + + definition.ShouldBe("""{"required":true,"maxLength":100}"""); + definition.ConvertDefinition(CustomFieldType.Text)!.Required.ShouldBeTrue(); + } +} diff --git a/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.TestBase/Unity.Flex.TestBase.csproj b/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.TestBase/Unity.Flex.TestBase.csproj index bc1afd5896..840463a334 100644 --- a/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.TestBase/Unity.Flex.TestBase.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.TestBase/Unity.Flex.TestBase.csproj @@ -1,4 +1,4 @@ - + @@ -9,30 +9,30 @@ - - - + + + all runtime; build; native; contentfiles; analyzers - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.Web.Tests/Unity.Flex.Web.Tests.csproj b/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.Web.Tests/Unity.Flex.Web.Tests.csproj index ce4d41158a..dac96e2ab9 100644 --- a/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.Web.Tests/Unity.Flex.Web.Tests.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Flex/test/Unity.Flex.Web.Tests/Unity.Flex.Web.Tests.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -10,24 +10,19 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - diff --git a/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Unity.Identity.Web.csproj b/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Unity.Identity.Web.csproj index ac7d62ebe2..86d284e211 100644 --- a/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Unity.Identity.Web.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Unity.Identity.Web.csproj @@ -31,17 +31,15 @@ - - - + + + - - - + - + diff --git a/applications/Unity.GrantManager/modules/Unity.Identity.Web/test/Unity.Identity.Web.Tests/Unity.Identity.Web.Tests.csproj b/applications/Unity.GrantManager/modules/Unity.Identity.Web/test/Unity.Identity.Web.Tests/Unity.Identity.Web.Tests.csproj index 4f55729056..cb31380f07 100644 --- a/applications/Unity.GrantManager/modules/Unity.Identity.Web/test/Unity.Identity.Web.Tests/Unity.Identity.Web.Tests.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Identity.Web/test/Unity.Identity.Web.Tests/Unity.Identity.Web.Tests.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -10,16 +10,16 @@ - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application.Contracts/Unity.Notifications.Application.Contracts.csproj b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application.Contracts/Unity.Notifications.Application.Contracts.csproj index 0e41f2f1c4..cb37cfb67e 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application.Contracts/Unity.Notifications.Application.Contracts.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application.Contracts/Unity.Notifications.Application.Contracts.csproj @@ -1,34 +1,33 @@ - - - - - - netstandard2.1;net10.0 - enable - Unity.Notifications - - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers - - - - - - - - + + + + + + netstandard2.1;net10.0 + enable + Unity.Notifications + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Emails/EmailAttachmentService.cs b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Emails/EmailAttachmentService.cs index 42c22f5d17..a68c3109cc 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Emails/EmailAttachmentService.cs +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Emails/EmailAttachmentService.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading.Tasks; using Unity.Notifications.Emails; +using Volo.Abp.Authorization; using Volo.Abp.DependencyInjection; using Volo.Abp.Users; @@ -77,13 +78,19 @@ public async Task UploadAttachmentAsync( ContentType = contentType, FileSize = fileContent.Length, Time = DateTime.UtcNow, + // Unlike UploadUserAttachmentAsync below, this path is reached from + // EmailNotificationHandler - a local event handler that can run for + // system/schedule-triggered emails with no interactive user in context, so a missing + // ICurrentUser.Id here isn't necessarily an error condition. The caller already wraps + // this in a try/catch that logs and sends the email without the attachment on any + // failure, so Guid.Empty (rather than throwing) is the intentional "no user" marker. UserId = _currentUser.Id ?? Guid.Empty, TenantId = tenantId }; await _emailLogAttachmentRepository.InsertAsync(attachment); return attachment; - } + } public async Task DownloadFromS3Async(string s3ObjectKey) { @@ -143,7 +150,10 @@ public async Task UploadUserAttachmentAsync( ContentType = contentType, FileSize = fileContent.Length, Time = DateTime.UtcNow, - UserId = _currentUser.Id ?? Guid.Empty, + // A missing ICurrentUser.Id means this was reached without an authenticated user - + // fail loudly rather than silently attributing the attachment to Guid.Empty, which + // would look like a valid, specific user rather than an error state. + UserId = _currentUser.Id ?? throw new AbpAuthorizationException("Cannot save an email attachment without an authenticated user."), TenantId = tenantId }; diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Emails/EmailLogAttachmentAppService.cs b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Emails/EmailLogAttachmentAppService.cs index 1933d5e6da..d5db9a21e3 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Emails/EmailLogAttachmentAppService.cs +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Emails/EmailLogAttachmentAppService.cs @@ -122,6 +122,12 @@ public async Task GetTotalFileSizeByEmailLogIdAsync(Guid? emailLogId, Guid return await emailAttachmentService.GetTotalFileSizeAsync(emailLogId, templateId); } + // Not exposed over HTTP: this method takes raw fileName/content/contentType with none of the + // allowlist/size/content-type validation AttachmentController enforces before calling it. It + // must only ever be reached in-process, via IEmailLogAttachmentUploadService, from a caller + // (AttachmentController) that has already run those checks - never directly by an HTTP client, + // which would bypass validation entirely despite still needing the Email.Send permission. + [RemoteService(false)] public async Task UploadAsync(Guid? emailLogId, Guid? templateId, Guid? tenantId, string fileName, byte[] content, string contentType) { var attachment = await emailAttachmentService.UploadUserAttachmentAsync(emailLogId, templateId, tenantId, fileName, content, contentType); diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Integrations/RabbitMQ/EmailQueueService.cs b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Integrations/RabbitMQ/EmailQueueService.cs index 6a954be51a..635e7c1034 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Integrations/RabbitMQ/EmailQueueService.cs +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Integrations/RabbitMQ/EmailQueueService.cs @@ -36,7 +36,7 @@ public async Task SendToEmailDelayedQueueAsync(EmailNotificationEvent emai await Task.Delay(TimeSpan.FromMilliseconds(FiveMinutesInMilliSeconds * (emailNotificationEvent.RetryAttempts + 1))); - _queueProducer.PublishMessage(message); + await _queueProducer.PublishMessageAsync(message); } catch (Exception ex) { var ExceptionMessage = ex.Message; @@ -45,7 +45,7 @@ public async Task SendToEmailDelayedQueueAsync(EmailNotificationEvent emai return Task.CompletedTask; } - public Task SendToEmailEventQueueAsync(EmailNotificationEvent emailNotificationEvent) + public async Task SendToEmailEventQueueAsync(EmailNotificationEvent emailNotificationEvent) { try { @@ -55,13 +55,11 @@ public Task SendToEmailEventQueueAsync(EmailNotificationEvent emailNotificationE TenantId = emailNotificationEvent.TenantId ?? Guid.Empty, EmailNotificationEvent = emailNotificationEvent }; - _queueProducer.PublishMessage(message); + await _queueProducer.PublishMessageAsync(message); } catch (Exception ex) { var ExceptionMessage = ex.Message; _logger.LogError(ex, "SendToEmailEventQueueAsync Exception: {ExceptionMessage}", ExceptionMessage); } - - return Task.CompletedTask; } } \ No newline at end of file diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Unity.Notifications.Application.csproj b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Unity.Notifications.Application.csproj index 0e50decfcd..7a8eacb9a4 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Unity.Notifications.Application.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Unity.Notifications.Application.csproj @@ -9,21 +9,19 @@ - - - - - - + + + + + + - - - - - - - - + + + + + + @@ -31,8 +29,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Domain.Shared/Unity.Notifications.Domain.Shared.csproj b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Domain.Shared/Unity.Notifications.Domain.Shared.csproj index 2eb1af44f6..5dcad10b53 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Domain.Shared/Unity.Notifications.Domain.Shared.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Domain.Shared/Unity.Notifications.Domain.Shared.csproj @@ -10,14 +10,14 @@ - - - - + + + + - + @@ -26,8 +26,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Domain/Unity.Notifications.Domain.csproj b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Domain/Unity.Notifications.Domain.csproj index a4cf76b437..926f8cad23 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Domain/Unity.Notifications.Domain.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Domain/Unity.Notifications.Domain.csproj @@ -9,16 +9,16 @@ - - - - + + + + - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.EntityFrameworkCore/Unity.Notifications.EntityFrameworkCore.csproj b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.EntityFrameworkCore/Unity.Notifications.EntityFrameworkCore.csproj index 35443ef745..02618456b9 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.EntityFrameworkCore/Unity.Notifications.EntityFrameworkCore.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.EntityFrameworkCore/Unity.Notifications.EntityFrameworkCore.csproj @@ -9,16 +9,16 @@ - - - - + + + + - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.HttpApi.Client/Unity.Notifications.HttpApi.Client.csproj b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.HttpApi.Client/Unity.Notifications.HttpApi.Client.csproj index 451ddaea79..c8c944ee7e 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.HttpApi.Client/Unity.Notifications.HttpApi.Client.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.HttpApi.Client/Unity.Notifications.HttpApi.Client.csproj @@ -9,9 +9,9 @@ - - - + + + @@ -21,8 +21,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.HttpApi/Unity.Notifications.HttpApi.csproj b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.HttpApi/Unity.Notifications.HttpApi.csproj index 4c17656712..7e309f547b 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.HttpApi/Unity.Notifications.HttpApi.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.HttpApi/Unity.Notifications.HttpApi.csproj @@ -9,15 +9,15 @@ - - - + + + - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Installer/Unity.Notifications.Installer.csproj b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Installer/Unity.Notifications.Installer.csproj index a6e97532ec..f9bdac728d 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Installer/Unity.Notifications.Installer.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Installer/Unity.Notifications.Installer.csproj @@ -10,8 +10,8 @@ - - + + @@ -23,8 +23,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Web/Unity.Notifications.Web.csproj b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Web/Unity.Notifications.Web.csproj index 3e110d2d7d..b5223b81ad 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Web/Unity.Notifications.Web.csproj +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Web/Unity.Notifications.Web.csproj @@ -12,15 +12,13 @@ - + - - - - - - - + + + + + @@ -29,7 +27,7 @@ - + @@ -56,8 +54,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Web/Views/Settings/NotificationsSettingGroup/Default.cshtml b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Web/Views/Settings/NotificationsSettingGroup/Default.cshtml index 1fc31ed1c2..015e4a3e13 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Web/Views/Settings/NotificationsSettingGroup/Default.cshtml +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Web/Views/Settings/NotificationsSettingGroup/Default.cshtml @@ -63,7 +63,7 @@