diff --git a/Cicm.Database/Cicm.Database.csproj b/Cicm.Database/Cicm.Database.csproj index 708729d0..13f6b5dc 100644 --- a/Cicm.Database/Cicm.Database.csproj +++ b/Cicm.Database/Cicm.Database.csproj @@ -1,12 +1,18 @@ - + netcoreapp2.1 + + + + ..\..\..\.nuget\packages\microsoft.aspnetcore.identity.entityframeworkcore\2.1.2\lib\netstandard2.0\Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll + + \ No newline at end of file diff --git a/Cicm.Database/Migrations/20180811130603_CreateIdentitySchema.Designer.cs b/Cicm.Database/Migrations/20180811130603_CreateIdentitySchema.Designer.cs new file mode 100644 index 00000000..7938693f --- /dev/null +++ b/Cicm.Database/Migrations/20180811130603_CreateIdentitySchema.Designer.cs @@ -0,0 +1,1956 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : 20180811130603_CreateIdentitySchema.Designer.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// Adds ASP.NET Identity tables +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +// +using System; +using Cicm.Database.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Cicm.Database.Migrations +{ + [DbContext(typeof(cicmContext))] + [Migration("20180811130603_CreateIdentitySchema")] + partial class CreateIdentitySchema + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Cicm.Database.Models.Admin", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Password") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("password") + .HasColumnType("char(50)") + .HasDefaultValueSql("''"); + + b.Property("User") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("user") + .HasColumnType("char(50)") + .HasDefaultValueSql("''"); + + b.HasKey("Id"); + + b.HasIndex("User") + .HasName("idx_admins_user"); + + b.ToTable("admins"); + }); + + modelBuilder.Entity("Cicm.Database.Models.BrowserTest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Agif") + .ValueGeneratedOnAdd() + .HasColumnName("agif") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("Browser") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("browser") + .HasColumnType("varchar(64)") + .HasDefaultValueSql("''"); + + b.Property("Colors") + .ValueGeneratedOnAdd() + .HasColumnName("colors") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("Flash") + .ValueGeneratedOnAdd() + .HasColumnName("flash") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("Frames") + .ValueGeneratedOnAdd() + .HasColumnName("frames") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("Gif87") + .ValueGeneratedOnAdd() + .HasColumnName("gif87") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("Gif89") + .ValueGeneratedOnAdd() + .HasColumnName("gif89") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("Jpeg") + .ValueGeneratedOnAdd() + .HasColumnName("jpeg") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("Js") + .ValueGeneratedOnAdd() + .HasColumnName("js") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("Os") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("os") + .HasColumnType("varchar(32)") + .HasDefaultValueSql("''"); + + b.Property("Platform") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("platform") + .HasColumnType("varchar(8)") + .HasDefaultValueSql("''"); + + b.Property("Png") + .ValueGeneratedOnAdd() + .HasColumnName("png") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("Pngt") + .ValueGeneratedOnAdd() + .HasColumnName("pngt") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("Table") + .ValueGeneratedOnAdd() + .HasColumnName("table") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("UserAgent") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("user_agent") + .HasColumnType("varchar(128)") + .HasDefaultValueSql("''"); + + b.Property("Version") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("version") + .HasColumnType("varchar(16)") + .HasDefaultValueSql("''"); + + b.HasKey("Id"); + + b.HasIndex("Browser") + .HasName("idx_browser_tests_browser"); + + b.HasIndex("Os") + .HasName("idx_browser_tests_os"); + + b.HasIndex("Platform") + .HasName("idx_browser_tests_platform"); + + b.HasIndex("UserAgent") + .HasName("idx_browser_tests_user_agent"); + + b.HasIndex("Version") + .HasName("idx_browser_tests_version"); + + b.ToTable("browser_tests"); + }); + + modelBuilder.Entity("Cicm.Database.Models.CicmDb", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Updated") + .ValueGeneratedOnAdd() + .HasColumnName("updated") + .HasColumnType("datetime") + .HasDefaultValueSql("'CURRENT_TIMESTAMP'"); + + b.Property("Version") + .HasColumnName("version") + .HasColumnType("int(11)"); + + b.HasKey("Id"); + + b.ToTable("cicm_db"); + }); + + modelBuilder.Entity("Cicm.Database.Models.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Address") + .HasColumnName("address") + .HasColumnType("varchar(80)"); + + b.Property("City") + .HasColumnName("city") + .HasColumnType("varchar(80)"); + + b.Property("CountryId") + .HasColumnName("country") + .HasColumnType("smallint(3)"); + + b.Property("Facebook") + .HasColumnName("facebook") + .HasColumnType("varchar(45)"); + + b.Property("Founded") + .HasColumnName("founded") + .HasColumnType("datetime"); + + b.Property("Name") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("name") + .HasColumnType("varchar(128)") + .HasDefaultValueSql("''"); + + b.Property("PostalCode") + .HasColumnName("postal_code") + .HasColumnType("varchar(25)"); + + b.Property("Province") + .HasColumnName("province") + .HasColumnType("varchar(80)"); + + b.Property("Sold") + .HasColumnName("sold") + .HasColumnType("datetime"); + + b.Property("SoldToId") + .HasColumnName("sold_to") + .HasColumnType("int(11)"); + + b.Property("Status") + .HasColumnName("status") + .HasColumnType("int(11)"); + + b.Property("Twitter") + .HasColumnName("twitter") + .HasColumnType("varchar(45)"); + + b.Property("Website") + .HasColumnName("website") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("Address") + .HasName("idx_companies_address"); + + b.HasIndex("City") + .HasName("idx_companies_city"); + + b.HasIndex("CountryId") + .HasName("idx_companies_country"); + + b.HasIndex("Facebook") + .HasName("idx_companies_facebook"); + + b.HasIndex("Founded") + .HasName("idx_companies_founded"); + + b.HasIndex("Name") + .HasName("idx_companies_name"); + + b.HasIndex("PostalCode") + .HasName("idx_companies_postal_code"); + + b.HasIndex("Province") + .HasName("idx_companies_province"); + + b.HasIndex("Sold") + .HasName("idx_companies_sold"); + + b.HasIndex("SoldToId") + .HasName("idx_companies_sold_to"); + + b.HasIndex("Status") + .HasName("idx_companies_status"); + + b.HasIndex("Twitter") + .HasName("idx_companies_twitter"); + + b.HasIndex("Website") + .HasName("idx_companies_website"); + + b.ToTable("companies"); + }); + + modelBuilder.Entity("Cicm.Database.Models.CompanyDescription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("CompanyId") + .HasColumnName("company_id") + .HasColumnType("int(11)"); + + b.Property("Text") + .HasColumnName("text") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId") + .IsUnique() + .HasName("idx_company_id"); + + b.HasIndex("Text") + .HasName("idx_text"); + + b.ToTable("company_descriptions"); + }); + + modelBuilder.Entity("Cicm.Database.Models.CompanyLogo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("CompanyId") + .HasColumnName("company_id") + .HasColumnType("int(11)"); + + b.Property("Guid") + .HasColumnName("logo_guid") + .HasColumnType("char(36)"); + + b.Property("Year") + .HasColumnName("year") + .HasColumnType("int(4)"); + + b.HasKey("Id", "CompanyId", "Guid"); + + b.HasIndex("CompanyId") + .HasName("idx_company_id"); + + b.HasIndex("Guid") + .HasName("idx_guid"); + + b.HasIndex("Id") + .IsUnique() + .HasName("idx_id"); + + b.ToTable("company_logos"); + }); + + modelBuilder.Entity("Cicm.Database.Models.Forbidden", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Browser") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("browser") + .HasColumnType("char(128)") + .HasDefaultValueSql("''"); + + b.Property("Date") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("date") + .HasColumnType("char(20)") + .HasDefaultValueSql("''"); + + b.Property("Ip") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("ip") + .HasColumnType("char(16)") + .HasDefaultValueSql("''"); + + b.Property("Referer") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("referer") + .HasColumnType("char(255)") + .HasDefaultValueSql("''"); + + b.HasKey("Id"); + + b.HasIndex("Browser") + .HasName("idx_forbidden_browser"); + + b.HasIndex("Date") + .HasName("idx_forbidden_date"); + + b.HasIndex("Ip") + .HasName("idx_forbidden_ip"); + + b.HasIndex("Referer") + .HasName("idx_forbidden_referer"); + + b.ToTable("forbidden"); + }); + + modelBuilder.Entity("Cicm.Database.Models.Gpu", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("CompanyId") + .HasColumnName("company") + .HasColumnType("int(11)"); + + b.Property("DieSize") + .HasColumnName("die_size"); + + b.Property("Introduced") + .HasColumnName("introduced") + .HasColumnType("datetime"); + + b.Property("ModelCode") + .HasColumnName("model_code") + .HasColumnType("varchar(45)"); + + b.Property("Name") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("name") + .HasColumnType("char(128)") + .HasDefaultValueSql("''"); + + b.Property("Package") + .HasColumnName("package") + .HasColumnType("varchar(45)"); + + b.Property("Process") + .HasColumnName("process") + .HasColumnType("varchar(45)"); + + b.Property("ProcessNm") + .HasColumnName("process_nm"); + + b.Property("Transistors") + .HasColumnName("transistors") + .HasColumnType("bigint(20)"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId") + .HasName("idx_gpus_company"); + + b.HasIndex("DieSize") + .HasName("idx_gpus_die_size"); + + b.HasIndex("Introduced") + .HasName("idx_gpus_introduced"); + + b.HasIndex("ModelCode") + .HasName("idx_gpus_model_code"); + + b.HasIndex("Name") + .HasName("idx_gpus_name"); + + b.HasIndex("Package") + .HasName("idx_gpus_package"); + + b.HasIndex("Process") + .HasName("idx_gpus_process"); + + b.HasIndex("ProcessNm") + .HasName("idx_gpus_process_nm"); + + b.HasIndex("Transistors") + .HasName("idx_gpus_transistors"); + + b.ToTable("gpus"); + }); + + modelBuilder.Entity("Cicm.Database.Models.GpusByMachine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("bigint(20)"); + + b.Property("GpuId") + .HasColumnName("gpu") + .HasColumnType("int(11)"); + + b.Property("MachineId") + .HasColumnName("machine") + .HasColumnType("int(11)"); + + b.HasKey("Id"); + + b.HasIndex("GpuId") + .HasName("idx_gpus_by_machine_gpus"); + + b.HasIndex("MachineId") + .HasName("idx_gpus_by_machine_machine"); + + b.ToTable("gpus_by_machine"); + }); + + modelBuilder.Entity("Cicm.Database.Models.InstructionSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Name") + .IsRequired() + .HasColumnName("instruction_set") + .HasColumnType("varchar(45)"); + + b.HasKey("Id"); + + b.ToTable("instruction_sets"); + }); + + modelBuilder.Entity("Cicm.Database.Models.InstructionSetExtension", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Extension") + .IsRequired() + .HasColumnName("extension") + .HasColumnType("varchar(45)"); + + b.HasKey("Id"); + + b.ToTable("instruction_set_extensions"); + }); + + modelBuilder.Entity("Cicm.Database.Models.InstructionSetExtensionsByProcessor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("ProcessorId") + .HasColumnName("processor_id") + .HasColumnType("int(11)"); + + b.Property("ExtensionId") + .HasColumnName("extension_id") + .HasColumnType("int(11)"); + + b.HasKey("Id", "ProcessorId", "ExtensionId"); + + b.HasIndex("ExtensionId") + .HasName("idx_setextension_extension"); + + b.HasIndex("ProcessorId") + .HasName("idx_setextension_processor"); + + b.ToTable("instruction_set_extensions_by_processor"); + }); + + modelBuilder.Entity("Cicm.Database.Models.Iso31661Numeric", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("smallint(3)"); + + b.Property("Name") + .IsRequired() + .HasColumnName("name") + .HasColumnType("varchar(64)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasName("idx_name"); + + b.ToTable("iso3166_1_numeric"); + }); + + modelBuilder.Entity("Cicm.Database.Models.Log", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Browser") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("browser") + .HasColumnType("char(128)") + .HasDefaultValueSql("''"); + + b.Property("Date") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("date") + .HasColumnType("char(20)") + .HasDefaultValueSql("''"); + + b.Property("Ip") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("ip") + .HasColumnType("char(16)") + .HasDefaultValueSql("''"); + + b.Property("Referer") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("referer") + .HasColumnType("char(255)") + .HasDefaultValueSql("''"); + + b.HasKey("Id"); + + b.HasIndex("Browser") + .HasName("idx_log_browser"); + + b.HasIndex("Date") + .HasName("idx_log_date"); + + b.HasIndex("Ip") + .HasName("idx_log_ip"); + + b.HasIndex("Referer") + .HasName("idx_log_referer"); + + b.ToTable("log"); + }); + + modelBuilder.Entity("Cicm.Database.Models.Machine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("CompanyId") + .ValueGeneratedOnAdd() + .HasColumnName("company") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("FamilyId") + .HasColumnName("family") + .HasColumnType("int(11)"); + + b.Property("Introduced") + .HasColumnName("introduced") + .HasColumnType("datetime"); + + b.Property("Model") + .HasColumnName("model") + .HasColumnType("varchar(50)"); + + b.Property("Name") + .IsRequired() + .HasColumnName("name") + .HasColumnType("varchar(255)"); + + b.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnName("type") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId") + .HasName("idx_machines_company"); + + b.HasIndex("FamilyId") + .HasName("idx_machines_family"); + + b.HasIndex("Introduced") + .HasName("idx_machines_introduced"); + + b.HasIndex("Model") + .HasName("idx_machines_model"); + + b.HasIndex("Name") + .HasName("idx_machines_name"); + + b.HasIndex("Type") + .HasName("idx_machines_type"); + + b.ToTable("machines"); + }); + + modelBuilder.Entity("Cicm.Database.Models.MachineFamily", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("CompanyId") + .HasColumnName("company") + .HasColumnType("int(11)"); + + b.Property("Name") + .IsRequired() + .HasColumnName("name") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId") + .HasName("idx_machine_families_company"); + + b.HasIndex("Name") + .HasName("idx_machine_families_name"); + + b.ToTable("machine_families"); + }); + + modelBuilder.Entity("Cicm.Database.Models.MemoryByMachine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("bigint(20)"); + + b.Property("MachineId") + .HasColumnName("machine") + .HasColumnType("int(11)"); + + b.Property("Size") + .HasColumnName("size") + .HasColumnType("bigint(20)"); + + b.Property("Speed") + .HasColumnName("speed"); + + b.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnName("type") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Usage") + .ValueGeneratedOnAdd() + .HasColumnName("usage") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.HasKey("Id"); + + b.HasIndex("MachineId") + .HasName("idx_memory_by_machine_machine"); + + b.HasIndex("Size") + .HasName("idx_memory_by_machine_size"); + + b.HasIndex("Speed") + .HasName("idx_memory_by_machine_speed"); + + b.HasIndex("Type") + .HasName("idx_memory_by_machine_type"); + + b.HasIndex("Usage") + .HasName("idx_memory_by_machine_usage"); + + b.ToTable("memory_by_machine"); + }); + + modelBuilder.Entity("Cicm.Database.Models.MoneyDonation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Donator") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("donator") + .HasColumnType("char(128)") + .HasDefaultValueSql("''"); + + b.Property("Quantity") + .ValueGeneratedOnAdd() + .HasColumnName("quantity") + .HasColumnType("decimal(11,2)") + .HasDefaultValueSql("'0.00'"); + + b.HasKey("Id"); + + b.HasIndex("Donator") + .HasName("idx_money_donations_donator"); + + b.HasIndex("Quantity") + .HasName("idx_money_donations_quantity"); + + b.ToTable("money_donations"); + }); + + modelBuilder.Entity("Cicm.Database.Models.News", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("AddedId") + .ValueGeneratedOnAdd() + .HasColumnName("added_id") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Date") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("date") + .HasColumnType("char(20)") + .HasDefaultValueSql("''"); + + b.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnName("type") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.HasKey("Id"); + + b.HasIndex("AddedId") + .HasName("idx_news_ip"); + + b.HasIndex("Date") + .HasName("idx_news_date"); + + b.HasIndex("Type") + .HasName("idx_news_type"); + + b.ToTable("news"); + }); + + modelBuilder.Entity("Cicm.Database.Models.OwnedComputer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Boxed") + .ValueGeneratedOnAdd() + .HasColumnName("boxed") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Cap1") + .ValueGeneratedOnAdd() + .HasColumnName("cap1") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Cap2") + .ValueGeneratedOnAdd() + .HasColumnName("cap2") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Cpu1") + .ValueGeneratedOnAdd() + .HasColumnName("cpu1") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Cpu2") + .ValueGeneratedOnAdd() + .HasColumnName("cpu2") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Date") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("date") + .HasColumnType("varchar(20)") + .HasDefaultValueSql("''"); + + b.Property("DbId") + .ValueGeneratedOnAdd() + .HasColumnName("db_id") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Disk1") + .ValueGeneratedOnAdd() + .HasColumnName("disk1") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Disk2") + .ValueGeneratedOnAdd() + .HasColumnName("disk2") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Manuals") + .ValueGeneratedOnAdd() + .HasColumnName("manuals") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Mhz1") + .ValueGeneratedOnAdd() + .HasColumnName("mhz1") + .HasColumnType("decimal(10,0)") + .HasDefaultValueSql("'0'"); + + b.Property("Mhz2") + .ValueGeneratedOnAdd() + .HasColumnName("mhz2") + .HasColumnType("decimal(10,0)") + .HasDefaultValueSql("'0'"); + + b.Property("Ram") + .ValueGeneratedOnAdd() + .HasColumnName("ram") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Rigid") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("rigid") + .HasColumnType("varchar(64)") + .HasDefaultValueSql("''"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnName("status") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Trade") + .ValueGeneratedOnAdd() + .HasColumnName("trade") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Vram") + .ValueGeneratedOnAdd() + .HasColumnName("vram") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.HasKey("Id"); + + b.HasIndex("Boxed") + .HasName("idx_owned_computers_boxed"); + + b.HasIndex("Cap1") + .HasName("idx_owned_computers_cap1"); + + b.HasIndex("Cap2") + .HasName("idx_owned_computers_cap2"); + + b.HasIndex("Cpu1") + .HasName("idx_owned_computers_cpu1"); + + b.HasIndex("Cpu2") + .HasName("idx_owned_computers_cpu2"); + + b.HasIndex("Date") + .HasName("idx_owned_computers_date"); + + b.HasIndex("DbId") + .HasName("idx_owned_computers_db_id"); + + b.HasIndex("Disk1") + .HasName("idx_owned_computers_disk1"); + + b.HasIndex("Disk2") + .HasName("idx_owned_computers_disk2"); + + b.HasIndex("Manuals") + .HasName("idx_owned_computers_manuals"); + + b.HasIndex("Mhz1") + .HasName("idx_owned_computers_mhz1"); + + b.HasIndex("Mhz2") + .HasName("idx_owned_computers_mhz2"); + + b.HasIndex("Ram") + .HasName("idx_owned_computers_ram"); + + b.HasIndex("Rigid") + .HasName("idx_owned_computers_rigid"); + + b.HasIndex("Status") + .HasName("idx_owned_computers_status"); + + b.HasIndex("Trade") + .HasName("idx_owned_computers_trade"); + + b.HasIndex("Vram") + .HasName("idx_owned_computers_vram"); + + b.ToTable("owned_computers"); + }); + + modelBuilder.Entity("Cicm.Database.Models.OwnedConsole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Boxed") + .ValueGeneratedOnAdd() + .HasColumnName("boxed") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Date") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("date") + .HasColumnType("char(20)") + .HasDefaultValueSql("''"); + + b.Property("DbId") + .ValueGeneratedOnAdd() + .HasColumnName("db_id") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Manuals") + .ValueGeneratedOnAdd() + .HasColumnName("manuals") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnName("status") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Trade") + .ValueGeneratedOnAdd() + .HasColumnName("trade") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.HasKey("Id"); + + b.HasIndex("Boxed") + .HasName("idx_owned_consoles_boxed"); + + b.HasIndex("Date") + .HasName("idx_owned_consoles_date"); + + b.HasIndex("DbId") + .HasName("idx_owned_consoles_db_id"); + + b.HasIndex("Manuals") + .HasName("idx_owned_consoles_manuals"); + + b.HasIndex("Status") + .HasName("idx_owned_consoles_status"); + + b.HasIndex("Trade") + .HasName("idx_owned_consoles_trade"); + + b.ToTable("owned_consoles"); + }); + + modelBuilder.Entity("Cicm.Database.Models.Processor", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("AddrBus") + .HasColumnName("addr_bus") + .HasColumnType("int(11)"); + + b.Property("CompanyId") + .HasColumnName("company") + .HasColumnType("int(11)"); + + b.Property("Cores") + .HasColumnName("cores") + .HasColumnType("int(11)"); + + b.Property("DataBus") + .HasColumnName("data_bus") + .HasColumnType("int(11)"); + + b.Property("DieSize") + .HasColumnName("die_size"); + + b.Property("FprSize") + .HasColumnName("FPR_size") + .HasColumnType("int(11)"); + + b.Property("Fprs") + .HasColumnName("FPRs") + .HasColumnType("int(11)"); + + b.Property("GprSize") + .HasColumnName("GPR_size") + .HasColumnType("int(11)"); + + b.Property("Gprs") + .HasColumnName("GPRs") + .HasColumnType("int(11)"); + + b.Property("InstructionSetId") + .HasColumnName("instruction_set") + .HasColumnType("int(11)"); + + b.Property("Introduced") + .HasColumnName("introduced") + .HasColumnType("datetime"); + + b.Property("L1Data") + .HasColumnName("L1_data"); + + b.Property("L1Instruction") + .HasColumnName("L1_instruction"); + + b.Property("L2"); + + b.Property("L3"); + + b.Property("ModelCode") + .HasColumnName("model_code") + .HasColumnType("varchar(45)"); + + b.Property("Name") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("name") + .HasColumnType("char(50)") + .HasDefaultValueSql("''"); + + b.Property("Package") + .HasColumnName("package") + .HasColumnType("varchar(45)"); + + b.Property("Process") + .HasColumnName("process") + .HasColumnType("varchar(45)"); + + b.Property("ProcessNm") + .HasColumnName("process_nm"); + + b.Property("SimdRegisters") + .HasColumnName("SIMD_registers") + .HasColumnType("int(11)"); + + b.Property("SimdSize") + .HasColumnName("SIMD_size") + .HasColumnType("int(11)"); + + b.Property("Speed") + .HasColumnName("speed"); + + b.Property("ThreadsPerCore") + .HasColumnName("threads_per_core") + .HasColumnType("int(11)"); + + b.Property("Transistors") + .HasColumnName("transistors") + .HasColumnType("bigint(20)"); + + b.HasKey("Id"); + + b.HasIndex("AddrBus") + .HasName("idx_processors_addr_bus"); + + b.HasIndex("CompanyId") + .HasName("idx_processors_company"); + + b.HasIndex("Cores") + .HasName("idx_processors_cores"); + + b.HasIndex("DataBus") + .HasName("idx_processors_data_bus"); + + b.HasIndex("DieSize") + .HasName("idx_processors_die_size"); + + b.HasIndex("FprSize") + .HasName("idx_processors_FPR_size"); + + b.HasIndex("Fprs") + .HasName("idx_processors_FPRs"); + + b.HasIndex("GprSize") + .HasName("idx_processors_GPR_size"); + + b.HasIndex("Gprs") + .HasName("idx_processors_GPRs"); + + b.HasIndex("InstructionSetId") + .HasName("idx_processors_instruction_set"); + + b.HasIndex("Introduced") + .HasName("idx_processors_introduced"); + + b.HasIndex("L1Data") + .HasName("idx_processors_L1_data"); + + b.HasIndex("L1Instruction") + .HasName("idx_processors_L1_instruction"); + + b.HasIndex("L2") + .HasName("idx_processors_L2"); + + b.HasIndex("L3") + .HasName("idx_processors_L3"); + + b.HasIndex("ModelCode") + .HasName("idx_processors_model_code"); + + b.HasIndex("Name") + .HasName("idx_processors_name"); + + b.HasIndex("Package") + .HasName("idx_processors_package"); + + b.HasIndex("Process") + .HasName("idx_processors_process"); + + b.HasIndex("ProcessNm") + .HasName("idx_processors_process_nm"); + + b.HasIndex("SimdRegisters") + .HasName("idx_processors_SIMD_registers"); + + b.HasIndex("SimdSize") + .HasName("idx_processors_SIMD_size"); + + b.HasIndex("Speed") + .HasName("idx_processors_speed"); + + b.HasIndex("ThreadsPerCore") + .HasName("idx_processors_threads_per_core"); + + b.HasIndex("Transistors") + .HasName("idx_processors_transistors"); + + b.ToTable("processors"); + }); + + modelBuilder.Entity("Cicm.Database.Models.ProcessorsByMachine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("bigint(20)"); + + b.Property("MachineId") + .HasColumnName("machine") + .HasColumnType("int(11)"); + + b.Property("ProcessorId") + .HasColumnName("processor") + .HasColumnType("int(11)"); + + b.Property("Speed") + .HasColumnName("speed"); + + b.HasKey("Id"); + + b.HasIndex("MachineId") + .HasName("idx_processors_by_machine_machine"); + + b.HasIndex("ProcessorId") + .HasName("idx_processors_by_machine_processor"); + + b.HasIndex("Speed") + .HasName("idx_processors_by_machine_speed"); + + b.ToTable("processors_by_machine"); + }); + + modelBuilder.Entity("Cicm.Database.Models.Resolution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("Chars") + .ValueGeneratedOnAdd() + .HasColumnName("chars") + .HasColumnType("tinyint(1)") + .HasDefaultValueSql("'0'"); + + b.Property("Colors") + .HasColumnName("colors") + .HasColumnType("bigint(20)"); + + b.Property("Height") + .ValueGeneratedOnAdd() + .HasColumnName("height") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("Palette") + .HasColumnName("palette") + .HasColumnType("bigint(20)"); + + b.Property("Width") + .ValueGeneratedOnAdd() + .HasColumnName("width") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.HasKey("Id"); + + b.HasIndex("Colors") + .HasName("idx_resolutions_colors"); + + b.HasIndex("Height") + .HasName("idx_resolutions_height"); + + b.HasIndex("Palette") + .HasName("idx_resolutions_palette"); + + b.HasIndex("Width") + .HasName("idx_resolutions_width"); + + b.HasIndex("Width", "Height") + .HasName("idx_resolutions_resolution"); + + b.HasIndex("Width", "Height", "Colors") + .HasName("idx_resolutions_resolution_with_color"); + + b.HasIndex("Width", "Height", "Colors", "Palette") + .HasName("idx_resolutions_resolution_with_color_and_palette"); + + b.ToTable("resolutions"); + }); + + modelBuilder.Entity("Cicm.Database.Models.ResolutionsByGpu", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("bigint(20)"); + + b.Property("GpuId") + .HasColumnName("gpu") + .HasColumnType("int(11)"); + + b.Property("ResolutionId") + .HasColumnName("resolution") + .HasColumnType("int(11)"); + + b.HasKey("Id"); + + b.HasIndex("GpuId") + .HasName("idx_resolutions_by_gpu_gpu"); + + b.HasIndex("ResolutionId") + .HasName("idx_resolutions_by_gpu_resolution"); + + b.ToTable("resolutions_by_gpu"); + }); + + modelBuilder.Entity("Cicm.Database.Models.SoundByMachine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("bigint(20)"); + + b.Property("MachineId") + .HasColumnName("machine") + .HasColumnType("int(11)"); + + b.Property("SoundSynthId") + .HasColumnName("sound_synth") + .HasColumnType("int(11)"); + + b.HasKey("Id"); + + b.HasIndex("MachineId") + .HasName("idx_sound_by_machine_machine"); + + b.HasIndex("SoundSynthId") + .HasName("idx_sound_by_machine_sound_synth"); + + b.ToTable("sound_by_machine"); + }); + + modelBuilder.Entity("Cicm.Database.Models.SoundSynth", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("int(11)"); + + b.Property("CompanyId") + .HasColumnName("company") + .HasColumnType("int(11)"); + + b.Property("Depth") + .HasColumnName("depth") + .HasColumnType("int(11)"); + + b.Property("Frequency") + .HasColumnName("frequency"); + + b.Property("Introduced") + .HasColumnName("introduced") + .HasColumnType("datetime"); + + b.Property("ModelCode") + .HasColumnName("model_code") + .HasColumnType("varchar(45)"); + + b.Property("Name") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnName("name") + .HasColumnType("char(50)") + .HasDefaultValueSql("''"); + + b.Property("SquareWave") + .HasColumnName("square_wave") + .HasColumnType("int(11)"); + + b.Property("Type") + .HasColumnName("type") + .HasColumnType("int(11)"); + + b.Property("Voices") + .HasColumnName("voices") + .HasColumnType("int(11)"); + + b.Property("WhiteNoise") + .HasColumnName("white_noise") + .HasColumnType("int(11)"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId") + .HasName("idx_sound_synths_company"); + + b.HasIndex("Depth") + .HasName("idx_sound_synths_depth"); + + b.HasIndex("Frequency") + .HasName("idx_sound_synths_frequency"); + + b.HasIndex("Introduced") + .HasName("idx_sound_synths_introduced"); + + b.HasIndex("ModelCode") + .HasName("idx_sound_synths_model_code"); + + b.HasIndex("Name") + .HasName("idx_sound_synths_name"); + + b.HasIndex("SquareWave") + .HasName("idx_sound_synths_square_wave"); + + b.HasIndex("Type") + .HasName("idx_sound_synths_type"); + + b.HasIndex("Voices") + .HasName("idx_sound_synths_voices"); + + b.HasIndex("WhiteNoise") + .HasName("idx_sound_synths_white_noise"); + + b.ToTable("sound_synths"); + }); + + modelBuilder.Entity("Cicm.Database.Models.StorageByMachine", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("bigint(20)"); + + b.Property("Capacity") + .HasColumnName("capacity") + .HasColumnType("bigint(20)"); + + b.Property("Interface") + .ValueGeneratedOnAdd() + .HasColumnName("interface") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.Property("MachineId") + .HasColumnName("machine") + .HasColumnType("int(11)"); + + b.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnName("type") + .HasColumnType("int(11)") + .HasDefaultValueSql("'0'"); + + b.HasKey("Id"); + + b.HasIndex("Capacity") + .HasName("idx_storage_capacity"); + + b.HasIndex("Interface") + .HasName("idx_storage_interface"); + + b.HasIndex("MachineId") + .HasName("idx_storage_machine"); + + b.HasIndex("Type") + .HasName("idx_storage_type"); + + b.ToTable("storage_by_machine"); + }); + + 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"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + 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"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + 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("Cicm.Database.Models.Company", b => + { + b.HasOne("Cicm.Database.Models.Iso31661Numeric", "Country") + .WithMany("Companies") + .HasForeignKey("CountryId") + .HasConstraintName("fk_companies_country"); + + b.HasOne("Cicm.Database.Models.Company", "SoldTo") + .WithMany("InverseSoldToNavigation") + .HasForeignKey("SoldToId") + .HasConstraintName("fk_companies_sold_to"); + }); + + modelBuilder.Entity("Cicm.Database.Models.CompanyDescription", b => + { + b.HasOne("Cicm.Database.Models.Company", "Company") + .WithOne("Description") + .HasForeignKey("Cicm.Database.Models.CompanyDescription", "Id") + .HasConstraintName("fk_company_id") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Cicm.Database.Models.CompanyLogo", b => + { + b.HasOne("Cicm.Database.Models.Company", "Company") + .WithMany("Logos") + .HasForeignKey("CompanyId") + .HasConstraintName("fk_company_logos_company1"); + }); + + modelBuilder.Entity("Cicm.Database.Models.Gpu", b => + { + b.HasOne("Cicm.Database.Models.Company", "Company") + .WithMany("Gpus") + .HasForeignKey("CompanyId") + .HasConstraintName("fk_gpus_company"); + }); + + modelBuilder.Entity("Cicm.Database.Models.GpusByMachine", b => + { + b.HasOne("Cicm.Database.Models.Gpu", "Gpu") + .WithMany("GpusByMachine") + .HasForeignKey("GpuId") + .HasConstraintName("fk_gpus_by_machine_gpu") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Cicm.Database.Models.Machine", "Machine") + .WithMany("Gpus") + .HasForeignKey("MachineId") + .HasConstraintName("fk_gpus_by_machine_machine") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Cicm.Database.Models.InstructionSetExtensionsByProcessor", b => + { + b.HasOne("Cicm.Database.Models.InstructionSetExtension", "Extension") + .WithMany("InstructionSetExtensionsByProcessor") + .HasForeignKey("ExtensionId") + .HasConstraintName("fk_extension_extension_id"); + + b.HasOne("Cicm.Database.Models.Processor", "Processor") + .WithMany("InstructionSetExtensions") + .HasForeignKey("ProcessorId") + .HasConstraintName("fk_extension_processor_id"); + }); + + modelBuilder.Entity("Cicm.Database.Models.Machine", b => + { + b.HasOne("Cicm.Database.Models.Company", "Company") + .WithMany("Machines") + .HasForeignKey("CompanyId") + .HasConstraintName("fk_machines_company"); + + b.HasOne("Cicm.Database.Models.MachineFamily", "Family") + .WithMany("Machines") + .HasForeignKey("FamilyId") + .HasConstraintName("fk_machines_family"); + }); + + modelBuilder.Entity("Cicm.Database.Models.MachineFamily", b => + { + b.HasOne("Cicm.Database.Models.Company", "Company") + .WithMany("MachineFamilies") + .HasForeignKey("CompanyId") + .HasConstraintName("fk_machine_families_company") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Cicm.Database.Models.MemoryByMachine", b => + { + b.HasOne("Cicm.Database.Models.Machine", "Machine") + .WithMany("Memory") + .HasForeignKey("MachineId") + .HasConstraintName("fk_memory_by_machine_machine") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Cicm.Database.Models.Processor", b => + { + b.HasOne("Cicm.Database.Models.Company", "Company") + .WithMany("Processors") + .HasForeignKey("CompanyId") + .HasConstraintName("fk_processors_company"); + + b.HasOne("Cicm.Database.Models.InstructionSet", "InstructionSet") + .WithMany("Processors") + .HasForeignKey("InstructionSetId") + .HasConstraintName("fk_processors_instruction_set"); + }); + + modelBuilder.Entity("Cicm.Database.Models.ProcessorsByMachine", b => + { + b.HasOne("Cicm.Database.Models.Machine", "Machine") + .WithMany("Processors") + .HasForeignKey("MachineId") + .HasConstraintName("fk_processors_by_machine_machine") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Cicm.Database.Models.Processor", "Processor") + .WithMany("ProcessorsByMachine") + .HasForeignKey("ProcessorId") + .HasConstraintName("fk_processors_by_machine_processor") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Cicm.Database.Models.ResolutionsByGpu", b => + { + b.HasOne("Cicm.Database.Models.Gpu", "Gpu") + .WithMany("ResolutionsByGpu") + .HasForeignKey("GpuId") + .HasConstraintName("fk_resolutions_by_gpu_gpu") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Cicm.Database.Models.Resolution", "Resolution") + .WithMany("ResolutionsByGpu") + .HasForeignKey("ResolutionId") + .HasConstraintName("fk_resolutions_by_gpu_resolution") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Cicm.Database.Models.SoundByMachine", b => + { + b.HasOne("Cicm.Database.Models.Machine", "Machine") + .WithMany("Sound") + .HasForeignKey("MachineId") + .HasConstraintName("fk_sound_by_machine_machine") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Cicm.Database.Models.SoundSynth", "SoundSynth") + .WithMany("SoundByMachine") + .HasForeignKey("SoundSynthId") + .HasConstraintName("fk_sound_by_machine_sound_synth") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Cicm.Database.Models.SoundSynth", b => + { + b.HasOne("Cicm.Database.Models.Company", "Company") + .WithMany("SoundSynths") + .HasForeignKey("CompanyId") + .HasConstraintName("fk_sound_synths_company"); + }); + + modelBuilder.Entity("Cicm.Database.Models.StorageByMachine", b => + { + b.HasOne("Cicm.Database.Models.Machine", "Machine") + .WithMany("Storage") + .HasForeignKey("MachineId") + .HasConstraintName("fk_storage_by_machine_machine") + .OnDelete(DeleteBehavior.Cascade); + }); + + 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); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Cicm.Database/Migrations/20180811130603_CreateIdentitySchema.cs b/Cicm.Database/Migrations/20180811130603_CreateIdentitySchema.cs new file mode 100644 index 00000000..d676f635 --- /dev/null +++ b/Cicm.Database/Migrations/20180811130603_CreateIdentitySchema.cs @@ -0,0 +1,184 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : 20180811130603_CreateIdentitySchema.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// Adds ASP.NET Identity tables +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Cicm.Database.Migrations +{ + public partial class CreateIdentitySchema : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable("AspNetRoles", + table => new + { + Id = table.Column(nullable: false), + Name = table.Column(maxLength: 256, nullable: true), + NormalizedName = table.Column(maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(nullable: true) + }, constraints: table => { table.PrimaryKey("PK_AspNetRoles", x => x.Id); }); + + migrationBuilder.CreateTable("AspNetUsers", + table => new + { + Id = table.Column(nullable: false), + UserName = + table.Column(maxLength: 256, nullable: true), + NormalizedUserName = + table.Column(maxLength: 256, nullable: true), + Email = + table.Column(maxLength: 256, nullable: true), + NormalizedEmail = + table.Column(maxLength: 256, nullable: true), + EmailConfirmed = table.Column(nullable: false), + PasswordHash = table.Column(nullable: true), + SecurityStamp = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + PhoneNumber = table.Column(nullable: true), + PhoneNumberConfirmed = table.Column(nullable: false), + TwoFactorEnabled = table.Column(nullable: false), + LockoutEnd = table.Column(nullable: true), + LockoutEnabled = table.Column(nullable: false), + AccessFailedCount = table.Column(nullable: false) + }, constraints: table => { table.PrimaryKey("PK_AspNetUsers", x => x.Id); }); + + migrationBuilder.CreateTable("AspNetRoleClaims", + table => new + { + Id = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(nullable: false), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true) + }, constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey("FK_AspNetRoleClaims_AspNetRoles_RoleId", x => x.RoleId, + "AspNetRoles", "Id", onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable("AspNetUserClaims", + table => new + { + Id = table.Column(nullable: false) + .Annotation("MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(nullable: false), + ClaimType = table.Column(nullable: true), + ClaimValue = table.Column(nullable: true) + }, constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey("FK_AspNetUserClaims_AspNetUsers_UserId", x => x.UserId, + "AspNetUsers", "Id", onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable("AspNetUserLogins", + table => new + { + LoginProvider = + table.Column(maxLength: 128, nullable: false), + ProviderKey = + table.Column(maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(nullable: true), + UserId = table.Column(nullable: false) + }, constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", + x => new {x.LoginProvider, x.ProviderKey}); + table.ForeignKey("FK_AspNetUserLogins_AspNetUsers_UserId", x => x.UserId, + "AspNetUsers", "Id", onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable("AspNetUserRoles", + table => new + { + UserId = table.Column(nullable: false), + RoleId = table.Column(nullable: false) + }, constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new {x.UserId, x.RoleId}); + table.ForeignKey("FK_AspNetUserRoles_AspNetRoles_RoleId", x => x.RoleId, + "AspNetRoles", "Id", onDelete: ReferentialAction.Cascade); + table.ForeignKey("FK_AspNetUserRoles_AspNetUsers_UserId", x => x.UserId, + "AspNetUsers", "Id", onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable("AspNetUserTokens", + table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(maxLength: 128, nullable: false), + Name = table.Column(maxLength: 128, nullable: false), + Value = table.Column(nullable: true) + }, constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", + x => new {x.UserId, x.LoginProvider, x.Name}); + table.ForeignKey("FK_AspNetUserTokens_AspNetUsers_UserId", x => x.UserId, + "AspNetUsers", "Id", onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex("IX_AspNetRoleClaims_RoleId", "AspNetRoleClaims", "RoleId"); + + migrationBuilder.CreateIndex("RoleNameIndex", "AspNetRoles", "NormalizedName", unique: true); + + migrationBuilder.CreateIndex("IX_AspNetUserClaims_UserId", "AspNetUserClaims", "UserId"); + + migrationBuilder.CreateIndex("IX_AspNetUserLogins_UserId", "AspNetUserLogins", "UserId"); + + migrationBuilder.CreateIndex("IX_AspNetUserRoles_RoleId", "AspNetUserRoles", "RoleId"); + + migrationBuilder.CreateIndex("EmailIndex", "AspNetUsers", "NormalizedEmail"); + + migrationBuilder.CreateIndex("UserNameIndex", "AspNetUsers", "NormalizedUserName", unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable("AspNetRoleClaims"); + + migrationBuilder.DropTable("AspNetUserClaims"); + + migrationBuilder.DropTable("AspNetUserLogins"); + + migrationBuilder.DropTable("AspNetUserRoles"); + + migrationBuilder.DropTable("AspNetUserTokens"); + + migrationBuilder.DropTable("AspNetRoles"); + + migrationBuilder.DropTable("AspNetUsers"); + } + } +} \ No newline at end of file diff --git a/Cicm.Database/Migrations/cicmContextModelSnapshot.cs b/Cicm.Database/Migrations/cicmContextModelSnapshot.cs index 16cb6d79..f8aa698a 100644 --- a/Cicm.Database/Migrations/cicmContextModelSnapshot.cs +++ b/Cicm.Database/Migrations/cicmContextModelSnapshot.cs @@ -925,6 +925,143 @@ namespace Cicm.Database.Migrations b.ToTable("storage_by_machine"); }); + 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"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id").ValueGeneratedOnAdd(); + + 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"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id").ValueGeneratedOnAdd(); + + 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("Cicm.Database.Models.Company", b => { b.HasOne("Cicm.Database.Models.Iso31661Numeric", "Country").WithMany("Companies") @@ -1053,6 +1190,43 @@ namespace Cicm.Database.Migrations .HasForeignKey("MachineId").HasConstraintName("fk_storage_by_machine_machine") .OnDelete(DeleteBehavior.Cascade); }); + + 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); + }); #pragma warning restore 612, 618 } } diff --git a/Cicm.Database/Models/cicmContext.cs b/Cicm.Database/Models/cicmContext.cs index 3840680d..47b976f9 100644 --- a/Cicm.Database/Models/cicmContext.cs +++ b/Cicm.Database/Models/cicmContext.cs @@ -1,4 +1,4 @@ -/****************************************************************************** +/****************************************************************************** // Canary Islands Computer Museum Website // ---------------------------------------------------------------------------- // @@ -28,11 +28,12 @@ // Copyright © 2003-2018 Natalia Portillo *******************************************************************************/ +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; namespace Cicm.Database.Models { - public class cicmContext : DbContext + public class cicmContext : IdentityDbContext { public cicmContext() { } @@ -79,6 +80,8 @@ namespace Cicm.Database.Models protected override void OnModelCreating(ModelBuilder modelBuilder) { + base.OnModelCreating(modelBuilder); + modelBuilder.Entity(entity => { entity.ToTable("admins"); diff --git a/cicm_web/Areas/Identity/IdentityHostingStartup.cs b/cicm_web/Areas/Identity/IdentityHostingStartup.cs new file mode 100644 index 00000000..9120b29a --- /dev/null +++ b/cicm_web/Areas/Identity/IdentityHostingStartup.cs @@ -0,0 +1,51 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : IdentityHostingStartup.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using Cicm.Database.Models; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +[assembly: HostingStartup(typeof(cicm_web.Areas.Identity.IdentityHostingStartup))] +namespace cicm_web.Areas.Identity +{ + public class IdentityHostingStartup : IHostingStartup + { + public void Configure(IWebHostBuilder builder) + { + builder.ConfigureServices((context, services) => { + }); + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/AccessDenied.cshtml b/cicm_web/Areas/Identity/Pages/Account/AccessDenied.cshtml new file mode 100644 index 00000000..236ac9dd --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/AccessDenied.cshtml @@ -0,0 +1,41 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : AccessDenied.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model AccessDeniedModel +@{ + ViewData["Title"] = "Access denied"; +} + +
+

