Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions website/MyWebApp/Controllers/AdminBlockTemplateController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public AdminBlockTemplateController(ApplicationDbContext db, HtmlSanitizerServic
private async Task LoadPagesAsync()
{
ViewBag.Pages = await _db.Pages.AsNoTracking().OrderBy(p => p.Slug).ToListAsync();
ViewBag.Roles = await _db.Roles.AsNoTracking().OrderBy(r => r.Name).ToListAsync();
}

public async Task<IActionResult> Index()
Expand Down Expand Up @@ -159,7 +160,7 @@ public async Task<IActionResult> AddToPage(int id)

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddToPage(int id, int pageId, string zone)
public async Task<IActionResult> AddToPage(int id, int pageId, string zone, int? roleId)
{
var template = await _db.BlockTemplates.FindAsync(id);
var page = await _db.Pages.FindAsync(pageId);
Expand All @@ -186,7 +187,8 @@ public async Task<IActionResult> AddToPage(int id, int pageId, string zone)
Zone = zone,
SortOrder = sort,
Html = template.Html,
Type = PageSectionType.Html
Type = PageSectionType.Html,
RoleId = roleId
};
_db.PageSections.Add(section);
await _db.SaveChangesAsync();
Expand Down
2 changes: 2 additions & 0 deletions website/MyWebApp/Controllers/AdminContentController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ private async Task LoadTemplatesAsync()
.OrderBy(t => t.Name).ToListAsync();
ViewBag.Permissions = await _db.Permissions.AsNoTracking()
.OrderBy(p => p.Name).ToListAsync();
ViewBag.Roles = await _db.Roles.AsNoTracking()
.OrderBy(r => r.Name).ToListAsync();
}

public async Task<IActionResult> Index()
Expand Down
1 change: 1 addition & 0 deletions website/MyWebApp/Controllers/AdminPageSectionController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
if (!string.IsNullOrWhiteSpace(q))
{
q = q.ToLowerInvariant();
query = query.Where(s => s.Zone.ToLower().Contains(q) || s.Html.ToLower().Contains(q) || s.Page.Slug.ToLower().Contains(q));

Check warning on line 31 in website/MyWebApp/Controllers/AdminPageSectionController.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 31 in website/MyWebApp/Controllers/AdminPageSectionController.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
}
var sections = await query.OrderBy(s => s.Page.Slug).ThenBy(s => s.Zone).ToListAsync();

Check warning on line 33 in website/MyWebApp/Controllers/AdminPageSectionController.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 33 in website/MyWebApp/Controllers/AdminPageSectionController.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
ViewBag.Query = q;
return View(sections);
}
Expand All @@ -39,6 +39,7 @@
{
ViewBag.Pages = await _db.Pages.AsNoTracking().OrderBy(p => p.Slug).ToListAsync();
ViewBag.Permissions = await _db.Permissions.AsNoTracking().OrderBy(p => p.Name).ToListAsync();
ViewBag.Roles = await _db.Roles.AsNoTracking().OrderBy(r => r.Name).ToListAsync();
}

public async Task<IActionResult> Create()
Expand Down
9 changes: 9 additions & 0 deletions website/MyWebApp/Controllers/PagesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ public async Task<IActionResult> Show(string? slug)
{
return NotFound();
}
var roles = HttpContext.Session.GetString("Roles")?.Split(',') ?? Array.Empty<string>();
if (page.RoleId != null)
{
var allowed = await Db.Roles.AsNoTracking().Where(r => roles.Contains(r.Name)).Select(r => r.Id).ToListAsync();
if (!allowed.Contains(page.RoleId.Value))
{
return Unauthorized();
}
}

var header = await _layout.GetSectionAsync(Db, page.Id, "header");
if (string.IsNullOrEmpty(header))
Expand Down
6 changes: 4 additions & 2 deletions website/MyWebApp/Data/ApplicationDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,16 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
Id = 1,
Slug = "layout",
Title = "Layout",
Layout = "single-column"
Layout = "single-column",
RoleId = null
},
new Page
{
Id = 2,
Slug = "home",
Title = "Home",
Layout = "single-column"
Layout = "single-column",
RoleId = null
});

