From 73e6e4d3b1d2bbe854f85c6f0db4f2ec58804ebe Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 25 Nov 2023 17:11:04 +0300 Subject: [PATCH] 20231125 --- anbs_cp/Classes/Serializer.cs | 25 +++++ anbs_cp/Classes/TypeConverter.cs | 6 +- anbs_cp/Extensions/BoolExtensions.cs | 16 +++ anbs_cp/Extensions/StringExtensions.cs | 14 +++ anbs_cp/Interfaces/ISerializable.cs | 19 ++++ anbs_cp/Structs/TwoDimSize.cs | 77 ++++++++++++- anbs_cp/anbs_cp.csproj | 3 +- anbs_cpfn/Classes/Sanitizer.cs | 122 +++++++++++++++++++++ anbs_cpfn/Enums/ESanitizerLevel.cs | 38 +++++++ anbs_cpfn/anbs_cpfn.csproj | 3 +- anbsoftware.componentspack.sln.DotSettings | 6 + demo/OsInfoFrm.cs | 17 ++- demo/SampleMapperTest.cs | 5 +- 13 files changed, 328 insertions(+), 23 deletions(-) create mode 100644 anbs_cp/Classes/Serializer.cs create mode 100644 anbs_cp/Extensions/BoolExtensions.cs create mode 100644 anbs_cp/Extensions/StringExtensions.cs create mode 100644 anbs_cp/Interfaces/ISerializable.cs create mode 100644 anbs_cpfn/Classes/Sanitizer.cs create mode 100644 anbs_cpfn/Enums/ESanitizerLevel.cs diff --git a/anbs_cp/Classes/Serializer.cs b/anbs_cp/Classes/Serializer.cs new file mode 100644 index 0000000..e8eafeb --- /dev/null +++ b/anbs_cp/Classes/Serializer.cs @@ -0,0 +1,25 @@ +using System.Text.Json; + +namespace anbs_cp.Classes; + +/// +/// Класс для сериализации моделей +/// +public static class Serializer +{ + /// + /// Сериализация данных в строку. + /// + /// Тип данных + /// Данные + /// Сериализованные данные + public static string Serialize(T data) => JsonSerializer.Serialize(data); + + /// + /// Десериализация данных из json-строки + /// + /// Ожидаемый тип данных + /// Сериализованные данные + /// Данные + public static T? Deserialize(string json) => JsonSerializer.Deserialize(json); +} \ No newline at end of file diff --git a/anbs_cp/Classes/TypeConverter.cs b/anbs_cp/Classes/TypeConverter.cs index 06c88d3..60e768b 100644 --- a/anbs_cp/Classes/TypeConverter.cs +++ b/anbs_cp/Classes/TypeConverter.cs @@ -1,7 +1,5 @@ using System.Globalization; -using Newtonsoft.Json; - namespace anbs_cp.Classes; /// @@ -73,7 +71,7 @@ public static class TypeConverter /// Тип /// Значение типа /// Значение в - public static string TypeToStr (T value) => JsonConvert.SerializeObject(value); + public static string TypeToStr (T value) => Serializer.Serialize(value); #endregion @@ -159,7 +157,7 @@ public static class TypeConverter /// Значение по умолчанию /// Значение в public static T StrToType(string value, T defaultValue) => - JsonConvert.DeserializeObject(value) ?? defaultValue; + Serializer.Deserialize(value) ?? defaultValue; #endregion } \ No newline at end of file diff --git a/anbs_cp/Extensions/BoolExtensions.cs b/anbs_cp/Extensions/BoolExtensions.cs new file mode 100644 index 0000000..edd8cd7 --- /dev/null +++ b/anbs_cp/Extensions/BoolExtensions.cs @@ -0,0 +1,16 @@ +namespace anbs_cp.Extensions; + +/// +/// Расширение типа "правда/ложь" +/// +public static class BooleanExtensions +{ + /// + /// Вывод в строку , если выражение правдиво и в противном случае. + /// + /// Выражение типа правда/ложь + /// Строка для правдивого выражения + /// Строка для лживого выражения + /// Вывод строки + public static string ExportToString (this bool b, string ifTrue, string ifFalse) => b ? ifTrue : ifFalse; +} \ No newline at end of file diff --git a/anbs_cp/Extensions/StringExtensions.cs b/anbs_cp/Extensions/StringExtensions.cs new file mode 100644 index 0000000..d58a98d --- /dev/null +++ b/anbs_cp/Extensions/StringExtensions.cs @@ -0,0 +1,14 @@ +namespace anbs_cp.Extensions; + +/// +/// Расширение строк +/// +public static class StringExtensions +{ + /// + /// Проверяет строку на пустоту + /// + /// Строка + /// Строка пусть (null) или содержит только пробелы + public static bool IsNullOrWhiteSpace(this string? s) => s == null || s.Trim().Length == 0; +} \ No newline at end of file diff --git a/anbs_cp/Interfaces/ISerializable.cs b/anbs_cp/Interfaces/ISerializable.cs new file mode 100644 index 0000000..0ef7d8a --- /dev/null +++ b/anbs_cp/Interfaces/ISerializable.cs @@ -0,0 +1,19 @@ +namespace anbs_cp.Interfaces; + +/// +/// Интерфейс для сериализации объектов +/// +public interface ISerializable +{ + /// + /// Сериализовать элемент в формат json + /// + /// Строка в формате json + string Serialize(); + + /// + /// Восстановить элемент из формата json + /// + /// Строка в формате json + void Deserialize(string json); +} \ No newline at end of file diff --git a/anbs_cp/Structs/TwoDimSize.cs b/anbs_cp/Structs/TwoDimSize.cs index 75476af..95b1415 100644 --- a/anbs_cp/Structs/TwoDimSize.cs +++ b/anbs_cp/Structs/TwoDimSize.cs @@ -1,11 +1,14 @@ -namespace anbs_cp.Structs; +using anbs_cp.Classes; +using anbs_cp.Interfaces; + +namespace anbs_cp.Structs; /// /// Двумерный размер /// /// Длина /// Высота -public struct TwoDimSize (int width = 0, int height = 0) +public struct TwoDimSize (int width = 0, int height = 0): ISerializable { #region Приватные поля /// @@ -14,7 +17,7 @@ public struct TwoDimSize (int width = 0, int height = 0) private int _pWidth = width; /// - /// Высота (приватное) + /// Ширина (приватное) /// private int _pHeight = height; #endregion @@ -30,7 +33,7 @@ public struct TwoDimSize (int width = 0, int height = 0) } /// - /// Высота + /// Ширина /// public int Height { @@ -38,4 +41,70 @@ public struct TwoDimSize (int width = 0, int height = 0) set => _pHeight = value < 0 ? 0 : value; } #endregion + + #region Методы + /// + /// Конвертация в строку + /// + /// Делитель размера + /// Строка + public readonly string ToString (char delimiter = ':') => $"{_pWidth}{delimiter}{_pHeight}"; + + /// + /// Получение размера из строки + /// + /// Строка + /// Разделитель размеров + /// Модель размеров + /// Если в строке не содержится символа + /// или таких разделителей слишком много + public static TwoDimSize Parse (string s, char delimiter = ':') + { + // Разделяю значения + string[] splitSizes = s.Split(delimiter); + + // Проверяю, что массив имеет ровно два элемента + if (splitSizes.Length != 2) + throw new ArgumentOutOfRangeException(delimiter.ToString(), + $"Похоже, что в строке {s} не содержится символа {delimiter} или таких разделителей слишком много!"); + + // Пытаюсь получить длину + if (!int.TryParse(splitSizes[0], out int width)) + width = 0; + + // Пытаюсь получить ширину + if (!int.TryParse(splitSizes[1], out int height)) + height = 0; + + // Вывожу значение + return new(width, height); + } + #endregion + + #region Реализация интерфейса ISerializable + /// + /// Сериализовать элемент в формат json + /// + /// Строка в формате json + public readonly string Serialize () => Serializer.Serialize(ToString()); + + /// + /// Восстановить элемент из формата json + /// + /// Строка в формате json + public void Deserialize (string json) + { + // Десериализую строку + string deserialized = Serializer.Deserialize(json) ?? "0:0"; + + // Перевожу строку в двумерный размер + TwoDimSize result = Parse(deserialized); + + // Присваиваю длину + _pWidth = result.Width; + + // Присваиваю ширину + _pHeight = result.Height; + } + #endregion } \ No newline at end of file diff --git a/anbs_cp/anbs_cp.csproj b/anbs_cp/anbs_cp.csproj index cbbba1c..54f25c9 100644 --- a/anbs_cp/anbs_cp.csproj +++ b/anbs_cp/anbs_cp.csproj @@ -2,7 +2,7 @@ net8.0 - 2023.1123.0 + 2023.1125.0 Александр Бабаев Набор компонентов ANB Software Библиотека полезных методов языка C# @@ -41,7 +41,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/anbs_cpfn/Classes/Sanitizer.cs b/anbs_cpfn/Classes/Sanitizer.cs new file mode 100644 index 0000000..e5357ad --- /dev/null +++ b/anbs_cpfn/Classes/Sanitizer.cs @@ -0,0 +1,122 @@ +using anbs_cp.ForNet.Enums; + +using Ganss.Xss; + +namespace anbs_cp.ForNet.Classes; + +/// +/// Очистка текста от лишних HTML-тегов +/// +public static class Sanitizer +{ + /// + /// Очистка текста по уровню очистки + /// + /// Текст + /// Уровень очистка + /// Очищенный текст + public static string SanitizeHtml (string html, ESanitizerLevel level) + { + HtmlSanitizer sanitizer = new() + { + KeepChildNodes = true + }; + + switch (level) + { + case ESanitizerLevel.NoTags: + PrepareForNone(ref sanitizer); + break; + case ESanitizerLevel.TextFormatOnly: + PrepareForTextFormatOnly(ref sanitizer); + break; + case ESanitizerLevel.ImageAndLinks: + PrepareForImageAndLinks(ref sanitizer); + break; + case ESanitizerLevel.AllExceptIFrame: + PrepareForAllExceptIFrame(ref sanitizer); + break; + default: + PrepareForNone(ref sanitizer); + break; + } + + return level != ESanitizerLevel.All ? sanitizer.Sanitize(html) : html; + } + + /// + /// Очистка всех тегов + /// + /// + private static void PrepareForNone (ref HtmlSanitizer sanitizer) + { + sanitizer.AllowedTags.Clear(); + sanitizer.AllowedSchemes.Clear(); + sanitizer.AllowedCssProperties.Clear(); + sanitizer.AllowedClasses.Clear(); + sanitizer.AllowedAttributes.Clear(); + sanitizer.AllowedAtRules.Clear(); + sanitizer.AllowDataAttributes = false; + } + + /// + /// Остаются только текстовые теги + /// + /// + private static void PrepareForTextFormatOnly (ref HtmlSanitizer sanitizer) + { + string[] allowedTags = + { + "strong", "b", "em", "i", "u", "hr", "strike", "div", "ol", "ul", "li", "p", "span", "h1", "h2", "h3", "h4" + }; + string[] allowedAttributes = + { + "align", "bgcolor", "border", "cellpadding", "cellspacing", "charset", "checked", "class", "clear", "color", "cols", "colspan", + "datetime", "disabled", "headers", "height", "high", "hspace", "label", "lang", "list", "low", "max", "maxlength", "min", "name", + "nowrap", "placeholder", "required", "rev", "rows", "rowspan", "rules", "selected", "size", "span", "spellcheck", "style", "summary", + "tabindex", "title", "type", "valign", "value", "vspace", "width", "wrap" + }; + + sanitizer.AllowedTags.Clear(); + + sanitizer.AllowedTags.UnionWith(allowedTags); + + sanitizer.AllowedAtRules.Clear(); + sanitizer.AllowDataAttributes = false; + + sanitizer.AllowedAttributes.Clear(); + sanitizer.AllowedAttributes.UnionWith(allowedAttributes); + + } + + /// + /// Остаются текстовые теги + изображения и ссылки + /// + /// + private static void PrepareForImageAndLinks (ref HtmlSanitizer sanitizer) + { + PrepareForTextFormatOnly(ref sanitizer); + string[] allowedTags = + { + "a", "img" + }; + + string[] allowedAttributes = + { + "alt", "href", "hreflang", "nohref", "rel", "src", "target" + }; + + sanitizer.AllowedTags.UnionWith(allowedTags); + + sanitizer.AllowedAttributes.UnionWith(allowedAttributes); + } + + /// + /// Остаются все теги, за исключением IFRAME + /// + /// + private static void PrepareForAllExceptIFrame (ref HtmlSanitizer sanitizer) + { + sanitizer.AllowedTags.Remove("iframe"); + } +} \ No newline at end of file diff --git a/anbs_cpfn/Enums/ESanitizerLevel.cs b/anbs_cpfn/Enums/ESanitizerLevel.cs new file mode 100644 index 0000000..8beeb1a --- /dev/null +++ b/anbs_cpfn/Enums/ESanitizerLevel.cs @@ -0,0 +1,38 @@ +namespace anbs_cp.ForNet.Enums; + +/// +/// Уровень очистки текста +/// атрибуты описаны на стр. https://github.com/mganss/HtmlSanitizer/wiki/Options +/// +public enum ESanitizerLevel +{ + /// + /// Все html-теги под запретом + /// + NoTags = 0, + + /// + /// Доступны только: + /// * теги формата шрифта (жирный, курсив, подчёркнутый, зачёркнутый) + /// * теги расположения текста (слева, по центру, справа) + /// + TextFormatOnly = 1, + + /// + /// Доступны только: + /// * все теги уровня lvlTextFormatOnly + /// * теги ссылки + /// * теги изображения + /// + ImageAndLinks = 2, + + /// + /// Доступны все теги, кроме вставки с другого сайта + /// + AllExceptIFrame = 3, + + /// + /// Доступны все теги + /// + All = 4 +} \ No newline at end of file diff --git a/anbs_cpfn/anbs_cpfn.csproj b/anbs_cpfn/anbs_cpfn.csproj index 9f0ac1e..e8223a3 100644 --- a/anbs_cpfn/anbs_cpfn.csproj +++ b/anbs_cpfn/anbs_cpfn.csproj @@ -6,7 +6,7 @@ enable True ANBSoftware.ComponentsPackForNet - 2023.11.15.0 + 2023.11.25.0 Александр Бабаев Набор компонентов ANB Software для ASP.NET Core Библиотека полезных методов языка C# для ASP.NET Core @@ -20,6 +20,7 @@ + diff --git a/anbsoftware.componentspack.sln.DotSettings b/anbsoftware.componentspack.sln.DotSettings index 94e58b5..04c25e0 100644 --- a/anbsoftware.componentspack.sln.DotSettings +++ b/anbsoftware.componentspack.sln.DotSettings @@ -12,16 +12,22 @@ True True True + True True + True + True True True True True True True + True True True + True True + True True True True diff --git a/demo/OsInfoFrm.cs b/demo/OsInfoFrm.cs index f92bea5..4c0e8b8 100644 --- a/demo/OsInfoFrm.cs +++ b/demo/OsInfoFrm.cs @@ -1,6 +1,5 @@ -using anbs_cp.OsInfo.Classes; - -using Newtonsoft.Json; +using anbs_cp.Classes; +using anbs_cp.OsInfo.Classes; namespace demo; public sealed partial class OsInfoFrm: Form @@ -13,16 +12,16 @@ public sealed partial class OsInfoFrm: Form private void OsInfoFrm_Load (object sender, EventArgs e) { textBox.Text = @"Процессор(ы) | "; - textBox.Text += JsonConvert.SerializeObject(OsInfo.Processors); + textBox.Text += Serializer.Serialize(OsInfo.Processors); textBox.Text += @"Оперативная память | "; - textBox.Text += JsonConvert.SerializeObject(OsInfo.RAM); + textBox.Text += Serializer.Serialize(OsInfo.RAM); textBox.Text += @"Видеокарта | "; - textBox.Text += JsonConvert.SerializeObject(OsInfo.Videos); + textBox.Text += Serializer.Serialize(OsInfo.Videos); textBox.Text += @"Диски | "; - textBox.Text += JsonConvert.SerializeObject(OsInfo.Drives); + textBox.Text += Serializer.Serialize(OsInfo.Drives); textBox.Text += @"Windows | "; - textBox.Text += JsonConvert.SerializeObject(OsInfo.Windows); + textBox.Text += Serializer.Serialize(OsInfo.Windows); textBox.Text += @"Net | "; - textBox.Text += JsonConvert.SerializeObject(OsInfo.Net); + textBox.Text += Serializer.Serialize(OsInfo.Net); } } diff --git a/demo/SampleMapperTest.cs b/demo/SampleMapperTest.cs index 0890aa8..2386164 100644 --- a/demo/SampleMapperTest.cs +++ b/demo/SampleMapperTest.cs @@ -1,5 +1,4 @@ using anbs_cp.Classes; -using Newtonsoft.Json; namespace demo; public sealed partial class SampleMapperTest: Form @@ -26,7 +25,7 @@ public sealed partial class SampleMapperTest: Form DemoDateTime = default }; - string serialize1 = JsonConvert.SerializeObject(demo2); + string serialize1 = Serializer.Serialize(demo2); SimpleMapper.MapMode mode = MapModeEdit.SelectedIndex switch { @@ -39,7 +38,7 @@ public sealed partial class SampleMapperTest: Form SimpleMapper.MapEx(demo1, ref demo2, mode, new List()); - string serialize2 = JsonConvert.SerializeObject(demo2); + string serialize2 = Serializer.Serialize(demo2); // ReSharper disable once LocalizableElement ResultArea.Text = $"Класс Demo2 до связывания:\r\n{serialize1}\r\nи после:\r\n{serialize2}";