Support primary extension

This commit is contained in:
snow flurry 2023-10-20 14:32:56 -07:00
parent bff4b2675c
commit fa882a1f4a
13 changed files with 870 additions and 11 deletions

View file

@ -0,0 +1,7 @@
namespace PhoneToolMX.Models
{
public class AllowNullAttribute : Attribute
{
}
}

View file

@ -38,6 +38,20 @@ namespace PhoneToolMX.Data {
var entry = await AddAsync(entity);
return entry;
}
/// <summary>
/// Gets a DbSet with all required relationships
/// </summary>
/// <typeparam name="TEntity">IOwnedModel</typeparam>
/// <returns>DbSet&lt;TEntity&gt;</returns>
private IQueryable<TEntity> GetFullSet<TEntity>() where TEntity: class, IModel
{
var set = Set<TEntity>().AsQueryable();
return typeof(TEntity).GetProperties()
.Where(p => p.GetCustomAttributes(typeof(AlwaysIncludeAttribute), true)
.Length != 0)
.Aggregate(set, (current, prop) => current.Include(prop.Name));
}
/// <summary>
/// Gets all entities of a certain model owned by the <see cref="User"/>.
@ -48,14 +62,11 @@ namespace PhoneToolMX.Data {
public ICollection<TEntity> GetOwned<TEntity>(User owner) where TEntity : class, IOwnedModel
{
if (owner == null) return null;
var entity = Set<TEntity>().Where(x => x.Owners.Any(o => o.Id == owner.Id));
var entity = GetFullSet<TEntity>().Where(x => x.Owners.Any(o => o.Id == owner.Id));
// eager load all w/ AlwaysInclude
entity = typeof(TEntity).GetProperties()
.Where(p => p.GetCustomAttributes(typeof(AlwaysIncludeAttribute), true)
.Length != 0)
.Aggregate(entity, (current, prop) => current.Include(prop.Name));
return entity.ToList();
}
/// <summary>
/// Gets a model entity by its Id
@ -67,7 +78,7 @@ namespace PhoneToolMX.Data {
/// </remarks>
public TEntity GetEntityById<TEntity>(int? id) where TEntity : class, IModel
{
return id == null ? null : Set<TEntity>().FirstOrDefault(o => o.Id == id);
return id == null ? null : GetFullSet<TEntity>().FirstOrDefault(o => o.Id == id);
}
#endregion
@ -79,6 +90,7 @@ namespace PhoneToolMX.Data {
ext.HasKey(x => x.Id);
ext.Property(x => x.Id).UseIdentityColumn();
ext.Property(x => x.ExtId).HasComputedColumnSql("\"Id\" + 1000", stored: true);
// Randomly generates a 24-char password
ext
.Property(p => p.Password)
.HasDefaultValueSql("encode(gen_random_bytes(18), 'base64')");
@ -89,7 +101,7 @@ namespace PhoneToolMX.Data {
.WithMany(x => x.Phones);
phone.HasKey(p => p.Id);
phone.Property(p => p.Id).UseIdentityColumn();
// Randomly generates a 24-char password
phone.HasOne(p => p.PrimaryExtension).WithMany();
// Wallpapers, ringtones, etc
var cd = modelBuilder.Entity<CustomData>();

View file

@ -0,0 +1,361 @@
// <auto-generated />
using System;
using System.Net.NetworkInformation;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using PhoneToolMX.Data;
#nullable disable
namespace PhoneToolMX.Models.Migrations
{
[DbContext(typeof(PTMXContext))]
[Migration("20231020205952_PrimaryExtension")]
partial class PrimaryExtension
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.23")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("ExtensionPhone", b =>
{
b.Property<int>("ExtensionsId")
.HasColumnType("integer");
b.Property<int>("PhonesId")
.HasColumnType("integer");
b.HasKey("ExtensionsId", "PhonesId");
b.HasIndex("PhonesId");
b.ToTable("ExtensionPhone");
});
modelBuilder.Entity("ExtensionUser", b =>
{
b.Property<int>("ExtensionsId")
.HasColumnType("integer");
b.Property<string>("OwnersId")
.HasColumnType("text");
b.HasKey("ExtensionsId", "OwnersId");
b.HasIndex("OwnersId");
b.ToTable("ExtensionUser");
});
modelBuilder.Entity("PhoneToolMX.Models.CustomData", b =>
{
b.Property<int?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int?>("Id"));
b.Property<byte[]>("Data")
.HasColumnType("bytea");
b.Property<int>("DataType")
.HasColumnType("integer");
b.Property<string>("FriendlyName")
.HasMaxLength(16)
.HasColumnType("character varying(16)");
b.Property<int?>("PhoneId")
.HasColumnType("integer");
b.Property<long>("Size")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("PhoneId");
b.ToTable("CustomData");
});
modelBuilder.Entity("PhoneToolMX.Models.Extension", b =>
{
b.Property<int?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int?>("Id"));
b.Property<string>("DirectoryName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ExtId")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("integer")
.HasComputedColumnSql("\"Id\" + 1000", true);
b.Property<int?>("HoldMusicId")
.HasColumnType("integer");
b.Property<bool>("Listed")
.HasColumnType("boolean");
b.Property<string>("Password")
.ValueGeneratedOnAdd()
.HasColumnType("text")
.HasDefaultValueSql("encode(gen_random_bytes(18), 'base64')");
b.HasKey("Id");
b.HasIndex("HoldMusicId");
b.ToTable("Extensions");
});
modelBuilder.Entity("PhoneToolMX.Models.Phone", b =>
{
b.Property<int?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int?>("Id"));
b.Property<int?>("BackgroundId")
.HasColumnType("integer");
b.Property<string>("FriendlyName")
.IsRequired()
.HasColumnType("text");
b.Property<PhysicalAddress>("MacAddress")
.IsRequired()
.HasColumnType("macaddr");
b.Property<int>("ModelId")
.HasColumnType("integer");
b.Property<int?>("PrimaryExtension")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("BackgroundId");
b.HasIndex("ModelId");
b.ToTable("Phones");
});
modelBuilder.Entity("PhoneToolMX.Models.PhoneModel", b =>
{
b.Property<int?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int?>("Id"));
b.Property<long>("MaxExtensions")
.HasColumnType("bigint");
b.Property<string>("ModelName")
.HasColumnType("text");
b.Property<bool>("PreVvxPolycom")
.HasColumnType("boolean");
b.HasKey("Id");
b.ToTable("PhoneModels");
b.HasData(
new
{
Id = 0,
MaxExtensions = 6L,
ModelName = "Polycom VVX300/310",
PreVvxPolycom = false
});
});
modelBuilder.Entity("PhoneToolMX.Models.Role", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("NormalizedName")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Role");
});
modelBuilder.Entity("PhoneToolMX.Models.User", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.HasColumnType("text");
b.Property<string>("Email")
.HasColumnType("text");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasColumnType("text");
b.Property<string>("NormalizedUserName")
.HasColumnType("text");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("PhoneUser", b =>
{
b.Property<string>("OwnersId")
.HasColumnType("text");
b.Property<int>("PhonesId")
.HasColumnType("integer");
b.HasKey("OwnersId", "PhonesId");
b.HasIndex("PhonesId");
b.ToTable("PhoneUser");
});
modelBuilder.Entity("ExtensionPhone", b =>
{
b.HasOne("PhoneToolMX.Models.Extension", null)
.WithMany()
.HasForeignKey("ExtensionsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PhoneToolMX.Models.Phone", null)
.WithMany()
.HasForeignKey("PhonesId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ExtensionUser", b =>
{
b.HasOne("PhoneToolMX.Models.Extension", null)
.WithMany()
.HasForeignKey("ExtensionsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PhoneToolMX.Models.User", null)
.WithMany()
.HasForeignKey("OwnersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("PhoneToolMX.Models.CustomData", b =>
{
b.HasOne("PhoneToolMX.Models.Phone", null)
.WithMany("Ringtones")
.HasForeignKey("PhoneId");
});
modelBuilder.Entity("PhoneToolMX.Models.Extension", b =>
{
b.HasOne("PhoneToolMX.Models.CustomData", "HoldMusic")
.WithMany()
.HasForeignKey("HoldMusicId");
b.Navigation("HoldMusic");
});
modelBuilder.Entity("PhoneToolMX.Models.Phone", b =>
{
b.HasOne("PhoneToolMX.Models.CustomData", "Background")
.WithMany()
.HasForeignKey("BackgroundId");
b.HasOne("PhoneToolMX.Models.PhoneModel", "Model")
.WithMany()
.HasForeignKey("ModelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Background");
b.Navigation("Model");
});
modelBuilder.Entity("PhoneUser", b =>
{
b.HasOne("PhoneToolMX.Models.User", null)
.WithMany()
.HasForeignKey("OwnersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PhoneToolMX.Models.Phone", null)
.WithMany()
.HasForeignKey("PhonesId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("PhoneToolMX.Models.Phone", b =>
{
b.Navigation("Ringtones");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace PhoneToolMX.Models.Migrations
{
public partial class PrimaryExtension : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "PrimaryExtension",
table: "Phones",
type: "integer",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PrimaryExtension",
table: "Phones");
}
}
}

View file

@ -0,0 +1,369 @@
// <auto-generated />
using System;
using System.Net.NetworkInformation;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using PhoneToolMX.Data;
#nullable disable
namespace PhoneToolMX.Models.Migrations
{
[DbContext(typeof(PTMXContext))]
[Migration("20231020213049_PrimaryExtensionAsExtension")]
partial class PrimaryExtensionAsExtension
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.23")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("ExtensionPhone", b =>
{
b.Property<int>("ExtensionsId")
.HasColumnType("integer");
b.Property<int>("PhonesId")
.HasColumnType("integer");
b.HasKey("ExtensionsId", "PhonesId");
b.HasIndex("PhonesId");
b.ToTable("ExtensionPhone");
});
modelBuilder.Entity("ExtensionUser", b =>
{
b.Property<int>("ExtensionsId")
.HasColumnType("integer");
b.Property<string>("OwnersId")
.HasColumnType("text");
b.HasKey("ExtensionsId", "OwnersId");
b.HasIndex("OwnersId");
b.ToTable("ExtensionUser");
});
modelBuilder.Entity("PhoneToolMX.Models.CustomData", b =>
{
b.Property<int?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int?>("Id"));
b.Property<byte[]>("Data")
.HasColumnType("bytea");
b.Property<int>("DataType")
.HasColumnType("integer");
b.Property<string>("FriendlyName")
.HasMaxLength(16)
.HasColumnType("character varying(16)");
b.Property<int?>("PhoneId")
.HasColumnType("integer");
b.Property<long>("Size")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("PhoneId");
b.ToTable("CustomData");
});
modelBuilder.Entity("PhoneToolMX.Models.Extension", b =>
{
b.Property<int?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int?>("Id"));
b.Property<string>("DirectoryName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ExtId")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("integer")
.HasComputedColumnSql("\"Id\" + 1000", true);
b.Property<int?>("HoldMusicId")
.HasColumnType("integer");
b.Property<bool>("Listed")
.HasColumnType("boolean");
b.Property<string>("Password")
.ValueGeneratedOnAdd()
.HasColumnType("text")
.HasDefaultValueSql("encode(gen_random_bytes(18), 'base64')");
b.HasKey("Id");
b.HasIndex("HoldMusicId");
b.ToTable("Extensions");
});
modelBuilder.Entity("PhoneToolMX.Models.Phone", b =>
{
b.Property<int?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int?>("Id"));
b.Property<int?>("BackgroundId")
.HasColumnType("integer");
b.Property<string>("FriendlyName")
.IsRequired()
.HasColumnType("text");
b.Property<PhysicalAddress>("MacAddress")
.IsRequired()
.HasColumnType("macaddr");
b.Property<int>("ModelId")
.HasColumnType("integer");
b.Property<int?>("PrimaryExtensionId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("BackgroundId");
b.HasIndex("ModelId");
b.HasIndex("PrimaryExtensionId");
b.ToTable("Phones");
});
modelBuilder.Entity("PhoneToolMX.Models.PhoneModel", b =>
{
b.Property<int?>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int?>("Id"));
b.Property<long>("MaxExtensions")
.HasColumnType("bigint");
b.Property<string>("ModelName")
.HasColumnType("text");
b.Property<bool>("PreVvxPolycom")
.HasColumnType("boolean");
b.HasKey("Id");
b.ToTable("PhoneModels");
b.HasData(
new
{
Id = 0,
MaxExtensions = 6L,
ModelName = "Polycom VVX300/310",
PreVvxPolycom = false
});
});
modelBuilder.Entity("PhoneToolMX.Models.Role", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("NormalizedName")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Role");
});
modelBuilder.Entity("PhoneToolMX.Models.User", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.HasColumnType("text");
b.Property<string>("Email")
.HasColumnType("text");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasColumnType("text");
b.Property<string>("NormalizedUserName")
.HasColumnType("text");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("PhoneUser", b =>
{
b.Property<string>("OwnersId")
.HasColumnType("text");
b.Property<int>("PhonesId")
.HasColumnType("integer");
b.HasKey("OwnersId", "PhonesId");
b.HasIndex("PhonesId");
b.ToTable("PhoneUser");
});
modelBuilder.Entity("ExtensionPhone", b =>
{
b.HasOne("PhoneToolMX.Models.Extension", null)
.WithMany()
.HasForeignKey("ExtensionsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PhoneToolMX.Models.Phone", null)
.WithMany()
.HasForeignKey("PhonesId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ExtensionUser", b =>
{
b.HasOne("PhoneToolMX.Models.Extension", null)
.WithMany()
.HasForeignKey("ExtensionsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PhoneToolMX.Models.User", null)
.WithMany()
.HasForeignKey("OwnersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("PhoneToolMX.Models.CustomData", b =>
{
b.HasOne("PhoneToolMX.Models.Phone", null)
.WithMany("Ringtones")
.HasForeignKey("PhoneId");
});
modelBuilder.Entity("PhoneToolMX.Models.Extension", b =>
{
b.HasOne("PhoneToolMX.Models.CustomData", "HoldMusic")
.WithMany()
.HasForeignKey("HoldMusicId");
b.Navigation("HoldMusic");
});
modelBuilder.Entity("PhoneToolMX.Models.Phone", b =>
{
b.HasOne("PhoneToolMX.Models.CustomData", "Background")
.WithMany()
.HasForeignKey("BackgroundId");
b.HasOne("PhoneToolMX.Models.PhoneModel", "Model")
.WithMany()
.HasForeignKey("ModelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PhoneToolMX.Models.Extension", "PrimaryExtension")
.WithMany()
.HasForeignKey("PrimaryExtensionId");
b.Navigation("Background");
b.Navigation("Model");
b.Navigation("PrimaryExtension");
});
modelBuilder.Entity("PhoneUser", b =>
{
b.HasOne("PhoneToolMX.Models.User", null)
.WithMany()
.HasForeignKey("OwnersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PhoneToolMX.Models.Phone", null)
.WithMany()
.HasForeignKey("PhonesId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("PhoneToolMX.Models.Phone", b =>
{
b.Navigation("Ringtones");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,45 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace PhoneToolMX.Models.Migrations
{
public partial class PrimaryExtensionAsExtension : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "PrimaryExtension",
table: "Phones",
newName: "PrimaryExtensionId");
migrationBuilder.CreateIndex(
name: "IX_Phones_PrimaryExtensionId",
table: "Phones",
column: "PrimaryExtensionId");
migrationBuilder.AddForeignKey(
name: "FK_Phones_Extensions_PrimaryExtensionId",
table: "Phones",
column: "PrimaryExtensionId",
principalTable: "Extensions",
principalColumn: "Id");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Phones_Extensions_PrimaryExtensionId",
table: "Phones");
migrationBuilder.DropIndex(
name: "IX_Phones_PrimaryExtensionId",
table: "Phones");
migrationBuilder.RenameColumn(
name: "PrimaryExtensionId",
table: "Phones",
newName: "PrimaryExtension");
}
}
}

View file

@ -141,12 +141,17 @@ namespace PhoneToolMX.Models.Migrations
b.Property<int>("ModelId")
.HasColumnType("integer");
b.Property<int?>("PrimaryExtensionId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("BackgroundId");
b.HasIndex("ModelId");
b.HasIndex("PrimaryExtensionId");
b.ToTable("Phones");
});
@ -326,9 +331,15 @@ namespace PhoneToolMX.Models.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PhoneToolMX.Models.Extension", "PrimaryExtension")
.WithMany()
.HasForeignKey("PrimaryExtensionId");
b.Navigation("Background");
b.Navigation("Model");
b.Navigation("PrimaryExtension");
});
modelBuilder.Entity("PhoneUser", b =>

View file

@ -12,6 +12,8 @@ namespace PhoneToolMX.Models
foreach (var prop in GetType().GetProperties().Where(p => p.CanWrite)) {
if (prop.GetValue(obj, null) is {} propVal) {
prop.SetValue(this, propVal, null);
} else if (prop.GetCustomAttributes(typeof(AllowNullAttribute)).FirstOrDefault() != null) {
prop.SetValue(this, null, null);
}
}
}

View file

@ -21,7 +21,10 @@ namespace PhoneToolMX.Models {
[AlwaysInclude]
public ICollection<Extension> Extensions { get; set; }
public CustomData Background { get; set; }
public ICollection<CustomData> Ringtones { get; set; }
public Extension PrimaryExtension { get; set; }
}
}

View file

@ -1,6 +1,4 @@
using PhoneToolMX.Data;
using PhoneToolMX.Models;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Net.NetworkInformation;
@ -25,18 +23,25 @@ namespace PhoneToolMX.Models.ViewModels
public ICollection<int?> Extensions { get; set; }
[AllowNull]
public int? PrimaryExtension { get; set; }
public int MaxExtensions { get; set; }
public IOwnedModel ToEntity(PTMXContext ctx)
{
var exts = Extensions?.Select(x => ctx.Extensions.FirstOrDefault(e => e.Id == x)).ToList();
return new Phone
{
Id = Id,
MacAddress = PhysicalAddress.Parse(MacAddress),
FriendlyName = FriendlyName,
Model = ctx.PhoneModels.FirstOrDefault(p => p.Id == Model)!,
Extensions = Extensions?.Select(x => ctx.Extensions.FirstOrDefault(e => e.Id == x)).ToList(),
Extensions = exts,
Owners = new List<User>(),
PrimaryExtension = PrimaryExtension != null
? exts?.FirstOrDefault(x => x.Id == PrimaryExtension)
: null,
};
}
@ -58,6 +63,7 @@ namespace PhoneToolMX.Models.ViewModels
Model = phoneEnt.Model!.Id,
Extensions = phoneEnt.Extensions?.Select(x => x.Id).ToList(),
MaxExtensions = (int)phoneEnt.Model!.MaxExtensions,
PrimaryExtension = phoneEnt.PrimaryExtension?.Id,
};
}
}

