fixed web controller pages

This commit is contained in:
adam 2025-06-12 00:40:37 -04:00
parent a8bf8a8488
commit b859d99c92
12 changed files with 586 additions and 78 deletions

View File

@ -0,0 +1,389 @@
// <auto-generated />
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
#pragma warning disable 612, 618, 8981
[DbContext(typeof(ChattingContext))]
[Migration("20250605145513_datamemos-more-data")]
partial class datamemosmoredata
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore");
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("AccountUAC", b =>
{
b.Property<Guid>("AccountInChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("AccountInChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("AccountUAC");
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.Property<Guid>("ChannelsId")
.HasColumnType("uuid");
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.HasKey("ChannelsId", "UACsId");
b.HasIndex("UACsId");
b.ToTable("ChannelUAC");
});
modelBuilder.Entity("UACUser", b =>
{
b.Property<Guid>("UACsId")
.HasColumnType("uuid");
b.Property<Guid>("UsersId")
.HasColumnType("uuid");
b.HasKey("UACsId", "UsersId");
b.HasIndex("UsersId");
b.ToTable("UACUser");
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("IsUserId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<Guid?>("SeenInChannelId")
.HasColumnType("uuid");
b.Property<string>("Username")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("IsUserId");
b.HasIndex("SeenInChannelId");
b.ToTable("Accounts");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<string>("ContentType")
.HasColumnType("text");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<int>("Size")
.HasColumnType("integer");
b.Property<string>("Source")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("MessageId");
b.ToTable("Attachments");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("ChannelType")
.HasColumnType("integer");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<decimal?>("MaxAttachmentBytes")
.HasColumnType("numeric(20,0)");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<bool?>("ReactionsPossible")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.ToTable("Channels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("ActedOn")
.HasColumnType("boolean");
b.Property<Guid?>("AuthorId")
.HasColumnType("uuid");
b.Property<Guid?>("ChannelId")
.HasColumnType("uuid");
b.Property<string>("Content")
.HasColumnType("text");
b.Property<string>("ExternalId")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
b.Property<string>("Protocol")
.HasColumnType("text");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("ChannelId");
b.ToTable("Messages");
});
modelBuilder.Entity("vassago.Models.UAC", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Dictionary<string, string>>("CommandAliases")
.HasColumnType("hstore");
b.Property<string>("Description")
.HasColumnType("text");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<Dictionary<string, string>>("Localization")
.HasColumnType("hstore");
b.Property<Guid>("OwnerId")
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("UACs");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("AccountUAC", b =>
{
b.HasOne("vassago.Models.Account", null)
.WithMany()
.HasForeignKey("AccountInChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("ChannelUAC", b =>
{
b.HasOne("vassago.Models.Channel", null)
.WithMany()
.HasForeignKey("ChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("UACUser", b =>
{
b.HasOne("vassago.Models.UAC", null)
.WithMany()
.HasForeignKey("UACsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.User", null)
.WithMany()
.HasForeignKey("UsersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("vassago.Models.Account", b =>
{
b.HasOne("vassago.Models.User", "IsUser")
.WithMany("Accounts")
.HasForeignKey("IsUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("vassago.Models.Channel", "SeenInChannel")
.WithMany("Users")
.HasForeignKey("SeenInChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("IsUser");
b.Navigation("SeenInChannel");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("ParentChannel");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.Account", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId")
.OnDelete(DeleteBehavior.Cascade);
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
b.Navigation("Users");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("Accounts");
});
#pragma warning restore 612, 618, 8981
}
}
}

View File

@ -0,0 +1,55 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace vassago.Migrations
{
#pragma warning disable 8981
/// <inheritdoc />
public partial class datamemosmoredata : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Dictionary<string, string>>(
name: "CommandAliases",
table: "UACs",
type: "hstore",
nullable: true);
migrationBuilder.AddColumn<Dictionary<string, string>>(
name: "Localization",
table: "UACs",
type: "hstore",
nullable: true);
//NOTE for future me: migrationBuilder.SQL("SELECT localization INTO Aliases from Channels;");, but also make the rows for it.
//too lazy now, really leaning on the "this will work fine for my 0 users"
migrationBuilder.DropColumn(
name: "Aliases",
table: "Channels");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Dictionary<string, string>>(
name: "Aliases",
table: "Channels",
type: "hstore",
nullable: true);
migrationBuilder.DropColumn(
name: "CommandAliases",
table: "UACs");
migrationBuilder.DropColumn(
name: "Localization",
table: "UACs");
}
}
}
#pragma warning restore 8981

View File

@ -148,9 +148,6 @@ namespace vassago.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<Dictionary<string, string>>("Aliases")
.HasColumnType("hstore");
b.Property<int>("ChannelType") b.Property<int>("ChannelType")
.HasColumnType("integer"); .HasColumnType("integer");
@ -236,12 +233,18 @@ namespace vassago.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<Dictionary<string, string>>("CommandAliases")
.HasColumnType("hstore");
b.Property<string>("Description") b.Property<string>("Description")
.HasColumnType("text"); .HasColumnType("text");
b.Property<string>("DisplayName") b.Property<string>("DisplayName")
.HasColumnType("text"); .HasColumnType("text");
b.Property<Dictionary<string, string>>("Localization")
.HasColumnType("hstore");
b.Property<Guid>("OwnerId") b.Property<Guid>("OwnerId")
.HasColumnType("uuid"); .HasColumnType("uuid");

View File

@ -36,8 +36,6 @@ public class Channel
public Enumerations.LewdnessFilterLevel? LewdnessFilterLevel { get; set; } public Enumerations.LewdnessFilterLevel? LewdnessFilterLevel { get; set; }
public Enumerations.MeannessFilterLevel? MeannessFilterLevel { get; set; } public Enumerations.MeannessFilterLevel? MeannessFilterLevel { get; set; }
public List<UAC> UACs { get; set; } public List<UAC> UACs { get; set; }
//both incoming and outgoing
//public Dictionary<string, string> Aliases { get; set; }
public DefinitePermissionSettings EffectivePermissions public DefinitePermissionSettings EffectivePermissions
{ {

View File

@ -5,6 +5,11 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using vassago.Models; using vassago.Models;
//TODO: rename.
//"uac" originally meant "user account control". but it might just be channel control. in fact, channel-control is much more fun,
//then the platform manages the permissions for you!
//but now I'm going to add locales to it, so it's kind of... "miscellaneous attached data". Official Sticky Notes, if you will.
public class UAC public class UAC
{ {
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
@ -26,4 +31,7 @@ public class UAC
///it's variably before and after compile time. shrug.emote. ///it's variably before and after compile time. shrug.emote.
///</summary> ///</summary>
public string Description { get; set; } public string Description { get; set; }
public Dictionary<string, string> CommandAliases {get; set;}
public Dictionary<string, string> Localization {get; set;}
} }

View File

@ -15,18 +15,23 @@ public static class Rememberer
{ {
dbAccessSemaphore.Wait(); dbAccessSemaphore.Wait();
channels = db.Channels.ToList(); channels = db.Channels.ToList();
Console.WriteLine($"caching channels. {channels.Count} channels retrieved");
foreach (Channel ch in channels) foreach (Channel ch in channels)
{ {
if (ch.ParentChannelId != null) if (ch.ParentChannelId != null)
{ {
ch.ParentChannel = channels.FirstOrDefault(c => c.Id == ch.ParentChannelId); ch.ParentChannel = channels.FirstOrDefault(c => c.Id == ch.ParentChannelId);
ch.ParentChannel.SubChannels ??= []; ch.ParentChannel.SubChannels ??= [];
ch.ParentChannel.SubChannels.Add(ch); if (!ch.ParentChannel.SubChannels.Contains(ch))
{
ch.ParentChannel.SubChannels.Add(ch);
}
} }
if (ch.Messages?.Count > 0) if (ch.Messages?.Count > 0)
{ {
Console.WriteLine($"{ch.DisplayName} got {ch.Messages.Count} messages"); Console.WriteLine($"{ch.DisplayName} got {ch.Messages.Count} messages");
} }
ch.SubChannels ??= [];
} }
channelCacheDirty = false; channelCacheDirty = false;
dbAccessSemaphore.Release(); dbAccessSemaphore.Release();
@ -179,6 +184,8 @@ public static class Rememberer
db.Channels.Remove(toForget); db.Channels.Remove(toForget);
db.SaveChanges(); db.SaveChanges();
dbAccessSemaphore.Release(); dbAccessSemaphore.Release();
channelCacheDirty = true;
cacheChannels();
} }
public static void ForgetMessage(Message toForget) public static void ForgetMessage(Message toForget)
{ {
@ -211,11 +218,9 @@ public static class Rememberer
} }
public static List<Channel> ChannelsOverview() public static List<Channel> ChannelsOverview()
{ {
List<Channel> toReturn; if (channelCacheDirty)
dbAccessSemaphore.Wait(); Task.Run(() => cacheChannels()).Wait();
toReturn = [.. db.Channels.Include(u => u.SubChannels).Include(c => c.ParentChannel)]; return channels;
dbAccessSemaphore.Release();
return toReturn;
} }
public static Account AccountDetail(Guid Id) public static Account AccountDetail(Guid Id)
{ {
@ -318,5 +323,7 @@ public static class Rememberer
db.Update(toRemember); db.Update(toRemember);
db.SaveChanges(); db.SaveChanges();
dbAccessSemaphore.Release(); dbAccessSemaphore.Release();
if (toRemember.Channels?.Count() > 0)
cacheChannels();
} }
} }

View File

@ -13,32 +13,28 @@ public class ChannelsController() : Controller
public IActionResult Details(Guid id) public IActionResult Details(Guid id)
{ {
var allChannels = Rememberer.ChannelsOverview(); var allChannels = Rememberer.ChannelsOverview();
if(allChannels == null) if (allChannels == null)
return Problem("Entity set '_db.Channels' is null."); return Problem("no channels.");
//"but adam", says the strawman, "why load *every* channel and walk your way up? surely there's a .Load command that works or something."
//eh. I checked. Not really. You could make an SQL view that recurses its way up, meh idk how. You could just eagerly load *every* related object...
//but that would take in all the messages.
//realistically I expect this will have less than 1MB of total "channels", and several GB of total messages per (text) channel.
var channel = allChannels.FirstOrDefault(u => u.Id == id); var channel = allChannels.FirstOrDefault(u => u.Id == id);
if(channel == null) if (channel == null)
{
return Problem("couldn't find that channle");
}
var walker = channel;
while(walker != null)
{ {
ViewData["breadcrumbs"] = $"<a href=\"{Url.ActionLink(action: "Details", controller: "Channels", values: new {id = walker.Id})}\">{walker.DisplayName}</a>/" + return Problem($"couldn't find channle {id}");
}
var walker = channel;
while (walker != null)
{
ViewData["breadcrumbs"] = $"<a href=\"{Url.ActionLink(action: "Details", controller: "Channels", values: new { id = walker.Id })}\">{walker.DisplayName}</a>/" +
ViewData["breadcrumbs"]; ViewData["breadcrumbs"];
walker = walker.ParentChannel; walker = walker.ParentChannel;
} }
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append('['); sb.Append('[');
sb.Append($"{{text: \"{channel.SubChannels?.Count}\", nodes: ["); sb.Append($"{{text: \"{channel.SubChannels?.Count}\", nodes: [");
var first=true; var first = true;
foreach(var subChannel in channel.SubChannels) foreach (var subChannel in channel.SubChannels)
{ {
if(!first) if (!first)
{ {
sb.Append(','); sb.Append(',');
} }
@ -46,11 +42,11 @@ public class ChannelsController() : Controller
{ {
first = false; first = false;
} }
sb.Append($"{{\"text\": \"<a href=\\\"{Url.ActionLink(action: "Details", controller: "Channels", values: new {id = subChannel.Id})}\\\">{subChannel.DisplayName}</a>\"}}"); sb.Append($"{{\"text\": \"<a href=\\\"{Url.ActionLink(action: "Details", controller: "Channels", values: new { id = subChannel.Id })}\\\">{subChannel.DisplayName}</a>\"}}");
} }
sb.Append("]}]"); sb.Append("]}]");
ViewData.Add("channelsTree", sb.ToString()); ViewData.Add("subChannelsTree", sb.ToString());
return View( return View(
new Tuple<Channel, Enumerations.LewdnessFilterLevel, Enumerations.MeannessFilterLevel>( new Tuple<Channel, Enumerations.LewdnessFilterLevel, Enumerations.MeannessFilterLevel>(
channel, channel.EffectivePermissions.LewdnessFilterLevel, channel.EffectivePermissions.MeannessFilterLevel channel, channel.EffectivePermissions.LewdnessFilterLevel, channel.EffectivePermissions.MeannessFilterLevel

View File

@ -43,7 +43,12 @@ public class HomeController : Controller
{ {
sb.Append(','); sb.Append(',');
} }
sb.Append($"{{\"text\": \"<a href=\\\"{Url.ActionLink(action: "Details", controller: "UACs", values: new {id = uac.Id})}\\\">{uac.DisplayName}</a>\"}}"); var displayedName = uac.DisplayName;
if(string.IsNullOrWhiteSpace(displayedName))
{
displayedName = $"[unnamed - {uac.Id}]";
}
sb.Append($"{{\"text\": \"<a href=\\\"{Url.ActionLink(action: "Details", controller: "UACs", values: new {id = uac.Id})}\\\">{displayedName}</a>\"}}");
} }
sb.Append("]}"); sb.Append("]}");
} }
@ -78,7 +83,7 @@ public class HomeController : Controller
//type error, e is not defined //type error, e is not defined
//channels //channels
sb.Append(",{text: \"channels\", expanded:true, nodes: ["); sb.Append(",{text: \"channels\", expanded:true, nodes: [");
var topLevelChannels = Rememberer.ChannelsOverview().Where(x => x.ParentChannel == null); var topLevelChannels = Rememberer.ChannelsOverview().Where(x => x.ParentChannel == null).ToList();
first = true; first = true;
foreach (var topLevelChannel in topLevelChannels) foreach (var topLevelChannel in topLevelChannels)
{ {

View File

@ -8,7 +8,7 @@ namespace vassago.Controllers.api;
[Route("api/[controller]")] [Route("api/[controller]")]
[ApiController] [ApiController]
public class UACController: ControllerBase public class UACController : ControllerBase
{ {
private readonly ILogger<UACController> _logger; private readonly ILogger<UACController> _logger;
@ -34,7 +34,7 @@ public class UACController: ControllerBase
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid); var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
if (uacFromDb == null) if (uacFromDb == null)
{ {
var err =$"attempt to link channel for uac {uac_guid}, not found"; var err = $"attempt to link channel for uac {uac_guid}, not found";
_logger.LogError(err); _logger.LogError(err);
return NotFound(err); return NotFound(err);
} }
@ -210,4 +210,23 @@ public class UACController: ControllerBase
Rememberer.RememberUAC(uacFromDb); Rememberer.RememberUAC(uacFromDb);
return Ok(uacFromDb); return Ok(uacFromDb);
} }
[HttpPut]
[Route("CreateForChannels/{Id}")]
[Produces("application/json")]
public IActionResult CreateForChannels(Guid Id)
{
_logger.LogDebug($"made it to controller. creating for channel {Id}");
var targetChannel = Rememberer.ChannelDetail(Id);
if (targetChannel == null)
{
return NotFound();
}
var newUAC = new UAC()
{
Channels = [targetChannel]
};
Rememberer.RememberUAC(newUAC);
Rememberer.RememberChannel(targetChannel);
return Ok(newUAC.Id);
}
} }

View File

@ -103,6 +103,12 @@
} }
</td> </td>
</tr> </tr>
<tr>
<th scope="row">Datamemos</th>
<td>
<div id="dataMemosTree"></div>
</td>
</tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<button onclick="forget()">forget</button> <button onclick="forget()">forget</button>
@ -138,15 +144,49 @@
deleteModel(jsonifyChannel().Id, window.history.back); deleteModel(jsonifyChannel().Id, window.history.back);
} }
} }
function createMemo()
{
console.log("creating memo for channel..");
createMemoFor((newMemoId) => {
window.location.href = "/UACs/Details/" + newMemoId;
});
}
function channelsTree() { function channelsTree() {
var tree = @Html.Raw(ViewData["channelsTree"]); //TOOD: see how accountsTree does all our HTML-ification over here in HTML land? but this doesn't? we should pick one and stick with it.
var tree = @Html.Raw(ViewData["subChannelsTree"]);
return tree; return tree;
} }
function dataMemosTree(){
@{
var dmsb = new StringBuilder();
dmsb.Append("[{text: \"Data Memos\", \"expanded\":true, nodes: [");
var firstMemo = true;
if(ThisChannel.UACs != null) foreach(var memo in ThisChannel.UACs)
{
if(!firstMemo)
dmsb.Append(',');
var effectiveDisplayName = memo.DisplayName;
if(string.IsNullOrWhiteSpace(effectiveDisplayName))
{
effectiveDisplayName = $"[nameless] {memo.Id}";
}
dmsb.Append($"{{text: \"<a href=\\\"/UACs/Details/{memo.Id}\\\">{effectiveDisplayName}</a>\"}}");
firstMemo = false;
}
if(!firstMemo)
dmsb.Append(',');
dmsb.Append($"{{text: \"<button type=\\\"button\\\" class=\\\"btn btn-primary\\\" onclick=\\\"createMemo()\\\">+</button>\"}}");
dmsb.Append("]}]");
}
var tree = @Html.Raw(dmsb.ToString());
return tree;
}
function accountsTree() { function accountsTree() {
@{ @{
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.Append("[{text: \"accounts\", \"expanded\":true, nodes: ["); sb.Append("[{text: \"accounts\", \"expanded\":true, nodes: [");
var first = true; var first = true;
foreach (var acc in ThisChannel.Users?.OrderBy(a => a?.SeenInChannel?.LineageSummary)) foreach (var acc in ThisChannel.Users?.OrderBy(a => a?.SeenInChannel?.LineageSummary))
@ -157,6 +197,7 @@
first=false; first=false;
} }
sb.Append("]}]"); sb.Append("]}]");
} }
//console.log(@Html.Raw(sb.ToString())); //console.log(@Html.Raw(sb.ToString()));
var tree = @Html.Raw(sb.ToString()); var tree = @Html.Raw(sb.ToString());
@ -164,6 +205,7 @@
} }
$('#channelsTree').bstreeview({ data: channelsTree() }); $('#channelsTree').bstreeview({ data: channelsTree() });
$('#accountsTree').bstreeview({ data: accountsTree() }); $('#accountsTree').bstreeview({ data: accountsTree() });
$('#dataMemosTree').bstreeview({ data: dataMemosTree() });
</script> </script>
} }

