添加项目文件。

This commit is contained in:
Zel
2025-01-22 23:31:03 +08:00
parent 1b8ba6771f
commit 2ae76476fb
894 changed files with 774558 additions and 0 deletions

View File

@@ -0,0 +1,199 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.AccountProfileViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Account
</h3>
<p>
Configuration for user account.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, new { navigation = Model.AccountProfile.Navigation }, FormMethod.Post, true, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}", enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
<div class="container">
<form>
@Html.HiddenFor(m => m.AccountProfile.UserId)
@Html.HiddenFor(m => m.AccountProfile.Navigation)
<div class="form-group row mb-1">
<label for="Avatar" class="col-sm-2 col-form-label"><strong>Avatar</strong></label>
<div class="col-sm-10">
@if (@Model.AccountProfile.Navigation != "")
{
<img src="@GlobalConfiguration.BasePath/Profile/@Model.AccountProfile.Navigation/Avatar?max=150" class="mb-3" />
}
<input type="file" id="Avatar" name="Avatar" class="form-control-file" onchange="fileCheck(this);" />
</div>
</div>
<div class="form-group row mb-1">
<label for="EmailAddress" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.EmailAddress)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.AccountProfile.EmailAddress, new { @class = "form-control" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.EmailAddress)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="EmailConfirmed" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.EmailConfirmed)</strong></label>
<div class="col-sm-10">
@Html.CheckBoxFor(m => m.AccountProfile.EmailConfirmed, new { @class = "input-control" })
</div>
</div>
<div class="form-group row mb-1">
<label for="AccountName" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.AccountName)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.AccountProfile.AccountName, new { @class = "form-control" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.AccountName)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="FirstName" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.FirstName)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.AccountProfile.FirstName, new { @class = "form-control" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.FirstName)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="LastName" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.LastName)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.AccountProfile.LastName, new { @class = "form-control" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.LastName)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Role" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.Role)</strong></label>
<div class="col-sm-10">
<select name="AccountProfile.Role" id="AccountProfile.Role" class="form-control">
<option value="" style="color:#ccc !important;">Select a role</option>
@foreach (var item in Model.Roles)
{
<option value="@item.Name" selected=@(Model.AccountProfile.Role == item.Name ? "selected" : null)>
@item.Name
</option>
}
</select>
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Role)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Theme" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.Theme)</strong></label>
<div class="col-sm-10">
<select name="AccountProfile.Theme" id="AccountProfile.Theme" class="form-control">
<option value="">System Default</option>
@foreach (var item in Model.Themes)
{
<option value="@item.Name" selected=@(Model.AccountProfile.Theme == item.Name ? "selected" : null)>
@item.Name
</option>
}
</select>
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Theme)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Country" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.Country)</strong></label>
<div class="col-sm-10">
<select name="AccountProfile.Country" id="AccountProfile.Country" class="form-control">
<option value="" style="color:#ccc !important;">Select a country</option>
@foreach (var item in Model.Countries)
{
<option value="@item.Value" selected=@(Model.AccountProfile.Country == item.Value ? "selected" : null)>
@item.Text
</option>
}
</select>
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Country)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Language" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.Language)</strong></label>
<div class="col-sm-10">
<select name="AccountProfile.Language" id="AccountProfile.Language" class="form-control">
<option value="" style="color:#ccc !important;">Select a language</option>
@foreach (var item in Model.Languages)
{
<option value="@item.Value" selected=@(Model.AccountProfile.Language == item.Value ? "selected" : null)>
@item.Text
</option>
}
</select>
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Language)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="TimeZone" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.TimeZone)</strong></label>
<div class="col-sm-10">
<select name="AccountProfile.TimeZone" id="AccountProfile.TimeZone" class="form-control">
<option value="" style="color:#ccc !important;">Select a time-zone</option>
@foreach (var item in Model.TimeZones)
{
<option value="@item.Value" selected=@(Model.AccountProfile.TimeZone == item.Value ? "selected" : null)>
@item.Text
</option>
}
</select>
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.TimeZone)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Password" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.Credential.Password)</strong></label>
<div class="col-sm-10">
@Html.PasswordFor(m => m.Credential.Password, new { @class = "form-control", value = Model.Credential.Password })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Credential.Password)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="ComparePassword" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.Credential.ComparePassword)</strong></label>
<div class="col-sm-10">
@Html.PasswordFor(m => m.Credential.ComparePassword, new { @class = "form-control", value = Model.Credential.Password })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Credential.ComparePassword)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Biography" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.Biography)</strong></label>
<div class="col-sm-10">
@Html.TextAreaFor(m => m.AccountProfile.Biography, new { @class = "form-control", style = "height:200px", Name = "Biography" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Biography)</div>
</div>
</div>
<div class="form-group row mb-1">
<div class="col-sm-10 offset-sm-2">
<button type="submit" class="btn btn btn-primary rounded-0">Save!</button>
</div>
</div>
</form>
</div>
}
<br />
<form action="@GlobalConfiguration.BasePath/Admin/DeleteAccount/@Model.AccountProfile.Navigation"><button type="submit" class="btn btn-danger rounded-0">Delete Account</button></form>

View File

@@ -0,0 +1,90 @@
@model TightWiki.Models.ViewModels.Admin.AccountsViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Accounts
</h3>
<p>
Global configuration for user accounts.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
<a class="btn btn-success btn-thin" href="@GlobalConfiguration.BasePath/Admin/AddAccount">Add new account</a>
<br />
<br />
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<div class="container">
<div class="d-flex justify-content-end mb-4">
<div class="flex-grow-1 me-2">
@Html.TextBoxFor(x => x.SearchString, new { @class = "form-control" })
</div>
<button type="submit" value="Search" class="btn btn-primary">Search</button>
</div>
</div>
@if (Model.Users.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Account")">Account</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "FirstName")">First Name</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "LastName")">Last Name</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Country")">Country</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "TimeZone")">TimeZone</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "EmailAddress")">EmailAddress</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Created")">CreatedDate</a></strong></td>
</tr>
</thead>
@foreach (var user in Model.Users)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/Admin/Account/@user.Navigation">@user.AccountName</a></td>
<td>@user.FirstName</td>
<td>@user.LastName</td>
<td>@user.Country</td>
<td>@user.TimeZone</td>
<td>@user.EmailAddress @Html.Raw(((user.EmailConfirmed == true) ? "&check;" : "")) </td>
<td>@user.CreatedDate</td>
</tr>
}
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>
Either there are no accounts configured or your search criteria returned no results.
</strong>
</div>
}
}
<br />
<a class="btn btn-success btn-thin" href="@GlobalConfiguration.BasePath/Admin/AddAccount">Add new account</a>
<script>
window.onload = function () {
document.getElementById("SearchString").focus();
}
</script>

