Merge branch 'feature/backend-refactor' into feature/files-refactor
# Conflicts: # products/ASC.Files/Core/GlobalUsings.cs # products/ASC.Files/Server/GlobalUsings.cs
This commit is contained in:
commit
d035deba31
160
.editorconfig
160
.editorconfig
@ -29,34 +29,30 @@ dotnet_style_qualification_for_method = false:silent
|
||||
dotnet_style_qualification_for_property = false:silent
|
||||
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
|
||||
dotnet_style_predefined_type_for_member_access = true:warning
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
||||
dotnet_style_predefined_type_for_member_access = true:silent
|
||||
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||
dotnet_style_parentheses_in_other_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
|
||||
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_coalesce_expression = true:warning
|
||||
dotnet_style_collection_initializer = true:warning
|
||||
dotnet_style_explicit_tuple_names = true:warning
|
||||
dotnet_style_null_propagation = true:warning
|
||||
dotnet_style_object_initializer = true:warning
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true:warning
|
||||
dotnet_style_prefer_compound_assignment = true:warning
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_auto_properties = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
|
||||
dotnet_style_prefer_inferred_tuple_names = true:warning
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true:warning
|
||||
dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||
|
||||
# Field preferences
|
||||
dotnet_style_readonly_field = true:warning
|
||||
@ -64,6 +60,13 @@ dotnet_style_readonly_field = true:warning
|
||||
# Parameter preferences
|
||||
dotnet_code_quality_unused_parameters = all:warning
|
||||
|
||||
# Suppression preferences
|
||||
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||
|
||||
# New line preferences
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = false
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = false
|
||||
|
||||
#### C# Coding Conventions ####
|
||||
|
||||
# var preferences
|
||||
@ -72,44 +75,48 @@ csharp_style_var_for_built_in_types = true:warning
|
||||
csharp_style_var_when_type_is_apparent = true:warning
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_accessors = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_methods = false:silent
|
||||
csharp_style_expression_bodied_constructors = false:silent
|
||||
csharp_style_expression_bodied_indexers = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_lambdas = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_local_functions = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_methods = false:suggestion
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:warning
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
|
||||
csharp_style_prefer_switch_expression = true:suggestion
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
|
||||
# Null-checking preferences
|
||||
csharp_style_conditional_delegate_call = true:warning
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
|
||||
# Modifier preferences
|
||||
csharp_prefer_static_local_function = true:suggestion
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
|
||||
|
||||
# Code-block preferences
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
csharp_prefer_braces = true:warning
|
||||
csharp_prefer_simple_using_statement = false:silent
|
||||
csharp_style_namespace_declarations = file_scoped:warning
|
||||
|
||||
# Expression-level preferences
|
||||
csharp_prefer_simple_default_expression = true:warning
|
||||
csharp_style_deconstructed_variable_declaration = true:warning
|
||||
csharp_style_inlined_variable_declaration = true:warning
|
||||
csharp_style_pattern_local_over_anonymous_function = true:warning
|
||||
csharp_style_prefer_index_operator = true:warning
|
||||
csharp_style_prefer_range_operator = true:warning
|
||||
csharp_style_throw_expression = true:warning
|
||||
csharp_prefer_simple_default_expression = false:silent
|
||||
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = false:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_prefer_index_operator = false:suggestion
|
||||
csharp_style_prefer_null_check_over_type_check = true:suggestion
|
||||
csharp_style_prefer_range_operator = false:suggestion
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion
|
||||
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
csharp_using_directive_placement = outside_namespace:warning
|
||||
|
||||
# New line preferences
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false:silent
|
||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:silent
|
||||
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
|
||||
|
||||
#### C# Formatting Rules ####
|
||||
|
||||
@ -126,7 +133,7 @@ csharp_new_line_between_query_expression_clauses = true
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = false
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
@ -162,32 +169,32 @@ csharp_preserve_single_line_statements = true
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_begin_with_underscore.severity = warning
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_begin_with_underscore.symbols = private_or_internal_field
|
||||
dotnet_naming_rule.private_or_internal_field_should_be_begin_with_underscore.style = begin_with_underscore
|
||||
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
|
||||
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
@ -200,5 +207,58 @@ dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||
|
||||
# Default severity for analyzer diagnostics with category 'Style'
|
||||
dotnet_analyzer_diagnostic.category-Style.severity = silent
|
||||
dotnet_naming_style.begin_with_underscore.required_prefix = _
|
||||
dotnet_naming_style.begin_with_underscore.required_suffix =
|
||||
dotnet_naming_style.begin_with_underscore.word_separator =
|
||||
dotnet_naming_style.begin_with_underscore.capitalization = camel_case
|
||||
csharp_style_expression_bodied_lambdas = true:silent
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
csharp_style_prefer_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_prefer_tuple_swap = true:suggestion
|
||||
csharp_prefer_static_local_function = true:suggestion
|
||||
csharp_style_prefer_switch_expression = true:suggestion
|
||||
csharp_style_prefer_pattern_matching = true:silent
|
||||
csharp_style_prefer_not_pattern = true:suggestion
|
||||
csharp_style_prefer_extended_property_pattern = true:suggestion
|
||||
dotnet_diagnostic.CA1001.severity = warning
|
||||
dotnet_diagnostic.CA1805.severity = warning
|
||||
dotnet_diagnostic.CA1841.severity = warning
|
||||
|
||||
[*.{cs,vb}]
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
tab_width = 4
|
||||
indent_size = 4
|
||||
end_of_line = crlf
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
||||
dotnet_style_prefer_auto_properties = true:silent
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_compound_assignment = true:suggestion
|
||||
dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||
dotnet_style_namespace_match_folder = true:suggestion
|
||||
dotnet_style_readonly_field = true:warning
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
||||
dotnet_style_predefined_type_for_member_access = true:silent
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = false:silent
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = false:silent
|
||||
dotnet_code_quality_unused_parameters = all:warning
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_operators = always_for_clarity:silent
|
||||
dotnet_style_qualification_for_field = false:silent
|
||||
dotnet_style_qualification_for_property = false:silent
|
||||
dotnet_style_qualification_for_method = false:silent
|
||||
dotnet_style_qualification_for_event = false:silent
|
||||
dotnet_diagnostic.CA1715.severity = warning
|
||||
dotnet_diagnostic.CA1716.severity = silent
|
||||
dotnet_diagnostic.CA5397.severity = warning
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -1,11 +1,9 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.ComponentModel;
|
||||
global using System.Globalization;
|
||||
global using System.Linq;
|
||||
global using System.ComponentModel;
|
||||
global using System.Globalization;
|
||||
|
||||
global using System.Linq.Expressions;
|
||||
global using System.Net;
|
||||
global using System.Net.Http;
|
||||
|
||||
global using System.Reflection;
|
||||
global using System.Runtime.Serialization;
|
||||
global using System.Security;
|
||||
@ -14,8 +12,7 @@ global using System.Security.Claims;
|
||||
global using System.Text.Encodings.Web;
|
||||
global using System.Text.Json;
|
||||
global using System.Text.Json.Serialization;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
|
||||
global using System.Web;
|
||||
global using System.Xml.Linq;
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<WarningsAsErrors></WarningsAsErrors>
|
||||
<NoWarn>$(NoWarn);NU1605</NoWarn>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
|
@ -1,32 +1,26 @@
|
||||
global using System;
|
||||
global using System.Collections;
|
||||
global using System.Collections;
|
||||
global using System.Collections.Concurrent;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Configuration;
|
||||
global using System.Diagnostics;
|
||||
global using System.Globalization;
|
||||
global using System.IO;
|
||||
global using System.Linq;
|
||||
global using System.Net;
|
||||
global using System.Net.Mail;
|
||||
global using System.Reflection;
|
||||
global using System.Runtime.Caching;
|
||||
global using System.Runtime.Loader;
|
||||
global using System.Runtime.Serialization;
|
||||
global using System.Security.Cryptography;
|
||||
global using System.Security.Principal;
|
||||
global using System.ServiceModel;
|
||||
global using System.Text;
|
||||
global using System.Text.RegularExpressions;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using System.Web;
|
||||
global using System.Xml.Linq;
|
||||
global using System.Xml.XPath;
|
||||
global using System.ServiceModel;
|
||||
global using System.Runtime.Serialization;
|
||||
|
||||
|
||||
global using ARSoft.Tools.Net;
|
||||
global using ARSoft.Tools.Net.Dns;
|
||||
|
||||
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Caching;
|
||||
global using ASC.Common.DependencyInjection;
|
||||
@ -35,26 +29,26 @@ global using ASC.Common.Security;
|
||||
global using ASC.Common.Security.Authorizing;
|
||||
global using ASC.Common.Utils;
|
||||
global using ASC.Security.Cryptography;
|
||||
|
||||
|
||||
global using Autofac;
|
||||
global using Autofac.Configuration;
|
||||
|
||||
|
||||
global using AutoMapper;
|
||||
|
||||
|
||||
global using Confluent.Kafka;
|
||||
global using Confluent.Kafka.Admin;
|
||||
|
||||
|
||||
global using Google.Protobuf;
|
||||
|
||||
|
||||
global using JWT;
|
||||
global using JWT.Algorithms;
|
||||
global using JWT.Serializers;
|
||||
|
||||
|
||||
global using log4net.Appender;
|
||||
global using log4net.Config;
|
||||
global using log4net.Core;
|
||||
global using log4net.Util;
|
||||
|
||||
|
||||
global using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
||||
global using Microsoft.AspNetCore.Http;
|
||||
global using Microsoft.AspNetCore.Http.Extensions;
|
||||
@ -70,16 +64,16 @@ global using Microsoft.Extensions.Hosting;
|
||||
global using Microsoft.Extensions.Logging;
|
||||
global using Microsoft.Extensions.Options;
|
||||
global using Microsoft.Net.Http.Headers;
|
||||
|
||||
|
||||
global using Newtonsoft.Json;
|
||||
global using Newtonsoft.Json.Serialization;
|
||||
|
||||
|
||||
global using NLog;
|
||||
global using NLog.Common;
|
||||
global using NLog.Targets;
|
||||
|
||||
|
||||
global using NVelocity;
|
||||
global using NVelocity.App;
|
||||
global using NVelocity.Runtime.Resource.Loader;
|
||||
|
||||
|
||||
global using StackExchange.Redis.Extensions.Core.Abstractions;
|
||||
|
@ -11,6 +11,7 @@
|
||||
<Copyright>(c) Ascensio System SIA. All rights reserved</Copyright>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugType>full</DebugType>
|
||||
|
@ -10,9 +10,9 @@ public class DbVoipCall
|
||||
public Guid AnsweredBy { get; set; }
|
||||
public DateTime DialDate { get; set; }
|
||||
public int DialDuration { get; set; }
|
||||
public string RecordSid { get; set; }
|
||||
public string RecordUrl { get; set; }
|
||||
public int RecordDuration { get; set; }
|
||||
public string Sid { get; set; }
|
||||
public string Uri { get; set; }
|
||||
public int Duration { get; set; }
|
||||
public decimal RecordPrice { get; set; }
|
||||
public int ContactId { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
@ -30,6 +30,7 @@ public static class DbVoipCallExtension
|
||||
|
||||
return modelBuilder;
|
||||
}
|
||||
|
||||
public static void MySqlAddDbVoipCall(this ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<DbVoipCall>(entity =>
|
||||
@ -89,19 +90,19 @@ public static class DbVoipCallExtension
|
||||
.HasColumnName("price")
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
entity.Property(e => e.RecordDuration).HasColumnName("record_duration");
|
||||
entity.Property(e => e.Duration).HasColumnName("record_duration");
|
||||
|
||||
entity.Property(e => e.RecordPrice)
|
||||
.HasColumnName("record_price")
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
entity.Property(e => e.RecordSid)
|
||||
entity.Property(e => e.Sid)
|
||||
.HasColumnName("record_sid")
|
||||
.HasColumnType("varchar(50)")
|
||||
.HasCharSet("utf8")
|
||||
.UseCollation("utf8_general_ci");
|
||||
|
||||
entity.Property(e => e.RecordUrl)
|
||||
entity.Property(e => e.Uri)
|
||||
.HasColumnName("record_url")
|
||||
.HasColumnType("text")
|
||||
.HasCharSet("utf8")
|
||||
@ -160,18 +161,18 @@ public static class DbVoipCallExtension
|
||||
.HasColumnType("numeric(10,4)")
|
||||
.HasDefaultValueSql("NULL");
|
||||
|
||||
entity.Property(e => e.RecordDuration).HasColumnName("record_duration");
|
||||
entity.Property(e => e.Duration).HasColumnName("record_duration");
|
||||
|
||||
entity.Property(e => e.RecordPrice)
|
||||
.HasColumnName("record_price")
|
||||
.HasColumnType("numeric(10,4)");
|
||||
|
||||
entity.Property(e => e.RecordSid)
|
||||
entity.Property(e => e.Sid)
|
||||
.HasColumnName("record_sid")
|
||||
.HasMaxLength(50)
|
||||
.HasDefaultValueSql("NULL");
|
||||
|
||||
entity.Property(e => e.RecordUrl).HasColumnName("record_url");
|
||||
entity.Property(e => e.Uri).HasColumnName("record_url");
|
||||
|
||||
entity.Property(e => e.Status).HasColumnName("status");
|
||||
|
||||
|
@ -1,16 +1,11 @@
|
||||
global using System;
|
||||
global using System.Collections;
|
||||
global using System.Collections;
|
||||
global using System.Collections.Concurrent;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Configuration;
|
||||
global using System.Data.Common;
|
||||
global using System.Diagnostics;
|
||||
global using System.Globalization;
|
||||
global using System.IO;
|
||||
global using System.Linq;
|
||||
global using System.Linq.Expressions;
|
||||
global using System.Net;
|
||||
global using System.Net.Http;
|
||||
global using System.Reflection;
|
||||
global using System.Resources;
|
||||
global using System.Runtime.Caching;
|
||||
@ -25,8 +20,6 @@ global using System.Text;
|
||||
global using System.Text.Json;
|
||||
global using System.Text.Json.Serialization;
|
||||
global using System.Text.RegularExpressions;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using System.Web;
|
||||
global using System.Xml;
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,20 +1,14 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.ComponentModel.DataAnnotations;
|
||||
global using System.ComponentModel.DataAnnotations;
|
||||
global using System.ComponentModel.DataAnnotations.Schema;
|
||||
global using System.Configuration;
|
||||
global using System.Data;
|
||||
global using System.Data.Common;
|
||||
global using System.Diagnostics;
|
||||
global using System.IO;
|
||||
global using System.Linq;
|
||||
global using System.Reflection;
|
||||
global using System.Security.Cryptography;
|
||||
global using System.ServiceModel;
|
||||
global using System.Text;
|
||||
global using System.Text.RegularExpressions;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using System.Xml;
|
||||
global using System.Xml.Linq;
|
||||
global using System.Xml.XPath;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -1,6 +1,4 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Globalization;
|
||||
global using System.Globalization;
|
||||
global using System.Security.Cryptography;
|
||||
global using System.Text;
|
||||
|
||||
@ -19,5 +17,4 @@ global using ASC.Web.Studio.Core.Notify;
|
||||
global using Microsoft.AspNetCore.Http;
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
global using Microsoft.Extensions.Options;
|
||||
global using Microsoft.Extensions.Primitives;
|
||||
|
||||
global using Microsoft.Extensions.Primitives;
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -1,19 +1,12 @@
|
||||
global using System;
|
||||
global using System.Collections.Concurrent;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Collections.Concurrent;
|
||||
global using System.Globalization;
|
||||
global using System.IO;
|
||||
global using System.Linq;
|
||||
global using System.Net;
|
||||
global using System.Net.Http;
|
||||
global using System.Runtime.Serialization;
|
||||
global using System.Security.Cryptography;
|
||||
global using System.ServiceModel;
|
||||
global using System.Text;
|
||||
global using System.Text.Json;
|
||||
global using System.Text.Json.Serialization;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using System.Web;
|
||||
|
||||
global using Amazon;
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -1,12 +1,7 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Data;
|
||||
global using System.Data;
|
||||
global using System.Diagnostics;
|
||||
global using System.Globalization;
|
||||
global using System.IO;
|
||||
global using System.Linq;
|
||||
global using System.Net;
|
||||
global using System.Net.Http;
|
||||
global using System.Net.Http.Headers;
|
||||
global using System.Reflection;
|
||||
global using System.Runtime.Serialization;
|
||||
@ -14,8 +9,6 @@ global using System.Security.Cryptography.Pkcs;
|
||||
global using System.Security.Cryptography.X509Certificates;
|
||||
global using System.Text;
|
||||
global using System.Text.Json.Serialization;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using System.Web;
|
||||
global using System.Xml.Linq;
|
||||
global using System.Xml.XPath;
|
||||
|
@ -5,6 +5,7 @@
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -1,26 +1,23 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Data;
|
||||
global using System.Linq;
|
||||
|
||||
global using System.Data;
|
||||
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Mapping;
|
||||
global using ASC.Core;
|
||||
global using ASC.Core.Common.EF;
|
||||
global using ASC.Feed.Models;
|
||||
global using ASC.Core.Common.EF.Model;
|
||||
global using ASC.Core.Tenants;
|
||||
global using ASC.Core.Users;
|
||||
global using ASC.Common.Mapping;
|
||||
global using ASC.Feed.Mapping;
|
||||
global using ASC.Feed.Data;
|
||||
global using ASC.Feed.Core;
|
||||
|
||||
global using AutoMapper;
|
||||
|
||||
global using ASC.Feed.Data;
|
||||
global using ASC.Feed.Mapping;
|
||||
global using ASC.Feed.Models;
|
||||
|
||||
global using Autofac;
|
||||
|
||||
|
||||
global using AutoMapper;
|
||||
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
global using Microsoft.EntityFrameworkCore.Migrations;
|
||||
global using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
global using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
global using Newtonsoft.Json;
|
||||
|
@ -5,6 +5,7 @@
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -1,11 +1,8 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Linq;
|
||||
global using System.Net;
|
||||
global using System.Net;
|
||||
global using System.Net.Sockets;
|
||||
global using System.Runtime.Serialization;
|
||||
global using System.Web;
|
||||
|
||||
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Caching;
|
||||
global using ASC.Common.Logging;
|
||||
@ -15,11 +12,11 @@ global using ASC.Core.Common.EF;
|
||||
global using ASC.Core.Common.EF.Context;
|
||||
global using ASC.Core.Common.EF.Model;
|
||||
global using ASC.Core.Common.Settings;
|
||||
|
||||
|
||||
global using AutoMapper;
|
||||
global using AutoMapper.QueryableExtensions;
|
||||
|
||||
global using Microsoft.AspNetCore.Http;
|
||||
global using Microsoft.AspNetCore.Http.Extensions;
|
||||
global using Microsoft.Extensions.Configuration;
|
||||
global using Microsoft.Extensions.Options;
|
||||
|
||||
global using AutoMapper;
|
||||
global using AutoMapper.QueryableExtensions;
|
@ -5,6 +5,7 @@
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -1,33 +1,29 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Linq;
|
||||
global using System.Threading;
|
||||
global using System.Web;
|
||||
|
||||
global using System.Web;
|
||||
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Logging;
|
||||
global using ASC.Common.Mapping;
|
||||
global using ASC.Core;
|
||||
global using ASC.Core.Common.EF;
|
||||
global using ASC.Core.Common.EF.Model;
|
||||
global using ASC.MessagingSystem.Mapping;
|
||||
global using ASC.MessagingSystem.Core;
|
||||
global using ASC.MessagingSystem.Core.Sender;
|
||||
global using ASC.MessagingSystem.Data;
|
||||
global using ASC.MessagingSystem.Mapping;
|
||||
global using ASC.MessagingSystem.Models;
|
||||
|
||||
|
||||
global using AutoMapper;
|
||||
|
||||
global using Microsoft.AspNetCore.Http;
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
global using Microsoft.EntityFrameworkCore.Migrations;
|
||||
global using Microsoft.EntityFrameworkCore.Metadata;
|
||||
global using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
global using Microsoft.EntityFrameworkCore.Metadata;
|
||||
global using Microsoft.EntityFrameworkCore.Migrations;
|
||||
global using Microsoft.Extensions.Configuration;
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
global using Microsoft.Extensions.Options;
|
||||
global using Microsoft.Extensions.Primitives;
|
||||
|
||||
global using AutoMapper;
|
||||
|
||||
|
||||
global using Newtonsoft.Json;
|
||||
|
||||
|
||||
global using UAParser;
|
||||
|
@ -6,6 +6,7 @@
|
||||
<Product>ASC.Notify.Textile</Product>
|
||||
<Copyright>(c) Ascensio System SIA. All rights reserved</Copyright>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugType>full</DebugType>
|
||||
|
@ -1,7 +1,4 @@
|
||||
global using System;
|
||||
global using System.IO;
|
||||
global using System.Linq;
|
||||
global using System.Reflection;
|
||||
global using System.Reflection;
|
||||
global using System.Text;
|
||||
global using System.Text.RegularExpressions;
|
||||
global using System.Web;
|
||||
|
@ -12,6 +12,7 @@
|
||||
<Copyright>(c) Ascensio System SIA. All rights reserved</Copyright>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugType>full</DebugType>
|
||||
|
@ -1,19 +1,18 @@
|
||||
namespace Textile
|
||||
namespace Textile;
|
||||
|
||||
public class BlockModifier
|
||||
{
|
||||
public class BlockModifier
|
||||
protected BlockModifier()
|
||||
{
|
||||
protected BlockModifier()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string ModifyLine(string line)
|
||||
{
|
||||
return line;
|
||||
}
|
||||
public virtual string ModifyLine(string line)
|
||||
{
|
||||
return line;
|
||||
}
|
||||
|
||||
public virtual string Conclude(string line)
|
||||
{
|
||||
return line;
|
||||
}
|
||||
public virtual string Conclude(string line)
|
||||
{
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
namespace Textile
|
||||
namespace Textile;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class BlockModifierAttribute : Attribute
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class BlockModifierAttribute : Attribute
|
||||
public BlockModifierAttribute()
|
||||
{
|
||||
public BlockModifierAttribute()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,142 +10,141 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile.Blocks
|
||||
{
|
||||
public static class BlockAttributesParser
|
||||
{
|
||||
public static StyleReader Styler { get; set; }
|
||||
namespace Textile.Blocks;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
static public string ParseBlockAttributes(string input)
|
||||
public static class BlockAttributesParser
|
||||
{
|
||||
public static StyleReader Styler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
static public string ParseBlockAttributes(string input)
|
||||
{
|
||||
return ParseBlockAttributes(input, "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="element"></param>
|
||||
/// <returns></returns>
|
||||
static public string ParseBlockAttributes(string input, string element)
|
||||
{
|
||||
var style = string.Empty;
|
||||
var cssClass = string.Empty;
|
||||
var lang = string.Empty;
|
||||
var colspan = string.Empty;
|
||||
var rowspan = string.Empty;
|
||||
var id = string.Empty;
|
||||
|
||||
if (Styler != null)
|
||||
{
|
||||
return ParseBlockAttributes(input, "");
|
||||
style = GetStyle(element, style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="element"></param>
|
||||
/// <returns></returns>
|
||||
static public string ParseBlockAttributes(string input, string element)
|
||||
if (input.Length == 0)
|
||||
return style.Length > 0 ? " style=\"" + style + "\"" : "";
|
||||
|
||||
|
||||
Match m;
|
||||
var matched = input;
|
||||
if (element == "td")
|
||||
{
|
||||
var style = string.Empty;
|
||||
var cssClass = string.Empty;
|
||||
var lang = string.Empty;
|
||||
var colspan = string.Empty;
|
||||
var rowspan = string.Empty;
|
||||
var id = string.Empty;
|
||||
|
||||
if (Styler != null)
|
||||
{
|
||||
style = GetStyle(element, style);
|
||||
}
|
||||
|
||||
if (input.Length == 0)
|
||||
return style.Length > 0 ? " style=\"" + style + "\"" : "";
|
||||
|
||||
|
||||
Match m;
|
||||
var matched = input;
|
||||
if (element == "td")
|
||||
{
|
||||
// column span
|
||||
m = Regex.Match(matched, @"\\(\d+)");
|
||||
if (m.Success)
|
||||
colspan = m.Groups[1].Value;
|
||||
// row span
|
||||
m = Regex.Match(matched, @"/(\d+)");
|
||||
if (m.Success)
|
||||
rowspan = m.Groups[1].Value;
|
||||
// vertical align
|
||||
m = Regex.Match(matched, @"(" + Globals.VerticalAlignPattern + @")");
|
||||
if (m.Success)
|
||||
style += "vertical-align:" + Globals.VerticalAlign[m.Captures[0].Value] + ";";
|
||||
}
|
||||
|
||||
// First, match custom styles
|
||||
m = Regex.Match(matched, @"\{([^}]*)\}");
|
||||
// column span
|
||||
m = Regex.Match(matched, @"\\(\d+)");
|
||||
if (m.Success)
|
||||
{
|
||||
style += m.Groups[1].Value + ";";
|
||||
matched = matched.Replace(m.ToString(), "");
|
||||
}
|
||||
|
||||
// Then match the language
|
||||
m = Regex.Match(matched, @"\[([^()]+)\]");
|
||||
colspan = m.Groups[1].Value;
|
||||
// row span
|
||||
m = Regex.Match(matched, @"/(\d+)");
|
||||
if (m.Success)
|
||||
{
|
||||
lang = m.Groups[1].Value;
|
||||
matched = matched.Replace(m.ToString(), "");
|
||||
}
|
||||
rowspan = m.Groups[1].Value;
|
||||
// vertical align
|
||||
m = Regex.Match(matched, @"(" + Globals.VerticalAlignPattern + @")");
|
||||
if (m.Success)
|
||||
style += "vertical-align:" + Globals.VerticalAlign[m.Captures[0].Value] + ";";
|
||||
}
|
||||
|
||||
// Match classes and IDs after that
|
||||
m = Regex.Match(matched, @"\(([^()]+)\)");
|
||||
// First, match custom styles
|
||||
m = Regex.Match(matched, @"\{([^}]*)\}");
|
||||
if (m.Success)
|
||||
{
|
||||
style += m.Groups[1].Value + ";";
|
||||
matched = matched.Replace(m.ToString(), "");
|
||||
}
|
||||
|
||||
// Then match the language
|
||||
m = Regex.Match(matched, @"\[([^()]+)\]");
|
||||
if (m.Success)
|
||||
{
|
||||
lang = m.Groups[1].Value;
|
||||
matched = matched.Replace(m.ToString(), "");
|
||||
}
|
||||
|
||||
// Match classes and IDs after that
|
||||
m = Regex.Match(matched, @"\(([^()]+)\)");
|
||||
if (m.Success)
|
||||
{
|
||||
cssClass = m.Groups[1].Value;
|
||||
matched = matched.Replace(m.ToString(), "");
|
||||
|
||||
// Separate the public class and the ID
|
||||
m = Regex.Match(cssClass, @"^(.*)#(.*)$");
|
||||
if (m.Success)
|
||||
{
|
||||
cssClass = m.Groups[1].Value;
|
||||
matched = matched.Replace(m.ToString(), "");
|
||||
|
||||
// Separate the public class and the ID
|
||||
m = Regex.Match(cssClass, @"^(.*)#(.*)$");
|
||||
if (m.Success)
|
||||
{
|
||||
cssClass = m.Groups[1].Value;
|
||||
id = m.Groups[2].Value;
|
||||
}
|
||||
if (Styler != null && !string.IsNullOrEmpty(cssClass))
|
||||
{
|
||||
style = GetStyle("." + cssClass, style);
|
||||
}
|
||||
|
||||
id = m.Groups[2].Value;
|
||||
}
|
||||
|
||||
// Get the padding on the left
|
||||
m = Regex.Match(matched, @"([(]+)");
|
||||
if (m.Success)
|
||||
if (Styler != null && !string.IsNullOrEmpty(cssClass))
|
||||
{
|
||||
style += "padding-left:" + m.Groups[1].Length + "em;";
|
||||
matched = matched.Replace(m.ToString(), "");
|
||||
style = GetStyle("." + cssClass, style);
|
||||
}
|
||||
|
||||
// Get the padding on the right
|
||||
m = Regex.Match(matched, @"([)]+)");
|
||||
if (m.Success)
|
||||
{
|
||||
style += "padding-right:" + m.Groups[1].Length + "em;";
|
||||
matched = matched.Replace(m.ToString(), "");
|
||||
}
|
||||
|
||||
// Get the text alignment
|
||||
m = Regex.Match(matched, "(" + Globals.HorizontalAlignPattern + ")");
|
||||
if (m.Success)
|
||||
style += "text-align:" + Globals.HorizontalAlign[m.Groups[1].Value] + ";";
|
||||
|
||||
|
||||
|
||||
return
|
||||
(style.Length > 0 ? " style=\"" + style + "\"" : "") +
|
||||
(cssClass.Length > 0 ? " class=\"" + cssClass + "\"" : "") +
|
||||
(lang.Length > 0 ? " lang=\"" + lang + "\"" : "") +
|
||||
(id.Length > 0 ? " id=\"" + id + "\"" : "") +
|
||||
(colspan.Length > 0 ? " colspan=\"" + colspan + "\"" : "") +
|
||||
(rowspan.Length > 0 ? " rowspan=\"" + rowspan + "\"" : "")
|
||||
;
|
||||
}
|
||||
|
||||
private static string GetStyle(string element, string style)
|
||||
// Get the padding on the left
|
||||
m = Regex.Match(matched, @"([(]+)");
|
||||
if (m.Success)
|
||||
{
|
||||
var styled = Styler.GetStyle(element);
|
||||
if (!string.IsNullOrEmpty(styled))
|
||||
{
|
||||
style += styled;
|
||||
}
|
||||
return style;
|
||||
style += "padding-left:" + m.Groups[1].Length + "em;";
|
||||
matched = matched.Replace(m.ToString(), "");
|
||||
}
|
||||
|
||||
// Get the padding on the right
|
||||
m = Regex.Match(matched, @"([)]+)");
|
||||
if (m.Success)
|
||||
{
|
||||
style += "padding-right:" + m.Groups[1].Length + "em;";
|
||||
matched = matched.Replace(m.ToString(), "");
|
||||
}
|
||||
|
||||
// Get the text alignment
|
||||
m = Regex.Match(matched, "(" + Globals.HorizontalAlignPattern + ")");
|
||||
if (m.Success)
|
||||
style += "text-align:" + Globals.HorizontalAlign[m.Groups[1].Value] + ";";
|
||||
|
||||
|
||||
|
||||
return
|
||||
(style.Length > 0 ? " style=\"" + style + "\"" : "") +
|
||||
(cssClass.Length > 0 ? " class=\"" + cssClass + "\"" : "") +
|
||||
(lang.Length > 0 ? " lang=\"" + lang + "\"" : "") +
|
||||
(id.Length > 0 ? " id=\"" + id + "\"" : "") +
|
||||
(colspan.Length > 0 ? " colspan=\"" + colspan + "\"" : "") +
|
||||
(rowspan.Length > 0 ? " rowspan=\"" + rowspan + "\"" : "")
|
||||
;
|
||||
}
|
||||
|
||||
private static string GetStyle(string element, string style)
|
||||
{
|
||||
var styled = Styler.GetStyle(element);
|
||||
if (!string.IsNullOrEmpty(styled))
|
||||
{
|
||||
style += styled;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class BoldPhraseBlockModifier : PhraseBlockModifier
|
||||
{
|
||||
public class BoldPhraseBlockModifier : PhraseBlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
return PhraseModifierFormat(line, @"\*\*", "b");
|
||||
}
|
||||
return PhraseModifierFormat(line, @"\*\*", "b");
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,16 @@
|
||||
namespace Textile.Blocks
|
||||
{
|
||||
public class CapitalsBlockModifier : BlockModifier
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
var me = new MatchEvaluator(CapitalsFormatMatchEvaluator);
|
||||
line = Regex.Replace(line, @"(?<=^|\s|" + Globals.PunctuationPattern + @")(?<caps>[A-Z][A-Z0-9]+)(?=$|\s|" + Globals.PunctuationPattern + @")", me);
|
||||
return line;
|
||||
}
|
||||
namespace Textile.Blocks;
|
||||
|
||||
private string CapitalsFormatMatchEvaluator(Match m)
|
||||
{
|
||||
return @"<span class=""caps"">" + m.Groups["caps"].Value + @"</span>";
|
||||
}
|
||||
public class CapitalsBlockModifier : BlockModifier
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
var me = new MatchEvaluator(CapitalsFormatMatchEvaluator);
|
||||
line = Regex.Replace(line, @"(?<=^|\s|" + Globals.PunctuationPattern + @")(?<caps>[A-Z][A-Z0-9]+)(?=$|\s|" + Globals.PunctuationPattern + @")", me);
|
||||
return line;
|
||||
}
|
||||
|
||||
private string CapitalsFormatMatchEvaluator(Match m)
|
||||
{
|
||||
return @"<span class=""caps"">" + m.Groups["caps"].Value + @"</span>";
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class CitePhraseBlockModifier : PhraseBlockModifier
|
||||
{
|
||||
public class CitePhraseBlockModifier : PhraseBlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
return PhraseModifierFormat(line, @"\?\?", "cite");
|
||||
}
|
||||
return PhraseModifierFormat(line, @"\?\?", "cite");
|
||||
}
|
||||
}
|
||||
|
@ -10,47 +10,46 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class CodeBlockModifier : BlockModifier
|
||||
{
|
||||
public class CodeBlockModifier : BlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
// Replace "@...@" zones with "<code>" tags.
|
||||
var me = new MatchEvaluator(CodeFormatMatchEvaluator);
|
||||
line = Regex.Replace(line,
|
||||
@"(?<before>^|([\s\([{]))" + // before
|
||||
"@" +
|
||||
@"(\|(?<lang>\w+)\|)?" + // lang
|
||||
"(?<code>[^@]+)" + // code
|
||||
"@" +
|
||||
@"(?<after>$|([\]}])|(?=" + Globals.PunctuationPattern + @"{1,2}|\s|$))", // after
|
||||
me);
|
||||
// Encode the contents of the "<code>" tags so that we don't
|
||||
// generate formatting out of it.
|
||||
line = NoTextileEncoder.EncodeNoTextileZones(line,
|
||||
@"(?<=(^|\s)<code(" + Globals.HtmlAttributesPattern + @")>)",
|
||||
@"(?=</code>)");
|
||||
return line;
|
||||
}
|
||||
|
||||
public override string Conclude(string line)
|
||||
{
|
||||
// Recode everything except "<" and ">";
|
||||
line = NoTextileEncoder.DecodeNoTextileZones(line,
|
||||
@"(?<=(^|\s)<code(" + Globals.HtmlAttributesPattern + @")>)",
|
||||
@"(?=</code>)",
|
||||
new string[] { "<", ">" });
|
||||
return line;
|
||||
}
|
||||
|
||||
public string CodeFormatMatchEvaluator(Match m)
|
||||
{
|
||||
var res = m.Groups["before"].Value + "<code";
|
||||
if (m.Groups["lang"].Length > 0)
|
||||
res += " language=\"" + m.Groups["lang"].Value + "\"";
|
||||
res += ">" + m.Groups["code"].Value + "</code>" + m.Groups["after"].Value;
|
||||
return res;
|
||||
}
|
||||
// Replace "@...@" zones with "<code>" tags.
|
||||
var me = new MatchEvaluator(CodeFormatMatchEvaluator);
|
||||
line = Regex.Replace(line,
|
||||
@"(?<before>^|([\s\([{]))" + // before
|
||||
"@" +
|
||||
@"(\|(?<lang>\w+)\|)?" + // lang
|
||||
"(?<code>[^@]+)" + // code
|
||||
"@" +
|
||||
@"(?<after>$|([\]}])|(?=" + Globals.PunctuationPattern + @"{1,2}|\s|$))", // after
|
||||
me);
|
||||
// Encode the contents of the "<code>" tags so that we don't
|
||||
// generate formatting out of it.
|
||||
line = NoTextileEncoder.EncodeNoTextileZones(line,
|
||||
@"(?<=(^|\s)<code(" + Globals.HtmlAttributesPattern + @")>)",
|
||||
@"(?=</code>)");
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
||||
public override string Conclude(string line)
|
||||
{
|
||||
// Recode everything except "<" and ">";
|
||||
line = NoTextileEncoder.DecodeNoTextileZones(line,
|
||||
@"(?<=(^|\s)<code(" + Globals.HtmlAttributesPattern + @")>)",
|
||||
@"(?=</code>)",
|
||||
new string[] { "<", ">" });
|
||||
return line;
|
||||
}
|
||||
|
||||
public string CodeFormatMatchEvaluator(Match m)
|
||||
{
|
||||
var res = m.Groups["before"].Value + "<code";
|
||||
if (m.Groups["lang"].Length > 0)
|
||||
res += " language=\"" + m.Groups["lang"].Value + "\"";
|
||||
res += ">" + m.Groups["code"].Value + "</code>" + m.Groups["after"].Value;
|
||||
return res;
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class DeletedPhraseBlockModifier : PhraseBlockModifier
|
||||
{
|
||||
public class DeletedPhraseBlockModifier : PhraseBlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
return PhraseModifierFormat(line, @"\-", "del");
|
||||
}
|
||||
return PhraseModifierFormat(line, @"\-", "del");
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class EmphasisPhraseBlockModifier : PhraseBlockModifier
|
||||
{
|
||||
public class EmphasisPhraseBlockModifier : PhraseBlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
return PhraseModifierFormat(line, @"_", "em");
|
||||
}
|
||||
return PhraseModifierFormat(line, @"_", "em");
|
||||
}
|
||||
}
|
||||
|
@ -10,13 +10,12 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class FootNoteReferenceBlockModifier : BlockModifier
|
||||
{
|
||||
public class FootNoteReferenceBlockModifier : BlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
return Regex.Replace(line, @"\b\[([0-9]+)\](\W)", "<sup><a href=\"#fn$1\">$1</a></sup>$2");
|
||||
}
|
||||
return Regex.Replace(line, @"\b\[([0-9]+)\](\W)", "<sup><a href=\"#fn$1\">$1</a></sup>$2");
|
||||
}
|
||||
}
|
||||
|
@ -10,79 +10,78 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class GlyphBlockModifier : BlockModifier
|
||||
{
|
||||
public class GlyphBlockModifier : BlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
line = Regex.Replace(line, "\"\\z", "\" ");
|
||||
|
||||
// fix: hackish
|
||||
string[,] glyphs = {
|
||||
{ @"([^\s[{(>_*])?\'(?(1)|(\s|s\b|" + Globals.PunctuationPattern + @"))", "$1’$2" }, // single closing
|
||||
{ @"\'", "‘" }, // single opening
|
||||
{ @"([^\s[{(>_*])?""(?(1)|(\s|" + Globals.PunctuationPattern + @"))", "$1”$2" }, // double closing
|
||||
{ @"""", "“" }, // double opening
|
||||
{ @"\b( )?\.{3}", "$1…" }, // ellipsis
|
||||
{ @"\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])", "<acronym title=\"$2\">$1</acronym>" }, // 3+ uppercase acronym
|
||||
{ @"(\s)?--(\s)?", "$1—$2" }, // em dash
|
||||
{ @"\s-\s", " – " }, // en dash
|
||||
{ @"(\d+)( )?x( )?(\d+)", "$1$2×$3$4" }, // dimension sign
|
||||
{ @"\b ?[([](TM|tm)[])]", "™" }, // trademark
|
||||
{ @"\b ?[([](R|r)[])]", "®" }, // registered
|
||||
{ @"\b ?[([](C|c)[])]", "©" } // copyright
|
||||
};
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (!Regex.IsMatch(line, "<.*>"))
|
||||
{
|
||||
line = Regex.Replace(line, "\"\\z", "\" ");
|
||||
|
||||
// fix: hackish
|
||||
string[,] glyphs = {
|
||||
{ @"([^\s[{(>_*])?\'(?(1)|(\s|s\b|" + Globals.PunctuationPattern + @"))", "$1’$2" }, // single closing
|
||||
{ @"\'", "‘" }, // single opening
|
||||
{ @"([^\s[{(>_*])?""(?(1)|(\s|" + Globals.PunctuationPattern + @"))", "$1”$2" }, // double closing
|
||||
{ @"""", "“" }, // double opening
|
||||
{ @"\b( )?\.{3}", "$1…" }, // ellipsis
|
||||
{ @"\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])", "<acronym title=\"$2\">$1</acronym>" }, // 3+ uppercase acronym
|
||||
{ @"(\s)?--(\s)?", "$1—$2" }, // em dash
|
||||
{ @"\s-\s", " – " }, // en dash
|
||||
{ @"(\d+)( )?x( )?(\d+)", "$1$2×$3$4" }, // dimension sign
|
||||
{ @"\b ?[([](TM|tm)[])]", "™" }, // trademark
|
||||
{ @"\b ?[([](R|r)[])]", "®" }, // registered
|
||||
{ @"\b ?[([](C|c)[])]", "©" } // copyright
|
||||
};
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (!Regex.IsMatch(line, "<.*>"))
|
||||
// If no HTML, do a simple search & replace.
|
||||
for (var i = 0; i < glyphs.GetLength(0); ++i)
|
||||
{
|
||||
// If no HTML, do a simple search & replace.
|
||||
for (var i = 0; i < glyphs.GetLength(0); ++i)
|
||||
{
|
||||
line = Regex.Replace(line, glyphs[i, 0], glyphs[i, 1]);
|
||||
}
|
||||
sb.Append(line);
|
||||
line = Regex.Replace(line, glyphs[i, 0], glyphs[i, 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var splits = Regex.Split(line, "(<.*?>)");
|
||||
var offtags = "code|pre|notextile";
|
||||
var codepre = false;
|
||||
|
||||
foreach (var split in splits)
|
||||
{
|
||||
var modifiedSplit = split;
|
||||
if (modifiedSplit.Length == 0)
|
||||
continue;
|
||||
|
||||
if (Regex.IsMatch(modifiedSplit, @"<(" + offtags + ")>"))
|
||||
codepre = true;
|
||||
if (Regex.IsMatch(modifiedSplit, @"<\/(" + offtags + ")>"))
|
||||
codepre = false;
|
||||
|
||||
if (!Regex.IsMatch(modifiedSplit, "<.*>") && !codepre)
|
||||
{
|
||||
for (var i = 0; i < glyphs.GetLength(0); ++i)
|
||||
{
|
||||
modifiedSplit = Regex.Replace(modifiedSplit, glyphs[i, 0], glyphs[i, 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// do htmlspecial if between <code>
|
||||
if (codepre)
|
||||
{
|
||||
//TODO: htmlspecialchars(line)
|
||||
//line = Regex.Replace(line, @"<(\/?" + offtags + ")>", "<$1>");
|
||||
//line = line.Replace("&#", "&#");
|
||||
}
|
||||
|
||||
sb.Append(modifiedSplit);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
sb.Append(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
var splits = Regex.Split(line, "(<.*?>)");
|
||||
var offtags = "code|pre|notextile";
|
||||
var codepre = false;
|
||||
|
||||
foreach (var split in splits)
|
||||
{
|
||||
var modifiedSplit = split;
|
||||
if (modifiedSplit.Length == 0)
|
||||
continue;
|
||||
|
||||
if (Regex.IsMatch(modifiedSplit, @"<(" + offtags + ")>"))
|
||||
codepre = true;
|
||||
if (Regex.IsMatch(modifiedSplit, @"<\/(" + offtags + ")>"))
|
||||
codepre = false;
|
||||
|
||||
if (!Regex.IsMatch(modifiedSplit, "<.*>") && !codepre)
|
||||
{
|
||||
for (var i = 0; i < glyphs.GetLength(0); ++i)
|
||||
{
|
||||
modifiedSplit = Regex.Replace(modifiedSplit, glyphs[i, 0], glyphs[i, 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// do htmlspecial if between <code>
|
||||
if (codepre)
|
||||
{
|
||||
//TODO: htmlspecialchars(line)
|
||||
//line = Regex.Replace(line, @"<(\/?" + offtags + ")>", "<$1>");
|
||||
//line = line.Replace("&#", "&#");
|
||||
}
|
||||
|
||||
sb.Append(modifiedSplit);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
@ -11,46 +11,45 @@
|
||||
#endregion
|
||||
|
||||
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class HyperLinkBlockModifier : BlockModifier
|
||||
{
|
||||
public class HyperLinkBlockModifier : BlockModifier
|
||||
private readonly string _rel = string.Empty;
|
||||
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
private readonly string m_rel = string.Empty;
|
||||
line = Regex.Replace(line,
|
||||
@"(?<pre>[\s[{(]|" + Globals.PunctuationPattern + @")?" + // $pre
|
||||
"\"" + // start
|
||||
Globals.BlockModifiersPattern + // attributes
|
||||
"(?<text>[\\w\\W]+?)" + // text
|
||||
@"\s?" +
|
||||
@"(?:\((?<title>[^)]+)\)(?=""))?" + // title
|
||||
"\":" +
|
||||
@"""(?<url>\S+[^""]+)""" + // url
|
||||
@"(?<slash>\/)?" + // slash
|
||||
@"(?<post>[^\w\/;]*)" + // post
|
||||
@"(?=\s|$)",
|
||||
new MatchEvaluator(HyperLinksFormatMatchEvaluator));
|
||||
return line;
|
||||
}
|
||||
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
line = Regex.Replace(line,
|
||||
@"(?<pre>[\s[{(]|" + Globals.PunctuationPattern + @")?" + // $pre
|
||||
"\"" + // start
|
||||
Globals.BlockModifiersPattern + // attributes
|
||||
"(?<text>[\\w\\W]+?)" + // text
|
||||
@"\s?" +
|
||||
@"(?:\((?<title>[^)]+)\)(?=""))?" + // title
|
||||
"\":" +
|
||||
@"""(?<url>\S+[^""]+)""" + // url
|
||||
@"(?<slash>\/)?" + // slash
|
||||
@"(?<post>[^\w\/;]*)" + // post
|
||||
@"(?=\s|$)",
|
||||
new MatchEvaluator(HyperLinksFormatMatchEvaluator));
|
||||
return line;
|
||||
}
|
||||
private string HyperLinksFormatMatchEvaluator(Match m)
|
||||
{
|
||||
//TODO: check the URL
|
||||
var atts = BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, "a");
|
||||
if (m.Groups["title"].Length > 0)
|
||||
atts += " title=\"" + m.Groups["title"].Value + "\"";
|
||||
var linkText = m.Groups["text"].Value.Trim(' ');
|
||||
|
||||
private string HyperLinksFormatMatchEvaluator(Match m)
|
||||
{
|
||||
//TODO: check the URL
|
||||
var atts = BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, "a");
|
||||
if (m.Groups["title"].Length > 0)
|
||||
atts += " title=\"" + m.Groups["title"].Value + "\"";
|
||||
var linkText = m.Groups["text"].Value.Trim(' ');
|
||||
|
||||
var str = m.Groups["pre"].Value + "<a ";
|
||||
if (!string.IsNullOrEmpty(m_rel))
|
||||
str += "ref=\"" + m_rel + "\" ";
|
||||
str += "href=\"" +
|
||||
m.Groups["url"].Value + m.Groups["slash"].Value + "\"" +
|
||||
atts +
|
||||
">" + linkText + "</a>" + m.Groups["post"].Value;
|
||||
return str;
|
||||
}
|
||||
var str = m.Groups["pre"].Value + "<a ";
|
||||
if (!string.IsNullOrEmpty(_rel))
|
||||
str += "ref=\"" + _rel + "\" ";
|
||||
str += "href=\"" +
|
||||
m.Groups["url"].Value + m.Groups["slash"].Value + "\"" +
|
||||
atts +
|
||||
">" + linkText + "</a>" + m.Groups["post"].Value;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
@ -10,60 +10,59 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class ImageBlockModifier : BlockModifier
|
||||
{
|
||||
public class ImageBlockModifier : BlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
line = Regex.Replace(line,
|
||||
@"\!" + // opening !
|
||||
@"(?<algn>\<|\=|\>)?" + // optional alignment atts
|
||||
Globals.BlockModifiersPattern + // optional style, public class atts
|
||||
@"(?:\. )?" + // optional dot-space
|
||||
@"(?<url>[^\s(!]+)" + // presume this is the src
|
||||
@"\s?" + // optional space
|
||||
@"(?:\((?<title>([^\)]+))\))?" +// optional title
|
||||
@"\!" + // closing
|
||||
@"(?::(?<href>(\S+)))?" + // optional href
|
||||
@"(?=\s|\.|,|;|\)|\||$)", // lookahead: space or simple punctuation or end of string
|
||||
new MatchEvaluator(ImageFormatMatchEvaluator)
|
||||
);
|
||||
return line;
|
||||
}
|
||||
|
||||
string ImageFormatMatchEvaluator(Match m)
|
||||
{
|
||||
var atts = BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, "img");
|
||||
if (m.Groups["algn"].Length > 0)
|
||||
atts += " align=\"" + Globals.ImageAlign[m.Groups["algn"].Value] + "\"";
|
||||
if (m.Groups["title"].Length > 0)
|
||||
{
|
||||
line = Regex.Replace(line,
|
||||
@"\!" + // opening !
|
||||
@"(?<algn>\<|\=|\>)?" + // optional alignment atts
|
||||
Globals.BlockModifiersPattern + // optional style, public class atts
|
||||
@"(?:\. )?" + // optional dot-space
|
||||
@"(?<url>[^\s(!]+)" + // presume this is the src
|
||||
@"\s?" + // optional space
|
||||
@"(?:\((?<title>([^\)]+))\))?" +// optional title
|
||||
@"\!" + // closing
|
||||
@"(?::(?<href>(\S+)))?" + // optional href
|
||||
@"(?=\s|\.|,|;|\)|\||$)", // lookahead: space or simple punctuation or end of string
|
||||
new MatchEvaluator(ImageFormatMatchEvaluator)
|
||||
);
|
||||
return line;
|
||||
atts += " title=\"" + m.Groups["title"].Value + "\"";
|
||||
atts += " alt=\"" + m.Groups["title"].Value + "\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
atts += " alt=\"\"";
|
||||
}
|
||||
// Get Image Size?
|
||||
|
||||
var res = "<img src=\"" + m.Groups["url"].Value + "\"" + atts + " />";
|
||||
|
||||
if (m.Groups["href"].Length > 0)
|
||||
{
|
||||
var href = m.Groups["href"].Value;
|
||||
var end = string.Empty;
|
||||
var endMatch = Regex.Match(href, @"(.*)(?<end>\.|,|;|\))$");
|
||||
if (m.Success && !string.IsNullOrEmpty(endMatch.Groups["end"].Value))
|
||||
{
|
||||
href = href[0..^1];
|
||||
end = endMatch.Groups["end"].Value;
|
||||
}
|
||||
res = "<a href=\"" + Globals.EncodeHTMLLink(href) + "\">" + res + "</a>" + end;
|
||||
}
|
||||
|
||||
string ImageFormatMatchEvaluator(Match m)
|
||||
{
|
||||
var atts = BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, "img");
|
||||
if (m.Groups["algn"].Length > 0)
|
||||
atts += " align=\"" + Globals.ImageAlign[m.Groups["algn"].Value] + "\"";
|
||||
if (m.Groups["title"].Length > 0)
|
||||
{
|
||||
atts += " title=\"" + m.Groups["title"].Value + "\"";
|
||||
atts += " alt=\"" + m.Groups["title"].Value + "\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
atts += " alt=\"\"";
|
||||
}
|
||||
// Get Image Size?
|
||||
|
||||
var res = "<img src=\"" + m.Groups["url"].Value + "\"" + atts + " />";
|
||||
|
||||
if (m.Groups["href"].Length > 0)
|
||||
{
|
||||
var href = m.Groups["href"].Value;
|
||||
var end = string.Empty;
|
||||
var endMatch = Regex.Match(href, @"(.*)(?<end>\.|,|;|\))$");
|
||||
if (m.Success && !string.IsNullOrEmpty(endMatch.Groups["end"].Value))
|
||||
{
|
||||
href = href[0..^1];
|
||||
end = endMatch.Groups["end"].Value;
|
||||
}
|
||||
res = "<a href=\"" + Globals.EncodeHTMLLink(href) + "\">" + res + "</a>" + end;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class InsertedPhraseBlockModifier : PhraseBlockModifier
|
||||
{
|
||||
public class InsertedPhraseBlockModifier : PhraseBlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
return PhraseModifierFormat(line, @"\+", "ins");
|
||||
}
|
||||
return PhraseModifierFormat(line, @"\+", "ins");
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class ItalicPhraseBlockModifier : PhraseBlockModifier
|
||||
{
|
||||
public class ItalicPhraseBlockModifier : PhraseBlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
return PhraseModifierFormat(line, @"__", "i");
|
||||
}
|
||||
return PhraseModifierFormat(line, @"__", "i");
|
||||
}
|
||||
}
|
||||
|
@ -11,22 +11,21 @@
|
||||
#endregion
|
||||
|
||||
|
||||
namespace Textile.Blocks
|
||||
{
|
||||
public class NoTextileBlockModifier : BlockModifier
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
line = NoTextileEncoder.EncodeNoTextileZones(line, @"(?<=^|\s)<notextile>", @"</notextile>(?=(\s|$)?)");
|
||||
line = NoTextileEncoder.EncodeNoTextileZones(line, @"==", @"==");
|
||||
return line;
|
||||
}
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public override string Conclude(string line)
|
||||
{
|
||||
line = NoTextileEncoder.DecodeNoTextileZones(line, @"(?<=^|\s)<notextile>", @"</notextile>(?=(\s|$)?)");
|
||||
line = NoTextileEncoder.DecodeNoTextileZones(line, @"==", @"==");
|
||||
return line;
|
||||
}
|
||||
public class NoTextileBlockModifier : BlockModifier
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
line = NoTextileEncoder.EncodeNoTextileZones(line, @"(?<=^|\s)<notextile>", @"</notextile>(?=(\s|$)?)");
|
||||
line = NoTextileEncoder.EncodeNoTextileZones(line, @"==", @"==");
|
||||
return line;
|
||||
}
|
||||
|
||||
public override string Conclude(string line)
|
||||
{
|
||||
line = NoTextileEncoder.DecodeNoTextileZones(line, @"(?<=^|\s)<notextile>", @"</notextile>(?=(\s|$)?)");
|
||||
line = NoTextileEncoder.DecodeNoTextileZones(line, @"==", @"==");
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
@ -1,79 +1,78 @@
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public static class NoTextileEncoder
|
||||
{
|
||||
public static class NoTextileEncoder
|
||||
private static readonly string[,] TextileModifiers = {
|
||||
{ "\"", """ },
|
||||
{ "%", "%" },
|
||||
{ "*", "*" },
|
||||
{ "+", "+" },
|
||||
{ "-", "-" },
|
||||
{ "<", "<" }, // or "<"
|
||||
{ "=", "=" },
|
||||
{ ">", ">" }, // or ">"
|
||||
{ "?", "?" },
|
||||
{ "^", "^" },
|
||||
{ "_", "_" },
|
||||
{ "~", "~" },
|
||||
{ "@", "@" },
|
||||
{ "'", "'" },
|
||||
{ "|", "|" },
|
||||
{ "!", "!" },
|
||||
{ "(", "(" },
|
||||
{ ")", ")" },
|
||||
{ ".", "." },
|
||||
{ "x", "x" }
|
||||
};
|
||||
|
||||
|
||||
public static string EncodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix)
|
||||
{
|
||||
private static readonly string[,] TextileModifiers = {
|
||||
{ "\"", """ },
|
||||
{ "%", "%" },
|
||||
{ "*", "*" },
|
||||
{ "+", "+" },
|
||||
{ "-", "-" },
|
||||
{ "<", "<" }, // or "<"
|
||||
{ "=", "=" },
|
||||
{ ">", ">" }, // or ">"
|
||||
{ "?", "?" },
|
||||
{ "^", "^" },
|
||||
{ "_", "_" },
|
||||
{ "~", "~" },
|
||||
{ "@", "@" },
|
||||
{ "'", "'" },
|
||||
{ "|", "|" },
|
||||
{ "!", "!" },
|
||||
{ "(", "(" },
|
||||
{ ")", ")" },
|
||||
{ ".", "." },
|
||||
{ "x", "x" }
|
||||
};
|
||||
return EncodeNoTextileZones(tmp, patternPrefix, patternSuffix, null);
|
||||
}
|
||||
|
||||
|
||||
public static string EncodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix)
|
||||
public static string EncodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix, string[] exceptions)
|
||||
{
|
||||
string evaluator(Match m)
|
||||
{
|
||||
return EncodeNoTextileZones(tmp, patternPrefix, patternSuffix, null);
|
||||
}
|
||||
|
||||
public static string EncodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix, string[] exceptions)
|
||||
{
|
||||
string evaluator(Match m)
|
||||
var toEncode = m.Groups["notex"].Value;
|
||||
if (toEncode.Length == 0)
|
||||
{
|
||||
var toEncode = m.Groups["notex"].Value;
|
||||
if (toEncode.Length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
for (var i = 0; i < TextileModifiers.GetLength(0); ++i)
|
||||
{
|
||||
if (exceptions == null || Array.IndexOf(exceptions, TextileModifiers[i, 0]) < 0)
|
||||
{
|
||||
toEncode = toEncode.Replace(TextileModifiers[i, 0], TextileModifiers[i, 1]);
|
||||
}
|
||||
}
|
||||
return patternPrefix + toEncode + patternSuffix;
|
||||
return string.Empty;
|
||||
}
|
||||
tmp = Regex.Replace(tmp, "("+ patternPrefix + "(?<notex>.+?)" + patternSuffix + ")*", new MatchEvaluator(evaluator));
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public static string DecodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix)
|
||||
{
|
||||
return DecodeNoTextileZones(tmp, patternPrefix, patternSuffix, null);
|
||||
}
|
||||
|
||||
public static string DecodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix, string[] exceptions)
|
||||
{
|
||||
string evaluator(Match m)
|
||||
for (var i = 0; i < TextileModifiers.GetLength(0); ++i)
|
||||
{
|
||||
var toEncode = m.Groups["notex"].Value;
|
||||
for (var i = 0; i < TextileModifiers.GetLength(0); ++i)
|
||||
if (exceptions == null || Array.IndexOf(exceptions, TextileModifiers[i, 0]) < 0)
|
||||
{
|
||||
if (exceptions == null || Array.IndexOf(exceptions, TextileModifiers[i, 0]) < 0)
|
||||
{
|
||||
toEncode = toEncode.Replace(TextileModifiers[i, 1], TextileModifiers[i, 0]);
|
||||
}
|
||||
toEncode = toEncode.Replace(TextileModifiers[i, 0], TextileModifiers[i, 1]);
|
||||
}
|
||||
return toEncode;
|
||||
}
|
||||
tmp = Regex.Replace(tmp, "(" + patternPrefix + "(?<notex>.+?)" + patternSuffix + ")*", new MatchEvaluator(evaluator));
|
||||
return tmp;
|
||||
return patternPrefix + toEncode + patternSuffix;
|
||||
}
|
||||
tmp = Regex.Replace(tmp, "("+ patternPrefix + "(?<notex>.+?)" + patternSuffix + ")*", new MatchEvaluator(evaluator));
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public static string DecodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix)
|
||||
{
|
||||
return DecodeNoTextileZones(tmp, patternPrefix, patternSuffix, null);
|
||||
}
|
||||
|
||||
public static string DecodeNoTextileZones(string tmp, string patternPrefix, string patternSuffix, string[] exceptions)
|
||||
{
|
||||
string evaluator(Match m)
|
||||
{
|
||||
var toEncode = m.Groups["notex"].Value;
|
||||
for (var i = 0; i < TextileModifiers.GetLength(0); ++i)
|
||||
{
|
||||
if (exceptions == null || Array.IndexOf(exceptions, TextileModifiers[i, 0]) < 0)
|
||||
{
|
||||
toEncode = toEncode.Replace(TextileModifiers[i, 1], TextileModifiers[i, 0]);
|
||||
}
|
||||
}
|
||||
return toEncode;
|
||||
}
|
||||
tmp = Regex.Replace(tmp, "(" + patternPrefix + "(?<notex>.+?)" + patternSuffix + ")*", new MatchEvaluator(evaluator));
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
@ -10,82 +10,81 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public abstract class PhraseBlockModifier : BlockModifier
|
||||
{
|
||||
public abstract class PhraseBlockModifier : BlockModifier
|
||||
protected PhraseBlockModifier()
|
||||
{
|
||||
protected PhraseBlockModifier()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected string PhraseModifierFormat(string input, string modifier, string tag)
|
||||
protected string PhraseModifierFormat(string input, string modifier, string tag)
|
||||
{
|
||||
// All phrase modifiers are one character, or a double character. Sometimes,
|
||||
// there's an additional escape character for the regex ('\').
|
||||
var compressedModifier = modifier;
|
||||
if (modifier.Length == 4)
|
||||
{
|
||||
// All phrase modifiers are one character, or a double character. Sometimes,
|
||||
// there's an additional escape character for the regex ('\').
|
||||
var compressedModifier = modifier;
|
||||
if (modifier.Length == 4)
|
||||
{
|
||||
compressedModifier = modifier.Substring(0, 2);
|
||||
}
|
||||
else if (modifier.Length == 2)
|
||||
{
|
||||
if (modifier[0] != '\\')
|
||||
compressedModifier = modifier[0].ToString();
|
||||
//else: compressedModifier = modifier;
|
||||
}
|
||||
compressedModifier = modifier.Substring(0, 2);
|
||||
}
|
||||
else if (modifier.Length == 2)
|
||||
{
|
||||
if (modifier[0] != '\\')
|
||||
compressedModifier = modifier[0].ToString();
|
||||
//else: compressedModifier = modifier;
|
||||
}
|
||||
//else: compressedModifier = modifier;
|
||||
|
||||
// We try to remove the Textile tag used for the formatting from
|
||||
// the punctuation pattern, so that we match the end of the formatted
|
||||
// zone correctly.
|
||||
var punctuationPattern = Globals.PunctuationPattern.Replace(compressedModifier, "");
|
||||
// We try to remove the Textile tag used for the formatting from
|
||||
// the punctuation pattern, so that we match the end of the formatted
|
||||
// zone correctly.
|
||||
var punctuationPattern = Globals.PunctuationPattern.Replace(compressedModifier, "");
|
||||
|
||||
// Now we can do the replacement.
|
||||
var pmme = new PhraseModifierMatchEvaluator(tag);
|
||||
var res = Regex.Replace(input,
|
||||
@"(?<=\s|" + punctuationPattern + @"|[{\(\[]|^)" +
|
||||
modifier +
|
||||
Globals.BlockModifiersPattern +
|
||||
@"(:(?<cite>(\S+)))?" +
|
||||
@"(?<content>[^" + compressedModifier + "]*)" +
|
||||
@"(?<end>" + punctuationPattern + @"*)" +
|
||||
modifier +
|
||||
@"(?=[\]\)}]|" + punctuationPattern + @"+|\s|$)",
|
||||
new MatchEvaluator(pmme.MatchEvaluator)
|
||||
);
|
||||
return res;
|
||||
// Now we can do the replacement.
|
||||
var pmme = new PhraseModifierMatchEvaluator(tag);
|
||||
var res = Regex.Replace(input,
|
||||
@"(?<=\s|" + punctuationPattern + @"|[{\(\[]|^)" +
|
||||
modifier +
|
||||
Globals.BlockModifiersPattern +
|
||||
@"(:(?<cite>(\S+)))?" +
|
||||
@"(?<content>[^" + compressedModifier + "]*)" +
|
||||
@"(?<end>" + punctuationPattern + @"*)" +
|
||||
modifier +
|
||||
@"(?=[\]\)}]|" + punctuationPattern + @"+|\s|$)",
|
||||
new MatchEvaluator(pmme.MatchEvaluator)
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
private sealed class PhraseModifierMatchEvaluator
|
||||
{
|
||||
private readonly string _tag;
|
||||
|
||||
public PhraseModifierMatchEvaluator(string tag)
|
||||
{
|
||||
_tag = tag;
|
||||
}
|
||||
|
||||
private sealed class PhraseModifierMatchEvaluator
|
||||
public string MatchEvaluator(Match m)
|
||||
{
|
||||
readonly string m_tag;
|
||||
|
||||
public PhraseModifierMatchEvaluator(string tag)
|
||||
if (m.Groups["content"].Length == 0)
|
||||
{
|
||||
m_tag = tag;
|
||||
// It's possible that the "atts" match groups eats the contents
|
||||
// when the user didn't want to give block attributes, but the content
|
||||
// happens to match the syntax. For example: "*(blah)*".
|
||||
if (m.Groups["atts"].Length == 0)
|
||||
return m.ToString();
|
||||
return "<" + _tag + ">" + m.Groups["atts"].Value + m.Groups["end"].Value + "</" + _tag + ">";
|
||||
}
|
||||
|
||||
public string MatchEvaluator(Match m)
|
||||
{
|
||||
if (m.Groups["content"].Length == 0)
|
||||
{
|
||||
// It's possible that the "atts" match groups eats the contents
|
||||
// when the user didn't want to give block attributes, but the content
|
||||
// happens to match the syntax. For example: "*(blah)*".
|
||||
if (m.Groups["atts"].Length == 0)
|
||||
return m.ToString();
|
||||
return "<" + m_tag + ">" + m.Groups["atts"].Value + m.Groups["end"].Value + "</" + m_tag + ">";
|
||||
}
|
||||
var atts = BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, _tag);
|
||||
if (m.Groups["cite"].Length > 0)
|
||||
atts += " cite=\"" + m.Groups["cite"] + "\"";
|
||||
|
||||
var atts = BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, m_tag);
|
||||
if (m.Groups["cite"].Length > 0)
|
||||
atts += " cite=\"" + m.Groups["cite"] + "\"";
|
||||
|
||||
var res = "<" + m_tag + atts + ">" +
|
||||
m.Groups["content"].Value + m.Groups["end"].Value +
|
||||
"</" + m_tag + ">";
|
||||
return res;
|
||||
}
|
||||
var res = "<" + _tag + atts + ">" +
|
||||
m.Groups["content"].Value + m.Groups["end"].Value +
|
||||
"</" + _tag + ">";
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,28 +10,27 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile.Blocks
|
||||
{
|
||||
public class PreBlockModifier : BlockModifier
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
// Encode the contents of the "<pre>" tags so that we don't
|
||||
// generate formatting out of it.
|
||||
line = NoTextileEncoder.EncodeNoTextileZones(line,
|
||||
@"(?<=(^|\s)<pre(" + Globals.HtmlAttributesPattern + @")>)",
|
||||
@"(?=</pre>)");
|
||||
return line;
|
||||
}
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public override string Conclude(string line)
|
||||
{
|
||||
// Recode everything.
|
||||
line = NoTextileEncoder.DecodeNoTextileZones(line,
|
||||
@"(?<=(^|\s)<pre(" + Globals.HtmlAttributesPattern + @")>)",
|
||||
@"(?=</pre>)",
|
||||
new string[] { "<", ">" });
|
||||
return line;
|
||||
}
|
||||
public class PreBlockModifier : BlockModifier
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
// Encode the contents of the "<pre>" tags so that we don't
|
||||
// generate formatting out of it.
|
||||
line = NoTextileEncoder.EncodeNoTextileZones(line,
|
||||
@"(?<=(^|\s)<pre(" + Globals.HtmlAttributesPattern + @")>)",
|
||||
@"(?=</pre>)");
|
||||
return line;
|
||||
}
|
||||
|
||||
public override string Conclude(string line)
|
||||
{
|
||||
// Recode everything.
|
||||
line = NoTextileEncoder.DecodeNoTextileZones(line,
|
||||
@"(?<=(^|\s)<pre(" + Globals.HtmlAttributesPattern + @")>)",
|
||||
@"(?=</pre>)",
|
||||
new string[] { "<", ">" });
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class SpanPhraseBlockModifier : PhraseBlockModifier
|
||||
{
|
||||
public class SpanPhraseBlockModifier : PhraseBlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
return PhraseModifierFormat(line, @"%", "span");
|
||||
}
|
||||
return PhraseModifierFormat(line, @"%", "span");
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class StrongPhraseBlockModifier : PhraseBlockModifier
|
||||
{
|
||||
public class StrongPhraseBlockModifier : PhraseBlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
return PhraseModifierFormat(line, @"\*", "strong");
|
||||
}
|
||||
return PhraseModifierFormat(line, @"\*", "strong");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class SubScriptPhraseBlockModifier : PhraseBlockModifier
|
||||
{
|
||||
public class SubScriptPhraseBlockModifier : PhraseBlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
return PhraseModifierFormat(line, @"~", "sub");
|
||||
}
|
||||
return PhraseModifierFormat(line, @"~", "sub");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace Textile.Blocks
|
||||
namespace Textile.Blocks;
|
||||
|
||||
public class SuperScriptPhraseBlockModifier : PhraseBlockModifier
|
||||
{
|
||||
public class SuperScriptPhraseBlockModifier : PhraseBlockModifier
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
public override string ModifyLine(string line)
|
||||
{
|
||||
return PhraseModifierFormat(line, @"\^", "sup");
|
||||
}
|
||||
return PhraseModifierFormat(line, @"\^", "sup");
|
||||
}
|
||||
}
|
||||
|
@ -10,126 +10,125 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile
|
||||
namespace Textile;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for formatter states.
|
||||
/// </summary>
|
||||
/// A formatter state describes the current situation
|
||||
/// of the text being currently processed. A state can
|
||||
/// write HTML code when entered, exited, and can modify
|
||||
/// each line of text it receives.
|
||||
public abstract class FormatterState
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for formatter states.
|
||||
/// The formatter this state belongs to.
|
||||
/// </summary>
|
||||
/// A formatter state describes the current situation
|
||||
/// of the text being currently processed. A state can
|
||||
/// write HTML code when entered, exited, and can modify
|
||||
/// each line of text it receives.
|
||||
public abstract class FormatterState
|
||||
public TextileFormatter Formatter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor.
|
||||
/// </summary>
|
||||
/// <param name="f">The parent formatter.</param>
|
||||
protected FormatterState(TextileFormatter formatter)
|
||||
{
|
||||
/// <summary>
|
||||
/// The formatter this state belongs to.
|
||||
/// </summary>
|
||||
public TextileFormatter Formatter { get; }
|
||||
Formatter = formatter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor.
|
||||
/// </summary>
|
||||
/// <param name="f">The parent formatter.</param>
|
||||
protected FormatterState(TextileFormatter formatter)
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="m"></param>
|
||||
/// <returns></returns>
|
||||
public abstract string Consume(string input, Match m);
|
||||
|
||||
/// <summary>
|
||||
/// Method called when the state is entered.
|
||||
/// </summary>
|
||||
public abstract void Enter();
|
||||
/// <summary>
|
||||
/// Method called when the state is exited.
|
||||
/// </summary>
|
||||
public abstract void Exit();
|
||||
/// <summary>
|
||||
/// Method called when a line of text should be written
|
||||
/// to the web form.
|
||||
/// </summary>
|
||||
/// <param name="input">The line of text.</param>
|
||||
public abstract void FormatLine(string input);
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this state can last for more than one line.
|
||||
/// </summary>
|
||||
/// <returns>A boolean value stating whether this state is only for one line.</returns>
|
||||
/// This method should return true only if this state is genuinely
|
||||
/// multi-line. For example, a header text is only one line long. You can
|
||||
/// have several consecutive lines of header texts, but they are not the same
|
||||
/// header - just several headers one after the other.
|
||||
/// Bulleted and numbered lists are good examples of multi-line states.
|
||||
//abstract public bool IsOneLineOnly();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public abstract bool ShouldExit(string input);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="actualTag"></param>
|
||||
/// <param name="alignNfo"></param>
|
||||
/// <param name="attNfo"></param>
|
||||
/// <returns></returns>
|
||||
public virtual bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether block formatting (quick phrase modifiers, etc.) should be
|
||||
/// applied to this line.
|
||||
/// </summary>
|
||||
/// <param name="input">The line of text</param>
|
||||
/// <returns>Whether the line should be formatted for blocks</returns>
|
||||
public virtual bool ShouldFormatBlocks(string input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the current state accepts being superceded by another one
|
||||
/// we would possibly find by parsing the input line of text.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public virtual bool ShouldParseForNewFormatterState(string input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the formatting state we should fallback to if we don't find anything
|
||||
/// relevant in a line of text.
|
||||
/// </summary>
|
||||
public virtual Type FallbackFormattingState
|
||||
{
|
||||
get
|
||||
{
|
||||
Formatter = formatter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="m"></param>
|
||||
/// <returns></returns>
|
||||
public abstract string Consume(string input, Match m);
|
||||
|
||||
/// <summary>
|
||||
/// Method called when the state is entered.
|
||||
/// </summary>
|
||||
public abstract void Enter();
|
||||
/// <summary>
|
||||
/// Method called when the state is exited.
|
||||
/// </summary>
|
||||
public abstract void Exit();
|
||||
/// <summary>
|
||||
/// Method called when a line of text should be written
|
||||
/// to the web form.
|
||||
/// </summary>
|
||||
/// <param name="input">The line of text.</param>
|
||||
public abstract void FormatLine(string input);
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether this state can last for more than one line.
|
||||
/// </summary>
|
||||
/// <returns>A boolean value stating whether this state is only for one line.</returns>
|
||||
/// This method should return true only if this state is genuinely
|
||||
/// multi-line. For example, a header text is only one line long. You can
|
||||
/// have several consecutive lines of header texts, but they are not the same
|
||||
/// header - just several headers one after the other.
|
||||
/// Bulleted and numbered lists are good examples of multi-line states.
|
||||
//abstract public bool IsOneLineOnly();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public abstract bool ShouldExit(string input);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="actualTag"></param>
|
||||
/// <param name="alignNfo"></param>
|
||||
/// <param name="attNfo"></param>
|
||||
/// <returns></returns>
|
||||
public virtual bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether block formatting (quick phrase modifiers, etc.) should be
|
||||
/// applied to this line.
|
||||
/// </summary>
|
||||
/// <param name="input">The line of text</param>
|
||||
/// <returns>Whether the line should be formatted for blocks</returns>
|
||||
public virtual bool ShouldFormatBlocks(string input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the current state accepts being superceded by another one
|
||||
/// we would possibly find by parsing the input line of text.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public virtual bool ShouldParseForNewFormatterState(string input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the formatting state we should fallback to if we don't find anything
|
||||
/// relevant in a line of text.
|
||||
/// </summary>
|
||||
public virtual Type FallbackFormattingState
|
||||
{
|
||||
get
|
||||
{
|
||||
return typeof(States.ParagraphFormatterState);
|
||||
}
|
||||
}
|
||||
|
||||
protected FormatterState CurrentFormatterState
|
||||
{
|
||||
get { return this.Formatter.CurrentState; }
|
||||
}
|
||||
|
||||
protected void ChangeFormatterState(FormatterState formatterState)
|
||||
{
|
||||
this.Formatter.ChangeState(formatterState);
|
||||
return typeof(States.ParagraphFormatterState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected FormatterState CurrentFormatterState
|
||||
{
|
||||
get { return this.Formatter.CurrentState; }
|
||||
}
|
||||
|
||||
protected void ChangeFormatterState(FormatterState formatterState)
|
||||
{
|
||||
this.Formatter.ChangeState(formatterState);
|
||||
}
|
||||
}
|
@ -1,21 +1,20 @@
|
||||
namespace Textile
|
||||
namespace Textile;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class FormatterStateAttribute : Attribute
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class FormatterStateAttribute : Attribute
|
||||
public string Pattern { get; }
|
||||
|
||||
public FormatterStateAttribute(string pattern)
|
||||
{
|
||||
public string Pattern { get; }
|
||||
Pattern = pattern;
|
||||
}
|
||||
|
||||
public FormatterStateAttribute(string pattern)
|
||||
{
|
||||
Pattern = pattern;
|
||||
}
|
||||
|
||||
public static FormatterStateAttribute Get(Type type)
|
||||
{
|
||||
var atts = type.GetCustomAttributes(typeof(FormatterStateAttribute), false);
|
||||
if (atts.Length == 0)
|
||||
return null;
|
||||
return (FormatterStateAttribute)atts[0];
|
||||
}
|
||||
public static FormatterStateAttribute Get(Type type)
|
||||
{
|
||||
var atts = type.GetCustomAttributes(typeof(FormatterStateAttribute), false);
|
||||
if (atts.Length == 0)
|
||||
return null;
|
||||
return (FormatterStateAttribute)atts[0];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Text;
|
||||
global using System.Text;
|
||||
global using System.Text.RegularExpressions;
|
||||
|
||||
global using Textile.Blocks;
|
||||
|
@ -11,78 +11,77 @@
|
||||
#endregion
|
||||
|
||||
|
||||
namespace Textile
|
||||
namespace Textile;
|
||||
|
||||
/// <summary>
|
||||
/// A utility class for global things used by the TextileFormatter.
|
||||
/// </summary>
|
||||
static class Globals
|
||||
{
|
||||
#region Global Regex Patterns
|
||||
|
||||
public const string HorizontalAlignPattern = @"(?:[()]*(\<(?!>)|(?<!<)\>|\<\>|=)[()]*)";
|
||||
public const string VerticalAlignPattern = @"[\-^~]";
|
||||
public const string CssClassPattern = @"(?:\([^)]+\))";
|
||||
public const string LanguagePattern = @"(?:\[[^]]+\])";
|
||||
public const string CssStylePattern = @"(?:\{[^}]+\})";
|
||||
public const string ColumnSpanPattern = @"(?:\\\d+)";
|
||||
public const string RowSpanPattern = @"(?:/\d+)";
|
||||
|
||||
public const string AlignPattern = "(?<align>" + HorizontalAlignPattern + "?" + VerticalAlignPattern + "?|" + VerticalAlignPattern + "?" + HorizontalAlignPattern + "?)";
|
||||
public const string SpanPattern = @"(?<span>" + ColumnSpanPattern + "?" + RowSpanPattern + "?|" + RowSpanPattern + "?" + ColumnSpanPattern + "?)";
|
||||
public const string BlockModifiersPattern = @"(?<atts>" + CssClassPattern + "?" + CssStylePattern + "?" + LanguagePattern + "?|" +
|
||||
CssStylePattern + "?" + LanguagePattern + "?" + CssClassPattern + "?|" +
|
||||
LanguagePattern + "?" + CssStylePattern + "?" + CssClassPattern + "?)";
|
||||
|
||||
public const string PunctuationPattern = @"[\!""#\$%&'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{}~]";
|
||||
|
||||
public const string HtmlAttributesPattern = @"(\s+\w+=((""[^""]+"")|('[^']+')))*";
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// A utility class for global things used by the TextileFormatter.
|
||||
/// Image alignment tags, mapped to their HTML meanings.
|
||||
/// </summary>
|
||||
static class Globals
|
||||
public static Dictionary<string, string> ImageAlign { get; set; }
|
||||
/// <summary>
|
||||
/// Horizontal text alignment tags, mapped to their HTML meanings.
|
||||
/// </summary>
|
||||
public static Dictionary<string, string> HorizontalAlign { get; set; }
|
||||
/// <summary>
|
||||
/// Vertical text alignment tags, mapped to their HTML meanings.
|
||||
/// </summary>
|
||||
public static Dictionary<string, string> VerticalAlign { get; set;}
|
||||
|
||||
static Globals()
|
||||
{
|
||||
#region Global Regex Patterns
|
||||
|
||||
public const string HorizontalAlignPattern = @"(?:[()]*(\<(?!>)|(?<!<)\>|\<\>|=)[()]*)";
|
||||
public const string VerticalAlignPattern = @"[\-^~]";
|
||||
public const string CssClassPattern = @"(?:\([^)]+\))";
|
||||
public const string LanguagePattern = @"(?:\[[^]]+\])";
|
||||
public const string CssStylePattern = @"(?:\{[^}]+\})";
|
||||
public const string ColumnSpanPattern = @"(?:\\\d+)";
|
||||
public const string RowSpanPattern = @"(?:/\d+)";
|
||||
|
||||
public const string AlignPattern = "(?<align>" + HorizontalAlignPattern + "?" + VerticalAlignPattern + "?|" + VerticalAlignPattern + "?" + HorizontalAlignPattern + "?)";
|
||||
public const string SpanPattern = @"(?<span>" + ColumnSpanPattern + "?" + RowSpanPattern + "?|" + RowSpanPattern + "?" + ColumnSpanPattern + "?)";
|
||||
public const string BlockModifiersPattern = @"(?<atts>" + CssClassPattern + "?" + CssStylePattern + "?" + LanguagePattern + "?|" +
|
||||
CssStylePattern + "?" + LanguagePattern + "?" + CssClassPattern + "?|" +
|
||||
LanguagePattern + "?" + CssStylePattern + "?" + CssClassPattern + "?)";
|
||||
|
||||
public const string PunctuationPattern = @"[\!""#\$%&'()\*\+,\-\./:;<=>\?@\[\\\]\^_`{}~]";
|
||||
|
||||
public const string HtmlAttributesPattern = @"(\s+\w+=((""[^""]+"")|('[^']+')))*";
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Image alignment tags, mapped to their HTML meanings.
|
||||
/// </summary>
|
||||
public static Dictionary<string, string> ImageAlign { get; set; }
|
||||
/// <summary>
|
||||
/// Horizontal text alignment tags, mapped to their HTML meanings.
|
||||
/// </summary>
|
||||
public static Dictionary<string, string> HorizontalAlign { get; set; }
|
||||
/// <summary>
|
||||
/// Vertical text alignment tags, mapped to their HTML meanings.
|
||||
/// </summary>
|
||||
public static Dictionary<string, string> VerticalAlign { get; set;}
|
||||
|
||||
static Globals()
|
||||
ImageAlign = new Dictionary<string, string>
|
||||
{
|
||||
ImageAlign = new Dictionary<string, string>
|
||||
{
|
||||
["<"] = "left",
|
||||
["="] = "center",
|
||||
[">"] = "right"
|
||||
};
|
||||
["<"] = "left",
|
||||
["="] = "center",
|
||||
[">"] = "right"
|
||||
};
|
||||
|
||||
HorizontalAlign = new Dictionary<string, string>
|
||||
{
|
||||
["<"] = "left",
|
||||
["="] = "center",
|
||||
[">"] = "right",
|
||||
["<>"] = "justify"
|
||||
};
|
||||
|
||||
VerticalAlign = new Dictionary<string, string>
|
||||
{
|
||||
["^"] = "top",
|
||||
["-"] = "middle",
|
||||
["~"] = "bottom"
|
||||
};
|
||||
}
|
||||
|
||||
public static string EncodeHTMLLink(string url)
|
||||
HorizontalAlign = new Dictionary<string, string>
|
||||
{
|
||||
url = url.Replace("&", "&");
|
||||
url = System.Text.RegularExpressions.Regex.Replace(url, "&(?=[^#])", "&");
|
||||
return url;
|
||||
}
|
||||
["<"] = "left",
|
||||
["="] = "center",
|
||||
[">"] = "right",
|
||||
["<>"] = "justify"
|
||||
};
|
||||
|
||||
VerticalAlign = new Dictionary<string, string>
|
||||
{
|
||||
["^"] = "top",
|
||||
["-"] = "middle",
|
||||
["~"] = "bottom"
|
||||
};
|
||||
}
|
||||
|
||||
public static string EncodeHTMLLink(string url)
|
||||
{
|
||||
url = url.Replace("&", "&");
|
||||
url = System.Text.RegularExpressions.Regex.Replace(url, "&(?=[^#])", "&");
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
@ -10,44 +10,39 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
#region Using Statements
|
||||
#endregion
|
||||
namespace Textile;
|
||||
|
||||
|
||||
namespace Textile
|
||||
/// <summary>
|
||||
/// Interface through which the HTML formatted text
|
||||
/// will be sent.
|
||||
/// </summary>
|
||||
/// Clients of the TextileFormatter class will have to provide
|
||||
/// an outputter that implements this interface. Most of the
|
||||
/// time, it'll be the WebForm itself.
|
||||
public interface IOutputter
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface through which the HTML formatted text
|
||||
/// will be sent.
|
||||
/// Method called just before the formatted text
|
||||
/// is sent to the outputter.
|
||||
/// </summary>
|
||||
/// Clients of the TextileFormatter class will have to provide
|
||||
/// an outputter that implements this interface. Most of the
|
||||
/// time, it'll be the WebForm itself.
|
||||
public interface IOutputter
|
||||
{
|
||||
/// <summary>
|
||||
/// Method called just before the formatted text
|
||||
/// is sent to the outputter.
|
||||
/// </summary>
|
||||
void Begin();
|
||||
void Begin();
|
||||
|
||||
/// <summary>
|
||||
/// Metohd called whenever the TextileFormatter wants to
|
||||
/// print some text.
|
||||
/// </summary>
|
||||
/// <param name="text">The formatted HTML text.</param>
|
||||
void Write(string text);
|
||||
/// <summary>
|
||||
/// Metohd called whenever the TextileFormatter wants to
|
||||
/// print some text. This should automatically print an
|
||||
/// additionnal end of line character.
|
||||
/// </summary>
|
||||
/// <param name="line">The formatted HTML text.</param>
|
||||
void WriteLine(string line);
|
||||
/// <summary>
|
||||
/// Metohd called whenever the TextileFormatter wants to
|
||||
/// print some text.
|
||||
/// </summary>
|
||||
/// <param name="text">The formatted HTML text.</param>
|
||||
void Write(string text);
|
||||
/// <summary>
|
||||
/// Metohd called whenever the TextileFormatter wants to
|
||||
/// print some text. This should automatically print an
|
||||
/// additionnal end of line character.
|
||||
/// </summary>
|
||||
/// <param name="line">The formatted HTML text.</param>
|
||||
void WriteLine(string line);
|
||||
|
||||
/// <summary>
|
||||
/// Method called at the end of the formatting.
|
||||
/// </summary>
|
||||
void End();
|
||||
}
|
||||
/// <summary>
|
||||
/// Method called at the end of the formatting.
|
||||
/// </summary>
|
||||
void End();
|
||||
}
|
||||
|
@ -10,42 +10,41 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"bq" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class BlockQuoteFormatterState : SimpleBlockFormatterState
|
||||
{
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"bq" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class BlockQuoteFormatterState : SimpleBlockFormatterState
|
||||
public BlockQuoteFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
public BlockQuoteFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.Write("<blockquote" + FormattedStylesAndAlignment("blockquote") + "><p>");
|
||||
}
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.Write("<blockquote" + FormattedStylesAndAlignment("blockquote") + "><p>");
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</p></blockquote>");
|
||||
}
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</p></blockquote>");
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.Write(input);
|
||||
}
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.Write(input);
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (Regex.IsMatch(input, @"^\s*$"))
|
||||
return true;
|
||||
Formatter.Output.WriteLine("<br />");
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (Regex.IsMatch(input, @"^\s*$"))
|
||||
return true;
|
||||
Formatter.Output.WriteLine("<br />");
|
||||
return false;
|
||||
}
|
||||
|
||||
public override Type FallbackFormattingState
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
public override Type FallbackFormattingState
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
}
|
||||
|
@ -1,78 +1,77 @@
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
[FormatterState(@"^\s*<code" + Globals.HtmlAttributesPattern + ">")]
|
||||
public class CodeFormatterState : FormatterState
|
||||
{
|
||||
[FormatterState(@"^\s*<code" + Globals.HtmlAttributesPattern + ">")]
|
||||
public class CodeFormatterState : FormatterState
|
||||
private bool _shouldExitNextTime = false;
|
||||
private bool _shouldFixHtmlEntities = false;
|
||||
|
||||
public CodeFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
bool m_shouldExitNextTime = false;
|
||||
bool m_shouldFixHtmlEntities = false;
|
||||
|
||||
public CodeFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
if (!Regex.IsMatch(input, "</code>"))
|
||||
{
|
||||
this.Formatter.ChangeState(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Formatter.ChangeState(new PassthroughFormatterState(this.Formatter));
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
m_shouldFixHtmlEntities = false;
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
if (m_shouldFixHtmlEntities)
|
||||
input = FixEntities(input);
|
||||
Formatter.Output.WriteLine(input);
|
||||
m_shouldFixHtmlEntities = true;
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (m_shouldExitNextTime)
|
||||
return true;
|
||||
m_shouldExitNextTime = Regex.IsMatch(input, @"</code>");
|
||||
m_shouldFixHtmlEntities = !m_shouldExitNextTime;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldFormatBlocks(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldParseForNewFormatterState(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private string FixEntities(string text)
|
||||
{
|
||||
// de-entify any remaining angle brackets or ampersands
|
||||
text = text.Replace("&", "&");
|
||||
text = text.Replace(">", ">");
|
||||
text = text.Replace("<", "<");
|
||||
//Regex.Replace(text, @"\b&([#a-z0-9]+;)", "x%x%");
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
if (!Regex.IsMatch(input, "</code>"))
|
||||
{
|
||||
this.Formatter.ChangeState(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Formatter.ChangeState(new PassthroughFormatterState(this.Formatter));
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
_shouldFixHtmlEntities = false;
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
if (_shouldFixHtmlEntities)
|
||||
input = FixEntities(input);
|
||||
Formatter.Output.WriteLine(input);
|
||||
_shouldFixHtmlEntities = true;
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (_shouldExitNextTime)
|
||||
return true;
|
||||
_shouldExitNextTime = Regex.IsMatch(input, @"</code>");
|
||||
_shouldFixHtmlEntities = !_shouldExitNextTime;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldFormatBlocks(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldParseForNewFormatterState(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private string FixEntities(string text)
|
||||
{
|
||||
// de-entify any remaining angle brackets or ampersands
|
||||
text = text.Replace("&", "&");
|
||||
text = text.Replace(">", ">");
|
||||
text = text.Replace("<", "<");
|
||||
//Regex.Replace(text, @"\b&([#a-z0-9]+;)", "x%x%");
|
||||
return text;
|
||||
}
|
||||
}
|
@ -11,50 +11,49 @@
|
||||
#endregion
|
||||
|
||||
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"fn[0-9]+" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class FootNoteFormatterState : SimpleBlockFormatterState
|
||||
{
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"fn[0-9]+" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class FootNoteFormatterState : SimpleBlockFormatterState
|
||||
private int _noteID = 0;
|
||||
|
||||
public FootNoteFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
int m_noteID = 0;
|
||||
}
|
||||
|
||||
public FootNoteFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
}
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.Write(
|
||||
string.Format("<p id=\"fn{0}\"{1}><sup>{2}</sup> ",
|
||||
_noteID,
|
||||
FormattedStylesAndAlignment("p"),
|
||||
_noteID));
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.Write(
|
||||
string.Format("<p id=\"fn{0}\"{1}><sup>{2}</sup> ",
|
||||
m_noteID,
|
||||
FormattedStylesAndAlignment("p"),
|
||||
m_noteID));
|
||||
}
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</p>");
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</p>");
|
||||
}
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.Write(input);
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.Write(input);
|
||||
}
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
protected override void OnContextAcquired()
|
||||
{
|
||||
var m = Regex.Match(Tag, @"^fn(?<id>[0-9]+)");
|
||||
_noteID = int.Parse(m.Groups["id"].Value);
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
protected override void OnContextAcquired()
|
||||
{
|
||||
var m = Regex.Match(Tag, @"^fn(?<id>[0-9]+)");
|
||||
m_noteID = int.Parse(m.Groups["id"].Value);
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -11,95 +11,94 @@
|
||||
#endregion
|
||||
|
||||
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"pad[0-9]+" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class PaddingFormatterState : SimpleBlockFormatterState
|
||||
{
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"pad[0-9]+" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class PaddingFormatterState : SimpleBlockFormatterState
|
||||
public PaddingFormatterState(TextileFormatter formatter)
|
||||
: base(formatter)
|
||||
{
|
||||
public PaddingFormatterState(TextileFormatter formatter)
|
||||
: base(formatter)
|
||||
}
|
||||
|
||||
public int HeaderLevel { get; private set; } = 0;
|
||||
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
for (var i = 0; i < HeaderLevel; i++)
|
||||
{
|
||||
}
|
||||
|
||||
public int HeaderLevel { get; private set; } = 0;
|
||||
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
for (var i = 0; i < HeaderLevel; i++)
|
||||
{
|
||||
Formatter.Output.Write($"<br {FormattedStylesAndAlignment("br")}/>");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnContextAcquired()
|
||||
{
|
||||
var m = Regex.Match(Tag, @"^pad(?<lvl>[0-9]+)");
|
||||
HeaderLevel = int.Parse(m.Groups["lvl"].Value);
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.Write(input);
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string intput)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
Formatter.Output.Write($"<br {FormattedStylesAndAlignment("br")}/>");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formatting state for headers and titles.
|
||||
/// </summary>
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"h[0-9]+" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class HeaderFormatterState : SimpleBlockFormatterState
|
||||
public override void Exit()
|
||||
{
|
||||
public int HeaderLevel { get; private set; } = 0;
|
||||
}
|
||||
|
||||
public HeaderFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
}
|
||||
protected override void OnContextAcquired()
|
||||
{
|
||||
var m = Regex.Match(Tag, @"^pad(?<lvl>[0-9]+)");
|
||||
HeaderLevel = int.Parse(m.Groups["lvl"].Value);
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.Write("<h" + HeaderLevel + FormattedStylesAndAlignment("h" + HeaderLevel) + ">");
|
||||
}
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.Write(input);
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</h" + HeaderLevel + ">");
|
||||
}
|
||||
public override bool ShouldExit(string intput)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnContextAcquired()
|
||||
{
|
||||
var m = Regex.Match(Tag, @"^h(?<lvl>[0-9]+)");
|
||||
HeaderLevel = int.Parse(m.Groups["lvl"].Value);
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.Write(input);
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string intput)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formatting state for headers and titles.
|
||||
/// </summary>
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"h[0-9]+" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class HeaderFormatterState : SimpleBlockFormatterState
|
||||
{
|
||||
public int HeaderLevel { get; private set; } = 0;
|
||||
|
||||
public HeaderFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.Write("<h" + HeaderLevel + FormattedStylesAndAlignment("h" + HeaderLevel) + ">");
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</h" + HeaderLevel + ">");
|
||||
}
|
||||
|
||||
protected override void OnContextAcquired()
|
||||
{
|
||||
var m = Regex.Match(Tag, @"^h(?<lvl>[0-9]+)");
|
||||
HeaderLevel = int.Parse(m.Groups["lvl"].Value);
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.Write(input);
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string intput)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
@ -21,15 +21,15 @@ namespace Textile.States
|
||||
internal const string PatternBegin = @"^\s*(?<tag>";
|
||||
internal const string PatternEnd = @")" + Globals.BlockModifiersPattern + @"(?:\s+)? (?<content>.*)$";
|
||||
|
||||
private bool m_firstItem = true;
|
||||
private bool m_firstItemLine = true;
|
||||
private string m_tag;
|
||||
private string m_attsInfo;
|
||||
private string m_alignInfo;
|
||||
private bool _firstItem = true;
|
||||
private bool _firstItemLine = true;
|
||||
private string _tag;
|
||||
private string _attsInfo;
|
||||
private string _alignInfo;
|
||||
|
||||
protected int NestingDepth
|
||||
{
|
||||
get { return m_tag.Length; }
|
||||
get { return _tag.Length; }
|
||||
}
|
||||
|
||||
protected ListFormatterState(TextileFormatter formatter)
|
||||
@ -39,9 +39,9 @@ namespace Textile.States
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
m_tag = m.Groups["tag"].Value;
|
||||
m_alignInfo = m.Groups["align"].Value;
|
||||
m_attsInfo = m.Groups["atts"].Value;
|
||||
_tag = m.Groups["tag"].Value;
|
||||
_alignInfo = m.Groups["align"].Value;
|
||||
_attsInfo = m.Groups["atts"].Value;
|
||||
input = m.Groups["content"].Value;
|
||||
|
||||
this.Formatter.ChangeState(this);
|
||||
@ -51,8 +51,8 @@ namespace Textile.States
|
||||
|
||||
public sealed override void Enter()
|
||||
{
|
||||
m_firstItem = true;
|
||||
m_firstItemLine = true;
|
||||
_firstItem = true;
|
||||
_firstItemLine = true;
|
||||
WriteIndent();
|
||||
}
|
||||
|
||||
@ -64,19 +64,19 @@ namespace Textile.States
|
||||
|
||||
public sealed override void FormatLine(string input)
|
||||
{
|
||||
if (m_firstItemLine)
|
||||
if (_firstItemLine)
|
||||
{
|
||||
if (!m_firstItem)
|
||||
if (!_firstItem)
|
||||
Formatter.Output.WriteLine("</li>");
|
||||
Formatter.Output.Write("<li " + FormattedStylesAndAlignment("li") + ">");
|
||||
m_firstItemLine = false;
|
||||
_firstItemLine = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Formatter.Output.WriteLine("<br />");
|
||||
}
|
||||
Formatter.Output.Write(input);
|
||||
m_firstItem = false;
|
||||
_firstItem = false;
|
||||
}
|
||||
|
||||
public sealed override bool ShouldNestState(FormatterState other)
|
||||
@ -109,7 +109,7 @@ namespace Textile.States
|
||||
// previously (no "**" or "##" tags), or if it's
|
||||
// a new list item.
|
||||
if (IsMatchForMe(input, NestingDepth, NestingDepth))
|
||||
m_firstItemLine = true;
|
||||
_firstItemLine = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -131,7 +131,7 @@ namespace Textile.States
|
||||
|
||||
protected string FormattedStylesAndAlignment(string element)
|
||||
{
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(m_alignInfo + m_attsInfo, element);
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(_alignInfo + _attsInfo, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,64 +1,63 @@
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
[FormatterState(@"^\s*<notextile>\s*$")]
|
||||
public class NoTextileFormatterState : FormatterState
|
||||
{
|
||||
[FormatterState(@"^\s*<notextile>\s*$")]
|
||||
public class NoTextileFormatterState : FormatterState
|
||||
private bool _shouldExitNextTime = false;
|
||||
|
||||
public NoTextileFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
bool m_shouldExitNextTime = false;
|
||||
}
|
||||
|
||||
public NoTextileFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
}
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
this.Formatter.ChangeState(this);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
this.Formatter.ChangeState(this);
|
||||
return string.Empty;
|
||||
}
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public override void Enter()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
}
|
||||
public override void Exit()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
}
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
if (!_shouldExitNextTime)
|
||||
Formatter.Output.WriteLine(input);
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
if (!m_shouldExitNextTime)
|
||||
Formatter.Output.WriteLine(input);
|
||||
}
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (_shouldExitNextTime)
|
||||
return true;
|
||||
_shouldExitNextTime = Regex.IsMatch(input, @"^\s*</notextile>\s*$");
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (m_shouldExitNextTime)
|
||||
return true;
|
||||
m_shouldExitNextTime = Regex.IsMatch(input, @"^\s*</notextile>\s*$");
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldFormatBlocks(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldFormatBlocks(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldParseForNewFormatterState(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldParseForNewFormatterState(string input)
|
||||
public override Type FallbackFormattingState
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override Type FallbackFormattingState
|
||||
{
|
||||
get
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,37 +11,36 @@
|
||||
#endregion
|
||||
|
||||
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
/// <summary>
|
||||
/// Formatting state for a numbered list.
|
||||
/// </summary>
|
||||
[FormatterState(ListFormatterState.PatternBegin + @"#+" + ListFormatterState.PatternEnd)]
|
||||
public class OrderedListFormatterState : ListFormatterState
|
||||
{
|
||||
/// <summary>
|
||||
/// Formatting state for a numbered list.
|
||||
/// </summary>
|
||||
[FormatterState(ListFormatterState.PatternBegin + @"#+" + ListFormatterState.PatternEnd)]
|
||||
public class OrderedListFormatterState : ListFormatterState
|
||||
public OrderedListFormatterState(TextileFormatter formatter)
|
||||
: base(formatter)
|
||||
{
|
||||
public OrderedListFormatterState(TextileFormatter formatter)
|
||||
: base(formatter)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WriteIndent()
|
||||
{
|
||||
Formatter.Output.WriteLine("<ol" + FormattedStylesAndAlignment("ol") + ">");
|
||||
}
|
||||
protected override void WriteIndent()
|
||||
{
|
||||
Formatter.Output.WriteLine("<ol" + FormattedStylesAndAlignment("ol") + ">");
|
||||
}
|
||||
|
||||
protected override void WriteOutdent()
|
||||
{
|
||||
Formatter.Output.WriteLine("</ol>");
|
||||
}
|
||||
protected override void WriteOutdent()
|
||||
{
|
||||
Formatter.Output.WriteLine("</ol>");
|
||||
}
|
||||
|
||||
protected override bool IsMatchForMe(string input, int minNestingDepth, int maxNestingDepth)
|
||||
{
|
||||
return Regex.IsMatch(input, @"^\s*([\*#]{" + (minNestingDepth - 1) + @"," + (maxNestingDepth - 1) + @"})#" + Globals.BlockModifiersPattern + @"\s");
|
||||
}
|
||||
protected override bool IsMatchForMe(string input, int minNestingDepth, int maxNestingDepth)
|
||||
{
|
||||
return Regex.IsMatch(input, @"^\s*([\*#]{" + (minNestingDepth - 1) + @"," + (maxNestingDepth - 1) + @"})#" + Globals.BlockModifiersPattern + @"\s");
|
||||
}
|
||||
|
||||
protected override bool IsMatchForOthers(string input, int minNestingDepth, int maxNestingDepth)
|
||||
{
|
||||
return Regex.IsMatch(input, @"^\s*([\*#]{" + (minNestingDepth - 1) + @"," + (maxNestingDepth - 1) + @"})\*" + Globals.BlockModifiersPattern + @"\s");
|
||||
}
|
||||
protected override bool IsMatchForOthers(string input, int minNestingDepth, int maxNestingDepth)
|
||||
{
|
||||
return Regex.IsMatch(input, @"^\s*([\*#]{" + (minNestingDepth - 1) + @"," + (maxNestingDepth - 1) + @"})\*" + Globals.BlockModifiersPattern + @"\s");
|
||||
}
|
||||
}
|
||||
|
@ -10,45 +10,44 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
/// <summary>
|
||||
/// Formatting state for a standard text (i.e. just paragraphs).
|
||||
/// </summary>
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"p" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class ParagraphFormatterState : SimpleBlockFormatterState
|
||||
{
|
||||
/// <summary>
|
||||
/// Formatting state for a standard text (i.e. just paragraphs).
|
||||
/// </summary>
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"p" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class ParagraphFormatterState : SimpleBlockFormatterState
|
||||
public ParagraphFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
public ParagraphFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.Write("<p" + FormattedStylesAndAlignment("p") + ">");
|
||||
}
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.Write("<p" + FormattedStylesAndAlignment("p") + ">");
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</p>");
|
||||
}
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</p>");
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.Write(input);
|
||||
}
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.Write(input);
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (Regex.IsMatch(input, @"^\s*$"))
|
||||
return true;
|
||||
Formatter.Output.WriteLine("<br />");
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (Regex.IsMatch(input, @"^\s*$"))
|
||||
return true;
|
||||
Formatter.Output.WriteLine("<br />");
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,40 +1,39 @@
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
[FormatterState(@"^\s*<(h[0-9]|p|pre|blockquote)" + Globals.HtmlAttributesPattern + ">")]
|
||||
public class PassthroughFormatterState : FormatterState
|
||||
{
|
||||
[FormatterState(@"^\s*<(h[0-9]|p|pre|blockquote)" + Globals.HtmlAttributesPattern + ">")]
|
||||
public class PassthroughFormatterState : FormatterState
|
||||
public PassthroughFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
public PassthroughFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
this.Formatter.ChangeState(this);
|
||||
return input;
|
||||
}
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
this.Formatter.ChangeState(this);
|
||||
return input;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
}
|
||||
public override void Enter()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
}
|
||||
public override void Exit()
|
||||
{
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.WriteLine(input);
|
||||
}
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.WriteLine(input);
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1,59 +1,58 @@
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"bc" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class PreCodeFormatterState : SimpleBlockFormatterState
|
||||
{
|
||||
[FormatterState(SimpleBlockFormatterState.PatternBegin + @"bc" + SimpleBlockFormatterState.PatternEnd)]
|
||||
public class PreCodeFormatterState : SimpleBlockFormatterState
|
||||
public PreCodeFormatterState(TextileFormatter formatter)
|
||||
: base(formatter)
|
||||
{
|
||||
public PreCodeFormatterState(TextileFormatter formatter)
|
||||
: base(formatter)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.Write("<pre><code>");
|
||||
}
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.Write("<pre><code>");
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</code></pre>");
|
||||
}
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</code></pre>");
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.WriteLine(FixEntities(input));
|
||||
}
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
Formatter.Output.WriteLine(FixEntities(input));
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (Regex.IsMatch(input, @"^\s*$"))
|
||||
return true;
|
||||
Formatter.Output.WriteLine("<br />");
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (Regex.IsMatch(input, @"^\s*$"))
|
||||
return true;
|
||||
Formatter.Output.WriteLine("<br />");
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldFormatBlocks(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldFormatBlocks(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldParseForNewFormatterState(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public override bool ShouldParseForNewFormatterState(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private string FixEntities(string text)
|
||||
{
|
||||
// de-entify any remaining angle brackets or ampersands
|
||||
text = text.Replace("&", "&");
|
||||
text = text.Replace(">", ">");
|
||||
text = text.Replace("<", "<");
|
||||
//Regex.Replace(text, @"\b&([#a-z0-9]+;)", "x%x%");
|
||||
return text;
|
||||
}
|
||||
private string FixEntities(string text)
|
||||
{
|
||||
// de-entify any remaining angle brackets or ampersands
|
||||
text = text.Replace("&", "&");
|
||||
text = text.Replace(">", ">");
|
||||
text = text.Replace("<", "<");
|
||||
//Regex.Replace(text, @"\b&([#a-z0-9]+;)", "x%x%");
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
@ -1,75 +1,74 @@
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
[FormatterState(@"^\s*<pre" + Globals.HtmlAttributesPattern + ">")]
|
||||
public class PreFormatterState : FormatterState
|
||||
{
|
||||
[FormatterState(@"^\s*<pre" + Globals.HtmlAttributesPattern + ">")]
|
||||
public class PreFormatterState : FormatterState
|
||||
private bool _shouldExitNextTime = false;
|
||||
private int _fakeNestingDepth = 0;
|
||||
|
||||
public PreFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
bool m_shouldExitNextTime = false;
|
||||
int m_fakeNestingDepth = 0;
|
||||
|
||||
public PreFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
if (!Regex.IsMatch(input, "</pre>"))
|
||||
{
|
||||
this.Formatter.ChangeState(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Formatter.ChangeState(new PassthroughFormatterState(this.Formatter));
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
if (Regex.IsMatch(input, "<pre>"))
|
||||
m_fakeNestingDepth++;
|
||||
|
||||
Formatter.Output.WriteLine(input);
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (m_shouldExitNextTime)
|
||||
return true;
|
||||
if (Regex.IsMatch(input, @"</pre>"))
|
||||
m_fakeNestingDepth--;
|
||||
if (m_fakeNestingDepth <= 0)
|
||||
m_shouldExitNextTime = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldFormatBlocks(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldParseForNewFormatterState(string input)
|
||||
{
|
||||
// Only allow a child formatting state for <code> tag.
|
||||
return Regex.IsMatch(input, @"^\s*<code");
|
||||
}
|
||||
|
||||
public override Type FallbackFormattingState
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
if (!Regex.IsMatch(input, "</pre>"))
|
||||
{
|
||||
this.Formatter.ChangeState(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Formatter.ChangeState(new PassthroughFormatterState(this.Formatter));
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
if (Regex.IsMatch(input, "<pre>"))
|
||||
_fakeNestingDepth++;
|
||||
|
||||
Formatter.Output.WriteLine(input);
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
if (_shouldExitNextTime)
|
||||
return true;
|
||||
if (Regex.IsMatch(input, @"</pre>"))
|
||||
_fakeNestingDepth--;
|
||||
if (_fakeNestingDepth <= 0)
|
||||
_shouldExitNextTime = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldFormatBlocks(string input)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldParseForNewFormatterState(string input)
|
||||
{
|
||||
// Only allow a child formatting state for <code> tag.
|
||||
return Regex.IsMatch(input, @"^\s*<code");
|
||||
}
|
||||
|
||||
public override Type FallbackFormattingState
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
}
|
@ -1,64 +1,63 @@
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
public abstract class SimpleBlockFormatterState : FormatterState
|
||||
{
|
||||
public abstract class SimpleBlockFormatterState : FormatterState
|
||||
internal const string PatternBegin = @"^\s*(?<tag>";
|
||||
internal const string PatternEnd = @")" + Globals.AlignPattern + Globals.BlockModifiersPattern + @"\.(?:\s+)?(?<content>.*)$";
|
||||
|
||||
public string Tag { get; private set; } = null;
|
||||
|
||||
public string AlignInfo { get; private set; } = null;
|
||||
|
||||
public string AttInfo { get; private set; } = null;
|
||||
|
||||
protected SimpleBlockFormatterState(TextileFormatter formatter)
|
||||
: base(formatter)
|
||||
{
|
||||
internal const string PatternBegin = @"^\s*(?<tag>";
|
||||
internal const string PatternEnd = @")" + Globals.AlignPattern + Globals.BlockModifiersPattern + @"\.(?:\s+)?(?<content>.*)$";
|
||||
|
||||
public string Tag { get; private set; } = null;
|
||||
|
||||
public string AlignInfo { get; private set; } = null;
|
||||
|
||||
public string AttInfo { get; private set; } = null;
|
||||
|
||||
protected SimpleBlockFormatterState(TextileFormatter formatter)
|
||||
: base(formatter)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
Tag = m.Groups["tag"].Value;
|
||||
AlignInfo = m.Groups["align"].Value;
|
||||
AttInfo = m.Groups["atts"].Value;
|
||||
input = m.Groups["content"].Value;
|
||||
|
||||
OnContextAcquired();
|
||||
|
||||
this.Formatter.ChangeState(this);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
var blockFormatterState = (SimpleBlockFormatterState)other;
|
||||
return blockFormatterState.Tag != Tag ||
|
||||
blockFormatterState.AlignInfo != AlignInfo ||
|
||||
blockFormatterState.AttInfo != AttInfo;
|
||||
}
|
||||
|
||||
protected virtual void OnContextAcquired()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected string FormattedAlignment()
|
||||
{
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(AlignInfo);
|
||||
}
|
||||
|
||||
protected string FormattedStyles(string element)
|
||||
{
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(AttInfo, element);
|
||||
}
|
||||
|
||||
protected string FormattedStylesAndAlignment(string element)
|
||||
{
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(AlignInfo + AttInfo, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
Tag = m.Groups["tag"].Value;
|
||||
AlignInfo = m.Groups["align"].Value;
|
||||
AttInfo = m.Groups["atts"].Value;
|
||||
input = m.Groups["content"].Value;
|
||||
|
||||
OnContextAcquired();
|
||||
|
||||
this.Formatter.ChangeState(this);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
var blockFormatterState = (SimpleBlockFormatterState)other;
|
||||
return blockFormatterState.Tag != Tag ||
|
||||
blockFormatterState.AlignInfo != AlignInfo ||
|
||||
blockFormatterState.AttInfo != AttInfo;
|
||||
}
|
||||
|
||||
protected virtual void OnContextAcquired()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected string FormattedAlignment()
|
||||
{
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(AlignInfo);
|
||||
}
|
||||
|
||||
protected string FormattedStyles(string element)
|
||||
{
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(AttInfo, element);
|
||||
}
|
||||
|
||||
protected string FormattedStylesAndAlignment(string element)
|
||||
{
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(AlignInfo + AttInfo, element);
|
||||
}
|
||||
}
|
@ -1,45 +1,44 @@
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
public class TableCellParser
|
||||
{
|
||||
public class TableCellParser
|
||||
readonly string _lineFragment;
|
||||
|
||||
public TableCellParser(string input)
|
||||
{
|
||||
readonly string m_lineFragment;
|
||||
|
||||
public TableCellParser(string input)
|
||||
{
|
||||
m_lineFragment = input;
|
||||
}
|
||||
|
||||
public string GetLineFragmentFormatting()
|
||||
{
|
||||
var htmlTag = "td";
|
||||
|
||||
var m = Regex.Match(m_lineFragment,
|
||||
@"^((?<head>_?)" +
|
||||
Globals.SpanPattern +
|
||||
Globals.AlignPattern +
|
||||
Globals.BlockModifiersPattern +
|
||||
@"(?<dot>\.)\s?)?" +
|
||||
@"(?<content>.*)"
|
||||
);
|
||||
if (!m.Success)
|
||||
throw new Exception("Couldn't parse table cell.");
|
||||
|
||||
if (m.Groups["head"].Value == "_")
|
||||
htmlTag = "th";
|
||||
//string opts = BlockAttributesParser.ParseBlockAttributes(m.Groups["span"].Value, "td") +
|
||||
// BlockAttributesParser.ParseBlockAttributes(m.Groups["align"].Value, "td") +
|
||||
// BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, "td");
|
||||
var opts = Blocks.BlockAttributesParser.ParseBlockAttributes(m.Groups["span"].Value + m.Groups["align"].Value + m.Groups["atts"].Value, "td");
|
||||
|
||||
var res = "<" + htmlTag + opts + ">";
|
||||
// It may be possible the user actually intended to have a dot at the beginning of
|
||||
// this cell's text, without any formatting (header tag or options).
|
||||
if (string.IsNullOrEmpty(opts) && htmlTag == "td" && !string.IsNullOrEmpty(m.Groups["dot"].Value))
|
||||
res += ".";
|
||||
res += m.Groups["content"].Value;
|
||||
res += "</" + htmlTag + ">";
|
||||
|
||||
return res;
|
||||
}
|
||||
_lineFragment = input;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetLineFragmentFormatting()
|
||||
{
|
||||
var htmlTag = "td";
|
||||
|
||||
var m = Regex.Match(_lineFragment,
|
||||
@"^((?<head>_?)" +
|
||||
Globals.SpanPattern +
|
||||
Globals.AlignPattern +
|
||||
Globals.BlockModifiersPattern +
|
||||
@"(?<dot>\.)\s?)?" +
|
||||
@"(?<content>.*)"
|
||||
);
|
||||
if (!m.Success)
|
||||
throw new Exception("Couldn't parse table cell.");
|
||||
|
||||
if (m.Groups["head"].Value == "_")
|
||||
htmlTag = "th";
|
||||
//string opts = BlockAttributesParser.ParseBlockAttributes(m.Groups["span"].Value, "td") +
|
||||
// BlockAttributesParser.ParseBlockAttributes(m.Groups["align"].Value, "td") +
|
||||
// BlockAttributesParser.ParseBlockAttributes(m.Groups["atts"].Value, "td");
|
||||
var opts = Blocks.BlockAttributesParser.ParseBlockAttributes(m.Groups["span"].Value + m.Groups["align"].Value + m.Groups["atts"].Value, "td");
|
||||
|
||||
var res = "<" + htmlTag + opts + ">";
|
||||
// It may be possible the user actually intended to have a dot at the beginning of
|
||||
// this cell's text, without any formatting (header tag or options).
|
||||
if (string.IsNullOrEmpty(opts) && htmlTag == "td" && !string.IsNullOrEmpty(m.Groups["dot"].Value))
|
||||
res += ".";
|
||||
res += m.Groups["content"].Value;
|
||||
res += "</" + htmlTag + ">";
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
@ -1,65 +1,64 @@
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
[FormatterState(@"^\s*(?<tag>table)" +
|
||||
Globals.SpanPattern +
|
||||
Globals.AlignPattern +
|
||||
Globals.BlockModifiersPattern +
|
||||
@"\.\s*$")]
|
||||
public class TableFormatterState : FormatterState
|
||||
{
|
||||
[FormatterState(@"^\s*(?<tag>table)" +
|
||||
Globals.SpanPattern +
|
||||
Globals.AlignPattern +
|
||||
Globals.BlockModifiersPattern +
|
||||
@"\.\s*$")]
|
||||
public class TableFormatterState : FormatterState
|
||||
private string _attsInfo;
|
||||
private string _alignInfo;
|
||||
|
||||
public TableFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
private string m_attsInfo;
|
||||
private string m_alignInfo;
|
||||
}
|
||||
|
||||
public TableFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
}
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
_alignInfo = m.Groups["align"].Value;
|
||||
_attsInfo = m.Groups["atts"].Value;
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
m_alignInfo = m.Groups["align"].Value;
|
||||
m_attsInfo = m.Groups["atts"].Value;
|
||||
//TODO: check the state (it could already be a table!)
|
||||
this.Formatter.ChangeState(this);
|
||||
|
||||
//TODO: check the state (it could already be a table!)
|
||||
this.Formatter.ChangeState(this);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.WriteLine("<table" + FormattedStylesAndAlignment() + ">");
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.WriteLine("<table" + FormattedStylesAndAlignment() + ">");
|
||||
}
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</table>");
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</table>");
|
||||
}
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
if (input.Length > 0)
|
||||
throw new Exception("The TableFormatter state is not supposed to format any lines!");
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
if (input.Length > 0)
|
||||
throw new Exception("The TableFormatter state is not supposed to format any lines!");
|
||||
}
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
var m = Regex.Match(input,
|
||||
@"^\s*" + Globals.AlignPattern + Globals.BlockModifiersPattern +
|
||||
@"(\.\s?)?(?<tag>\|)" +
|
||||
@"(?<content>.*)(?=\|)"
|
||||
);
|
||||
return !m.Success;
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
var m = Regex.Match(input,
|
||||
@"^\s*" + Globals.AlignPattern + Globals.BlockModifiersPattern +
|
||||
@"(\.\s?)?(?<tag>\|)" +
|
||||
@"(?<content>.*)(?=\|)"
|
||||
);
|
||||
return !m.Success;
|
||||
}
|
||||
|
||||
protected string FormattedStylesAndAlignment()
|
||||
{
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(m_alignInfo + m_attsInfo);
|
||||
}
|
||||
protected string FormattedStylesAndAlignment()
|
||||
{
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(_alignInfo + _attsInfo);
|
||||
}
|
||||
}
|
||||
|
@ -1,73 +1,72 @@
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
[FormatterState(@"^\s*(" + Globals.AlignPattern + Globals.BlockModifiersPattern + @"\.\s?)?" +
|
||||
@"\|(?<content>.*)\|\s*$")]
|
||||
public class TableRowFormatterState : FormatterState
|
||||
{
|
||||
[FormatterState(@"^\s*(" + Globals.AlignPattern + Globals.BlockModifiersPattern + @"\.\s?)?" +
|
||||
@"\|(?<content>.*)\|\s*$")]
|
||||
public class TableRowFormatterState : FormatterState
|
||||
private string _attsInfo;
|
||||
private string _alignInfo;
|
||||
|
||||
public TableRowFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
private string m_attsInfo;
|
||||
private string m_alignInfo;
|
||||
|
||||
public TableRowFormatterState(TextileFormatter f)
|
||||
: base(f)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
m_alignInfo = m.Groups["align"].Value;
|
||||
m_attsInfo = m.Groups["atts"].Value;
|
||||
input = "|" + m.Groups["content"].Value + "|";
|
||||
|
||||
if (!(this.Formatter.CurrentState is TableFormatterState))
|
||||
{
|
||||
var s = new TableFormatterState(this.Formatter);
|
||||
this.Formatter.ChangeState(s);
|
||||
}
|
||||
|
||||
this.Formatter.ChangeState(this);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.WriteLine("<tr" + FormattedStylesAndAlignment() + ">");
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</tr>");
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
// can get: Align & Classes
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var cellsInput = input.Split('|');
|
||||
for (var i = 1; i < cellsInput.Length - 1; i++)
|
||||
{
|
||||
var cellInput = cellsInput[i];
|
||||
var tcp = new TableCellParser(cellInput);
|
||||
sb.Append(tcp.GetLineFragmentFormatting());
|
||||
}
|
||||
|
||||
Formatter.Output.WriteLine(sb.ToString());
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected string FormattedStylesAndAlignment()
|
||||
{
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(m_alignInfo + m_attsInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string Consume(string input, Match m)
|
||||
{
|
||||
_alignInfo = m.Groups["align"].Value;
|
||||
_attsInfo = m.Groups["atts"].Value;
|
||||
input = "|" + m.Groups["content"].Value + "|";
|
||||
|
||||
if (!(this.Formatter.CurrentState is TableFormatterState))
|
||||
{
|
||||
var s = new TableFormatterState(this.Formatter);
|
||||
this.Formatter.ChangeState(s);
|
||||
}
|
||||
|
||||
this.Formatter.ChangeState(this);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
public override bool ShouldNestState(FormatterState other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Enter()
|
||||
{
|
||||
Formatter.Output.WriteLine("<tr" + FormattedStylesAndAlignment() + ">");
|
||||
}
|
||||
|
||||
public override void Exit()
|
||||
{
|
||||
Formatter.Output.WriteLine("</tr>");
|
||||
}
|
||||
|
||||
public override void FormatLine(string input)
|
||||
{
|
||||
// can get: Align & Classes
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var cellsInput = input.Split('|');
|
||||
for (var i = 1; i < cellsInput.Length - 1; i++)
|
||||
{
|
||||
var cellInput = cellsInput[i];
|
||||
var tcp = new TableCellParser(cellInput);
|
||||
sb.Append(tcp.GetLineFragmentFormatting());
|
||||
}
|
||||
|
||||
Formatter.Output.WriteLine(sb.ToString());
|
||||
}
|
||||
|
||||
public override bool ShouldExit(string input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected string FormattedStylesAndAlignment()
|
||||
{
|
||||
return Blocks.BlockAttributesParser.ParseBlockAttributes(_alignInfo + _attsInfo);
|
||||
}
|
||||
}
|
@ -10,37 +10,36 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile.States
|
||||
namespace Textile.States;
|
||||
|
||||
/// <summary>
|
||||
/// Formatting state for a bulleted list.
|
||||
/// </summary>
|
||||
[FormatterState(ListFormatterState.PatternBegin + @"\*+" + ListFormatterState.PatternEnd)]
|
||||
public class UnorderedListFormatterState : ListFormatterState
|
||||
{
|
||||
/// <summary>
|
||||
/// Formatting state for a bulleted list.
|
||||
/// </summary>
|
||||
[FormatterState(ListFormatterState.PatternBegin + @"\*+" + ListFormatterState.PatternEnd)]
|
||||
public class UnorderedListFormatterState : ListFormatterState
|
||||
public UnorderedListFormatterState(TextileFormatter formatter)
|
||||
: base(formatter)
|
||||
{
|
||||
public UnorderedListFormatterState(TextileFormatter formatter)
|
||||
: base(formatter)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void WriteIndent()
|
||||
{
|
||||
Formatter.Output.WriteLine("<ul" + FormattedStylesAndAlignment("ul") + ">");
|
||||
}
|
||||
|
||||
protected override void WriteOutdent()
|
||||
{
|
||||
Formatter.Output.WriteLine("</ul>");
|
||||
}
|
||||
|
||||
protected override bool IsMatchForMe(string input, int minNestingDepth, int maxNestingDepth)
|
||||
{
|
||||
return Regex.IsMatch(input, @"^\s*[\*]{" + minNestingDepth + @"," + maxNestingDepth + @"}" + Globals.BlockModifiersPattern + @"\s");
|
||||
}
|
||||
|
||||
protected override bool IsMatchForOthers(string input, int minNestingDepth, int maxNestingDepth)
|
||||
{
|
||||
return Regex.IsMatch(input, @"^\s*[#]{" + minNestingDepth + @"," + maxNestingDepth + @"}" + Globals.BlockModifiersPattern + @"\s");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WriteIndent()
|
||||
{
|
||||
Formatter.Output.WriteLine("<ul" + FormattedStylesAndAlignment("ul") + ">");
|
||||
}
|
||||
|
||||
protected override void WriteOutdent()
|
||||
{
|
||||
Formatter.Output.WriteLine("</ul>");
|
||||
}
|
||||
|
||||
protected override bool IsMatchForMe(string input, int minNestingDepth, int maxNestingDepth)
|
||||
{
|
||||
return Regex.IsMatch(input, @"^\s*[\*]{" + minNestingDepth + @"," + maxNestingDepth + @"}" + Globals.BlockModifiersPattern + @"\s");
|
||||
}
|
||||
|
||||
protected override bool IsMatchForOthers(string input, int minNestingDepth, int maxNestingDepth)
|
||||
{
|
||||
return Regex.IsMatch(input, @"^\s*[#]{" + minNestingDepth + @"," + maxNestingDepth + @"}" + Globals.BlockModifiersPattern + @"\s");
|
||||
}
|
||||
}
|
@ -11,42 +11,41 @@
|
||||
#endregion
|
||||
|
||||
|
||||
namespace Textile
|
||||
namespace Textile;
|
||||
|
||||
public class StringBuilderTextileFormatter : IOutputter
|
||||
{
|
||||
public class StringBuilderTextileFormatter : IOutputter
|
||||
private StringBuilder _stringBuilder = null;
|
||||
|
||||
public StringBuilderTextileFormatter()
|
||||
{
|
||||
StringBuilder m_stringBuilder = null;
|
||||
|
||||
public StringBuilderTextileFormatter()
|
||||
{
|
||||
}
|
||||
|
||||
public string GetFormattedText()
|
||||
{
|
||||
return m_stringBuilder.ToString();
|
||||
}
|
||||
|
||||
#region IOutputter Members
|
||||
|
||||
public void Begin()
|
||||
{
|
||||
m_stringBuilder = new StringBuilder();
|
||||
}
|
||||
|
||||
public void End()
|
||||
{
|
||||
}
|
||||
|
||||
public void Write(string text)
|
||||
{
|
||||
m_stringBuilder.Append(text);
|
||||
}
|
||||
|
||||
public void WriteLine(string line)
|
||||
{
|
||||
m_stringBuilder.AppendLine(line);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public string GetFormattedText()
|
||||
{
|
||||
return _stringBuilder.ToString();
|
||||
}
|
||||
|
||||
#region IOutputter Members
|
||||
|
||||
public void Begin()
|
||||
{
|
||||
_stringBuilder = new StringBuilder();
|
||||
}
|
||||
|
||||
public void End()
|
||||
{
|
||||
}
|
||||
|
||||
public void Write(string text)
|
||||
{
|
||||
_stringBuilder.Append(text);
|
||||
}
|
||||
|
||||
public void WriteLine(string line)
|
||||
{
|
||||
_stringBuilder.AppendLine(line);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@ -1,28 +1,26 @@
|
||||
namespace Textile
|
||||
namespace Textile;
|
||||
|
||||
public class StyleReader
|
||||
{
|
||||
public class StyleReader
|
||||
private readonly Regex _styleParser = new Regex(@"(?<selector>[^\{]+)(?<style>[^\}]+)");
|
||||
private readonly Regex _minimizer = new Regex(@";\s+");
|
||||
private readonly System.Collections.Specialized.StringDictionary _tagStyler = new System.Collections.Specialized.StringDictionary();
|
||||
|
||||
public StyleReader(string styles)
|
||||
{
|
||||
private readonly Regex _styleParser = new Regex(@"(?<selector>[^\{]+)(?<style>[^\}]+)");
|
||||
private readonly Regex _minimizer = new Regex(@";\s+");
|
||||
|
||||
private readonly System.Collections.Specialized.StringDictionary _tagStyler = new System.Collections.Specialized.StringDictionary();
|
||||
|
||||
public StyleReader(string styles)
|
||||
//Read it
|
||||
var matches = _styleParser.Matches(styles.Replace(System.Environment.NewLine, ""));
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
//Read it
|
||||
var matches = _styleParser.Matches(styles.Replace(System.Environment.NewLine, ""));
|
||||
foreach (Match match in matches)
|
||||
if (match.Success)
|
||||
{
|
||||
if (match.Success)
|
||||
{
|
||||
_tagStyler.Add(match.Groups["selector"].Value.Trim('{', '}', ' '), _minimizer.Replace(match.Groups["style"].Value.Trim('{', '}', ' '), ";"));
|
||||
}
|
||||
_tagStyler.Add(match.Groups["selector"].Value.Trim('{', '}', ' '), _minimizer.Replace(match.Groups["style"].Value.Trim('{', '}', ' '), ";"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GetStyle(string tag)
|
||||
{
|
||||
return _tagStyler[tag];
|
||||
}
|
||||
public string GetStyle(string tag)
|
||||
{
|
||||
return _tagStyler[tag];
|
||||
}
|
||||
}
|
@ -12,160 +12,159 @@
|
||||
|
||||
|
||||
|
||||
namespace Textile
|
||||
namespace Textile;
|
||||
|
||||
/// <summary>
|
||||
/// Class for formatting Textile input into HTML.
|
||||
/// </summary>
|
||||
/// This class takes raw Textile text and sends the
|
||||
/// formatted, ready to display HTML string to the
|
||||
/// outputter defined in the constructor of the
|
||||
/// class.
|
||||
public partial class TextileFormatter
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for formatting Textile input into HTML.
|
||||
/// </summary>
|
||||
/// This class takes raw Textile text and sends the
|
||||
/// formatted, ready to display HTML string to the
|
||||
/// outputter defined in the constructor of the
|
||||
/// class.
|
||||
public partial class TextileFormatter
|
||||
static TextileFormatter()
|
||||
{
|
||||
static TextileFormatter()
|
||||
{
|
||||
RegisterFormatterState(typeof(HeaderFormatterState));
|
||||
RegisterFormatterState(typeof(PaddingFormatterState));
|
||||
RegisterFormatterState(typeof(BlockQuoteFormatterState));
|
||||
RegisterFormatterState(typeof(ParagraphFormatterState));
|
||||
RegisterFormatterState(typeof(FootNoteFormatterState));
|
||||
RegisterFormatterState(typeof(OrderedListFormatterState));
|
||||
RegisterFormatterState(typeof(UnorderedListFormatterState));
|
||||
RegisterFormatterState(typeof(TableFormatterState));
|
||||
RegisterFormatterState(typeof(TableRowFormatterState));
|
||||
RegisterFormatterState(typeof(CodeFormatterState));
|
||||
RegisterFormatterState(typeof(PreFormatterState));
|
||||
RegisterFormatterState(typeof(PreCodeFormatterState));
|
||||
RegisterFormatterState(typeof(NoTextileFormatterState));
|
||||
RegisterFormatterState(typeof(HeaderFormatterState));
|
||||
RegisterFormatterState(typeof(PaddingFormatterState));
|
||||
RegisterFormatterState(typeof(BlockQuoteFormatterState));
|
||||
RegisterFormatterState(typeof(ParagraphFormatterState));
|
||||
RegisterFormatterState(typeof(FootNoteFormatterState));
|
||||
RegisterFormatterState(typeof(OrderedListFormatterState));
|
||||
RegisterFormatterState(typeof(UnorderedListFormatterState));
|
||||
RegisterFormatterState(typeof(TableFormatterState));
|
||||
RegisterFormatterState(typeof(TableRowFormatterState));
|
||||
RegisterFormatterState(typeof(CodeFormatterState));
|
||||
RegisterFormatterState(typeof(PreFormatterState));
|
||||
RegisterFormatterState(typeof(PreCodeFormatterState));
|
||||
RegisterFormatterState(typeof(NoTextileFormatterState));
|
||||
|
||||
RegisterBlockModifier(new NoTextileBlockModifier());
|
||||
RegisterBlockModifier(new CodeBlockModifier());
|
||||
RegisterBlockModifier(new PreBlockModifier());
|
||||
RegisterBlockModifier(new HyperLinkBlockModifier());
|
||||
RegisterBlockModifier(new ImageBlockModifier());
|
||||
RegisterBlockModifier(new GlyphBlockModifier());
|
||||
RegisterBlockModifier(new EmphasisPhraseBlockModifier());
|
||||
RegisterBlockModifier(new StrongPhraseBlockModifier());
|
||||
RegisterBlockModifier(new ItalicPhraseBlockModifier());
|
||||
RegisterBlockModifier(new BoldPhraseBlockModifier());
|
||||
RegisterBlockModifier(new CitePhraseBlockModifier());
|
||||
RegisterBlockModifier(new DeletedPhraseBlockModifier());
|
||||
RegisterBlockModifier(new InsertedPhraseBlockModifier());
|
||||
RegisterBlockModifier(new SuperScriptPhraseBlockModifier());
|
||||
RegisterBlockModifier(new SubScriptPhraseBlockModifier());
|
||||
RegisterBlockModifier(new SpanPhraseBlockModifier());
|
||||
RegisterBlockModifier(new FootNoteReferenceBlockModifier());
|
||||
RegisterBlockModifier(new NoTextileBlockModifier());
|
||||
RegisterBlockModifier(new CodeBlockModifier());
|
||||
RegisterBlockModifier(new PreBlockModifier());
|
||||
RegisterBlockModifier(new HyperLinkBlockModifier());
|
||||
RegisterBlockModifier(new ImageBlockModifier());
|
||||
RegisterBlockModifier(new GlyphBlockModifier());
|
||||
RegisterBlockModifier(new EmphasisPhraseBlockModifier());
|
||||
RegisterBlockModifier(new StrongPhraseBlockModifier());
|
||||
RegisterBlockModifier(new ItalicPhraseBlockModifier());
|
||||
RegisterBlockModifier(new BoldPhraseBlockModifier());
|
||||
RegisterBlockModifier(new CitePhraseBlockModifier());
|
||||
RegisterBlockModifier(new DeletedPhraseBlockModifier());
|
||||
RegisterBlockModifier(new InsertedPhraseBlockModifier());
|
||||
RegisterBlockModifier(new SuperScriptPhraseBlockModifier());
|
||||
RegisterBlockModifier(new SubScriptPhraseBlockModifier());
|
||||
RegisterBlockModifier(new SpanPhraseBlockModifier());
|
||||
RegisterBlockModifier(new FootNoteReferenceBlockModifier());
|
||||
|
||||
//TODO: capitals block modifier
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor, where the formatter is hooked up
|
||||
/// to an outputter.
|
||||
/// </summary>
|
||||
/// <param name="output">The outputter to be used.</param>
|
||||
public TextileFormatter(IOutputter output)
|
||||
{
|
||||
Output = output;
|
||||
}
|
||||
|
||||
#region Properties for Output
|
||||
|
||||
/// <summary>
|
||||
/// The ouputter to which the formatted text
|
||||
/// is sent to.
|
||||
/// </summary>
|
||||
public IOutputter Output { get; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The offset for the header tags.
|
||||
/// </summary>
|
||||
/// When the formatted text is inserted into another page
|
||||
/// there might be a need to offset all headers (h1 becomes
|
||||
/// h4, for instance). The header offset allows this.
|
||||
public int HeaderOffset { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties for Conversion
|
||||
|
||||
public bool FormatImages
|
||||
{
|
||||
get { return IsBlockModifierEnabled(typeof(ImageBlockModifier)); }
|
||||
set { SwitchBlockModifier(typeof(ImageBlockModifier), value); }
|
||||
}
|
||||
|
||||
public bool FormatLinks
|
||||
{
|
||||
get { return IsBlockModifierEnabled(typeof(HyperLinkBlockModifier)); }
|
||||
set { SwitchBlockModifier(typeof(HyperLinkBlockModifier), value); }
|
||||
}
|
||||
|
||||
public bool FormatLists
|
||||
{
|
||||
get { return IsBlockModifierEnabled(typeof(OrderedListFormatterState)); }
|
||||
set
|
||||
{
|
||||
SwitchBlockModifier(typeof(OrderedListFormatterState), value);
|
||||
SwitchBlockModifier(typeof(UnorderedListFormatterState), value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool FormatFootNotes
|
||||
{
|
||||
get { return IsBlockModifierEnabled(typeof(FootNoteReferenceBlockModifier)); }
|
||||
set
|
||||
{
|
||||
SwitchBlockModifier(typeof(FootNoteReferenceBlockModifier), value);
|
||||
SwitchFormatterState(typeof(FootNoteFormatterState), value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool FormatTables
|
||||
{
|
||||
get { return IsFormatterStateEnabled(typeof(TableFormatterState)); }
|
||||
set
|
||||
{
|
||||
SwitchFormatterState(typeof(TableFormatterState), value);
|
||||
SwitchFormatterState(typeof(TableRowFormatterState), value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attribute to add to all links.
|
||||
/// </summary>
|
||||
public string Rel { get; set; } = string.Empty;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utility Methods
|
||||
|
||||
/// <summary>
|
||||
/// Utility method for quickly formatting a text without having
|
||||
/// to create a TextileFormatter with an IOutputter.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to format</param>
|
||||
/// <returns>The formatted version of the string</returns>
|
||||
public static string FormatString(string input)
|
||||
{
|
||||
var s = new StringBuilderTextileFormatter();
|
||||
var f = new TextileFormatter(s);
|
||||
f.Format(input);
|
||||
return s.GetFormattedText();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method for formatting a text with a given outputter.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to format</param>
|
||||
/// <param name="outputter">The IOutputter to use</param>
|
||||
public static void FormatString(string input, IOutputter outputter)
|
||||
{
|
||||
var f = new TextileFormatter(outputter);
|
||||
f.Format(input);
|
||||
}
|
||||
|
||||
#endregion
|
||||
//TODO: capitals block modifier
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor, where the formatter is hooked up
|
||||
/// to an outputter.
|
||||
/// </summary>
|
||||
/// <param name="output">The outputter to be used.</param>
|
||||
public TextileFormatter(IOutputter output)
|
||||
{
|
||||
Output = output;
|
||||
}
|
||||
|
||||
#region Properties for Output
|
||||
|
||||
/// <summary>
|
||||
/// The ouputter to which the formatted text
|
||||
/// is sent to.
|
||||
/// </summary>
|
||||
public IOutputter Output { get; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The offset for the header tags.
|
||||
/// </summary>
|
||||
/// When the formatted text is inserted into another page
|
||||
/// there might be a need to offset all headers (h1 becomes
|
||||
/// h4, for instance). The header offset allows this.
|
||||
public int HeaderOffset { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties for Conversion
|
||||
|
||||
public bool FormatImages
|
||||
{
|
||||
get { return IsBlockModifierEnabled(typeof(ImageBlockModifier)); }
|
||||
set { SwitchBlockModifier(typeof(ImageBlockModifier), value); }
|
||||
}
|
||||
|
||||
public bool FormatLinks
|
||||
{
|
||||
get { return IsBlockModifierEnabled(typeof(HyperLinkBlockModifier)); }
|
||||
set { SwitchBlockModifier(typeof(HyperLinkBlockModifier), value); }
|
||||
}
|
||||
|
||||
public bool FormatLists
|
||||
{
|
||||
get { return IsBlockModifierEnabled(typeof(OrderedListFormatterState)); }
|
||||
set
|
||||
{
|
||||
SwitchBlockModifier(typeof(OrderedListFormatterState), value);
|
||||
SwitchBlockModifier(typeof(UnorderedListFormatterState), value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool FormatFootNotes
|
||||
{
|
||||
get { return IsBlockModifierEnabled(typeof(FootNoteReferenceBlockModifier)); }
|
||||
set
|
||||
{
|
||||
SwitchBlockModifier(typeof(FootNoteReferenceBlockModifier), value);
|
||||
SwitchFormatterState(typeof(FootNoteFormatterState), value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool FormatTables
|
||||
{
|
||||
get { return IsFormatterStateEnabled(typeof(TableFormatterState)); }
|
||||
set
|
||||
{
|
||||
SwitchFormatterState(typeof(TableFormatterState), value);
|
||||
SwitchFormatterState(typeof(TableRowFormatterState), value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attribute to add to all links.
|
||||
/// </summary>
|
||||
public string Rel { get; set; } = string.Empty;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utility Methods
|
||||
|
||||
/// <summary>
|
||||
/// Utility method for quickly formatting a text without having
|
||||
/// to create a TextileFormatter with an IOutputter.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to format</param>
|
||||
/// <returns>The formatted version of the string</returns>
|
||||
public static string FormatString(string input)
|
||||
{
|
||||
var s = new StringBuilderTextileFormatter();
|
||||
var f = new TextileFormatter(s);
|
||||
f.Format(input);
|
||||
return s.GetFormattedText();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method for formatting a text with a given outputter.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to format</param>
|
||||
/// <param name="outputter">The IOutputter to use</param>
|
||||
public static void FormatString(string input, IOutputter outputter)
|
||||
{
|
||||
var f = new TextileFormatter(outputter);
|
||||
f.Format(input);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@ -11,40 +11,39 @@
|
||||
#endregion
|
||||
|
||||
|
||||
namespace Textile
|
||||
namespace Textile;
|
||||
|
||||
public partial class TextileFormatter
|
||||
{
|
||||
public partial class TextileFormatter
|
||||
#region Block Modifiers Registration
|
||||
|
||||
private static readonly List<BlockModifier> _blockModifiers = new List<BlockModifier>();
|
||||
private static readonly List<Type> _blockModifiersTypes = new List<Type>();
|
||||
|
||||
public static void RegisterBlockModifier(BlockModifier blockModifer)
|
||||
{
|
||||
#region Block Modifiers Registration
|
||||
|
||||
private static readonly List<BlockModifier> s_blockModifiers = new List<BlockModifier>();
|
||||
private static readonly List<Type> s_blockModifiersTypes = new List<Type>();
|
||||
|
||||
public static void RegisterBlockModifier(BlockModifier blockModifer)
|
||||
{
|
||||
s_blockModifiers.Add(blockModifer);
|
||||
s_blockModifiersTypes.Add(blockModifer.GetType());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Block Modifiers Management
|
||||
|
||||
private readonly List<Type> m_disabledBlockModifiers = new List<Type>();
|
||||
|
||||
public bool IsBlockModifierEnabled(Type type)
|
||||
{
|
||||
return !m_disabledBlockModifiers.Contains(type);
|
||||
}
|
||||
|
||||
public void SwitchBlockModifier(Type type, bool onOff)
|
||||
{
|
||||
if (onOff)
|
||||
m_disabledBlockModifiers.Remove(type);
|
||||
else if (!m_disabledBlockModifiers.Contains(type))
|
||||
m_disabledBlockModifiers.Add(type);
|
||||
}
|
||||
|
||||
#endregion
|
||||
_blockModifiers.Add(blockModifer);
|
||||
_blockModifiersTypes.Add(blockModifer.GetType());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Block Modifiers Management
|
||||
|
||||
private readonly List<Type> _disabledBlockModifiers = new List<Type>();
|
||||
|
||||
public bool IsBlockModifierEnabled(Type type)
|
||||
{
|
||||
return !_disabledBlockModifiers.Contains(type);
|
||||
}
|
||||
|
||||
public void SwitchBlockModifier(Type type, bool onOff)
|
||||
{
|
||||
if (onOff)
|
||||
_disabledBlockModifiers.Remove(type);
|
||||
else if (!_disabledBlockModifiers.Contains(type))
|
||||
_disabledBlockModifiers.Add(type);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@ -11,108 +11,107 @@
|
||||
#endregion
|
||||
|
||||
|
||||
namespace Textile
|
||||
namespace Textile;
|
||||
|
||||
public partial class TextileFormatter
|
||||
{
|
||||
public partial class TextileFormatter
|
||||
private readonly Regex _velocityArguments =
|
||||
new Regex("nostyle(?<arg>.*?)/nostyle", RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
private string ArgMatchReplace(Match match)
|
||||
{
|
||||
private readonly Regex VelocityArguments =
|
||||
new Regex("nostyle(?<arg>.*?)/nostyle", RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
return match.Result("${arg}");
|
||||
}
|
||||
|
||||
private string ArgMatchReplace(Match match)
|
||||
#region Formatting Methods
|
||||
|
||||
/// <summary>
|
||||
/// Formats the given text.
|
||||
/// </summary>
|
||||
/// <param name="input">The text to format.</param>
|
||||
public void Format(string input)
|
||||
{
|
||||
Output.Begin();
|
||||
|
||||
// Clean the text...
|
||||
var str = PrepareInputForFormatting(input);
|
||||
// ...and format each line.
|
||||
foreach (var line in str.Split('\n'))
|
||||
{
|
||||
return match.Result("${arg}");
|
||||
}
|
||||
var tmp = line;
|
||||
|
||||
#region Formatting Methods
|
||||
|
||||
/// <summary>
|
||||
/// Formats the given text.
|
||||
/// </summary>
|
||||
/// <param name="input">The text to format.</param>
|
||||
public void Format(string input)
|
||||
{
|
||||
Output.Begin();
|
||||
|
||||
// Clean the text...
|
||||
var str = PrepareInputForFormatting(input);
|
||||
// ...and format each line.
|
||||
foreach (var line in str.Split('\n'))
|
||||
{
|
||||
var tmp = line;
|
||||
|
||||
// Let's see if the current state(s) is(are) finished...
|
||||
while (CurrentState != null && CurrentState.ShouldExit(tmp))
|
||||
PopState();
|
||||
|
||||
if (!Regex.IsMatch(tmp, @"^\s*$"))
|
||||
{
|
||||
// Figure out the new state for this text line, if possible.
|
||||
if (CurrentState == null || CurrentState.ShouldParseForNewFormatterState(tmp))
|
||||
{
|
||||
tmp = HandleFormattingState(tmp);
|
||||
}
|
||||
// else, the current state doesn't want to be superceded by
|
||||
// a new one. We'll leave him be.
|
||||
|
||||
// Modify the line with our block modifiers.
|
||||
if (CurrentState == null || CurrentState.ShouldFormatBlocks(tmp))
|
||||
{
|
||||
foreach (var blockModifier in s_blockModifiers)
|
||||
{
|
||||
//TODO: if not disabled...
|
||||
tmp = blockModifier.ModifyLine(tmp);
|
||||
}
|
||||
|
||||
for (var i = s_blockModifiers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var blockModifier = s_blockModifiers[i];
|
||||
tmp = blockModifier.Conclude(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
tmp = VelocityArguments.Replace(tmp, ArgMatchReplace);
|
||||
|
||||
// Format the current line.
|
||||
CurrentState.FormatLine(tmp);
|
||||
}
|
||||
}
|
||||
// We're done. There might be a few states still on
|
||||
// the stack (for example if the text ends with a nested
|
||||
// list), so we must pop them all so that they have
|
||||
// their "Exit" method called correctly.
|
||||
while (m_stackOfStates.Count > 0)
|
||||
// Let's see if the current state(s) is(are) finished...
|
||||
while (CurrentState != null && CurrentState.ShouldExit(tmp))
|
||||
PopState();
|
||||
|
||||
Output.End();
|
||||
if (!Regex.IsMatch(tmp, @"^\s*$"))
|
||||
{
|
||||
// Figure out the new state for this text line, if possible.
|
||||
if (CurrentState == null || CurrentState.ShouldParseForNewFormatterState(tmp))
|
||||
{
|
||||
tmp = HandleFormattingState(tmp);
|
||||
}
|
||||
// else, the current state doesn't want to be superceded by
|
||||
// a new one. We'll leave him be.
|
||||
|
||||
// Modify the line with our block modifiers.
|
||||
if (CurrentState == null || CurrentState.ShouldFormatBlocks(tmp))
|
||||
{
|
||||
foreach (var blockModifier in _blockModifiers)
|
||||
{
|
||||
//TODO: if not disabled...
|
||||
tmp = blockModifier.ModifyLine(tmp);
|
||||
}
|
||||
|
||||
for (var i = _blockModifiers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var blockModifier = _blockModifiers[i];
|
||||
tmp = blockModifier.Conclude(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
tmp = _velocityArguments.Replace(tmp, ArgMatchReplace);
|
||||
|
||||
// Format the current line.
|
||||
CurrentState.FormatLine(tmp);
|
||||
}
|
||||
}
|
||||
// We're done. There might be a few states still on
|
||||
// the stack (for example if the text ends with a nested
|
||||
// list), so we must pop them all so that they have
|
||||
// their "Exit" method called correctly.
|
||||
while (_stackOfStates.Count > 0)
|
||||
PopState();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Preparation Methods
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up a text before formatting.
|
||||
/// </summary>
|
||||
/// <param name="input">The text to clean up.</param>
|
||||
/// <returns>The clean text.</returns>
|
||||
/// This method cleans stuff like line endings, so that
|
||||
/// we don't have to bother with it while formatting.
|
||||
private string PrepareInputForFormatting(string input)
|
||||
{
|
||||
input = CleanWhiteSpace(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
private string CleanWhiteSpace(string text)
|
||||
{
|
||||
text = text.Replace("\r\n", "\n");
|
||||
text = text.Replace("\t", "");
|
||||
text = Regex.Replace(text, @"\n{3,}", "\n\n");
|
||||
text = Regex.Replace(text, @"\n *\n", "\n\n");
|
||||
text = Regex.Replace(text, "\"$", "\" ");
|
||||
return text;
|
||||
}
|
||||
|
||||
#endregion
|
||||
Output.End();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Preparation Methods
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up a text before formatting.
|
||||
/// </summary>
|
||||
/// <param name="input">The text to clean up.</param>
|
||||
/// <returns>The clean text.</returns>
|
||||
/// This method cleans stuff like line endings, so that
|
||||
/// we don't have to bother with it while formatting.
|
||||
private string PrepareInputForFormatting(string input)
|
||||
{
|
||||
input = CleanWhiteSpace(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
private string CleanWhiteSpace(string text)
|
||||
{
|
||||
text = text.Replace("\r\n", "\n");
|
||||
text = text.Replace("\t", "");
|
||||
text = Regex.Replace(text, @"\n{3,}", "\n\n");
|
||||
text = Regex.Replace(text, @"\n *\n", "\n\n");
|
||||
text = Regex.Replace(text, "\"$", "\" ");
|
||||
return text;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@ -10,144 +10,142 @@
|
||||
// You must not remove this notice, or any other, from this software.
|
||||
#endregion
|
||||
|
||||
namespace Textile
|
||||
namespace Textile;
|
||||
public partial class TextileFormatter
|
||||
{
|
||||
public partial class TextileFormatter
|
||||
#region State Registration
|
||||
|
||||
private static readonly List<Type> _registeredStates = new List<Type>();
|
||||
private static readonly List<FormatterStateAttribute> _registeredStatesAttributes = new List<FormatterStateAttribute>();
|
||||
|
||||
public static void RegisterFormatterState(Type formatterStateType)
|
||||
{
|
||||
#region State Registration
|
||||
if (!formatterStateType.IsSubclassOf(typeof(FormatterState)))
|
||||
throw new ArgumentException("The formatter state must be a sub-public class of FormatterStateBase.");
|
||||
|
||||
private static readonly List<Type> s_registeredStates = new List<Type>();
|
||||
private static readonly List<FormatterStateAttribute> s_registeredStatesAttributes = new List<FormatterStateAttribute>();
|
||||
if (formatterStateType.GetConstructor(new Type[] { typeof(TextileFormatter) }) == null)
|
||||
throw new ArgumentException("The formatter state must have a constructor that takes a TextileFormatter reference.");
|
||||
|
||||
public static void RegisterFormatterState(Type formatterStateType)
|
||||
{
|
||||
if (!formatterStateType.IsSubclassOf(typeof(FormatterState)))
|
||||
throw new ArgumentException("The formatter state must be a sub-public class of FormatterStateBase.");
|
||||
var att = FormatterStateAttribute.Get(formatterStateType);
|
||||
if (att == null)
|
||||
throw new ArgumentException("The formatter state must have the FormatterStateAttribute.");
|
||||
|
||||
if (formatterStateType.GetConstructor(new Type[] { typeof(TextileFormatter) }) == null)
|
||||
throw new ArgumentException("The formatter state must have a constructor that takes a TextileFormatter reference.");
|
||||
|
||||
var att = FormatterStateAttribute.Get(formatterStateType);
|
||||
if (att == null)
|
||||
throw new ArgumentException("The formatter state must have the FormatterStateAttribute.");
|
||||
|
||||
s_registeredStates.Add(formatterStateType);
|
||||
s_registeredStatesAttributes.Add(att);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region State Management
|
||||
|
||||
private readonly List<Type> m_disabledFormatterStates = new List<Type>();
|
||||
private readonly Stack<FormatterState> m_stackOfStates = new Stack<FormatterState>();
|
||||
|
||||
private bool IsFormatterStateEnabled(Type type)
|
||||
{
|
||||
return !m_disabledFormatterStates.Contains(type);
|
||||
}
|
||||
|
||||
private void SwitchFormatterState(Type type, bool onOff)
|
||||
{
|
||||
if (onOff)
|
||||
m_disabledFormatterStates.Remove(type);
|
||||
else if (!m_disabledFormatterStates.Contains(type))
|
||||
m_disabledFormatterStates.Add(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a new state on the stack.
|
||||
/// </summary>
|
||||
/// <param name="s">The state to push.</param>
|
||||
/// The state will be entered automatically.
|
||||
private void PushState(FormatterState s)
|
||||
{
|
||||
m_stackOfStates.Push(s);
|
||||
s.Enter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the last state from the stack.
|
||||
/// </summary>
|
||||
/// The state will be exited automatically.
|
||||
private void PopState()
|
||||
{
|
||||
m_stackOfStates.Peek().Exit();
|
||||
m_stackOfStates.Pop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current state, if any.
|
||||
/// </summary>
|
||||
internal FormatterState CurrentState
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_stackOfStates.Count > 0)
|
||||
return m_stackOfStates.Peek();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal void ChangeState(FormatterState formatterState)
|
||||
{
|
||||
if (CurrentState != null && CurrentState.GetType() == formatterState.GetType())
|
||||
{
|
||||
if (!CurrentState.ShouldNestState(formatterState))
|
||||
return;
|
||||
}
|
||||
PushState(formatterState);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region State Handling
|
||||
|
||||
/// <summary>
|
||||
/// Parses the string and updates the state accordingly.
|
||||
/// </summary>
|
||||
/// <param name="input">The text to process.</param>
|
||||
/// <returns>The text, ready for formatting.</returns>
|
||||
/// This method modifies the text because it removes some
|
||||
/// syntax stuff. Maybe the states themselves should handle
|
||||
/// their own syntax and remove it?
|
||||
private string HandleFormattingState(string input)
|
||||
{
|
||||
for (var i = 0; i < s_registeredStates.Count; i++)
|
||||
{
|
||||
var type = s_registeredStates[i];
|
||||
if (IsFormatterStateEnabled(type))
|
||||
{
|
||||
var att = s_registeredStatesAttributes[i];
|
||||
var m = Regex.Match(input, att.Pattern);
|
||||
if (m.Success)
|
||||
{
|
||||
var formatterState = (FormatterState)Activator.CreateInstance(type, this);
|
||||
return formatterState.Consume(input, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default, when no block is specified, we ask the current state, or
|
||||
// use the paragraph state.
|
||||
if (CurrentState != null)
|
||||
{
|
||||
if (CurrentState.FallbackFormattingState != null)
|
||||
{
|
||||
var formatterState = (FormatterState)Activator.CreateInstance(CurrentState.FallbackFormattingState, this);
|
||||
ChangeState(formatterState);
|
||||
}
|
||||
// else, the current state doesn't want to be superceded by
|
||||
// a new one. We'll leave him be.
|
||||
}
|
||||
else
|
||||
{
|
||||
ChangeState(new States.ParagraphFormatterState(this));
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
#endregion
|
||||
_registeredStates.Add(formatterStateType);
|
||||
_registeredStatesAttributes.Add(att);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region State Management
|
||||
|
||||
private readonly List<Type> _disabledFormatterStates = new List<Type>();
|
||||
private readonly Stack<FormatterState> _stackOfStates = new Stack<FormatterState>();
|
||||
|
||||
private bool IsFormatterStateEnabled(Type type)
|
||||
{
|
||||
return !_disabledFormatterStates.Contains(type);
|
||||
}
|
||||
|
||||
private void SwitchFormatterState(Type type, bool onOff)
|
||||
{
|
||||
if (onOff)
|
||||
_disabledFormatterStates.Remove(type);
|
||||
else if (!_disabledFormatterStates.Contains(type))
|
||||
_disabledFormatterStates.Add(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a new state on the stack.
|
||||
/// </summary>
|
||||
/// <param name="s">The state to push.</param>
|
||||
/// The state will be entered automatically.
|
||||
private void PushState(FormatterState s)
|
||||
{
|
||||
_stackOfStates.Push(s);
|
||||
s.Enter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the last state from the stack.
|
||||
/// </summary>
|
||||
/// The state will be exited automatically.
|
||||
private void PopState()
|
||||
{
|
||||
_stackOfStates.Peek().Exit();
|
||||
_stackOfStates.Pop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current state, if any.
|
||||
/// </summary>
|
||||
internal FormatterState CurrentState
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_stackOfStates.Count > 0)
|
||||
return _stackOfStates.Peek();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal void ChangeState(FormatterState formatterState)
|
||||
{
|
||||
if (CurrentState != null && CurrentState.GetType() == formatterState.GetType())
|
||||
{
|
||||
if (!CurrentState.ShouldNestState(formatterState))
|
||||
return;
|
||||
}
|
||||
PushState(formatterState);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region State Handling
|
||||
|
||||
/// <summary>
|
||||
/// Parses the string and updates the state accordingly.
|
||||
/// </summary>
|
||||
/// <param name="input">The text to process.</param>
|
||||
/// <returns>The text, ready for formatting.</returns>
|
||||
/// This method modifies the text because it removes some
|
||||
/// syntax stuff. Maybe the states themselves should handle
|
||||
/// their own syntax and remove it?
|
||||
private string HandleFormattingState(string input)
|
||||
{
|
||||
for (var i = 0; i < _registeredStates.Count; i++)
|
||||
{
|
||||
var type = _registeredStates[i];
|
||||
if (IsFormatterStateEnabled(type))
|
||||
{
|
||||
var att = _registeredStatesAttributes[i];
|
||||
var m = Regex.Match(input, att.Pattern);
|
||||
if (m.Success)
|
||||
{
|
||||
var formatterState = (FormatterState)Activator.CreateInstance(type, this);
|
||||
return formatterState.Consume(input, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default, when no block is specified, we ask the current state, or
|
||||
// use the paragraph state.
|
||||
if (CurrentState != null)
|
||||
{
|
||||
if (CurrentState.FallbackFormattingState != null)
|
||||
{
|
||||
var formatterState = (FormatterState)Activator.CreateInstance(CurrentState.FallbackFormattingState, this);
|
||||
ChangeState(formatterState);
|
||||
}
|
||||
// else, the current state doesn't want to be superceded by
|
||||
// a new one. We'll leave him be.
|
||||
}
|
||||
else
|
||||
{
|
||||
ChangeState(new States.ParagraphFormatterState(this));
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -24,39 +24,37 @@
|
||||
*/
|
||||
|
||||
|
||||
namespace ASC.VoipService.Dao
|
||||
namespace ASC.VoipService.Dao;
|
||||
|
||||
public class AbstractDao
|
||||
{
|
||||
public class AbstractDao
|
||||
private readonly string _dbid = "default";
|
||||
private readonly Lazy<VoipDbContext> _lazyVoipDbContext;
|
||||
|
||||
protected VoipDbContext VoipDbContext { get => _lazyVoipDbContext.Value; }
|
||||
protected int TenantID
|
||||
{
|
||||
private readonly string dbid = "default";
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private Lazy<VoipDbContext> LazyVoipDbContext { get; }
|
||||
protected VoipDbContext VoipDbContext { get => LazyVoipDbContext.Value; }
|
||||
protected AbstractDao(DbContextManager<VoipDbContext> dbOptions, TenantManager tenantManager)
|
||||
{
|
||||
_lazyVoipDbContext = new Lazy<VoipDbContext>(() => dbOptions.Get(_dbid));
|
||||
TenantID = tenantManager.GetCurrentTenant().Id;
|
||||
}
|
||||
|
||||
protected AbstractDao(DbContextManager<VoipDbContext> dbOptions, TenantManager tenantManager)
|
||||
{
|
||||
LazyVoipDbContext = new Lazy<VoipDbContext>(() => dbOptions.Get(dbid));
|
||||
TenantID = tenantManager.GetCurrentTenant().Id;
|
||||
}
|
||||
|
||||
protected int TenantID
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected string GetTenantColumnName(string table)
|
||||
{
|
||||
const string tenant = "tenant_id";
|
||||
if (!table.Contains(' ')) return tenant;
|
||||
return table.Substring(table.IndexOf(" ", StringComparison.Ordinal)).Trim() + "." + tenant;
|
||||
}
|
||||
protected string GetTenantColumnName(string table)
|
||||
{
|
||||
const string tenant = "tenant_id";
|
||||
if (!table.Contains(' ')) return tenant;
|
||||
return table.Substring(table.IndexOf(" ", StringComparison.Ordinal)).Trim() + "." + tenant;
|
||||
}
|
||||
|
||||
|
||||
protected static Guid ToGuid(object guid)
|
||||
{
|
||||
var str = guid as string;
|
||||
return !string.IsNullOrEmpty(str) ? new Guid(str) : Guid.Empty;
|
||||
}
|
||||
protected static Guid ToGuid(object guid)
|
||||
{
|
||||
var str = guid as string;
|
||||
return !string.IsNullOrEmpty(str) ? new Guid(str) : Guid.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -1,100 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* (c) Copyright Ascensio System Limited 2010-2018
|
||||
*
|
||||
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
|
||||
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
|
||||
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
|
||||
*
|
||||
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
|
||||
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
|
||||
*
|
||||
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
|
||||
* relevant author attributions when distributing the software. If the display of the logo in its graphic
|
||||
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
|
||||
* in every copy of the program you distribute.
|
||||
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.VoipService.Dao
|
||||
{
|
||||
[Singletone]
|
||||
public class VoipDaoCache
|
||||
{
|
||||
internal ICache Cache { get; }
|
||||
private ICacheNotify<CachedVoipItem> Notify { get; }
|
||||
|
||||
public VoipDaoCache(ICacheNotify<CachedVoipItem> notify, ICache cache)
|
||||
{
|
||||
Cache = cache;
|
||||
Notify = notify;
|
||||
Notify.Subscribe((c) => Cache.Remove(CachedVoipDao.GetCacheKey(c.Tenant)), Common.Caching.CacheNotifyAction.Any);
|
||||
}
|
||||
|
||||
public void ResetCache(int tenant)
|
||||
{
|
||||
Notify.Publish(new CachedVoipItem { Tenant = tenant }, Common.Caching.CacheNotifyAction.Any);
|
||||
}
|
||||
}
|
||||
|
||||
[Scope]
|
||||
public class CachedVoipDao : VoipDao
|
||||
{
|
||||
private readonly ICache cache;
|
||||
private static readonly TimeSpan timeout = TimeSpan.FromDays(1);
|
||||
|
||||
private VoipDaoCache VoipDaoCache { get; }
|
||||
|
||||
public CachedVoipDao(
|
||||
TenantManager tenantManager,
|
||||
DbContextManager<VoipDbContext> dbOptions,
|
||||
AuthContext authContext,
|
||||
TenantUtil tenantUtil,
|
||||
SecurityContext securityContext,
|
||||
BaseCommonLinkUtility baseCommonLinkUtility,
|
||||
ConsumerFactory consumerFactory,
|
||||
VoipDaoCache voipDaoCache)
|
||||
: base(tenantManager, dbOptions, authContext, tenantUtil, securityContext, baseCommonLinkUtility, consumerFactory)
|
||||
{
|
||||
cache = voipDaoCache.Cache;
|
||||
VoipDaoCache = voipDaoCache;
|
||||
}
|
||||
|
||||
public override VoipPhone SaveOrUpdateNumber(VoipPhone phone)
|
||||
{
|
||||
var result = base.SaveOrUpdateNumber(phone);
|
||||
VoipDaoCache.ResetCache(TenantID);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void DeleteNumber(string phoneId = "")
|
||||
{
|
||||
base.DeleteNumber(phoneId);
|
||||
VoipDaoCache.ResetCache(TenantID);
|
||||
}
|
||||
|
||||
public override IEnumerable<VoipPhone> GetNumbers(params string[] ids)
|
||||
{
|
||||
var numbers = cache.Get<List<VoipPhone>>(GetCacheKey(TenantID));
|
||||
if (numbers == null)
|
||||
{
|
||||
numbers = new List<VoipPhone>(base.GetAllNumbers());
|
||||
cache.Insert(GetCacheKey(TenantID), numbers, DateTime.UtcNow.Add(timeout));
|
||||
}
|
||||
|
||||
return ids.Length > 0 ? numbers.Where(r => ids.Contains(r.Id) || ids.Contains(r.Number)).ToList() : numbers;
|
||||
}
|
||||
|
||||
public static string GetCacheKey(int tenant)
|
||||
{
|
||||
return "voip" + tenant.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,92 +23,79 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.VoipService.Dao
|
||||
namespace ASC.VoipService.Dao;
|
||||
|
||||
public class VoipCallFilter
|
||||
{
|
||||
public class VoipCallFilter
|
||||
public string Type { get; set; }
|
||||
public DateTime? FromDate { get; set; }
|
||||
public DateTime? ToDate { get; set; }
|
||||
public Guid? Agent { get; set; }
|
||||
public int? Client { get; set; }
|
||||
public int? ContactID { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string ParentId { get; set; }
|
||||
public string SortBy { get; set; }
|
||||
public bool SortOrder { get; set; }
|
||||
public string SearchText { get; set; }
|
||||
public long Offset { get; set; }
|
||||
public long Max { get; set; }
|
||||
|
||||
public int? TypeStatus
|
||||
{
|
||||
public string Type { get; set; }
|
||||
|
||||
public DateTime? FromDate { get; set; }
|
||||
|
||||
public DateTime? ToDate { get; set; }
|
||||
|
||||
public Guid? Agent { get; set; }
|
||||
|
||||
public int? Client { get; set; }
|
||||
|
||||
public int? ContactID { get; set; }
|
||||
|
||||
public string Id { get; set; }
|
||||
|
||||
public string ParentId { get; set; }
|
||||
|
||||
public string SortBy { get; set; }
|
||||
|
||||
public bool SortOrder { get; set; }
|
||||
|
||||
public string SearchText { get; set; }
|
||||
|
||||
public long Offset { get; set; }
|
||||
|
||||
public long Max { get; set; }
|
||||
|
||||
public int? TypeStatus
|
||||
get
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Type)) return null;
|
||||
if (TypeStatuses.TryGetValue(Type, out var status)) return status;
|
||||
if (string.IsNullOrWhiteSpace(Type)) return null;
|
||||
if (TypeStatuses.TryGetValue(Type, out var status)) return status;
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public string SortByColumn
|
||||
public string SortByColumn
|
||||
{
|
||||
get
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(SortBy)) return null;
|
||||
return SortColumns.ContainsKey(SortBy) ? SortColumns[SortBy] : null;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(SortBy)) return null;
|
||||
return SortColumns.ContainsKey(SortBy) ? SortColumns[SortBy] : null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, int> TypeStatuses
|
||||
private static Dictionary<string, int> TypeStatuses
|
||||
{
|
||||
get
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Dictionary<string, int>
|
||||
return new Dictionary<string, int>
|
||||
{
|
||||
{
|
||||
{
|
||||
"answered", (int)VoipCallStatus.Answered
|
||||
},
|
||||
{
|
||||
"missed", (int)VoipCallStatus.Missed
|
||||
},
|
||||
{
|
||||
"outgoing", (int)VoipCallStatus.Outcoming
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> SortColumns
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Dictionary<string, string>
|
||||
"answered", (int)VoipCallStatus.Answered
|
||||
},
|
||||
{
|
||||
{
|
||||
"date", "dial_date"
|
||||
},
|
||||
{
|
||||
"duration", "dial_duration"
|
||||
},
|
||||
{
|
||||
"price", "price"
|
||||
},
|
||||
};
|
||||
}
|
||||
"missed", (int)VoipCallStatus.Missed
|
||||
},
|
||||
{
|
||||
"outgoing", (int)VoipCallStatus.Outcoming
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> SortColumns
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Dictionary<string, string>
|
||||
{
|
||||
{
|
||||
"date", "dial_date"
|
||||
},
|
||||
{
|
||||
"duration", "dial_duration"
|
||||
},
|
||||
{
|
||||
"price", "price"
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -23,342 +23,303 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.VoipService.Dao
|
||||
namespace ASC.VoipService.Dao;
|
||||
|
||||
[Scope(Additional = typeof(EventTypeConverterExtension))]
|
||||
public class VoipDao : AbstractDao
|
||||
{
|
||||
[Scope(typeof(CachedVoipDao))]
|
||||
public class VoipDao : AbstractDao
|
||||
private readonly AuthContext _authContext;
|
||||
private readonly TenantUtil _tenantUtil;
|
||||
private readonly SecurityContext _securityContext;
|
||||
private readonly BaseCommonLinkUtility _baseCommonLinkUtility;
|
||||
private readonly ConsumerFactory _consumerFactory;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public VoipDao(
|
||||
TenantManager tenantManager,
|
||||
DbContextManager<VoipDbContext> dbOptions,
|
||||
AuthContext authContext,
|
||||
TenantUtil tenantUtil,
|
||||
SecurityContext securityContext,
|
||||
BaseCommonLinkUtility baseCommonLinkUtility,
|
||||
ConsumerFactory consumerFactory,
|
||||
IMapper mapper)
|
||||
: base(dbOptions, tenantManager)
|
||||
{
|
||||
public VoipDao(
|
||||
TenantManager tenantManager,
|
||||
DbContextManager<VoipDbContext> dbOptions,
|
||||
AuthContext authContext,
|
||||
TenantUtil tenantUtil,
|
||||
SecurityContext securityContext,
|
||||
BaseCommonLinkUtility baseCommonLinkUtility,
|
||||
ConsumerFactory consumerFactory)
|
||||
: base(dbOptions, tenantManager)
|
||||
{
|
||||
AuthContext = authContext;
|
||||
TenantUtil = tenantUtil;
|
||||
SecurityContext = securityContext;
|
||||
BaseCommonLinkUtility = baseCommonLinkUtility;
|
||||
ConsumerFactory = consumerFactory;
|
||||
}
|
||||
|
||||
public virtual VoipPhone SaveOrUpdateNumber(VoipPhone phone)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(phone.Number))
|
||||
{
|
||||
phone.Number = phone.Number.TrimStart('+');
|
||||
}
|
||||
|
||||
var voipNumber = new VoipNumber
|
||||
{
|
||||
Id = phone.Id,
|
||||
Number = phone.Number,
|
||||
Alias = phone.Alias,
|
||||
Settings = phone.Settings.ToString(),
|
||||
TenantId = TenantID
|
||||
};
|
||||
|
||||
VoipDbContext.VoipNumbers.Add(voipNumber);
|
||||
VoipDbContext.SaveChanges();
|
||||
|
||||
return phone;
|
||||
}
|
||||
|
||||
public virtual void DeleteNumber(string phoneId = "")
|
||||
{
|
||||
var number = VoipDbContext.VoipNumbers.Where(r => r.Id == phoneId && r.TenantId == TenantID).FirstOrDefault();
|
||||
VoipDbContext.VoipNumbers.Remove(number);
|
||||
VoipDbContext.SaveChanges();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<VoipPhone> GetAllNumbers()
|
||||
{
|
||||
return VoipDbContext.VoipNumbers
|
||||
.Where(r => r.TenantId == TenantID)
|
||||
.ToList()
|
||||
.ConvertAll(ToPhone);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<VoipPhone> GetNumbers(params string[] ids)
|
||||
{
|
||||
var numbers = VoipDbContext.VoipNumbers.Where(r => r.TenantId == TenantID);
|
||||
|
||||
if (ids.Length > 0)
|
||||
{
|
||||
numbers = numbers.Where(r => ids.Any(a => a == r.Number || a == r.Id));
|
||||
}
|
||||
|
||||
return numbers.ToList().ConvertAll(ToPhone);
|
||||
}
|
||||
|
||||
public VoipPhone GetNumber(string id)
|
||||
{
|
||||
return GetNumbers(id.TrimStart('+')).FirstOrDefault();
|
||||
}
|
||||
|
||||
public virtual VoipPhone GetCurrentNumber()
|
||||
{
|
||||
return GetNumbers().FirstOrDefault(r => r.Caller != null);
|
||||
}
|
||||
|
||||
|
||||
public VoipCall SaveOrUpdateCall(VoipCall call)
|
||||
{
|
||||
var voipCall = new DbVoipCall
|
||||
{
|
||||
TenantId = TenantID,
|
||||
Id = call.Id,
|
||||
NumberFrom = call.From,
|
||||
NumberTo = call.To,
|
||||
ContactId = call.ContactId
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(call.ParentID))
|
||||
{
|
||||
voipCall.ParentCallId = call.ParentID;
|
||||
}
|
||||
|
||||
if (call.Status.HasValue)
|
||||
{
|
||||
voipCall.Status = (int)call.Status.Value;
|
||||
}
|
||||
|
||||
if (!call.AnsweredBy.Equals(Guid.Empty))
|
||||
{
|
||||
voipCall.AnsweredBy = call.AnsweredBy;
|
||||
}
|
||||
|
||||
if (call.DialDate == DateTime.MinValue)
|
||||
{
|
||||
call.DialDate = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
voipCall.DialDate = TenantUtil.DateTimeToUtc(call.DialDate);
|
||||
|
||||
if (call.DialDuration > 0)
|
||||
{
|
||||
voipCall.DialDuration = call.DialDuration;
|
||||
}
|
||||
|
||||
if (call.Price > decimal.Zero)
|
||||
{
|
||||
voipCall.Price = call.Price;
|
||||
}
|
||||
|
||||
if (call.VoipRecord != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(call.VoipRecord.Id))
|
||||
{
|
||||
voipCall.RecordSid = call.VoipRecord.Id;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(call.VoipRecord.Uri))
|
||||
{
|
||||
voipCall.RecordUrl = call.VoipRecord.Uri;
|
||||
}
|
||||
|
||||
if (call.VoipRecord.Duration != 0)
|
||||
{
|
||||
voipCall.RecordDuration = call.VoipRecord.Duration;
|
||||
}
|
||||
|
||||
if (call.VoipRecord.Price != default)
|
||||
{
|
||||
voipCall.RecordPrice = call.VoipRecord.Price;
|
||||
}
|
||||
}
|
||||
|
||||
VoipDbContext.VoipCalls.Add(voipCall);
|
||||
VoipDbContext.SaveChanges();
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
public IEnumerable<VoipCall> GetCalls(VoipCallFilter filter)
|
||||
{
|
||||
var query = GetCallsQuery(filter);
|
||||
|
||||
if (filter.SortByColumn != null)
|
||||
{
|
||||
query.OrderBy(filter.SortByColumn, filter.SortOrder);
|
||||
}
|
||||
|
||||
query = query.Skip((int)filter.Offset);
|
||||
query = query.Take((int)filter.Max * 3);
|
||||
|
||||
var calls = query.ToList().ConvertAll(ToCall);
|
||||
|
||||
calls = calls.GroupJoin(calls, call => call.Id, h => h.ParentID, (call, h) =>
|
||||
{
|
||||
call.ChildCalls.AddRange(h);
|
||||
return call;
|
||||
}).Where(r => string.IsNullOrEmpty(r.ParentID)).ToList();
|
||||
|
||||
return calls;
|
||||
}
|
||||
|
||||
public VoipCall GetCall(string id)
|
||||
{
|
||||
return GetCalls(new VoipCallFilter { Id = id }).FirstOrDefault();
|
||||
}
|
||||
|
||||
public int GetCallsCount(VoipCallFilter filter)
|
||||
{
|
||||
return GetCallsQuery(filter).Where(r => r.DbVoipCall.ParentCallId == "").Count();
|
||||
}
|
||||
|
||||
public IEnumerable<VoipCall> GetMissedCalls(Guid agent, long count = 0, DateTime? from = null)
|
||||
{
|
||||
var query = GetCallsQuery(new VoipCallFilter { Agent = agent, SortBy = "date", SortOrder = true, Type = "missed" });
|
||||
|
||||
if (from.HasValue)
|
||||
{
|
||||
query = query.Where(r => r.DbVoipCall.DialDate >= TenantUtil.DateTimeFromUtc(from.Value));
|
||||
}
|
||||
|
||||
if (count != 0)
|
||||
{
|
||||
query = query.Take((int)count);
|
||||
}
|
||||
|
||||
var a = query.Select(ca => new
|
||||
{
|
||||
dbVoipCall = ca,
|
||||
tmpDate = VoipDbContext.VoipCalls
|
||||
.Where(tmp => tmp.TenantId == ca.DbVoipCall.TenantId)
|
||||
.Where(tmp => tmp.NumberFrom == ca.DbVoipCall.NumberFrom || tmp.NumberTo == ca.DbVoipCall.NumberFrom)
|
||||
.Where(tmp => tmp.Status <= (int)VoipCallStatus.Missed)
|
||||
.Max(tmp => tmp.DialDate)
|
||||
}).Where(r => r.dbVoipCall.DbVoipCall.DialDate >= r.tmpDate || r.tmpDate == default);
|
||||
|
||||
return a.ToList().ConvertAll(r => ToCall(r.dbVoipCall));
|
||||
}
|
||||
|
||||
private IQueryable<CallContact> GetCallsQuery(VoipCallFilter filter)
|
||||
{
|
||||
var q = VoipDbContext.VoipCalls
|
||||
.Where(r => r.TenantId == TenantID);
|
||||
|
||||
if (!string.IsNullOrEmpty(filter.Id))
|
||||
{
|
||||
q = q.Where(r => r.Id == filter.Id || r.ParentCallId == filter.Id);
|
||||
}
|
||||
|
||||
if (filter.ContactID.HasValue)
|
||||
{
|
||||
q = q.Where(r => r.ContactId == filter.ContactID.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(filter.SearchText))
|
||||
{
|
||||
q = q.Where(r => r.Id.StartsWith(filter.SearchText));
|
||||
}
|
||||
|
||||
if (filter.TypeStatus.HasValue)
|
||||
{
|
||||
q = q.Where(r => r.Status == filter.TypeStatus.Value);
|
||||
}
|
||||
|
||||
if (filter.FromDate.HasValue)
|
||||
{
|
||||
q = q.Where(r => r.DialDate >= filter.FromDate.Value);
|
||||
}
|
||||
|
||||
if (filter.ToDate.HasValue)
|
||||
{
|
||||
q = q.Where(r => r.DialDate <= filter.ToDate.Value);
|
||||
}
|
||||
|
||||
if (filter.Agent.HasValue)
|
||||
{
|
||||
q = q.Where(r => r.AnsweredBy == filter.Agent.Value);
|
||||
}
|
||||
|
||||
return q
|
||||
.GroupBy(r => r.Id, r => r)
|
||||
.Join(
|
||||
VoipDbContext.CrmContact.DefaultIfEmpty(),
|
||||
r => r.FirstOrDefault().ContactId,
|
||||
c => c.Id,
|
||||
(call, contact) => new CallContact { DbVoipCall = call.FirstOrDefault(), CrmContact = contact })
|
||||
;
|
||||
}
|
||||
|
||||
class CallContact
|
||||
{
|
||||
public DbVoipCall DbVoipCall { get; set; }
|
||||
public CrmContact CrmContact { get; set; }
|
||||
}
|
||||
|
||||
#region Converters
|
||||
|
||||
private VoipPhone ToPhone(VoipNumber r)
|
||||
{
|
||||
return GetProvider().GetPhone(r);
|
||||
}
|
||||
|
||||
private VoipCall ToCall(CallContact dbVoipCall)
|
||||
{
|
||||
var call = new VoipCall
|
||||
{
|
||||
Id = dbVoipCall.DbVoipCall.Id,
|
||||
ParentID = dbVoipCall.DbVoipCall.ParentCallId,
|
||||
From = dbVoipCall.DbVoipCall.NumberFrom,
|
||||
To = dbVoipCall.DbVoipCall.NumberTo,
|
||||
AnsweredBy = dbVoipCall.DbVoipCall.AnsweredBy,
|
||||
DialDate = TenantUtil.DateTimeFromUtc(dbVoipCall.DbVoipCall.DialDate),
|
||||
DialDuration = dbVoipCall.DbVoipCall.DialDuration,
|
||||
Price = dbVoipCall.DbVoipCall.Price,
|
||||
Status = (VoipCallStatus)dbVoipCall.DbVoipCall.Status,
|
||||
VoipRecord = new VoipRecord
|
||||
{
|
||||
Id = dbVoipCall.DbVoipCall.RecordSid,
|
||||
Uri = dbVoipCall.DbVoipCall.RecordUrl,
|
||||
Duration = dbVoipCall.DbVoipCall.RecordDuration,
|
||||
Price = dbVoipCall.DbVoipCall.RecordPrice
|
||||
},
|
||||
ContactId = dbVoipCall.CrmContact.Id,
|
||||
ContactIsCompany = dbVoipCall.CrmContact.IsCompany,
|
||||
};
|
||||
|
||||
if (call.ContactId != 0)
|
||||
{
|
||||
call.ContactTitle = call.ContactIsCompany
|
||||
? dbVoipCall.CrmContact.CompanyName
|
||||
: dbVoipCall.CrmContact.FirstName == null || dbVoipCall.CrmContact.LastName == null ? null : $"{dbVoipCall.CrmContact.FirstName} {dbVoipCall.CrmContact.LastName}";
|
||||
}
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
public Consumer Consumer
|
||||
{
|
||||
get { return ConsumerFactory.GetByKey("twilio"); }
|
||||
}
|
||||
|
||||
public TwilioProvider GetProvider()
|
||||
{
|
||||
return new TwilioProvider(Consumer["twilioAccountSid"], Consumer["twilioAuthToken"], AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility);
|
||||
}
|
||||
|
||||
public bool ConfigSettingsExist
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrEmpty(Consumer["twilioAccountSid"]) &&
|
||||
!string.IsNullOrEmpty(Consumer["twilioAuthToken"]);
|
||||
}
|
||||
}
|
||||
|
||||
private AuthContext AuthContext { get; }
|
||||
private TenantUtil TenantUtil { get; }
|
||||
private SecurityContext SecurityContext { get; }
|
||||
private BaseCommonLinkUtility BaseCommonLinkUtility { get; }
|
||||
private ConsumerFactory ConsumerFactory { get; }
|
||||
|
||||
#endregion
|
||||
_authContext = authContext;
|
||||
_tenantUtil = tenantUtil;
|
||||
_securityContext = securityContext;
|
||||
_baseCommonLinkUtility = baseCommonLinkUtility;
|
||||
_consumerFactory = consumerFactory;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public virtual VoipPhone SaveOrUpdateNumber(VoipPhone phone)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(phone.Number))
|
||||
{
|
||||
phone.Number = phone.Number.TrimStart('+');
|
||||
}
|
||||
|
||||
var voipNumber = new VoipNumber
|
||||
{
|
||||
Id = phone.Id,
|
||||
Number = phone.Number,
|
||||
Alias = phone.Alias,
|
||||
Settings = phone.Settings.ToString(),
|
||||
TenantId = TenantID
|
||||
};
|
||||
|
||||
VoipDbContext.VoipNumbers.Add(voipNumber);
|
||||
VoipDbContext.SaveChanges();
|
||||
|
||||
return phone;
|
||||
}
|
||||
|
||||
public virtual void DeleteNumber(string phoneId = "")
|
||||
{
|
||||
var number = VoipDbContext.VoipNumbers.Where(r => r.Id == phoneId && r.TenantId == TenantID).FirstOrDefault();
|
||||
VoipDbContext.VoipNumbers.Remove(number);
|
||||
VoipDbContext.SaveChanges();
|
||||
}
|
||||
|
||||
public virtual IEnumerable<VoipPhone> GetAllNumbers()
|
||||
{
|
||||
return VoipDbContext.VoipNumbers
|
||||
.Where(r => r.TenantId == TenantID)
|
||||
.ToList()
|
||||
.ConvertAll(ToPhone);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<VoipPhone> GetNumbers(params string[] ids)
|
||||
{
|
||||
var numbers = VoipDbContext.VoipNumbers.Where(r => r.TenantId == TenantID);
|
||||
|
||||
if (ids.Length > 0)
|
||||
{
|
||||
numbers = numbers.Where(r => ids.Any(a => a == r.Number || a == r.Id));
|
||||
}
|
||||
|
||||
return numbers.ToList().ConvertAll(ToPhone);
|
||||
}
|
||||
|
||||
public VoipPhone GetNumber(string id)
|
||||
{
|
||||
return GetNumbers(id.TrimStart('+')).FirstOrDefault();
|
||||
}
|
||||
|
||||
public virtual VoipPhone GetCurrentNumber()
|
||||
{
|
||||
return GetNumbers().FirstOrDefault(r => r.Caller != null);
|
||||
}
|
||||
|
||||
|
||||
public VoipCall SaveOrUpdateCall(VoipCall call)
|
||||
{
|
||||
var voipCall = new DbVoipCall
|
||||
{
|
||||
TenantId = TenantID,
|
||||
Id = call.Id,
|
||||
NumberFrom = call.NumberFrom,
|
||||
NumberTo = call.NumberTo,
|
||||
ContactId = call.ContactId
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(call.ParentCallId))
|
||||
{
|
||||
voipCall.ParentCallId = call.ParentCallId;
|
||||
}
|
||||
|
||||
if (call.Status.HasValue)
|
||||
{
|
||||
voipCall.Status = (int)call.Status.Value;
|
||||
}
|
||||
|
||||
if (!call.AnsweredBy.Equals(Guid.Empty))
|
||||
{
|
||||
voipCall.AnsweredBy = call.AnsweredBy;
|
||||
}
|
||||
|
||||
if (call.DialDate == DateTime.MinValue)
|
||||
{
|
||||
call.DialDate = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
voipCall.DialDate = _tenantUtil.DateTimeToUtc(call.DialDate);
|
||||
|
||||
if (call.DialDuration > 0)
|
||||
{
|
||||
voipCall.DialDuration = call.DialDuration;
|
||||
}
|
||||
|
||||
if (call.Price > decimal.Zero)
|
||||
{
|
||||
voipCall.Price = call.Price;
|
||||
}
|
||||
|
||||
if (call.VoipRecord != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(call.VoipRecord.Sid))
|
||||
{
|
||||
voipCall.Sid = call.VoipRecord.Sid;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(call.VoipRecord.Uri))
|
||||
{
|
||||
voipCall.Uri = call.VoipRecord.Uri;
|
||||
}
|
||||
|
||||
if (call.VoipRecord.Duration != 0)
|
||||
{
|
||||
voipCall.Duration = call.VoipRecord.Duration;
|
||||
}
|
||||
|
||||
if (call.VoipRecord.Price != default)
|
||||
{
|
||||
voipCall.RecordPrice = call.VoipRecord.Price;
|
||||
}
|
||||
}
|
||||
|
||||
VoipDbContext.VoipCalls.Add(voipCall);
|
||||
VoipDbContext.SaveChanges();
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
public IEnumerable<VoipCall> GetCalls(VoipCallFilter filter)
|
||||
{
|
||||
var query = GetCallsQuery(filter);
|
||||
|
||||
if (filter.SortByColumn != null)
|
||||
{
|
||||
query.OrderBy(filter.SortByColumn, filter.SortOrder);
|
||||
}
|
||||
|
||||
query = query.Skip((int)filter.Offset);
|
||||
query = query.Take((int)filter.Max * 3);
|
||||
|
||||
var calls = _mapper.Map<List<CallContact>, IEnumerable<VoipCall>>(query.ToList());
|
||||
|
||||
calls = calls.GroupJoin(calls, call => call.Id, h => h.ParentCallId, (call, h) =>
|
||||
{
|
||||
call.ChildCalls.AddRange(h);
|
||||
return call;
|
||||
}).Where(r => string.IsNullOrEmpty(r.ParentCallId)).ToList();
|
||||
|
||||
return calls;
|
||||
}
|
||||
|
||||
public VoipCall GetCall(string id)
|
||||
{
|
||||
return GetCalls(new VoipCallFilter { Id = id }).FirstOrDefault();
|
||||
}
|
||||
|
||||
public int GetCallsCount(VoipCallFilter filter)
|
||||
{
|
||||
return GetCallsQuery(filter).Where(r => r.DbVoipCall.ParentCallId == "").Count();
|
||||
}
|
||||
|
||||
public IEnumerable<VoipCall> GetMissedCalls(Guid agent, long count = 0, DateTime? from = null)
|
||||
{
|
||||
var query = GetCallsQuery(new VoipCallFilter { Agent = agent, SortBy = "date", SortOrder = true, Type = "missed" });
|
||||
|
||||
if (from.HasValue)
|
||||
{
|
||||
query = query.Where(r => r.DbVoipCall.DialDate >= _tenantUtil.DateTimeFromUtc(from.Value));
|
||||
}
|
||||
|
||||
if (count != 0)
|
||||
{
|
||||
query = query.Take((int)count);
|
||||
}
|
||||
|
||||
query = query.Select(ca => new
|
||||
{
|
||||
dbVoipCall = ca,
|
||||
tmpDate = VoipDbContext.VoipCalls
|
||||
.Where(tmp => tmp.TenantId == ca.DbVoipCall.TenantId)
|
||||
.Where(tmp => tmp.NumberFrom == ca.DbVoipCall.NumberFrom || tmp.NumberTo == ca.DbVoipCall.NumberFrom)
|
||||
.Where(tmp => tmp.Status <= (int)VoipCallStatus.Missed)
|
||||
.Max(tmp => tmp.DialDate)
|
||||
}).Where(r => r.dbVoipCall.DbVoipCall.DialDate >= r.tmpDate || r.tmpDate == default)
|
||||
.Select(q=> q.dbVoipCall);
|
||||
|
||||
return _mapper.Map<List<CallContact>, IEnumerable<VoipCall>>(query.ToList());
|
||||
}
|
||||
|
||||
private IQueryable<CallContact> GetCallsQuery(VoipCallFilter filter)
|
||||
{
|
||||
var q = VoipDbContext.VoipCalls
|
||||
.Where(r => r.TenantId == TenantID);
|
||||
|
||||
if (!string.IsNullOrEmpty(filter.Id))
|
||||
{
|
||||
q = q.Where(r => r.Id == filter.Id || r.ParentCallId == filter.Id);
|
||||
}
|
||||
|
||||
if (filter.ContactID.HasValue)
|
||||
{
|
||||
q = q.Where(r => r.ContactId == filter.ContactID.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(filter.SearchText))
|
||||
{
|
||||
q = q.Where(r => r.Id.StartsWith(filter.SearchText));
|
||||
}
|
||||
|
||||
if (filter.TypeStatus.HasValue)
|
||||
{
|
||||
q = q.Where(r => r.Status == filter.TypeStatus.Value);
|
||||
}
|
||||
|
||||
if (filter.FromDate.HasValue)
|
||||
{
|
||||
q = q.Where(r => r.DialDate >= filter.FromDate.Value);
|
||||
}
|
||||
|
||||
if (filter.ToDate.HasValue)
|
||||
{
|
||||
q = q.Where(r => r.DialDate <= filter.ToDate.Value);
|
||||
}
|
||||
|
||||
if (filter.Agent.HasValue)
|
||||
{
|
||||
q = q.Where(r => r.AnsweredBy == filter.Agent.Value);
|
||||
}
|
||||
|
||||
return from voipCalls in q
|
||||
join crmContact in VoipDbContext.CrmContact on voipCalls.ContactId equals crmContact.Id into grouping
|
||||
from g in grouping.DefaultIfEmpty()
|
||||
select new CallContact { DbVoipCall = voipCalls, CrmContact = g };
|
||||
}
|
||||
|
||||
private VoipPhone ToPhone(VoipNumber r)
|
||||
{
|
||||
return GetProvider().GetPhone(r);
|
||||
}
|
||||
|
||||
public Consumer Consumer
|
||||
{
|
||||
get { return _consumerFactory.GetByKey("twilio"); }
|
||||
}
|
||||
|
||||
public TwilioProvider GetProvider()
|
||||
{
|
||||
return new TwilioProvider(Consumer["twilioAccountSid"], Consumer["twilioAuthToken"], _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility);
|
||||
}
|
||||
|
||||
public bool ConfigSettingsExist
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrEmpty(Consumer["twilioAccountSid"]) &&
|
||||
!string.IsNullOrEmpty(Consumer["twilioAuthToken"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CallContact
|
||||
{
|
||||
public DbVoipCall DbVoipCall { get; set; }
|
||||
public CrmContact CrmContact { get; set; }
|
||||
}
|
@ -1,14 +1,9 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Globalization;
|
||||
global using System.Linq;
|
||||
global using System.Reflection;
|
||||
global using System.Reflection;
|
||||
global using System.Text;
|
||||
global using System.Threading;
|
||||
global using System.Web;
|
||||
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Caching;
|
||||
global using ASC.Common.Mapping;
|
||||
global using ASC.Core;
|
||||
global using ASC.Core.Common;
|
||||
global using ASC.Core.Common.Configuration;
|
||||
@ -16,19 +11,22 @@ global using ASC.Core.Common.EF;
|
||||
global using ASC.Core.Common.EF.Context;
|
||||
global using ASC.Core.Common.EF.Model;
|
||||
global using ASC.Core.Tenants;
|
||||
global using ASC.VoipService.Dao;
|
||||
global using ASC.VoipService.Mappings;
|
||||
global using ASC.VoipService.Twilio;
|
||||
|
||||
global using AutoMapper;
|
||||
|
||||
global using Newtonsoft.Json;
|
||||
global using Newtonsoft.Json.Linq;
|
||||
global using Newtonsoft.Json.Serialization;
|
||||
|
||||
global using Twilio.Clients;
|
||||
global using Twilio.Exceptions;
|
||||
global using Twilio.Http;
|
||||
global using Twilio.Jwt;
|
||||
global using Twilio.Jwt.Client;
|
||||
global using Twilio.Rest.Api.V2010.Account;
|
||||
global using Twilio.Rest.Api.V2010.Account.AvailablePhoneNumberCountry;
|
||||
global using Twilio.Rest.Api.V2010.Account.Queue;
|
||||
global using Twilio.TwiML;
|
||||
global using Twilio.Types;
|
||||
global using Twilio.Types;
|
@ -23,32 +23,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.VoipService
|
||||
namespace ASC.VoipService;
|
||||
|
||||
public interface IVoipProvider
|
||||
{
|
||||
public interface IVoipProvider
|
||||
{
|
||||
IEnumerable<VoipPhone> GetExistingPhoneNumbers();
|
||||
IEnumerable<VoipPhone> GetExistingPhoneNumbers();
|
||||
|
||||
IEnumerable<VoipPhone> GetAvailablePhoneNumbers(PhoneNumberType phoneNumberType, string isoCountryCode);
|
||||
IEnumerable<VoipPhone> GetAvailablePhoneNumbers(PhoneNumberType phoneNumberType, string isoCountryCode);
|
||||
|
||||
VoipPhone BuyNumber(string phoneNumber);
|
||||
VoipPhone BuyNumber(string phoneNumber);
|
||||
|
||||
VoipPhone DeleteNumber(VoipPhone phone);
|
||||
VoipPhone DeleteNumber(VoipPhone phone);
|
||||
|
||||
VoipPhone GetPhone(VoipNumber r);
|
||||
VoipPhone GetPhone(VoipNumber r);
|
||||
|
||||
VoipPhone GetPhone(string id);
|
||||
VoipPhone GetPhone(string id);
|
||||
|
||||
VoipCall GetCall(string callId);
|
||||
VoipCall GetCall(string callId);
|
||||
|
||||
string GetToken(Agent agent, int seconds = 60 * 60 * 24);
|
||||
string GetToken(Agent agent, int seconds = 60 * 60 * 24);
|
||||
|
||||
void UpdateSettings(VoipPhone phone);
|
||||
void UpdateSettings(VoipPhone phone);
|
||||
|
||||
VoipRecord GetRecord(string callId, string recordId);
|
||||
VoipRecord GetRecord(string callId, string recordId);
|
||||
|
||||
void CreateQueue(VoipPhone newPhone);
|
||||
void CreateQueue(VoipPhone newPhone);
|
||||
|
||||
void DisablePhone(VoipPhone phone);
|
||||
}
|
||||
void DisablePhone(VoipPhone phone);
|
||||
}
|
||||
|
34
common/ASC.VoipService/Mappings/CallTypeConverter.cs
Normal file
34
common/ASC.VoipService/Mappings/CallTypeConverter.cs
Normal file
@ -0,0 +1,34 @@
|
||||
namespace ASC.VoipService.Mappings;
|
||||
|
||||
[Scope]
|
||||
public class CallTypeConverter : ITypeConverter<CallContact, VoipCall>
|
||||
{
|
||||
public VoipCall Convert(CallContact source, VoipCall destination, ResolutionContext context)
|
||||
{
|
||||
var result = context.Mapper.Map<DbVoipCall, VoipCall>(source.DbVoipCall);
|
||||
result.VoipRecord = context.Mapper.Map<DbVoipCall, VoipRecord>(source.DbVoipCall);
|
||||
|
||||
if (source.CrmContact != null)
|
||||
{
|
||||
result.ContactId = source.CrmContact.Id;
|
||||
result.ContactIsCompany = source.CrmContact.IsCompany;
|
||||
result.ContactTitle = result.ContactIsCompany
|
||||
? source.CrmContact.CompanyName
|
||||
: source.CrmContact.FirstName == null || source.CrmContact.LastName == null ? null : $"{source.CrmContact.FirstName} {source.CrmContact.LastName}";
|
||||
}
|
||||
else
|
||||
{
|
||||
result.ContactId = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventTypeConverterExtension
|
||||
{
|
||||
public static void Register(DIHelper services)
|
||||
{
|
||||
services.TryAdd<CallTypeConverter>();
|
||||
}
|
||||
}
|
@ -23,103 +23,103 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.VoipService.Twilio
|
||||
using HttpMethod = Twilio.Http.HttpMethod;
|
||||
|
||||
namespace ASC.VoipService.Twilio;
|
||||
|
||||
public class TwilioPhone : VoipPhone
|
||||
{
|
||||
public class TwilioPhone : VoipPhone
|
||||
private readonly TwilioRestClient _twilio;
|
||||
public TwilioPhone(
|
||||
TwilioRestClient twilio,
|
||||
AuthContext authContext,
|
||||
TenantUtil tenantUtil,
|
||||
SecurityContext securityContext,
|
||||
BaseCommonLinkUtility baseCommonLinkUtility) :
|
||||
base(authContext, tenantUtil, securityContext, baseCommonLinkUtility)
|
||||
{
|
||||
private readonly TwilioRestClient twilio;
|
||||
|
||||
public TwilioPhone(
|
||||
TwilioRestClient twilio,
|
||||
AuthContext authContext,
|
||||
TenantUtil tenantUtil,
|
||||
SecurityContext securityContext,
|
||||
BaseCommonLinkUtility baseCommonLinkUtility) :
|
||||
base(authContext, tenantUtil, securityContext, baseCommonLinkUtility)
|
||||
{
|
||||
this.twilio = twilio;
|
||||
Settings = new TwilioVoipSettings(authContext, tenantUtil, securityContext, baseCommonLinkUtility);
|
||||
}
|
||||
|
||||
#region Calls
|
||||
|
||||
public override VoipCall Call(string to, string contactId = null)
|
||||
{
|
||||
var number = to.Split('#');
|
||||
|
||||
var call = CallResource.Create(new CreateCallOptions(new PhoneNumber("+" + number[0].TrimStart('+')), new PhoneNumber("+" + Number.TrimStart('+')))
|
||||
{
|
||||
SendDigits = number.Length > 1 ? number[1] + "#" : string.Empty,
|
||||
Record = Settings.Caller.Record,
|
||||
Url = new System.Uri(Settings.Connect(contactId: contactId))
|
||||
}, twilio);
|
||||
|
||||
return new VoipCall { Id = call.Sid, From = call.From, To = call.To };
|
||||
}
|
||||
|
||||
public override VoipCall LocalCall(string to)
|
||||
{
|
||||
return Call(Number + "#" + to);
|
||||
}
|
||||
|
||||
public override VoipCall RedirectCall(string callId, string to)
|
||||
{
|
||||
var call = CallResource.Update(callId, url: new System.Uri(Settings.Redirect(to)), method: HttpMethod.Post, client: twilio);
|
||||
return new VoipCall { Id = call.Sid, To = to };
|
||||
}
|
||||
|
||||
public override VoipCall HoldUp(string callId)
|
||||
{
|
||||
return RedirectCall(callId, "hold");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Queue
|
||||
|
||||
public Queue CreateQueue(string name, int size, string waitUrl, int waitTime)
|
||||
{
|
||||
var queues = QueueResource.Read(new ReadQueueOptions(), twilio);
|
||||
var queue = queues.FirstOrDefault(r => r.FriendlyName == name);
|
||||
if (queue == null)
|
||||
{
|
||||
queue = QueueResource.Create(name, client: twilio);
|
||||
}
|
||||
return new Queue(queue.Sid, name, size, waitUrl, waitTime);
|
||||
}
|
||||
|
||||
public string GetQueue(string name)
|
||||
{
|
||||
var queues = QueueResource.Read(new ReadQueueOptions(), twilio);
|
||||
return queues.First(r => r.FriendlyName == name).Sid;
|
||||
}
|
||||
|
||||
public IEnumerable<string> QueueCalls(string id)
|
||||
{
|
||||
var calls = MemberResource.Read(id, client: twilio);
|
||||
return calls.Select(r => r.CallSid);
|
||||
}
|
||||
|
||||
private void AnswerQueueCall(string queueId, string callId, bool reject = false)
|
||||
{
|
||||
var calls = QueueCalls(queueId);
|
||||
if (calls.Contains(callId))
|
||||
{
|
||||
MemberResource.Update(queueId, callId, new System.Uri(Settings.Dequeue(reject)), HttpMethod.Post,
|
||||
client: twilio);
|
||||
}
|
||||
}
|
||||
|
||||
public override void AnswerQueueCall(string callId)
|
||||
{
|
||||
AnswerQueueCall(Settings.Queue.Id, callId);
|
||||
}
|
||||
|
||||
public override void RejectQueueCall(string callId)
|
||||
{
|
||||
AnswerQueueCall(Settings.Queue.Id, callId, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
_twilio = twilio;
|
||||
Settings = new TwilioVoipSettings(authContext, tenantUtil, securityContext, baseCommonLinkUtility);
|
||||
}
|
||||
|
||||
#region Calls
|
||||
|
||||
public override VoipCall Call(string to, string contactId = null)
|
||||
{
|
||||
var number = to.Split('#');
|
||||
|
||||
var call = CallResource.Create(new CreateCallOptions(new PhoneNumber("+" + number[0].TrimStart('+')), new PhoneNumber("+" + Number.TrimStart('+')))
|
||||
{
|
||||
SendDigits = number.Length > 1 ? number[1] + "#" : string.Empty,
|
||||
Record = Settings.Caller.Record,
|
||||
Url = new System.Uri(Settings.Connect(contactId: contactId))
|
||||
}, _twilio);
|
||||
|
||||
return new VoipCall { Id = call.Sid, NumberFrom = call.From, NumberTo = call.To };
|
||||
}
|
||||
|
||||
public override VoipCall LocalCall(string to)
|
||||
{
|
||||
return Call(Number + "#" + to);
|
||||
}
|
||||
|
||||
public override VoipCall RedirectCall(string callId, string to)
|
||||
{
|
||||
var call = CallResource.Update(callId, url: new System.Uri(Settings.Redirect(to)), method: HttpMethod.Post, client: _twilio);
|
||||
return new VoipCall { Id = call.Sid, NumberTo = to };
|
||||
}
|
||||
|
||||
public override VoipCall HoldUp(string callId)
|
||||
{
|
||||
return RedirectCall(callId, "hold");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Queue
|
||||
|
||||
public Queue CreateQueue(string name, int size, string waitUrl, int waitTime)
|
||||
{
|
||||
var queues = QueueResource.Read(new ReadQueueOptions(), _twilio);
|
||||
var queue = queues.FirstOrDefault(r => r.FriendlyName == name);
|
||||
if (queue == null)
|
||||
{
|
||||
queue = QueueResource.Create(name, client: _twilio);
|
||||
}
|
||||
return new Queue(queue.Sid, name, size, waitUrl, waitTime);
|
||||
}
|
||||
|
||||
public string GetQueue(string name)
|
||||
{
|
||||
var queues = QueueResource.Read(new ReadQueueOptions(), _twilio);
|
||||
return queues.First(r => r.FriendlyName == name).Sid;
|
||||
}
|
||||
|
||||
public IEnumerable<string> QueueCalls(string id)
|
||||
{
|
||||
var calls = MemberResource.Read(id, client: _twilio);
|
||||
return calls.Select(r => r.CallSid);
|
||||
}
|
||||
|
||||
private void AnswerQueueCall(string queueId, string callId, bool reject = false)
|
||||
{
|
||||
var calls = QueueCalls(queueId);
|
||||
if (calls.Contains(callId))
|
||||
{
|
||||
MemberResource.Update(queueId, callId, new System.Uri(Settings.Dequeue(reject)), HttpMethod.Post,
|
||||
client: _twilio);
|
||||
}
|
||||
}
|
||||
|
||||
public override void AnswerQueueCall(string callId)
|
||||
{
|
||||
AnswerQueueCall(Settings.Queue.Id, callId);
|
||||
}
|
||||
|
||||
public override void RejectQueueCall(string callId)
|
||||
{
|
||||
AnswerQueueCall(Settings.Queue.Id, callId, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@ -27,208 +27,206 @@
|
||||
using RecordingResource = Twilio.Rest.Api.V2010.Account.Call.RecordingResource;
|
||||
|
||||
|
||||
namespace ASC.VoipService.Twilio
|
||||
namespace ASC.VoipService.Twilio;
|
||||
|
||||
public class TwilioProvider : IVoipProvider
|
||||
{
|
||||
public class TwilioProvider : IVoipProvider
|
||||
private readonly string _accountSid;
|
||||
private readonly string _authToken;
|
||||
private readonly TwilioRestClient _client;
|
||||
private readonly AuthContext _authContext;
|
||||
private readonly TenantUtil _tenantUtil;
|
||||
private readonly SecurityContext _securityContext;
|
||||
private readonly BaseCommonLinkUtility _baseCommonLinkUtility;
|
||||
|
||||
public TwilioProvider(string accountSid, string authToken, AuthContext authContext, TenantUtil tenantUtil, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility)
|
||||
{
|
||||
private readonly string accountSid;
|
||||
private readonly string authToken;
|
||||
private readonly TwilioRestClient client;
|
||||
if (string.IsNullOrEmpty(accountSid)) throw new ArgumentNullException(nameof(accountSid));
|
||||
if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException(nameof(authToken));
|
||||
|
||||
private AuthContext AuthContext { get; }
|
||||
private TenantUtil TenantUtil { get; }
|
||||
private SecurityContext SecurityContext { get; }
|
||||
private BaseCommonLinkUtility BaseCommonLinkUtility { get; }
|
||||
_authToken = authToken;
|
||||
_authContext = authContext;
|
||||
_tenantUtil = tenantUtil;
|
||||
_securityContext = securityContext;
|
||||
_baseCommonLinkUtility = baseCommonLinkUtility;
|
||||
_accountSid = accountSid;
|
||||
|
||||
public TwilioProvider(string accountSid, string authToken, AuthContext authContext, TenantUtil tenantUtil, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility)
|
||||
_client = new TwilioRestClient(accountSid, authToken);
|
||||
}
|
||||
|
||||
#region Call
|
||||
|
||||
public VoipRecord GetRecord(string callId, string recordSid)
|
||||
{
|
||||
var result = new VoipRecord { Sid = recordSid };
|
||||
var count = 6;
|
||||
|
||||
while (count > 0)
|
||||
{
|
||||
if (string.IsNullOrEmpty(accountSid)) throw new ArgumentNullException(nameof(accountSid));
|
||||
if (string.IsNullOrEmpty(authToken)) throw new ArgumentNullException(nameof(authToken));
|
||||
|
||||
this.authToken = authToken;
|
||||
AuthContext = authContext;
|
||||
TenantUtil = tenantUtil;
|
||||
SecurityContext = securityContext;
|
||||
BaseCommonLinkUtility = baseCommonLinkUtility;
|
||||
this.accountSid = accountSid;
|
||||
|
||||
client = new TwilioRestClient(accountSid, authToken);
|
||||
}
|
||||
|
||||
#region Call
|
||||
|
||||
public VoipRecord GetRecord(string callId, string recordSid)
|
||||
{
|
||||
var result = new VoipRecord { Id = recordSid };
|
||||
var count = 6;
|
||||
|
||||
while (count > 0)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var record = RecordingResource.Fetch(callId, recordSid, client: client);
|
||||
var record = RecordingResource.Fetch(callId, recordSid, client: _client);
|
||||
|
||||
if (!record.Price.HasValue)
|
||||
{
|
||||
count--;
|
||||
Thread.Sleep(10000);
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Price = (-1) * record.Price.Value;
|
||||
|
||||
result.Duration = Convert.ToInt32(record.Duration);
|
||||
if (record.Uri != null)
|
||||
{
|
||||
result.Uri = record.Uri;
|
||||
}
|
||||
break;
|
||||
}
|
||||
catch (ApiException)
|
||||
if (!record.Price.HasValue)
|
||||
{
|
||||
count--;
|
||||
Thread.Sleep(10000);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
result.Price = (-1) * record.Price.Value;
|
||||
|
||||
public void CreateQueue(VoipPhone newPhone)
|
||||
{
|
||||
newPhone.Settings.Queue = ((TwilioPhone)newPhone).CreateQueue(newPhone.Number, 5, string.Empty, 5);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Numbers
|
||||
|
||||
public VoipPhone BuyNumber(string phoneNumber)
|
||||
{
|
||||
var newNumber = IncomingPhoneNumberResource.Create(
|
||||
new CreateIncomingPhoneNumberOptions
|
||||
result.Duration = Convert.ToInt32(record.Duration);
|
||||
if (record.Uri != null)
|
||||
{
|
||||
PathAccountSid = accountSid,
|
||||
PhoneNumber = new PhoneNumber(phoneNumber)
|
||||
}, client);
|
||||
|
||||
return new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { Id = newNumber.Sid, Number = phoneNumber.Substring(1) };
|
||||
}
|
||||
|
||||
public VoipPhone DeleteNumber(VoipPhone phone)
|
||||
{
|
||||
IncomingPhoneNumberResource.Delete(phone.Id, client: client);
|
||||
return phone;
|
||||
}
|
||||
|
||||
public IEnumerable<VoipPhone> GetExistingPhoneNumbers()
|
||||
{
|
||||
var result = IncomingPhoneNumberResource.Read(client: client);
|
||||
return result.Select(r => new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { Id = r.Sid, Number = r.PhoneNumber.ToString() });
|
||||
}
|
||||
|
||||
public IEnumerable<VoipPhone> GetAvailablePhoneNumbers(PhoneNumberType phoneNumberType, string isoCountryCode)
|
||||
{
|
||||
return phoneNumberType switch
|
||||
{
|
||||
PhoneNumberType.Local => LocalResource.Read(isoCountryCode, voiceEnabled: true, client: client).Select(r => new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { Number = r.PhoneNumber.ToString() }),
|
||||
PhoneNumberType.TollFree => TollFreeResource.Read(isoCountryCode, voiceEnabled: true, client: client).Select(r => new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { Number = r.PhoneNumber.ToString() }),
|
||||
|
||||
_ => new List<VoipPhone>(),
|
||||
};
|
||||
}
|
||||
|
||||
public VoipPhone GetPhone(string phoneSid)
|
||||
{
|
||||
var phone = IncomingPhoneNumberResource.Fetch(phoneSid, client: client);
|
||||
|
||||
var result = new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility)
|
||||
{
|
||||
Id = phone.Sid,
|
||||
Number = phone.PhoneNumber.ToString(),
|
||||
Settings = new TwilioVoipSettings(AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility)
|
||||
};
|
||||
|
||||
if (phone.VoiceUrl == null)
|
||||
{
|
||||
result.Settings.VoiceUrl = result.Settings.Connect(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public VoipPhone GetPhone(VoipNumber data)
|
||||
{
|
||||
return new TwilioPhone(client, AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility)
|
||||
{
|
||||
Id = data.Id,
|
||||
Number = data.Number,
|
||||
Alias = data.Alias,
|
||||
Settings = new TwilioVoipSettings(data.Settings, AuthContext)
|
||||
};
|
||||
}
|
||||
|
||||
public VoipCall GetCall(string callId)
|
||||
{
|
||||
var result = new VoipCall { Id = callId };
|
||||
var count = 6;
|
||||
|
||||
while (count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
var call = CallResource.Fetch(result.Id, client: client);
|
||||
if (!call.Price.HasValue || string.IsNullOrEmpty(call.Duration))
|
||||
{
|
||||
count--;
|
||||
Thread.Sleep(10000);
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Price = (-1) * call.Price.Value;
|
||||
result.DialDuration = Convert.ToInt32(call.Duration);
|
||||
break;
|
||||
result.Uri = record.Uri;
|
||||
}
|
||||
catch (ApiException)
|
||||
break;
|
||||
}
|
||||
catch (ApiException)
|
||||
{
|
||||
count--;
|
||||
Thread.Sleep(10000);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void CreateQueue(VoipPhone newPhone)
|
||||
{
|
||||
newPhone.Settings.Queue = ((TwilioPhone)newPhone).CreateQueue(newPhone.Number, 5, string.Empty, 5);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Numbers
|
||||
|
||||
public VoipPhone BuyNumber(string phoneNumber)
|
||||
{
|
||||
var newNumber = IncomingPhoneNumberResource.Create(
|
||||
new CreateIncomingPhoneNumberOptions
|
||||
{
|
||||
PathAccountSid = _accountSid,
|
||||
PhoneNumber = new PhoneNumber(phoneNumber)
|
||||
}, _client);
|
||||
|
||||
return new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility) { Id = newNumber.Sid, Number = phoneNumber.Substring(1) };
|
||||
}
|
||||
|
||||
public VoipPhone DeleteNumber(VoipPhone phone)
|
||||
{
|
||||
IncomingPhoneNumberResource.Delete(phone.Id, client: _client);
|
||||
return phone;
|
||||
}
|
||||
|
||||
public IEnumerable<VoipPhone> GetExistingPhoneNumbers()
|
||||
{
|
||||
var result = IncomingPhoneNumberResource.Read(client: _client);
|
||||
return result.Select(r => new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility) { Id = r.Sid, Number = r.PhoneNumber.ToString() });
|
||||
}
|
||||
|
||||
public IEnumerable<VoipPhone> GetAvailablePhoneNumbers(PhoneNumberType phoneNumberType, string isoCountryCode)
|
||||
{
|
||||
return phoneNumberType switch
|
||||
{
|
||||
PhoneNumberType.Local => LocalResource.Read(isoCountryCode, voiceEnabled: true, client: _client).Select(r => new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility) { Number = r.PhoneNumber.ToString() }),
|
||||
PhoneNumberType.TollFree => TollFreeResource.Read(isoCountryCode, voiceEnabled: true, client: _client).Select(r => new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility) { Number = r.PhoneNumber.ToString() }),
|
||||
|
||||
_ => new List<VoipPhone>(),
|
||||
};
|
||||
}
|
||||
|
||||
public VoipPhone GetPhone(string phoneSid)
|
||||
{
|
||||
var phone = IncomingPhoneNumberResource.Fetch(phoneSid, client: _client);
|
||||
|
||||
var result = new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility)
|
||||
{
|
||||
Id = phone.Sid,
|
||||
Number = phone.PhoneNumber.ToString(),
|
||||
Settings = new TwilioVoipSettings(_authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility)
|
||||
};
|
||||
|
||||
if (phone.VoiceUrl == null)
|
||||
{
|
||||
result.Settings.VoiceUrl = result.Settings.Connect(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public VoipPhone GetPhone(VoipNumber data)
|
||||
{
|
||||
return new TwilioPhone(_client, _authContext, _tenantUtil, _securityContext, _baseCommonLinkUtility)
|
||||
{
|
||||
Id = data.Id,
|
||||
Number = data.Number,
|
||||
Alias = data.Alias,
|
||||
Settings = new TwilioVoipSettings(data.Settings, _authContext)
|
||||
};
|
||||
}
|
||||
|
||||
public VoipCall GetCall(string callId)
|
||||
{
|
||||
var result = new VoipCall { Id = callId };
|
||||
var count = 6;
|
||||
|
||||
while (count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
var call = CallResource.Fetch(result.Id, client: _client);
|
||||
if (!call.Price.HasValue || string.IsNullOrEmpty(call.Duration))
|
||||
{
|
||||
count--;
|
||||
Thread.Sleep(10000);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
result.Price = (-1) * call.Price.Value;
|
||||
result.DialDuration = Convert.ToInt32(call.Duration);
|
||||
break;
|
||||
}
|
||||
catch (ApiException)
|
||||
{
|
||||
count--;
|
||||
Thread.Sleep(10000);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetToken(Agent agent, int seconds = 60 * 60 * 24)
|
||||
{
|
||||
var scopes = new HashSet<IScope>
|
||||
return result;
|
||||
}
|
||||
|
||||
public string GetToken(Agent agent, int seconds = 60 * 60 * 24)
|
||||
{
|
||||
var scopes = new HashSet<IScope>
|
||||
{
|
||||
new IncomingClientScope(agent.ClientID)
|
||||
};
|
||||
var capability = new ClientCapability(accountSid, authToken, scopes: scopes);
|
||||
var capability = new ClientCapability(_accountSid, _authToken, scopes: scopes);
|
||||
|
||||
return capability.ToJwt();
|
||||
}
|
||||
|
||||
public void UpdateSettings(VoipPhone phone)
|
||||
{
|
||||
IncomingPhoneNumberResource.Update(phone.Id, voiceUrl: new Uri(phone.Settings.Connect(false)), client: client);
|
||||
}
|
||||
|
||||
public void DisablePhone(VoipPhone phone)
|
||||
{
|
||||
IncomingPhoneNumberResource.Update(phone.Id, voiceUrl: new Uri("https://demo.twilio.com/welcome/voice/"), client: client);
|
||||
}
|
||||
|
||||
#endregion
|
||||
return capability.ToJwt();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum PhoneNumberType
|
||||
public void UpdateSettings(VoipPhone phone)
|
||||
{
|
||||
Local,
|
||||
/* Mobile,*/
|
||||
TollFree
|
||||
IncomingPhoneNumberResource.Update(phone.Id, voiceUrl: new Uri(phone.Settings.Connect(false)), client: _client);
|
||||
}
|
||||
}
|
||||
|
||||
public void DisablePhone(VoipPhone phone)
|
||||
{
|
||||
IncomingPhoneNumberResource.Update(phone.Id, voiceUrl: new Uri("https://demo.twilio.com/welcome/voice/"), client: _client);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum PhoneNumberType
|
||||
{
|
||||
Local,
|
||||
/* Mobile,*/
|
||||
TollFree
|
||||
}
|
||||
|
@ -23,189 +23,187 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.VoipService.Twilio
|
||||
namespace ASC.VoipService.Twilio;
|
||||
|
||||
public class TwilioResponseHelper
|
||||
{
|
||||
public class TwilioResponseHelper
|
||||
private readonly VoipSettings _settings;
|
||||
private readonly string _baseUrl;
|
||||
private readonly AuthContext _authContext;
|
||||
private readonly TenantUtil _tenantUtil;
|
||||
private readonly SecurityContext _securityContext;
|
||||
|
||||
public TwilioResponseHelper(
|
||||
VoipSettings settings,
|
||||
string baseUrl,
|
||||
AuthContext authContext,
|
||||
TenantUtil tenantUtil,
|
||||
SecurityContext securityContext)
|
||||
{
|
||||
private readonly VoipSettings settings;
|
||||
private readonly string baseUrl;
|
||||
_settings = settings;
|
||||
_authContext = authContext;
|
||||
_tenantUtil = tenantUtil;
|
||||
_securityContext = securityContext;
|
||||
_baseUrl = baseUrl.TrimEnd('/') + "/twilio/";
|
||||
}
|
||||
|
||||
private AuthContext AuthContext { get; }
|
||||
private TenantUtil TenantUtil { get; }
|
||||
private SecurityContext SecurityContext { get; }
|
||||
public VoiceResponse Inbound(Tuple<Agent, bool> agentTuple)
|
||||
{
|
||||
var agent = agentTuple?.Item1;
|
||||
var anyOnline = agentTuple != null && agentTuple.Item2;
|
||||
var response = new VoiceResponse();
|
||||
|
||||
public TwilioResponseHelper(
|
||||
VoipSettings settings,
|
||||
string baseUrl,
|
||||
AuthContext authContext,
|
||||
TenantUtil tenantUtil,
|
||||
SecurityContext securityContext)
|
||||
if (_settings.WorkingHours != null && _settings.WorkingHours.Enabled)
|
||||
{
|
||||
this.settings = settings;
|
||||
AuthContext = authContext;
|
||||
TenantUtil = tenantUtil;
|
||||
SecurityContext = securityContext;
|
||||
this.baseUrl = baseUrl.TrimEnd('/') + "/twilio/";
|
||||
var now = _tenantUtil.DateTimeFromUtc(DateTime.UtcNow);
|
||||
if (!(_settings.WorkingHours.From <= now.TimeOfDay && _settings.WorkingHours.To >= now.TimeOfDay))
|
||||
{
|
||||
return AddVoiceMail(response);
|
||||
}
|
||||
}
|
||||
|
||||
public VoiceResponse Inbound(Tuple<Agent, bool> agentTuple)
|
||||
if (anyOnline)
|
||||
{
|
||||
var agent = agentTuple?.Item1;
|
||||
var anyOnline = agentTuple != null && agentTuple.Item2;
|
||||
var response = new VoiceResponse();
|
||||
|
||||
if (settings.WorkingHours != null && settings.WorkingHours.Enabled)
|
||||
if (!string.IsNullOrEmpty(_settings.GreetingAudio))
|
||||
{
|
||||
var now = TenantUtil.DateTimeFromUtc(DateTime.UtcNow);
|
||||
if (!(settings.WorkingHours.From <= now.TimeOfDay && settings.WorkingHours.To >= now.TimeOfDay))
|
||||
{
|
||||
return AddVoiceMail(response);
|
||||
}
|
||||
response.Play(Uri.EscapeDataString(_settings.GreetingAudio));
|
||||
}
|
||||
|
||||
if (anyOnline)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(settings.GreetingAudio))
|
||||
{
|
||||
response.Play(Uri.EscapeDataString(settings.GreetingAudio));
|
||||
}
|
||||
|
||||
response.Enqueue(settings.Queue.Name, GetEcho("Enqueue", agent != null), "POST",
|
||||
GetEcho("Wait", agent != null), "POST");
|
||||
}
|
||||
|
||||
return AddVoiceMail(response);
|
||||
response.Enqueue(_settings.Queue.Name, GetEcho("Enqueue", agent != null), "POST",
|
||||
GetEcho("Wait", agent != null), "POST");
|
||||
}
|
||||
|
||||
public VoiceResponse Outbound()
|
||||
return AddVoiceMail(response);
|
||||
}
|
||||
|
||||
public VoiceResponse Outbound()
|
||||
{
|
||||
return !_settings.Caller.AllowOutgoingCalls
|
||||
? new VoiceResponse()
|
||||
: AddToResponse(new VoiceResponse(), _settings.Caller);
|
||||
}
|
||||
|
||||
public VoiceResponse Dial()
|
||||
{
|
||||
return new VoiceResponse();
|
||||
}
|
||||
|
||||
public VoiceResponse Queue()
|
||||
{
|
||||
return new VoiceResponse();
|
||||
}
|
||||
|
||||
public VoiceResponse Enqueue(string queueResult)
|
||||
{
|
||||
return queueResult == "leave" ? AddVoiceMail(new VoiceResponse()) : new VoiceResponse();
|
||||
}
|
||||
|
||||
public VoiceResponse Dequeue()
|
||||
{
|
||||
return AddToResponse(new VoiceResponse(), _settings.Caller);
|
||||
}
|
||||
|
||||
public VoiceResponse Leave()
|
||||
{
|
||||
return AddVoiceMail(new VoiceResponse());
|
||||
}
|
||||
|
||||
public VoiceResponse Wait(string queueTime, string queueSize)
|
||||
{
|
||||
var response = new VoiceResponse();
|
||||
var queue = _settings.Queue;
|
||||
|
||||
if (Convert.ToInt32(queueTime) > queue.WaitTime || Convert.ToInt32(queueSize) > queue.Size) return response.Leave();
|
||||
|
||||
if (!string.IsNullOrEmpty(queue.WaitUrl))
|
||||
{
|
||||
return !settings.Caller.AllowOutgoingCalls
|
||||
? new VoiceResponse()
|
||||
: AddToResponse(new VoiceResponse(), settings.Caller);
|
||||
var gather = new Gather(method: "POST", action: GetEcho("gatherQueue"));
|
||||
gather.Play(Uri.EscapeDataString(queue.WaitUrl));
|
||||
response.Gather(gather);
|
||||
}
|
||||
|
||||
public VoiceResponse Dial()
|
||||
else
|
||||
{
|
||||
return new VoiceResponse();
|
||||
response.Pause(queue.WaitTime);
|
||||
}
|
||||
|
||||
public VoiceResponse Queue()
|
||||
return response;
|
||||
}
|
||||
|
||||
public VoiceResponse GatherQueue(string digits, List<Agent> availableOperators)
|
||||
{
|
||||
var response = new VoiceResponse();
|
||||
|
||||
if (digits == "#") return AddVoiceMail(response);
|
||||
|
||||
var oper = _settings.Operators.Find(r => r.PostFix == digits && availableOperators.Contains(r)) ??
|
||||
_settings.Operators.FirstOrDefault(r => availableOperators.Contains(r));
|
||||
|
||||
return oper != null ? AddToResponse(response, oper) : response;
|
||||
}
|
||||
|
||||
public VoiceResponse Redirect(string to)
|
||||
{
|
||||
if (to == "hold")
|
||||
{
|
||||
return new VoiceResponse();
|
||||
return new VoiceResponse().Play(Uri.EscapeDataString(_settings.HoldAudio), 0);
|
||||
}
|
||||
|
||||
public VoiceResponse Enqueue(string queueResult)
|
||||
|
||||
if (Guid.TryParse(to, out var newCallerId))
|
||||
{
|
||||
return queueResult == "leave" ? AddVoiceMail(new VoiceResponse()) : new VoiceResponse();
|
||||
_securityContext.AuthenticateMeWithoutCookie(newCallerId);
|
||||
}
|
||||
|
||||
public VoiceResponse Dequeue()
|
||||
return new VoiceResponse().Enqueue(_settings.Queue.Name, GetEcho("enqueue"), "POST",
|
||||
GetEcho("wait") + "&RedirectTo=" + to, "POST");
|
||||
}
|
||||
|
||||
public VoiceResponse VoiceMail()
|
||||
{
|
||||
return new VoiceResponse();
|
||||
}
|
||||
|
||||
private VoiceResponse AddToResponse(VoiceResponse response, Agent agent)
|
||||
{
|
||||
var dial = new Dial(method: "POST", action: GetEcho("dial"), timeout: agent.TimeOut, record: agent.Record ? "record-from-answer" : "do-not-record");
|
||||
|
||||
switch (agent.Answer)
|
||||
{
|
||||
return AddToResponse(new VoiceResponse(), settings.Caller);
|
||||
case AnswerType.Number:
|
||||
response.Dial(dial.Number(agent.PhoneNumber, method: "POST", url: GetEcho("client")));
|
||||
break;
|
||||
case AnswerType.Client:
|
||||
response.Dial(dial.Client(agent.ClientID, "POST", GetEcho("client")));
|
||||
break;
|
||||
case AnswerType.Sip:
|
||||
response.Dial(dial.Sip(agent.ClientID, method: "POST", url: GetEcho("client")));
|
||||
break;
|
||||
}
|
||||
|
||||
public VoiceResponse Leave()
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
private VoiceResponse AddVoiceMail(VoiceResponse response)
|
||||
{
|
||||
return string.IsNullOrEmpty(_settings.VoiceMail)
|
||||
? response.Say("")
|
||||
: response.Play(Uri.EscapeDataString(_settings.VoiceMail)).Record(method: "POST", action: GetEcho("voiceMail"), maxLength: 30);
|
||||
}
|
||||
|
||||
public string GetEcho(string action, bool user = true)
|
||||
{
|
||||
var result = _baseUrl.TrimEnd('/');
|
||||
|
||||
if (!string.IsNullOrEmpty(action))
|
||||
{
|
||||
return AddVoiceMail(new VoiceResponse());
|
||||
result += "/" + action.TrimStart('/');
|
||||
}
|
||||
|
||||
public VoiceResponse Wait(string queueTime, string queueSize)
|
||||
if (user)
|
||||
{
|
||||
var response = new VoiceResponse();
|
||||
var queue = settings.Queue;
|
||||
|
||||
if (Convert.ToInt32(queueTime) > queue.WaitTime || Convert.ToInt32(queueSize) > queue.Size) return response.Leave();
|
||||
|
||||
if (!string.IsNullOrEmpty(queue.WaitUrl))
|
||||
{
|
||||
var gather = new Gather(method: "POST", action: GetEcho("gatherQueue"));
|
||||
gather.Play(Uri.EscapeDataString(queue.WaitUrl));
|
||||
response.Gather(gather);
|
||||
}
|
||||
else
|
||||
{
|
||||
response.Pause(queue.WaitTime);
|
||||
}
|
||||
|
||||
return response;
|
||||
result += "?CallerId=" + _authContext.CurrentAccount.ID;
|
||||
}
|
||||
|
||||
public VoiceResponse GatherQueue(string digits, List<Agent> availableOperators)
|
||||
{
|
||||
var response = new VoiceResponse();
|
||||
|
||||
if (digits == "#") return AddVoiceMail(response);
|
||||
|
||||
var oper = settings.Operators.Find(r => r.PostFix == digits && availableOperators.Contains(r)) ??
|
||||
settings.Operators.FirstOrDefault(r => availableOperators.Contains(r));
|
||||
|
||||
return oper != null ? AddToResponse(response, oper) : response;
|
||||
}
|
||||
|
||||
public VoiceResponse Redirect(string to)
|
||||
{
|
||||
if (to == "hold")
|
||||
{
|
||||
return new VoiceResponse().Play(Uri.EscapeDataString(settings.HoldAudio), 0);
|
||||
}
|
||||
|
||||
|
||||
if (Guid.TryParse(to, out var newCallerId))
|
||||
{
|
||||
SecurityContext.AuthenticateMeWithoutCookie(newCallerId);
|
||||
}
|
||||
|
||||
return new VoiceResponse().Enqueue(settings.Queue.Name, GetEcho("enqueue"), "POST",
|
||||
GetEcho("wait") + "&RedirectTo=" + to, "POST");
|
||||
}
|
||||
|
||||
public VoiceResponse VoiceMail()
|
||||
{
|
||||
return new VoiceResponse();
|
||||
}
|
||||
|
||||
private VoiceResponse AddToResponse(VoiceResponse response, Agent agent)
|
||||
{
|
||||
var dial = new Dial(method: "POST", action: GetEcho("dial"), timeout: agent.TimeOut, record: agent.Record ? "record-from-answer" : "do-not-record");
|
||||
|
||||
switch (agent.Answer)
|
||||
{
|
||||
case AnswerType.Number:
|
||||
response.Dial(dial.Number(agent.PhoneNumber, method: "POST", url: GetEcho("client")));
|
||||
break;
|
||||
case AnswerType.Client:
|
||||
response.Dial(dial.Client(agent.ClientID, "POST", GetEcho("client")));
|
||||
break;
|
||||
case AnswerType.Sip:
|
||||
response.Dial(dial.Sip(agent.ClientID, method: "POST", url: GetEcho("client")));
|
||||
break;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
private VoiceResponse AddVoiceMail(VoiceResponse response)
|
||||
{
|
||||
return string.IsNullOrEmpty(settings.VoiceMail)
|
||||
? response.Say("")
|
||||
: response.Play(Uri.EscapeDataString(settings.VoiceMail)).Record(method: "POST", action: GetEcho("voiceMail"), maxLength: 30);
|
||||
}
|
||||
|
||||
public string GetEcho(string action, bool user = true)
|
||||
{
|
||||
var result = baseUrl.TrimEnd('/');
|
||||
|
||||
if (!string.IsNullOrEmpty(action))
|
||||
{
|
||||
result += "/" + action.TrimStart('/');
|
||||
}
|
||||
if (user)
|
||||
{
|
||||
result += "?CallerId=" + AuthContext.CurrentAccount.ID;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -26,58 +26,57 @@
|
||||
|
||||
using Uri = System.Uri;
|
||||
|
||||
namespace ASC.VoipService.Twilio
|
||||
namespace ASC.VoipService.Twilio;
|
||||
|
||||
public class TwilioVoipSettings : VoipSettings
|
||||
{
|
||||
public class TwilioVoipSettings : VoipSettings
|
||||
public TwilioVoipSettings(
|
||||
AuthContext authContext,
|
||||
TenantUtil tenantUtil,
|
||||
SecurityContext securityContext,
|
||||
BaseCommonLinkUtility baseCommonLinkUtility) :
|
||||
base(authContext, tenantUtil, securityContext, baseCommonLinkUtility)
|
||||
{ }
|
||||
|
||||
public TwilioVoipSettings(
|
||||
Uri voiceUrl,
|
||||
AuthContext authContext,
|
||||
TenantUtil tenantUtil,
|
||||
SecurityContext securityContext,
|
||||
BaseCommonLinkUtility baseCommonLinkUtility) :
|
||||
this(authContext, tenantUtil, securityContext, baseCommonLinkUtility)
|
||||
{
|
||||
public TwilioVoipSettings(
|
||||
AuthContext authContext,
|
||||
TenantUtil tenantUtil,
|
||||
SecurityContext securityContext,
|
||||
BaseCommonLinkUtility baseCommonLinkUtility) :
|
||||
base(authContext, tenantUtil, securityContext, baseCommonLinkUtility)
|
||||
{ }
|
||||
if (string.IsNullOrEmpty(voiceUrl.Query)) return;
|
||||
|
||||
public TwilioVoipSettings(
|
||||
Uri voiceUrl,
|
||||
AuthContext authContext,
|
||||
TenantUtil tenantUtil,
|
||||
SecurityContext securityContext,
|
||||
BaseCommonLinkUtility baseCommonLinkUtility) :
|
||||
this(authContext, tenantUtil, securityContext, baseCommonLinkUtility)
|
||||
JsonSettings = Encoding.UTF8.GetString(Convert.FromBase64String(HttpUtility.UrlDecode(HttpUtility.ParseQueryString(voiceUrl.Query)["settings"])));
|
||||
}
|
||||
|
||||
public TwilioVoipSettings(string settings, AuthContext authContext) : base(settings, authContext)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Connect(bool user = true, string contactId = null)
|
||||
{
|
||||
var result = GetEcho("", user);
|
||||
if (!string.IsNullOrEmpty(contactId))
|
||||
{
|
||||
if (string.IsNullOrEmpty(voiceUrl.Query)) return;
|
||||
|
||||
JsonSettings = Encoding.UTF8.GetString(Convert.FromBase64String(HttpUtility.UrlDecode(HttpUtility.ParseQueryString(voiceUrl.Query)["settings"])));
|
||||
result += "&ContactId=" + contactId;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public TwilioVoipSettings(string settings, AuthContext authContext) : base(settings, authContext)
|
||||
{
|
||||
}
|
||||
public override string Redirect(string to)
|
||||
{
|
||||
return GetEcho("redirect") + "&RedirectTo=" + to;
|
||||
}
|
||||
|
||||
public override string Connect(bool user = true, string contactId = null)
|
||||
{
|
||||
var result = GetEcho("", user);
|
||||
if (!string.IsNullOrEmpty(contactId))
|
||||
{
|
||||
result += "&ContactId=" + contactId;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public override string Dequeue(bool reject)
|
||||
{
|
||||
return GetEcho("dequeue") + "&Reject=" + reject;
|
||||
}
|
||||
|
||||
public override string Redirect(string to)
|
||||
{
|
||||
return GetEcho("redirect") + "&RedirectTo=" + to;
|
||||
}
|
||||
|
||||
public override string Dequeue(bool reject)
|
||||
{
|
||||
return GetEcho("dequeue") + "&Reject=" + reject;
|
||||
}
|
||||
|
||||
private string GetEcho(string method, bool user = true)
|
||||
{
|
||||
return new TwilioResponseHelper(this, BaseCommonLinkUtility.GetFullAbsolutePath(""), AuthContext, TenantUtil, SecurityContext).GetEcho(method, user);
|
||||
}
|
||||
private string GetEcho(string method, bool user = true)
|
||||
{
|
||||
return new TwilioResponseHelper(this, BaseCommonLinkUtility.GetFullAbsolutePath(""), AuthContext, TenantUtil, SecurityContext).GetEcho(method, user);
|
||||
}
|
||||
}
|
||||
|
@ -23,54 +23,46 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.VoipService
|
||||
namespace ASC.VoipService;
|
||||
|
||||
public class VoipCall : IMapFrom<CallContact>
|
||||
{
|
||||
public class VoipCall
|
||||
public string Id { get; set; }
|
||||
public string ParentCallId { get; set; }
|
||||
public string NumberFrom { get; set; }
|
||||
public string NumberTo { get; set; }
|
||||
public Guid AnsweredBy { get; set; }
|
||||
public DateTime DialDate { get; set; }
|
||||
public int DialDuration { get; set; }
|
||||
public VoipCallStatus? Status { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public int ContactId { get; set; }
|
||||
public bool ContactIsCompany { get; set; }
|
||||
public string ContactTitle { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public DateTime EndDialDate { get; set; }
|
||||
public VoipRecord VoipRecord { get; set; }
|
||||
public List<VoipCall> ChildCalls { get; set; }
|
||||
|
||||
public VoipCall()
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string ParentID { get; set; }
|
||||
|
||||
public string From { get; set; }
|
||||
|
||||
public string To { get; set; }
|
||||
|
||||
public Guid AnsweredBy { get; set; }
|
||||
|
||||
public DateTime DialDate { get; set; }
|
||||
|
||||
public int DialDuration { get; set; }
|
||||
|
||||
public VoipCallStatus? Status { get; set; }
|
||||
|
||||
public decimal Price { get; set; }
|
||||
|
||||
public int ContactId { get; set; }
|
||||
|
||||
public bool ContactIsCompany { get; set; }
|
||||
|
||||
public string ContactTitle { get; set; }
|
||||
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
public DateTime EndDialDate { get; set; }
|
||||
|
||||
public VoipRecord VoipRecord { get; set; }
|
||||
|
||||
public List<VoipCall> ChildCalls { get; set; }
|
||||
|
||||
public VoipCall()
|
||||
{
|
||||
ChildCalls = new List<VoipCall>();
|
||||
VoipRecord = new VoipRecord();
|
||||
}
|
||||
ChildCalls = new List<VoipCall>();
|
||||
VoipRecord = new VoipRecord();
|
||||
}
|
||||
|
||||
public enum VoipCallStatus
|
||||
public void Mapping(Profile profile)
|
||||
{
|
||||
Incoming,
|
||||
Outcoming,
|
||||
Answered,
|
||||
Missed
|
||||
profile.CreateMap<DbVoipCall, VoipCall>();
|
||||
|
||||
profile.CreateMap<CallContact, VoipCall>()
|
||||
.ConvertUsing<CallTypeConverter>();
|
||||
}
|
||||
}
|
||||
|
||||
public enum VoipCallStatus
|
||||
{
|
||||
Incoming,
|
||||
Outcoming,
|
||||
Answered,
|
||||
Missed
|
||||
}
|
@ -23,142 +23,128 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.VoipService
|
||||
namespace ASC.VoipService;
|
||||
|
||||
public class Agent
|
||||
{
|
||||
public class Agent
|
||||
public Guid Id { get; set; }
|
||||
public AnswerType Answer { get; set; }
|
||||
public string ClientID { get { return PhoneNumber + PostFix; } }
|
||||
public bool Record { get; set; }
|
||||
public int TimeOut { get; set; }
|
||||
public AgentStatus Status { get; set; }
|
||||
public bool AllowOutgoingCalls { get; set; }
|
||||
public string PostFix { get; set; }
|
||||
public string PhoneNumber { get; set; }
|
||||
public string RedirectToNumber { get; set; }
|
||||
|
||||
public Agent()
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public AnswerType Answer { get; set; }
|
||||
|
||||
public string ClientID { get { return PhoneNumber + PostFix; } }
|
||||
|
||||
public bool Record { get; set; }
|
||||
|
||||
public int TimeOut { get; set; }
|
||||
|
||||
public AgentStatus Status { get; set; }
|
||||
|
||||
public bool AllowOutgoingCalls { get; set; }
|
||||
|
||||
public string PostFix { get; set; }
|
||||
|
||||
public string PhoneNumber { get; set; }
|
||||
|
||||
public string RedirectToNumber { get; set; }
|
||||
|
||||
public Agent()
|
||||
{
|
||||
Status = AgentStatus.Offline;
|
||||
TimeOut = 30;
|
||||
AllowOutgoingCalls = true;
|
||||
Record = true;
|
||||
}
|
||||
|
||||
public Agent(Guid id, AnswerType answer, VoipPhone phone, string postFix)
|
||||
: this()
|
||||
{
|
||||
Id = id;
|
||||
Answer = answer;
|
||||
PhoneNumber = phone.Number;
|
||||
AllowOutgoingCalls = phone.Settings.AllowOutgoingCalls;
|
||||
Record = phone.Settings.Record;
|
||||
PostFix = postFix;
|
||||
}
|
||||
Status = AgentStatus.Offline;
|
||||
TimeOut = 30;
|
||||
AllowOutgoingCalls = true;
|
||||
Record = true;
|
||||
}
|
||||
|
||||
public class Queue
|
||||
public Agent(Guid id, AnswerType answer, VoipPhone phone, string postFix)
|
||||
: this()
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int Size { get; set; }
|
||||
public string WaitUrl { get; set; }
|
||||
public int WaitTime { get; set; }
|
||||
|
||||
public Queue() { }
|
||||
|
||||
public Queue(string id, string name, int size, string waitUrl, int waitTime)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
WaitUrl = waitUrl;
|
||||
WaitTime = waitTime;
|
||||
Size = size;
|
||||
}
|
||||
Id = id;
|
||||
Answer = answer;
|
||||
PhoneNumber = phone.Number;
|
||||
AllowOutgoingCalls = phone.Settings.AllowOutgoingCalls;
|
||||
Record = phone.Settings.Record;
|
||||
PostFix = postFix;
|
||||
}
|
||||
|
||||
public sealed class WorkingHours
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public TimeSpan? From { get; set; }
|
||||
public TimeSpan? To { get; set; }
|
||||
|
||||
public WorkingHours() { }
|
||||
|
||||
public WorkingHours(TimeSpan from, TimeSpan to)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
}
|
||||
|
||||
|
||||
private bool Equals(WorkingHours other)
|
||||
{
|
||||
return Enabled.Equals(other.Enabled) && From.Equals(other.From) && To.Equals(other.To);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != GetType()) return false;
|
||||
return Equals((WorkingHours)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Enabled, From, To);
|
||||
}
|
||||
}
|
||||
|
||||
public class VoipUpload
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Path { get; set; }
|
||||
public AudioType AudioType { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
}
|
||||
|
||||
#region Enum
|
||||
|
||||
public enum AnswerType
|
||||
{
|
||||
Number,
|
||||
Sip,
|
||||
Client
|
||||
}
|
||||
|
||||
public enum GreetingMessageVoice
|
||||
{
|
||||
Man,
|
||||
Woman,
|
||||
Alice
|
||||
}
|
||||
|
||||
public enum AgentStatus
|
||||
{
|
||||
Online,
|
||||
Paused,
|
||||
Offline
|
||||
}
|
||||
|
||||
public enum AudioType
|
||||
{
|
||||
Greeting,
|
||||
HoldUp,
|
||||
VoiceMail,
|
||||
Queue
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class Queue
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int Size { get; set; }
|
||||
public string WaitUrl { get; set; }
|
||||
public int WaitTime { get; set; }
|
||||
|
||||
public Queue() { }
|
||||
|
||||
public Queue(string id, string name, int size, string waitUrl, int waitTime)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
WaitUrl = waitUrl;
|
||||
WaitTime = waitTime;
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class WorkingHours
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public TimeSpan? From { get; set; }
|
||||
public TimeSpan? To { get; set; }
|
||||
|
||||
public WorkingHours() { }
|
||||
|
||||
public WorkingHours(TimeSpan from, TimeSpan to)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
}
|
||||
|
||||
|
||||
private bool Equals(WorkingHours other)
|
||||
{
|
||||
return Enabled.Equals(other.Enabled) && From.Equals(other.From) && To.Equals(other.To);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != GetType()) return false;
|
||||
return Equals((WorkingHours)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Enabled, From, To);
|
||||
}
|
||||
}
|
||||
|
||||
public class VoipUpload
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Path { get; set; }
|
||||
public AudioType AudioType { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
}
|
||||
|
||||
public enum AnswerType
|
||||
{
|
||||
Number,
|
||||
Sip,
|
||||
Client
|
||||
}
|
||||
|
||||
public enum GreetingMessageVoice
|
||||
{
|
||||
Man,
|
||||
Woman,
|
||||
Alice
|
||||
}
|
||||
|
||||
public enum AgentStatus
|
||||
{
|
||||
Online,
|
||||
Paused,
|
||||
Offline
|
||||
}
|
||||
|
||||
public enum AudioType
|
||||
{
|
||||
Greeting,
|
||||
HoldUp,
|
||||
VoiceMail,
|
||||
Queue
|
||||
}
|
@ -23,63 +23,67 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.VoipService
|
||||
using AutoMapper;
|
||||
|
||||
namespace ASC.VoipService;
|
||||
|
||||
public class VoipPhone
|
||||
{
|
||||
public class VoipPhone
|
||||
public string Id { get; set; }
|
||||
public string Number { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public VoipSettings Settings { get; set; }
|
||||
public Agent Caller
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Number { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public VoipSettings Settings { get; set; }
|
||||
public Agent Caller
|
||||
{
|
||||
get { return Settings.Caller; }
|
||||
}
|
||||
|
||||
public VoipPhone(AuthContext authContext, TenantUtil tenantUtil, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility)
|
||||
{
|
||||
Settings = new VoipSettings(authContext, tenantUtil, securityContext, baseCommonLinkUtility);
|
||||
}
|
||||
|
||||
public virtual VoipCall Call(string to, string contactId = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual VoipCall LocalCall(string to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual VoipCall RedirectCall(string callId, string to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual VoipCall HoldUp(string callId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual void AnswerQueueCall(string callId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual void RejectQueueCall(string callId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
get { return Settings.Caller; }
|
||||
}
|
||||
|
||||
public class VoipRecord
|
||||
public VoipPhone(AuthContext authContext, TenantUtil tenantUtil, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility)
|
||||
{
|
||||
public string Id { get; set; }
|
||||
Settings = new VoipSettings(authContext, tenantUtil, securityContext, baseCommonLinkUtility);
|
||||
}
|
||||
|
||||
public string Uri { get; set; }
|
||||
public virtual VoipCall Call(string to, string contactId = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public int Duration { get; set; }
|
||||
public virtual VoipCall LocalCall(string to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public decimal Price { get; set; }
|
||||
public virtual VoipCall RedirectCall(string callId, string to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual VoipCall HoldUp(string callId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual void AnswerQueueCall(string callId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual void RejectQueueCall(string callId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class VoipRecord : IMapFrom<DbVoipCall>
|
||||
{
|
||||
public string Sid { get; set; }
|
||||
public string Uri { get; set; }
|
||||
public int Duration { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
|
||||
public void Mapping(Profile profile)
|
||||
{
|
||||
profile.CreateMap<DbVoipCall, VoipRecord>()
|
||||
.ForMember(dest => dest.Price, opt => opt.MapFrom(src => src.RecordPrice));
|
||||
}
|
||||
}
|
@ -23,165 +23,152 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ASC.VoipService
|
||||
namespace ASC.VoipService;
|
||||
|
||||
public class VoipSettings
|
||||
{
|
||||
public class VoipSettings
|
||||
public string VoiceUrl { get; set; }
|
||||
public string Name { get; set; }
|
||||
public List<Agent> Operators { get; set; }
|
||||
public Queue Queue { get; set; }
|
||||
public Agent Caller { get { return Operators.FirstOrDefault(r => r.Id == AuthContext.CurrentAccount.ID); } }
|
||||
public WorkingHours WorkingHours { get; set; }
|
||||
public string VoiceMail { get; set; }
|
||||
public string GreetingAudio { get; set; }
|
||||
public string HoldAudio { get; set; }
|
||||
public bool AllowOutgoingCalls { get; set; }
|
||||
public bool Pause { get; set; }
|
||||
public bool Record { get; set; }
|
||||
internal string JsonSettings
|
||||
{
|
||||
public string VoiceUrl { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public List<Agent> Operators { get; set; }
|
||||
|
||||
public Queue Queue { get; set; }
|
||||
|
||||
public Agent Caller { get { return Operators.FirstOrDefault(r => r.Id == AuthContext.CurrentAccount.ID); } }
|
||||
|
||||
public WorkingHours WorkingHours { get; set; }
|
||||
|
||||
public string VoiceMail { get; set; }
|
||||
|
||||
public string GreetingAudio { get; set; }
|
||||
|
||||
public string HoldAudio { get; set; }
|
||||
|
||||
public bool AllowOutgoingCalls { get; set; }
|
||||
|
||||
public bool Pause { get; set; }
|
||||
|
||||
public bool Record { get; set; }
|
||||
|
||||
internal string JsonSettings
|
||||
get
|
||||
{
|
||||
get
|
||||
return JsonConvert.SerializeObject(
|
||||
new
|
||||
{
|
||||
Operators,
|
||||
GreetingAudio,
|
||||
Name,
|
||||
Queue,
|
||||
WorkingHours,
|
||||
VoiceMail,
|
||||
HoldAudio,
|
||||
AllowOutgoingCalls,
|
||||
Pause,
|
||||
Record
|
||||
},
|
||||
new JsonSerializerSettings { ContractResolver = CustomSerializeContractResolver.Instance });
|
||||
}
|
||||
set
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonConvert.SerializeObject(
|
||||
new
|
||||
{
|
||||
Operators,
|
||||
GreetingAudio,
|
||||
Name,
|
||||
Queue,
|
||||
WorkingHours,
|
||||
VoiceMail,
|
||||
HoldAudio,
|
||||
AllowOutgoingCalls,
|
||||
Pause,
|
||||
Record
|
||||
},
|
||||
new JsonSerializerSettings { ContractResolver = CustomSerializeContractResolver.Instance });
|
||||
var settings = JsonConvert.DeserializeObject<VoipSettings>(value, new JsonSerializerSettings { ContractResolver = CustomSerializeContractResolver.Instance });
|
||||
|
||||
Operators = settings.Operators ?? new List<Agent>();
|
||||
Name = settings.Name;
|
||||
Queue = settings.Queue;
|
||||
WorkingHours = settings.WorkingHours;
|
||||
GreetingAudio = settings.GreetingAudio;
|
||||
VoiceMail = settings.VoiceMail;
|
||||
HoldAudio = settings.HoldAudio;
|
||||
AllowOutgoingCalls = settings.AllowOutgoingCalls;
|
||||
Pause = settings.Pause;
|
||||
Record = settings.Record;
|
||||
}
|
||||
set
|
||||
catch (Exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = JsonConvert.DeserializeObject<VoipSettings>(value, new JsonSerializerSettings { ContractResolver = CustomSerializeContractResolver.Instance });
|
||||
|
||||
Operators = settings.Operators ?? new List<Agent>();
|
||||
Name = settings.Name;
|
||||
Queue = settings.Queue;
|
||||
WorkingHours = settings.WorkingHours;
|
||||
GreetingAudio = settings.GreetingAudio;
|
||||
VoiceMail = settings.VoiceMail;
|
||||
HoldAudio = settings.HoldAudio;
|
||||
AllowOutgoingCalls = settings.AllowOutgoingCalls;
|
||||
Pause = settings.Pause;
|
||||
Record = settings.Record;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected AuthContext AuthContext { get; }
|
||||
protected TenantUtil TenantUtil { get; }
|
||||
protected SecurityContext SecurityContext { get; }
|
||||
protected BaseCommonLinkUtility BaseCommonLinkUtility { get; }
|
||||
|
||||
public VoipSettings(AuthContext authContext, TenantUtil tenantUtil, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility)
|
||||
{
|
||||
Operators = new List<Agent>();
|
||||
AuthContext = authContext;
|
||||
TenantUtil = tenantUtil;
|
||||
SecurityContext = securityContext;
|
||||
BaseCommonLinkUtility = baseCommonLinkUtility;
|
||||
}
|
||||
|
||||
public VoipSettings(string settings, AuthContext authContext)
|
||||
{
|
||||
JsonSettings = settings;
|
||||
AuthContext = authContext;
|
||||
}
|
||||
|
||||
public virtual string Connect(bool user = true, string contactId = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual string Redirect(string to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual string Dequeue(bool reject)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return JsonSettings;
|
||||
}
|
||||
|
||||
public VoipSettings GetSettings(string settings)
|
||||
{
|
||||
return new VoipSettings(AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { JsonSettings = settings };
|
||||
}
|
||||
}
|
||||
|
||||
class CustomSerializeContractResolver : CamelCasePropertyNamesContractResolver
|
||||
protected AuthContext AuthContext { get; }
|
||||
protected TenantUtil TenantUtil { get; }
|
||||
protected SecurityContext SecurityContext { get; }
|
||||
protected BaseCommonLinkUtility BaseCommonLinkUtility { get; }
|
||||
|
||||
public VoipSettings(AuthContext authContext, TenantUtil tenantUtil, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility)
|
||||
{
|
||||
public static readonly CustomSerializeContractResolver Instance = new CustomSerializeContractResolver();
|
||||
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
var property = base.CreateProperty(member, memberSerialization);
|
||||
|
||||
if (property.PropertyName == "voiceMail")
|
||||
{
|
||||
property.Converter = new VoiceMailConverter();
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
Operators = new List<Agent>();
|
||||
AuthContext = authContext;
|
||||
TenantUtil = tenantUtil;
|
||||
SecurityContext = securityContext;
|
||||
BaseCommonLinkUtility = baseCommonLinkUtility;
|
||||
}
|
||||
|
||||
class VoiceMailConverter : JsonConverter
|
||||
public VoipSettings(string settings, AuthContext authContext)
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
JsonSettings = settings;
|
||||
AuthContext = authContext;
|
||||
}
|
||||
|
||||
public virtual string Connect(bool user = true, string contactId = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual string Redirect(string to)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual string Dequeue(bool reject)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return JsonSettings;
|
||||
}
|
||||
|
||||
public VoipSettings GetSettings(string settings)
|
||||
{
|
||||
return new VoipSettings(AuthContext, TenantUtil, SecurityContext, BaseCommonLinkUtility) { JsonSettings = settings };
|
||||
}
|
||||
}
|
||||
|
||||
class CustomSerializeContractResolver : CamelCasePropertyNamesContractResolver
|
||||
{
|
||||
public static readonly CustomSerializeContractResolver Instance = new CustomSerializeContractResolver();
|
||||
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
var property = base.CreateProperty(member, memberSerialization);
|
||||
|
||||
if (property.PropertyName == "voiceMail")
|
||||
{
|
||||
serializer.Serialize(writer, value);
|
||||
property.Converter = new VoiceMailConverter();
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
class VoiceMailConverter : JsonConverter
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
serializer.Serialize(writer, value);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.ValueType != null && reader.ValueType.Name == "String")
|
||||
{
|
||||
if (reader.ValueType != null && reader.ValueType.Name == "String")
|
||||
{
|
||||
return reader.Value;
|
||||
}
|
||||
|
||||
var jObject = JObject.Load(reader);
|
||||
var url = jObject.Value<string>("url");
|
||||
|
||||
return !string.IsNullOrEmpty(url) ? url : "";
|
||||
return reader.Value;
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var jObject = JObject.Load(reader);
|
||||
var url = jObject.Value<string>("url");
|
||||
|
||||
return !string.IsNullOrEmpty(url) ? url : "";
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -1,8 +1,4 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Linq;
|
||||
|
||||
global using ASC.Common;
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Caching;
|
||||
global using ASC.Common.Logging;
|
||||
global using ASC.Core;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -1,37 +1,31 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Globalization;
|
||||
global using System.IO;
|
||||
global using System.Linq;
|
||||
global using System.Text;
|
||||
global using System.Globalization;
|
||||
global using System.Reflection;
|
||||
|
||||
global using System.Text;
|
||||
|
||||
global using ASC.AuditTrail.Attributes;
|
||||
global using ASC.AuditTrail.Mappers;
|
||||
global using ASC.AuditTrail.Models;
|
||||
global using ASC.AuditTrail.Models.Mappings;
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Logging;
|
||||
global using ASC.Common.Mapping;
|
||||
global using ASC.Core.Common.EF;
|
||||
global using ASC.Core.Users;
|
||||
global using ASC.MessagingSystem.Core;
|
||||
global using ASC.MessagingSystem.Data;
|
||||
global using ASC.MessagingSystem.Models;
|
||||
global using ASC.Web.Studio.Utility;
|
||||
global using ASC.AuditTrail.Attributes;
|
||||
global using ASC.Web.Core.Files;
|
||||
global using ASC.Web.Files.Classes;
|
||||
global using ASC.Web.Files.Utils;
|
||||
global using ASC.AuditTrail.Models;
|
||||
global using ASC.AuditTrail.Models.Mappings;
|
||||
global using ASC.Common.Mapping;
|
||||
|
||||
global using ASC.Web.Studio.Utility;
|
||||
|
||||
global using Autofac;
|
||||
|
||||
global using Microsoft.Extensions.Configuration;
|
||||
global using Microsoft.Extensions.Options;
|
||||
|
||||
global using Newtonsoft.Json;
|
||||
|
||||
|
||||
global using AutoMapper;
|
||||
|
||||
|
||||
global using CsvHelper;
|
||||
|
||||
global using CsvHelper.Configuration;
|
||||
global using CsvHelper.Configuration;
|
||||
|
||||
global using Microsoft.Extensions.Options;
|
||||
|
||||
global using Newtonsoft.Json;
|
||||
|
@ -5,6 +5,7 @@
|
||||
<ApplicationIcon />
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -1,32 +1,22 @@
|
||||
global using System;
|
||||
global using System.IO;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Linq;
|
||||
global using System.Linq.Expressions;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
|
||||
global using System.Linq.Expressions;
|
||||
|
||||
global using ASC.Api.Core;
|
||||
global using ASC.ClearEvents.Services;
|
||||
global using ASC.Common;
|
||||
global using ASC.Common.Utils;
|
||||
global using ASC.Common.Caching;
|
||||
global using ASC.Common.DependencyInjection;
|
||||
global using ASC.Common.Logging;
|
||||
global using ASC.Common.Utils;
|
||||
global using ASC.Core.Common.EF;
|
||||
global using ASC.Core.Tenants;
|
||||
global using ASC.Core.Tenants;
|
||||
global using ASC.MessagingSystem.Data;
|
||||
global using ASC.MessagingSystem.Models;
|
||||
|
||||
|
||||
global using Autofac;
|
||||
global using Autofac.Extensions.DependencyInjection;
|
||||
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
global using Microsoft.Extensions.Configuration;
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
global using Microsoft.AspNetCore.Builder;
|
||||
global using Microsoft.Extensions.Hosting.WindowsServices;
|
||||
global using Microsoft.Extensions.Hosting;
|
||||
global using Microsoft.Extensions.Options;
|
||||
|
||||
|
||||
global using StackExchange.Redis.Extensions.Core.Configuration;
|
@ -6,7 +6,8 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<RazorCompileOnBuild>false</RazorCompileOnBuild>
|
||||
<GenerateMvcApplicationPartsAssemblyAttributes>false</GenerateMvcApplicationPartsAssemblyAttributes>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
|
@ -1,10 +1,4 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.IO;
|
||||
global using System.Linq;
|
||||
global using System.Runtime.InteropServices;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using System.Runtime.InteropServices;
|
||||
|
||||
global using ASC.Api.Collections;
|
||||
global using ASC.Api.Core;
|
||||
@ -18,7 +12,6 @@ global using ASC.Core.Billing;
|
||||
global using ASC.Data.Backup;
|
||||
global using ASC.Data.Backup.ApiModels;
|
||||
global using ASC.Data.Backup.Contracts;
|
||||
global using ASC.Data.Backup.Controllers;
|
||||
global using ASC.Data.Backup.Services;
|
||||
global using ASC.Data.Backup.Storage;
|
||||
global using ASC.Files.Core;
|
||||
@ -29,12 +22,7 @@ global using ASC.Web.Studio.Utility;
|
||||
global using Autofac;
|
||||
global using Autofac.Extensions.DependencyInjection;
|
||||
|
||||
global using Microsoft.AspNetCore.Builder;
|
||||
global using Microsoft.AspNetCore.Hosting;
|
||||
global using Microsoft.AspNetCore.Mvc;
|
||||
global using Microsoft.Extensions.Configuration;
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
global using Microsoft.Extensions.Hosting;
|
||||
global using Microsoft.Extensions.Options;
|
||||
|
||||
global using static ASC.Data.Backup.BackupAjaxHandler;
|
||||
|
@ -10,6 +10,7 @@
|
||||
<Product>ASC.ElasticSearch</Product>
|
||||
<Copyright>(c) Ascensio System SIA. All rights reserved</Copyright>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugType>full</DebugType>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user