diff --git a/Data/Data.csproj b/Data/Data.csproj index 135585ae..f10b843c 100644 --- a/Data/Data.csproj +++ b/Data/Data.csproj @@ -11,6 +11,7 @@ + diff --git a/Data/DataModel/Puzzle.cs b/Data/DataModel/Puzzle.cs index feb44be9..fc545b9b 100644 --- a/Data/DataModel/Puzzle.cs +++ b/Data/DataModel/Puzzle.cs @@ -7,6 +7,13 @@ namespace ServerCore.DataModel { + public enum PieceMetaUsage + { + None, + GroupOnly, + EntireEvent + } + /// /// A Puzzle is the record of a solvable puzzle in the database /// Sometimes a Puzzle is used as a workaround for things like time prerequisites @@ -188,6 +195,11 @@ public Puzzle (Puzzle source) /// public string Description { get; set; } + /// + /// Determines whether the puzzle can use the basic piece-driven meta. + /// + public PieceMetaUsage PieceMetaUsage { get; set; } + // // WARNING: If you add new properties add them to the constructor as well so importing will work. // diff --git a/Data/Migrations/20190606022134_PieceMetaUsage.Designer.cs b/Data/Migrations/20190606022134_PieceMetaUsage.Designer.cs new file mode 100644 index 00000000..4e491b7b --- /dev/null +++ b/Data/Migrations/20190606022134_PieceMetaUsage.Designer.cs @@ -0,0 +1,1055 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ServerCore.DataModel; + +namespace Data.Migrations +{ + [DbContext(typeof(PuzzleServerContext))] + [Migration("20190606022134_PieceMetaUsage")] + partial class PieceMetaUsage + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.11-servicing-32099") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(128); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(128); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Annotation", b => + { + b.Property("PuzzleID"); + + b.Property("TeamID"); + + b.Property("Key"); + + b.Property("Contents") + .IsRequired() + .HasMaxLength(255); + + b.Property("Timestamp"); + + b.Property("Version"); + + b.HasKey("PuzzleID", "TeamID", "Key"); + + b.HasIndex("TeamID"); + + b.ToTable("Annotations"); + }); + + modelBuilder.Entity("ServerCore.DataModel.ContentFile", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("EventID"); + + b.Property("FileType"); + + b.Property("PuzzleID"); + + b.Property("ShortName") + .IsRequired(); + + b.Property("UrlString") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("PuzzleID"); + + b.HasIndex("EventID", "ShortName") + .IsUnique(); + + b.ToTable("ContentFiles"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Event", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AllowFeedback"); + + b.Property("AnswerSubmissionEnd"); + + b.Property("AnswersAvailableBegin"); + + b.Property("ContactEmail"); + + b.Property("EventBegin"); + + b.Property("HideHints"); + + b.Property("HomePartial"); + + b.Property("IsInternEvent"); + + b.Property("LockoutDurationMultiplier"); + + b.Property("LockoutIncorrectGuessLimit"); + + b.Property("LockoutIncorrectGuessPeriod"); + + b.Property("MaxExternalsPerTeam"); + + b.Property("MaxNumberOfTeams"); + + b.Property("MaxSubmissionCount"); + + b.Property("MaxTeamSize"); + + b.Property("Name") + .IsRequired(); + + b.Property("ShowFastestSolves"); + + b.Property("StandingsAvailableBegin"); + + b.Property("StandingsOverride"); + + b.Property("TeamDeleteEnd"); + + b.Property("TeamMembershipChangeEnd"); + + b.Property("TeamMiscDataChangeEnd"); + + b.Property("TeamNameChangeEnd"); + + b.Property("TeamRegistrationBegin"); + + b.Property("TeamRegistrationEnd"); + + b.Property("UrlString"); + + b.HasKey("ID"); + + b.HasIndex("UrlString") + .IsUnique() + .HasFilter("[UrlString] IS NOT NULL"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("ServerCore.DataModel.EventAdmins", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AdminID"); + + b.Property("EventID"); + + b.HasKey("ID"); + + b.HasIndex("AdminID"); + + b.HasIndex("EventID"); + + b.ToTable("EventAdmins"); + }); + + modelBuilder.Entity("ServerCore.DataModel.EventAuthors", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AuthorID"); + + b.Property("EventID"); + + b.HasKey("ID"); + + b.HasIndex("AuthorID"); + + b.HasIndex("EventID"); + + b.ToTable("EventAuthors"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Feedback", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Difficulty"); + + b.Property("Fun"); + + b.Property("PuzzleID"); + + b.Property("SubmissionTime"); + + b.Property("SubmitterID"); + + b.Property("WrittenFeedback"); + + b.HasKey("ID"); + + b.HasIndex("PuzzleID"); + + b.HasIndex("SubmitterID"); + + b.ToTable("Feedback"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Hint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Content") + .IsRequired(); + + b.Property("Cost"); + + b.Property("Description") + .IsRequired(); + + b.Property("DisplayOrder"); + + b.Property("PuzzleID"); + + b.HasKey("Id"); + + b.HasIndex("PuzzleID"); + + b.ToTable("Hints"); + }); + + modelBuilder.Entity("ServerCore.DataModel.HintStatePerTeam", b => + { + b.Property("TeamID"); + + b.Property("HintID"); + + b.Property("UnlockTime"); + + b.HasKey("TeamID", "HintID"); + + b.HasIndex("HintID"); + + b.ToTable("HintStatePerTeam"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Invitation", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("EmailAddress"); + + b.Property("Expiration"); + + b.Property("InvitationCode"); + + b.Property("InvitationType"); + + b.Property("TeamID"); + + b.HasKey("ID"); + + b.HasIndex("TeamID"); + + b.ToTable("Invitations"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Piece", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Contents") + .IsRequired(); + + b.Property("ProgressLevel"); + + b.Property("PuzzleID"); + + b.HasKey("ID"); + + b.HasIndex("ProgressLevel"); + + b.HasIndex("PuzzleID"); + + b.ToTable("Pieces"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Prerequisites", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("PrerequisiteID"); + + b.Property("PuzzleID"); + + b.HasKey("ID"); + + b.HasIndex("PrerequisiteID"); + + b.HasIndex("PuzzleID"); + + b.ToTable("Prerequisites"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Puzzle", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CustomURL"); + + b.Property("Description"); + + b.Property("Errata"); + + b.Property("EventID"); + + b.Property("Group"); + + b.Property("HasDataConfirmation"); + + b.Property("HintCoinsForSolve"); + + b.Property("HintsAreCumulative"); + + b.Property("IsCheatCode"); + + b.Property("IsFinalPuzzle"); + + b.Property("IsGloballyVisiblePrerequisite"); + + b.Property("IsMetaPuzzle"); + + b.Property("IsPuzzle"); + + b.Property("MaxAnnotationKey"); + + b.Property("MinPrerequisiteCount"); + + b.Property("MinutesOfEventLockout"); + + b.Property("MinutesToAutomaticallySolve"); + + b.Property("Name") + .IsRequired(); + + b.Property("OrderInGroup"); + + b.Property("PieceMetaUsage"); + + b.Property("PuzzleVersion"); + + b.Property("SolveValue"); + + b.Property("SupportEmailAlias"); + + b.Property("Token"); + + b.HasKey("ID"); + + b.HasIndex("EventID"); + + b.ToTable("Puzzles"); + }); + + modelBuilder.Entity("ServerCore.DataModel.PuzzleAuthors", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AuthorID"); + + b.Property("PuzzleID"); + + b.HasKey("ID"); + + b.HasIndex("AuthorID"); + + b.HasIndex("PuzzleID"); + + b.ToTable("PuzzleAuthors"); + }); + + modelBuilder.Entity("ServerCore.DataModel.PuzzleStatePerTeam", b => + { + b.Property("PuzzleID"); + + b.Property("TeamID"); + + b.Property("IsEmailOnlyMode"); + + b.Property("LockoutExpiryTime"); + + b.Property("Notes"); + + b.Property("Printed"); + + b.Property("SolvedTime"); + + b.Property("UnlockedTime"); + + b.Property("WrongSubmissionCountBuffer"); + + b.HasKey("PuzzleID", "TeamID"); + + b.HasIndex("TeamID"); + + b.ToTable("PuzzleStatePerTeam"); + }); + + modelBuilder.Entity("ServerCore.DataModel.PuzzleUser", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Email"); + + b.Property("EmployeeAlias"); + + b.Property("IdentityUserId") + .IsRequired(); + + b.Property("IsGlobalAdmin"); + + b.Property("MayBeAdminOrAuthor"); + + b.Property("Name"); + + b.Property("PhoneNumber"); + + b.Property("TShirtSize"); + + b.Property("VisibleToOthers"); + + b.HasKey("ID"); + + b.ToTable("PuzzleUsers"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Response", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("IsSolution"); + + b.Property("Note"); + + b.Property("PuzzleID"); + + b.Property("ResponseText") + .IsRequired(); + + b.Property("SubmittedText") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("PuzzleID"); + + b.ToTable("Responses"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Submission", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("PuzzleID"); + + b.Property("ResponseID"); + + b.Property("SubmissionText") + .IsRequired(); + + b.Property("SubmitterID"); + + b.Property("TeamID"); + + b.Property("TeamID1"); + + b.Property("TimeSubmitted"); + + b.HasKey("ID"); + + b.HasIndex("PuzzleID"); + + b.HasIndex("ResponseID"); + + b.HasIndex("SubmitterID"); + + b.HasIndex("TeamID"); + + b.HasIndex("TeamID1"); + + b.ToTable("Submissions"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Swag", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("EventId"); + + b.Property("Lunch"); + + b.Property("LunchModifications"); + + b.Property("PlayerId"); + + b.Property("ShirtSize"); + + b.HasKey("ID"); + + b.HasIndex("EventId"); + + b.HasIndex("PlayerId"); + + b.ToTable("Swag"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Team", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Bio"); + + b.Property("CustomRoom"); + + b.Property("EventID"); + + b.Property("HintCoinCount"); + + b.Property("HintCoinsUsed"); + + b.Property("IsLookingForTeammates"); + + b.Property("Name") + .IsRequired(); + + b.Property("Password"); + + b.Property("PrimaryContactEmail"); + + b.Property("PrimaryPhoneNumber"); + + b.Property("RoomID"); + + b.Property("SecondaryPhoneNumber"); + + b.HasKey("ID"); + + b.HasIndex("EventID"); + + b.ToTable("Teams"); + }); + + modelBuilder.Entity("ServerCore.DataModel.TeamApplication", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("PlayerID"); + + b.Property("TeamID"); + + b.HasKey("ID"); + + b.HasIndex("PlayerID"); + + b.HasIndex("TeamID"); + + b.ToTable("TeamApplications"); + }); + + modelBuilder.Entity("ServerCore.DataModel.TeamMembers", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Team.ID") + .IsRequired(); + + b.Property("User.ID") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("Team.ID"); + + b.HasIndex("User.ID"); + + b.ToTable("TeamMembers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.Annotation", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("ServerCore.DataModel.ContentFile", b => + { + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany("Contents") + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.EventAdmins", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "Admin") + .WithMany() + .HasForeignKey("AdminID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.EventAuthors", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "Author") + .WithMany() + .HasForeignKey("AuthorID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.Feedback", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Submitter") + .WithMany() + .HasForeignKey("SubmitterID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.Hint", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.HintStatePerTeam", b => + { + b.HasOne("ServerCore.DataModel.Hint", "Hint") + .WithMany() + .HasForeignKey("HintID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("ServerCore.DataModel.Invitation", b => + { + b.HasOne("ServerCore.DataModel.Team") + .WithMany("Invitations") + .HasForeignKey("TeamID"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Piece", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.Prerequisites", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Prerequisite") + .WithMany() + .HasForeignKey("PrerequisiteID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.Puzzle", b => + { + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.PuzzleAuthors", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "Author") + .WithMany() + .HasForeignKey("AuthorID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.PuzzleStatePerTeam", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("ServerCore.DataModel.Response", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany() + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.Submission", b => + { + b.HasOne("ServerCore.DataModel.Puzzle", "Puzzle") + .WithMany("Submissions") + .HasForeignKey("PuzzleID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.Response", "Response") + .WithMany() + .HasForeignKey("ResponseID"); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Submitter") + .WithMany() + .HasForeignKey("SubmitterID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("ServerCore.DataModel.Team") + .WithMany("Submissions") + .HasForeignKey("TeamID1"); + }); + + modelBuilder.Entity("ServerCore.DataModel.Swag", b => + { + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Player") + .WithMany() + .HasForeignKey("PlayerId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.Team", b => + { + b.HasOne("ServerCore.DataModel.Event", "Event") + .WithMany() + .HasForeignKey("EventID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.TeamApplication", b => + { + b.HasOne("ServerCore.DataModel.PuzzleUser", "Player") + .WithMany() + .HasForeignKey("PlayerID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("TeamID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("ServerCore.DataModel.TeamMembers", b => + { + b.HasOne("ServerCore.DataModel.Team", "Team") + .WithMany() + .HasForeignKey("Team.ID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("ServerCore.DataModel.PuzzleUser", "Member") + .WithMany() + .HasForeignKey("User.ID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Data/Migrations/20190606022134_PieceMetaUsage.cs b/Data/Migrations/20190606022134_PieceMetaUsage.cs new file mode 100644 index 00000000..5bfc97d2 --- /dev/null +++ b/Data/Migrations/20190606022134_PieceMetaUsage.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Data.Migrations +{ + public partial class PieceMetaUsage : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PieceMetaUsage", + table: "Puzzles", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PieceMetaUsage", + table: "Puzzles"); + } + } +} diff --git a/Data/Migrations/PuzzleServerContextModelSnapshot.cs b/Data/Migrations/PuzzleServerContextModelSnapshot.cs index 58cdb590..7a80b205 100644 --- a/Data/Migrations/PuzzleServerContextModelSnapshot.cs +++ b/Data/Migrations/PuzzleServerContextModelSnapshot.cs @@ -15,7 +15,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.8-servicing-32085") + .HasAnnotation("ProductVersion", "2.1.11-servicing-32099") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -517,6 +517,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("OrderInGroup"); + b.Property("PieceMetaUsage"); + b.Property("PuzzleVersion"); b.Property("SolveValue"); diff --git a/ServerCore/Pages/Pieces/SimpleMeta.cshtml b/ServerCore/Pages/Pieces/SimpleMeta.cshtml new file mode 100644 index 00000000..1b953fad --- /dev/null +++ b/ServerCore/Pages/Pieces/SimpleMeta.cshtml @@ -0,0 +1,20 @@ +@page "/{eventId}/{eventRole}/Teams/{teamId}/SimpleMeta/{puzzleId}" +@model ServerCore.Pages.Pieces.SimpleMetaModel + +@{ + ViewData["Title"] = $"{Model.Puzzle.Name} Meta"; +} + +

@(Model.Puzzle.Name) Pieces Earned

+ + + +@foreach (var item in Model.EarnedPieces) { + + + +} + +
+ @Html.DisplayFor(modelItem => item.Contents) +
diff --git a/ServerCore/Pages/Pieces/SimpleMeta.cshtml.cs b/ServerCore/Pages/Pieces/SimpleMeta.cshtml.cs new file mode 100644 index 00000000..4c83a18f --- /dev/null +++ b/ServerCore/Pages/Pieces/SimpleMeta.cshtml.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using ServerCore.DataModel; +using ServerCore.Helpers; +using ServerCore.ModelBases; + +namespace ServerCore.Pages.Pieces +{ + [Authorize(Policy = "PlayerCanSeePuzzle")] + public class SimpleMetaModel : EventSpecificPageModel + { + public SimpleMetaModel(PuzzleServerContext serverContext, UserManager userManager) : base(serverContext, userManager) + { + } + + public IList EarnedPieces { get; set; } + + public Puzzle Puzzle { get; set; } + + public async Task OnGetAsync(int puzzleId) + { + Puzzle = await _context.Puzzles.Where(p => p.ID == puzzleId).FirstOrDefaultAsync(); + + if (Puzzle == null) + { + return NotFound("Puzzle does not exist."); + } + + Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser); + int solvedPuzzleCount = 0; + + switch(Puzzle.PieceMetaUsage) + { + case PieceMetaUsage.EntireEvent: + solvedPuzzleCount = await _context.PuzzleStatePerTeam.Where(ps => ps.Team == team && ps.SolvedTime != null && ps.Puzzle.SolveValue >= 10).CountAsync(); + break; + + case PieceMetaUsage.GroupOnly: + solvedPuzzleCount = await _context.PuzzleStatePerTeam.Where(ps => ps.Team == team && ps.SolvedTime != null && ps.Puzzle.SolveValue >= 10 && ps.Puzzle.Group == Puzzle.Group).CountAsync(); + break; + + default: + return NotFound("Puzzle does not support the simple meta view."); + } + + EarnedPieces = await _context.Pieces.Where(p => p.PuzzleID == puzzleId && p.ProgressLevel <= solvedPuzzleCount).OrderBy(p => p.ProgressLevel).ToListAsync(); + + return Page(); + } + } +} diff --git a/ServerCore/Pages/Puzzles/Edit.cshtml b/ServerCore/Pages/Puzzles/Edit.cshtml index 43cdef67..27df9c08 100644 --- a/ServerCore/Pages/Puzzles/Edit.cshtml +++ b/ServerCore/Pages/Puzzles/Edit.cshtml @@ -148,6 +148,10 @@ +
+ + +
diff --git a/ServerCore/Pages/Teams/Play.cshtml b/ServerCore/Pages/Teams/Play.cshtml index f33281b5..a5217a46 100644 --- a/ServerCore/Pages/Teams/Play.cshtml +++ b/ServerCore/Pages/Teams/Play.cshtml @@ -100,6 +100,11 @@ { @Html.DisplayFor(modelItem => item.Name) } + + @if (item.PieceMetaUsage != DataModel.PieceMetaUsage.None) + { + (Meta Pieces) + } @if (Model.ShowAnswers) { diff --git a/ServerCore/Pages/Teams/Play.cshtml.cs b/ServerCore/Pages/Teams/Play.cshtml.cs index fa308502..82fc491a 100644 --- a/ServerCore/Pages/Teams/Play.cshtml.cs +++ b/ServerCore/Pages/Teams/Play.cshtml.cs @@ -74,7 +74,7 @@ public async Task OnGetAsync(SortOrder? sort, int teamId, PuzzleStateFilter? sta // Note: EF gotcha is that you have to join into anonymous types in order to not lose valuable stuff var visiblePuzzlesQ = from Puzzle puzzle in puzzlesInEventQ join PuzzleStatePerTeam pspt in stateForTeamQ on puzzle.ID equals pspt.PuzzleID - select new PuzzleView { ID = puzzle.ID, Group = puzzle.Group, OrderInGroup = puzzle.OrderInGroup, Name = puzzle.Name, CustomUrl = puzzle.CustomURL, Errata = puzzle.Errata, SolvedTime = pspt.SolvedTime }; + select new PuzzleView { ID = puzzle.ID, Group = puzzle.Group, OrderInGroup = puzzle.OrderInGroup, Name = puzzle.Name, CustomUrl = puzzle.CustomURL, Errata = puzzle.Errata, SolvedTime = pspt.SolvedTime, PieceMetaUsage = puzzle.PieceMetaUsage }; switch (sort ?? DefaultSort) { @@ -159,6 +159,7 @@ public class PuzzleView public DateTime? SolvedTime { get; set; } public ContentFile Content { get; set; } public ContentFile Answer { get; set; } + public PieceMetaUsage PieceMetaUsage { get; set; } } public enum SortOrder