View File

@ -1,42 +0,0 @@
@model IEnumerable<Channel>
@{
ViewData["Title"] = "Channels";
}
<table class="table">
<thead>
<tr>
<th>
protocol
</th>
<th>type</th>
<th>
display name
</th>
<th>
Lineage
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td class="@item.Protocol">
<div class="protocol-icon">&nbsp;</div>
</td>
<td class="@item.ChannelType">
<div class="channel-type-icon">&nbsp;</div>
</td>
<td>
@Html.DisplayFor(modelItem => item.DisplayName)
</td>
<td>
@item.LineageSummary
</td>
<td>
<a asp-action="Details" asp-route-id="@item.Id">Details</a>
</td>
</tr>
}
</tbody>
</table>

View File

@ -49,7 +49,7 @@ function deleteModel(id, callback)
var components = window.location.pathname.split('/'); var components = window.location.pathname.split('/');
var type=components[1]; var type=components[1];
let result = null; let result = null;
var id=components[3]; var id=components[3]; //wait... i send it the ID then overwrite it? lmao what? TODO: fix
fetch(apiUrl + 'Rememberer/' + type + '/' + id, { fetch(apiUrl + 'Rememberer/' + type + '/' + id, {
method: 'DELETE', method: 'DELETE',
headers: { headers: {
@ -71,9 +71,37 @@ function deleteModel(id, callback)
console.error('Error:', error); console.error('Error:', error);
}); });
} }
function createMemoFor(callback)
{
let components = window.location.pathname.split('/');
let type=components[1];
type = type.charAt(0).toUpperCase() + type.slice(1).toLowerCase();
let result = null;
let id=components[3];
console.log("createMemoFor" + type + "(" + id + ")");
fetch(apiUrl + "UAC/CreateFor" + type + "/" + id, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not "ok". which is not ok.');
}
return response.json();
})
.then(returnedSuccessdata => {
console.log("success");
console.log('returnedSuccessdata:', returnedSuccessdata);
if(callback !== null) { callback(returnedSuccessdata); }
})
.catch(error => {
console.error('Error:', error);
});
}
function linkUAC_Channel(channel_guid, callback) function linkUAC_Channel(channel_guid, callback)
{ {
var components = window.location.pathname.split('/'); var components = window.location.pathname.split('/');
var id=components[3]; var id=components[3];
let model={"uac_guid": id, let model={"uac_guid": id,