modelBuilder.Entity<PageSection>().HasData(
Expand Down
4 changes: 4 additions & 0 deletions website/MyWebApp/Models/Page.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ public class Page
[MaxLength(256)]
public string? FeaturedImage { get; set; }

public int? RoleId { get; set; }

public Role? Role { get; set; }

public ICollection<PageSection> Sections { get; set; } = new List<PageSection>();
}
}
4 changes: 4 additions & 0 deletions website/MyWebApp/Models/PageSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,12 @@ public class PageSection

public int? PermissionId { get; set; }

public int? RoleId { get; set; }


public Page? Page { get; set; }

public Permission? Permission { get; set; }

public Role? Role { get; set; }
}
6 changes: 6 additions & 0 deletions website/MyWebApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ FOREIGN KEY(PageId) REFERENCES Pages(Id) ON DELETE CASCADE
db.Database.ExecuteSqlRaw("ALTER TABLE PageSections ADD COLUMN EndDate TEXT");
if (!columns.Contains("PermissionId"))
db.Database.ExecuteSqlRaw("ALTER TABLE PageSections ADD COLUMN PermissionId INTEGER");
if (!columns.Contains("RoleId"))
db.Database.ExecuteSqlRaw("ALTER TABLE PageSections ADD COLUMN RoleId INTEGER");

cmd.CommandText = "PRAGMA index_list('PageSections')";
using var idx = cmd.ExecuteReader();
Expand Down Expand Up @@ -438,6 +440,10 @@ static void UpgradePagesTable(ApplicationDbContext db)
{
db.Database.ExecuteSqlRaw("ALTER TABLE Pages ADD COLUMN FeaturedImage TEXT");
}
if (!columns.Contains("RoleId"))
{
db.Database.ExecuteSqlRaw("ALTER TABLE Pages ADD COLUMN RoleId INTEGER");
}
}
catch (Exception ex)
{
Expand Down
32 changes: 25 additions & 7 deletions website/MyWebApp/Services/LayoutService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,26 @@
.ToListAsync();
}

private async Task<List<int>> GetRoleIdsAsync(ApplicationDbContext db, string[] roles)
{
if (roles.Length == 0) return new List<int>();
return await db.Roles.AsNoTracking()
.Where(r => roles.Contains(r.Name))
.Select(r => r.Id)
.ToListAsync();
}

