diff --git a/DI/XDIHelperExtension.cs b/DI/XDIHelperExtension.cs
new file mode 100644
index 0000000..6580181
--- /dev/null
+++ b/DI/XDIHelperExtension.cs
@@ -0,0 +1,100 @@
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using xCommons.Configurations;
+using xCommons.Extensions;
+using xExceptions.Constants;
+using xStringService.Interfaces.Dtos;
+using xTermsAndConditionsService.Interfaces;
+using xTermsAndConditionsService.Providers;
+
+namespace xTermsAndConditionsService.DI
+{
+ public static class XDIHelperExtension
+ {
+ ///
+ /// Register TermsConditions Provider ...
+ ///
+ ///
+ ///
+ public static void AddXTermsConditionsService(
+ this IServiceCollection services,
+ ServiceLifetime lifeTime = ServiceLifetime.Transient
+ )
+ {
+ //
+ AddTermsConditionsService(
+ services: services,
+ lifeTime: lifeTime
+ );
+ }
+
+ //
+ #region Private ...
+ ///
+ /// a LogTag for Service ...
+ ///
+ private static string XLogTag = "XTermsConditionsService";
+
+ ///
+ /// print a log in Console ...
+ ///
+ ///
+ private static void Log(string message)
+ {
+ Console.WriteLine($"{XLogTag} => {message}");
+ }
+
+ ///
+ /// Add Terms and Conditions Provider Service ...
+ ///
+ ///
+ ///
+ private static void AddTermsConditionsService(
+ IServiceCollection services,
+ ServiceLifetime lifeTime = ServiceLifetime.Transient
+ )
+ {
+ //
+ var exception = XException.InvalidArgs.ToException(); ;
+
+ //
+ // Services ...
+ var isServicesExists = !services.IsNull();
+ if (!isServicesExists)
+ {
+ //
+ Log("AddTermsConditions failed, services not provided ...");
+ throw exception;
+ }
+
+ //
+ // Lifetime ...
+ var isLifetimeExists = !lifeTime.IsNull();
+ if (!isLifetimeExists)
+ {
+ //
+ Log("AddTermsConditions failed, lifetime not provided ...");
+ throw exception;
+ }
+
+ //
+ // Check Dependencies Exists ...
+ var isDependenciesPassed = !services
+ .GetRegisteredService()
+ .IsNull() &&
+ !services.GetRegisteredService()
+ .IsNull();
+ if (!isDependenciesPassed)
+ {
+ //
+ Log("AddTermsConditions failed due Dependencies Issues, IXStringRepository or XAppConfiguration not provided ...");
+ throw exception;
+ }
+
+ //
+ // Register Resource Providers ...
+ services.Add(new ServiceDescriptor(typeof(IXTermsAndComditionsProvider), typeof(XTermsAndComditionsProvider), lifeTime));
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Hubs/.gitkeep b/Hubs/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/Hubs/XTermsHub.cs b/Hubs/XTermsHub.cs
new file mode 100644
index 0000000..497e855
--- /dev/null
+++ b/Hubs/XTermsHub.cs
@@ -0,0 +1,107 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.SignalR;
+using Microsoft.Extensions.Logging;
+using xCommons.Extensions;
+using xPushService.Base;
+using xPushService.Constants;
+using xStringService.Models.Dtos;
+
+namespace xTermsAndConditionsService.Hubs
+{
+ ///
+ /// a Hub for Notifing Terms and Conditions Events to Connected Clients ...
+ ///
+ public class XTermsHub : XBaseHub
+ {
+ public XTermsHub(
+ ILogger logger
+ ) : base(logger)
+ {
+ }
+
+ //
+ #region Hub Actions ...
+ ///
+ /// Add Specified Terms and Conditions ...
+ ///
+ ///
+ ///
+ ///
+ public async Task Add(
+ XStringDto entity,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ var connectionId = Context.ConnectionId;
+ if (!connectionId.IsNullOrEmpty())
+ {
+ //
+ await Clients
+ .AllExcept(connectionId)
+ .SendAsync(
+ XBaseEntityHubAction.Add.GetStringValue(),
+ entity.ToJSON(camelCase: true),
+ connectionId,
+ cancellationToken
+ );
+ }
+ }
+
+ ///
+ /// Deleted Specified Terms and Conditions ...
+ ///
+ ///
+ ///
+ ///
+ public async Task Delete(
+ XStringDto entity,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ var connectionId = Context.ConnectionId;
+ if (!connectionId.IsNullOrEmpty())
+ {
+ //
+ await Clients
+ .AllExcept(connectionId)
+ .SendAsync(
+ XBaseEntityHubAction.Delete.GetStringValue(),
+ entity.ToJSON(camelCase: true),
+ connectionId,
+ cancellationToken
+ );
+ }
+ }
+
+ ///
+ /// Notify an Entity Updated ...
+ ///
+ ///
+ ///
+ ///
+ public async Task Update(
+ XStringDto entity,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ var connectionId = Context.ConnectionId;
+ if (!connectionId.IsNullOrEmpty())
+ {
+ //
+ await Clients
+ .AllExcept(connectionId)
+ .SendAsync(
+ XBaseEntityHubAction.Update.GetStringValue(),
+ entity.ToJSON(camelCase: true),
+ connectionId,
+ cancellationToken
+ );
+ }
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Interfaces/IXTermsAndComditionsController.cs b/Interfaces/IXTermsAndComditionsController.cs
new file mode 100644
index 0000000..32ff929
--- /dev/null
+++ b/Interfaces/IXTermsAndComditionsController.cs
@@ -0,0 +1,82 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using xModels.Base;
+
+namespace xTermsAndConditionsService.Interfaces
+{
+ ///
+ /// an Interface for Describing Terms and Conditions Management Controller Actions ...
+ ///
+ public interface IXTermsAndComditionsController
+ {
+ ///
+ /// Retrieved all Exists Languages Terms and Conditions ...
+ ///
+ ///
+ [HttpGet("Terms/Languages")]
+ Task>> TermsLanguages(
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Check Has Terms and Conditions based on Specified Language ...
+ ///
+ /// if null, used Default Language ...
+ ///
+ [HttpGet("Terms/{language}/Has")]
+ Task> HasTerms(
+ [FromRoute] string language = null,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Retrieve Terms and Conditions for Specified Language ...
+ ///
+ /// if null, used Default Language ...
+ ///
+ [HttpGet("Terms/{language}")]
+ Task> GetTerms(
+ [FromRoute] string language = null,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Add Terms and Conditions for Specified Language ...
+ ///
+ ///
+ ///
+ ///
+ [HttpPost("Terms/{language}")]
+ Task> AddTerms(
+ [FromRoute] string language,
+ [FromBody] XBaseValueContainer terms,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Remove Terms and Conditions of Specified Language ...
+ ///
+ ///
+ ///
+ [HttpDelete("Terms/{language}")]
+ Task> RemoveTerms(
+ [FromRoute] string language,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Update Terms and Conditions of Specified Language ...
+ ///
+ ///
+ ///
+ ///
+ [HttpPut("Terms/{language}")]
+ Task> UpdateTerms(
+ [FromRoute] string language,
+ [FromBody] XBaseValueContainer terms,
+ CancellationToken cancellationToken = default
+ );
+ }
+}
\ No newline at end of file
diff --git a/Interfaces/IXTermsAndComditionsProvider.cs b/Interfaces/IXTermsAndComditionsProvider.cs
new file mode 100644
index 0000000..0a840e9
--- /dev/null
+++ b/Interfaces/IXTermsAndComditionsProvider.cs
@@ -0,0 +1,95 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using xIdentityModels.Models;
+
+namespace xTermsAndConditionsService.Interfaces
+{
+ ///
+ /// an Interface for Describing Terms and Conditions Management Provided Actions ...
+ ///
+ public interface IXTermsAndComditionsProvider
+ {
+ #region Actions ...
+ ///
+ /// Retrieved all Exists Languages Terms and Conditions ...
+ ///
+ ///
+ ///
+ Task> TermsLanguages(
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Check Has Terms and Conditions based on Specified Language ...
+ ///
+ /// if null, used Default Language ...
+ ///
+ ///
+ Task HasTerms(
+ string language = null,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Retrieve Terms and Conditions for Specified Language ...
+ ///
+ /// if null, used Default Language ...
+ ///
+ ///
+ Task GetTerms(
+ string language = null,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Add Terms and Conditions for Specified Language ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task AddTerms(
+ string language,
+ string terms,
+ XUserClaimsInfoDto userInfo = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Remove Terms and Conditions of Specified Language ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task RemoveTerms(
+ string language,
+ XUserClaimsInfoDto userInfo = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Update Terms and Conditions of Specified Language ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task UpdateTerms(
+ string language,
+ string terms,
+ XUserClaimsInfoDto userInfo = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ );
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Providers/XTermsAndComditionsProvider.cs b/Providers/XTermsAndComditionsProvider.cs
new file mode 100644
index 0000000..31b09a9
--- /dev/null
+++ b/Providers/XTermsAndComditionsProvider.cs
@@ -0,0 +1,490 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.SignalR;
+using xCommons.Configurations;
+using xCommons.Extensions;
+using xExceptions.Constants;
+using xIdentityModels.Extensions;
+using xIdentityModels.Models;
+using xPushService.Constants;
+using xStringService.Interfaces.Dtos;
+using xStringService.Models.Dtos;
+using xTermsAndConditionsService.Hubs;
+using xTermsAndConditionsService.Interfaces;
+
+namespace xTermsAndConditionsService.Providers
+{
+ ///
+ /// an Implementation of Terms and Conditions Management Provided Actions ...
+ ///
+ public class XTermsAndComditionsProvider : IXTermsAndComditionsProvider
+ {
+ //
+ #region Props ...
+ private readonly string termsId;
+ private readonly string[] allowedRoles;
+ private readonly IHubContext hub;
+ private readonly XAppConfiguration appConfiguration;
+ private readonly IXStringServiceProvider stringProvider;
+ #endregion
+
+ //
+ #region Constructor ...
+ public XTermsAndComditionsProvider(
+ XAppConfiguration appConfiguration,
+ IXStringServiceProvider stringProvider,
+ IHubContext hub = null,
+ string termsId = "terms"
+ )
+ {
+ //
+ this.hub = hub;
+ this.termsId = termsId;
+ this.stringProvider = stringProvider;
+ this.appConfiguration = appConfiguration;
+
+ //
+ // Specified roles for Handling Dangerouse Actions ...
+ this.allowedRoles = new string[]
+ {
+ "admin"
+ };
+ }
+ #endregion
+
+ //
+ #region Actions ...
+ ///
+ /// Retrieved all Exists Languages Terms and Conditions ...
+ ///
+ ///
+ ///
+ public async Task> TermsLanguages(
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ var result = (await stringProvider
+ .FindManyAsync(
+ includeBuilder: null,
+ ignoreSoftDeleteds: true,
+ cancellationToken: cancellationToken,
+ predicate: x => x.ResourceTitle == termsId,
+ orderBuilder: x => x.OrderBy(y => y.ResourceTitle)
+ ))
+ .Select(x => x.Language);
+ return result;
+ }
+
+ ///
+ /// Check Has Terms and Conditions based on Specified Language ...
+ ///
+ /// if null, used Default Language ...
+ ///
+ ///
+ public async Task HasTerms(
+ string language = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ // Normalize ...
+ if (language.IsNullOrEmpty())
+ {
+ language = appConfiguration.DefaultLanguage;
+ }
+
+ //
+ var result = !(await GetString(
+ language: language,
+ cancellationToken: cancellationToken
+ ))
+ .IsNullOrDefault();
+
+ //
+ return result;
+ }
+
+ ///
+ /// Retrieve Terms and Conditions for Specified Language ...
+ ///
+ /// if null, used Default Language ...
+ ///
+ ///
+ public async Task GetTerms(
+ string language = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ var result = string.Empty;
+ var item = await GetString(
+ language: language,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ if (!item.IsNullOrDefault())
+ {
+ result = item.TranslatedValue;
+ }
+
+ //
+ return result;
+ }
+
+ ///
+ /// Add Terms and Conditions for Specified Language ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task AddTerms(
+ string language,
+ string terms,
+ XUserClaimsInfoDto userInfo = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ // Validate ...
+ var isValid =
+ !language.IsNullOrEmpty() &&
+ !terms.IsNullOrEmpty() &&
+ !userInfo.IsNullOrDefault() &&
+ language != appConfiguration.DefaultLanguage;
+ if (!isValid)
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Check Exists ...
+ isValid = await HasTerms(
+ language: language,
+ cancellationToken: cancellationToken
+ );
+ if (isValid)
+ {
+ XException.Duplicate.Throw();
+ }
+
+ //
+ // Check Permission ...
+ isValid = userInfo.HasAccess(allowedRoles);
+ if (!isValid)
+ {
+ XException.NotAllowed.Throw();
+ }
+
+ //
+ // Add Terms ...
+ XStringDto item = null;
+ try
+ {
+ //
+ item = new XStringDto
+ {
+ Language = language,
+ ResourceTitle = termsId,
+ TranslatedValue = terms,
+ };
+ item = await stringProvider.AddAsync(
+ item: item,
+ saveChanges: true,
+ cancellationToken: cancellationToken
+ );
+ }
+ catch { }
+
+ //
+ var result = string.Empty;
+ if (!item.IsNullOrDefault())
+ {
+ result = item.TranslatedValue;
+ }
+
+ //
+ if (!result.IsNullOrEmpty())
+ {
+ //
+ await SendPush(
+ connectionId: connectionId,
+ cancellationToken: cancellationToken,
+ payLoad: item.ToJSON(camelCase: true),
+ action: XBaseEntityHubAction.Add.GetStringValue()
+ );
+ }
+
+ //
+ return result;
+ }
+
+ ///
+ /// Remove Terms and Conditions of Specified Language ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task RemoveTerms(
+ string language,
+ XUserClaimsInfoDto userInfo = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ var isValid =
+ !language.IsNullOrEmpty() &&
+ !userInfo.IsNullOrDefault() &&
+ language != appConfiguration.DefaultLanguage;
+ if (!isValid)
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Check Exists ...
+ var item = await GetString(
+ language: language,
+ cancellationToken: cancellationToken
+ );
+ isValid = !item.IsNullOrDefault();
+ if (!isValid)
+ {
+ XException.NotFound.Throw();
+ }
+
+ //
+ // Check Permission ...
+ isValid = userInfo.HasAccess(allowedRoles);
+ if (!isValid)
+ {
+ XException.NotAllowed.Throw();
+ }
+
+ //
+ try
+ {
+ //
+ item = await stringProvider.RemoveAsync(
+ id: item.Id,
+ softDelete: false,
+ saveChanges: true,
+ cancellationToken: cancellationToken
+ );
+ }
+ catch
+ {
+ item = null;
+ }
+
+ //
+ var result = !item.IsNullOrDefault();
+ if (result)
+ {
+ //
+ await SendPush(
+ connectionId: connectionId,
+ cancellationToken: cancellationToken,
+ payLoad: item.ToJSON(camelCase: true),
+ action: XBaseEntityHubAction.Delete.GetStringValue()
+ );
+ }
+
+ //
+ return result;
+ }
+
+ ///
+ /// Update Terms and Conditions of Specified Language ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task UpdateTerms(
+ string language,
+ string terms,
+ XUserClaimsInfoDto userInfo = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ var isValid =
+ !terms.IsNullOrEmpty() &&
+ !language.IsNullOrEmpty() &&
+ !userInfo.IsNullOrDefault();
+ if (!isValid)
+ {
+ XException.InvalidArgs.Throw();
+ }
+
+ //
+ // Check Exists ...
+ var item = await GetString(
+ language: language,
+ cancellationToken: cancellationToken
+ );
+ isValid = !item.IsNullOrDefault();
+ if (!isValid)
+ {
+ XException.NotFound.Throw();
+ }
+
+ //
+ // Check Permission ...
+ isValid = userInfo.HasAccess(allowedRoles);
+ if (!isValid)
+ {
+ XException.NotAllowed.Throw();
+ }
+
+ //
+ try
+ {
+ //
+ item.TranslatedValue = terms;
+ item = await stringProvider.UpdateAsync(
+ id: item.Id,
+ item: item,
+ saveChanges: true,
+ cancellationToken: cancellationToken
+ );
+ }
+ catch
+ {
+ item = null;
+ }
+
+ //
+ var result = !item.IsNullOrDefault();
+ if (result)
+ {
+ //
+ await SendPush(
+ connectionId: connectionId,
+ cancellationToken: cancellationToken,
+ payLoad: item.ToJSON(camelCase: true),
+ action: XBaseEntityHubAction.Update.GetStringValue()
+ );
+ }
+
+ //
+ return result;
+ }
+ #endregion
+
+ //
+ #region Hub Actions ...
+ ///
+ /// Send Custom Push Message ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task SendPush(
+ string action,
+ string payLoad,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ var actions = new List
+ {
+ XBaseEntityHubAction.Add.GetStringValue(),
+ XBaseEntityHubAction.Update.GetStringValue(),
+ XBaseEntityHubAction.Delete.GetStringValue(),
+ };
+
+ //
+ // Validate ...
+ var isValid =
+ !hub.IsNull() &&
+ !action.IsNullOrEmpty() &&
+ actions.Contains(action);
+ if (!isValid)
+ {
+ return;
+ }
+
+ //
+ var clients = hub.Clients.All;
+ if (!connectionId.IsNullOrEmpty())
+ {
+ clients = hub.Clients.AllExcept(connectionId);
+ }
+ try
+ {
+ //
+ await clients.SendAsync(
+ action,
+ payLoad,
+ connectionId,
+ cancellationToken
+ );
+ }
+ catch { }
+ }
+ #endregion
+
+ //
+ #region Private ...
+ ///
+ /// Retrieve Terms and Conditions XString Entity ...
+ ///
+ ///
+ ///
+ ///
+ private async Task GetString(
+ string language = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ // Normalize ...
+ if (language.IsNullOrEmpty())
+ {
+ language = appConfiguration.DefaultLanguage;
+ }
+
+ //
+ // Retrieve ...
+ XStringDto result = null;
+ try
+ {
+ //
+ result = await stringProvider.FindOneAsync(
+ includeBuilder: null,
+ ignoreSoftDeleteds: true,
+ predicate: x =>
+ x.Language == language &&
+ x.ResourceTitle == termsId,
+ cancellationToken: cancellationToken
+ );
+ }
+ catch
+ {
+ }
+
+ //
+ return result;
+ }
+ #endregion
+ }
+}
\ No newline at end of file