using Blazored.LocalStorage; using collAnon.Client.Services; using decePubClient.Helpers; using decePubClient.Models; using decePubClient.Services; using Markdig; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using PrivaPub.ClientModels; using System.Collections.Specialized; using System.ComponentModel; using System.Globalization; using System.Net; using System.Net.Http.Json; using System.Text.Json; using System.Web; namespace decePubClient.Extensions { public static class ExtensionMethods { #region Navigation Manager public static NameValueCollection QueryString(this NavigationManager navigationManager) => HttpUtility.ParseQueryString(new Uri(navigationManager.Uri).Query); public static string QueryString(this NavigationManager navigationManager, string key) => navigationManager.QueryString()[key]; public static T QueryString(this NavigationManager navigationManager, string key) { var value = navigationManager.QueryString()[key]; if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value)) return default; return (T)Convert.ChangeType(value, typeof(T)); } #endregion Navigation Manager #region Setups /// The languages with resources (Resources/AllStrings.*.resx); any other browser language gets English. public static readonly string[] SupportedLanguages = ["en", "it"]; public static async Task SetDefaultCulture(this WebAssemblyHost host, CancellationToken cancellationToken = default) { var culture = ToCulture(default); try { var storage = host.Services.GetRequiredService(); var publicCache = await storage.GetItemAsync(nameof(PublicCacheData), cancellationToken); if (publicCache is null) { var browserLanguage = host.Services.GetRequiredService().Language(); publicCache = new(); publicCache.PageSettings.CurrentLanguageCode = SupportedLanguages.FirstOrDefault(language => browserLanguage?.StartsWith(language) ?? false) ?? "en"; await storage.SetItemAsync(nameof(PublicCacheData), publicCache, cancellationToken); } culture = ToCulture(publicCache.PageSettings.CurrentLanguageCode); } catch (Exception ex) { Console.WriteLine(ex); } CultureInfo.DefaultThreadCurrentCulture = culture; CultureInfo.DefaultThreadCurrentUICulture = culture; } static CultureInfo ToCulture(string languageCode) => languageCode is "it" ? new("it-IT") : new("en-GB"); #endregion Setups public static string GetLogLevelClass(this string logType) => logType switch { "Debug" => "text-base-content/60", "Information" => "text-info", "Warning" => "text-warning", "Error" => "text-error", "Fatal" => "text-error font-bold", _ => string.Empty }; #region Formats public static MarkdownPipeline Pipeline => new MarkdownPipelineBuilder().UseGenericAttributes().Build(); public static string ToJson(this object value) => JsonSerializer.Serialize(value); public static string ToBase64PngImage(this string base64Image) => $"data:image/png;base64,{base64Image}"; #endregion Formats #region Models public static string ToString(this WebResult webResult, CoalescingStringLocalizer localizer) => localizer["{0} - {1}", webResult.StatusCode, webResult.ErrorMessage]; /// /// Reads a failed response as a WebResult: a failure HttpService made up (FailureHeader) as is, 401/403 and 5xx /// as a generic message, validation ProblemDetails as their errors, anything else as the server's WebResult. /// A cancelled request (410) becomes SUtility.DefaultTaskCancelledResult. /// public static async Task ReadWebResult(this HttpContent content, HttpStatusCode statusCode, CoalescingStringLocalizer localizer, CancellationToken cancellationToken = default) { if (statusCode is HttpStatusCode.Gone) return SUtility.DefaultTaskCancelledResult; var result = new WebResult(); try { var json = await content.ReadAsStringAsync(cancellationToken); if (content.Headers.Contains(SUtility.FailureHeader)) return JsonSerializer.Deserialize(json, SUtility.DefaultSerializer); if (statusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden) return result.Invalidate(localizer["You are not authorized to do this."], (int)statusCode, FailureCodes.Unauthorized); if ((int)statusCode > 499) return result.Invalidate(localizer["The server reported an internal error (HTTP {0}).", (int)statusCode], (int)statusCode, FailureCodes.ServerError); if (json.Contains("\"title\"")) { var validationErrors = JsonSerializer.Deserialize(json, SUtility.DefaultSerializer); var lines = validationErrors.Errors.Select(error => $"{error.Key}: {string.Join(", ", error.Value)}").Prepend(validationErrors.Title); return result.Invalidate(string.Join('\n', lines), validationErrors.Status); } return JsonSerializer.Deserialize(json, SUtility.DefaultSerializer) ?? result.Invalidate(localizer["The server sent an invalid response."], (int)statusCode); } catch (OperationCanceledException) { return SUtility.DefaultTaskCancelledResult; } catch (Exception ex) { return result.Invalidate(localizer["The server sent an invalid response."], (int)HttpStatusCode.BadRequest, exception: ex); } } /// Reads a successful response body; a cancelled read returns SUtility.CancellationDefault. public static async Task DefaultReadFromJsonAsync(this HttpResponseMessage response, CancellationToken cancellationToken = default) { try { return await response.Content.ReadFromJsonAsync(SUtility.DefaultSerializer, cancellationToken); } catch (OperationCanceledException) { return SUtility.CancellationDefault(); } } public static string Timeframe(this DateTime creationDate, CoalescingStringLocalizer localizer) { var timeframe = DateTime.Now - creationDate.ToLocalTime(); switch ((int)timeframe.TotalHours) { case >= 24: return localizer["{0} day/s", (int)timeframe.TotalDays]; case >= 1 and < 24: return localizer["{0} hour/s", (int)timeframe.TotalHours]; case 0: return localizer["{0} minute/s", (int)timeframe.TotalMinutes]; default: return string.Empty; } } public static AuthorizationPolicy IsAdminPolicy() => new AuthorizationPolicyBuilder().RequireAuthenticatedUser() .RequireClaim(Policies.IsAdmin, "true") .RequireClaim(Policies.IsUser, "true") .RequireClaim(Policies.IsModerator, "true") .Build(); public static AuthorizationPolicy IsUserPolicy() => new AuthorizationPolicyBuilder().RequireAuthenticatedUser() .RequireClaim(Policies.IsUser, "true") .Build(); public static AuthorizationPolicy IsUserModeratorPolicy() => new AuthorizationPolicyBuilder().RequireAuthenticatedUser() .RequireClaim(Policies.IsModerator, "true") .Build(); #endregion } }