@page "/admin/homepageeditor"
@layout AdminControlPanelLayout
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.EntityFrameworkCore
@using Microsoft.Extensions.Caching.Memory
@using Microsoft.Extensions.Options
@using Microsoft.AspNetCore.Authorization
@using MudExRichTextEditor
@using MudBlazor
@using OpenArchival.Blazor
@using OpenArchival.Blazor.AdminPages.Shared
@using OpenArchival.Blazor.Config
@using OpenArchival.DataAccess
@using OpenArchival.DataAccess.FileAccessManager
@using OpenArchival.Blazor.CustomComponents
@attribute [Authorize(Roles = UserRoles.Admin)]
Home Page Editor
@if (!_homePageConfigMissing)
{
Upload Main Photo
@if (context != null)
{
@context.Name
}
else if (_homePageConfiguration.HomePageBanner != null)
{
@_homePageConfiguration.HomePageBanner.OriginalName
}
else
{
No Main Photo Selected
}
This is the content that will be displayed on the site's home page underneath the banner image
Save
Home Page Featured Sliders
Controls what artifacts are shown on the home page. This is controlled via selecting tags. The first artifact with the listed tags will be shown in the slider. Only tags belonging to valid archive entries can be used. Entries must have all of the listed tags to be included in the slider.
Add Slider
@if (_homePageConfiguration.SliderEntries != null)
{
@foreach (SearchPageSliderEntry entry in _homePageConfiguration.SliderEntries)
{
Delete
Title: @entry.Title
Description: @entry.Description
Tags:
}
}
} else
{
Failed to query home page configuration from database, was your database modified outside the application?
}
@inject ArtifactEntrySharedHelpers Helpers;
@inject ISnackbar Snackbar;
@inject IOptions AppOptions;
@inject IMemoryCache MemoryCache;
@inject IDbContextFactory ContextFactory;
@inject IFileAccessManager FileAccessManager;
@code {
private MudFileUpload _bannerFileUpload = default!;
private MudExRichTextEdit _editor = default!;
private bool _hasLoaded = false;
private string _editorContent = "";
private IBrowserFile? _bannerPhotoFile { get; set; }
// Flipped if the browser file has changed for the file picker, this lets us know to delete the old file before creating a new one
private bool _hasFileChanged { get; set; }
private HomePageConfiguration _homePageConfiguration { get; set; }
private bool _homePageConfigMissing { get; set; } = true;
// Slider management state
private string _tagsInputValue { get; set; } = "";
private List _selectedTagStrings { get; set; } = [];
private List _selectedTags { get; set; } = [];
private string _titleInputValue { get; set; } = "";
private string _descriptionInputValue { get; set; } = "";
private ChipContainer _tagsChipContainer { get; set; } = default!;
protected override async Task OnInitializedAsync()
{
await using var context = await ContextFactory.CreateDbContextAsync();
var tempConfig = await context.HomePageConfiguration
.Include(config => config.HomePageBanner)
.Include(config => config.SliderEntries)
.ThenInclude(se => se.FilterTags)
.FirstOrDefaultAsync();
if (tempConfig == null)
{
Snackbar.Add("Database invalid, no HomePageConfiguration", Severity.Error);
_homePageConfigMissing = true;
}
else
{
_homePageConfigMissing = false;
_homePageConfiguration = tempConfig;
if (_homePageConfiguration.SliderEntries == null)
{
_homePageConfiguration.SliderEntries = new List();
}
}
}
private async Task OnSaveHomePage(Microsoft.AspNetCore.Components.Web.MouseEventArgs args)
{
await using var context = await ContextFactory.CreateDbContextAsync();
if (_bannerPhotoFile != null)
{
// In this case we know to delete the current FilePathListing and make a new one
// as the file has changed
if (_hasFileChanged)
{
FilePathListing? bannerListing = _homePageConfiguration.HomePageBanner;
if (bannerListing != null)
{
await FileAccessManager.DeleteFileAsync(bannerListing.Id);
}
FilePathListing? newBannerFileListing = null;
newBannerFileListing = await FileAccessManager.UploadFileAsync(_bannerPhotoFile);
newBannerFileListing.HomePageConfiguration = _homePageConfiguration;
_homePageConfiguration.HomePageBanner = newBannerFileListing;
}
}
// Page content is already bound by the text editor so just save
context.HomePageConfiguration.Attach(_homePageConfiguration);
context.Entry(_homePageConfiguration).State = EntityState.Modified;
await context.SaveChangesAsync();
// Clear the memory cache of the home page values
MemoryCache.Remove(OpenArchivalConstants.HomePageConfigurationCacheKey);
Snackbar.Add("Saved changes to home page configuration!", Severity.Success);
}
private async Task OnFilesChanged(InputFileChangeEventArgs args)
{
_hasFileChanged = true;
_bannerPhotoFile = args.File;
}
public async Task HandleChipContainerEnter(KeyboardEventArgs args, ChipContainer container, Type value, Action resetInputAction)
{
if (args.Key == "Enter")
{
await using var context = await ContextFactory.CreateDbContextAsync();
ArtifactEntryTag? tagToAdd = await context.ArtifactEntryTags.Where(tag => tag.Name == _tagsInputValue).FirstOrDefaultAsync();
if (tagToAdd is null)
{
tagToAdd = new ArtifactEntryTag() { Name = _tagsInputValue };
}
_selectedTags.Add(tagToAdd);
await container.AddItem(value);
resetInputAction?.Invoke();
StateHasChanged();
}
}
private async Task OnAddSlider(MouseEventArgs args)
{
if (string.IsNullOrEmpty(_titleInputValue))
{
Snackbar.Add("A slider title is required.", Severity.Error);
return;
}
if (!_selectedTags.Any())
{
Snackbar.Add("At least one tag is required", Severity.Error);
return;
}
await using var context = await ContextFactory.CreateDbContextAsync();
// Attach the parent configuration first. This recursively attaches all loaded sliders and their tags as Unchanged.
context.HomePageConfiguration.Attach(_homePageConfiguration);
// Prepare the tags for the NEW entry.
// We must ensure we use tracked instances if they are already in the context from the Attach above.
List tagsToUse = new();
foreach (var tag in _selectedTags)
{
if (tag.Id != 0)
{
var trackedTag = context.ArtifactEntryTags.Local.FirstOrDefault(t => t.Id == tag.Id);
if (trackedTag != null)
{
tagsToUse.Add(trackedTag);
}
else
{
context.ArtifactEntryTags.Attach(tag);
tagsToUse.Add(tag);
}
}
else
{
tagsToUse.Add(tag);
}
}
var newEntry = new SearchPageSliderEntry()
{
Title = _titleInputValue,
Description = _descriptionInputValue,
FilterTags = tagsToUse,
IsHomePageSlider = true,
HomePageConfiguration = _homePageConfiguration
};
_homePageConfiguration.SliderEntries!.Add(newEntry);
await context.SaveChangesAsync();
Snackbar.Add("Added slider", Severity.Success);
_selectedTags = new List();
_selectedTagStrings.Clear();
_titleInputValue = "";
_descriptionInputValue = "";
// Clear the memory cache of the home page values
MemoryCache.Remove(OpenArchivalConstants.HomePageConfigurationCacheKey);
StateHasChanged();
}
private void OnTagRemoved(string tagString)
{
int countRemoved = _selectedTags.RemoveAll(t => t.Name == tagString);
if (countRemoved != 1)
{
throw new ArgumentException("Did not remove 1 element from the list of tags");
}
}
private async Task OnDeleteSliderClicked(SearchPageSliderEntry entry)
{
await using var context = await ContextFactory.CreateDbContextAsync();
SearchPageSliderEntry? entryToRemove = await context.SearchPageSliderEntries.Where(en => en.Id == entry.Id).FirstOrDefaultAsync();
if (entryToRemove is null)
{
throw new ArgumentException("The entry to remove was null and not in the database");
}
_homePageConfiguration.SliderEntries!.Remove(entry);
context.SearchPageSliderEntries.Remove(entryToRemove);
await context.SaveChangesAsync();
// Clear the memory cache of the home page values
MemoryCache.Remove(OpenArchivalConstants.HomePageConfigurationCacheKey);
StateHasChanged();
}
}