View File

@@ -0,0 +1,166 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.AccountProfileViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Add Account
</h3>
<p>
Create new user account.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, new { navigation = Model.AccountProfile.Navigation }, FormMethod.Post, true, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}", enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
<div class="container">
<form>
@Html.HiddenFor(m => m.AccountProfile.Navigation)
<div class="form-group row mb-1">
<label for="Avatar" class="col-sm-2 col-form-label"><strong>Avatar</strong></label>
<div class="col-sm-10">
<input type="file" id="Avatar" name="Avatar" class="form-control-file" onchange="fileCheck(this);" />
</div>
</div>
<div class="form-group row mb-1">
<label for="EmailAddress" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.EmailAddress)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.AccountProfile.EmailAddress, new { @class = "form-control", placeholder = "required" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.EmailAddress)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="AccountName" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.AccountName)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.AccountProfile.AccountName, new { @class = "form-control", placeholder = "required" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.AccountName)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="FirstName" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.FirstName)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.AccountProfile.FirstName, new { @class = "form-control", placeholder = "not required" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.FirstName)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="LastName" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.LastName)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.AccountProfile.LastName, new { @class = "form-control", placeholder = "not required" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.LastName)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Role" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.Role)</strong></label>
<div class="col-sm-10">
<select name="AccountProfile.Role" id="AccountProfile.Role" class="form-control">
<option value="" style="color:#ccc !important;">Select a role</option>
@foreach (var item in Model.Roles)
{
<option value="@item.Name" selected=@(Model.AccountProfile.Role == item.Name ? "selected" : null)>
@item.Name
</option>
}
</select>
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Role)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Country" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.Country)</strong></label>
<div class="col-sm-10">
<select name="AccountProfile.Country" id="AccountProfile.Country" class="form-control">
<option value="" style="color:#ccc !important;">Select a country</option>
@foreach (var item in Model.Countries)
{
<option value="@item.Value" selected=@(Model.AccountProfile.Country == item.Value ? "selected" : null)>
@item.Text
</option>
}
</select>
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Country)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Language" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.Language)</strong></label>
<div class="col-sm-10">
<select name="AccountProfile.Language" id="AccountProfile.Language" class="form-control">
<option value="" style="color:#ccc !important;">Select a language</option>
@foreach (var item in Model.Languages)
{
<option value="@item.Value" selected=@(Model.AccountProfile.Language == item.Value ? "selected" : null)>
@item.Text
</option>
}
</select>
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Language)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="TimeZone" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.TimeZone)</strong></label>
<div class="col-sm-10">
<select name="AccountProfile.TimeZone" id="AccountProfile.TimeZone" class="form-control">
<option value="" style="color:#ccc !important;">Select a time-zone</option>
@foreach (var item in Model.TimeZones)
{
<option value="@item.Value" selected=@(Model.AccountProfile.TimeZone == item.Value ? "selected" : null)>
@item.Text
</option>
}
</select>
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.TimeZone)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Password" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.Credential.Password)</strong></label>
<div class="col-sm-10">
@Html.PasswordFor(m => m.Credential.Password, new { @class = "form-control", value = Model.Credential.Password })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Credential.Password)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="ComparePassword" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.Credential.ComparePassword)</strong></label>
<div class="col-sm-10">
@Html.PasswordFor(m => m.Credential.ComparePassword, new { @class = "form-control", value = Model.Credential.Password })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Credential.ComparePassword)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Biography" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.AccountProfile.Biography)</strong></label>
<div class="col-sm-10">
@Html.TextAreaFor(m => m.AccountProfile.Biography, new { @class = "form-control", style = "height:200px", Name = "Biography" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Biography)</div>
</div>
</div>
<div class="form-group row mb-1">
<div class="col-sm-10 offset-sm-2">
<button type="submit" class="btn btn-success rounded-0">Save!</button>
</div>
</div>
</form>
</div>
}

View File

@@ -0,0 +1,78 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.AddEmojiViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<script type="text/javascript">
window.addEventListener('DOMContentLoaded', (event) => {
const imageData = document.getElementById('ImageData');
const categories = document.getElementById('Categories');
const name = document.getElementById('Name');
imageData.addEventListener('change', (event) => {
const selectedFile = event.target.files[0];
name.value = selectedFile.name.substr(0, selectedFile.name.lastIndexOf('.')).replace(/ /g, '-').replace(/_/g, '-');
categories.value = name.value.replace(/-/g, ',');
});
});
</script>
<h3>
Add Emoji
</h3>
<p>
Configuration to add an emoji.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, null, FormMethod.Post, true, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}", enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
@Html.HiddenFor(model => model.OriginalName)
@Html.HiddenFor(model => model.Id)
<div class="container">
<form>
<div class="form-group row mb-1">
<label for="ImageData" class="col-sm-2 col-form-label"><strong>Image</strong></label>
<div class="col-sm-10">
<input type="file" id="ImageData" name="ImageData" class="form-control-file" onchange="fileCheck(this);" accept="image/png, image/jpeg, image/gif" />
</div>
</div>
<div class="form-group row mb-1">
<label for="Name" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.Name)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.Name, new { @class = "form-control", placeholder = "required" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Name)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Categories" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.Categories)</strong> (comma separated)</label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.Categories, new { @class = "form-control" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Categories)</div>
</div>
</div>
<div class="form-group row mb-1">
<div class="col-sm-10 offset-sm-2">
<button type="submit" class="btn btn-success rounded-0">Save!</button>
</div>
</div>
</form>
</div>
}
<br />

View File