@ViewData["Title"]

+

You do not have access to this resource.

+
diff --git a/cicm_web/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs new file mode 100644 index 00000000..98d71c57 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs @@ -0,0 +1,39 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : AccessDenied.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + public class AccessDeniedModel : PageModel + { + public void OnGet() { } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml b/cicm_web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml new file mode 100644 index 00000000..c529d5f3 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml @@ -0,0 +1,43 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ConfirmEmail.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model ConfirmEmailModel +@{ + ViewData["Title"] = "Confirm email"; +} + +

@ViewData["Title"]

+
+

+ Thank you for confirming your email. +

+
diff --git a/cicm_web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs new file mode 100644 index 00000000..4a2dedff --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs @@ -0,0 +1,64 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ConfirmEmail.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ConfirmEmailModel : PageModel + { + readonly UserManager _userManager; + + public ConfirmEmailModel(UserManager userManager) + { + _userManager = userManager; + } + + public async Task OnGetAsync(string userId, string code) + { + if(userId == null || code == null) return RedirectToPage("/Index"); + + IdentityUser user = await _userManager.FindByIdAsync(userId); + if(user == null) return NotFound($"Unable to load user with ID '{userId}'."); + + IdentityResult result = await _userManager.ConfirmEmailAsync(user, code); + if(!result.Succeeded) + throw new InvalidOperationException($"Error confirming email for user with ID '{userId}':"); + + return Page(); + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/ExternalLogin.cshtml b/cicm_web/Areas/Identity/Pages/Account/ExternalLogin.cshtml new file mode 100644 index 00000000..9bdc4f23 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ExternalLogin.cshtml @@ -0,0 +1,65 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ExternalLogin.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model ExternalLoginModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"]

+

Associate your @Model.LoginProvider account.

+
+ +

+ You've successfully authenticated with @Model.LoginProvider. + Please enter an email address for this site below and click the Register button to finish + logging in. +

+ +
+
+
+
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} + diff --git a/cicm_web/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs new file mode 100644 index 00000000..f9d4ad88 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs @@ -0,0 +1,157 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ExternalLogin.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.ComponentModel.DataAnnotations; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using SignInResult = Microsoft.AspNetCore.Identity.SignInResult; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ExternalLoginModel : PageModel + { + readonly ILogger _logger; + readonly SignInManager _signInManager; + readonly UserManager _userManager; + + public ExternalLoginModel(SignInManager signInManager, UserManager userManager, + ILogger logger) + { + _signInManager = signInManager; + _userManager = userManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public string LoginProvider { get; set; } + + public string ReturnUrl { get; set; } + + [TempData] + public string ErrorMessage { get; set; } + + public IActionResult OnGetAsync() => RedirectToPage("./Login"); + + public IActionResult OnPost(string provider, string returnUrl = null) + { + // Request a redirect to the external login provider. + string redirectUrl = Url.Page("./ExternalLogin", "Callback", new {returnUrl}); + AuthenticationProperties properties = + _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return new ChallengeResult(provider, properties); + } + + public async Task OnGetCallbackAsync(string returnUrl = null, string remoteError = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + if(remoteError != null) + { + ErrorMessage = $"Error from external provider: {remoteError}"; + return RedirectToPage("./Login", new {ReturnUrl = returnUrl}); + } + + ExternalLoginInfo info = await _signInManager.GetExternalLoginInfoAsync(); + if(info == null) + { + ErrorMessage = "Error loading external login information."; + return RedirectToPage("./Login", new {ReturnUrl = returnUrl}); + } + + // Sign in the user with this external login provider if the user already has a login. + SignInResult result = + await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, false, true); + if(result.Succeeded) + { + _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, + info.LoginProvider); + return LocalRedirect(returnUrl); + } + + if(result.IsLockedOut) return RedirectToPage("./Lockout"); + + // If the user does not have an account, then ask the user to create an account. + ReturnUrl = returnUrl; + LoginProvider = info.LoginProvider; + if(info.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) + Input = new InputModel {Email = info.Principal.FindFirstValue(ClaimTypes.Email)}; + return Page(); + } + + public async Task OnPostConfirmationAsync(string returnUrl = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + // Get the information about the user from the external login provider + ExternalLoginInfo info = await _signInManager.GetExternalLoginInfoAsync(); + if(info == null) + { + ErrorMessage = "Error loading external login information during confirmation."; + return RedirectToPage("./Login", new {ReturnUrl = returnUrl}); + } + + if(ModelState.IsValid) + { + IdentityUser user = new IdentityUser {UserName = Input.Email, Email = Input.Email}; + IdentityResult result = await _userManager.CreateAsync(user); + if(result.Succeeded) + { + result = await _userManager.AddLoginAsync(user, info); + if(result.Succeeded) + { + await _signInManager.SignInAsync(user, false); + _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider); + return LocalRedirect(returnUrl); + } + } + + foreach(IdentityError error in result.Errors) ModelState.AddModelError(string.Empty, error.Description); + } + + LoginProvider = info.LoginProvider; + ReturnUrl = returnUrl; + return Page(); + } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/ForgotPassword.cshtml b/cicm_web/Areas/Identity/Pages/Account/ForgotPassword.cshtml new file mode 100644 index 00000000..f67cb9ec --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ForgotPassword.cshtml @@ -0,0 +1,57 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ForgotPassword.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model ForgotPasswordModel +@{ + ViewData["Title"] = "Forgot your password?"; +} + +

@ViewData["Title"]

+

Enter your email.

+
+
+
+
+
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/cicm_web/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs new file mode 100644 index 00000000..e77eae75 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs @@ -0,0 +1,86 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ForgotPassword.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.ComponentModel.DataAnnotations; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ForgotPasswordModel : PageModel + { + readonly IEmailSender _emailSender; + readonly UserManager _userManager; + + public ForgotPasswordModel(UserManager userManager, IEmailSender emailSender) + { + _userManager = userManager; + _emailSender = emailSender; + } + + [BindProperty] + public InputModel Input { get; set; } + + public async Task OnPostAsync() + { + if(ModelState.IsValid) + { + IdentityUser user = await _userManager.FindByEmailAsync(Input.Email); + if(user == null || !await _userManager.IsEmailConfirmedAsync(user)) + return RedirectToPage("./ForgotPasswordConfirmation"); + + // For more information on how to enable account confirmation and password reset please + // visit https://go.microsoft.com/fwlink/?LinkID=532713 + string code = await _userManager.GeneratePasswordResetTokenAsync(user); + string callbackUrl = Url.Page("/Account/ResetPassword", null, new {code}, Request.Scheme); + + await _emailSender.SendEmailAsync(Input.Email, "Reset Password", + $"Please reset your password by clicking here."); + + return RedirectToPage("./ForgotPasswordConfirmation"); + } + + return Page(); + } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml b/cicm_web/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml new file mode 100644 index 00000000..3c750220 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml @@ -0,0 +1,42 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ForgotPasswordConfirmation.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model ForgotPasswordConfirmation +@{ + ViewData["Title"] = "Forgot password confirmation"; +} + +

@ViewData["Title"]

+

+ Please check your email to reset your password. +

+ diff --git a/cicm_web/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs new file mode 100644 index 00000000..297db5e0 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs @@ -0,0 +1,41 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ForgotPasswordConfirmation.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ForgotPasswordConfirmation : PageModel + { + public void OnGet() { } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Lockout.cshtml b/cicm_web/Areas/Identity/Pages/Account/Lockout.cshtml new file mode 100644 index 00000000..cddca71f --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Lockout.cshtml @@ -0,0 +1,41 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Lockout.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model LockoutModel +@{ + ViewData["Title"] = "Locked out"; +} + +
+

@ViewData["Title"]

+

This account has been locked out, please try again later.

+
diff --git a/cicm_web/Areas/Identity/Pages/Account/Lockout.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Lockout.cshtml.cs new file mode 100644 index 00000000..01710541 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Lockout.cshtml.cs @@ -0,0 +1,41 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Lockout.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class LockoutModel : PageModel + { + public void OnGet() { } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Login.cshtml b/cicm_web/Areas/Identity/Pages/Account/Login.cshtml new file mode 100644 index 00000000..c36425d6 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Login.cshtml @@ -0,0 +1,113 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Login.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model LoginModel + +@{ + ViewData["Title"] = "Log in"; +} + +

@ViewData["Title"]

+
+
+
+
+

Use a local account to log in.

+
+
+
+ + + +
+
+ + + +
+
+
+ +
+
+
+ +
+ +
+
+
+
+
+

Use another service to log in.

+
+ @{ + if ((Model.ExternalLogins?.Count ?? 0) == 0) + { +
+

+ There are no external authentication services configured. See this article + for details on setting up this ASP.NET application to support logging in via external services. +

+
+ } + else + { +
+
+

+ @foreach (var provider in Model.ExternalLogins) + { + + } +

+
+
+ } + } +
+
+
+ +@section Scripts { + +} diff --git a/cicm_web/Areas/Identity/Pages/Account/Login.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Login.cshtml.cs new file mode 100644 index 00000000..d4d51e8d --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Login.cshtml.cs @@ -0,0 +1,128 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Login.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using SignInResult = Microsoft.AspNetCore.Identity.SignInResult; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class LoginModel : PageModel + { + readonly ILogger _logger; + readonly SignInManager _signInManager; + + public LoginModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public IList ExternalLogins { get; set; } + + public string ReturnUrl { get; set; } + + [TempData] + public string ErrorMessage { get; set; } + + public async Task OnGetAsync(string returnUrl = null) + { + if(!string.IsNullOrEmpty(ErrorMessage)) ModelState.AddModelError(string.Empty, ErrorMessage); + + returnUrl = returnUrl ?? Url.Content("~/"); + + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); + + ReturnUrl = returnUrl; + } + + public async Task OnPostAsync(string returnUrl = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + + if(ModelState.IsValid) + { + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, set lockoutOnFailure: true + SignInResult result = + await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, true); + if(result.Succeeded) + { + _logger.LogInformation("User logged in."); + return LocalRedirect(returnUrl); + } + + if(result.RequiresTwoFactor) + return RedirectToPage("./LoginWith2fa", new {ReturnUrl = returnUrl, Input.RememberMe}); + + if(result.IsLockedOut) + { + _logger.LogWarning("User account locked out."); + return RedirectToPage("./Lockout"); + } + + ModelState.AddModelError(string.Empty, "Invalid login attempt."); + return Page(); + } + + // If we got this far, something failed, redisplay form + return Page(); + } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/LoginWith2fa.cshtml b/cicm_web/Areas/Identity/Pages/Account/LoginWith2fa.cshtml new file mode 100644 index 00000000..3354847d --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/LoginWith2fa.cshtml @@ -0,0 +1,72 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : LoginWith2fa.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model LoginWith2faModel +@{ + ViewData["Title"] = "Two-factor authentication"; +} + +

@ViewData["Title"]

+
+

Your login is protected with an authenticator app. Enter your authenticator code below.

+
+
+
+ +
+
+ + + +
+
+
+ +
+
+
+ +
+
+
+
+

+ Don't have access to your authenticator device? You can + log in with a recovery code. +

+ +@section Scripts { + +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs new file mode 100644 index 00000000..70826d31 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs @@ -0,0 +1,120 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : LoginWith2fa.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using SignInResult = Microsoft.AspNetCore.Identity.SignInResult; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class LoginWith2faModel : PageModel + { + readonly ILogger _logger; + readonly SignInManager _signInManager; + + public LoginWith2faModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public bool RememberMe { get; set; } + + public string ReturnUrl { get; set; } + + public async Task OnGetAsync(bool rememberMe, string returnUrl = null) + { + // Ensure the user has gone through the username & password screen first + IdentityUser user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + + if(user == null) throw new InvalidOperationException($"Unable to load two-factor authentication user."); + + ReturnUrl = returnUrl; + RememberMe = rememberMe; + + return Page(); + } + + public async Task OnPostAsync(bool rememberMe, string returnUrl = null) + { + if(!ModelState.IsValid) return Page(); + + returnUrl = returnUrl ?? Url.Content("~/"); + + IdentityUser user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if(user == null) throw new InvalidOperationException($"Unable to load two-factor authentication user."); + + string authenticatorCode = Input.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty); + + SignInResult result = + await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, + Input.RememberMachine); + + if(result.Succeeded) + { + _logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", user.Id); + return LocalRedirect(returnUrl); + } + + if(result.IsLockedOut) + { + _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id); + return RedirectToPage("./Lockout"); + } + + _logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", user.Id); + ModelState.AddModelError(string.Empty, "Invalid authenticator code."); + return Page(); + } + + public class InputModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", + MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Authenticator code")] + public string TwoFactorCode { get; set; } + + [Display(Name = "Remember this machine")] + public bool RememberMachine { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml b/cicm_web/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml new file mode 100644 index 00000000..2fddefcc --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml @@ -0,0 +1,60 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : LoginWithRecoveryCode.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model LoginWithRecoveryCodeModel +@{ + ViewData["Title"] = "Recovery code verification"; +} + +

@ViewData["Title"]

+
+

+ You have requested to log in with a recovery code. This login will not be remembered until you provide + an authenticator app code at log in or disable 2FA and log in again. +

+
+
+
+
+
+ + + +
+ +
+
+
+ + @section Scripts { + +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs new file mode 100644 index 00000000..d5e510b4 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs @@ -0,0 +1,109 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : LoginWithRecoveryCode.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using SignInResult = Microsoft.AspNetCore.Identity.SignInResult; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class LoginWithRecoveryCodeModel : PageModel + { + readonly ILogger _logger; + readonly SignInManager _signInManager; + + public LoginWithRecoveryCodeModel(SignInManager signInManager, + ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public string ReturnUrl { get; set; } + + public async Task OnGetAsync(string returnUrl = null) + { + // Ensure the user has gone through the username & password screen first + IdentityUser user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if(user == null) throw new InvalidOperationException($"Unable to load two-factor authentication user."); + + ReturnUrl = returnUrl; + + return Page(); + } + + public async Task OnPostAsync(string returnUrl = null) + { + if(!ModelState.IsValid) return Page(); + + IdentityUser user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); + if(user == null) throw new InvalidOperationException($"Unable to load two-factor authentication user."); + + string recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty); + + SignInResult result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode); + + if(result.Succeeded) + { + _logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", user.Id); + return LocalRedirect(returnUrl ?? Url.Content("~/")); + } + + if(result.IsLockedOut) + { + _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id); + return RedirectToPage("./Lockout"); + } + + _logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", user.Id); + ModelState.AddModelError(string.Empty, "Invalid recovery code entered."); + return Page(); + } + + public class InputModel + { + [BindProperty] + [Required] + [DataType(DataType.Text)] + [Display(Name = "Recovery Code")] + public string RecoveryCode { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Logout.cshtml b/cicm_web/Areas/Identity/Pages/Account/Logout.cshtml new file mode 100644 index 00000000..0d2dfd99 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Logout.cshtml @@ -0,0 +1,41 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Logout.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model LogoutModel +@{ + ViewData["Title"] = "Log out"; +} + +
+

@ViewData["Title"]

+

You have successfully logged out of the application.

+
\ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Logout.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Logout.cshtml.cs new file mode 100644 index 00000000..8890a736 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Logout.cshtml.cs @@ -0,0 +1,63 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Logout.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class LogoutModel : PageModel + { + readonly ILogger _logger; + readonly SignInManager _signInManager; + + public LogoutModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + public void OnGet() { } + + public async Task OnPost(string returnUrl = null) + { + await _signInManager.SignOutAsync(); + _logger.LogInformation("User logged out."); + if(returnUrl != null) return LocalRedirect(returnUrl); + + return Page(); + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml new file mode 100644 index 00000000..ff3d6295 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml @@ -0,0 +1,66 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ChangePassword.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model ChangePasswordModel +@{ + ViewData["Title"] = "Change password"; +} + +

@ViewData["Title"]

+@Html.Partial("_StatusMessage", Model.StatusMessage) +
+
+
+
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs new file mode 100644 index 00000000..e6825386 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs @@ -0,0 +1,114 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ChangePassword.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class ChangePasswordModel : PageModel + { + readonly ILogger _logger; + readonly SignInManager _signInManager; + readonly UserManager _userManager; + + public ChangePasswordModel(UserManager userManager, SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGetAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + bool hasPassword = await _userManager.HasPasswordAsync(user); + if(!hasPassword) return RedirectToPage("./SetPassword"); + + return Page(); + } + + public async Task OnPostAsync() + { + if(!ModelState.IsValid) return Page(); + + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + IdentityResult changePasswordResult = + await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); + if(!changePasswordResult.Succeeded) + { + foreach(IdentityError error in changePasswordResult.Errors) + ModelState.AddModelError(string.Empty, error.Description); + return Page(); + } + + await _signInManager.RefreshSignInAsync(user); + _logger.LogInformation("User changed their password successfully."); + StatusMessage = "Your password has been changed."; + + return RedirectToPage(); + } + + public class InputModel + { + [Required] + [DataType(DataType.Password)] + [Display(Name = "Current password")] + public string OldPassword { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", + MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml new file mode 100644 index 00000000..3df6b23a --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml @@ -0,0 +1,65 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : DeletePersonalData.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model DeletePersonalDataModel +@{ + ViewData["Title"] = "Delete Personal Data"; + ViewData["ActivePage"] = ManageNavPages.DeletePersonalData; +} + +

@ViewData["Title"]

+ + + +
+
+
+ @if (Model.RequirePassword) + { +
+ + + +
+ } + +
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs new file mode 100644 index 00000000..b953ef9e --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs @@ -0,0 +1,102 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : DeletePersonalData.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class DeletePersonalDataModel : PageModel + { + readonly ILogger _logger; + readonly SignInManager _signInManager; + readonly UserManager _userManager; + + public DeletePersonalDataModel(UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public bool RequirePassword { get; set; } + + public async Task OnGet() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + RequirePassword = await _userManager.HasPasswordAsync(user); + return Page(); + } + + public async Task OnPostAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + RequirePassword = await _userManager.HasPasswordAsync(user); + if(RequirePassword) + if(!await _userManager.CheckPasswordAsync(user, Input.Password)) + { + ModelState.AddModelError(string.Empty, "Password not correct."); + return Page(); + } + + IdentityResult result = await _userManager.DeleteAsync(user); + string userId = await _userManager.GetUserIdAsync(user); + if(!result.Succeeded) + throw new InvalidOperationException($"Unexpected error occurred deleteing user with ID '{userId}'."); + + await _signInManager.SignOutAsync(); + + _logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId); + + return Redirect("~/"); + } + + public class InputModel + { + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml new file mode 100644 index 00000000..f2f34c5f --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml @@ -0,0 +1,57 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Disable2fa.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model Disable2faModel +@{ + ViewData["Title"] = "Disable two-factor authentication (2FA)"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +

@ViewData["Title"]

+ + + +
+
+ +
+
diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs new file mode 100644 index 00000000..05507175 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs @@ -0,0 +1,81 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Disable2fa.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class Disable2faModel : PageModel + { + readonly ILogger _logger; + readonly UserManager _userManager; + + public Disable2faModel(UserManager userManager, ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGet() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + if(!await _userManager.GetTwoFactorEnabledAsync(user)) + throw new + InvalidOperationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled."); + + return Page(); + } + + public async Task OnPostAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + IdentityResult disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); + if(!disable2faResult.Succeeded) + throw new + InvalidOperationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'."); + + _logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User)); + StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app"; + return RedirectToPage("./TwoFactorAuthentication"); + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml new file mode 100644 index 00000000..2179be24 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml @@ -0,0 +1,43 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : DownloadPersonalData.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model DownloadPersonalDataModel +@{ + ViewData["Title"] = "Download Your Data"; + ViewData["ActivePage"] = ManageNavPages.DownloadPersonalData; +} + +

@ViewData["Title"]

+ +@section Scripts { + +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs new file mode 100644 index 00000000..4281ccf8 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs @@ -0,0 +1,80 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : DownloadPersonalData.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class DownloadPersonalDataModel : PageModel + { + readonly ILogger _logger; + readonly UserManager _userManager; + + public DownloadPersonalDataModel(UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public async Task OnPostAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + _logger.LogInformation("User with ID '{UserId}' asked for their personal data.", + _userManager.GetUserId(User)); + + // Only include personal data for download + Dictionary personalData = new Dictionary(); + IEnumerable personalDataProps = typeof(IdentityUser) + .GetProperties() + .Where(prop => + Attribute.IsDefined(prop, + typeof(PersonalDataAttribute))); + foreach(PropertyInfo p in personalDataProps) + personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); + + Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json"); + return new FileContentResult(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(personalData)), + "text/json"); + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml new file mode 100644 index 00000000..dfb4629d --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml @@ -0,0 +1,85 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : EnableAuthenticator.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model EnableAuthenticatorModel +@{ + ViewData["Title"] = "Configure authenticator app"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +

@ViewData["Title"]

+
+

To use an authenticator app go through the following steps:

+
    +
  1. +

    + Download a two-factor authenticator app like Microsoft Authenticator for + Windows Phone, + Android and + iOS or + Google Authenticator for + Android and + iOS. +

    +
  2. +
  3. +

    Scan the QR Code or enter this key @Model.SharedKey into your two factor authenticator app. Spaces and casing do not matter.

    +
    To enable QR code generation please read our documentation.
    +
    +
    +
  4. +
  5. +

    + Once you have scanned the QR code or input the key above, your two factor authentication app will provide you + with a unique code. Enter the code in the confirmation box below. +

    +
    +
    +
    +
    + + + +
    + +
    +
    +
    +
    +
  6. +
+
+ +@section Scripts { + +} diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs new file mode 100644 index 00000000..2206f3b3 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs @@ -0,0 +1,172 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : EnableAuthenticator.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class EnableAuthenticatorModel : PageModel + { + const string AuthenticatorUriFormat = + "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; + readonly ILogger _logger; + readonly UrlEncoder _urlEncoder; + readonly UserManager _userManager; + + public EnableAuthenticatorModel(UserManager userManager, ILogger logger, + UrlEncoder urlEncoder) + { + _userManager = userManager; + _logger = logger; + _urlEncoder = urlEncoder; + } + + public string SharedKey { get; set; } + + public string AuthenticatorUri { get; set; } + + [TempData] + public string[] RecoveryCodes { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + [BindProperty] + public InputModel Input { get; set; } + + public async Task OnGetAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + await LoadSharedKeyAndQrCodeUriAsync(user); + + return Page(); + } + + public async Task OnPostAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + if(!ModelState.IsValid) + { + await LoadSharedKeyAndQrCodeUriAsync(user); + return Page(); + } + + // Strip spaces and hypens + string verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty); + + bool is2faTokenValid = + await _userManager.VerifyTwoFactorTokenAsync(user, + _userManager.Options.Tokens.AuthenticatorTokenProvider, + verificationCode); + + if(!is2faTokenValid) + { + ModelState.AddModelError("Input.Code", "Verification code is invalid."); + await LoadSharedKeyAndQrCodeUriAsync(user); + return Page(); + } + + await _userManager.SetTwoFactorEnabledAsync(user, true); + string userId = await _userManager.GetUserIdAsync(user); + _logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId); + + StatusMessage = "Your authenticator app has been verified."; + + if(await _userManager.CountRecoveryCodesAsync(user) == 0) + { + IEnumerable recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + RecoveryCodes = recoveryCodes.ToArray(); + return RedirectToPage("./ShowRecoveryCodes"); + } + + return RedirectToPage("./TwoFactorAuthentication"); + } + + async Task LoadSharedKeyAndQrCodeUriAsync(IdentityUser user) + { + // Load the authenticator key & QR code URI to display on the form + string unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + if(string.IsNullOrEmpty(unformattedKey)) + { + await _userManager.ResetAuthenticatorKeyAsync(user); + unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); + } + + SharedKey = FormatKey(unformattedKey); + + string email = await _userManager.GetEmailAsync(user); + AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey); + } + + string FormatKey(string unformattedKey) + { + StringBuilder result = new StringBuilder(); + int currentPosition = 0; + while(currentPosition + 4 < unformattedKey.Length) + { + result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" "); + currentPosition += 4; + } + + if(currentPosition < unformattedKey.Length) result.Append(unformattedKey.Substring(currentPosition)); + + return result.ToString().ToLowerInvariant(); + } + + string GenerateQrCodeUri(string email, string unformattedKey) + { + return string.Format(AuthenticatorUriFormat, _urlEncoder.Encode("cicm_web"), _urlEncoder.Encode(email), + unformattedKey); + } + + public class InputModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", + MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Verification Code")] + public string Code { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml new file mode 100644 index 00000000..c7570218 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml @@ -0,0 +1,83 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ExternalLogins.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model ExternalLoginsModel +@{ + ViewData["Title"] = "Manage your external logins"; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +@if (Model.CurrentLogins?.Count > 0) +{ +

Registered Logins

+ + + @foreach (var login in Model.CurrentLogins) + { + + + + + } + +
@login.LoginProvider + @if (Model.ShowRemoveButton) + { +
+
+ + + +
+
+ } + else + { + @:   + } +
+} +@if (Model.OtherLogins?.Count > 0) +{ +

Add another service to log in.

+
+ +} diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs new file mode 100644 index 00000000..9108b26c --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs @@ -0,0 +1,128 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ExternalLogins.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class ExternalLoginsModel : PageModel + { + readonly SignInManager _signInManager; + readonly UserManager _userManager; + + public ExternalLoginsModel(UserManager userManager, SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + + public IList CurrentLogins { get; set; } + + public IList OtherLogins { get; set; } + + public bool ShowRemoveButton { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGetAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + CurrentLogins = await _userManager.GetLoginsAsync(user); + OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()) + .Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider)).ToList(); + ShowRemoveButton = user.PasswordHash != null || CurrentLogins.Count > 1; + return Page(); + } + + public async Task OnPostRemoveLoginAsync(string loginProvider, string providerKey) + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + IdentityResult result = await _userManager.RemoveLoginAsync(user, loginProvider, providerKey); + if(!result.Succeeded) + { + string userId = await _userManager.GetUserIdAsync(user); + throw new + InvalidOperationException($"Unexpected error occurred removing external login for user with ID '{userId}'."); + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "The external login was removed."; + return RedirectToPage(); + } + + public async Task OnPostLinkLoginAsync(string provider) + { + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + // Request a redirect to the external login provider to link a login for the current user + string redirectUrl = Url.Page("./ExternalLogins", "LinkLoginCallback"); + AuthenticationProperties properties = + _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, + _userManager.GetUserId(User)); + return new ChallengeResult(provider, properties); + } + + public async Task OnGetLinkLoginCallbackAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + ExternalLoginInfo info = + await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user)); + if(info == null) + throw new + InvalidOperationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'."); + + IdentityResult result = await _userManager.AddLoginAsync(user, info); + if(!result.Succeeded) + throw new + InvalidOperationException($"Unexpected error occurred adding external login for user with ID '{user.Id}'."); + + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + StatusMessage = "The external login was added."; + return RedirectToPage(); + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml new file mode 100644 index 00000000..f7b1589e --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml @@ -0,0 +1,58 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : GenerateRecoveryCodes.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model GenerateRecoveryCodesModel +@{ + ViewData["Title"] = "Generate two-factor authentication (2FA) recovery codes"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +

@ViewData["Title"]

+ +
+
+ +
+
\ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs new file mode 100644 index 00000000..00ddefaf --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs @@ -0,0 +1,95 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : GenerateRecoveryCodes.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class GenerateRecoveryCodesModel : PageModel + { + readonly ILogger _logger; + readonly UserManager _userManager; + + public GenerateRecoveryCodesModel(UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + [TempData] + public string[] RecoveryCodes { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGetAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + bool isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); + if(!isTwoFactorEnabled) + { + string userId = await _userManager.GetUserIdAsync(user); + throw new + InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' because they do not have 2FA enabled."); + } + + return Page(); + } + + public async Task OnPostAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + bool isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); + string userId = await _userManager.GetUserIdAsync(user); + if(!isTwoFactorEnabled) + throw new + InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' as they do not have 2FA enabled."); + + IEnumerable recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + RecoveryCodes = recoveryCodes.ToArray(); + + _logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId); + StatusMessage = "You have generated new recovery codes."; + return RedirectToPage("./ShowRecoveryCodes"); + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/Index.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/Index.cshtml new file mode 100644 index 00000000..52644549 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/Index.cshtml @@ -0,0 +1,76 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Index.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model IndexModel +@{ + ViewData["Title"] = "Profile"; +} + +

@ViewData["Title"]

+@Html.Partial("_StatusMessage", Model.StatusMessage) +
+
+
+
+
+ + +
+
+ + @if (Model.IsEmailConfirmed) + { +
+ + +
+ } + else + { + + + } + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs new file mode 100644 index 00000000..916198bf --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs @@ -0,0 +1,149 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Index.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using System.ComponentModel.DataAnnotations; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class IndexModel : PageModel + { + readonly IEmailSender _emailSender; + readonly SignInManager _signInManager; + readonly UserManager _userManager; + + public IndexModel(UserManager userManager, SignInManager signInManager, + IEmailSender emailSender) + { + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + } + + public string Username { get; set; } + + public bool IsEmailConfirmed { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + [BindProperty] + public InputModel Input { get; set; } + + public async Task OnGetAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + string userName = await _userManager.GetUserNameAsync(user); + string email = await _userManager.GetEmailAsync(user); + string phoneNumber = await _userManager.GetPhoneNumberAsync(user); + + Username = userName; + + Input = new InputModel {Email = email, PhoneNumber = phoneNumber}; + + IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); + + return Page(); + } + + public async Task OnPostAsync() + { + if(!ModelState.IsValid) return Page(); + + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + string email = await _userManager.GetEmailAsync(user); + if(Input.Email != email) + { + IdentityResult setEmailResult = await _userManager.SetEmailAsync(user, Input.Email); + if(!setEmailResult.Succeeded) + { + string userId = await _userManager.GetUserIdAsync(user); + throw new + InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'."); + } + } + + string phoneNumber = await _userManager.GetPhoneNumberAsync(user); + if(Input.PhoneNumber != phoneNumber) + { + IdentityResult setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); + if(!setPhoneResult.Succeeded) + { + string userId = await _userManager.GetUserIdAsync(user); + throw new + InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{userId}'."); + } + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your profile has been updated"; + return RedirectToPage(); + } + + public async Task OnPostSendVerificationEmailAsync() + { + if(!ModelState.IsValid) return Page(); + + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + string userId = await _userManager.GetUserIdAsync(user); + string email = await _userManager.GetEmailAsync(user); + string code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + string callbackUrl = Url.Page("/Account/ConfirmEmail", null, new {userId, code}, Request.Scheme); + await _emailSender.SendEmailAsync(email, "Confirm your email", + $"Please confirm your account by clicking here."); + + StatusMessage = "Verification email sent. Please check your email."; + return RedirectToPage(); + } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs new file mode 100644 index 00000000..0622b446 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs @@ -0,0 +1,79 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ManageNavPages.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System; +using System.IO; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public static class ManageNavPages + { + public static string Index => "Index"; + + public static string ChangePassword => "ChangePassword"; + + public static string DownloadPersonalData => "DownloadPersonalData"; + + public static string DeletePersonalData => "DeletePersonalData"; + + public static string ExternalLogins => "ExternalLogins"; + + public static string PersonalData => "PersonalData"; + + public static string TwoFactorAuthentication => "TwoFactorAuthentication"; + + public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); + + public static string ChangePasswordNavClass(ViewContext viewContext) => + PageNavClass(viewContext, ChangePassword); + + public static string DownloadPersonalDataNavClass(ViewContext viewContext) => + PageNavClass(viewContext, DownloadPersonalData); + + public static string DeletePersonalDataNavClass(ViewContext viewContext) => + PageNavClass(viewContext, DeletePersonalData); + + public static string ExternalLoginsNavClass(ViewContext viewContext) => + PageNavClass(viewContext, ExternalLogins); + + public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData); + + public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => + PageNavClass(viewContext, TwoFactorAuthentication); + + public static string PageNavClass(ViewContext viewContext, string page) + { + string activePage = viewContext.ViewData["ActivePage"] as string ?? + Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName); + return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml new file mode 100644 index 00000000..fd66c6b8 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml @@ -0,0 +1,58 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : PersonalData.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model PersonalDataModel +@{ + ViewData["Title"] = "Personal Data"; + ViewData["ActivePage"] = ManageNavPages.PersonalData; +} + +

@ViewData["Title"]

+ +
+
+

Your account contains personal data that you have given us. This page allows you to download or delete that data.

+

+ Deleting this data will permanently remove your account, and this cannot be recovered. +

+
+ +
+

+ Delete +

+
+
+ +@section Scripts { + +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs new file mode 100644 index 00000000..830c0cd4 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs @@ -0,0 +1,58 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : PersonalData.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class PersonalDataModel : PageModel + { + readonly ILogger _logger; + readonly UserManager _userManager; + + public PersonalDataModel(UserManager userManager, ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + public async Task OnGet() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + return Page(); + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml new file mode 100644 index 00000000..986865f9 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml @@ -0,0 +1,55 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ResetAuthenticator.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model ResetAuthenticatorModel +@{ + ViewData["Title"] = "Reset authenticator key"; + ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +

@ViewData["Title"]

+ +
+
+ +
+
\ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs new file mode 100644 index 00000000..2dcc07c0 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs @@ -0,0 +1,81 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ResetAuthenticator.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class ResetAuthenticatorModel : PageModel + { + readonly SignInManager _signInManager; + ILogger _logger; + UserManager _userManager; + + public ResetAuthenticatorModel(UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGet() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + return Page(); + } + + public async Task OnPostAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + await _userManager.SetTwoFactorEnabledAsync(user, false); + await _userManager.ResetAuthenticatorKeyAsync(user); + _logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", user.Id); + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = + "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key."; + + return RedirectToPage("./EnableAuthenticator"); + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml new file mode 100644 index 00000000..2f9a8ce0 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml @@ -0,0 +1,66 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : SetPassword.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model SetPasswordModel +@{ + ViewData["Title"] = "Set password"; + ViewData["ActivePage"] = ManageNavPages.ChangePassword; +} + +

Set your password

+@Html.Partial("_StatusMessage", Model.StatusMessage) +

+ You do not have a local username/password for this site. Add a local + account so you can log in without an external login. +

+
+
+
+
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs new file mode 100644 index 00000000..c0edbd76 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs @@ -0,0 +1,104 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : SetPassword.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class SetPasswordModel : PageModel + { + readonly SignInManager _signInManager; + readonly UserManager _userManager; + + public SetPasswordModel(UserManager userManager, SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + + [BindProperty] + public InputModel Input { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGetAsync() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + bool hasPassword = await _userManager.HasPasswordAsync(user); + + if(hasPassword) return RedirectToPage("./ChangePassword"); + + return Page(); + } + + public async Task OnPostAsync() + { + if(!ModelState.IsValid) return Page(); + + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + IdentityResult addPasswordResult = await _userManager.AddPasswordAsync(user, Input.NewPassword); + if(!addPasswordResult.Succeeded) + { + foreach(IdentityError error in addPasswordResult.Errors) + ModelState.AddModelError(string.Empty, error.Description); + return Page(); + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your password has been set."; + + return RedirectToPage(); + } + + public class InputModel + { + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", + MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml new file mode 100644 index 00000000..8cd78216 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml @@ -0,0 +1,87 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : TwoFactorAuthentication.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model TwoFactorAuthenticationModel +@{ + ViewData["Title"] = "Two-factor authentication (2FA)"; +} + +@Html.Partial("_StatusMessage", Model.StatusMessage) +

@ViewData["Title"]

+@if (Model.Is2faEnabled) +{ + if (Model.RecoveryCodesLeft == 0) + { +
+ You have no recovery codes left. +

You must generate a new set of recovery codes before you can log in with a recovery code.

+
+ } + else if (Model.RecoveryCodesLeft == 1) + { +
+ You have 1 recovery code left. +

You can generate a new set of recovery codes.

+
+ } + else if (Model.RecoveryCodesLeft <= 3) + { +
+ You have @Model.RecoveryCodesLeft recovery codes left. +

You should generate a new set of recovery codes.

+
+ } + + if (Model.IsMachineRemembered) + { +
+ +
+ } + Disable 2FA + Reset recovery codes +} + +
Authenticator app
+@if (!Model.HasAuthenticator) +{ + Add authenticator app +} +else +{ + Setup authenticator app + Reset authenticator app +} + +@section Scripts { + +} diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs new file mode 100644 index 00000000..7709b60c --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs @@ -0,0 +1,93 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : TwoFactorAuthentication.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace cicm_web.Areas.Identity.Pages.Account.Manage +{ + public class TwoFactorAuthenticationModel : PageModel + { + const string AuthenicatorUriFormat = + "otpauth://totp/{0}:{1}?secret={2}&issuer={0}"; + readonly ILogger _logger; + readonly SignInManager _signInManager; + + readonly UserManager _userManager; + + public TwoFactorAuthenticationModel(UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + public bool HasAuthenticator { get; set; } + + public int RecoveryCodesLeft { get; set; } + + [BindProperty] + public bool Is2faEnabled { get; set; } + + public bool IsMachineRemembered { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGet() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null; + Is2faEnabled = await _userManager.GetTwoFactorEnabledAsync(user); + IsMachineRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user); + RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user); + + return Page(); + } + + public async Task OnPost() + { + IdentityUser user = await _userManager.GetUserAsync(User); + if(user == null) return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + + await _signInManager.ForgetTwoFactorClientAsync(); + StatusMessage = + "The current browser has been forgotten. When you login again from this browser you will be prompted for your 2fa code."; + return RedirectToPage(); + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/_Layout.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/_Layout.cshtml new file mode 100644 index 00000000..9a21270e --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/_Layout.cshtml @@ -0,0 +1,53 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : _Layout.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@{ + Layout = "/Areas/Identity/Pages/_Layout.cshtml"; +} + +

Manage your account

+ +
+

Change your account settings

+
+
+
+ +
+
+ @RenderBody() +
+
+
+ +@section Scripts { + @RenderSection("Scripts", required: false) +} diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml new file mode 100644 index 00000000..b6f6dabc --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml @@ -0,0 +1,45 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : _ManageNav.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@inject SignInManager SignInManager +@{ + var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any(); +} + diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml new file mode 100644 index 00000000..44e7836d --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml @@ -0,0 +1,41 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : _StatusMessage.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@model string + +@if (!String.IsNullOrEmpty(Model)) +{ + var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; + +} diff --git a/cicm_web/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml b/cicm_web/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml new file mode 100644 index 00000000..9cc7404d --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml @@ -0,0 +1,32 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : _ViewImports.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@using cicm_web.Areas.Identity.Pages.Account.Manage diff --git a/cicm_web/Areas/Identity/Pages/Account/Register.cshtml b/cicm_web/Areas/Identity/Pages/Account/Register.cshtml new file mode 100644 index 00000000..53030240 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Register.cshtml @@ -0,0 +1,68 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Register.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model RegisterModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"]

+ +
+
+
+

Create a new account.

+
+
+
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/cicm_web/Areas/Identity/Pages/Account/Register.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/Register.cshtml.cs new file mode 100644 index 00000000..7236fecc --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -0,0 +1,119 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Register.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.ComponentModel.DataAnnotations; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class RegisterModel : PageModel + { + readonly IEmailSender _emailSender; + readonly ILogger _logger; + readonly SignInManager _signInManager; + readonly UserManager _userManager; + + public RegisterModel(UserManager userManager, SignInManager signInManager, + ILogger logger, IEmailSender emailSender) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + _emailSender = emailSender; + } + + [BindProperty] + public InputModel Input { get; set; } + + public string ReturnUrl { get; set; } + + public void OnGet(string returnUrl = null) + { + ReturnUrl = returnUrl; + } + + public async Task OnPostAsync(string returnUrl = null) + { + returnUrl = returnUrl ?? Url.Content("~/"); + if(ModelState.IsValid) + { + IdentityUser user = new IdentityUser {UserName = Input.Email, Email = Input.Email}; + IdentityResult result = await _userManager.CreateAsync(user, Input.Password); + if(result.Succeeded) + { + _logger.LogInformation("User created a new account with password."); + + string code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + string callbackUrl = Url.Page("/Account/ConfirmEmail", null, new {userId = user.Id, code}, + Request.Scheme); + + await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", + $"Please confirm your account by clicking here."); + + await _signInManager.SignInAsync(user, false); + return LocalRedirect(returnUrl); + } + + foreach(IdentityError error in result.Errors) ModelState.AddModelError(string.Empty, error.Description); + } + + // If we got this far, something failed, redisplay form + return Page(); + } + + public class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", + MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/ResetPassword.cshtml b/cicm_web/Areas/Identity/Pages/Account/ResetPassword.cshtml new file mode 100644 index 00000000..e3df8c1a --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ResetPassword.cshtml @@ -0,0 +1,68 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ResetPassword.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model ResetPasswordModel +@{ + ViewData["Title"] = "Reset password"; +} + +

@ViewData["Title"]

+

Reset your password.

+
+
+
+
+
+ +
+ + + +
+
+ + + +
+
+ + + +
+ +
+
+
+ +@section Scripts { + +} diff --git a/cicm_web/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs new file mode 100644 index 00000000..377cf51f --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs @@ -0,0 +1,95 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ResetPassword.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ResetPasswordModel : PageModel + { + readonly UserManager _userManager; + + public ResetPasswordModel(UserManager userManager) + { + _userManager = userManager; + } + + [BindProperty] + public InputModel Input { get; set; } + + public IActionResult OnGet(string code = null) + { + if(code == null) return BadRequest("A code must be supplied for password reset."); + + Input = new InputModel {Code = code}; + return Page(); + } + + public async Task OnPostAsync() + { + if(!ModelState.IsValid) return Page(); + + IdentityUser user = await _userManager.FindByEmailAsync(Input.Email); + if(user == null) return RedirectToPage("./ResetPasswordConfirmation"); + + IdentityResult result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password); + if(result.Succeeded) return RedirectToPage("./ResetPasswordConfirmation"); + + foreach(IdentityError error in result.Errors) ModelState.AddModelError(string.Empty, error.Description); + return Page(); + } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", + MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + + public string Code { get; set; } + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml b/cicm_web/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml new file mode 100644 index 00000000..07fe51bd --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml @@ -0,0 +1,41 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ResetPasswordConfirmation.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model ResetPasswordConfirmationModel +@{ + ViewData["Title"] = "Reset password confirmation"; +} + +

@ViewData["Title"]

+

+ Your password has been reset. Please click here to log in. +

diff --git a/cicm_web/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs b/cicm_web/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs new file mode 100644 index 00000000..3c1dd9f1 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs @@ -0,0 +1,41 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : ResetPasswordConfirmation.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace cicm_web.Areas.Identity.Pages.Account +{ + [AllowAnonymous] + public class ResetPasswordConfirmationModel : PageModel + { + public void OnGet() { } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Account/_ViewImports.cshtml b/cicm_web/Areas/Identity/Pages/Account/_ViewImports.cshtml new file mode 100644 index 00000000..b8d98056 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Account/_ViewImports.cshtml @@ -0,0 +1,32 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : _ViewImports.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@using cicm_web.Areas.Identity.Pages.Account \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/Error.cshtml b/cicm_web/Areas/Identity/Pages/Error.cshtml new file mode 100644 index 00000000..b6a424c3 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Error.cshtml @@ -0,0 +1,54 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Error.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@page +@model ErrorModel +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

+ +@if (Model.ShowRequestId) +{ +

+ Request ID: @Model.RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. +

diff --git a/cicm_web/Areas/Identity/Pages/Error.cshtml.cs b/cicm_web/Areas/Identity/Pages/Error.cshtml.cs new file mode 100644 index 00000000..90d01e71 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/Error.cshtml.cs @@ -0,0 +1,51 @@ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : Error.cshtml.cs +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ + +using System.Diagnostics; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace cicm_web.Areas.Identity.Pages +{ + [AllowAnonymous] + public class ErrorModel : PageModel + { + public string RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public void OnGet() + { + RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; + } + } +} \ No newline at end of file diff --git a/cicm_web/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml b/cicm_web/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml new file mode 100644 index 00000000..834f3e3d --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml @@ -0,0 +1,49 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : _ValidationScriptsPartial.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} + + + + + + + + diff --git a/cicm_web/Areas/Identity/Pages/_ViewImports.cshtml b/cicm_web/Areas/Identity/Pages/_ViewImports.cshtml new file mode 100644 index 00000000..b17bf810 --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/_ViewImports.cshtml @@ -0,0 +1,36 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : _ViewImports.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} +@using Microsoft.AspNetCore.Identity +@using cicm_web.Areas.Identity +@using Microsoft.AspNetCore.Identity +@namespace cicm_web.Areas.Identity.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/cicm_web/Areas/Identity/Pages/_ViewStart.cshtml b/cicm_web/Areas/Identity/Pages/_ViewStart.cshtml new file mode 100644 index 00000000..79b7544d --- /dev/null +++ b/cicm_web/Areas/Identity/Pages/_ViewStart.cshtml @@ -0,0 +1,35 @@ +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : _ViewStart.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// ASP.NET Identify management +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} + +@{ + Layout = "/Views/Shared/_Layout.cshtml"; +} diff --git a/cicm_web/Startup.cs b/cicm_web/Startup.cs index 5cf09b1c..f378b388 100644 --- a/cicm_web/Startup.cs +++ b/cicm_web/Startup.cs @@ -31,6 +31,8 @@ using Cicm.Database.Models; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -50,12 +52,19 @@ namespace cicm_web // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddMvc(); - - #warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings. + services.Configure(options => + { + // This lambda determines whether user consent for non-essential cookies is needed for a given request. + options.CheckConsentNeeded = context => true; + options.MinimumSameSitePolicy = SameSiteMode.None; + }); +#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings. services.AddDbContext(options => options .UseLazyLoadingProxies() .UseMySql("server=localhost;port=3306;user=cicm;password=cicmpass;database=cicm")); + services.AddDefaultIdentity() + .AddEntityFrameworkStores(); + services.AddMvc(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -65,6 +74,8 @@ namespace cicm_web else app.UseExceptionHandler("/Home/Error"); app.UseStaticFiles(); + app.UseAuthentication(); + app.UseCookiePolicy(); app.UseMvc(routes => { routes.MapRoute( name: "areas", diff --git a/cicm_web/Views/Shared/_CookieConsentPartial.cshtml b/cicm_web/Views/Shared/_CookieConsentPartial.cshtml new file mode 100644 index 00000000..f19879a1 --- /dev/null +++ b/cicm_web/Views/Shared/_CookieConsentPartial.cshtml @@ -0,0 +1,72 @@ +@using Microsoft.AspNetCore.Http.Features +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : _CookieConsentPartial.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// Shows cookie consent menu +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} + +@{ + var consentFeature = Context.Features.Get(); + var showBanner = !consentFeature?.CanTrack ?? false; + var cookieString = consentFeature?.CreateConsentCookie(); +} + +@if (showBanner) +{ + + +} \ No newline at end of file diff --git a/cicm_web/Views/Shared/_Layout.cshtml b/cicm_web/Views/Shared/_Layout.cshtml index 0239b830..4961f562 100644 --- a/cicm_web/Views/Shared/_Layout.cshtml +++ b/cicm_web/Views/Shared/_Layout.cshtml @@ -1,4 +1,4 @@ -@using Microsoft.Extensions.PlatformAbstractions +@using Microsoft.Extensions.PlatformAbstractions @{ /****************************************************************************** // Canary Islands Computer Museum Website @@ -134,7 +134,8 @@ - + +
@RenderBody() diff --git a/cicm_web/Views/Shared/_LoginPartial.cshtml b/cicm_web/Views/Shared/_LoginPartial.cshtml new file mode 100644 index 00000000..5820db43 --- /dev/null +++ b/cicm_web/Views/Shared/_LoginPartial.cshtml @@ -0,0 +1,68 @@ +@using Microsoft.AspNetCore.Identity +@inject SignInManager SignInManager +@inject UserManager UserManager +@{ +/****************************************************************************** +// Canary Islands Computer Museum Website +// ---------------------------------------------------------------------------- +// +// Filename : _LoginPartial.cshtml +// Author(s) : Natalia Portillo +// +// --[ Description ] ---------------------------------------------------------- +// +// Provides login options to navigation bar. +// +// --[ License ] -------------------------------------------------------------- +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2003-2018 Natalia Portillo +*******************************************************************************/ +} + +@if (SignInManager.IsSignedIn(User)) +{ + +} +else +{ + +} diff --git a/cicm_web/cicm_web.csproj b/cicm_web/cicm_web.csproj index d94ae205..6e4b196a 100644 --- a/cicm_web/cicm_web.csproj +++ b/cicm_web/cicm_web.csproj @@ -2,7 +2,7 @@ netcoreapp2.1 - 3.0.99.330 + 3.0.99.340 Canary Islands Computer Museum Copyright © 2003-2018 Natalia Portillo Canary Islands Computer Museum Website @@ -30,9 +30,6 @@ - - - diff --git a/cicm_web/wwwroot/assets/flags/countries/png/.donotremove b/cicm_web/wwwroot/assets/flags/countries/png/.donotremove new file mode 100644 index 00000000..e69de29b diff --git a/cicm_web/wwwroot/assets/flags/countries/webp/.donotremove b/cicm_web/wwwroot/assets/flags/countries/webp/.donotremove new file mode 100644 index 00000000..e69de29b diff --git a/cicm_web/wwwroot/assets/logos/png/.donotremove b/cicm_web/wwwroot/assets/logos/png/.donotremove new file mode 100644 index 00000000..e69de29b diff --git a/cicm_web/wwwroot/assets/logos/thumbs/.donotremove b/cicm_web/wwwroot/assets/logos/thumbs/.donotremove new file mode 100644 index 00000000..e69de29b diff --git a/cicm_web/wwwroot/assets/logos/webp/.donotremove b/cicm_web/wwwroot/assets/logos/webp/.donotremove new file mode 100644 index 00000000..e69de29b diff --git a/cicm_web/wwwroot/images/banner1.svg b/cicm_web/wwwroot/images/banner1.svg new file mode 100644 index 00000000..557f31f6 --- /dev/null +++ b/cicm_web/wwwroot/images/banner1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cicm_web/wwwroot/images/banner2.svg b/cicm_web/wwwroot/images/banner2.svg new file mode 100644 index 00000000..4c488089 --- /dev/null +++ b/cicm_web/wwwroot/images/banner2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cicm_web/wwwroot/images/banner3.svg b/cicm_web/wwwroot/images/banner3.svg new file mode 100644 index 00000000..ab2171c1 --- /dev/null +++ b/cicm_web/wwwroot/images/banner3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/cicm_web/wwwroot/js/site.js b/cicm_web/wwwroot/js/site.js old mode 100644 new mode 100755 diff --git a/cicm_web/wwwroot/js/site.min.js b/cicm_web/wwwroot/js/site.min.js old mode 100644 new mode 100755 diff --git a/cicm_web/wwwroot/lib/bootstrap/LICENSE b/cicm_web/wwwroot/lib/bootstrap/LICENSE new file mode 100644 index 00000000..b7d9b03a --- /dev/null +++ b/cicm_web/wwwroot/lib/bootstrap/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011-2016 Twitter, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot b/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 00000000..94c8a5d8 Binary files /dev/null and b/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.eot differ diff --git a/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.svg b/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 00000000..3806b9ba --- /dev/null +++ b/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf b/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 00000000..d3cde83c Binary files /dev/null and b/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf differ diff --git a/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff b/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 00000000..6ae0b5d2 Binary files /dev/null and b/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff differ diff --git a/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 b/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 00000000..c5d4fe6e Binary files /dev/null and b/cicm_web/wwwroot/lib/bootstrap/dist/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/cicm_web/wwwroot/lib/bootstrap/dist/js/bootstrap.js b/cicm_web/wwwroot/lib/bootstrap/dist/js/bootstrap.js old mode 100644 new mode 100755 diff --git a/cicm_web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js b/cicm_web/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js old mode 100644 new mode 100755 diff --git a/cicm_web/wwwroot/lib/bootstrap/dist/js/npm.js b/cicm_web/wwwroot/lib/bootstrap/dist/js/npm.js new file mode 100755 index 00000000..2cee2a0a --- /dev/null +++ b/cicm_web/wwwroot/lib/bootstrap/dist/js/npm.js @@ -0,0 +1,13 @@ +// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. +require('../../js/transition.js'); +require('../../js/alert.js'); +require('../../js/button.js'); +require('../../js/carousel.js'); +require('../../js/collapse.js'); +require('../../js/dropdown.js'); +require('../../js/modal.js'); +require('../../js/tooltip.js'); +require('../../js/popover.js'); +require('../../js/scrollspy.js'); +require('../../js/tab.js'); +require('../../js/affix.js'); \ No newline at end of file diff --git a/cicm_web/wwwroot/lib/jquery-validation-unobtrusive/.bower.json b/cicm_web/wwwroot/lib/jquery-validation-unobtrusive/.bower.json new file mode 100644 index 00000000..a450c890 --- /dev/null +++ b/cicm_web/wwwroot/lib/jquery-validation-unobtrusive/.bower.json @@ -0,0 +1,15 @@ +{ + "name": "jquery-validation-unobtrusive", + "homepage": "https://github.com/aspnet/jquery-validation-unobtrusive", + "version": "3.2.9", + "_release": "3.2.9", + "_resolution": { + "type": "version", + "tag": "v3.2.9", + "commit": "a91f5401898e125f10771c5f5f0909d8c4c82396" + }, + "_source": "https://github.com/aspnet/jquery-validation-unobtrusive.git", + "_target": "^3.2.9", + "_originalSource": "jquery-validation-unobtrusive", + "_direct": true +} \ No newline at end of file diff --git a/cicm_web/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js b/cicm_web/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js new file mode 100755 index 00000000..7537b7ec --- /dev/null +++ b/cicm_web/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js @@ -0,0 +1,431 @@ +// Unobtrusive validation support library for jQuery and jQuery Validate +// Copyright (C) Microsoft Corporation. All rights reserved. +// @version v3.2.9 + +/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */ +/*global document: false, jQuery: false */ + +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define("jquery.validate.unobtrusive", ['jquery.validation'], factory); + } else if (typeof module === 'object' && module.exports) { + // CommonJS-like environments that support module.exports + module.exports = factory(require('jquery-validation')); + } else { + // Browser global + jQuery.validator.unobtrusive = factory(jQuery); + } +}(function ($) { + var $jQval = $.validator, + adapters, + data_validation = "unobtrusiveValidation"; + + function setValidationValues(options, ruleName, value) { + options.rules[ruleName] = value; + if (options.message) { + options.messages[ruleName] = options.message; + } + } + + function splitAndTrim(value) { + return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g); + } + + function escapeAttributeValue(value) { + // As mentioned on http://api.jquery.com/category/selectors/ + return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1"); + } + + function getModelPrefix(fieldName) { + return fieldName.substr(0, fieldName.lastIndexOf(".") + 1); + } + + function appendModelPrefix(value, prefix) { + if (value.indexOf("*.") === 0) { + value = value.replace("*.", prefix); + } + return value; + } + + function onError(error, inputElement) { // 'this' is the form element + var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"), + replaceAttrValue = container.attr("data-valmsg-replace"), + replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null; + + container.removeClass("field-validation-valid").addClass("field-validation-error"); + error.data("unobtrusiveContainer", container); + + if (replace) { + container.empty(); + error.removeClass("input-validation-error").appendTo(container); + } + else { + error.hide(); + } + } + + function onErrors(event, validator) { // 'this' is the form element + var container = $(this).find("[data-valmsg-summary=true]"), + list = container.find("ul"); + + if (list && list.length && validator.errorList.length) { + list.empty(); + container.addClass("validation-summary-errors").removeClass("validation-summary-valid"); + + $.each(validator.errorList, function () { + $("
  • ").html(this.message).appendTo(list); + }); + } + } + + function onSuccess(error) { // 'this' is the form element + var container = error.data("unobtrusiveContainer"); + + if (container) { + var replaceAttrValue = container.attr("data-valmsg-replace"), + replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null; + + container.addClass("field-validation-valid").removeClass("field-validation-error"); + error.removeData("unobtrusiveContainer"); + + if (replace) { + container.empty(); + } + } + } + + function onReset(event) { // 'this' is the form element + var $form = $(this), + key = '__jquery_unobtrusive_validation_form_reset'; + if ($form.data(key)) { + return; + } + // Set a flag that indicates we're currently resetting the form. + $form.data(key, true); + try { + $form.data("validator").resetForm(); + } finally { + $form.removeData(key); + } + + $form.find(".validation-summary-errors") + .addClass("validation-summary-valid") + .removeClass("validation-summary-errors"); + $form.find(".field-validation-error") + .addClass("field-validation-valid") + .removeClass("field-validation-error") + .removeData("unobtrusiveContainer") + .find(">*") // If we were using valmsg-replace, get the underlying error + .removeData("unobtrusiveContainer"); + } + + function validationInfo(form) { + var $form = $(form), + result = $form.data(data_validation), + onResetProxy = $.proxy(onReset, form), + defaultOptions = $jQval.unobtrusive.options || {}, + execInContext = function (name, args) { + var func = defaultOptions[name]; + func && $.isFunction(func) && func.apply(form, args); + }; + + if (!result) { + result = { + options: { // options structure passed to jQuery Validate's validate() method + errorClass: defaultOptions.errorClass || "input-validation-error", + errorElement: defaultOptions.errorElement || "span", + errorPlacement: function () { + onError.apply(form, arguments); + execInContext("errorPlacement", arguments); + }, + invalidHandler: function () { + onErrors.apply(form, arguments); + execInContext("invalidHandler", arguments); + }, + messages: {}, + rules: {}, + success: function () { + onSuccess.apply(form, arguments); + execInContext("success", arguments); + } + }, + attachValidation: function () { + $form + .off("reset." + data_validation, onResetProxy) + .on("reset." + data_validation, onResetProxy) + .validate(this.options); + }, + validate: function () { // a validation function that is called by unobtrusive Ajax + $form.validate(); + return $form.valid(); + } + }; + $form.data(data_validation, result); + } + + return result; + } + + $jQval.unobtrusive = { + adapters: [], + + parseElement: function (element, skipAttach) { + /// + /// Parses a single HTML element for unobtrusive validation attributes. + /// + /// The HTML element to be parsed. + /// [Optional] true to skip attaching the + /// validation to the form. If parsing just this single element, you should specify true. + /// If parsing several elements, you should specify false, and manually attach the validation + /// to the form when you are finished. The default is false. + var $element = $(element), + form = $element.parents("form")[0], + valInfo, rules, messages; + + if (!form) { // Cannot do client-side validation without a form + return; + } + + valInfo = validationInfo(form); + valInfo.options.rules[element.name] = rules = {}; + valInfo.options.messages[element.name] = messages = {}; + + $.each(this.adapters, function () { + var prefix = "data-val-" + this.name, + message = $element.attr(prefix), + paramValues = {}; + + if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy) + prefix += "-"; + + $.each(this.params, function () { + paramValues[this] = $element.attr(prefix + this); + }); + + this.adapt({ + element: element, + form: form, + message: message, + params: paramValues, + rules: rules, + messages: messages + }); + } + }); + + $.extend(rules, { "__dummy__": true }); + + if (!skipAttach) { + valInfo.attachValidation(); + } + }, + + parse: function (selector) { + /// + /// Parses all the HTML elements in the specified selector. It looks for input elements decorated + /// with the [data-val=true] attribute value and enables validation according to the data-val-* + /// attribute values. + /// + /// Any valid jQuery selector. + + // $forms includes all forms in selector's DOM hierarchy (parent, children and self) that have at least one + // element with data-val=true + var $selector = $(selector), + $forms = $selector.parents() + .addBack() + .filter("form") + .add($selector.find("form")) + .has("[data-val=true]"); + + $selector.find("[data-val=true]").each(function () { + $jQval.unobtrusive.parseElement(this, true); + }); + + $forms.each(function () { + var info = validationInfo(this); + if (info) { + info.attachValidation(); + } + }); + } + }; + + adapters = $jQval.unobtrusive.adapters; + + adapters.add = function (adapterName, params, fn) { + /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation. + /// The name of the adapter to be added. This matches the name used + /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). + /// [Optional] An array of parameter names (strings) that will + /// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and + /// mmmm is the parameter name). + /// The function to call, which adapts the values from the HTML + /// attributes into jQuery Validate rules and/or messages. + /// + if (!fn) { // Called with no params, just a function + fn = params; + params = []; + } + this.push({ name: adapterName, params: params, adapt: fn }); + return this; + }; + + adapters.addBool = function (adapterName, ruleName) { + /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where + /// the jQuery Validate validation rule has no parameter values. + /// The name of the adapter to be added. This matches the name used + /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). + /// [Optional] The name of the jQuery Validate rule. If not provided, the value + /// of adapterName will be used instead. + /// + return this.add(adapterName, function (options) { + setValidationValues(options, ruleName || adapterName, true); + }); + }; + + adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) { + /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where + /// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and + /// one for min-and-max). The HTML parameters are expected to be named -min and -max. + /// The name of the adapter to be added. This matches the name used + /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). + /// The name of the jQuery Validate rule to be used when you only + /// have a minimum value. + /// The name of the jQuery Validate rule to be used when you only + /// have a maximum value. + /// The name of the jQuery Validate rule to be used when you + /// have both a minimum and maximum value. + /// [Optional] The name of the HTML attribute that + /// contains the minimum value. The default is "min". + /// [Optional] The name of the HTML attribute that + /// contains the maximum value. The default is "max". + /// + return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) { + var min = options.params.min, + max = options.params.max; + + if (min && max) { + setValidationValues(options, minMaxRuleName, [min, max]); + } + else if (min) { + setValidationValues(options, minRuleName, min); + } + else if (max) { + setValidationValues(options, maxRuleName, max); + } + }); + }; + + adapters.addSingleVal = function (adapterName, attribute, ruleName) { + /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where + /// the jQuery Validate validation rule has a single value. + /// The name of the adapter to be added. This matches the name used + /// in the data-val-nnnn HTML attribute(where nnnn is the adapter name). + /// [Optional] The name of the HTML attribute that contains the value. + /// The default is "val". + /// [Optional] The name of the jQuery Validate rule. If not provided, the value + /// of adapterName will be used instead. + /// + return this.add(adapterName, [attribute || "val"], function (options) { + setValidationValues(options, ruleName || adapterName, options.params[attribute]); + }); + }; + + $jQval.addMethod("__dummy__", function (value, element, params) { + return true; + }); + + $jQval.addMethod("regex", function (value, element, params) { + var match; + if (this.optional(element)) { + return true; + } + + match = new RegExp(params).exec(value); + return (match && (match.index === 0) && (match[0].length === value.length)); + }); + + $jQval.addMethod("nonalphamin", function (value, element, nonalphamin) { + var match; + if (nonalphamin) { + match = value.match(/\W/g); + match = match && match.length >= nonalphamin; + } + return match; + }); + + if ($jQval.methods.extension) { + adapters.addSingleVal("accept", "mimtype"); + adapters.addSingleVal("extension", "extension"); + } else { + // for backward compatibility, when the 'extension' validation method does not exist, such as with versions + // of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for + // validating the extension, and ignore mime-type validations as they are not supported. + adapters.addSingleVal("extension", "extension", "accept"); + } + + adapters.addSingleVal("regex", "pattern"); + adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"); + adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range"); + adapters.addMinMax("minlength", "minlength").addMinMax("maxlength", "minlength", "maxlength"); + adapters.add("equalto", ["other"], function (options) { + var prefix = getModelPrefix(options.element.name), + other = options.params.other, + fullOtherName = appendModelPrefix(other, prefix), + element = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(fullOtherName) + "']")[0]; + + setValidationValues(options, "equalTo", element); + }); + adapters.add("required", function (options) { + // jQuery Validate equates "required" with "mandatory" for checkbox elements + if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") { + setValidationValues(options, "required", true); + } + }); + adapters.add("remote", ["url", "type", "additionalfields"], function (options) { + var value = { + url: options.params.url, + type: options.params.type || "GET", + data: {} + }, + prefix = getModelPrefix(options.element.name); + + $.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) { + var paramName = appendModelPrefix(fieldName, prefix); + value.data[paramName] = function () { + var field = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(paramName) + "']"); + // For checkboxes and radio buttons, only pick up values from checked fields. + if (field.is(":checkbox")) { + return field.filter(":checked").val() || field.filter(":hidden").val() || ''; + } + else if (field.is(":radio")) { + return field.filter(":checked").val() || ''; + } + return field.val(); + }; + }); + + setValidationValues(options, "remote", value); + }); + adapters.add("password", ["min", "nonalphamin", "regex"], function (options) { + if (options.params.min) { + setValidationValues(options, "minlength", options.params.min); + } + if (options.params.nonalphamin) { + setValidationValues(options, "nonalphamin", options.params.nonalphamin); + } + if (options.params.regex) { + setValidationValues(options, "regex", options.params.regex); + } + }); + adapters.add("fileextensions", ["extensions"], function (options) { + setValidationValues(options, "extension", options.params.extensions); + }); + + $(function () { + $jQval.unobtrusive.parse(document); + }); + + return $jQval.unobtrusive; +})); \ No newline at end of file diff --git a/cicm_web/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js b/cicm_web/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js new file mode 100755 index 00000000..b7e94b19 --- /dev/null +++ b/cicm_web/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js @@ -0,0 +1,4 @@ +// Unobtrusive validation support library for jQuery and jQuery Validate +// Copyright (C) Microsoft Corporation. All rights reserved. +// @version v3.2.9 +!function(a){"function"==typeof define&&define.amd?define("jquery.validate.unobtrusive",["jquery.validation"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery-validation")):jQuery.validator.unobtrusive=a(jQuery)}(function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function u(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=f.unobtrusive.options||{},u=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),u("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),u("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),u("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var m,f=a.validator,v="unobtrusiveValidation";return f.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=u(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){f.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=u(this);a&&a.attachValidation()})}},m=f.unobtrusive.adapters,m.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},m.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},m.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},m.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},f.addMethod("__dummy__",function(a,e,n){return!0}),f.addMethod("regex",function(a,e,n){var t;return!!this.optional(e)||(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),f.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),f.methods.extension?(m.addSingleVal("accept","mimtype"),m.addSingleVal("extension","extension")):m.addSingleVal("extension","extension","accept"),m.addSingleVal("regex","pattern"),m.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),m.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),m.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),m.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),m.add("required",function(a){"INPUT"===a.element.tagName.toUpperCase()&&"CHECKBOX"===a.element.type.toUpperCase()||e(a,"required",!0)}),m.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),m.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),m.add("fileextensions",["extensions"],function(a){e(a,"extension",a.params.extensions)}),a(function(){f.unobtrusive.parse(document)}),f.unobtrusive}); \ No newline at end of file diff --git a/cicm_web/wwwroot/lib/jquery-validation/.bower.json b/cicm_web/wwwroot/lib/jquery-validation/.bower.json new file mode 100644 index 00000000..59eda5d4 --- /dev/null +++ b/cicm_web/wwwroot/lib/jquery-validation/.bower.json @@ -0,0 +1,41 @@ +{ + "name": "jquery-validation", + "homepage": "https://jqueryvalidation.org/", + "repository": { + "type": "git", + "url": "git://github.com/jquery-validation/jquery-validation.git" + }, + "authors": [ + "Jörn Zaefferer " + ], + "description": "Form validation made easy", + "main": "dist/jquery.validate.js", + "keywords": [ + "forms", + "validation", + "validate" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "demo", + "lib" + ], + "dependencies": { + "jquery": ">= 1.7.2" + }, + "version": "1.17.0", + "_release": "1.17.0", + "_resolution": { + "type": "version", + "tag": "1.17.0", + "commit": "fc9b12d3bfaa2d0c04605855b896edb2934c0772" + }, + "_source": "https://github.com/jzaefferer/jquery-validation.git", + "_target": "^1.17.0", + "_originalSource": "jquery-validation", + "_direct": true +} \ No newline at end of file diff --git a/cicm_web/wwwroot/lib/jquery-validation/LICENSE.md b/cicm_web/wwwroot/lib/jquery-validation/LICENSE.md new file mode 100644 index 00000000..da92340b --- /dev/null +++ b/cicm_web/wwwroot/lib/jquery-validation/LICENSE.md @@ -0,0 +1,22 @@ +The MIT License (MIT) +===================== + +Copyright Jörn Zaefferer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/cicm_web/wwwroot/lib/jquery-validation/dist/Jquery.validate.min.js b/cicm_web/wwwroot/lib/jquery-validation/dist/Jquery.validate.min.js new file mode 100755 index 00000000..3de341f8 --- /dev/null +++ b/cicm_web/wwwroot/lib/jquery-validation/dist/Jquery.validate.min.js @@ -0,0 +1,4 @@ +/*! jQuery Validation Plugin - v1.17.0 - 7/29/2017 + * https://jqueryvalidation.org/ + * Copyright (c) 2017 Jörn Zaefferer; Licensed MIT */ +!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){a.extend(a.fn,{validate:function(b){if(!this.length)return void(b&&b.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."));var c=a.data(this[0],"validator");return c?c:(this.attr("novalidate","novalidate"),c=new a.validator(b,this[0]),a.data(this[0],"validator",c),c.settings.onsubmit&&(this.on("click.validate",":submit",function(b){c.submitButton=b.currentTarget,a(this).hasClass("cancel")&&(c.cancelSubmit=!0),void 0!==a(this).attr("formnovalidate")&&(c.cancelSubmit=!0)}),this.on("submit.validate",function(b){function d(){var d,e;return c.submitButton&&(c.settings.submitHandler||c.formSubmitted)&&(d=a("").attr("name",c.submitButton.name).val(a(c.submitButton).val()).appendTo(c.currentForm)),!c.settings.submitHandler||(e=c.settings.submitHandler.call(c,c.currentForm,b),d&&d.remove(),void 0!==e&&e)}return c.settings.debug&&b.preventDefault(),c.cancelSubmit?(c.cancelSubmit=!1,d()):c.form()?c.pendingRequest?(c.formSubmitted=!0,!1):d():(c.focusInvalid(),!1)})),c)},valid:function(){var b,c,d;return a(this[0]).is("form")?b=this.validate().form():(d=[],b=!0,c=a(this[0].form).validate(),this.each(function(){b=c.element(this)&&b,b||(d=d.concat(c.errorList))}),c.errorList=d),b},rules:function(b,c){var d,e,f,g,h,i,j=this[0];if(null!=j&&(!j.form&&j.hasAttribute("contenteditable")&&(j.form=this.closest("form")[0],j.name=this.attr("name")),null!=j.form)){if(b)switch(d=a.data(j.form,"validator").settings,e=d.rules,f=a.validator.staticRules(j),b){case"add":a.extend(f,a.validator.normalizeRule(c)),delete f.messages,e[j.name]=f,c.messages&&(d.messages[j.name]=a.extend(d.messages[j.name],c.messages));break;case"remove":return c?(i={},a.each(c.split(/\s/),function(a,b){i[b]=f[b],delete f[b]}),i):(delete e[j.name],f)}return g=a.validator.normalizeRules(a.extend({},a.validator.classRules(j),a.validator.attributeRules(j),a.validator.dataRules(j),a.validator.staticRules(j)),j),g.required&&(h=g.required,delete g.required,g=a.extend({required:h},g)),g.remote&&(h=g.remote,delete g.remote,g=a.extend(g,{remote:h})),g}}}),a.extend(a.expr.pseudos||a.expr[":"],{blank:function(b){return!a.trim(""+a(b).val())},filled:function(b){var c=a(b).val();return null!==c&&!!a.trim(""+c)},unchecked:function(b){return!a(b).prop("checked")}}),a.validator=function(b,c){this.settings=a.extend(!0,{},a.validator.defaults,b),this.currentForm=c,this.init()},a.validator.format=function(b,c){return 1===arguments.length?function(){var c=a.makeArray(arguments);return c.unshift(b),a.validator.format.apply(this,c)}:void 0===c?b:(arguments.length>2&&c.constructor!==Array&&(c=a.makeArray(arguments).slice(1)),c.constructor!==Array&&(c=[c]),a.each(c,function(a,c){b=b.replace(new RegExp("\\{"+a+"\\}","g"),function(){return c})}),b)},a.extend(a.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",pendingClass:"pending",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:a([]),errorLabelContainer:a([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(a){this.lastActive=a,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(a)))},onfocusout:function(a){this.checkable(a)||!(a.name in this.submitted)&&this.optional(a)||this.element(a)},onkeyup:function(b,c){var d=[16,17,18,20,35,36,37,38,39,40,45,144,225];9===c.which&&""===this.elementValue(b)||a.inArray(c.keyCode,d)!==-1||(b.name in this.submitted||b.name in this.invalid)&&this.element(b)},onclick:function(a){a.name in this.submitted?this.element(a):a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).addClass(c).removeClass(d):a(b).addClass(c).removeClass(d)},unhighlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).removeClass(c).addClass(d):a(b).removeClass(c).addClass(d)}},setDefaults:function(b){a.extend(a.validator.defaults,b)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",equalTo:"Please enter the same value again.",maxlength:a.validator.format("Please enter no more than {0} characters."),minlength:a.validator.format("Please enter at least {0} characters."),rangelength:a.validator.format("Please enter a value between {0} and {1} characters long."),range:a.validator.format("Please enter a value between {0} and {1}."),max:a.validator.format("Please enter a value less than or equal to {0}."),min:a.validator.format("Please enter a value greater than or equal to {0}."),step:a.validator.format("Please enter a multiple of {0}.")},autoCreateRanges:!1,prototype:{init:function(){function b(b){!this.form&&this.hasAttribute("contenteditable")&&(this.form=a(this).closest("form")[0],this.name=a(this).attr("name"));var c=a.data(this.form,"validator"),d="on"+b.type.replace(/^validate/,""),e=c.settings;e[d]&&!a(this).is(e.ignore)&&e[d].call(c,this,b)}this.labelContainer=a(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||a(this.currentForm),this.containers=a(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var c,d=this.groups={};a.each(this.settings.groups,function(b,c){"string"==typeof c&&(c=c.split(/\s/)),a.each(c,function(a,c){d[c]=b})}),c=this.settings.rules,a.each(c,function(b,d){c[b]=a.validator.normalizeRule(d)}),a(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox'], [contenteditable], [type='button']",b).on("click.validate","select, option, [type='radio'], [type='checkbox']",b),this.settings.invalidHandler&&a(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler)},form:function(){return this.checkForm(),a.extend(this.submitted,this.errorMap),this.invalid=a.extend({},this.errorMap),this.valid()||a(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(b){var c,d,e=this.clean(b),f=this.validationTargetFor(e),g=this,h=!0;return void 0===f?delete this.invalid[e.name]:(this.prepareElement(f),this.currentElements=a(f),d=this.groups[f.name],d&&a.each(this.groups,function(a,b){b===d&&a!==f.name&&(e=g.validationTargetFor(g.clean(g.findByName(a))),e&&e.name in g.invalid&&(g.currentElements.push(e),h=g.check(e)&&h))}),c=this.check(f)!==!1,h=h&&c,c?this.invalid[f.name]=!1:this.invalid[f.name]=!0,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),a(b).attr("aria-invalid",!c)),h},showErrors:function(b){if(b){var c=this;a.extend(this.errorMap,b),this.errorList=a.map(this.errorMap,function(a,b){return{message:a,element:c.findByName(b)[0]}}),this.successList=a.grep(this.successList,function(a){return!(a.name in b)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){a.fn.resetForm&&a(this.currentForm).resetForm(),this.invalid={},this.submitted={},this.prepareForm(),this.hideErrors();var b=this.elements().removeData("previousValue").removeAttr("aria-invalid");this.resetElements(b)},resetElements:function(a){var b;if(this.settings.unhighlight)for(b=0;a[b];b++)this.settings.unhighlight.call(this,a[b],this.settings.errorClass,""),this.findByName(a[b].name).removeClass(this.settings.validClass);else a.removeClass(this.settings.errorClass).removeClass(this.settings.validClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b,c=0;for(b in a)void 0!==a[b]&&null!==a[b]&&a[b]!==!1&&c++;return c},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(a){a.not(this.containers).text(""),this.addWrapper(a).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{a(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(b){}},findLastActive:function(){var b=this.lastActive;return b&&1===a.grep(this.errorList,function(a){return a.element.name===b.name}).length&&b},elements:function(){var b=this,c={};return a(this.currentForm).find("input, select, textarea, [contenteditable]").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){var d=this.name||a(this).attr("name");return!d&&b.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.hasAttribute("contenteditable")&&(this.form=a(this).closest("form")[0],this.name=d),!(d in c||!b.objectLength(a(this).rules()))&&(c[d]=!0,!0)})},clean:function(b){return a(b)[0]},errors:function(){var b=this.settings.errorClass.split(" ").join(".");return a(this.settings.errorElement+"."+b,this.errorContext)},resetInternals:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=a([]),this.toHide=a([])},reset:function(){this.resetInternals(),this.currentElements=a([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset(),this.toHide=this.errorsFor(a)},elementValue:function(b){var c,d,e=a(b),f=b.type;return"radio"===f||"checkbox"===f?this.findByName(b.name).filter(":checked").val():"number"===f&&"undefined"!=typeof b.validity?b.validity.badInput?"NaN":e.val():(c=b.hasAttribute("contenteditable")?e.text():e.val(),"file"===f?"C:\\fakepath\\"===c.substr(0,12)?c.substr(12):(d=c.lastIndexOf("/"),d>=0?c.substr(d+1):(d=c.lastIndexOf("\\"),d>=0?c.substr(d+1):c)):"string"==typeof c?c.replace(/\r/g,""):c)},check:function(b){b=this.validationTargetFor(this.clean(b));var c,d,e,f,g=a(b).rules(),h=a.map(g,function(a,b){return b}).length,i=!1,j=this.elementValue(b);if("function"==typeof g.normalizer?f=g.normalizer:"function"==typeof this.settings.normalizer&&(f=this.settings.normalizer),f){if(j=f.call(b,j),"string"!=typeof j)throw new TypeError("The normalizer should return a string value.");delete g.normalizer}for(d in g){e={method:d,parameters:g[d]};try{if(c=a.validator.methods[d].call(this,j,b,e.parameters),"dependency-mismatch"===c&&1===h){i=!0;continue}if(i=!1,"pending"===c)return void(this.toHide=this.toHide.not(this.errorsFor(b)));if(!c)return this.formatAndAdd(b,e),!1}catch(k){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+b.id+", check the '"+e.method+"' method.",k),k instanceof TypeError&&(k.message+=". Exception occurred when checking element "+b.id+", check the '"+e.method+"' method."),k}}if(!i)return this.objectLength(g)&&this.successList.push(b),!0},customDataMessage:function(b,c){return a(b).data("msg"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase())||a(b).data("msg")},customMessage:function(a,b){var c=this.settings.messages[a];return c&&(c.constructor===String?c:c[b])},findDefined:function(){for(var a=0;aWarning: No message defined for "+b.name+""),e=/\$?\{(\d+)\}/g;return"function"==typeof d?d=d.call(this,c.parameters,b):e.test(d)&&(d=a.validator.format(d.replace(e,"{$1}"),c.parameters)),d},formatAndAdd:function(a,b){var c=this.defaultMessage(a,b);this.errorList.push({message:c,element:a,method:b.method}),this.errorMap[a.name]=c,this.submitted[a.name]=c},addWrapper:function(a){return this.settings.wrapper&&(a=a.add(a.parent(this.settings.wrapper))),a},defaultShowErrors:function(){var a,b,c;for(a=0;this.errorList[a];a++)c=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,c.element,this.settings.errorClass,this.settings.validClass),this.showLabel(c.element,c.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return a(this.errorList).map(function(){return this.element})},showLabel:function(b,c){var d,e,f,g,h=this.errorsFor(b),i=this.idOrName(b),j=a(b).attr("aria-describedby");h.length?(h.removeClass(this.settings.validClass).addClass(this.settings.errorClass),h.html(c)):(h=a("<"+this.settings.errorElement+">").attr("id",i+"-error").addClass(this.settings.errorClass).html(c||""),d=h,this.settings.wrapper&&(d=h.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(d):this.settings.errorPlacement?this.settings.errorPlacement.call(this,d,a(b)):d.insertAfter(b),h.is("label")?h.attr("for",i):0===h.parents("label[for='"+this.escapeCssMeta(i)+"']").length&&(f=h.attr("id"),j?j.match(new RegExp("\\b"+this.escapeCssMeta(f)+"\\b"))||(j+=" "+f):j=f,a(b).attr("aria-describedby",j),e=this.groups[b.name],e&&(g=this,a.each(g.groups,function(b,c){c===e&&a("[name='"+g.escapeCssMeta(b)+"']",g.currentForm).attr("aria-describedby",h.attr("id"))})))),!c&&this.settings.success&&(h.text(""),"string"==typeof this.settings.success?h.addClass(this.settings.success):this.settings.success(h,b)),this.toShow=this.toShow.add(h)},errorsFor:function(b){var c=this.escapeCssMeta(this.idOrName(b)),d=a(b).attr("aria-describedby"),e="label[for='"+c+"'], label[for='"+c+"'] *";return d&&(e=e+", #"+this.escapeCssMeta(d).replace(/\s+/g,", #")),this.errors().filter(e)},escapeCssMeta:function(a){return a.replace(/([\\!"#$%&'()*+,.\/:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},validationTargetFor:function(b){return this.checkable(b)&&(b=this.findByName(b.name)),a(b).not(this.settings.ignore)[0]},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(b){return a(this.currentForm).find("[name='"+this.escapeCssMeta(b)+"']")},getLength:function(b,c){switch(c.nodeName.toLowerCase()){case"select":return a("option:selected",c).length;case"input":if(this.checkable(c))return this.findByName(c.name).filter(":checked").length}return b.length},depend:function(a,b){return!this.dependTypes[typeof a]||this.dependTypes[typeof a](a,b)},dependTypes:{"boolean":function(a){return a},string:function(b,c){return!!a(b,c.form).length},"function":function(a,b){return a(b)}},optional:function(b){var c=this.elementValue(b);return!a.validator.methods.required.call(this,c,b)&&"dependency-mismatch"},startRequest:function(b){this.pending[b.name]||(this.pendingRequest++,a(b).addClass(this.settings.pendingClass),this.pending[b.name]=!0)},stopRequest:function(b,c){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[b.name],a(b).removeClass(this.settings.pendingClass),c&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(a(this.currentForm).submit(),this.submitButton&&a("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!c&&0===this.pendingRequest&&this.formSubmitted&&(a(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(b,c){return c="string"==typeof c&&c||"remote",a.data(b,"previousValue")||a.data(b,"previousValue",{old:null,valid:!0,message:this.defaultMessage(b,{method:c})})},destroy:function(){this.resetForm(),a(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(b,c){b.constructor===String?this.classRuleSettings[b]=c:a.extend(this.classRuleSettings,b)},classRules:function(b){var c={},d=a(b).attr("class");return d&&a.each(d.split(" "),function(){this in a.validator.classRuleSettings&&a.extend(c,a.validator.classRuleSettings[this])}),c},normalizeAttributeRule:function(a,b,c,d){/min|max|step/.test(c)&&(null===b||/number|range|text/.test(b))&&(d=Number(d),isNaN(d)&&(d=void 0)),d||0===d?a[c]=d:b===c&&"range"!==b&&(a[c]=!0)},attributeRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)"required"===c?(d=b.getAttribute(c),""===d&&(d=!0),d=!!d):d=f.attr(c),this.normalizeAttributeRule(e,g,c,d);return e.maxlength&&/-1|2147483647|524288/.test(e.maxlength)&&delete e.maxlength,e},dataRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)d=f.data("rule"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase()),this.normalizeAttributeRule(e,g,c,d);return e},staticRules:function(b){var c={},d=a.data(b.form,"validator");return d.settings.rules&&(c=a.validator.normalizeRule(d.settings.rules[b.name])||{}),c},normalizeRules:function(b,c){return a.each(b,function(d,e){if(e===!1)return void delete b[d];if(e.param||e.depends){var f=!0;switch(typeof e.depends){case"string":f=!!a(e.depends,c.form).length;break;case"function":f=e.depends.call(c,c)}f?b[d]=void 0===e.param||e.param:(a.data(c.form,"validator").resetElements(a(c)),delete b[d])}}),a.each(b,function(d,e){b[d]=a.isFunction(e)&&"normalizer"!==d?e(c):e}),a.each(["minlength","maxlength"],function(){b[this]&&(b[this]=Number(b[this]))}),a.each(["rangelength","range"],function(){var c;b[this]&&(a.isArray(b[this])?b[this]=[Number(b[this][0]),Number(b[this][1])]:"string"==typeof b[this]&&(c=b[this].replace(/[\[\]]/g,"").split(/[\s,]+/),b[this]=[Number(c[0]),Number(c[1])]))}),a.validator.autoCreateRanges&&(null!=b.min&&null!=b.max&&(b.range=[b.min,b.max],delete b.min,delete b.max),null!=b.minlength&&null!=b.maxlength&&(b.rangelength=[b.minlength,b.maxlength],delete b.minlength,delete b.maxlength)),b},normalizeRule:function(b){if("string"==typeof b){var c={};a.each(b.split(/\s/),function(){c[this]=!0}),b=c}return b},addMethod:function(b,c,d){a.validator.methods[b]=c,a.validator.messages[b]=void 0!==d?d:a.validator.messages[b],c.length<3&&a.validator.addClassRules(b,a.validator.normalizeRule(b))},methods:{required:function(b,c,d){if(!this.depend(d,c))return"dependency-mismatch";if("select"===c.nodeName.toLowerCase()){var e=a(c).val();return e&&e.length>0}return this.checkable(c)?this.getLength(b,c)>0:b.length>0},email:function(a,b){return this.optional(b)||/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(a)},url:function(a,b){return this.optional(b)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[\/?#]\S*)?$/i.test(a)},date:function(a,b){return this.optional(b)||!/Invalid|NaN/.test(new Date(a).toString())},dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(a)},number:function(a,b){return this.optional(b)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},minlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d},maxlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e<=d},rangelength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d[0]&&e<=d[1]},min:function(a,b,c){return this.optional(b)||a>=c},max:function(a,b,c){return this.optional(b)||a<=c},range:function(a,b,c){return this.optional(b)||a>=c[0]&&a<=c[1]},step:function(b,c,d){var e,f=a(c).attr("type"),g="Step attribute on input type "+f+" is not supported.",h=["text","number","range"],i=new RegExp("\\b"+f+"\\b"),j=f&&!i.test(h.join()),k=function(a){var b=(""+a).match(/(?:\.(\d+))?$/);return b&&b[1]?b[1].length:0},l=function(a){return Math.round(a*Math.pow(10,e))},m=!0;if(j)throw new Error(g);return e=k(d),(k(b)>e||l(b)%l(d)!==0)&&(m=!1),this.optional(c)||m},equalTo:function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-equalTo-blur").length&&e.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){a(c).valid()}),b===e.val()},remote:function(b,c,d,e){if(this.optional(c))return"dependency-mismatch";e="string"==typeof e&&e||"remote";var f,g,h,i=this.previousValue(c,e);return this.settings.messages[c.name]||(this.settings.messages[c.name]={}),i.originalMessage=i.originalMessage||this.settings.messages[c.name][e],this.settings.messages[c.name][e]=i.message,d="string"==typeof d&&{url:d}||d,h=a.param(a.extend({data:b},d.data)),i.old===h?i.valid:(i.old=h,f=this,this.startRequest(c),g={},g[c.name]=b,a.ajax(a.extend(!0,{mode:"abort",port:"validate"+c.name,dataType:"json",data:g,context:f.currentForm,success:function(a){var d,g,h,j=a===!0||"true"===a;f.settings.messages[c.name][e]=i.originalMessage,j?(h=f.formSubmitted,f.resetInternals(),f.toHide=f.errorsFor(c),f.formSubmitted=h,f.successList.push(c),f.invalid[c.name]=!1,f.showErrors()):(d={},g=a||f.defaultMessage(c,{method:e,parameters:b}),d[c.name]=i.message=g,f.invalid[c.name]=!0,f.showErrors(d)),i.valid=j,f.stopRequest(c,j)}},d)),"pending")}}});var b,c={};return a.ajaxPrefilter?a.ajaxPrefilter(function(a,b,d){var e=a.port;"abort"===a.mode&&(c[e]&&c[e].abort(),c[e]=d)}):(b=a.ajax,a.ajax=function(d){var e=("mode"in d?d:a.ajaxSettings).mode,f=("port"in d?d:a.ajaxSettings).port;return"abort"===e?(c[f]&&c[f].abort(),c[f]=b.apply(this,arguments),c[f]):b.apply(this,arguments)}),a}); \ No newline at end of file diff --git a/cicm_web/wwwroot/lib/jquery-validation/dist/additional-methods.js b/cicm_web/wwwroot/lib/jquery-validation/dist/additional-methods.js new file mode 100755 index 00000000..3982d60e --- /dev/null +++ b/cicm_web/wwwroot/lib/jquery-validation/dist/additional-methods.js @@ -0,0 +1,1158 @@ +/*! + * jQuery Validation Plugin v1.17.0 + * + * https://jqueryvalidation.org/ + * + * Copyright (c) 2017 Jörn Zaefferer + * Released under the MIT license + */ +(function( factory ) { + if ( typeof define === "function" && define.amd ) { + define( ["jquery", "./jquery.validate"], factory ); + } else if (typeof module === "object" && module.exports) { + module.exports = factory( require( "jquery" ) ); + } else { + factory( jQuery ); + } +}(function( $ ) { + +( function() { + + function stripHtml( value ) { + + // Remove html tags and space chars + return value.replace( /<.[^<>]*?>/g, " " ).replace( / | /gi, " " ) + + // Remove punctuation + .replace( /[.(),;:!?%#$'\"_+=\/\-“”’]*/g, "" ); + } + + $.validator.addMethod( "maxWords", function( value, element, params ) { + return this.optional( element ) || stripHtml( value ).match( /\b\w+\b/g ).length <= params; + }, $.validator.format( "Please enter {0} words or less." ) ); + + $.validator.addMethod( "minWords", function( value, element, params ) { + return this.optional( element ) || stripHtml( value ).match( /\b\w+\b/g ).length >= params; + }, $.validator.format( "Please enter at least {0} words." ) ); + + $.validator.addMethod( "rangeWords", function( value, element, params ) { + var valueStripped = stripHtml( value ), + regex = /\b\w+\b/g; + return this.optional( element ) || valueStripped.match( regex ).length >= params[ 0 ] && valueStripped.match( regex ).length <= params[ 1 ]; + }, $.validator.format( "Please enter between {0} and {1} words." ) ); + +}() ); + +// Accept a value from a file input based on a required mimetype +$.validator.addMethod( "accept", function( value, element, param ) { + + // Split mime on commas in case we have multiple types we can accept + var typeParam = typeof param === "string" ? param.replace( /\s/g, "" ) : "image/*", + optionalValue = this.optional( element ), + i, file, regex; + + // Element is optional + if ( optionalValue ) { + return optionalValue; + } + + if ( $( element ).attr( "type" ) === "file" ) { + + // Escape string to be used in the regex + // see: https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex + // Escape also "/*" as "/.*" as a wildcard + typeParam = typeParam + .replace( /[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, "\\$&" ) + .replace( /,/g, "|" ) + .replace( /\/\*/g, "/.*" ); + + // Check if the element has a FileList before checking each file + if ( element.files && element.files.length ) { + regex = new RegExp( ".?(" + typeParam + ")$", "i" ); + for ( i = 0; i < element.files.length; i++ ) { + file = element.files[ i ]; + + // Grab the mimetype from the loaded file, verify it matches + if ( !file.type.match( regex ) ) { + return false; + } + } + } + } + + // Either return true because we've validated each file, or because the + // browser does not support element.files and the FileList feature + return true; +}, $.validator.format( "Please enter a value with a valid mimetype." ) ); + +$.validator.addMethod( "alphanumeric", function( value, element ) { + return this.optional( element ) || /^\w+$/i.test( value ); +}, "Letters, numbers, and underscores only please" ); + +/* + * Dutch bank account numbers (not 'giro' numbers) have 9 digits + * and pass the '11 check'. + * We accept the notation with spaces, as that is common. + * acceptable: 123456789 or 12 34 56 789 + */ +$.validator.addMethod( "bankaccountNL", function( value, element ) { + if ( this.optional( element ) ) { + return true; + } + if ( !( /^[0-9]{9}|([0-9]{2} ){3}[0-9]{3}$/.test( value ) ) ) { + return false; + } + + // Now '11 check' + var account = value.replace( / /g, "" ), // Remove spaces + sum = 0, + len = account.length, + pos, factor, digit; + for ( pos = 0; pos < len; pos++ ) { + factor = len - pos; + digit = account.substring( pos, pos + 1 ); + sum = sum + factor * digit; + } + return sum % 11 === 0; +}, "Please specify a valid bank account number" ); + +$.validator.addMethod( "bankorgiroaccountNL", function( value, element ) { + return this.optional( element ) || + ( $.validator.methods.bankaccountNL.call( this, value, element ) ) || + ( $.validator.methods.giroaccountNL.call( this, value, element ) ); +}, "Please specify a valid bank or giro account number" ); + +/** + * BIC is the business identifier code (ISO 9362). This BIC check is not a guarantee for authenticity. + * + * BIC pattern: BBBBCCLLbbb (8 or 11 characters long; bbb is optional) + * + * Validation is case-insensitive. Please make sure to normalize input yourself. + * + * BIC definition in detail: + * - First 4 characters - bank code (only letters) + * - Next 2 characters - ISO 3166-1 alpha-2 country code (only letters) + * - Next 2 characters - location code (letters and digits) + * a. shall not start with '0' or '1' + * b. second character must be a letter ('O' is not allowed) or digit ('0' for test (therefore not allowed), '1' denoting passive participant, '2' typically reverse-billing) + * - Last 3 characters - branch code, optional (shall not start with 'X' except in case of 'XXX' for primary office) (letters and digits) + */ +$.validator.addMethod( "bic", function( value, element ) { + return this.optional( element ) || /^([A-Z]{6}[A-Z2-9][A-NP-Z1-9])(X{3}|[A-WY-Z0-9][A-Z0-9]{2})?$/.test( value.toUpperCase() ); +}, "Please specify a valid BIC code" ); + +/* + * Código de identificación fiscal ( CIF ) is the tax identification code for Spanish legal entities + * Further rules can be found in Spanish on http://es.wikipedia.org/wiki/C%C3%B3digo_de_identificaci%C3%B3n_fiscal + * + * Spanish CIF structure: + * + * [ T ][ P ][ P ][ N ][ N ][ N ][ N ][ N ][ C ] + * + * Where: + * + * T: 1 character. Kind of Organization Letter: [ABCDEFGHJKLMNPQRSUVW] + * P: 2 characters. Province. + * N: 5 characters. Secuencial Number within the province. + * C: 1 character. Control Digit: [0-9A-J]. + * + * [ T ]: Kind of Organizations. Possible values: + * + * A. Corporations + * B. LLCs + * C. General partnerships + * D. Companies limited partnerships + * E. Communities of goods + * F. Cooperative Societies + * G. Associations + * H. Communities of homeowners in horizontal property regime + * J. Civil Societies + * K. Old format + * L. Old format + * M. Old format + * N. Nonresident entities + * P. Local authorities + * Q. Autonomous bodies, state or not, and the like, and congregations and religious institutions + * R. Congregations and religious institutions (since 2008 ORDER EHA/451/2008) + * S. Organs of State Administration and regions + * V. Agrarian Transformation + * W. Permanent establishments of non-resident in Spain + * + * [ C ]: Control Digit. It can be a number or a letter depending on T value: + * [ T ] --> [ C ] + * ------ ---------- + * A Number + * B Number + * E Number + * H Number + * K Letter + * P Letter + * Q Letter + * S Letter + * + */ +$.validator.addMethod( "cifES", function( value, element ) { + "use strict"; + + if ( this.optional( element ) ) { + return true; + } + + var cifRegEx = new RegExp( /^([ABCDEFGHJKLMNPQRSUVW])(\d{7})([0-9A-J])$/gi ); + var letter = value.substring( 0, 1 ), // [ T ] + number = value.substring( 1, 8 ), // [ P ][ P ][ N ][ N ][ N ][ N ][ N ] + control = value.substring( 8, 9 ), // [ C ] + all_sum = 0, + even_sum = 0, + odd_sum = 0, + i, n, + control_digit, + control_letter; + + function isOdd( n ) { + return n % 2 === 0; + } + + // Quick format test + if ( value.length !== 9 || !cifRegEx.test( value ) ) { + return false; + } + + for ( i = 0; i < number.length; i++ ) { + n = parseInt( number[ i ], 10 ); + + // Odd positions + if ( isOdd( i ) ) { + + // Odd positions are multiplied first. + n *= 2; + + // If the multiplication is bigger than 10 we need to adjust + odd_sum += n < 10 ? n : n - 9; + + // Even positions + // Just sum them + } else { + even_sum += n; + } + } + + all_sum = even_sum + odd_sum; + control_digit = ( 10 - ( all_sum ).toString().substr( -1 ) ).toString(); + control_digit = parseInt( control_digit, 10 ) > 9 ? "0" : control_digit; + control_letter = "JABCDEFGHI".substr( control_digit, 1 ).toString(); + + // Control must be a digit + if ( letter.match( /[ABEH]/ ) ) { + return control === control_digit; + + // Control must be a letter + } else if ( letter.match( /[KPQS]/ ) ) { + return control === control_letter; + } + + // Can be either + return control === control_digit || control === control_letter; + +}, "Please specify a valid CIF number." ); + +/* + * Brazillian CPF number (Cadastrado de Pessoas Físicas) is the equivalent of a Brazilian tax registration number. + * CPF numbers have 11 digits in total: 9 numbers followed by 2 check numbers that are being used for validation. + */ +$.validator.addMethod( "cpfBR", function( value ) { + + // Removing special characters from value + value = value.replace( /([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g, "" ); + + // Checking value to have 11 digits only + if ( value.length !== 11 ) { + return false; + } + + var sum = 0, + firstCN, secondCN, checkResult, i; + + firstCN = parseInt( value.substring( 9, 10 ), 10 ); + secondCN = parseInt( value.substring( 10, 11 ), 10 ); + + checkResult = function( sum, cn ) { + var result = ( sum * 10 ) % 11; + if ( ( result === 10 ) || ( result === 11 ) ) { + result = 0; + } + return ( result === cn ); + }; + + // Checking for dump data + if ( value === "" || + value === "00000000000" || + value === "11111111111" || + value === "22222222222" || + value === "33333333333" || + value === "44444444444" || + value === "55555555555" || + value === "66666666666" || + value === "77777777777" || + value === "88888888888" || + value === "99999999999" + ) { + return false; + } + + // Step 1 - using first Check Number: + for ( i = 1; i <= 9; i++ ) { + sum = sum + parseInt( value.substring( i - 1, i ), 10 ) * ( 11 - i ); + } + + // If first Check Number (CN) is valid, move to Step 2 - using second Check Number: + if ( checkResult( sum, firstCN ) ) { + sum = 0; + for ( i = 1; i <= 10; i++ ) { + sum = sum + parseInt( value.substring( i - 1, i ), 10 ) * ( 12 - i ); + } + return checkResult( sum, secondCN ); + } + return false; + +}, "Please specify a valid CPF number" ); + +// https://jqueryvalidation.org/creditcard-method/ +// based on https://en.wikipedia.org/wiki/Luhn_algorithm +$.validator.addMethod( "creditcard", function( value, element ) { + if ( this.optional( element ) ) { + return "dependency-mismatch"; + } + + // Accept only spaces, digits and dashes + if ( /[^0-9 \-]+/.test( value ) ) { + return false; + } + + var nCheck = 0, + nDigit = 0, + bEven = false, + n, cDigit; + + value = value.replace( /\D/g, "" ); + + // Basing min and max length on + // https://developer.ean.com/general_info/Valid_Credit_Card_Types + if ( value.length < 13 || value.length > 19 ) { + return false; + } + + for ( n = value.length - 1; n >= 0; n-- ) { + cDigit = value.charAt( n ); + nDigit = parseInt( cDigit, 10 ); + if ( bEven ) { + if ( ( nDigit *= 2 ) > 9 ) { + nDigit -= 9; + } + } + + nCheck += nDigit; + bEven = !bEven; + } + + return ( nCheck % 10 ) === 0; +}, "Please enter a valid credit card number." ); + +/* NOTICE: Modified version of Castle.Components.Validator.CreditCardValidator + * Redistributed under the the Apache License 2.0 at http://www.apache.org/licenses/LICENSE-2.0 + * Valid Types: mastercard, visa, amex, dinersclub, enroute, discover, jcb, unknown, all (overrides all other settings) + */ +$.validator.addMethod( "creditcardtypes", function( value, element, param ) { + if ( /[^0-9\-]+/.test( value ) ) { + return false; + } + + value = value.replace( /\D/g, "" ); + + var validTypes = 0x0000; + + if ( param.mastercard ) { + validTypes |= 0x0001; + } + if ( param.visa ) { + validTypes |= 0x0002; + } + if ( param.amex ) { + validTypes |= 0x0004; + } + if ( param.dinersclub ) { + validTypes |= 0x0008; + } + if ( param.enroute ) { + validTypes |= 0x0010; + } + if ( param.discover ) { + validTypes |= 0x0020; + } + if ( param.jcb ) { + validTypes |= 0x0040; + } + if ( param.unknown ) { + validTypes |= 0x0080; + } + if ( param.all ) { + validTypes = 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040 | 0x0080; + } + if ( validTypes & 0x0001 && /^(5[12345])/.test( value ) ) { // Mastercard + return value.length === 16; + } + if ( validTypes & 0x0002 && /^(4)/.test( value ) ) { // Visa + return value.length === 16; + } + if ( validTypes & 0x0004 && /^(3[47])/.test( value ) ) { // Amex + return value.length === 15; + } + if ( validTypes & 0x0008 && /^(3(0[012345]|[68]))/.test( value ) ) { // Dinersclub + return value.length === 14; + } + if ( validTypes & 0x0010 && /^(2(014|149))/.test( value ) ) { // Enroute + return value.length === 15; + } + if ( validTypes & 0x0020 && /^(6011)/.test( value ) ) { // Discover + return value.length === 16; + } + if ( validTypes & 0x0040 && /^(3)/.test( value ) ) { // Jcb + return value.length === 16; + } + if ( validTypes & 0x0040 && /^(2131|1800)/.test( value ) ) { // Jcb + return value.length === 15; + } + if ( validTypes & 0x0080 ) { // Unknown + return true; + } + return false; +}, "Please enter a valid credit card number." ); + +/** + * Validates currencies with any given symbols by @jameslouiz + * Symbols can be optional or required. Symbols required by default + * + * Usage examples: + * currency: ["£", false] - Use false for soft currency validation + * currency: ["$", false] + * currency: ["RM", false] - also works with text based symbols such as "RM" - Malaysia Ringgit etc + * + * + * + * Soft symbol checking + * currencyInput: { + * currency: ["$", false] + * } + * + * Strict symbol checking (default) + * currencyInput: { + * currency: "$" + * //OR + * currency: ["$", true] + * } + * + * Multiple Symbols + * currencyInput: { + * currency: "$,£,¢" + * } + */ +$.validator.addMethod( "currency", function( value, element, param ) { + var isParamString = typeof param === "string", + symbol = isParamString ? param : param[ 0 ], + soft = isParamString ? true : param[ 1 ], + regex; + + symbol = symbol.replace( /,/g, "" ); + symbol = soft ? symbol + "]" : symbol + "]?"; + regex = "^[" + symbol + "([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$"; + regex = new RegExp( regex ); + return this.optional( element ) || regex.test( value ); + +}, "Please specify a valid currency" ); + +$.validator.addMethod( "dateFA", function( value, element ) { + return this.optional( element ) || /^[1-4]\d{3}\/((0?[1-6]\/((3[0-1])|([1-2][0-9])|(0?[1-9])))|((1[0-2]|(0?[7-9]))\/(30|([1-2][0-9])|(0?[1-9]))))$/.test( value ); +}, $.validator.messages.date ); + +/** + * Return true, if the value is a valid date, also making this formal check dd/mm/yyyy. + * + * @example $.validator.methods.date("01/01/1900") + * @result true + * + * @example $.validator.methods.date("01/13/1990") + * @result false + * + * @example $.validator.methods.date("01.01.1900") + * @result false + * + * @example + * @desc Declares an optional input element whose value must be a valid date. + * + * @name $.validator.methods.dateITA + * @type Boolean + * @cat Plugins/Validate/Methods + */ +$.validator.addMethod( "dateITA", function( value, element ) { + var check = false, + re = /^\d{1,2}\/\d{1,2}\/\d{4}$/, + adata, gg, mm, aaaa, xdata; + if ( re.test( value ) ) { + adata = value.split( "/" ); + gg = parseInt( adata[ 0 ], 10 ); + mm = parseInt( adata[ 1 ], 10 ); + aaaa = parseInt( adata[ 2 ], 10 ); + xdata = new Date( Date.UTC( aaaa, mm - 1, gg, 12, 0, 0, 0 ) ); + if ( ( xdata.getUTCFullYear() === aaaa ) && ( xdata.getUTCMonth() === mm - 1 ) && ( xdata.getUTCDate() === gg ) ) { + check = true; + } else { + check = false; + } + } else { + check = false; + } + return this.optional( element ) || check; +}, $.validator.messages.date ); + +$.validator.addMethod( "dateNL", function( value, element ) { + return this.optional( element ) || /^(0?[1-9]|[12]\d|3[01])[\.\/\-](0?[1-9]|1[012])[\.\/\-]([12]\d)?(\d\d)$/.test( value ); +}, $.validator.messages.date ); + +// Older "accept" file extension method. Old docs: http://docs.jquery.com/Plugins/Validation/Methods/accept +$.validator.addMethod( "extension", function( value, element, param ) { + param = typeof param === "string" ? param.replace( /,/g, "|" ) : "png|jpe?g|gif"; + return this.optional( element ) || value.match( new RegExp( "\\.(" + param + ")$", "i" ) ); +}, $.validator.format( "Please enter a value with a valid extension." ) ); + +/** + * Dutch giro account numbers (not bank numbers) have max 7 digits + */ +$.validator.addMethod( "giroaccountNL", function( value, element ) { + return this.optional( element ) || /^[0-9]{1,7}$/.test( value ); +}, "Please specify a valid giro account number" ); + +/** + * IBAN is the international bank account number. + * It has a country - specific format, that is checked here too + * + * Validation is case-insensitive. Please make sure to normalize input yourself. + */ +$.validator.addMethod( "iban", function( value, element ) { + + // Some quick simple tests to prevent needless work + if ( this.optional( element ) ) { + return true; + } + + // Remove spaces and to upper case + var iban = value.replace( / /g, "" ).toUpperCase(), + ibancheckdigits = "", + leadingZeroes = true, + cRest = "", + cOperator = "", + countrycode, ibancheck, charAt, cChar, bbanpattern, bbancountrypatterns, ibanregexp, i, p; + + // Check for IBAN code length. + // It contains: + // country code ISO 3166-1 - two letters, + // two check digits, + // Basic Bank Account Number (BBAN) - up to 30 chars + var minimalIBANlength = 5; + if ( iban.length < minimalIBANlength ) { + return false; + } + + // Check the country code and find the country specific format + countrycode = iban.substring( 0, 2 ); + bbancountrypatterns = { + "AL": "\\d{8}[\\dA-Z]{16}", + "AD": "\\d{8}[\\dA-Z]{12}", + "AT": "\\d{16}", + "AZ": "[\\dA-Z]{4}\\d{20}", + "BE": "\\d{12}", + "BH": "[A-Z]{4}[\\dA-Z]{14}", + "BA": "\\d{16}", + "BR": "\\d{23}[A-Z][\\dA-Z]", + "BG": "[A-Z]{4}\\d{6}[\\dA-Z]{8}", + "CR": "\\d{17}", + "HR": "\\d{17}", + "CY": "\\d{8}[\\dA-Z]{16}", + "CZ": "\\d{20}", + "DK": "\\d{14}", + "DO": "[A-Z]{4}\\d{20}", + "EE": "\\d{16}", + "FO": "\\d{14}", + "FI": "\\d{14}", + "FR": "\\d{10}[\\dA-Z]{11}\\d{2}", + "GE": "[\\dA-Z]{2}\\d{16}", + "DE": "\\d{18}", + "GI": "[A-Z]{4}[\\dA-Z]{15}", + "GR": "\\d{7}[\\dA-Z]{16}", + "GL": "\\d{14}", + "GT": "[\\dA-Z]{4}[\\dA-Z]{20}", + "HU": "\\d{24}", + "IS": "\\d{22}", + "IE": "[\\dA-Z]{4}\\d{14}", + "IL": "\\d{19}", + "IT": "[A-Z]\\d{10}[\\dA-Z]{12}", + "KZ": "\\d{3}[\\dA-Z]{13}", + "KW": "[A-Z]{4}[\\dA-Z]{22}", + "LV": "[A-Z]{4}[\\dA-Z]{13}", + "LB": "\\d{4}[\\dA-Z]{20}", + "LI": "\\d{5}[\\dA-Z]{12}", + "LT": "\\d{16}", + "LU": "\\d{3}[\\dA-Z]{13}", + "MK": "\\d{3}[\\dA-Z]{10}\\d{2}", + "MT": "[A-Z]{4}\\d{5}[\\dA-Z]{18}", + "MR": "\\d{23}", + "MU": "[A-Z]{4}\\d{19}[A-Z]{3}", + "MC": "\\d{10}[\\dA-Z]{11}\\d{2}", + "MD": "[\\dA-Z]{2}\\d{18}", + "ME": "\\d{18}", + "NL": "[A-Z]{4}\\d{10}", + "NO": "\\d{11}", + "PK": "[\\dA-Z]{4}\\d{16}", + "PS": "[\\dA-Z]{4}\\d{21}", + "PL": "\\d{24}", + "PT": "\\d{21}", + "RO": "[A-Z]{4}[\\dA-Z]{16}", + "SM": "[A-Z]\\d{10}[\\dA-Z]{12}", + "SA": "\\d{2}[\\dA-Z]{18}", + "RS": "\\d{18}", + "SK": "\\d{20}", + "SI": "\\d{15}", + "ES": "\\d{20}", + "SE": "\\d{20}", + "CH": "\\d{5}[\\dA-Z]{12}", + "TN": "\\d{20}", + "TR": "\\d{5}[\\dA-Z]{17}", + "AE": "\\d{3}\\d{16}", + "GB": "[A-Z]{4}\\d{14}", + "VG": "[\\dA-Z]{4}\\d{16}" + }; + + bbanpattern = bbancountrypatterns[ countrycode ]; + + // As new countries will start using IBAN in the + // future, we only check if the countrycode is known. + // This prevents false negatives, while almost all + // false positives introduced by this, will be caught + // by the checksum validation below anyway. + // Strict checking should return FALSE for unknown + // countries. + if ( typeof bbanpattern !== "undefined" ) { + ibanregexp = new RegExp( "^[A-Z]{2}\\d{2}" + bbanpattern + "$", "" ); + if ( !( ibanregexp.test( iban ) ) ) { + return false; // Invalid country specific format + } + } + + // Now check the checksum, first convert to digits + ibancheck = iban.substring( 4, iban.length ) + iban.substring( 0, 4 ); + for ( i = 0; i < ibancheck.length; i++ ) { + charAt = ibancheck.charAt( i ); + if ( charAt !== "0" ) { + leadingZeroes = false; + } + if ( !leadingZeroes ) { + ibancheckdigits += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf( charAt ); + } + } + + // Calculate the result of: ibancheckdigits % 97 + for ( p = 0; p < ibancheckdigits.length; p++ ) { + cChar = ibancheckdigits.charAt( p ); + cOperator = "" + cRest + "" + cChar; + cRest = cOperator % 97; + } + return cRest === 1; +}, "Please specify a valid IBAN" ); + +$.validator.addMethod( "integer", function( value, element ) { + return this.optional( element ) || /^-?\d+$/.test( value ); +}, "A positive or negative non-decimal number please" ); + +$.validator.addMethod( "ipv4", function( value, element ) { + return this.optional( element ) || /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i.test( value ); +}, "Please enter a valid IP v4 address." ); + +$.validator.addMethod( "ipv6", function( value, element ) { + return this.optional( element ) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test( value ); +}, "Please enter a valid IP v6 address." ); + +$.validator.addMethod( "lettersonly", function( value, element ) { + return this.optional( element ) || /^[a-z]+$/i.test( value ); +}, "Letters only please" ); + +$.validator.addMethod( "letterswithbasicpunc", function( value, element ) { + return this.optional( element ) || /^[a-z\-.,()'"\s]+$/i.test( value ); +}, "Letters or punctuation only please" ); + +$.validator.addMethod( "mobileNL", function( value, element ) { + return this.optional( element ) || /^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)6((\s|\s?\-\s?)?[0-9]){8}$/.test( value ); +}, "Please specify a valid mobile number" ); + +/* For UK phone functions, do the following server side processing: + * Compare original input with this RegEx pattern: + * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ + * Extract $1 and set $prefix to '+44' if $1 is '44', otherwise set $prefix to '0' + * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. + * A number of very detailed GB telephone number RegEx patterns can also be found at: + * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers + */ +$.validator.addMethod( "mobileUK", function( phone_number, element ) { + phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); + return this.optional( element ) || phone_number.length > 9 && + phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?|0)7(?:[1345789]\d{2}|624)\s?\d{3}\s?\d{3})$/ ); +}, "Please specify a valid mobile number" ); + +$.validator.addMethod( "netmask", function( value, element ) { + return this.optional( element ) || /^(254|252|248|240|224|192|128)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)/i.test( value ); +}, "Please enter a valid netmask." ); + +/* + * The NIE (Número de Identificación de Extranjero) is a Spanish tax identification number assigned by the Spanish + * authorities to any foreigner. + * + * The NIE is the equivalent of a Spaniards Número de Identificación Fiscal (NIF) which serves as a fiscal + * identification number. The CIF number (Certificado de Identificación Fiscal) is equivalent to the NIF, but applies to + * companies rather than individuals. The NIE consists of an 'X' or 'Y' followed by 7 or 8 digits then another letter. + */ +$.validator.addMethod( "nieES", function( value, element ) { + "use strict"; + + if ( this.optional( element ) ) { + return true; + } + + var nieRegEx = new RegExp( /^[MXYZ]{1}[0-9]{7,8}[TRWAGMYFPDXBNJZSQVHLCKET]{1}$/gi ); + var validChars = "TRWAGMYFPDXBNJZSQVHLCKET", + letter = value.substr( value.length - 1 ).toUpperCase(), + number; + + value = value.toString().toUpperCase(); + + // Quick format test + if ( value.length > 10 || value.length < 9 || !nieRegEx.test( value ) ) { + return false; + } + + // X means same number + // Y means number + 10000000 + // Z means number + 20000000 + value = value.replace( /^[X]/, "0" ) + .replace( /^[Y]/, "1" ) + .replace( /^[Z]/, "2" ); + + number = value.length === 9 ? value.substr( 0, 8 ) : value.substr( 0, 9 ); + + return validChars.charAt( parseInt( number, 10 ) % 23 ) === letter; + +}, "Please specify a valid NIE number." ); + +/* + * The Número de Identificación Fiscal ( NIF ) is the way tax identification used in Spain for individuals + */ +$.validator.addMethod( "nifES", function( value, element ) { + "use strict"; + + if ( this.optional( element ) ) { + return true; + } + + value = value.toUpperCase(); + + // Basic format test + if ( !value.match( "((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)" ) ) { + return false; + } + + // Test NIF + if ( /^[0-9]{8}[A-Z]{1}$/.test( value ) ) { + return ( "TRWAGMYFPDXBNJZSQVHLCKE".charAt( value.substring( 8, 0 ) % 23 ) === value.charAt( 8 ) ); + } + + // Test specials NIF (starts with K, L or M) + if ( /^[KLM]{1}/.test( value ) ) { + return ( value[ 8 ] === "TRWAGMYFPDXBNJZSQVHLCKE".charAt( value.substring( 8, 1 ) % 23 ) ); + } + + return false; + +}, "Please specify a valid NIF number." ); + +/* + * Numer identyfikacji podatkowej ( NIP ) is the way tax identification used in Poland for companies + */ +$.validator.addMethod( "nipPL", function( value ) { + "use strict"; + + value = value.replace( /[^0-9]/g, "" ); + + if ( value.length !== 10 ) { + return false; + } + + var arrSteps = [ 6, 5, 7, 2, 3, 4, 5, 6, 7 ]; + var intSum = 0; + for ( var i = 0; i < 9; i++ ) { + intSum += arrSteps[ i ] * value[ i ]; + } + var int2 = intSum % 11; + var intControlNr = ( int2 === 10 ) ? 0 : int2; + + return ( intControlNr === parseInt( value[ 9 ], 10 ) ); +}, "Please specify a valid NIP number." ); + +$.validator.addMethod( "notEqualTo", function( value, element, param ) { + return this.optional( element ) || !$.validator.methods.equalTo.call( this, value, element, param ); +}, "Please enter a different value, values must not be the same." ); + +$.validator.addMethod( "nowhitespace", function( value, element ) { + return this.optional( element ) || /^\S+$/i.test( value ); +}, "No white space please" ); + +/** +* Return true if the field value matches the given format RegExp +* +* @example $.validator.methods.pattern("AR1004",element,/^AR\d{4}$/) +* @result true +* +* @example $.validator.methods.pattern("BR1004",element,/^AR\d{4}$/) +* @result false +* +* @name $.validator.methods.pattern +* @type Boolean +* @cat Plugins/Validate/Methods +*/ +$.validator.addMethod( "pattern", function( value, element, param ) { + if ( this.optional( element ) ) { + return true; + } + if ( typeof param === "string" ) { + param = new RegExp( "^(?:" + param + ")$" ); + } + return param.test( value ); +}, "Invalid format." ); + +/** + * Dutch phone numbers have 10 digits (or 11 and start with +31). + */ +$.validator.addMethod( "phoneNL", function( value, element ) { + return this.optional( element ) || /^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9]){8}$/.test( value ); +}, "Please specify a valid phone number." ); + +/* For UK phone functions, do the following server side processing: + * Compare original input with this RegEx pattern: + * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ + * Extract $1 and set $prefix to '+44' if $1 is '44', otherwise set $prefix to '0' + * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. + * A number of very detailed GB telephone number RegEx patterns can also be found at: + * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers + */ + +// Matches UK landline + mobile, accepting only 01-3 for landline or 07 for mobile to exclude many premium numbers +$.validator.addMethod( "phonesUK", function( phone_number, element ) { + phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); + return this.optional( element ) || phone_number.length > 9 && + phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|7(?:[1345789]\d{8}|624\d{6})))$/ ); +}, "Please specify a valid uk phone number" ); + +/* For UK phone functions, do the following server side processing: + * Compare original input with this RegEx pattern: + * ^\(?(?:(?:00\)?[\s\-]?\(?|\+)(44)\)?[\s\-]?\(?(?:0\)?[\s\-]?\(?)?|0)([1-9]\d{1,4}\)?[\s\d\-]+)$ + * Extract $1 and set $prefix to '+44' if $1 is '44', otherwise set $prefix to '0' + * Extract $2 and remove hyphens, spaces and parentheses. Phone number is combined $prefix and $2. + * A number of very detailed GB telephone number RegEx patterns can also be found at: + * http://www.aa-asterisk.org.uk/index.php/Regular_Expressions_for_Validating_and_Formatting_GB_Telephone_Numbers + */ +$.validator.addMethod( "phoneUK", function( phone_number, element ) { + phone_number = phone_number.replace( /\(|\)|\s+|-/g, "" ); + return this.optional( element ) || phone_number.length > 9 && + phone_number.match( /^(?:(?:(?:00\s?|\+)44\s?)|(?:\(?0))(?:\d{2}\)?\s?\d{4}\s?\d{4}|\d{3}\)?\s?\d{3}\s?\d{3,4}|\d{4}\)?\s?(?:\d{5}|\d{3}\s?\d{3})|\d{5}\)?\s?\d{4,5})$/ ); +}, "Please specify a valid phone number" ); + +/** + * Matches US phone number format + * + * where the area code may not start with 1 and the prefix may not start with 1 + * allows '-' or ' ' as a separator and allows parens around area code + * some people may want to put a '1' in front of their number + * + * 1(212)-999-2345 or + * 212 999 2344 or + * 212-999-0983 + * + * but not + * 111-123-5434 + * and not + * 212 123 4567 + */ +$.validator.addMethod( "phoneUS", function( phone_number, element ) { + phone_number = phone_number.replace( /\s+/g, "" ); + return this.optional( element ) || phone_number.length > 9 && + phone_number.match( /^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]([02-9]\d|1[02-9])-?\d{4}$/ ); +}, "Please specify a valid phone number" ); + +/* +* Valida CEPs do brasileiros: +* +* Formatos aceitos: +* 99999-999 +* 99.999-999 +* 99999999 +*/ +$.validator.addMethod( "postalcodeBR", function( cep_value, element ) { + return this.optional( element ) || /^\d{2}.\d{3}-\d{3}?$|^\d{5}-?\d{3}?$/.test( cep_value ); +}, "Informe um CEP válido." ); + +/** + * Matches a valid Canadian Postal Code + * + * @example jQuery.validator.methods.postalCodeCA( "H0H 0H0", element ) + * @result true + * + * @example jQuery.validator.methods.postalCodeCA( "H0H0H0", element ) + * @result false + * + * @name jQuery.validator.methods.postalCodeCA + * @type Boolean + * @cat Plugins/Validate/Methods + */ +$.validator.addMethod( "postalCodeCA", function( value, element ) { + return this.optional( element ) || /^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ] *\d[ABCEGHJKLMNPRSTVWXYZ]\d$/i.test( value ); +}, "Please specify a valid postal code" ); + +/* Matches Italian postcode (CAP) */ +$.validator.addMethod( "postalcodeIT", function( value, element ) { + return this.optional( element ) || /^\d{5}$/.test( value ); +}, "Please specify a valid postal code" ); + +$.validator.addMethod( "postalcodeNL", function( value, element ) { + return this.optional( element ) || /^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test( value ); +}, "Please specify a valid postal code" ); + +// Matches UK postcode. Does not match to UK Channel Islands that have their own postcodes (non standard UK) +$.validator.addMethod( "postcodeUK", function( value, element ) { + return this.optional( element ) || /^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\s?(0AA))$/i.test( value ); +}, "Please specify a valid UK postcode" ); + +/* + * Lets you say "at least X inputs that match selector Y must be filled." + * + * The end result is that neither of these inputs: + * + * + * + * + * ...will validate unless at least one of them is filled. + * + * partnumber: {require_from_group: [1,".productinfo"]}, + * description: {require_from_group: [1,".productinfo"]} + * + * options[0]: number of fields that must be filled in the group + * options[1]: CSS selector that defines the group of conditionally required fields + */ +$.validator.addMethod( "require_from_group", function( value, element, options ) { + var $fields = $( options[ 1 ], element.form ), + $fieldsFirst = $fields.eq( 0 ), + validator = $fieldsFirst.data( "valid_req_grp" ) ? $fieldsFirst.data( "valid_req_grp" ) : $.extend( {}, this ), + isValid = $fields.filter( function() { + return validator.elementValue( this ); + } ).length >= options[ 0 ]; + + // Store the cloned validator for future validation + $fieldsFirst.data( "valid_req_grp", validator ); + + // If element isn't being validated, run each require_from_group field's validation rules + if ( !$( element ).data( "being_validated" ) ) { + $fields.data( "being_validated", true ); + $fields.each( function() { + validator.element( this ); + } ); + $fields.data( "being_validated", false ); + } + return isValid; +}, $.validator.format( "Please fill at least {0} of these fields." ) ); + +/* + * Lets you say "either at least X inputs that match selector Y must be filled, + * OR they must all be skipped (left blank)." + * + * The end result, is that none of these inputs: + * + * + * + * + * + * ...will validate unless either at least two of them are filled, + * OR none of them are. + * + * partnumber: {skip_or_fill_minimum: [2,".productinfo"]}, + * description: {skip_or_fill_minimum: [2,".productinfo"]}, + * color: {skip_or_fill_minimum: [2,".productinfo"]} + * + * options[0]: number of fields that must be filled in the group + * options[1]: CSS selector that defines the group of conditionally required fields + * + */ +$.validator.addMethod( "skip_or_fill_minimum", function( value, element, options ) { + var $fields = $( options[ 1 ], element.form ), + $fieldsFirst = $fields.eq( 0 ), + validator = $fieldsFirst.data( "valid_skip" ) ? $fieldsFirst.data( "valid_skip" ) : $.extend( {}, this ), + numberFilled = $fields.filter( function() { + return validator.elementValue( this ); + } ).length, + isValid = numberFilled === 0 || numberFilled >= options[ 0 ]; + + // Store the cloned validator for future validation + $fieldsFirst.data( "valid_skip", validator ); + + // If element isn't being validated, run each skip_or_fill_minimum field's validation rules + if ( !$( element ).data( "being_validated" ) ) { + $fields.data( "being_validated", true ); + $fields.each( function() { + validator.element( this ); + } ); + $fields.data( "being_validated", false ); + } + return isValid; +}, $.validator.format( "Please either skip these fields or fill at least {0} of them." ) ); + +/* Validates US States and/or Territories by @jdforsythe + * Can be case insensitive or require capitalization - default is case insensitive + * Can include US Territories or not - default does not + * Can include US Military postal abbreviations (AA, AE, AP) - default does not + * + * Note: "States" always includes DC (District of Colombia) + * + * Usage examples: + * + * This is the default - case insensitive, no territories, no military zones + * stateInput: { + * caseSensitive: false, + * includeTerritories: false, + * includeMilitary: false + * } + * + * Only allow capital letters, no territories, no military zones + * stateInput: { + * caseSensitive: false + * } + * + * Case insensitive, include territories but not military zones + * stateInput: { + * includeTerritories: true + * } + * + * Only allow capital letters, include territories and military zones + * stateInput: { + * caseSensitive: true, + * includeTerritories: true, + * includeMilitary: true + * } + * + */ +$.validator.addMethod( "stateUS", function( value, element, options ) { + var isDefault = typeof options === "undefined", + caseSensitive = ( isDefault || typeof options.caseSensitive === "undefined" ) ? false : options.caseSensitive, + includeTerritories = ( isDefault || typeof options.includeTerritories === "undefined" ) ? false : options.includeTerritories, + includeMilitary = ( isDefault || typeof options.includeMilitary === "undefined" ) ? false : options.includeMilitary, + regex; + + if ( !includeTerritories && !includeMilitary ) { + regex = "^(A[KLRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$"; + } else if ( includeTerritories && includeMilitary ) { + regex = "^(A[AEKLPRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$"; + } else if ( includeTerritories ) { + regex = "^(A[KLRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$"; + } else { + regex = "^(A[AEKLPRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$"; + } + + regex = caseSensitive ? new RegExp( regex ) : new RegExp( regex, "i" ); + return this.optional( element ) || regex.test( value ); +}, "Please specify a valid state" ); + +// TODO check if value starts with <, otherwise don't try stripping anything +$.validator.addMethod( "strippedminlength", function( value, element, param ) { + return $( value ).text().length >= param; +}, $.validator.format( "Please enter at least {0} characters" ) ); + +$.validator.addMethod( "time", function( value, element ) { + return this.optional( element ) || /^([01]\d|2[0-3]|[0-9])(:[0-5]\d){1,2}$/.test( value ); +}, "Please enter a valid time, between 00:00 and 23:59" ); + +$.validator.addMethod( "time12h", function( value, element ) { + return this.optional( element ) || /^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test( value ); +}, "Please enter a valid time in 12-hour am/pm format" ); + +// Same as url, but TLD is optional +$.validator.addMethod( "url2", function( value, element ) { + return this.optional( element ) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test( value ); +}, $.validator.messages.url ); + +/** + * Return true, if the value is a valid vehicle identification number (VIN). + * + * Works with all kind of text inputs. + * + * @example + * @desc Declares a required input element whose value must be a valid vehicle identification number. + * + * @name $.validator.methods.vinUS + * @type Boolean + * @cat Plugins/Validate/Methods + */ +$.validator.addMethod( "vinUS", function( v ) { + if ( v.length !== 17 ) { + return false; + } + + var LL = [ "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" ], + VL = [ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 7, 9, 2, 3, 4, 5, 6, 7, 8, 9 ], + FL = [ 8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2 ], + rs = 0, + i, n, d, f, cd, cdv; + + for ( i = 0; i < 17; i++ ) { + f = FL[ i ]; + d = v.slice( i, i + 1 ); + if ( i === 8 ) { + cdv = d; + } + if ( !isNaN( d ) ) { + d *= f; + } else { + for ( n = 0; n < LL.length; n++ ) { + if ( d.toUpperCase() === LL[ n ] ) { + d = VL[ n ]; + d *= f; + if ( isNaN( cdv ) && n === 8 ) { + cdv = LL[ n ]; + } + break; + } + } + } + rs += d; + } + cd = rs % 11; + if ( cd === 10 ) { + cd = "X"; + } + if ( cd === cdv ) { + return true; + } + return false; +}, "The specified vehicle identification number (VIN) is invalid." ); + +$.validator.addMethod( "zipcodeUS", function( value, element ) { + return this.optional( element ) || /^\d{5}(-\d{4})?$/.test( value ); +}, "The specified US ZIP Code is invalid" ); + +$.validator.addMethod( "ziprange", function( value, element ) { + return this.optional( element ) || /^90[2-5]\d\{2\}-\d{4}$/.test( value ); +}, "Your ZIP-code must be in the range 902xx-xxxx to 905xx-xxxx" ); +return $; +})); \ No newline at end of file diff --git a/cicm_web/wwwroot/lib/jquery-validation/dist/additional-methods.min.js b/cicm_web/wwwroot/lib/jquery-validation/dist/additional-methods.min.js new file mode 100755 index 00000000..f4e9a239 --- /dev/null +++ b/cicm_web/wwwroot/lib/jquery-validation/dist/additional-methods.min.js @@ -0,0 +1,4 @@ +/*! jQuery Validation Plugin - v1.17.0 - 7/29/2017 + * https://jqueryvalidation.org/ + * Copyright (c) 2017 Jörn Zaefferer; Licensed MIT */ +!function(a){"function"==typeof define&&define.amd?define(["jquery","./jquery.validate.min"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){return function(){function b(a){return a.replace(/<.[^<>]*?>/g," ").replace(/ | /gi," ").replace(/[.(),;:!?%#$'\"_+=\/\-“”’]*/g,"")}a.validator.addMethod("maxWords",function(a,c,d){return this.optional(c)||b(a).match(/\b\w+\b/g).length<=d},a.validator.format("Please enter {0} words or less.")),a.validator.addMethod("minWords",function(a,c,d){return this.optional(c)||b(a).match(/\b\w+\b/g).length>=d},a.validator.format("Please enter at least {0} words.")),a.validator.addMethod("rangeWords",function(a,c,d){var e=b(a),f=/\b\w+\b/g;return this.optional(c)||e.match(f).length>=d[0]&&e.match(f).length<=d[1]},a.validator.format("Please enter between {0} and {1} words."))}(),a.validator.addMethod("accept",function(b,c,d){var e,f,g,h="string"==typeof d?d.replace(/\s/g,""):"image/*",i=this.optional(c);if(i)return i;if("file"===a(c).attr("type")&&(h=h.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g,"\\$&").replace(/,/g,"|").replace(/\/\*/g,"/.*"),c.files&&c.files.length))for(g=new RegExp(".?("+h+")$","i"),e=0;e9?"0":f,g="JABCDEFGHI".substr(f,1).toString(),i.match(/[ABEH]/)?k===f:i.match(/[KPQS]/)?k===g:k===f||k===g},"Please specify a valid CIF number."),a.validator.addMethod("cpfBR",function(a){if(a=a.replace(/([~!@#$%^&*()_+=`{}\[\]\-|\\:;'<>,.\/? ])+/g,""),11!==a.length)return!1;var b,c,d,e,f=0;if(b=parseInt(a.substring(9,10),10),c=parseInt(a.substring(10,11),10),d=function(a,b){var c=10*a%11;return 10!==c&&11!==c||(c=0),c===b},""===a||"00000000000"===a||"11111111111"===a||"22222222222"===a||"33333333333"===a||"44444444444"===a||"55555555555"===a||"66666666666"===a||"77777777777"===a||"88888888888"===a||"99999999999"===a)return!1;for(e=1;e<=9;e++)f+=parseInt(a.substring(e-1,e),10)*(11-e);if(d(f,b)){for(f=0,e=1;e<=10;e++)f+=parseInt(a.substring(e-1,e),10)*(12-e);return d(f,c)}return!1},"Please specify a valid CPF number"),a.validator.addMethod("creditcard",function(a,b){if(this.optional(b))return"dependency-mismatch";if(/[^0-9 \-]+/.test(a))return!1;var c,d,e=0,f=0,g=!1;if(a=a.replace(/\D/g,""),a.length<13||a.length>19)return!1;for(c=a.length-1;c>=0;c--)d=a.charAt(c),f=parseInt(d,10),g&&(f*=2)>9&&(f-=9),e+=f,g=!g;return e%10===0},"Please enter a valid credit card number."),a.validator.addMethod("creditcardtypes",function(a,b,c){if(/[^0-9\-]+/.test(a))return!1;a=a.replace(/\D/g,"");var d=0;return c.mastercard&&(d|=1),c.visa&&(d|=2),c.amex&&(d|=4),c.dinersclub&&(d|=8),c.enroute&&(d|=16),c.discover&&(d|=32),c.jcb&&(d|=64),c.unknown&&(d|=128),c.all&&(d=255),1&d&&/^(5[12345])/.test(a)?16===a.length:2&d&&/^(4)/.test(a)?16===a.length:4&d&&/^(3[47])/.test(a)?15===a.length:8&d&&/^(3(0[012345]|[68]))/.test(a)?14===a.length:16&d&&/^(2(014|149))/.test(a)?15===a.length:32&d&&/^(6011)/.test(a)?16===a.length:64&d&&/^(3)/.test(a)?16===a.length:64&d&&/^(2131|1800)/.test(a)?15===a.length:!!(128&d)},"Please enter a valid credit card number."),a.validator.addMethod("currency",function(a,b,c){var d,e="string"==typeof c,f=e?c:c[0],g=!!e||c[1];return f=f.replace(/,/g,""),f=g?f+"]":f+"]?",d="^["+f+"([1-9]{1}[0-9]{0,2}(\\,[0-9]{3})*(\\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\\.[0-9]{0,2})?|0(\\.[0-9]{0,2})?|(\\.[0-9]{1,2})?)$",d=new RegExp(d),this.optional(b)||d.test(a)},"Please specify a valid currency"),a.validator.addMethod("dateFA",function(a,b){return this.optional(b)||/^[1-4]\d{3}\/((0?[1-6]\/((3[0-1])|([1-2][0-9])|(0?[1-9])))|((1[0-2]|(0?[7-9]))\/(30|([1-2][0-9])|(0?[1-9]))))$/.test(a)},a.validator.messages.date),a.validator.addMethod("dateITA",function(a,b){var c,d,e,f,g,h=!1,i=/^\d{1,2}\/\d{1,2}\/\d{4}$/;return i.test(a)?(c=a.split("/"),d=parseInt(c[0],10),e=parseInt(c[1],10),f=parseInt(c[2],10),g=new Date(Date.UTC(f,e-1,d,12,0,0,0)),h=g.getUTCFullYear()===f&&g.getUTCMonth()===e-1&&g.getUTCDate()===d):h=!1,this.optional(b)||h},a.validator.messages.date),a.validator.addMethod("dateNL",function(a,b){return this.optional(b)||/^(0?[1-9]|[12]\d|3[01])[\.\/\-](0?[1-9]|1[012])[\.\/\-]([12]\d)?(\d\d)$/.test(a)},a.validator.messages.date),a.validator.addMethod("extension",function(a,b,c){return c="string"==typeof c?c.replace(/,/g,"|"):"png|jpe?g|gif",this.optional(b)||a.match(new RegExp("\\.("+c+")$","i"))},a.validator.format("Please enter a value with a valid extension.")),a.validator.addMethod("giroaccountNL",function(a,b){return this.optional(b)||/^[0-9]{1,7}$/.test(a)},"Please specify a valid giro account number"),a.validator.addMethod("iban",function(a,b){if(this.optional(b))return!0;var c,d,e,f,g,h,i,j,k,l=a.replace(/ /g,"").toUpperCase(),m="",n=!0,o="",p="",q=5;if(l.length9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)7(?:[1345789]\d{2}|624)\s?\d{3}\s?\d{3})$/)},"Please specify a valid mobile number"),a.validator.addMethod("netmask",function(a,b){return this.optional(b)||/^(254|252|248|240|224|192|128)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)/i.test(a)},"Please enter a valid netmask."),a.validator.addMethod("nieES",function(a,b){"use strict";if(this.optional(b))return!0;var c,d=new RegExp(/^[MXYZ]{1}[0-9]{7,8}[TRWAGMYFPDXBNJZSQVHLCKET]{1}$/gi),e="TRWAGMYFPDXBNJZSQVHLCKET",f=a.substr(a.length-1).toUpperCase();return a=a.toString().toUpperCase(),!(a.length>10||a.length<9||!d.test(a))&&(a=a.replace(/^[X]/,"0").replace(/^[Y]/,"1").replace(/^[Z]/,"2"),c=9===a.length?a.substr(0,8):a.substr(0,9),e.charAt(parseInt(c,10)%23)===f)},"Please specify a valid NIE number."),a.validator.addMethod("nifES",function(a,b){"use strict";return!!this.optional(b)||(a=a.toUpperCase(),!!a.match("((^[A-Z]{1}[0-9]{7}[A-Z0-9]{1}$|^[T]{1}[A-Z0-9]{8}$)|^[0-9]{8}[A-Z]{1}$)")&&(/^[0-9]{8}[A-Z]{1}$/.test(a)?"TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.substring(8,0)%23)===a.charAt(8):!!/^[KLM]{1}/.test(a)&&a[8]==="TRWAGMYFPDXBNJZSQVHLCKE".charAt(a.substring(8,1)%23)))},"Please specify a valid NIF number."),a.validator.addMethod("nipPL",function(a){"use strict";if(a=a.replace(/[^0-9]/g,""),10!==a.length)return!1;for(var b=[6,5,7,2,3,4,5,6,7],c=0,d=0;d<9;d++)c+=b[d]*a[d];var e=c%11,f=10===e?0:e;return f===parseInt(a[9],10)},"Please specify a valid NIP number."),a.validator.addMethod("notEqualTo",function(b,c,d){return this.optional(c)||!a.validator.methods.equalTo.call(this,b,c,d)},"Please enter a different value, values must not be the same."),a.validator.addMethod("nowhitespace",function(a,b){return this.optional(b)||/^\S+$/i.test(a)},"No white space please"),a.validator.addMethod("pattern",function(a,b,c){return!!this.optional(b)||("string"==typeof c&&(c=new RegExp("^(?:"+c+")$")),c.test(a))},"Invalid format."),a.validator.addMethod("phoneNL",function(a,b){return this.optional(b)||/^((\+|00(\s|\s?\-\s?)?)31(\s|\s?\-\s?)?(\(0\)[\-\s]?)?|0)[1-9]((\s|\s?\-\s?)?[0-9]){8}$/.test(a)},"Please specify a valid phone number."),a.validator.addMethod("phonesUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?|0)(?:1\d{8,9}|[23]\d{9}|7(?:[1345789]\d{8}|624\d{6})))$/)},"Please specify a valid uk phone number"),a.validator.addMethod("phoneUK",function(a,b){return a=a.replace(/\(|\)|\s+|-/g,""),this.optional(b)||a.length>9&&a.match(/^(?:(?:(?:00\s?|\+)44\s?)|(?:\(?0))(?:\d{2}\)?\s?\d{4}\s?\d{4}|\d{3}\)?\s?\d{3}\s?\d{3,4}|\d{4}\)?\s?(?:\d{5}|\d{3}\s?\d{3})|\d{5}\)?\s?\d{4,5})$/)},"Please specify a valid phone number"),a.validator.addMethod("phoneUS",function(a,b){return a=a.replace(/\s+/g,""),this.optional(b)||a.length>9&&a.match(/^(\+?1-?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?[2-9]([02-9]\d|1[02-9])-?\d{4}$/)},"Please specify a valid phone number"),a.validator.addMethod("postalcodeBR",function(a,b){return this.optional(b)||/^\d{2}.\d{3}-\d{3}?$|^\d{5}-?\d{3}?$/.test(a)},"Informe um CEP válido."),a.validator.addMethod("postalCodeCA",function(a,b){return this.optional(b)||/^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ] *\d[ABCEGHJKLMNPRSTVWXYZ]\d$/i.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postalcodeIT",function(a,b){return this.optional(b)||/^\d{5}$/.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postalcodeNL",function(a,b){return this.optional(b)||/^[1-9][0-9]{3}\s?[a-zA-Z]{2}$/.test(a)},"Please specify a valid postal code"),a.validator.addMethod("postcodeUK",function(a,b){return this.optional(b)||/^((([A-PR-UWYZ][0-9])|([A-PR-UWYZ][0-9][0-9])|([A-PR-UWYZ][A-HK-Y][0-9])|([A-PR-UWYZ][A-HK-Y][0-9][0-9])|([A-PR-UWYZ][0-9][A-HJKSTUW])|([A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRVWXY]))\s?([0-9][ABD-HJLNP-UW-Z]{2})|(GIR)\s?(0AA))$/i.test(a)},"Please specify a valid UK postcode"),a.validator.addMethod("require_from_group",function(b,c,d){var e=a(d[1],c.form),f=e.eq(0),g=f.data("valid_req_grp")?f.data("valid_req_grp"):a.extend({},this),h=e.filter(function(){return g.elementValue(this)}).length>=d[0];return f.data("valid_req_grp",g),a(c).data("being_validated")||(e.data("being_validated",!0),e.each(function(){g.element(this)}),e.data("being_validated",!1)),h},a.validator.format("Please fill at least {0} of these fields.")),a.validator.addMethod("skip_or_fill_minimum",function(b,c,d){var e=a(d[1],c.form),f=e.eq(0),g=f.data("valid_skip")?f.data("valid_skip"):a.extend({},this),h=e.filter(function(){return g.elementValue(this)}).length,i=0===h||h>=d[0];return f.data("valid_skip",g),a(c).data("being_validated")||(e.data("being_validated",!0),e.each(function(){g.element(this)}),e.data("being_validated",!1)),i},a.validator.format("Please either skip these fields or fill at least {0} of them.")),a.validator.addMethod("stateUS",function(a,b,c){var d,e="undefined"==typeof c,f=!e&&"undefined"!=typeof c.caseSensitive&&c.caseSensitive,g=!e&&"undefined"!=typeof c.includeTerritories&&c.includeTerritories,h=!e&&"undefined"!=typeof c.includeMilitary&&c.includeMilitary;return d=g||h?g&&h?"^(A[AEKLPRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$":g?"^(A[KLRSZ]|C[AOT]|D[CE]|FL|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEINOPST]|N[CDEHJMVY]|O[HKR]|P[AR]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$":"^(A[AEKLPRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$":"^(A[KLRZ]|C[AOT]|D[CE]|FL|GA|HI|I[ADLN]|K[SY]|LA|M[ADEINOST]|N[CDEHJMVY]|O[HKR]|PA|RI|S[CD]|T[NX]|UT|V[AT]|W[AIVY])$",d=f?new RegExp(d):new RegExp(d,"i"),this.optional(b)||d.test(a)},"Please specify a valid state"),a.validator.addMethod("strippedminlength",function(b,c,d){return a(b).text().length>=d},a.validator.format("Please enter at least {0} characters")),a.validator.addMethod("time",function(a,b){return this.optional(b)||/^([01]\d|2[0-3]|[0-9])(:[0-5]\d){1,2}$/.test(a)},"Please enter a valid time, between 00:00 and 23:59"),a.validator.addMethod("time12h",function(a,b){return this.optional(b)||/^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test(a)},"Please enter a valid time in 12-hour am/pm format"),a.validator.addMethod("url2",function(a,b){return this.optional(b)||/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)*(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(a)},a.validator.messages.url),a.validator.addMethod("vinUS",function(a){if(17!==a.length)return!1;var b,c,d,e,f,g,h=["A","B","C","D","E","F","G","H","J","K","L","M","N","P","R","S","T","U","V","W","X","Y","Z"],i=[1,2,3,4,5,6,7,8,1,2,3,4,5,7,9,2,3,4,5,6,7,8,9],j=[8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2],k=0;for(b=0;b<17;b++){if(e=j[b],d=a.slice(b,b+1),8===b&&(g=d),isNaN(d)){for(c=0;c