diff --git a/Oqtane.Client/Modules/Admin/Visitors/Index.razor b/Oqtane.Client/Modules/Admin/Visitors/Index.razor index 16b5d1a8..11deb70f 100644 --- a/Oqtane.Client/Modules/Admin/Visitors/Index.razor +++ b/Oqtane.Client/Modules/Admin/Visitors/Index.razor @@ -81,6 +81,15 @@ else +
+ +
+ +
+

@@ -96,6 +105,7 @@ else private string _tracking; private string _filter = ""; private string _retention = ""; + private string _correlation = "true"; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; @@ -120,6 +130,7 @@ else var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); _filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter); _retention = SettingService.GetSetting(settings, "VisitorRetention", "30"); + _correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true"); } private async void TypeChanged(ChangeEventArgs e) @@ -170,6 +181,7 @@ else var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true); settings = SettingService.SetSetting(settings, "VisitorRetention", _retention, true); + settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true); await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId); AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success); diff --git a/Oqtane.Client/Resources/Modules/Admin/Visitors/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Visitors/Index.resx index f852194f..28bc46d1 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Visitors/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Visitors/Index.resx @@ -186,4 +186,10 @@ Retention (Days): + + Indicate if new visitors to this site should be correlated based on their IP Address + + + Correlate Visitors? + \ No newline at end of file diff --git a/Oqtane.Database.Sqlite/SqliteDatabase.cs b/Oqtane.Database.Sqlite/SqliteDatabase.cs index a6b078c9..8b91ffba 100644 --- a/Oqtane.Database.Sqlite/SqliteDatabase.cs +++ b/Oqtane.Database.Sqlite/SqliteDatabase.cs @@ -30,6 +30,11 @@ namespace Oqtane.Database.Sqlite return table.Column(name: name, nullable: false).Annotation("Sqlite:Autoincrement", true); } + public override void DropColumn(MigrationBuilder builder, string name, string table) + { + // not implemented as SQLite does not support dropping columns + } + public override string ConcatenateSql(params string[] values) { var returnValue = String.Empty; diff --git a/Oqtane.Server/Databases/DatabaseBase.cs b/Oqtane.Server/Databases/DatabaseBase.cs index bad8ad6c..d9a25371 100644 --- a/Oqtane.Server/Databases/DatabaseBase.cs +++ b/Oqtane.Server/Databases/DatabaseBase.cs @@ -1,6 +1,7 @@ using System; using System.Data; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations.Operations; using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders; using Oqtane.Databases.Interfaces; @@ -75,6 +76,11 @@ namespace Oqtane.Databases } + public virtual void DropColumn(MigrationBuilder builder, string name, string table) + { + builder.DropColumn(name, table); + } + public abstract DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString); } } diff --git a/Oqtane.Server/Databases/Interfaces/IDatabase.cs b/Oqtane.Server/Databases/Interfaces/IDatabase.cs index 2a6c0999..8d80e39e 100644 --- a/Oqtane.Server/Databases/Interfaces/IDatabase.cs +++ b/Oqtane.Server/Databases/Interfaces/IDatabase.cs @@ -1,5 +1,6 @@ using System.Data; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations.Operations; using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders; @@ -31,6 +32,8 @@ namespace Oqtane.Databases.Interfaces public void UpdateIdentityStoreTableNames(ModelBuilder builder); + public void DropColumn(MigrationBuilder builder, string name, string table); + public DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString); } } diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index d8693bd2..89f9895a 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -17,6 +17,7 @@ using Oqtane.Infrastructure; using Oqtane.Modules; using Oqtane.Repository; using Oqtane.Security; +using Oqtane.Services; using Oqtane.Shared; namespace Microsoft.Extensions.DependencyInjection @@ -222,12 +223,15 @@ namespace Microsoft.Extensions.DependencyInjection } } - // register server startup services - var startUps = assembly.GetInstances(); - foreach (var startup in startUps) - { - startup.ConfigureServices(services); - } + // dynamically register server startup services + assembly.GetInstances() + .ToList() + .ForEach(x => x.ConfigureServices(services)); + + // dynamically register client startup services (these services will only be used when running on Blazor Server) + assembly.GetInstances() + .ToList() + .ForEach(x => x.ConfigureServices(services)); } return services; } diff --git a/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs b/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs index 07c68631..d9275734 100644 --- a/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs +++ b/Oqtane.Server/Migrations/EntityBuilders/BaseEntityBuilder.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net.NetworkInformation; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations.Operations; using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders; using Oqtane.Databases.Interfaces; -using Oqtane.Interfaces; // ReSharper disable BuiltInTypeReferenceStyleForMemberAccess namespace Oqtane.Migrations.EntityBuilders @@ -126,7 +124,7 @@ namespace Oqtane.Migrations.EntityBuilders public void DropColumn(string name) { - _migrationBuilder.DropColumn(RewriteName(name), RewriteName(EntityTableName)); + ActiveDatabase.DropColumn(_migrationBuilder, RewriteName(name), RewriteName(EntityTableName)); } diff --git a/Oqtane.Server/Pages/_Host.cshtml.cs b/Oqtane.Server/Pages/_Host.cshtml.cs index 7c76e354..a5c89a9b 100644 --- a/Oqtane.Server/Pages/_Host.cshtml.cs +++ b/Oqtane.Server/Pages/_Host.cshtml.cs @@ -253,6 +253,35 @@ namespace Oqtane.Pages var VisitorCookie = "APP_VISITOR_" + SiteId.ToString(); if (!int.TryParse(Request.Cookies[VisitorCookie], out VisitorId)) + { + VisitorId = -1; + bool correlate = true; + setting = _settings.GetSetting(EntityNames.Site, SiteId, "VisitorCorrelation"); + if (setting != null) + { + correlate = bool.Parse(setting.SettingValue); + } + if (correlate) + { + var visitor = _visitors.GetVisitor(SiteId, RemoteIPAddress); + if (visitor != null) + { + VisitorId = visitor.VisitorId; + + Response.Cookies.Append( + VisitorCookie, + VisitorId.ToString(), + new CookieOptions() + { + Expires = DateTimeOffset.UtcNow.AddYears(1), + IsEssential = true + } + ); + } + } + } + + if (VisitorId == -1) { var visitor = new Visitor(); visitor.SiteId = SiteId; diff --git a/Oqtane.Server/Repository/Interfaces/IVisitorRepository.cs b/Oqtane.Server/Repository/Interfaces/IVisitorRepository.cs index 3b05ba90..d73a02ed 100644 --- a/Oqtane.Server/Repository/Interfaces/IVisitorRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IVisitorRepository.cs @@ -10,6 +10,7 @@ namespace Oqtane.Repository Visitor AddVisitor(Visitor visitor); Visitor UpdateVisitor(Visitor visitor); Visitor GetVisitor(int visitorId); + Visitor GetVisitor(int siteId, string IPAddress); void DeleteVisitor(int visitorId); int DeleteVisitors(int age); } diff --git a/Oqtane.Server/Repository/VisitorRepository.cs b/Oqtane.Server/Repository/VisitorRepository.cs index 6572d973..2f7c10dc 100644 --- a/Oqtane.Server/Repository/VisitorRepository.cs +++ b/Oqtane.Server/Repository/VisitorRepository.cs @@ -41,6 +41,11 @@ namespace Oqtane.Repository return _db.Visitor.Find(visitorId); } + public Visitor GetVisitor(int siteId, string IPAddress) + { + return _db.Visitor.FirstOrDefault(item => item.SiteId == siteId && item.IPAddress == IPAddress); + } + public void DeleteVisitor(int visitorId) { Visitor visitor = _db.Visitor.Find(visitorId); diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak index 461409ed..a4866e07 100644 Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak differ diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak index a99a0724..e5ebbf28 100644 Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak differ diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak index 859f8844..6f38671f 100644 Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak differ diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak index ad6c9b2a..acf13f1c 100644 Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak differ