@@ -0,0 +1,73 @@
@model TightWiki.Models.ViewModels.Admin.PageCompilationStatisticsViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Compilations
</h3>
<p>
The compilation statistics for all wiki page operations.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateWarnLink(GlobalConfiguration.BasePath,
"Purging the page compilation statistics will delete all of the stored compilation statistics for all pages. Continue?",
"Purge Compilation Statistics", "/Admin/PurgeCompilationStatistics", Context.Request.Path.Value))
<br />
<br />
@if (Model.Statistics.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Namespace")">Name</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "CreatedDate")">Date/Time</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Compilations")">Compilations</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "AvgBuildTimeMs")">Duration</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "AvgWikiMatches")">Matches</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "TotalErrorCount")">Errors</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "AvgOutgoingLinkCount")">Links</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "AvgTagCount")">Tags</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "AvgRawBodySize")">Raw Size</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "AvgWikifiedBodySize")">Wikified Size</a></strong></td>
</tr>
@foreach (var stat in Model.Statistics)
{
<tr>
<td>
@if (string.IsNullOrEmpty(@stat.Namespace) == false)
{
<text><a href="@GlobalConfiguration.BasePath/Admin/Namespace/@stat.Namespace">@stat.Namespace</a> :: </text>
} <a href="@GlobalConfiguration.BasePath/@stat.Navigation">@stat.Title</a>
</td>
<td>@stat.LatestBuild</td>
<td>@stat.Compilations.ToString("N0")</td>
<td>@stat.AvgBuildTimeMs.ToString("N0")ms</td>
<td>@stat.AvgWikiMatches.ToString("N0")</td>
<td>@stat.TotalErrorCount.ToString("N0")</td>
<td>@stat.AvgOutgoingLinkCount.ToString("N0")</td>
<td>@stat.AvgTagCount.ToString("N0")</td>
<td>@NTDLS.Helpers.Formatters.FileSize((long)stat.AvgRawBodySize)</td>
<td>@NTDLS.Helpers.Formatters.FileSize((long)stat.AvgWikifiedBodySize)</td>
</tr>
}
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}

View File

@@ -0,0 +1,145 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.ConfigurationViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Configuration
</h3>
<p>
Global configuration values for the wiki, its functionality, behavior, formatting and branding.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, FormMethod.Post, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
@Html.AntiForgeryToken()
<div class="form-group"><button type="submit" class="btn btn-primary rounded-0">Save!</button></div>
<br />
<br />
<div class="container">
@foreach (var group in Model.Nest)
{
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h4>@group.Name</h4>
<p>@group.Description</p>
</div>
<div class="card-body">
@foreach (var entry in group.Entries)
{
<div class="form-group row mb-3">
<label for="@entry.ConfigurationGroupId:@entry.Id" class="col-sm-2 col-form-label">
<strong>@entry.Name</strong><br />
<small class="text-muted">@entry.Description</small>
</label>
<div class="col-sm-10">
@if ($"{group.Name}:{entry.Name}" == "Customization:Theme")
{
<select name="@entry.ConfigurationGroupId:@entry.Id" id="@entry.ConfigurationGroupId:@entry.Id" class="form-control">
<option value="" style="color:#ccc !important;">Select a Theme</option>
@foreach (var item in Model.Themes)
{
<option value="@item.Name" selected=@(entry.Value == item.Name ? "selected" : null)>@item.Name</option>
}
</select>
}
else if ($"{group.Name}:{entry.Name}" == "Membership:Default Signup Role")
{
<select name="@entry.ConfigurationGroupId:@entry.Id" id="@entry.ConfigurationGroupId:@entry.Id" class="form-control">
<option value="" style="color:#ccc !important;">Select a role</option>
@foreach (var item in Model.Roles)
{
<option value="@item.Name" selected=@(entry.Value == item.Name ? "selected" : null)>@item.Name</option>
}
</select>
}
else if ($"{group.Name}:{entry.Name}" == "Membership:Default TimeZone" || $"{group.Name}:{entry.Name}" == "Customization:Default TimeZone")
{
<select name="@entry.ConfigurationGroupId:@entry.Id" id="@entry.ConfigurationGroupId:@entry.Id" class="form-control">
<option value="" style="color:#ccc !important;">Select a time-zone</option>
@foreach (var item in Model.TimeZones)
{
<option value="@item.Value" selected=@(entry.Value == item.Value ? "selected" : null)>@item.Text</option>
}
</select>
}
else if ($"{group.Name}:{entry.Name}" == "Membership:Default Language" || $"{group.Name}:{entry.Name}" == "Customization:Default Language")
{
<select name="@entry.ConfigurationGroupId:@entry.Id" id="@entry.ConfigurationGroupId:@entry.Id" class="form-control">
<option value="" style="color:#ccc !important;">Select a language</option>
@foreach (var item in Model.Languages)
{
<option value="@item.Value" selected=@(entry.Value == item.Value ? "selected" : null)>@item.Text</option>
}
</select>
}
else if ($"{group.Name}:{entry.Name}" == "Membership:Default Country" || $"{group.Name}:{entry.Name}" == "Customization:Default Country")
{
<select name="@entry.ConfigurationGroupId:@entry.Id" id="@entry.ConfigurationGroupId:@entry.Id" class="form-control">
<option value="" style="color:#ccc !important;">Select a country</option>
@foreach (var item in Model.Countries)
{
<option value="@item.Value" selected=@(entry.Value == item.Value ? "selected" : null)>@item.Text</option>
}
</select>
}
else if (@entry.DataType == "string")
{
if (@entry.IsEncrypted == true)
{
<input type="password" value="@entry.Value" id="@entry.ConfigurationGroupId:@entry.Id" name="@entry.ConfigurationGroupId:@entry.Id" class="form-control">
}
else
{
<input type="text" name="@entry.ConfigurationGroupId:@entry.Id" id="@entry.ConfigurationGroupId:@entry.Id" value="@entry.Value" class="form-control">
}
}
else if (@entry.DataType == "text")
{
<textarea name="@entry.ConfigurationGroupId:@entry.Id" id="@entry.ConfigurationGroupId:@entry.Id" rows="4" class="form-control">@entry.Value</textarea>
}
else if (@entry.DataType == "integer" || @entry.DataType == "decimal")
{
<input type="number" name="@entry.ConfigurationGroupId:@entry.Id" id="@entry.ConfigurationGroupId:@entry.Id" value="@entry.Value" class="form-control">
}
else if (@entry.DataType == "boolean")
{
<div class="form-check form-check-inline">
<input type="radio" id="@entry.ConfigurationGroupId:@entry.Id:1" name="@entry.ConfigurationGroupId:@entry.Id" value="1" class="form-check-input" @(entry.Value == "1" ? "checked" : "")>
<label for="@entry.ConfigurationGroupId:@entry.Id:1" class="form-check-label">Yes</label>
</div>
<div class="form-check form-check-inline">
<input type="radio" id="@entry.ConfigurationGroupId:@entry.Id:0" name="@entry.ConfigurationGroupId:@entry.Id" value="0" class="form-check-input" @(entry.Value == "0" ? "checked" : "")>
<label for="@entry.ConfigurationGroupId:@entry.Id:0" class="form-check-label">No</label>
</div>
}
</div>
</div>
}
</div>
</div>
</div>
</div>
}
</div>
<br />
<div class="form-group"><button type="submit" class="btn btn-primary rounded-0">Save!</button></div>
}