View file

@ -17,8 +17,10 @@ namespace PhoneToolMX.Controllers
{
var myExts = _context.GetOwned<Extension>(await CurrentUser());
var phoneModels = _context.PhoneModels.ToList();
var selectedExts = pvm?.Extensions == null ? null : myExts.Where(x => pvm.Extensions.Contains(x.Id)).ToList();
ViewBag.MyExtensions = myExts;
ViewBag.SelectedExtensions = pvm?.Extensions == null ? null : myExts.Where(x => pvm.Extensions.Contains(x.Id)).ToList();
ViewBag.SelectedExtensions = selectedExts;
ViewBag.PrimaryExtension = selectedExts?.FirstOrDefault(x => x.Id == (pvm.PrimaryExtension ?? -1));
ViewBag.ModelNumbers = phoneModels;
ViewBag.CurrentModel = pvm?.Model == null ? null : phoneModels.Where(m => m.Id == pvm.Model);
}

View file

@ -24,6 +24,14 @@
<th>@Html.LabelFor(m => m.Extensions, "Extensions:")</th>
<td>@Html.ListBoxFor(m => m.Extensions, new MultiSelectList(ViewBag.MyExtensions, "Id", "ListViewName", ViewBag.SelectedExtensions))
</tr>
<tr>
<th>@Html.LabelFor(m => m.PrimaryExtension, "Primary Extension:")</th>
<td>@if (ViewBag.SelectedExtensions == null || ((ICollection<Extension>)ViewBag.SelectedExtensions).Count == 0) {
@Html.DropDownListFor(m => m.PrimaryExtension, new List<SelectListItem>(), "-- No Extensions Selected --", new { @disabled=true })
} else {
@Html.DropDownListFor(m => m.PrimaryExtension, new SelectList(ViewBag.SelectedExtensions, "Id", "ListViewName", ViewBag.PrimaryExtension), "None")
}</td>
</tr>
<tr>
<td colspan="2" align="right">
<input type="reset" value="Reset" />

View file

@ -54,6 +54,14 @@ namespace PolyProv.Controllers
public IActionResult PhoneSettings(string addr)
{
var phone = GetByMacStr(addr);
// ReSharper disable once InvertIf
if (phone?.PrimaryExtension is {} primary && phone.Extensions.FirstOrDefault() != primary) {
// primary extension exists and is not first, reorder so it's first
phone.Extensions.Remove(primary);
phone.Extensions = phone.Extensions.Prepend(primary).ToList();
}
return phone != null ? View("Phone", phone) : NotFound();
}