From e749c7290950df66b55abd0fd190ee7e8a206911 Mon Sep 17 00:00:00 2001 From: Masterjun3 Date: Wed, 28 Feb 2024 22:57:32 +0100 Subject: [PATCH 1/3] implement custom localization --- TASVideos.Common/Constants.cs | 2 + TASVideos.Core/Services/UserManager.cs | 6 + TASVideos.Data/Entity/User.cs | 57 + ...28201932_AddUserLocaleSettings.Designer.cs | 3524 +++++++++++++++++ .../20240228201932_AddUserLocaleSettings.cs | 50 + .../ApplicationDbContextModelSnapshot.cs | 12 + .../CustomLocalizationMiddleware.cs | 131 + .../Profile/Models/ProfileSettingsModel.cs | 9 + TASVideos/Pages/Profile/Settings.cshtml | 22 + TASVideos/Pages/Profile/Settings.cshtml.cs | 45 +- TASVideos/Startup.cs | 2 + 11 files changed, 3859 insertions(+), 1 deletion(-) create mode 100644 TASVideos.Data/Migrations/20240228201932_AddUserLocaleSettings.Designer.cs create mode 100644 TASVideos.Data/Migrations/20240228201932_AddUserLocaleSettings.cs create mode 100644 TASVideos/Middleware/CustomLocalizationMiddleware.cs diff --git a/TASVideos.Common/Constants.cs b/TASVideos.Common/Constants.cs index 9308e6ec2..4ebe4dda3 100644 --- a/TASVideos.Common/Constants.cs +++ b/TASVideos.Common/Constants.cs @@ -24,6 +24,8 @@ public static class CacheKeys public const string AwardsCache = "AwardsCache"; public const string UnreadMessageCount = "UnreadMessageCountCache-"; public const string MovieTokens = "MovieTokenData"; + public const string UsersWithCustomLocale = "UsersWithCustomLocale"; + public const string CustomUserLocalePrefix = "CustomUserLocale-"; } // These perform site functions, maybe they should be in the database? diff --git a/TASVideos.Core/Services/UserManager.cs b/TASVideos.Core/Services/UserManager.cs index b396de6e4..90b1961f6 100644 --- a/TASVideos.Core/Services/UserManager.cs +++ b/TASVideos.Core/Services/UserManager.cs @@ -550,4 +550,10 @@ public bool IsPasswordAllowed(string? userName, string? email, string? password) return true; } + + public void ClearCustomLocaleCache(int userId) + { + _cache.Remove(CacheKeys.UsersWithCustomLocale); + _cache.Remove(CacheKeys.CustomUserLocalePrefix + userId); + } } diff --git a/TASVideos.Data/Entity/User.cs b/TASVideos.Data/Entity/User.cs index f11513aae..27b98b272 100644 --- a/TASVideos.Data/Entity/User.cs +++ b/TASVideos.Data/Entity/User.cs @@ -75,6 +75,10 @@ public class User : IdentityUser, ITrackable public UserPreference? AutoWatchTopic { get; set; } + public UserDateFormat DateFormat { get; set; } = UserDateFormat.Auto; + public UserTimeFormat TimeFormat { get; set; } = UserTimeFormat.Auto; + public UserDecimalFormat DecimalFormat { get; set; } = UserDecimalFormat.Auto; + public virtual ICollection UserRoles { get; set; } = new HashSet(); public virtual ICollection Submissions { get; set; } = new HashSet(); public virtual ICollection Publications { get; set; } = new HashSet(); @@ -130,6 +134,54 @@ public enum PreferredPronounTypes Other } +public enum UserDateFormat +{ + [Display(Name = "Automatic")] + Auto = 0, + + [Display(Name = "yyyy-MM-dd (2024-02-29)")] + YMMDD, + + [Display(Name = "dd/MM/yyyy (29/02/2024)")] + DDMMY, + + [Display(Name = "dd.MM.yyyy (29.02.2024)")] + DDMMYDot, + + [Display(Name = "d/M/yyyy (29/2/2024)")] + DMY, + + [Display(Name = "MM/dd/yyyy (02/29/2024)")] + MMDDY, + + [Display(Name = "M/d/yyyy (2/29/2024)")] + MDY, +} + +public enum UserTimeFormat +{ + [Display(Name = "Automatic")] + Auto = 0, + + [Display(Name = "24-hour clock (17:35)")] + Clock24Hour, + + [Display(Name = "12-hour clock (5:35 PM)")] + Clock12Hour +} + +public enum UserDecimalFormat +{ + [Display(Name = "Automatic")] + Auto = 0, + + [Display(Name = "Dot (1.23)")] + Dot, + + [Display(Name = "Comma (1,23)")] + Comma +} + public static class UserExtensions { public static async Task Exists(this IQueryable query, string userName) @@ -168,4 +220,9 @@ public static IQueryable ForUsers(this IQueryable query, IEnumerable { return query.Where(u => users.Contains(u.UserName)); } + + public static IQueryable ThatHaveCustomLocale(this IQueryable query) + { + return query.Where(u => u.DateFormat != UserDateFormat.Auto || u.TimeFormat != UserTimeFormat.Auto || u.DecimalFormat != UserDecimalFormat.Auto); + } } diff --git a/TASVideos.Data/Migrations/20240228201932_AddUserLocaleSettings.Designer.cs b/TASVideos.Data/Migrations/20240228201932_AddUserLocaleSettings.Designer.cs new file mode 100644 index 000000000..7b77c9283 --- /dev/null +++ b/TASVideos.Data/Migrations/20240228201932_AddUserLocaleSettings.Designer.cs @@ -0,0 +1,3524 @@ +// +using System; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; +using TASVideos.Data; + +#nullable disable + +namespace TASVideos.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240228201932_AddUserLocaleSettings")] + partial class AddUserLocaleSettings + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "citext"); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pg_trgm"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("TASVideos.Data.CustomAutoHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Changed") + .HasColumnType("citext") + .HasColumnName("changed"); + + b.Property("Created") + .HasColumnType("timestamp without time zone") + .HasColumnName("created"); + + b.Property("Kind") + .HasColumnType("integer") + .HasColumnName("kind"); + + b.Property("RowId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("row_id"); + + b.Property("TableName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("citext") + .HasColumnName("table_name"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_auto_history"); + + b.ToTable("auto_history", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Awards.Award", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("citext") + .HasColumnName("short_name"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("pk_awards"); + + b.HasIndex("ShortName") + .IsUnique() + .HasDatabaseName("ix_awards_short_name"); + + b.ToTable("awards", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Awards.PublicationAward", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AwardId") + .HasColumnType("integer") + .HasColumnName("award_id"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("Year") + .HasColumnType("integer") + .HasColumnName("year"); + + b.HasKey("Id") + .HasName("pk_publication_awards"); + + b.HasIndex("AwardId") + .HasDatabaseName("ix_publication_awards_award_id"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_awards_publication_id"); + + b.ToTable("publication_awards", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Awards.UserAward", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AwardId") + .HasColumnType("integer") + .HasColumnName("award_id"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("Year") + .HasColumnType("integer") + .HasColumnName("year"); + + b.HasKey("Id") + .HasName("pk_user_awards"); + + b.HasIndex("AwardId") + .HasDatabaseName("ix_user_awards_award_id"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_user_awards_user_id"); + + b.ToTable("user_awards", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.DeprecatedMovieFormat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Deprecated") + .HasColumnType("boolean") + .HasColumnName("deprecated"); + + b.Property("FileExtension") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("citext") + .HasColumnName("file_extension"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.HasKey("Id") + .HasName("pk_deprecated_movie_formats"); + + b.HasIndex("FileExtension") + .IsUnique() + .HasDatabaseName("ix_deprecated_movie_formats_file_extension"); + + b.ToTable("deprecated_movie_formats", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Flag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IconPath") + .HasMaxLength(48) + .HasColumnType("citext") + .HasColumnName("icon_path"); + + b.Property("LinkPath") + .HasMaxLength(48) + .HasColumnType("citext") + .HasColumnName("link_path"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("PermissionRestriction") + .HasColumnType("integer") + .HasColumnName("permission_restriction"); + + b.Property("Token") + .IsRequired() + .HasMaxLength(24) + .HasColumnType("citext") + .HasColumnName("token"); + + b.HasKey("Id") + .HasName("pk_flags"); + + b.HasIndex("Token") + .IsUnique() + .HasDatabaseName("ix_flags_token"); + + b.ToTable("flags", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.Forum", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("integer") + .HasColumnName("category_id"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("Ordinal") + .HasColumnType("integer") + .HasColumnName("ordinal"); + + b.Property("Restricted") + .HasColumnType("boolean") + .HasColumnName("restricted"); + + b.Property("ShortName") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("citext") + .HasColumnName("short_name"); + + b.HasKey("Id") + .HasName("pk_forums"); + + b.HasIndex("CategoryId") + .HasDatabaseName("ix_forums_category_id"); + + b.ToTable("forums", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Ordinal") + .HasColumnType("integer") + .HasColumnName("ordinal"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("citext") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("pk_forum_categories"); + + b.ToTable("forum_categories", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPoll", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CloseDate") + .HasColumnType("timestamp without time zone") + .HasColumnName("close_date"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("MultiSelect") + .HasColumnType("boolean") + .HasColumnName("multi_select"); + + b.Property("Question") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("question"); + + b.Property("TopicId") + .HasColumnType("integer") + .HasColumnName("topic_id"); + + b.HasKey("Id") + .HasName("pk_forum_polls"); + + b.ToTable("forum_polls", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPollOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Ordinal") + .HasColumnType("integer") + .HasColumnName("ordinal"); + + b.Property("PollId") + .HasColumnType("integer") + .HasColumnName("poll_id"); + + b.Property("Text") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("text"); + + b.HasKey("Id") + .HasName("pk_forum_poll_options"); + + b.HasIndex("PollId") + .HasDatabaseName("ix_forum_poll_options_poll_id"); + + b.ToTable("forum_poll_options", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPollOptionVote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("ip_address"); + + b.Property("PollOptionId") + .HasColumnType("integer") + .HasColumnName("poll_option_id"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_forum_poll_option_votes"); + + b.HasIndex("PollOptionId") + .HasDatabaseName("ix_forum_poll_option_votes_poll_option_id"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_forum_poll_option_votes_user_id"); + + b.ToTable("forum_poll_option_votes", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("EnableBbCode") + .HasColumnType("boolean") + .HasColumnName("enable_bb_code"); + + b.Property("EnableHtml") + .HasColumnType("boolean") + .HasColumnName("enable_html"); + + b.Property("ForumId") + .HasColumnType("integer") + .HasColumnName("forum_id"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("ip_address"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("PostEditedTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("post_edited_timestamp"); + + b.Property("PosterId") + .HasColumnType("integer") + .HasColumnName("poster_id"); + + b.Property("PosterMood") + .HasColumnType("integer") + .HasColumnName("poster_mood"); + + b.Property("SearchVector") + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasColumnName("search_vector") + .HasAnnotation("Npgsql:TsVectorConfig", "english") + .HasAnnotation("Npgsql:TsVectorProperties", new[] { "Text" }); + + b.Property("Subject") + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("subject"); + + b.Property("Text") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("text"); + + b.Property("TopicId") + .HasColumnType("integer") + .HasColumnName("topic_id"); + + b.HasKey("Id") + .HasName("pk_forum_posts"); + + b.HasIndex("ForumId") + .HasDatabaseName("ix_forum_posts_forum_id"); + + b.HasIndex("PosterId") + .HasDatabaseName("ix_forum_posts_poster_id"); + + b.HasIndex("SearchVector") + .HasDatabaseName("ix_forum_posts_search_vector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("SearchVector"), "GIN"); + + b.HasIndex("Text") + .HasDatabaseName("ix_forum_posts_text"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Text"), "gin"); + NpgsqlIndexBuilderExtensions.HasOperators(b.HasIndex("Text"), new[] { "gin_trgm_ops" }); + + b.HasIndex("TopicId") + .HasDatabaseName("ix_forum_posts_topic_id"); + + b.ToTable("forum_posts", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumTopic", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("ForumId") + .HasColumnType("integer") + .HasColumnName("forum_id"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("IsLocked") + .HasColumnType("boolean") + .HasColumnName("is_locked"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("PollId") + .HasColumnType("integer") + .HasColumnName("poll_id"); + + b.Property("PosterId") + .HasColumnType("integer") + .HasColumnName("poster_id"); + + b.Property("SubmissionId") + .HasColumnType("integer") + .HasColumnName("submission_id"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("title"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("pk_forum_topics"); + + b.HasIndex("ForumId") + .HasDatabaseName("ix_forum_topics_forum_id"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_forum_topics_game_id"); + + b.HasIndex("PollId") + .IsUnique() + .HasDatabaseName("ix_forum_topics_poll_id"); + + b.HasIndex("PosterId") + .HasDatabaseName("ix_forum_topics_poster_id"); + + b.HasIndex("SubmissionId") + .IsUnique() + .HasDatabaseName("ix_forum_topics_submission_id"); + + b.ToTable("forum_topics", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumTopicWatch", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("ForumTopicId") + .HasColumnType("integer") + .HasColumnName("forum_topic_id"); + + b.Property("IsNotified") + .HasColumnType("boolean") + .HasColumnName("is_notified"); + + b.HasKey("UserId", "ForumTopicId") + .HasName("pk_forum_topic_watches"); + + b.HasIndex("ForumTopicId") + .HasDatabaseName("ix_forum_topic_watches_forum_topic_id"); + + b.ToTable("forum_topic_watches", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.Game", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Abbreviation") + .HasMaxLength(24) + .HasColumnType("citext") + .HasColumnName("abbreviation"); + + b.Property("Aliases") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("aliases"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.Property("GameResourcesPage") + .HasMaxLength(300) + .HasColumnType("citext") + .HasColumnName("game_resources_page"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("ScreenshotUrl") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("screenshot_url"); + + b.HasKey("Id") + .HasName("pk_games"); + + b.HasIndex("Abbreviation") + .IsUnique() + .HasDatabaseName("ix_games_abbreviation"); + + b.ToTable("games", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGameGroup", b => + { + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("GameGroupId") + .HasColumnType("integer") + .HasColumnName("game_group_id"); + + b.HasKey("GameId", "GameGroupId") + .HasName("pk_game_game_groups"); + + b.HasIndex("GameGroupId") + .HasDatabaseName("ix_game_game_groups_game_group_id"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_game_game_groups_game_id"); + + b.ToTable("game_game_groups", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGenre", b => + { + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("GenreId") + .HasColumnType("integer") + .HasColumnName("genre_id"); + + b.HasKey("GameId", "GenreId") + .HasName("pk_game_genres"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_game_genres_game_id"); + + b.HasIndex("GenreId") + .HasDatabaseName("ix_game_genres_genre_id"); + + b.ToTable("game_genres", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGoal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.HasKey("Id") + .HasName("pk_game_goals"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_game_goals_game_id"); + + b.ToTable("game_goals", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Abbreviation") + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("abbreviation"); + + b.Property("Description") + .HasMaxLength(2000) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_game_groups"); + + b.HasIndex("Abbreviation") + .IsUnique() + .HasDatabaseName("ix_game_groups_abbreviation"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_game_groups_name"); + + b.ToTable("game_groups", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameSystem", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id") + .HasAnnotation("DatabaseGenerated", DatabaseGeneratedOption.None); + + b.Property("Code") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("citext") + .HasColumnName("code"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.HasKey("Id") + .HasName("pk_game_systems"); + + b.HasIndex("Code") + .IsUnique() + .HasDatabaseName("ix_game_systems_code"); + + b.ToTable("game_systems", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameSystemFrameRate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("FrameRate") + .HasColumnType("double precision") + .HasColumnName("frame_rate"); + + b.Property("GameSystemId") + .HasColumnType("integer") + .HasColumnName("game_system_id"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Obsolete") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("obsolete"); + + b.Property("Preliminary") + .HasColumnType("boolean") + .HasColumnName("preliminary"); + + b.Property("RegionCode") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("citext") + .HasColumnName("region_code"); + + b.HasKey("Id") + .HasName("pk_game_system_frame_rates"); + + b.HasIndex("GameSystemId") + .HasDatabaseName("ix_game_system_frame_rates_game_system_id"); + + b.ToTable("game_system_frame_rates", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Md5") + .HasMaxLength(32) + .HasColumnType("citext") + .HasColumnName("md5"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("Region") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("region"); + + b.Property("Sha1") + .HasMaxLength(40) + .HasColumnType("citext") + .HasColumnName("sha1"); + + b.Property("SystemId") + .HasColumnType("integer") + .HasColumnName("system_id"); + + b.Property("TitleOverride") + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("title_override"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("Version") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("version"); + + b.HasKey("Id") + .HasName("pk_game_versions"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_game_versions_game_id"); + + b.HasIndex("SystemId") + .HasDatabaseName("ix_game_versions_system_id"); + + b.ToTable("game_versions", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.Genre", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.HasKey("Id") + .HasName("pk_genres"); + + b.ToTable("genres", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.IpBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Mask") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("citext") + .HasColumnName("mask"); + + b.HasKey("Id") + .HasName("pk_ip_bans"); + + b.HasIndex("Mask") + .IsUnique() + .HasDatabaseName("ix_ip_bans_mask"); + + b.ToTable("ip_bans", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.MediaPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Body") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("citext") + .HasColumnName("body"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Group") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("group"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Link") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("link"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("citext") + .HasColumnName("title"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("type"); + + b.Property("User") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("user"); + + b.HasKey("Id") + .HasName("pk_media_posts"); + + b.ToTable("media_posts", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PrivateMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("DeletedForFromUser") + .HasColumnType("boolean") + .HasColumnName("deleted_for_from_user"); + + b.Property("DeletedForToUser") + .HasColumnType("boolean") + .HasColumnName("deleted_for_to_user"); + + b.Property("EnableBbCode") + .HasColumnType("boolean") + .HasColumnName("enable_bb_code"); + + b.Property("EnableHtml") + .HasColumnType("boolean") + .HasColumnName("enable_html"); + + b.Property("FromUserId") + .HasColumnType("integer") + .HasColumnName("from_user_id"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("ReadOn") + .HasColumnType("timestamp without time zone") + .HasColumnName("read_on"); + + b.Property("SavedForFromUser") + .HasColumnType("boolean") + .HasColumnName("saved_for_from_user"); + + b.Property("SavedForToUser") + .HasColumnType("boolean") + .HasColumnName("saved_for_to_user"); + + b.Property("Subject") + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("subject"); + + b.Property("Text") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("text"); + + b.Property("ToUserId") + .HasColumnType("integer") + .HasColumnName("to_user_id"); + + b.HasKey("Id") + .HasName("pk_private_messages"); + + b.HasIndex("FromUserId") + .HasDatabaseName("ix_private_messages_from_user_id"); + + b.HasIndex("ToUserId", "ReadOn", "DeletedForToUser") + .HasDatabaseName("ix_private_messages_to_user_id_read_on_deleted_for_to_user"); + + b.ToTable("private_messages", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Publication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdditionalAuthors") + .HasMaxLength(200) + .HasColumnType("citext") + .HasColumnName("additional_authors"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("EmulatorVersion") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("emulator_version"); + + b.Property("Frames") + .HasColumnType("integer") + .HasColumnName("frames"); + + b.Property("GameGoalId") + .HasColumnType("integer") + .HasColumnName("game_goal_id"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("GameVersionId") + .HasColumnType("integer") + .HasColumnName("game_version_id"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("MovieFile") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("movie_file"); + + b.Property("MovieFileName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("citext") + .HasColumnName("movie_file_name"); + + b.Property("ObsoletedById") + .HasColumnType("integer") + .HasColumnName("obsoleted_by_id"); + + b.Property("PublicationClassId") + .HasColumnType("integer") + .HasColumnName("publication_class_id"); + + b.Property("RerecordCount") + .HasColumnType("integer") + .HasColumnName("rerecord_count"); + + b.Property("SubmissionId") + .HasColumnType("integer") + .HasColumnName("submission_id"); + + b.Property("SystemFrameRateId") + .HasColumnType("integer") + .HasColumnName("system_frame_rate_id"); + + b.Property("SystemId") + .HasColumnType("integer") + .HasColumnName("system_id"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("pk_publications"); + + b.HasIndex("GameGoalId") + .HasDatabaseName("ix_publications_game_goal_id"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_publications_game_id"); + + b.HasIndex("GameVersionId") + .HasDatabaseName("ix_publications_game_version_id"); + + b.HasIndex("MovieFileName") + .IsUnique() + .HasDatabaseName("ix_publications_movie_file_name"); + + b.HasIndex("ObsoletedById") + .HasDatabaseName("ix_publications_obsoleted_by_id"); + + b.HasIndex("PublicationClassId") + .HasDatabaseName("ix_publications_publication_class_id"); + + b.HasIndex("SubmissionId") + .IsUnique() + .HasDatabaseName("ix_publications_submission_id"); + + b.HasIndex("SystemFrameRateId") + .HasDatabaseName("ix_publications_system_frame_rate_id"); + + b.HasIndex("SystemId") + .HasDatabaseName("ix_publications_system_id"); + + b.ToTable("publications", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationAuthor", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("Ordinal") + .HasColumnType("integer") + .HasColumnName("ordinal"); + + b.HasKey("UserId", "PublicationId") + .HasName("pk_publication_authors"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_authors_publication_id"); + + b.ToTable("publication_authors", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationClass", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id") + .HasAnnotation("DatabaseGenerated", DatabaseGeneratedOption.None); + + b.Property("IconPath") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("icon_path"); + + b.Property("Link") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("link"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("Weight") + .HasColumnType("double precision") + .HasColumnName("weight"); + + b.HasKey("Id") + .HasName("pk_publication_classes"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("ix_publication_classes_name"); + + b.ToTable("publication_classes", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Description") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("FileData") + .HasColumnType("bytea") + .HasColumnName("file_data"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("path"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("pk_publication_files"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_files_publication_id"); + + b.ToTable("publication_files", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationFlag", b => + { + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("FlagId") + .HasColumnType("integer") + .HasColumnName("flag_id"); + + b.HasKey("PublicationId", "FlagId") + .HasName("pk_publication_flags"); + + b.HasIndex("FlagId") + .HasDatabaseName("ix_publication_flags_flag_id"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_flags_publication_id"); + + b.ToTable("publication_flags", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationMaintenanceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Log") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("citext") + .HasColumnName("log"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("TimeStamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("time_stamp"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_publication_maintenance_logs"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_maintenance_logs_publication_id"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_publication_maintenance_logs_user_id"); + + b.ToTable("publication_maintenance_logs", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationRating", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("Value") + .HasColumnType("double precision") + .HasColumnName("value"); + + b.HasKey("UserId", "PublicationId") + .HasName("pk_publication_ratings"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_ratings_publication_id"); + + b.HasIndex("UserId", "PublicationId") + .IsUnique() + .HasDatabaseName("ix_publication_ratings_user_id_publication_id"); + + b.ToTable("publication_ratings", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationTag", b => + { + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("TagId") + .HasColumnType("integer") + .HasColumnName("tag_id"); + + b.HasKey("PublicationId", "TagId") + .HasName("pk_publication_tags"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_tags_publication_id"); + + b.HasIndex("TagId") + .HasDatabaseName("ix_publication_tags_tag_id"); + + b.ToTable("publication_tags", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationUrl", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("DisplayName") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("PublicationId") + .HasColumnType("integer") + .HasColumnName("publication_id"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("url"); + + b.HasKey("Id") + .HasName("pk_publication_urls"); + + b.HasIndex("PublicationId") + .HasDatabaseName("ix_publication_urls_publication_id"); + + b.HasIndex("Type") + .HasDatabaseName("ix_publication_urls_type"); + + b.ToTable("publication_urls", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AutoAssignPostCount") + .HasColumnType("integer") + .HasColumnName("auto_assign_post_count"); + + b.Property("AutoAssignPublications") + .HasColumnType("boolean") + .HasColumnName("auto_assign_publications"); + + b.Property("ConcurrencyStamp") + .HasColumnType("citext") + .HasColumnName("concurrency_stamp"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("IsDefault") + .HasColumnType("boolean") + .HasColumnName("is_default"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("NormalizedName") + .HasColumnType("citext") + .HasColumnName("normalized_name"); + + b.HasKey("Id") + .HasName("pk_roles"); + + b.ToTable("roles", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.RoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("citext") + .HasColumnName("claim_type"); + + b.Property("ClaimValue") + .HasColumnType("citext") + .HasColumnName("claim_value"); + + b.Property("RoleId") + .HasColumnType("integer") + .HasColumnName("role_id"); + + b.HasKey("Id") + .HasName("pk_role_claims"); + + b.HasIndex("RoleId") + .HasDatabaseName("ix_role_claims_role_id"); + + b.ToTable("role_claims", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.RoleLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Link") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("citext") + .HasColumnName("link"); + + b.Property("RoleId") + .HasColumnType("integer") + .HasColumnName("role_id"); + + b.HasKey("Id") + .HasName("pk_role_links"); + + b.HasIndex("RoleId") + .HasDatabaseName("ix_role_links_role_id"); + + b.ToTable("role_links", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.RolePermission", b => + { + b.Property("RoleId") + .HasColumnType("integer") + .HasColumnName("role_id"); + + b.Property("PermissionId") + .HasColumnType("integer") + .HasColumnName("permission_id"); + + b.Property("CanAssign") + .HasColumnType("boolean") + .HasColumnName("can_assign"); + + b.HasKey("RoleId", "PermissionId") + .HasName("pk_role_permission"); + + b.ToTable("role_permission", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Submission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdditionalAuthors") + .HasMaxLength(200) + .HasColumnType("citext") + .HasColumnName("additional_authors"); + + b.Property("Annotations") + .HasColumnType("citext") + .HasColumnName("annotations"); + + b.Property("Branch") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("branch"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("CycleCount") + .HasColumnType("bigint") + .HasColumnName("cycle_count"); + + b.Property("EmulatorVersion") + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("emulator_version"); + + b.Property("EncodeEmbedLink") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("encode_embed_link"); + + b.Property("Frames") + .HasColumnType("integer") + .HasColumnName("frames"); + + b.Property("GameGoalId") + .HasColumnType("integer") + .HasColumnName("game_goal_id"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("GameName") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("game_name"); + + b.Property("GameVersionId") + .HasColumnType("integer") + .HasColumnName("game_version_id"); + + b.Property("ImportedTime") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(16, 4)") + .HasDefaultValue(0m) + .HasColumnName("imported_time"); + + b.Property("IntendedClassId") + .HasColumnType("integer") + .HasColumnName("intended_class_id"); + + b.Property("JudgeId") + .HasColumnType("integer") + .HasColumnName("judge_id"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("LegacyTime") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(16, 4)") + .HasDefaultValue(0m) + .HasColumnName("legacy_time"); + + b.Property("MovieExtension") + .HasMaxLength(8) + .HasColumnType("citext") + .HasColumnName("movie_extension"); + + b.Property("MovieFile") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("movie_file"); + + b.Property("MovieStartType") + .HasColumnType("integer") + .HasColumnName("movie_start_type"); + + b.Property("PublisherId") + .HasColumnType("integer") + .HasColumnName("publisher_id"); + + b.Property("RejectionReasonId") + .HasColumnType("integer") + .HasColumnName("rejection_reason_id"); + + b.Property("RerecordCount") + .HasColumnType("integer") + .HasColumnName("rerecord_count"); + + b.Property("RomName") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("rom_name"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("SubmittedGameVersion") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("submitted_game_version"); + + b.Property("SubmitterId") + .HasColumnType("integer") + .HasColumnName("submitter_id"); + + b.Property("SystemFrameRateId") + .HasColumnType("integer") + .HasColumnName("system_frame_rate_id"); + + b.Property("SystemId") + .HasColumnType("integer") + .HasColumnName("system_id"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("citext") + .HasColumnName("title"); + + b.Property("TopicId") + .HasColumnType("integer") + .HasColumnName("topic_id"); + + b.Property("Warnings") + .HasMaxLength(4096) + .HasColumnType("citext") + .HasColumnName("warnings"); + + b.HasKey("Id") + .HasName("pk_submissions"); + + b.HasIndex("GameGoalId") + .HasDatabaseName("ix_submissions_game_goal_id"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_submissions_game_id"); + + b.HasIndex("GameVersionId") + .HasDatabaseName("ix_submissions_game_version_id"); + + b.HasIndex("IntendedClassId") + .HasDatabaseName("ix_submissions_intended_class_id"); + + b.HasIndex("JudgeId") + .HasDatabaseName("ix_submissions_judge_id"); + + b.HasIndex("PublisherId") + .HasDatabaseName("ix_submissions_publisher_id"); + + b.HasIndex("RejectionReasonId") + .HasDatabaseName("ix_submissions_rejection_reason_id"); + + b.HasIndex("Status") + .HasDatabaseName("ix_submissions_status"); + + b.HasIndex("SubmitterId") + .HasDatabaseName("ix_submissions_submitter_id"); + + b.HasIndex("SystemFrameRateId") + .HasDatabaseName("ix_submissions_system_frame_rate_id"); + + b.HasIndex("SystemId") + .HasDatabaseName("ix_submissions_system_id"); + + b.ToTable("submissions", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionAuthor", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("SubmissionId") + .HasColumnType("integer") + .HasColumnName("submission_id"); + + b.Property("Ordinal") + .HasColumnType("integer") + .HasColumnName("ordinal"); + + b.HasKey("UserId", "SubmissionId") + .HasName("pk_submission_authors"); + + b.HasIndex("SubmissionId") + .HasDatabaseName("ix_submission_authors_submission_id"); + + b.ToTable("submission_authors", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionRejectionReason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.HasKey("Id") + .HasName("pk_submission_rejection_reasons"); + + b.HasIndex("DisplayName") + .IsUnique() + .HasDatabaseName("ix_submission_rejection_reasons_display_name"); + + b.ToTable("submission_rejection_reasons", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionStatusHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("SubmissionId") + .HasColumnType("integer") + .HasColumnName("submission_id"); + + b.HasKey("Id") + .HasName("pk_submission_status_history"); + + b.HasIndex("SubmissionId") + .HasDatabaseName("ix_submission_status_history_submission_id"); + + b.ToTable("submission_status_history", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("citext") + .HasColumnName("code"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("display_name"); + + b.HasKey("Id") + .HasName("pk_tags"); + + b.HasIndex("Code") + .IsUnique() + .HasDatabaseName("ix_tags_code"); + + b.ToTable("tags", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccessFailedCount") + .HasColumnType("integer") + .HasColumnName("access_failed_count"); + + b.Property("AutoWatchTopic") + .HasColumnType("integer") + .HasColumnName("auto_watch_topic"); + + b.Property("Avatar") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("avatar"); + + b.Property("ConcurrencyStamp") + .HasColumnType("citext") + .HasColumnName("concurrency_stamp"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("DateFormat") + .HasColumnType("integer") + .HasColumnName("date_format"); + + b.Property("DecimalFormat") + .HasColumnType("integer") + .HasColumnName("decimal_format"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("email"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean") + .HasColumnName("email_confirmed"); + + b.Property("EmailOnPrivateMessage") + .HasColumnType("boolean") + .HasColumnName("email_on_private_message"); + + b.Property("From") + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("from"); + + b.Property("LastLoggedInTimeStamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_logged_in_time_stamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean") + .HasColumnName("lockout_enabled"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone") + .HasColumnName("lockout_end"); + + b.Property("ModeratorComments") + .HasMaxLength(1024) + .HasColumnType("citext") + .HasColumnName("moderator_comments"); + + b.Property("MoodAvatarUrlBase") + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("mood_avatar_url_base"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("normalized_email"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("normalized_user_name"); + + b.Property("PasswordHash") + .HasColumnType("citext") + .HasColumnName("password_hash"); + + b.Property("PhoneNumber") + .HasColumnType("citext") + .HasColumnName("phone_number"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean") + .HasColumnName("phone_number_confirmed"); + + b.Property("PreferredPronouns") + .HasColumnType("integer") + .HasColumnName("preferred_pronouns"); + + b.Property("PublicRatings") + .HasColumnType("boolean") + .HasColumnName("public_ratings"); + + b.Property("SecurityStamp") + .HasColumnType("citext") + .HasColumnName("security_stamp"); + + b.Property("Signature") + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("signature"); + + b.Property("TimeFormat") + .HasColumnType("integer") + .HasColumnName("time_format"); + + b.Property("TimeZoneId") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("time_zone_id"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean") + .HasColumnName("two_factor_enabled"); + + b.Property("UseRatings") + .HasColumnType("boolean") + .HasColumnName("use_ratings"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("pk_users"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("ix_users_normalized_user_name"); + + b.ToTable("users", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("citext") + .HasColumnName("claim_type"); + + b.Property("ClaimValue") + .HasColumnType("citext") + .HasColumnName("claim_value"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_user_claims"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_user_claims_user_id"); + + b.ToTable("user_claims", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserDisallow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("RegexPattern") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("citext") + .HasColumnName("regex_pattern"); + + b.HasKey("Id") + .HasName("pk_user_disallows"); + + b.HasIndex("RegexPattern") + .IsUnique() + .HasDatabaseName("ix_user_disallows_regex_pattern"); + + b.ToTable("user_disallows", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFile", b => + { + b.Property("Id") + .HasColumnType("bigint") + .HasColumnName("id") + .HasAnnotation("DatabaseGenerated", DatabaseGeneratedOption.None); + + b.Property("Annotations") + .HasColumnType("citext") + .HasColumnName("annotations"); + + b.Property("AuthorId") + .HasColumnType("integer") + .HasColumnName("author_id"); + + b.Property("Class") + .HasColumnType("integer") + .HasColumnName("class"); + + b.Property("CompressionType") + .HasColumnType("integer") + .HasColumnName("compression_type"); + + b.Property("Content") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("content"); + + b.Property("Description") + .HasColumnType("citext") + .HasColumnName("description"); + + b.Property("Downloads") + .HasColumnType("integer") + .HasColumnName("downloads"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("file_name"); + + b.Property("Frames") + .HasColumnType("integer") + .HasColumnName("frames"); + + b.Property("GameId") + .HasColumnType("integer") + .HasColumnName("game_id"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("decimal(10, 3)") + .HasColumnName("length"); + + b.Property("LogicalLength") + .HasColumnType("integer") + .HasColumnName("logical_length"); + + b.Property("PhysicalLength") + .HasColumnType("integer") + .HasColumnName("physical_length"); + + b.Property("Rerecords") + .HasColumnType("integer") + .HasColumnName("rerecords"); + + b.Property("SystemId") + .HasColumnType("integer") + .HasColumnName("system_id"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("title"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("citext") + .HasColumnName("type"); + + b.Property("UploadTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("upload_timestamp"); + + b.Property("Warnings") + .HasColumnType("citext") + .HasColumnName("warnings"); + + b.HasKey("Id") + .HasName("pk_user_files"); + + b.HasIndex("AuthorId") + .HasDatabaseName("ix_user_files_author_id"); + + b.HasIndex("GameId") + .HasDatabaseName("ix_user_files_game_id"); + + b.HasIndex("Hidden") + .HasDatabaseName("ix_user_files_hidden"); + + b.HasIndex("SystemId") + .HasDatabaseName("ix_user_files_system_id"); + + b.ToTable("user_files", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFileComment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationTimeStamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("creation_time_stamp"); + + b.Property("Ip") + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("ip"); + + b.Property("ParentId") + .HasColumnType("integer") + .HasColumnName("parent_id"); + + b.Property("Text") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("text"); + + b.Property("Title") + .HasMaxLength(255) + .HasColumnType("citext") + .HasColumnName("title"); + + b.Property("UserFileId") + .HasColumnType("bigint") + .HasColumnName("user_file_id"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_user_file_comments"); + + b.HasIndex("ParentId") + .HasDatabaseName("ix_user_file_comments_parent_id"); + + b.HasIndex("UserFileId") + .HasDatabaseName("ix_user_file_comments_user_file_id"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_user_file_comments_user_id"); + + b.ToTable("user_file_comments", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("citext") + .HasColumnName("login_provider"); + + b.Property("ProviderKey") + .HasColumnType("citext") + .HasColumnName("provider_key"); + + b.Property("ProviderDisplayName") + .HasColumnType("citext") + .HasColumnName("provider_display_name"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("LoginProvider", "ProviderKey") + .HasName("pk_user_logins"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_user_logins_user_id"); + + b.ToTable("user_logins", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserMaintenanceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EditorId") + .HasColumnType("integer") + .HasColumnName("editor_id"); + + b.Property("Log") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("citext") + .HasColumnName("log"); + + b.Property("TimeStamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("time_stamp"); + + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("pk_user_maintenance_logs"); + + b.HasIndex("EditorId") + .HasDatabaseName("ix_user_maintenance_logs_editor_id"); + + b.HasIndex("UserId") + .HasDatabaseName("ix_user_maintenance_logs_user_id"); + + b.ToTable("user_maintenance_logs", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserRole", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("RoleId") + .HasColumnType("integer") + .HasColumnName("role_id"); + + b.HasKey("UserId", "RoleId") + .HasName("pk_user_roles"); + + b.HasIndex("RoleId") + .HasDatabaseName("ix_user_roles_role_id"); + + b.ToTable("user_roles", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserToken", b => + { + b.Property("UserId") + .HasColumnType("integer") + .HasColumnName("user_id"); + + b.Property("LoginProvider") + .HasColumnType("citext") + .HasColumnName("login_provider"); + + b.Property("Name") + .HasColumnType("citext") + .HasColumnName("name"); + + b.Property("Value") + .HasColumnType("citext") + .HasColumnName("value"); + + b.HasKey("UserId", "LoginProvider", "Name") + .HasName("pk_user_tokens"); + + b.ToTable("user_tokens", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.WikiPage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AuthorId") + .HasColumnType("integer") + .HasColumnName("author_id"); + + b.Property("ChildId") + .HasColumnType("integer") + .HasColumnName("child_id"); + + b.Property("CreateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("create_timestamp"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("LastUpdateTimestamp") + .HasColumnType("timestamp without time zone") + .HasColumnName("last_update_timestamp"); + + b.Property("Markup") + .IsRequired() + .HasColumnType("citext") + .HasColumnName("markup"); + + b.Property("MinorEdit") + .HasColumnType("boolean") + .HasColumnName("minor_edit"); + + b.Property("PageName") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("page_name"); + + b.Property("Revision") + .HasColumnType("integer") + .HasColumnName("revision"); + + b.Property("RevisionMessage") + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("revision_message"); + + b.Property("SearchVector") + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("tsvector") + .HasColumnName("search_vector") + .HasAnnotation("Npgsql:TsVectorConfig", "english") + .HasAnnotation("Npgsql:TsVectorProperties", new[] { "PageName", "Markup" }); + + b.HasKey("Id") + .HasName("pk_wiki_pages"); + + b.HasIndex("AuthorId") + .HasDatabaseName("ix_wiki_pages_author_id"); + + b.HasIndex("ChildId") + .HasDatabaseName("ix_wiki_pages_child_id"); + + b.HasIndex("Markup") + .HasDatabaseName("ix_wiki_pages_markup"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Markup"), "gin"); + NpgsqlIndexBuilderExtensions.HasOperators(b.HasIndex("Markup"), new[] { "gin_trgm_ops" }); + + b.HasIndex("SearchVector") + .HasDatabaseName("ix_wiki_pages_search_vector"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("SearchVector"), "GIN"); + + b.HasIndex("PageName", "Revision") + .IsUnique() + .HasDatabaseName("ix_wiki_pages_page_name_revision"); + + b.ToTable("wiki_pages", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.WikiPageReferral", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Excerpt") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("excerpt"); + + b.Property("Referral") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("citext") + .HasColumnName("referral"); + + b.Property("Referrer") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("citext") + .HasColumnName("referrer"); + + b.HasKey("Id") + .HasName("pk_wiki_referrals"); + + b.ToTable("wiki_referrals", (string)null); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Awards.PublicationAward", b => + { + b.HasOne("TASVideos.Data.Entity.Awards.Award", "Award") + .WithMany() + .HasForeignKey("AwardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_awards_awards_award_id"); + + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationAwards") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_awards_publications_publication_id"); + + b.Navigation("Award"); + + b.Navigation("Publication"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Awards.UserAward", b => + { + b.HasOne("TASVideos.Data.Entity.Awards.Award", "Award") + .WithMany() + .HasForeignKey("AwardId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_awards_awards_award_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("UserAwards") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_awards_users_user_id"); + + b.Navigation("Award"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.Forum", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.ForumCategory", "Category") + .WithMany("Forums") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forums_forum_categories_category_id"); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPollOption", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.ForumPoll", "Poll") + .WithMany("PollOptions") + .HasForeignKey("PollId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_poll_options_forum_polls_poll_id"); + + b.Navigation("Poll"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPollOptionVote", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.ForumPollOption", "PollOption") + .WithMany("Votes") + .HasForeignKey("PollOptionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_poll_option_votes_forum_poll_options_poll_option_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_poll_option_votes_users_user_id"); + + b.Navigation("PollOption"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPost", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.Forum", "Forum") + .WithMany("ForumPosts") + .HasForeignKey("ForumId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_posts_forums_forum_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Poster") + .WithMany("Posts") + .HasForeignKey("PosterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_posts_users_poster_id"); + + b.HasOne("TASVideos.Data.Entity.Forum.ForumTopic", "Topic") + .WithMany("ForumPosts") + .HasForeignKey("TopicId") + .HasConstraintName("fk_forum_posts_forum_topics_topic_id"); + + b.Navigation("Forum"); + + b.Navigation("Poster"); + + b.Navigation("Topic"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumTopic", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.Forum", "Forum") + .WithMany("ForumTopics") + .HasForeignKey("ForumId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_topics_forums_forum_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany() + .HasForeignKey("GameId") + .HasConstraintName("fk_forum_topics_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Forum.ForumPoll", "Poll") + .WithOne("Topic") + .HasForeignKey("TASVideos.Data.Entity.Forum.ForumTopic", "PollId") + .HasConstraintName("fk_forum_topics_forum_polls_poll_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Poster") + .WithMany("Topics") + .HasForeignKey("PosterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_topics_users_poster_id"); + + b.HasOne("TASVideos.Data.Entity.Submission", "Submission") + .WithOne("Topic") + .HasForeignKey("TASVideos.Data.Entity.Forum.ForumTopic", "SubmissionId") + .HasConstraintName("fk_forum_topics_submissions_submission_id"); + + b.Navigation("Forum"); + + b.Navigation("Game"); + + b.Navigation("Poll"); + + b.Navigation("Poster"); + + b.Navigation("Submission"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumTopicWatch", b => + { + b.HasOne("TASVideos.Data.Entity.Forum.ForumTopic", "ForumTopic") + .WithMany("ForumTopicWatches") + .HasForeignKey("ForumTopicId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_forum_topic_watches_forum_topics_forum_topic_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("ForumTopicWatches") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_forum_topic_watches_users_user_id"); + + b.Navigation("ForumTopic"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGameGroup", b => + { + b.HasOne("TASVideos.Data.Entity.Game.GameGroup", "GameGroup") + .WithMany("Games") + .HasForeignKey("GameGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_game_groups_game_groups_game_group_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("GameGroups") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_game_groups_games_game_id"); + + b.Navigation("Game"); + + b.Navigation("GameGroup"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGenre", b => + { + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("GameGenres") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_genres_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Genre", "Genre") + .WithMany("GameGenres") + .HasForeignKey("GenreId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_genres_genres_genre_id"); + + b.Navigation("Game"); + + b.Navigation("Genre"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGoal", b => + { + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("GameGoals") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_goals_games_game_id"); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameSystemFrameRate", b => + { + b.HasOne("TASVideos.Data.Entity.Game.GameSystem", "System") + .WithMany("SystemFrameRates") + .HasForeignKey("GameSystemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_game_system_frame_rates_game_systems_game_system_id"); + + b.Navigation("System"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameVersion", b => + { + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("GameVersions") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_versions_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystem", "System") + .WithMany("GameVersions") + .HasForeignKey("SystemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_game_versions_game_systems_system_id"); + + b.Navigation("Game"); + + b.Navigation("System"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PrivateMessage", b => + { + b.HasOne("TASVideos.Data.Entity.User", "FromUser") + .WithMany("SentPrivateMessages") + .HasForeignKey("FromUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_private_messages_users_from_user_id"); + + b.HasOne("TASVideos.Data.Entity.User", "ToUser") + .WithMany("ReceivedPrivateMessages") + .HasForeignKey("ToUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_private_messages_users_to_user_id"); + + b.Navigation("FromUser"); + + b.Navigation("ToUser"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Publication", b => + { + b.HasOne("TASVideos.Data.Entity.Game.GameGoal", "GameGoal") + .WithMany("Publications") + .HasForeignKey("GameGoalId") + .HasConstraintName("fk_publications_game_goals_game_goal_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("Publications") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publications_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameVersion", "GameVersion") + .WithMany("Publications") + .HasForeignKey("GameVersionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_publications_game_versions_game_version_id"); + + b.HasOne("TASVideos.Data.Entity.Publication", "ObsoletedBy") + .WithMany("ObsoletedMovies") + .HasForeignKey("ObsoletedById") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("fk_publications_publications_obsoleted_by_id"); + + b.HasOne("TASVideos.Data.Entity.PublicationClass", "PublicationClass") + .WithMany() + .HasForeignKey("PublicationClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publications_publication_classes_publication_class_id"); + + b.HasOne("TASVideos.Data.Entity.Submission", "Submission") + .WithOne("Publication") + .HasForeignKey("TASVideos.Data.Entity.Publication", "SubmissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publications_submissions_submission_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystemFrameRate", "SystemFrameRate") + .WithMany() + .HasForeignKey("SystemFrameRateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publications_game_system_frame_rates_system_frame_rate_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystem", "System") + .WithMany("Publications") + .HasForeignKey("SystemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_publications_game_systems_system_id"); + + b.Navigation("Game"); + + b.Navigation("GameGoal"); + + b.Navigation("GameVersion"); + + b.Navigation("ObsoletedBy"); + + b.Navigation("PublicationClass"); + + b.Navigation("Submission"); + + b.Navigation("System"); + + b.Navigation("SystemFrameRate"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationAuthor", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("Authors") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_authors_publications_publication_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Author") + .WithMany("Publications") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_authors_users_user_id"); + + b.Navigation("Author"); + + b.Navigation("Publication"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationFile", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("Files") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_files_publications_publication_id"); + + b.Navigation("Publication"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationFlag", b => + { + b.HasOne("TASVideos.Data.Entity.Flag", "Flag") + .WithMany() + .HasForeignKey("FlagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_flags_flags_flag_id"); + + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationFlags") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_flags_publications_publication_id"); + + b.Navigation("Flag"); + + b.Navigation("Publication"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationMaintenanceLog", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationMaintenanceLogs") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_maintenance_logs_publications_publication_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("PublicationMaintenanceLogs") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_maintenance_logs_users_user_id"); + + b.Navigation("Publication"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationRating", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationRatings") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_ratings_publications_publication_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("PublicationRatings") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_ratings_users_user_id"); + + b.Navigation("Publication"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationTag", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationTags") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_tags_publications_publication_id"); + + b.HasOne("TASVideos.Data.Entity.Tag", "Tag") + .WithMany("PublicationTags") + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_tags_tags_tag_id"); + + b.Navigation("Publication"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.PublicationUrl", b => + { + b.HasOne("TASVideos.Data.Entity.Publication", "Publication") + .WithMany("PublicationUrls") + .HasForeignKey("PublicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_publication_urls_publications_publication_id"); + + b.Navigation("Publication"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.RoleLink", b => + { + b.HasOne("TASVideos.Data.Entity.Role", "Role") + .WithMany("RoleLinks") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_role_links_roles_role_id"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.RolePermission", b => + { + b.HasOne("TASVideos.Data.Entity.Role", "Role") + .WithMany("RolePermission") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_role_permission_roles_role_id"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Submission", b => + { + b.HasOne("TASVideos.Data.Entity.Game.GameGoal", "GameGoal") + .WithMany("Submissions") + .HasForeignKey("GameGoalId") + .HasConstraintName("fk_submissions_game_goals_game_goal_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("Submissions") + .HasForeignKey("GameId") + .HasConstraintName("fk_submissions_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameVersion", "GameVersion") + .WithMany("Submissions") + .HasForeignKey("GameVersionId") + .HasConstraintName("fk_submissions_game_versions_game_version_id"); + + b.HasOne("TASVideos.Data.Entity.PublicationClass", "IntendedClass") + .WithMany() + .HasForeignKey("IntendedClassId") + .HasConstraintName("fk_submissions_publication_classes_intended_class_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Judge") + .WithMany() + .HasForeignKey("JudgeId") + .HasConstraintName("fk_submissions_users_judge_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Publisher") + .WithMany() + .HasForeignKey("PublisherId") + .HasConstraintName("fk_submissions_users_publisher_id"); + + b.HasOne("TASVideos.Data.Entity.SubmissionRejectionReason", "RejectionReason") + .WithMany("Submissions") + .HasForeignKey("RejectionReasonId") + .HasConstraintName("fk_submissions_submission_rejection_reasons_rejection_reason_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Submitter") + .WithMany() + .HasForeignKey("SubmitterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_submissions_users_submitter_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystemFrameRate", "SystemFrameRate") + .WithMany() + .HasForeignKey("SystemFrameRateId") + .HasConstraintName("fk_submissions_game_system_frame_rates_system_frame_rate_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystem", "System") + .WithMany("Submissions") + .HasForeignKey("SystemId") + .HasConstraintName("fk_submissions_game_systems_system_id"); + + b.Navigation("Game"); + + b.Navigation("GameGoal"); + + b.Navigation("GameVersion"); + + b.Navigation("IntendedClass"); + + b.Navigation("Judge"); + + b.Navigation("Publisher"); + + b.Navigation("RejectionReason"); + + b.Navigation("Submitter"); + + b.Navigation("System"); + + b.Navigation("SystemFrameRate"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionAuthor", b => + { + b.HasOne("TASVideos.Data.Entity.Submission", "Submission") + .WithMany("SubmissionAuthors") + .HasForeignKey("SubmissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_submission_authors_submissions_submission_id"); + + b.HasOne("TASVideos.Data.Entity.User", "Author") + .WithMany("Submissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_submission_authors_users_user_id"); + + b.Navigation("Author"); + + b.Navigation("Submission"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionStatusHistory", b => + { + b.HasOne("TASVideos.Data.Entity.Submission", "Submission") + .WithMany("History") + .HasForeignKey("SubmissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_submission_status_history_submissions_submission_id"); + + b.Navigation("Submission"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFile", b => + { + b.HasOne("TASVideos.Data.Entity.User", "Author") + .WithMany("UserFiles") + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_user_files_users_author_id"); + + b.HasOne("TASVideos.Data.Entity.Game.Game", "Game") + .WithMany("UserFiles") + .HasForeignKey("GameId") + .HasConstraintName("fk_user_files_games_game_id"); + + b.HasOne("TASVideos.Data.Entity.Game.GameSystem", "System") + .WithMany() + .HasForeignKey("SystemId") + .HasConstraintName("fk_user_files_game_systems_system_id"); + + b.Navigation("Author"); + + b.Navigation("Game"); + + b.Navigation("System"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFileComment", b => + { + b.HasOne("TASVideos.Data.Entity.UserFileComment", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId") + .HasConstraintName("fk_user_file_comments_user_file_comments_parent_id"); + + b.HasOne("TASVideos.Data.Entity.UserFile", "UserFile") + .WithMany("Comments") + .HasForeignKey("UserFileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_file_comments_user_files_user_file_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("UserFileComments") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired() + .HasConstraintName("fk_user_file_comments_users_user_id"); + + b.Navigation("Parent"); + + b.Navigation("User"); + + b.Navigation("UserFile"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserMaintenanceLog", b => + { + b.HasOne("TASVideos.Data.Entity.User", "Editor") + .WithMany("EditMaintenanceLogs") + .HasForeignKey("EditorId") + .HasConstraintName("fk_user_maintenance_logs_users_editor_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("UserMaintenanceLogs") + .HasForeignKey("UserId") + .HasConstraintName("fk_user_maintenance_logs_users_user_id"); + + b.Navigation("Editor"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserRole", b => + { + b.HasOne("TASVideos.Data.Entity.Role", "Role") + .WithMany("UserRole") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_roles_roles_role_id"); + + b.HasOne("TASVideos.Data.Entity.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_user_roles_users_user_id"); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.WikiPage", b => + { + b.HasOne("TASVideos.Data.Entity.User", "Author") + .WithMany("WikiRevisions") + .HasForeignKey("AuthorId") + .HasConstraintName("fk_wiki_pages_users_author_id"); + + b.HasOne("TASVideos.Data.Entity.WikiPage", "Child") + .WithMany() + .HasForeignKey("ChildId") + .HasConstraintName("fk_wiki_pages_wiki_pages_child_id"); + + b.Navigation("Author"); + + b.Navigation("Child"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.Forum", b => + { + b.Navigation("ForumPosts"); + + b.Navigation("ForumTopics"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumCategory", b => + { + b.Navigation("Forums"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPoll", b => + { + b.Navigation("PollOptions"); + + b.Navigation("Topic"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumPollOption", b => + { + b.Navigation("Votes"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Forum.ForumTopic", b => + { + b.Navigation("ForumPosts"); + + b.Navigation("ForumTopicWatches"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.Game", b => + { + b.Navigation("GameGenres"); + + b.Navigation("GameGoals"); + + b.Navigation("GameGroups"); + + b.Navigation("GameVersions"); + + b.Navigation("Publications"); + + b.Navigation("Submissions"); + + b.Navigation("UserFiles"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGoal", b => + { + b.Navigation("Publications"); + + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameGroup", b => + { + b.Navigation("Games"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameSystem", b => + { + b.Navigation("GameVersions"); + + b.Navigation("Publications"); + + b.Navigation("Submissions"); + + b.Navigation("SystemFrameRates"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.GameVersion", b => + { + b.Navigation("Publications"); + + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Game.Genre", b => + { + b.Navigation("GameGenres"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Publication", b => + { + b.Navigation("Authors"); + + b.Navigation("Files"); + + b.Navigation("ObsoletedMovies"); + + b.Navigation("PublicationAwards"); + + b.Navigation("PublicationFlags"); + + b.Navigation("PublicationMaintenanceLogs"); + + b.Navigation("PublicationRatings"); + + b.Navigation("PublicationTags"); + + b.Navigation("PublicationUrls"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Role", b => + { + b.Navigation("RoleLinks"); + + b.Navigation("RolePermission"); + + b.Navigation("UserRole"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Submission", b => + { + b.Navigation("History"); + + b.Navigation("Publication"); + + b.Navigation("SubmissionAuthors"); + + b.Navigation("Topic"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.SubmissionRejectionReason", b => + { + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.Tag", b => + { + b.Navigation("PublicationTags"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.User", b => + { + b.Navigation("EditMaintenanceLogs"); + + b.Navigation("ForumTopicWatches"); + + b.Navigation("Posts"); + + b.Navigation("PublicationMaintenanceLogs"); + + b.Navigation("PublicationRatings"); + + b.Navigation("Publications"); + + b.Navigation("ReceivedPrivateMessages"); + + b.Navigation("SentPrivateMessages"); + + b.Navigation("Submissions"); + + b.Navigation("Topics"); + + b.Navigation("UserAwards"); + + b.Navigation("UserFileComments"); + + b.Navigation("UserFiles"); + + b.Navigation("UserMaintenanceLogs"); + + b.Navigation("UserRoles"); + + b.Navigation("WikiRevisions"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFile", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("TASVideos.Data.Entity.UserFileComment", b => + { + b.Navigation("Children"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TASVideos.Data/Migrations/20240228201932_AddUserLocaleSettings.cs b/TASVideos.Data/Migrations/20240228201932_AddUserLocaleSettings.cs new file mode 100644 index 000000000..3b9701375 --- /dev/null +++ b/TASVideos.Data/Migrations/20240228201932_AddUserLocaleSettings.cs @@ -0,0 +1,50 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TASVideos.Data.Migrations; + +/// +public partial class AddUserLocaleSettings : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "date_format", + table: "users", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "decimal_format", + table: "users", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "time_format", + table: "users", + type: "integer", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "date_format", + table: "users"); + + migrationBuilder.DropColumn( + name: "decimal_format", + table: "users"); + + migrationBuilder.DropColumn( + name: "time_format", + table: "users"); + } +} diff --git a/TASVideos.Data/Migrations/ApplicationDbContextModelSnapshot.cs b/TASVideos.Data/Migrations/ApplicationDbContextModelSnapshot.cs index 223c1e9a2..713de3e42 100644 --- a/TASVideos.Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/TASVideos.Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -2021,6 +2021,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp without time zone") .HasColumnName("create_timestamp"); + b.Property("DateFormat") + .HasColumnType("integer") + .HasColumnName("date_format"); + + b.Property("DecimalFormat") + .HasColumnType("integer") + .HasColumnName("decimal_format"); + b.Property("Email") .IsRequired() .HasMaxLength(100) @@ -2107,6 +2115,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("citext") .HasColumnName("signature"); + b.Property("TimeFormat") + .HasColumnType("integer") + .HasColumnName("time_format"); + b.Property("TimeZoneId") .IsRequired() .HasMaxLength(250) diff --git a/TASVideos/Middleware/CustomLocalizationMiddleware.cs b/TASVideos/Middleware/CustomLocalizationMiddleware.cs new file mode 100644 index 000000000..31d807c56 --- /dev/null +++ b/TASVideos/Middleware/CustomLocalizationMiddleware.cs @@ -0,0 +1,131 @@ +using System.Globalization; +using TASVideos.Core.Services; +using TASVideos.Data; +using TASVideos.Data.Entity; + +namespace TASVideos.Middleware; + +public class CustomLocalizationMiddleware +{ + private readonly RequestDelegate _next; + + public CustomLocalizationMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context, ApplicationDbContext db, ICacheService cache) + { + if (context.User.IsLoggedIn()) + { + if (!cache.TryGetValue(CacheKeys.UsersWithCustomLocale, out HashSet usersWithCustomLocale)) + { + usersWithCustomLocale = (await db.Users + .ThatHaveCustomLocale() + .Select(u => u.UserName) + .ToListAsync()) + .ToHashSet(); + cache.Set(CacheKeys.UsersWithCustomLocale, usersWithCustomLocale, Durations.OneYearInSeconds); + } + + if (usersWithCustomLocale.Contains(context.User.Name())) + { + int userId = context.User.GetUserId(); + + if (!cache.TryGetValue(CacheKeys.CustomUserLocalePrefix + userId, out CultureInfo customCultureInfo)) + { + CustomCultureData? customCultureData = await db.Users + .Where(u => u.Id == userId) + .Select(u => new CustomCultureData + { + DateFormat = u.DateFormat, + TimeFormat = u.TimeFormat, + DecimalFormat = u.DecimalFormat, + }) + .SingleOrDefaultAsync(); + customCultureInfo = ConstructCustomCulture(customCultureData); + cache.Set(CacheKeys.CustomUserLocalePrefix + userId, customCultureInfo, Durations.OneYearInSeconds); + } + + CultureInfo.CurrentCulture = customCultureInfo; + } + } + + await _next(context); + } + + private class CustomCultureData + { + public UserDateFormat DateFormat { get; set; } + public UserTimeFormat TimeFormat { get; set; } + public UserDecimalFormat DecimalFormat { get; set; } + } + + private CultureInfo ConstructCustomCulture(CustomCultureData? customCultureData) + { + if (customCultureData == null) + { + return CultureInfo.CurrentCulture; + } + + CultureInfo cultureInfo = (CultureInfo)CultureInfo.CurrentCulture.Clone(); // clone to make it editable + switch (customCultureData.DateFormat) + { + default: + case UserDateFormat.Auto: + break; + case UserDateFormat.YMMDD: + cultureInfo.DateTimeFormat.ShortDatePattern = "yyyy-MM-dd"; + break; + case UserDateFormat.DDMMY: + cultureInfo.DateTimeFormat.ShortDatePattern = @"dd\/MM\/yyyy"; + break; + case UserDateFormat.DDMMYDot: + cultureInfo.DateTimeFormat.ShortDatePattern = "dd.MM.yyyy"; + break; + case UserDateFormat.DMY: + cultureInfo.DateTimeFormat.ShortDatePattern = @"d\/M\/yyyy"; + break; + case UserDateFormat.MMDDY: + cultureInfo.DateTimeFormat.ShortDatePattern = @"MM\/dd\/yyyy"; + break; + case UserDateFormat.MDY: + cultureInfo.DateTimeFormat.ShortDatePattern = @"M\/d\/yyyy"; + break; + } + + switch (customCultureData.TimeFormat) + { + default: + case UserTimeFormat.Auto: + break; + case UserTimeFormat.Clock24Hour: + cultureInfo.DateTimeFormat.ShortTimePattern = @"HH\:mm"; + cultureInfo.DateTimeFormat.LongTimePattern = @"HH\:mm\:ss"; + break; + case UserTimeFormat.Clock12Hour: + cultureInfo.DateTimeFormat.AMDesignator = "AM"; + cultureInfo.DateTimeFormat.PMDesignator = "PM"; + cultureInfo.DateTimeFormat.ShortTimePattern = @"h\:mm tt"; + cultureInfo.DateTimeFormat.LongTimePattern = @"h\:mm\:ss tt"; + break; + } + + switch (customCultureData.DecimalFormat) + { + default: + case UserDecimalFormat.Auto: + break; + case UserDecimalFormat.Dot: + cultureInfo.NumberFormat.NumberDecimalSeparator = "."; + cultureInfo.NumberFormat.NumberGroupSeparator = ","; + break; + case UserDecimalFormat.Comma: + cultureInfo.NumberFormat.NumberDecimalSeparator = ","; + cultureInfo.NumberFormat.NumberGroupSeparator = "."; + break; + } + + return cultureInfo; + } +} diff --git a/TASVideos/Pages/Profile/Models/ProfileSettingsModel.cs b/TASVideos/Pages/Profile/Models/ProfileSettingsModel.cs index 962dce576..deee7444f 100644 --- a/TASVideos/Pages/Profile/Models/ProfileSettingsModel.cs +++ b/TASVideos/Pages/Profile/Models/ProfileSettingsModel.cs @@ -42,5 +42,14 @@ public class ProfileSettingsModel [Display(Name = "Automatically Watch Topics When Posting")] public UserPreference AutoWatchTopic { get; set; } + [Display(Name = "Date Format")] + public UserDateFormat UserDateFormat { get; set; } + + [Display(Name = "Time Format")] + public UserTimeFormat UserTimeFormat { get; set; } + + [Display(Name = "Decimal Format")] + public UserDecimalFormat UserDecimalFormat { get; set; } + public IEnumerable Roles { get; set; } = new List(); } diff --git a/TASVideos/Pages/Profile/Settings.cshtml b/TASVideos/Pages/Profile/Settings.cshtml index 2a2289cff..0792e9987 100644 --- a/TASVideos/Pages/Profile/Settings.cshtml +++ b/TASVideos/Pages/Profile/Settings.cshtml @@ -117,6 +117,28 @@ @await Component.RenderWiki(SystemWiki.MoodAvatarRequirements) + @{ + DateTime exampleDate = new DateTime(2024, 2, 29, 17, 35, 0); + double exampleNumber = 1.23; + } + + + + + + + + + + + + + + + + + + diff --git a/TASVideos/Pages/Profile/Settings.cshtml.cs b/TASVideos/Pages/Profile/Settings.cshtml.cs index e01517047..1f4891dc2 100644 --- a/TASVideos/Pages/Profile/Settings.cshtml.cs +++ b/TASVideos/Pages/Profile/Settings.cshtml.cs @@ -46,6 +46,36 @@ public SettingsModel( }) .ToList(); + public static readonly IEnumerable AvailableDateFormats = Enum + .GetValues(typeof(UserDateFormat)) + .Cast() + .Select(m => new SelectListItem + { + Value = ((int)m).ToString(), + Text = m.EnumDisplayName() + }) + .ToList(); + + public static readonly IEnumerable AvailableTimeFormats = Enum + .GetValues(typeof(UserTimeFormat)) + .Cast() + .Select(m => new SelectListItem + { + Value = ((int)m).ToString(), + Text = m.EnumDisplayName() + }) + .ToList(); + + public static readonly IEnumerable AvailableDecimalFormats = Enum + .GetValues(typeof(UserDecimalFormat)) + .Cast() + .Select(m => new SelectListItem + { + Value = ((int)m).ToString(), + Text = m.EnumDisplayName() + }) + .ToList(); + [BindProperty] public ProfileSettingsModel Settings { get; set; } = new(); @@ -66,7 +96,10 @@ public async Task OnGet() PreferredPronouns = user.PreferredPronouns, EmailOnPrivateMessage = user.EmailOnPrivateMessage, Roles = await _userManager.UserRoles(user.Id), - AutoWatchTopic = user.AutoWatchTopic ?? UserPreference.Auto + AutoWatchTopic = user.AutoWatchTopic ?? UserPreference.Auto, + UserDateFormat = user.DateFormat, + UserTimeFormat = user.TimeFormat, + UserDecimalFormat = user.DecimalFormat, }; } @@ -107,6 +140,8 @@ public async Task OnPost() return Page(); } + bool hasUserCustomLocaleChanged = user.DateFormat != Settings.UserDateFormat || user.TimeFormat != Settings.UserTimeFormat || user.DecimalFormat != Settings.UserDecimalFormat; + user.TimeZoneId = Settings.TimeZoneId; user.PublicRatings = Settings.PublicRatings; user.From = Settings.From; @@ -115,6 +150,9 @@ public async Task OnPost() user.PreferredPronouns = Settings.PreferredPronouns; user.EmailOnPrivateMessage = Settings.EmailOnPrivateMessage; user.AutoWatchTopic = Settings.AutoWatchTopic; + user.DateFormat = Settings.UserDateFormat; + user.TimeFormat = Settings.UserTimeFormat; + user.DecimalFormat = Settings.UserDecimalFormat; if (User.Has(PermissionTo.EditSignature)) { user.Signature = Settings.Signature; @@ -122,6 +160,11 @@ public async Task OnPost() await _db.SaveChangesAsync(); + if (hasUserCustomLocaleChanged) + { + _userManager.ClearCustomLocaleCache(User.GetUserId()); + } + SuccessStatusMessage("Your profile has been updated"); return BasePageRedirect("Settings"); } diff --git a/TASVideos/Startup.cs b/TASVideos/Startup.cs index 3f587f327..9c351d16f 100644 --- a/TASVideos/Startup.cs +++ b/TASVideos/Startup.cs @@ -2,6 +2,7 @@ using TASVideos.Core; using TASVideos.Core.Settings; using TASVideos.Data; +using TASVideos.Middleware; using TASVideos.Services; namespace TASVideos; @@ -65,6 +66,7 @@ public void Configure(IApplicationBuilder app, IHostEnvironment env) .UseWebOptimizer() .UseStaticFilesWithExtensionMapping() .UseAuthentication() + .UseMiddleware() .UseSwaggerUi(Environment) .UseLogging() .UseMvcWithOptions(env); From 236f66d64d4ecae8f97cc1fd4198eaf59d2f4551 Mon Sep 17 00:00:00 2001 From: Masterjun3 Date: Thu, 29 Feb 2024 17:05:38 +0100 Subject: [PATCH 2/3] move the locale settings next to the timezone --- TASVideos/Pages/Profile/Settings.cshtml | 54 ++++++++++++------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/TASVideos/Pages/Profile/Settings.cshtml b/TASVideos/Pages/Profile/Settings.cshtml index 0792e9987..4f1e69548 100644 --- a/TASVideos/Pages/Profile/Settings.cshtml +++ b/TASVideos/Pages/Profile/Settings.cshtml @@ -75,8 +75,9 @@
- - + + +
@@ -84,9 +85,30 @@
- - - + + +
+ @{ + DateTime exampleDate = new DateTime(2024, 2, 29, 17, 35, 0); + double exampleNumber = 1.23; + } +
+ + + + +
+
+ + + + +
+
+ + + +
@@ -117,28 +139,6 @@ @await Component.RenderWiki(SystemWiki.MoodAvatarRequirements)
- @{ - DateTime exampleDate = new DateTime(2024, 2, 29, 17, 35, 0); - double exampleNumber = 1.23; - } - - - - - - - - - - - - - - - - - - From 40c6224c346f83290a3eeb95bb7e417162cdce90 Mon Sep 17 00:00:00 2001 From: Masterjun3 Date: Thu, 29 Feb 2024 17:56:13 +0100 Subject: [PATCH 3/3] align form-select font-size to form-control --- TASVideos/wwwroot/css/partials/_customizations.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TASVideos/wwwroot/css/partials/_customizations.scss b/TASVideos/wwwroot/css/partials/_customizations.scss index 910d5ea26..4f593ef27 100644 --- a/TASVideos/wwwroot/css/partials/_customizations.scss +++ b/TASVideos/wwwroot/css/partials/_customizations.scss @@ -107,7 +107,7 @@ h4 { } } -.form-control { +.form-control, .form-select { font-size: .875rem; &:disabled, &[readonly] {