View File

@@ -0,0 +1,57 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.DatabaseViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Database
</h3>
<p>
Various utilities to assist in management of the SQLite database.
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<table class="table fixedTable100 table-striped" border="0" width="100%" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong>Name</strong></td>
<td><strong>Version</strong></td>
<td><strong>Size</strong></td>
<td><strong>Action</strong></td>
</tr>
</thead>
@foreach (var info in Model.Info)
{
<tr>
<td>@info.Name</td>
<td>@info.Version</td>
<td>@NTDLS.Helpers.Formatters.FileSize((long)info.DatabaseSize)</td>
<td>
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateSafeLink(GlobalConfiguration.BasePath,
"This will optimize the database indexes and structure. Continue?",
"Optimize", $"/Admin/Database/Optimize/{info.Name}", Context.Request.Path.Value))
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateSafeLink(GlobalConfiguration.BasePath,
"This will remove empty space from the database, which can free space if a lot of data has been deleted. Continue?",
"Vacuum", $"/Admin/Database/Vacuum/{info.Name}", Context.Request.Path.Value))
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateSafeLink(GlobalConfiguration.BasePath,
"This will check the database integrity and validate all foreign keys. Continue?",
"Verify", $"/Admin/Database/Verify/{info.Name}", Context.Request.Path.Value))
</td>
</tr>
}
</table>
}

View File

@@ -0,0 +1,37 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Profile.DeleteAccountViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@if (string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="card border-danger mb-3">
<div class="card-header bg-danger text-white">
<strong>Delete account "@Model.AccountName"?</strong>
</div>
<div class="card-body">
Deleting "@Model.AccountName" will permanently remove the account. All pages created or modified by this user will be attributed to a stand-in account.
<strong>You will not be able to revert this action.</strong>
<br /><br />
Are you sure you want to continue with this deletion?<br /><br />
@using (Html.BeginForm(null, null, FormMethod.Post, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<div class="form-group"><button type="submit" class="btn btn-danger rounded-0" name="IsActionConfirmed" value="true">Yes</button>&nbsp;&nbsp;<button type="submit" class="btn btn-success rounded-0" name="IsActionConfirmed" value="false">No</button></div>
}
</div>
</div>
}

View File

@@ -0,0 +1,37 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.EmojiViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@if (string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="card card-danger border-danger">
<div class="card-header bg-danger text-white">
<strong>Delete emoji "@Model?.OriginalName"?</strong>
</div>
<div class="card-body">
Deleting the emoji "@Model?.OriginalName" will permanently remove the image. Any references to it will be orphaned.
<strong>You will not be able to revert this deletion.</strong>
<br /><br />
Are you sure you want to continue with this deletion?<br /><br />
@using (Html.BeginForm(null, null, FormMethod.Post, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<div class="form-group"><button type="submit" class="btn btn-danger rounded-0" name="IsActionConfirmed" value="true">Yes</button>&nbsp;&nbsp;<button type="submit" class="btn btn-success rounded-0" name="IsActionConfirmed" value="false">No</button></div>
}
</div>
</div>
}

View File

@@ -0,0 +1,37 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.MenuItemViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@if (string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="card border-danger mb-3">
<div class="card-header bg-danger text-white">
<strong>Delete menu item "@Model.Name"?</strong>
</div>
<div class="card-body">
Deleting "@Model.Name" will permanently remove the menu item.
<strong>You will not be able to revert this action.</strong>
<br /><br />
Are you sure you want to continue with this deletion?<br /><br />
@using (Html.BeginForm(null, null, FormMethod.Post, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<div class="form-group"><button type="submit" class="btn btn-danger rounded-0" name="IsActionConfirmed" value="true">Yes</button>&nbsp;&nbsp;<button type="submit" class="btn btn-success rounded-0" name="IsActionConfirmed" value="false">No</button></div>
}
</div>
</div>
}

View File

@@ -0,0 +1,24 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Page.DeletedPageViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<div class="card border-warning mb-3">
<div class="card-header bg-warning">
<strong>Viewing a deleted page</strong>
</div>
<div class="card-body">
<p class="card-text">
You are viewing a page which was deleted on @Model.DeletedDate by @Model.DeletedByUserName.<br />
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateWarnLink(GlobalConfiguration.BasePath,
"This will restore the deleted page and all of its history. Continue?",
"Restore This Page", "/Admin/RestoreDeletedPage/" + @Model.PageId, "/Admin/DeletedPages", Context.Request.Path.Value))
<br />
</p>
</div>
</div>
@Html.Raw(Model.Body)

View File

@@ -0,0 +1,24 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Page.DeletedPageRevisionViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<div class="card border-warning mb-3">
<div class="card-header bg-warning">
<strong>Viewing a deleted page revision</strong>
</div>
<div class="card-body">
<p class="card-text">
You are viewing a page revision which was deleted on @Model.DeletedDate by @Model.DeletedByUserName.<br />
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateWarnLink(GlobalConfiguration.BasePath,
"This will restore the deleted page and all of its history. Continue?",
"Restore This Revision", $"/Admin/RestoreDeletedPageRevision/{Model.PageId}/{Model.Revision}", "/Admin/DeletedPageRevisions", Context.Request.Path.Value))
<br />
</p>
</div>
</div>
@Html.Raw(Model.Body)

View File

@@ -0,0 +1,69 @@
@model TightWiki.Models.ViewModels.Admin.DeletedPagesRevisionsViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Deleted Page Revisions
</h3>
<p>
Page revision that have been deleted for the given page. These can be purged or restored.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateDangerLink(GlobalConfiguration.BasePath,
$"This will permanently purge all deleted pages revisions for \"{Model.Name}\". Continue?",
"Purge Deleted Revisions", $"/Admin/PurgeDeletedPageRevisions/{Model.PageId}", Context.Request.Path.Value))
<br />
<br />
@if (Model.Revisions.Count > 0)
{
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Revision")">Revision</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "DeletedDate")">Deleted Date</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "DeletedBy")">Deleted By</a></strong></td>
<td><strong>Action</strong></td>
</tr>
</thead>
@foreach (var p in Model.Revisions)
{
<tr>
<td>
<a href="@GlobalConfiguration.BasePath/Admin/DeletedPageRevision/@p.Id/@p.Revision">@p.Revision</a>
</td>
<td>@p.DeletedDate</td>
<td>@p.DeletedByUserName</td>
<td>
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateSafeLink(GlobalConfiguration.BasePath,
"This will restore the deleted page and all of its history. Continue?",
"Restore", $"/Admin/RestoreDeletedPageRevision/{p.Id}/{p.Revision}", Context.Request.Path.Value))
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateDangerLink(GlobalConfiguration.BasePath,
"This will permanently delete the specified page, all revisions and attachments. Continue?",
"Purge", $"/Admin/PurgeDeletedPageRevision/{p.Id}/{p.Revision}", Context.Request.Path.Value))
</td>
</tr>
}
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
}