public async Task<string> GetHeaderAsync(ApplicationDbContext db)
{
var roles = GetRoles();
var roleIds = await GetRoleIdsAsync(db, roles);
if (roles.Length == 0)
{
return await _cache.GetOrCreateAsync(HeaderKey, async e =>
{
e.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
var parts = await db.PageSections.AsNoTracking()
.Where(s => s.Page.Slug == "layout" && s.Zone == "header" && s.PermissionId == null)
.Where(s => s.Page.Slug == "layout" && s.Zone == "header" && s.PermissionId == null && s.RoleId == null)

Check warning on line 74 in website/MyWebApp/Services/LayoutService.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 74 in website/MyWebApp/Services/LayoutService.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
.OrderBy(s => s.SortOrder)
.Select(s => s.Html)
.ToListAsync();
Expand All @@ -72,8 +82,10 @@

var allowed = await GetAllowedPermissionsAsync(db, roles);
var query = db.PageSections.AsNoTracking()
.Where(s => s.Page.Slug == "layout" && s.Zone == "header");

Check warning on line 85 in website/MyWebApp/Services/LayoutService.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 85 in website/MyWebApp/Services/LayoutService.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
query = query.Where(s => s.PermissionId == null || allowed.Contains(s.PermissionId.Value));
query = query.Where(s =>
(s.PermissionId == null || allowed.Contains(s.PermissionId.Value)) &&
(s.RoleId == null || roleIds.Contains(s.RoleId.Value)));
var parts2 = await query.OrderBy(s => s.SortOrder).Select(s => s.Html).ToListAsync();
var html2 = string.Join(System.Environment.NewLine, parts2);
return await _tokens.RenderAsync(db, html2);
Expand All @@ -82,13 +94,14 @@
public async Task<string> GetFooterAsync(ApplicationDbContext db)
{
var roles = GetRoles();
var roleIds = await GetRoleIdsAsync(db, roles);
if (roles.Length == 0)
{
return await _cache.GetOrCreateAsync(FooterKey, async e =>
{
e.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
var parts = await db.PageSections.AsNoTracking()
.Where(s => s.Page.Slug == "layout" && s.Zone == "footer" && s.PermissionId == null)
.Where(s => s.Page.Slug == "layout" && s.Zone == "footer" && s.PermissionId == null && s.RoleId == null)

Check warning on line 104 in website/MyWebApp/Services/LayoutService.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 104 in website/MyWebApp/Services/LayoutService.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
.OrderBy(s => s.SortOrder)
.Select(s => s.Html)
.ToListAsync();
Expand All @@ -99,8 +112,10 @@

var allowed = await GetAllowedPermissionsAsync(db, roles);
var query = db.PageSections.AsNoTracking()
.Where(s => s.Page.Slug == "layout" && s.Zone == "footer");

Check warning on line 115 in website/MyWebApp/Services/LayoutService.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 115 in website/MyWebApp/Services/LayoutService.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
query = query.Where(s => s.PermissionId == null || allowed.Contains(s.PermissionId.Value));
query = query.Where(s =>
(s.PermissionId == null || allowed.Contains(s.PermissionId.Value)) &&
(s.RoleId == null || roleIds.Contains(s.RoleId.Value)));
var parts2 = await query.OrderBy(s => s.SortOrder).Select(s => s.Html).ToListAsync();
var html2 = string.Join(System.Environment.NewLine, parts2);
return await _tokens.RenderAsync(db, html2);
Expand All @@ -109,13 +124,16 @@
public async Task<string> GetSectionAsync(ApplicationDbContext db, int pageId, string zone)
{
var roles = GetRoles();
var roleIds = await GetRoleIdsAsync(db, roles);
var allowed = await GetAllowedPermissionsAsync(db, roles);
var query = db.PageSections.AsNoTracking()
.Where(s => s.PageId == pageId && s.Zone == zone);
if (allowed.Count == 0)
query = query.Where(s => s.PermissionId == null);
if (allowed.Count == 0 && roleIds.Count == 0)
query = query.Where(s => s.PermissionId == null && s.RoleId == null);
else
query = query.Where(s => s.PermissionId == null || allowed.Contains(s.PermissionId.Value));
query = query.Where(s =>
(s.PermissionId == null || allowed.Contains(s.PermissionId.Value)) &&
(s.RoleId == null || roleIds.Contains(s.RoleId.Value)));
var parts = await query.OrderBy(s => s.SortOrder).Select(s => s.Html).ToListAsync();
var html = string.Join(System.Environment.NewLine, parts);
return await _tokens.RenderAsync(db, html);
Expand Down
22 changes: 18 additions & 4 deletions website/MyWebApp/Services/TokenRenderService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ private async Task<List<int>> GetAllowedPermissionsAsync(ApplicationDbContext db
.ToListAsync();
}

private async Task<List<int>> GetRoleIdsAsync(ApplicationDbContext db, string[] roles)
{
if (roles.Length == 0) return new List<int>();
return await db.Roles.AsNoTracking()
.Where(r => roles.Contains(r.Name))
.Select(r => r.Id)
.ToListAsync();
}

public Task<string> RenderAsync(ApplicationDbContext db, string html)
{
return RenderAsync(db, html, new HashSet<int>());
Expand All @@ -43,8 +52,10 @@ async Task<string> Replace(Match match)
{
if (match.Value.StartsWith("{{nav", StringComparison.OrdinalIgnoreCase))
{
var roles = GetRoles();
var roleIds = await GetRoleIdsAsync(db, roles);
var pages = await db.Pages.AsNoTracking()
.Where(p => p.IsPublished && p.Slug != "layout" && p.Slug != "home")
.Where(p => p.IsPublished && p.Slug != "layout" && p.Slug != "home" && (p.RoleId == null || roleIds.Contains(p.RoleId.Value)))
.OrderBy(p => p.Title)
.Select(p => new { p.Slug, p.Title })
.ToListAsync();
Expand Down Expand Up @@ -75,13 +86,16 @@ async Task<string> Replace(Match match)
{
var zone = parts[1];
var roles = GetRoles();
var roleIds = await GetRoleIdsAsync(db, roles);
var allowed = await GetAllowedPermissionsAsync(db, roles);
var query = db.PageSections.AsNoTracking()
.Where(s => s.PageId == pageId && s.Zone == zone);
if (allowed.Count == 0)
query = query.Where(s => s.PermissionId == null);
if (allowed.Count == 0 && roleIds.Count == 0)
query = query.Where(s => s.PermissionId == null && s.RoleId == null);
else
query = query.Where(s => s.PermissionId == null || allowed.Contains(s.PermissionId.Value));
query = query.Where(s =>
(s.PermissionId == null || allowed.Contains(s.PermissionId.Value)) &&
(s.RoleId == null || roleIds.Contains(s.RoleId.Value)));
var htmlParts = await query
.OrderBy(s => s.SortOrder)
.Select(s => s.Html)
Expand Down
10 changes: 10 additions & 0 deletions website/MyWebApp/Views/AdminBlockTemplate/AddToPage.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<div>
<label>Page</label>
<select name="pageId">
@foreach (var p in ViewBag.Pages as List<Page>)

Check warning on line 12 in website/MyWebApp/Views/AdminBlockTemplate/AddToPage.cshtml

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 12 in website/MyWebApp/Views/AdminBlockTemplate/AddToPage.cshtml

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
{
<option value="@p.Id">@p.Slug</option>
}
Expand All @@ -19,5 +19,15 @@
<label>Zone</label>
<input type="text" name="zone" />
</div>
<div>
<label>Role</label>
<select name="roleId">
<option value="">(none)</option>
@foreach (var r in ViewBag.Roles as List<Role>)

Check warning on line 26 in website/MyWebApp/Views/AdminBlockTemplate/AddToPage.cshtml

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 26 in website/MyWebApp/Views/AdminBlockTemplate/AddToPage.cshtml

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
{
<option value="@r.Id">@r.Name</option>
}
</select>
</div>
<button type="submit">Add</button>
</form>
15 changes: 13 additions & 2 deletions website/MyWebApp/Views/AdminContent/PageEditor.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@using Microsoft.AspNetCore.Mvc.ViewFeatures
@{
var sections = ViewBag.Sections as List<PageSection> ?? new List<PageSection>();
var roles = ViewBag.Roles as List<Role> ?? new List<Role>();
Layout = "../Admin/_AdminLayout";
var isNew = Model.Id == 0;
ViewData["Title"] = isNew ? "Create Page" : "Edit Page";
Expand Down Expand Up @@ -40,6 +41,16 @@
</select>
<div id="layout-preview" class="layout-preview"></div>
</div>
<div>
<label asp-for="RoleId">Role</label>
<select asp-for="RoleId">
<option value="">(none)</option>
@foreach (var r in roles)
{
<option value="@r.Id">@r.Name</option>
}
</select>
</div>
<div><label asp-for="IsPublished"></label><input asp-for="IsPublished" /></div>
<div><label asp-for="PublishDate"></label><input asp-for="PublishDate" type="datetime-local" /></div>
<details>
Expand All @@ -56,7 +67,7 @@
<div class="mt-3" id="sections-container">
@for (int i = 0; i < sections.Count; i++)
{
var vd = new ViewDataDictionary(ViewData) { ["Index"] = i };
var vd = new ViewDataDictionary(ViewData) { ["Index"] = i, ["Roles"] = roles };
@await Html.PartialAsync("~/Views/Shared/_SectionEditor.cshtml", sections[i], vd)
}
</div>
Expand All @@ -74,7 +85,7 @@
}
<button type="button" id="add-section">Add Section</button>
<div id="section-template" style="display:none">
@await Html.PartialAsync("~/Views/Shared/_SectionEditor.cshtml", new PageSection(), new ViewDataDictionary(ViewData) { ["Index"] = "__index__" })
@await Html.PartialAsync("~/Views/Shared/_SectionEditor.cshtml", new PageSection(), new ViewDataDictionary(ViewData) { ["Index"] = "__index__", ["Roles"] = roles })
</div>
<button type="submit">Save</button>
</form>
Expand Down
3 changes: 2 additions & 1 deletion website/MyWebApp/Views/AdminPageSection/Create.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Layout = "../Admin/_AdminLayout";
var pages = ViewBag.Pages as List<MyWebApp.Models.Page>;
var permissions = ViewBag.Permissions as List<MyWebApp.Models.Permission>;
var roles = ViewBag.Roles as List<MyWebApp.Models.Role> ?? new List<MyWebApp.Models.Role>();
}
<h2>Create Section</h2>
<form asp-action="Create" method="post" enctype="multipart/form-data">
Expand All @@ -16,7 +17,7 @@
<select id="zone-select" asp-for="Zone" data-selected="@Model.Zone"></select>
</div>

@await Html.PartialAsync("~/Views/Shared/_SectionEditor.cshtml", Model)
@await Html.PartialAsync("~/Views/Shared/_SectionEditor.cshtml", Model, new ViewDataDictionary(ViewData) { ["Roles"] = roles })

<button type="submit">Save</button>
</form>
Expand Down
3 changes: 2 additions & 1 deletion website/MyWebApp/Views/AdminPageSection/Edit.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Layout = "../Admin/_AdminLayout";
var pages = ViewBag.Pages as List<MyWebApp.Models.Page>;
var permissions = ViewBag.Permissions as List<MyWebApp.Models.Permission>;
var roles = ViewBag.Roles as List<MyWebApp.Models.Role> ?? new List<MyWebApp.Models.Role>();
}
<h2>Edit Section</h2>
<form asp-action="Edit" method="post" enctype="multipart/form-data">
Expand All @@ -17,7 +18,7 @@
<select id="zone-select" asp-for="Zone" data-selected="@Model.Zone"></select>
</div>

@await Html.PartialAsync("~/Views/Shared/_SectionEditor.cshtml", Model)
@await Html.PartialAsync("~/Views/Shared/_SectionEditor.cshtml", Model, new ViewDataDictionary(ViewData) { ["Roles"] = roles })

<button type="submit">Save</button>
</form>
Expand Down
11 changes: 11 additions & 0 deletions website/MyWebApp/Views/Shared/_SectionEditor.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
var idxObj = ViewData["Index"];
var idx = idxObj?.ToString() ?? "0";
var prefix = idxObj != null ? $"Sections[{idx}]." : string.Empty;
var roles = ViewData["Roles"] as List<Role> ?? new List<Role>();
}
<div class="section-editor" data-index="@idx" draggable="true">
<input type="hidden" name="@($"{prefix}Id")" value="@Model.Id" />
Expand All @@ -12,6 +13,16 @@
<label>Zone</label>
<select class="zone-select" data-index="@idx" name="@($"{prefix}Zone")" data-selected="@Model.Zone"></select>
</div>
<div>
<label>Role</label>
<select name="@($"{prefix}RoleId")">
<option value="">(none)</option>
@foreach (var r in roles)
{
<option value="@r.Id" @(Model.RoleId == r.Id ? "selected" : "")>@r.Name</option>

Check failure on line 22 in website/MyWebApp/Views/Shared/_SectionEditor.cshtml

View workflow job for this annotation

GitHub Actions / build

The tag helper 'option' must not have C# in the element's attribute declaration area.

Check failure on line 22 in website/MyWebApp/Views/Shared/_SectionEditor.cshtml

View workflow job for this annotation

GitHub Actions / build

The tag helper 'option' must not have C# in the element's attribute declaration area.

Check failure on line 22 in website/MyWebApp/Views/Shared/_SectionEditor.cshtml

View workflow job for this annotation

GitHub Actions / build

The tag helper 'option' must not have C# in the element's attribute declaration area.

Check failure on line 22 in website/MyWebApp/Views/Shared/_SectionEditor.cshtml

View workflow job for this annotation

GitHub Actions / build

The tag helper 'option' must not have C# in the element's attribute declaration area.
}
</select>
</div>
<div>
<label>Type</label>
<select id="type-select-@idx" name="@($"{prefix}Type")" asp-items="Html.GetEnumSelectList<PageSectionType>()" value="@Model.Type"></select>
Expand Down
Loading