View File

@@ -0,0 +1,90 @@
@model TightWiki.Models.ViewModels.Admin.DeletedPagesViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Deleted Pages
</h3>
<p>
Pages that have been deleted. Can be purged or restored.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateDangerLink(GlobalConfiguration.BasePath,
"This will permanently purge all deleted pages. Continue?",
"Purge Deleted Pages", "/Admin/PurgeDeletedPages", Context.Request.Path.Value))
<br />
<br />
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<div class="container">
<div class="d-flex justify-content-end mb-4">
<div class="flex-grow-1 me-2">
@Html.TextBoxFor(x => x.SearchString, new { @class = "form-control" })
</div>
<button type="submit" class="btn btn-primary">Search</button>
</div>
</div>
<br />
@if (Model.Pages.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Page")">Page</a></strong></td>
<td><strong>Action</strong></td>
</tr>
</thead>
@foreach (var p in Model.Pages)
{
<tr>
<td>
@if (string.IsNullOrEmpty(@p.Namespace) == false)
{
<text><a href="@GlobalConfiguration.BasePath/Admin/Namespace/@p.Namespace">@p.Namespace</a> :: </text>
} <a href="@GlobalConfiguration.BasePath/Admin/DeletedPage/@p.Id">@p.Title</a>
</td>
<td>
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateSafeLink(GlobalConfiguration.BasePath,
"This will restore the deleted page and all of its history. Continue?",
"Restore", "/Admin/RestoreDeletedPage/" + @p.Id, Context.Request.Path.Value))
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateDangerLink(GlobalConfiguration.BasePath,
"This will permanently delete the specified page, all revisions and attachments. Continue?",
"Purge", "/Admin/PurgeDeletedPage/" + @p.Id, Context.Request.Path.Value))
</td>
</tr>
}
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>
Either the deleted pages queue is empty or your search criteria returned no results.
</strong>
</div>
}
}
<script>
window.onload = function () {
document.getElementById("SearchString").focus();
}
</script>

View File

@@ -0,0 +1,68 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.EmojiViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>Emoji</h3>
<p>Configuration for an emoji.</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, new { navigation = Model.Emoji.Name }, FormMethod.Post, true, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}", enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
@Html.HiddenFor(m => m.OriginalName)
@Html.HiddenFor(m => m.Emoji.Id)
<div class="container">
<div class="form-group row mb-1">
<label for="ImageData" class="col-sm-2 col-form-label"><strong>Image</strong></label>
<div class="col-sm-10">
@if (@Model.Emoji.Name != "")
{
<img src="@GlobalConfiguration.BasePath/File/Emoji/@Model.Emoji.Name" class="mb-3" />
<br />
}
<input type="file" id="ImageData" name="ImageData" class="form-control-file" onchange="fileCheck(this);" accept="image/png, image/jpeg, image/gif" />
</div>
</div>
<div class="form-group row mb-1">
<label for="Name" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.Emoji.Name)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.Emoji.Name, new { @class = "form-control" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Emoji.Name)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Categories" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.Categories)</strong> (comma separated)</label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.Categories, new { @class = "form-control" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Categories)</div>
</div>
</div>
<div class="form-group row mb-1">
<div class="col-sm-10 offset-sm-2">
<button type="submit" class="btn btn-success rounded-0">Save!</button>
</div>
</div>
</div>
}
<br />
<form action="@GlobalConfiguration.BasePath/Admin/DeleteEmoji/@Model.Emoji.Name">
<button type="submit" class="btn btn-danger rounded-0">Delete Emoji</button>
</form>

View File

@@ -0,0 +1,82 @@
@model TightWiki.Models.ViewModels.Admin.EmojisViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Emojis
</h3>
<p>
Global configuration for Emojis.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
<a class="btn btn-success btn-thin" href="@GlobalConfiguration.BasePath/Admin/AddEmoji">Add new emoji</a>
<br />
<br />
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<div class="container">
<div class="d-flex justify-content-end mb-4">
<div class="flex-grow-1 me-2">
@Html.TextBoxFor(x => x.SearchString, new { @class = "form-control" })
</div>
<button type="submit" class="btn btn-primary">Search</button>
</div>
</div>
<br />
@if (Model.Emojis.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Name")">Name</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Shortcut")">Shortcut</a></strong></td>
<td><strong>Image</strong></td>
</tr>
</thead>
@foreach (var emoji in Model.Emojis)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/Admin/Emoji/@emoji.Name">@emoji.Name</a></td>
<td>@emoji.Shortcut</td>
<td><img src="@GlobalConfiguration.BasePath/File/Emoji/@emoji.Name" alt="@emoji.Name" /></td>
</tr>
}
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>
Either there are no emojis configured or your search criteria returned no results.
</strong>
</div>
}
}
<br />
<a class="btn btn-success btn-thin" href="@GlobalConfiguration.BasePath/Admin/AddEmoji">Add new emoji</a>
<script>
window.onload = function () {
document.getElementById("SearchString").focus();
}
</script>

View File

@@ -0,0 +1,48 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.ExceptionViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Exception
</h3>
<p>
Server and page exception.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<tr><td><strong>Id</strong></td></tr>
<tr><td>@Model.Exception.Id</td></tr>
<tr><td><strong>Date / Time</strong></td></tr>
<tr><td>@Model.Exception.CreatedDate</td></tr>
@if (string.IsNullOrEmpty(Model.Exception.Text) == false)
{
<tr><td><strong>Text</strong></td></tr>
<tr><td>@Model.Exception.Text</td></tr>
}
@if (string.IsNullOrEmpty(Model.Exception.ExceptionText) == false)
{
<tr><td><strong>Exception</strong></td></tr>
<tr><td>@Model.Exception.ExceptionText</td></tr>
}
@if (string.IsNullOrEmpty(Model.Exception.StackTrace) == false)
{
<tr><td><strong>Stack Trace</strong></td></tr>
<tr><td>@Model.Exception.StackTrace</td></tr>
}
</table>
}

View File

@@ -0,0 +1,67 @@
@model TightWiki.Models.ViewModels.Admin.ExceptionsViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Exceptions
</h3>
<p>
Server and page exceptions.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateWarnLink(GlobalConfiguration.BasePath,
"This will permanently purge all exceptions. Continue?",
"Purge Exceptions", "/Admin/PurgeExceptions", Context.Request.Path.Value))
<br />
<br />
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
@if (Model.Exceptions.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Id")">Id</a></strong></td>
<td><strong>Text</strong></td>
<td><strong>Exception</strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "CreatedDate")">Date/Time</a></strong></td>
</tr>
</thead>
@foreach (var ex in Model.Exceptions)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/Admin/Exception/@ex.Id">@ex.Id</a></td>
<td>@ex.Text</td>
<td>@ex.ExceptionText</td>
<td>@ex.CreatedDate</td>
</tr>
}
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>
The exception log is currently empty.
</strong>
</div>
}
}

View File

@@ -0,0 +1,60 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.MenuItemViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>Site Menu Item</h3>
<p>Global configuration for the site menu item.</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, FormMethod.Post, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
@Html.AntiForgeryToken()
@Html.HiddenFor(m => m.Id)
<div class="container">
<div class="form-group row mb-1">
<label for="Name" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.Name)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.Name, new { @class = "form-control", placeholder = "required" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Name)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Link" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.Link)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.Link, new { @class = "form-control", placeholder = "required" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Link)</div>
</div>
</div>
<div class="form-group row mb-1">
<label for="Ordinal" class="col-sm-2 col-form-label"><strong>@Html.LabelFor(m => m.Ordinal)</strong></label>
<div class="col-sm-10">
@Html.TextBoxFor(m => m.Ordinal, new { @class = "form-control", placeholder = "required" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Ordinal)</div>
</div>
</div>
<div class="form-group row mb-1">
<div class="col-sm-10 offset-sm-2">
<button type="submit" class="btn btn-primary rounded-0">Save!</button>
</div>
</div>
</div>
}
<br />
<form action="@GlobalConfiguration.BasePath/Admin/DeleteMenuItem/@Model.Id">
<button type="submit" class="btn btn-danger rounded-0">Delete Menu Item</button>
</form>

View File

@@ -0,0 +1,66 @@
@model TightWiki.Models.ViewModels.Admin.MenuItemsViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Site Menu Items
</h3>
<p>
Global configuration for the site menu.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
<a class="btn btn-success btn-thin" href="@GlobalConfiguration.BasePath/Admin/MenuItem">Add new item</a>
<br />
<br />
@using (Html.BeginForm(null, null, FormMethod.Post, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
@Html.AntiForgeryToken()
@if (Model.Items.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Name")">Name</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Link")">Link</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Ordinal")">Ordinal</a></strong></td>
</tr>
</thead>
@foreach (var item in Model.Items)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/Admin/MenuItem/@item.Id">@item.Name</a></td>
<td>@item.Link</td>
<td>@item.Ordinal</td>
</tr>
}
</table>
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>
Their are no menu items configured.
</strong>
</div>
}
}
<br />
<a class="btn btn-success btn-thin" href="@GlobalConfiguration.BasePath/Admin/MenuItem">Add new item</a>

View File

@@ -0,0 +1,93 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.MetricsViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Metrics
</h3>
<p>
<a href="http://NetworkDLS.com/">NetworkDLS</a> <a href="http://TightWiki.com/">TightWiki</a> version @Model.ApplicationVersion<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
<h2>General</h2>
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<tr>
<td><strong>Pages</strong></td>
<td><strong>Page Revisions</strong></td>
<td><strong>Page Attachments</strong></td>
<td><strong>Inter-Page Links</strong></td>
</tr>
<tr>
<td><a href="@GlobalConfiguration.BasePath/Admin/Pages">@Model.Metrics.Pages.ToString("N0")</a></td>
<td>@Model.Metrics.PageRevisions.ToString("N0")</td>
<td>@Model.Metrics.PageAttachments.ToString("N0")</td>
<td>@Model.Metrics.IntraLinks.ToString("N0")</td>
</tr>
<tr>
<td><strong>Page Tags</strong></td>
<td><strong>Page Attachment Revisions</strong></td>
<td><strong>Page Search Tokens</strong></td>
<td><strong>Namespaces</strong></td>
</tr>
<tr>
<td>@Model.Metrics.PageTags.ToString("N0")</td>
<td>@Model.Metrics.PageAttachmentRevisions.ToString("N0")</td>
<td><a href="@GlobalConfiguration.BasePath/Page/Search">@Model.Metrics.PageSearchTokens.ToString("N0")</a></td>
<td><a href="@GlobalConfiguration.BasePath/Admin/Namespaces">@Model.Metrics.Namespaces.ToString("N0")</a></td>
</tr>
<tr>
<td><strong>Users</strong></td>
<td><strong>Profiles</strong></td>
<td><strong>Exceptions</strong></td>
<td><strong>&nbsp;</strong></td>
</tr>
<tr>
<td><a href="@GlobalConfiguration.BasePath/Admin/Accounts">@Model.Metrics.Users.ToString("N0")</a></td>
<td><a href="@GlobalConfiguration.BasePath/Admin/Accounts">@Model.Metrics.Profiles.ToString("N0")</a></td>
<td><a href="@GlobalConfiguration.BasePath/Admin/Exceptions">@Model.Metrics.Exceptions.ToString("N0")</a></td>
<td>&nbsp;</td>
</tr>
</table>
<h2>Cache</h2>
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateWarnLink(GlobalConfiguration.BasePath,
"Flushing the memory cache will cause any cached items to be dropped and require database hits as pages are requested. Continue?",
"Purge Memory Cache", "/Admin/PurgeMemoryCache", Context.Request.Path.Value))
<br />
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<tr>
<td><strong>Items</strong></td>
<td><strong>Puts</strong></td>
<td><strong>Gets</strong></td>
<td><strong>Hits</strong></td>
<td><strong>Misses</strong></td>
<td><strong>Limit(MB)</strong></td>
</tr>
<tr>
<td>@TightWiki.Caching.WikiCache.CacheItemCount.ToString("N0")</td>
<td>@TightWiki.Caching.WikiCache.CachePuts.ToString("N0")</td>
<td>@TightWiki.Caching.WikiCache.CacheGets.ToString("N0")</td>
<td>@TightWiki.Caching.WikiCache.CacheHits.ToString("N0")</td>
<td>@TightWiki.Caching.WikiCache.CacheMisses.ToString("N0")</td>
<td>@NTDLS.Helpers.Formatters.FileSize((long)TightWiki.Caching.WikiCache.CacheMemoryLimit)</td>
</tr>
</table>

View File

@@ -0,0 +1,58 @@
@model TightWiki.Models.ViewModels.Admin.MissingPagesViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Missing Pages
</h3>
<p>
Pages that have been linked to, but do not exist.<br /><br />
</p>
<td height="52" valign="middle" align="left">
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
@if (Model.Pages.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "SourcePage")">Source Page</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "TargetPage")">Target Page</a></strong></td>
</tr>
</thead>
@foreach (var p in Model.Pages)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/@p.SourcePageNavigation">@p.SourcePageName</a></td>
<td><a href="@GlobalConfiguration.BasePath/@p.TargetPageNavigation/Edit?Name=@p.TargetPageName">@p.TargetPageName</a></td>
</tr>
}
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>
There are currently no missing pages being referenced by other wiki pages.
</strong>
</div>
}
}

View File

@@ -0,0 +1,81 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.PageModerateViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Page Moderate
</h3>
<p>
Browse pages marked with various processing instructions to see what is in draft, pending deletion, protected, etc.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<div class="container">
<div class="row mb-3">
<div class="col-md-6">
@Html.DropDownListFor(m => m.Instruction,
new SelectList(Model.Instructions),
"Select an instruction",
new { @class = "form-control", id = "instructionDropdown" })
</div>
<div class="col-md-6">
<button type="submit" class="btn btn-primary">Refresh</button>
</div>
</div>
</div>
<br />
@if (Model.Pages.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" width="100%" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong>Name</strong></td>
<td><strong>Revision</strong></td>
<td><strong>Modified By</strong></td>
<td><strong>Modified Date</strong></td>
</tr>
</thead>
@foreach (var p in Model.Pages)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/@p.Navigation">@p.Name</a></td>
<td><a href="@GlobalConfiguration.BasePath/@p.Navigation/Revisions">@p.Revision</a></td>
<td>@p.ModifiedByUserName</td>
<td>@p.ModifiedDate</td>
</tr>
}
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>
There are no pages which contain the selected processing instruction.
</strong>
</div>
}
}
<script>
document.getElementById("instructionDropdown").addEventListener("change", function () {
this.form.submit();
});
</script>

View File

@@ -0,0 +1,50 @@
@model TightWiki.Models.ViewModels.Admin.NamespaceViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Namespace
</h3>
<p>
All pages contained in the namespace.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Name")">Name</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Revision")">Revision</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "ModifiedBy")">Modified By</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "ModifiedDate")">Modified Date</a></strong></td>
</tr>
</thead>
@foreach (var p in Model.Pages)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/@p.Navigation">@p.Name</a></td>
<td><a href="@GlobalConfiguration.BasePath/@p.Navigation/Revisions">@p.Revision</a></td>
<td>@p.ModifiedByUserName</td>
<td>@p.ModifiedDate</td>
</tr>
}
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}

View File

@@ -0,0 +1,57 @@
@model TightWiki.Models.ViewModels.Admin.NamespacesViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Namespaces
</h3>
<p>
All namespaces contained in the wiki.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
@if (Model.Namespaces.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Name")">Name</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Pages")">Pages</a></strong></td>
</tr>
</thead>
@foreach (var p in Model.Namespaces)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/Admin/Namespace/@(p.Namespace?? string.Empty)">@(string.IsNullOrEmpty(p.Namespace) ? "(Default)" : p.Namespace)</a></td>
<td>@p.CountOfPages.ToString("N0")</td>
</tr>
}
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>
There are currently no pages which exist within namespaces.
</strong>
</div>
}
}

View File

@@ -0,0 +1,68 @@
@model TightWiki.Models.ViewModels.Admin.OrphanedPageAttachmentsViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Orphaned Page Attachments
</h3>
<p>
These are all of the page attachments that are no longer attached to any page revision.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateDangerLink(GlobalConfiguration.BasePath,
"This will permanently purge all orphaned page attachments. Continue?",
"Purge Orphaned Attachments", "/Admin/PurgeOrphanedAttachments", Context.Request.Path.Value))
<br />
<br />
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
@if (Model.Files.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Page")">Page</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "File")">File</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Size")">Size</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Revision")">Revision</a></strong></td>
<td><strong>Action</strong></td>
</tr>
@foreach (var p in Model.Files)
{
<tr>
<td>
@if (string.IsNullOrEmpty(@p.Namespace) == false)
{
<text><a href="@GlobalConfiguration.BasePath/Admin/Namespace/@p.Namespace">@p.Namespace</a> :: </text>
} <a href="@GlobalConfiguration.BasePath/@p.PageNavigation">@p.PageTitle</a>
</td>
<td><a href="@GlobalConfiguration.BasePath/File/Binary/@p.PageNavigation/@p.FileNavigation/@p.FileRevision">@p.FileName</a></td>
<td>@NTDLS.Helpers.Formatters.FileSize((long)@p.Size)</td>
<td>@p.FileRevision</td>
<td>
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateDangerLink(GlobalConfiguration.BasePath,
"This will permanently delete the specified attachment. Continue?",
"Delete", $"/Admin/PurgeOrphanedAttachment/{@p.PageFileId}/{@p.FileRevision}", Context.Request.Path.Value))
</td>
</tr>
}
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
}

View File

@@ -0,0 +1,75 @@
@model TightWiki.Models.ViewModels.Admin.PageRevisionsViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Page revisions for <a href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation">@sessionState.Page.Name</a>.
</h3>
<p>
All changes that have been made to the page.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
@if (Model.Revisions.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Revision")">Revision</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "ModifiedBy")">Modified By</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "ModifiedDate")">Modified Date</a></strong></td>
<td><strong>Summary</strong></td>
<td><strong>Action</strong></td>
</tr>
</thead>
<tbody>
@foreach (var h in Model.Revisions)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation/@h.Revision" target="_blank" rel="noopener">@Html.DisplayTextFor(x => h.Revision)</a></td>
<td><a href="@GlobalConfiguration.BasePath/Profile/@h.ModifiedByUserName/Public">@Html.DisplayTextFor(x => h.ModifiedByUserName)</a></td>
<td>@Html.DisplayTextFor(x => h.ModifiedDate)</td>
<td>@Html.DisplayTextFor(x => h.ChangeSummary)</td>
<td>
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateWarnLink(GlobalConfiguration.BasePath,
$"Reverting {h.Name} from revision {h.HighestRevision} to {h.Revision} will rollback {h.HigherRevisionCount} changes.<br />"
+ "Reverting does not mean that changes will be lost however, the revert process will create a new revision with the reverted changes.<br /><br />"
+ "Are you sure you want to continue?<br /><br />",
"Revert", $"/Admin/RevertPageRevision/{h.Navigation}/{h.Revision}", Context.Request.Path.Value))
@Html.Raw(TightWiki.Library.ConfirmActionHelper.GenerateDangerLink(GlobalConfiguration.BasePath,
$"Deleting revision {h.Revision} of \"{h.Name}\" will move the page revision to the deletion queue. This action can only be undone by an administrator or moderator. Continue?",
"Delete", $"/Admin/DeletePageRevision/{h.Navigation}/{h.Revision}", Context.Request.Path.Value))
</td>
</tr>
}
</tbody>
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>
The selected page does not have any revisions.
</strong>
</div>
}
}

View File

@@ -0,0 +1,104 @@
@model TightWiki.Models.ViewModels.Admin.PagesViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Pages
</h3>
<p>
All pages contained in the wiki.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@Html.Raw(ConfirmActionHelper.GenerateWarnLink(GlobalConfiguration.BasePath,
"This will rebuild all pages in the wiki. This could take some time! Continue?",
"Rebuild All Pages", "/Admin/RebuildAllPages", Context.Request.Path.Value))
@Html.Raw(ConfirmActionHelper.GenerateWarnLink(GlobalConfiguration.BasePath,
"This will compile all pages in the wiki and store them in cache. This could take some time! Continue?",
"Pre-Cache All Pages", "/Admin/PreCacheAllPages", Context.Request.Path.Value))
@Html.Raw(ConfirmActionHelper.GenerateDangerLink(GlobalConfiguration.BasePath,
"Truncating the page revisions will delete all the revision history for all pages and page attachments. This will leave only the most current revision of all objects. This is generally considered a big deal! Continue?",
"Truncate Page Revisions", "/Admin/TruncatePageRevisions", Context.Request.Path.Value))
<br />
<br />
@using (Html.BeginForm(null, null, FormMethod.Get, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<div class="container">
<div class="d-flex justify-content-end mb-4">
<div class="flex-grow-1 me-2">
@Html.TextBoxFor(x => x.SearchString, new { @class = "form-control" })
</div>
<button type="submit" class="btn btn-primary">Search</button>
</div>
</div>
<br />
@if (Model.Pages.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Name")">Name</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Revision")">Revision</a></strong></td>
<td><strong>Deleted Revisions</strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "ModifiedBy")">Modified By</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "ModifiedDate")">Modified Date</a></strong></td>
<td><strong>Action</strong></td>
</tr>
</thead>
@foreach (var p in Model.Pages)
{
<tr>
<td>
@if (string.IsNullOrEmpty(@p.Namespace) == false)
{
<text><a href="@GlobalConfiguration.BasePath/Admin/Namespace/@p.Namespace">@p.Namespace</a> :: </text>
} <a href="@GlobalConfiguration.BasePath/@p.Navigation">@p.Title</a>
</td>
<td><a href="@GlobalConfiguration.BasePath/Admin/PageRevisions/@p.Navigation">@p.Revision</a></td>
<td><a href="@GlobalConfiguration.BasePath/Admin/DeletedPageRevisions/@p.Id">@p.DeletedRevisionCount</a></td>
<td>@p.ModifiedByUserName</td>
<td>@p.ModifiedDate</td>
<td>
@Html.Raw(ConfirmActionHelper.GenerateDangerLink(GlobalConfiguration.BasePath,
$"Deleting \"{p.Name}\" will move the page, all {p.Revision} revisions and associated file attachments to the deletion queue. This action can only be undone by an administrator or moderator. Continue?",
"Delete", "/Admin/DeletePage/" + @p.Id, Context.Request.Path.Value))
</td>
</tr>
}
</table>
@Html.Raw(PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>
Either there are no pages or your search criteria returned no results.
</strong>
</div>
}
}
<script>
window.onload = function () {
document.getElementById("SearchString").focus();
}
</script>

View File

@@ -0,0 +1,43 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Admin.RoleViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Role '@Model.Name'.
</h3>
<p>
Role membership.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, FormMethod.Post, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
@Html.AntiForgeryToken()
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong>Name</strong></td>
</tr>
</thead>
@foreach (var user in Model.Users)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/Admin/Account/@user.Navigation">@user.AccountName</a></td>
</tr>
}
</table>
}

View File

@@ -0,0 +1,57 @@
@model TightWiki.Models.ViewModels.Admin.RolesViewModel
@using TightWiki.Library
@using TightWiki.Models
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Roles
</h3>
<p>
Global configuration for security roles and membership.<br /><br />
</p>
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">@Html.Raw(Model.SuccessMessage)</div>
}
@using (Html.BeginForm(null, null, FormMethod.Post, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
@Html.AntiForgeryToken()
@if (Model.Roles.Count > 0)
{
<table class="table fixedTable100 table-striped" border="0" width="100%" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Name")">Name</a></strong></td>
<td><strong><a href="?@QueryStringConverter.OrderHelper(sessionState, "Description")">Description</a></strong></td>
</tr>
</thead>
@foreach (var role in Model.Roles)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/Admin/Role/@role.Name">@role.Name</a></td>
<td>@role.Description</td>
</tr>
}
</table>
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>
There are no security roles configured.
</strong>
</div>
}
}