添加项目文件。

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>
}
}

View File

@@ -0,0 +1,96 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.File.FileAttachmentViewModel
@{
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<style>
.list-group-item:nth-child(odd) {
background-color: #f8f9fa;
}
</style>
@foreach (var file in sessionState.UserTheme.Files)
{
<link rel="stylesheet" href="@Url.Content($"~{file}")" />
}
<link id="themeStylesheet" rel="stylesheet" href="" />
<script>
$(document).ready(function () {
localStorage.setItem('theme', 'dark');
$('nav').removeClass('navbar-light bg-white').addClass('@sessionState.UserTheme.ClassNavBar');
$('a.nav-link').removeClass('text-dark').addClass('@sessionState.UserTheme.ClassNavLink');
$('a.dropdown-item').removeClass('text-dark').addClass('@sessionState.UserTheme.ClassDropdown');
$('span.glyphicon').removeClass('text-dark').addClass('@sessionState.UserTheme.ClassBranding');
$('a[href="#detachLink"]').click(function (e) {
var fileName = e.target.id;
if (confirm("Are you sure you want to detach '" + fileName + "'?")) {
$.ajax({
type: 'POST',
url: '@GlobalConfiguration.BasePath/File/Detach/@Model.PageNavigation/' + fileName + '/@Model.PageRevision',
success: function (result) {
$("#uploadedFiles").load("@GlobalConfiguration.BasePath/File/PageAttachments/@Model.PageNavigation");
}
});
}
});
});
function copyToClipboard(text, element) {
var ext = text.split('.').pop().toLowerCase();
if (ext.includes('png') || ext.includes('jpg') || ext.includes('jpeg') || ext.includes('gif') || ext.includes('bmp')) {
text = '##Image(' + text + ')';
}
else {
text = '##File(' + text + ')';
}
navigator.clipboard.writeText(text).then(function () {
var originalText = element.innerHTML;
var originalClass = element.className;
element.innerHTML = '<i class="bi bi-check-circle"></i> Tag Copied';
element.className = 'btn btn-sm btn-success';
setTimeout(function () {
element.innerHTML = originalText;
element.className = originalClass;
}, 2000);
}).catch(function (error) {
console.error('Error copying text: ', error);
});
}
</script>
</head>
<body>
@foreach (var x in Model.Files)
{
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<a href="@GlobalConfiguration.BasePath/File/Binary/@x.PageNavigation/@x.FileNavigation" target="_blank">@x.Name</a> (@x.FriendlySize)
<a href="@GlobalConfiguration.BasePath/File/Revisions/@x.PageNavigation/@x.FileNavigation" target="_blank">@x.FileRevision revisions</a>
</div>
<div class="btn-group">
<button class="btn btn-sm btn-outline-primary" onclick="copyToClipboard('@x.Name', this); return false;">
<i class="bi bi-clipboard"></i> Copy Tag
</button>&nbsp;
@if (@sessionState.CanDelete == true)
{
<a href="#detachLink" id="@x.FileNavigation" class="btn btn-sm btn-outline-danger">Detach</a>
}
</div>
</div>
}
</body>
</html>

View File

@@ -0,0 +1,51 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.File.PageFileRevisionsViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Page File Revisions
</h3>
<p>
All changes that have been made to the attached file. <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>Revision</strong></td>
<td><strong>Modified By</strong></td>
<td><strong>Modified Date</strong></td>
<td><strong>Summary</strong></td>
</tr>
</thead>
<tbody>
@foreach (var h in Model.Revisions)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/File/Binary/@Model.PageNavigation/@Model.FileNavigation/@h.FileRevision" target="_blank" rel="noopener">@Html.DisplayTextFor(x => h.FileRevision)</a></td>
<td><a href="@GlobalConfiguration.BasePath/Profile/@h.CreatedByUserName/Public">@Html.DisplayTextFor(x => h.CreatedByUserName)</a></td>
<td>@Html.DisplayTextFor(x => h.CreatedDate)</td>
<td>@Html.DisplayTextFor(x => h.FriendlySize)</td>
</tr>
}
</tbody>
</table>
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}

View File

@@ -0,0 +1,80 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Page.PageCommentsViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Comments for <a href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation">@sessionState.Page.Name</a>.
</h3>
<p>
All comment made on the given page.
</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}" }))
{
if (sessionState.IsAuthenticated == true)
{
<div class="container">
<div class="d-flex justify-content-end mb-4">
<div class="flex-grow-1 me-2">
<input type="text" name="Comment" id="Comment" class="form-control" placeholder="Type comment..." />
</div>
<button type="submit" value="Find" class="btn btn-primary">Post</button>
</div>
</div>
}
else
{
@:<a href="@GlobalConfiguration.BasePath/Identity/Account/Login?returnUrl=@Context.Request.Path.Value">Login to leave a comment</a>.
}
if (Model.Comments.Count > 0)
{
<div class="container mt-4">
@foreach (var h in Model.Comments)
{
<div class="border rounded mb-2 p-2">
<p class="mb-1">@Html.Raw(h.Body)</p>
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center small text-muted mb-0">
<img src="/Profile/@h.UserNavigation/Avatar?Exact=16" class="rounded-circle me-2" alt="Avatar" />
@if (TightWiki.Models.GlobalConfiguration.EnablePublicProfiles)
{
<a href="@GlobalConfiguration.BasePath/Profile/@h.UserNavigation/Public" class="small text-decoration-none mb-0">@Html.DisplayTextFor(x => h.UserName)</a>
}
else
{
<span class="small text-decoration-none mb-0">@Html.DisplayTextFor(x => h.UserName)</span>
}
&nbsp;&nbsp;<p class="small text-decoration-none text-muted mb-0">@Html.DisplayTextFor(x => h.CreatedDate)</p>
</div>
@if (sessionState.CanModerate == true || h.UserId == sessionState.Profile?.UserId)
{
<a href="?Delete=@h.Id" class="small text-danger text-decoration-none" onclick="return confirm('Are you sure you want to delete this comment?')">Delete</a>
}
</div>
</div>
}
</div>
}
else
{
<div class="d-flex small text-muted mb-0">
<strong>The page does not have any comments, why don't you add one?</strong>
</div>
}
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}

View File

@@ -0,0 +1,38 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Page.PageDeleteViewModel
@{
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 page "@Model.PageName"?</strong>
</div>
<div class="card-body">
Deleting "@Model.PageName" will move the page, all @Model.PageRevision revisions and @Model.CountOfAttachments file attachments to the deletion queue.
This action can only be undone by an administrator or moderator.
<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,98 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Page.PageDisplayViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
@if (Model.Revision < Model.MostCurrentRevision)
{
<div class="card border-info mb-3">
<div class="card-header bg-info">
<strong>Viewing a historical version</strong>
</div>
<div class="card-body">
<p class="card-text">
You are viewing revision @Model.Revision of "@Model.Name" modified by @Model.ModifiedByUserName on @Model.ModifiedDate.<br />
<a class="btn btn-success btn-thin" href="@GlobalConfiguration.BasePath/@Model.Navigation">View latest revision</a>
@if(sessionState.CanModerate)
{
<a class="btn btn-warning btn-thin" href="@GlobalConfiguration.BasePath/@Model.Navigation/revert/@Model.Revision">Revert to revision @Model.Revision</a>
}
</p>
</div>
</div>
}
@Html.Raw(Model.Body)
@if (TightWiki.Models.GlobalConfiguration.ShowLastModifiedOnPageFooter && string.IsNullOrWhiteSpace(@Model.ModifiedByUserName) == false && Model.HideFooterLastModified != true)
{
<br />
if (TightWiki.Models.GlobalConfiguration.EnablePublicProfiles)
{
<small><cite title="Modified By">Last modified by <a href="@GlobalConfiguration.BasePath/Profile/@Model.ModifiedByUserName/Public">@Model.ModifiedByUserName</a> @@ @Model.ModifiedDate</cite></small>
}
else
{
<small><cite title="Modified By">Last modified by @Model.ModifiedByUserName @@ @Model.ModifiedDate</cite></small>
}
<br />
}
@if (Model.HideFooterComments == false)
{
@if (TightWiki.Models.GlobalConfiguration.EnablePageComments && TightWiki.Models.GlobalConfiguration.ShowCommentsOnPageFooter)
{
<hr class="mt-5 mb-5">
<h2>Comments</h2>
}
@if (TightWiki.Models.GlobalConfiguration.EnablePageComments && TightWiki.Models.GlobalConfiguration.ShowCommentsOnPageFooter)
{
if (@sessionState.IsAuthenticated == true)
{
<form method="post" action="@GlobalConfiguration.BasePath/@sessionState.PageNavigation/Comments">
<div class="container">
<div class="d-flex justify-content-end mb-4">
<div class="flex-grow-1 me-2">
<input type="text" name="Comment" id="Comment" class="form-control" placeholder="Type comment..." />
</div>
<button type="submit" value="Post" class="btn btn-primary">Post</button>
</div>
</div>
</form>
}
else
{
@:<a href="@GlobalConfiguration.BasePath/Identity/Account/Login?returnUrl=@Context.Request.Path.Value">Login to leave a comment</a>.
<br />
}
}
@if (TightWiki.Models.GlobalConfiguration.EnablePageComments && TightWiki.Models.GlobalConfiguration.ShowCommentsOnPageFooter && Model.Comments != null && Model.Comments.Count > 0)
{
<div class="container mt-4">
@foreach (var h in Model.Comments)
{
<div class="border rounded mb-2 p-2">
<p class="mb-1">@Html.Raw(h.Body)</p>
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center small text-muted mb-0">
<img src="@GlobalConfiguration.BasePath/Profile/@h.UserNavigation/Avatar?Exact=16" class="rounded-circle me-2" alt="Avatar" />
<a href="@GlobalConfiguration.BasePath/Profile/@h.UserNavigation/Public" class="small text-decoration-none mb-0">@Html.DisplayTextFor(x => h.UserName)</a>
&nbsp;&nbsp;<p class="small text-decoration-none text-muted mb-0">@Html.DisplayTextFor(x => h.CreatedDate)</p>
</div>
@if (sessionState.CanModerate == true || h.UserId == sessionState.Profile?.UserId)
{
<a href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation/Comments?Delete=@h.Id" class="small text-danger text-decoration-none" onclick="return confirm('Are you sure you want to delete this comment?')">Delete</a>
}
</div>
</div>
}
</div>
}
@if (TightWiki.Models.GlobalConfiguration.EnablePageComments && TightWiki.Models.GlobalConfiguration.ShowCommentsOnPageFooter)
{
<a href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation/Comments">View all comments</a>
}
}

View File

@@ -0,0 +1,218 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Page.PageEditViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<link rel="stylesheet" href=@Url.Content("~/codemirror/codemirror.css")>
<link rel="stylesheet" href=@Url.Content("~/codemirror/theme/light.css")>
<link rel="stylesheet" href=@Url.Content("~/codemirror/theme/dark.css")>
<link rel="stylesheet" href=@Url.Content("~/codemirror/addon/scroll/simplescrollbars.css")>
<script src="@Url.Content("~/codemirror/codemirror.js")"></script>
<script src="@Url.Content("~/codemirror/addon/edit/matchbrackets.js")"></script>
<script src="@Url.Content("~/codemirror/mode/tightwiki/tightwiki.js")"></script>
<script src="@Url.Content("~/codemirror/addon/scroll/simplescrollbars.js")"></script>
<style type="text/css">
.CodeMirror {
border: 1px solid #eee;
height: 50em;
}
.active {
background-color: lightgreen !important;
}
#dropSection {
width: 100%; /* Makes the section fully responsive */
height: auto; /* Allow the height to adjust with content */
min-height: 150px; /* Minimum height to allow space for content */
border: 2px dashed #007bff;
border-radius: 5px;
text-align: center;
font-size: 18px;
color: #007bff;
margin-bottom: 20px;
padding: 20px;
background-color: var(--bs-card-bg); /* Uses Bootstrap background color */
word-wrap: break-word; /* Forces long words to break and wrap */
white-space: normal; /* Ensures normal text wrapping */
}
#dropSection span {
display: inline-block; /* Ensure the text behaves like inline elements */
max-width: 100%; /* Restrict the width to 100% of the container */
line-height: normal; /* Use normal line height for multi-line text */
}
#dropSection.dragover {
background-color: #e9ecef;
border-color: #007bff;
color: #007bff;
}
</style>
<script>
function readyFn(jQuery) {
$("#uploadedFiles").load("@GlobalConfiguration.BasePath/File/PageAttachments/@sessionState.PageNavigationEscaped");
}
$(document).ready(readyFn);
document.addEventListener("DOMContentLoaded", function () {
const dropSection = document.getElementById('dropSection');
const fileInput = document.getElementById('fileInput');
const uploadStatus = document.getElementById('uploadStatus');
// Handle dragover event
dropSection.addEventListener('dragover', (e) => {
e.preventDefault();
dropSection.classList.add('dragover');
dropSection.textContent = "Drop files here...";
});
// Handle dragleave event
dropSection.addEventListener('dragleave', () => {
dropSection.classList.remove('dragover');
dropSection.textContent = "Attach files by dropping them here or by manually selecting them below:";
});
// Handle drop event
dropSection.addEventListener('drop', (e) => {
e.preventDefault();
dropSection.classList.remove('dragover');
dropSection.textContent = "Attach files by dropping them here or by manually selecting them below:";
const files = e.dataTransfer.files;
handleFileUpload(files);
});
// Open file input dialog on click
dropSection.addEventListener('click', () => {
fileInput.click();
});
// Handle file input change (manual upload)
fileInput.addEventListener('change', () => {
const files = fileInput.files;
handleFileUpload(files);
});
// Function to handle file upload
function handleFileUpload(files) {
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append('postedFiles', files[i]);
}
uploadFiles(formData);
}
// Function to upload files using fetch API
function uploadFiles(formData) {
fetch('@Url.Action("UploadDragDrop", "File")/@sessionState.PageNavigationEscaped', {
method: 'POST',
body: formData
})
.then(response => response.json()) // Read the response as JSON
.then(result => {
if (result.success) {
uploadStatus.innerHTML = `<p>Upload successful: ${result.message}</p>`;
} else {
uploadStatus.innerHTML = `<p>Error: ${result.message}</p>`;
}
$("#uploadedFiles").load("@GlobalConfiguration.BasePath/File/PageAttachments/@sessionState.PageNavigationEscaped");
})
.catch(error => {
uploadStatus.innerHTML = `<p>Unexpected Error: ${error.message}</p>`;
});
}
});
</script>
<div class="bodyDiv">
<div class="card">
<div class="card-header"><h3>Content</h3></div>
<div class="card-body">
@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.HiddenFor(x => x.Id)
<div class="form-group">
<button type="submit" class="btn btn-primary rounded-0">Save</button>
<a href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation" target="_blank" rel="noopener" class="btn btn-success rounded-0" role="button">View</a>
</div>
<br />
<strong>@Html.LabelFor(x => x.Name)</strong>
<br />
@Html.TextBoxFor(x => x.Name, new { style = "width:50%" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Name)</div>
<br />
<strong>@Html.LabelFor(x => x.Description)</strong>
<br />
@Html.TextBoxFor(x => x.Description, new { style = "width:90%" })
<div class="text-danger">@Html.ValidationMessageFor(m => m.Description)</div>
<br />
<strong>@Html.LabelFor(x => x.Body)</strong>
<br />
<div class="text-danger">@Html.ValidationMessageFor(m => m.Body)</div>
<textarea id=Body name="Body">@Model.Body</textarea>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("Body"), {
theme: "@sessionState.UserTheme.EditorTheme",
lineNumbers: true,
mode: "text/x-tightwiki",
matchBrackets: true,
viewportMargin: Infinity,
lineWrapping: true,
scrollbarStyle: "simple"
});
</script>
<br />
<div class="form-group">
<button type="submit" class="btn btn-primary rounded-0">Save</button>
<a href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation" target="_blank" rel="noopener" class="btn btn-success rounded-0" role="button">View</a>
</div>
}
</div>
</div>
<br />
<div class="card">
<div class="card-header"><h3>Attachments</h3></div>
<div class="card-body">
@if (Model?.Id > 0 && @sessionState.CanCreate == true)
{
<div id="dropSection" class="dropSection">
<span class="d-inline-block">Drop file attachments here or click to upload manually.</span>
</div>
<input type="file" id="fileInput" style="display:none" multiple>
<div id="uploadStatus"></div>
<div id="uploadedFiles"></div>
}
else
{
<div>Save the page before uploading files.</div>
}
</div>
</div>
</div>

View File

@@ -0,0 +1,31 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Page.PageRevertViewModel
@{
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>
}
<div class="card border-warning mb-3">
<div class="card-header bg-warning">
<strong>Revert to revision @sessionState.Page.Revision?</strong>
</div>
<div class="card-body">
Reverting "@Model.PageName" from revision @sessionState.Page.MostCurrentRevision to @sessionState.Page.Revision will rollback @Model.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 />
@using (Html.BeginForm(null, null, FormMethod.Post, new { action = $"{GlobalConfiguration.BasePath}{Context.Request.Path}" }))
{
<div class="form-group"><button type="submit" class="btn btn-warning 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,63 @@
@model TightWiki.Models.ViewModels.Page.RevisionsViewModel
@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>
</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>
</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,67 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Page.PageSearchViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<h3>
Page Search
</h3>
<p>
If it's here, you should be able to find it.<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="d-flex justify-content-end mb-4">
<div class="flex-grow-1 me-2">
@Html.TextBoxFor(x => x.SearchString, new { @class = "form-control", style = "width:100%" })
</div>
<button type="submit" value="Find" class="btn btn-primary">Search</button>
</div>
</div>
<br />
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
@foreach (var p in Model.Pages)
{
<tr>
<td>
@if ((p.Namespace ?? "") != "")
{
<font class="text-muted">@p.Namespace ::</font>
}<a href="@GlobalConfiguration.BasePath/@p.Navigation">@p.Title</a>@(String.IsNullOrEmpty(@p.Description) ? "" : " : ") @p.Description
</td>
</tr>
}
</table>
if (Model.Pages.Count == 0)
{
<div class="d-flex small text-muted mb-0">
<strong>
Your search criteria resulted in no pages, simplify your search text.
</strong>
</div>
}
@Html.Raw(TightWiki.Library.PageSelectorGenerator.Generate(Context.Request.QueryString, Model.PaginationPageCount))
}
<script>
window.onload = function () {
document.getElementById("SearchString").focus();
}
</script>

View File

@@ -0,0 +1,26 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Profile.ConfirmViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
}
<div class="card">
<div class="card-header">
<h3>Account Email Verification</h3>
</div>
<div class="card-body">
@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>
}
<p>
Already a member? <a href="@GlobalConfiguration.BasePath/Account/Login">Login</a>.<br />
Forgot your password? @Html.ActionLink("Reset it.", "Forgot", "Account").<br />
</p>
</div>
</div>

View File

@@ -0,0 +1,39 @@
@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,23 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Profile.DeletedAccountViewModel
@{
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-danger mb-3">
<div class="card-header bg-danger text-white">
<strong>Account Deleted!</strong>
</div>
<div class="card-body">
The account has been deleted and all content that was created or modified by this account has been attributed to an anonymized profile..<br /><br />
<br /><br />
@if (TightWiki.Models.GlobalConfiguration.AllowSignup == true)
{
@:Not a member? <a href="@GlobalConfiguration.BasePath/Identity/Account/Register">Register as a new user</a>.<br />
}
Already a member? <a href="@GlobalConfiguration.BasePath/Identity/Account/Login">Login</a>.<br />
</div>
</div>

View File

@@ -0,0 +1,206 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Profile.AccountProfileViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
}
<h3>
Profile
</h3>
<p>
This is your profile, do with it what you may.<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()
<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-1" />
}
<input type="file" id="Avatar" name="Avatar" class="form-control-file" onchange="fileCheck(this);" />
</div>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.EmailAddress)</div>
</div>
</div>
<div class="form-group row mb-3">
<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.DisplayFor(m => m.AccountProfile.EmailAddress, new { @class = "form-control-plaintext" })
</div>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.AccountName)</div>
</div>
</div>
<div class="form-group row mb-3">
<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>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.FirstName)</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>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.LastName)</div>
</div>
</div>
<div class="form-group row mb-3">
<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>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Role)</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">
@Html.DisplayFor(m => m.AccountProfile.Role, new { @class = "form-control-plaintext" })
</div>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Theme)</div>
</div>
</div>
<div class="form-group row mb-3">
<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>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Country)</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>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Language)</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>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.TimeZone)</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>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div class="text-danger">@Html.ValidationMessageFor(m => m.AccountProfile.Biography)</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>
</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>
</form>
</div>
}
<br />
<form action="@GlobalConfiguration.BasePath/Profile/Delete"><button type="submit" class="btn btn-danger rounded-0">Delete Account</button></form>

View File

@@ -0,0 +1,82 @@
@using TightWiki.Models
@model TightWiki.Models.ViewModels.Profile.PublicViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
}
<table class="fixedTable100" border="0" cellpadding="5">
<tbody>
<tr>
<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>
}
@if (string.IsNullOrWhiteSpace(Model.Navigation) == false)
{
<table border="0">
<tr class="padded">
<td><img src="/Profile/@Model.Navigation/Avatar?max=150" /></td>
<td>&nbsp;</td>
</tr>
<tr><td colspan="2"><div class="text-danger">@Html.ValidationMessageFor(m => m.AccountName)</div></td></tr>
<tr class="padded">
<td><strong>@Html.LabelFor(m => m.AccountName)</strong></td>
<td>@Html.DisplayFor(m => m.AccountName)</td>
</tr>
<tr><td colspan="2"><div class="text-danger">@Html.ValidationMessageFor(m => m.Country)</div></td></tr>
<tr class="padded">
<td><strong>@Html.LabelFor(m => m.Country)</strong></td>
<td>@Html.DisplayFor(m => m.Country)</td>
</tr>
<tr><td colspan="2"><div class="text-danger">@Html.ValidationMessageFor(m => m.Language)</div></td></tr>
<tr class="padded">
<td><strong>@Html.LabelFor(m => m.Language)</strong></td>
<td>@Html.DisplayFor(m => m.Language)</td>
</tr>
<tr><td colspan="2"><div class="text-danger">@Html.ValidationMessageFor(m => m.TimeZone)</div></td></tr>
<tr class="padded">
<td><strong>@Html.LabelFor(m => m.TimeZone)</strong></td>
<td>@Html.DisplayFor(m => m.TimeZone)</td>
</tr>
<tr><td colspan="2"><div class="text-danger">@Html.ValidationMessageFor(m => m.Biography)</div></td></tr>
<tr class="padded">
<td><strong>@Html.LabelFor(m => m.Biography)</strong></td>
<td><div style="word-wrap: break-word;">@Html.Raw(Model.Biography)</div></td>
</tr>
</table>
}
@if (Model.RecentlyModified.Count > 0)
{
<hr class="mt-5 mb-5">
<h2>Recently Modified</h2>
<table class="table fixedTable100 table-striped" border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<td><strong>Page</strong></td>
<td><strong>Modified</strong></td>
<td><strong>Summary</strong></td>
</tr>
</thead>
<tbody>
@foreach (var h in Model.RecentlyModified)
{
<tr>
<td><a href="@GlobalConfiguration.BasePath/@h.Navigation">@Html.DisplayTextFor(x => h.Name)</a></td>
<td>@Html.DisplayTextFor(x => h.ModifiedDate)</td>
<td>@Html.DisplayTextFor(x => h.ChangeSummary)</td>
</tr>
}
</tbody>
</table>
}
</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,13 @@
@model TightWiki.Models.ViewModels.ExternalLoginCallbackViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
}
<div class="card">
<div class="card-header">
<h3>External Login Exception</h3>
</div>
<div class="card-body">
<div class="alert alert-danger">@Html.Raw(Model.ErrorMessage)</div>
</div>
</div>

View File

@@ -0,0 +1,268 @@
@using TightWiki;
@using System.Text.Encodings.Web
@using TightWiki.Models
@{
var sessionState = ViewData["SessionState"] as SessionState ?? throw new Exception("Wiki State Context cannot be null.");
string navBarClass = $"navbar navbar-expand-sm navbar-toggleable-sm {(TightWiki.Models.GlobalConfiguration.FixedMenuPosition ? "fixed-top" : "")} navbar-light bg-white border-bottom box-shadow mb-3";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@(sessionState.PageTitle ?? sessionState.Page.Name)</title>
<!-- Syntax Highlighter:Begin -->
<script src="@Url.Content("~/syntax/highlight.min.js")"></script>
<script>hljs.highlightAll();</script>
<!-- Syntax Highlighter:End -->
@if (string.IsNullOrWhiteSpace(TightWiki.Models.GlobalConfiguration.HTMLHeader) == false)
{
<!-- HTMLHeader:Begin -->
@Html.Raw(TightWiki.Models.GlobalConfiguration.HTMLHeader)
<!-- HTMLHeader:Begin -->
}
@if (TightWiki.Models.GlobalConfiguration.IncludeWikiDescriptionInMeta)
{
<!-- PageDescription:Begin -->
<meta name="description" content="@sessionState.Page.Description">
<!-- PageDescription:Begin -->
}
@if (TightWiki.Models.GlobalConfiguration.IncludeWikiTagsInMeta)
{
<!-- PageTags:Begin -->
<meta name="keywords" content="@sessionState.PageTags">
<!-- PageTags:Begin -->
}
<script src="@Url.Content("~/lib/jquery/dist/jquery.min.js")"></script>
<script src="@Url.Content("~/lib/bootstrap/dist/js/bootstrap.bundle.min.js")"></script>
<!-- Theme:Begin -->
@foreach (var file in sessionState.UserTheme.Files)
{
<link rel="stylesheet" href="@Url.Content($"~{file}")" />
}
<!-- Theme:End-->
<link rel="stylesheet" href="@Url.Content("~/css/base.css")" />
<script src="@Url.Content("~/js/site.js")" asp-append-version="true"></script>
<link id="themeStylesheet" rel="stylesheet" href="" />
<script>
$(document).ready(function () {
localStorage.setItem('theme', 'dark');
$('nav').removeClass('navbar-light bg-white').addClass('@sessionState.UserTheme.ClassNavBar');
$('a.nav-link').removeClass('text-dark').addClass('@sessionState.UserTheme.ClassNavLink');
$('a.dropdown-item').removeClass('text-dark').addClass('@sessionState.UserTheme.ClassDropdown');
$('span.glyphicon').removeClass('text-dark').addClass('@sessionState.UserTheme.ClassBranding');
});
</script>
@RenderSection("Scripts", required: false)
</head>
<body>
<header>
<nav class="@navBarClass">
<div class="container">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand" href="@GlobalConfiguration.BasePath/"><span class="glyphicon text-dark"><img src="@Url.Content(TightWiki.Models.GlobalConfiguration.BrandImageSmall)" /> @TightWiki.Models.GlobalConfiguration.Name</span></a>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
@foreach (TightWiki.Models.DataModels.MenuItem item in TightWiki.Models.GlobalConfiguration.MenuItems)
{
if (item.Link.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) || item.Link.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
{
<li class="nav-item"><a class="nav-link text-dark" href="@item.Link">@item.Name</a></li>
}
<li class="nav-item"><a class="nav-link text-dark" href="@GlobalConfiguration.BasePath@item.Link">@item.Name</a></li>
}
@if (sessionState.CanCreate == true
|| (sessionState.ShouldCreatePage == true && sessionState.CanCreate == true)
|| (sessionState.Page.Exists == true && (sessionState.CanEdit == true || sessionState.CanModerate == true || sessionState.CanDelete == true))
)
{
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle text-dark" href="#" id="navbarDropdownMenuLink" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Page</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
@if (sessionState.CanCreate == true)
{
if (sessionState.ShouldCreatePage == true)
{
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation/Page/Create">Create "@sessionState.PageNavigation"</a>
}
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Page/Create">Create new page</a>
}
@if (sessionState.Page.Exists == true)
{
@if (sessionState.CanCreate == true)
{
<div class="dropdown-divider"></div>
}
if (sessionState.CanView == true && TightWiki.Models.GlobalConfiguration.EnablePageComments)
{
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation/Comments">Comments</a>
}
if (sessionState.CanDelete == true)
{
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation/Delete">Delete this page</a>
}
if (sessionState.CanEdit == true && sessionState.Page.IsHistoricalVersion == false)
{
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation/Edit">Edit</a>
}
if (sessionState.CanView == true)
{
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation/Revisions">Revisions</a>
}
if (sessionState.CanCreate == true)
{
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation/Refresh">Refresh</a>
}
if (sessionState.CanModerate == true && sessionState.Page.IsHistoricalVersion == true)
{
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation/revert/@sessionState.Page.Revision">Revert to revision @sessionState.Page.Revision</a>
}
if (sessionState.CanView == true && TightWiki.Models.GlobalConfiguration.EnablePageComments)
{
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/@sessionState.PageNavigation">View</a>
}
}
</div>
</li>
}
@if (sessionState.IsAuthenticated == true && (sessionState.CanModerate == true || sessionState.Role == TightWiki.Library.Constants.Roles.Administrator))
{
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle text-dark" href="#" id="navbarDropdownMenuLink" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Admin</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
@if (sessionState.Role == TightWiki.Library.Constants.Roles.Administrator)
{
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Accounts">Accounts</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Roles">Roles</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Config">Configuration</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Emojis">Emojis</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/MenuItems">Site Menu</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/CompilationStatistics">Compilations</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Database">Database</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Exceptions">Exceptions</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Metrics">Metrics</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/DeletedPages">Deleted Pages</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/MissingPages">Missing Pages</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Moderate">Moderate</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Namespaces">Namespaces</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/OrphanedPageAttachments">Orphaned Attachments</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Pages">Pages</a>
}
else if (sessionState.CanModerate == true)
{
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/DeletedPages">Deleted Pages</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/MissingPages">Missing Pages</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Moderate">Moderate</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Namespaces">Namespaces</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Admin/Pages">Pages</a>
}
</div>
</li>
}
@if (sessionState.IsAuthenticated == true)
{
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle text-dark" href="#" id="navbarDropdownMenuLink" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">My</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Profile/My">Profile</a>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Identity/Account/Manage">Account</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-dark" href="@GlobalConfiguration.BasePath/Identity/Account/Logout?returnUrl=/">Logout</a>
</div>
</li>
}
@if (sessionState.IsAuthenticated == false)
{
<li class="nav-item"><a class="nav-link text-dark" href="@GlobalConfiguration.BasePath/Identity/Account/Login?returnUrl=@UrlEncoder.Default.Encode($"{GlobalConfiguration.BasePath}{Context.Request.Path.Value}")">Login</a></li>
}
@if (TightWiki.Models.GlobalConfiguration.IncludeSearchOnNavbar)
{
<li class="nav-item ms-auto d-none d-lg-block">
<div class="collapse navbar-collapse justify-content-end">
<form class="d-flex" role="search" method="GET" action="@GlobalConfiguration.BasePath/Page/Search">
<input class="form-control me-2" type="search" placeholder="Search" name="SearchString" aria-label="Search">
<button class="btn btn-outline-primary" type="submit">Search</button>
</form>
</div>
</li>
}
</ul>
</div>
</div>
</nav>
</header>
<div class="container" @(TightWiki.Models.GlobalConfiguration.FixedMenuPosition ? "style=margin-top:80px;" : "")>
<main role="main" class="pb-3">
@Html.Raw(TightWiki.Models.GlobalConfiguration.HTMLPreBody)
@if (TightWiki.Repository.UsersRepository.AdminPasswordStatus() != TightWiki.Library.Constants.AdminPasswordChangeState.HasBeenChanged)
{
<div class="card bg-warning mb-3">
<div class="card-header"><strong>Default password has not been changed</strong></div>
<div class="card-body">
<p class="card-text">
The admin password is set to its default value, it is <i>recommended</i> that you change it immediately!<br />
<br />
You can change this password by logging in, navigating to My -&gt; Account -&gt; Password.<br />
<br />
<strong>Current admin login:</strong><br />
&nbsp;&nbsp;&nbsp;<strong>Username:</strong>"admin@tightwiki.com"<br />
&nbsp;&nbsp;&nbsp;<strong>Password:</strong>"@TightWiki.Library.Constants.DEFAULTPASSWORD"<br />
<br />
</p>
</div>
</div>
}
@RenderBody()
@Html.Raw(TightWiki.Models.GlobalConfiguration.HTMLPostBody)
</main>
</div>
<div class="container">
@if (TightWiki.Models.GlobalConfiguration.FooterBlurb != null || TightWiki.Models.GlobalConfiguration.Copyright != null)
{
<hr class="mt-5 mb-5">
@if (TightWiki.Models.GlobalConfiguration.FooterBlurb != null)
{
@Html.Raw(TightWiki.Models.GlobalConfiguration.FooterBlurb)
}
@if (TightWiki.Models.GlobalConfiguration.Copyright != null)
{
<br />
@Html.Raw(TightWiki.Models.GlobalConfiguration.Copyright)
}
}
@Html.Raw(TightWiki.Models.GlobalConfiguration.HTMLFooter)
</div>
</body>
</html>

View File

@@ -0,0 +1,2 @@
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

View File

@@ -0,0 +1,15 @@
@model TightWiki.Models.ViewModels.Page.BrowseViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
}
<div class="card">
<div class="card-header"><strong>Associated Tags</strong></div>
<div class="card-body">@Html.Raw(Model.TagCloud)</div>
</div>
<div class="card">
<div class="card-header"><strong>Associated Pages</strong></div>
<div class="card-body">@Html.Raw(Model.AssociatedPages)</div>
</div>

View File

@@ -0,0 +1,59 @@
@model TightWiki.Models.ViewModels.Utility.ConfirmActionViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
string cardClass = "card mb-3";
string cardHeaderClass = "card-header";
if (string.Equals(Model.Style, "Danger", StringComparison.InvariantCultureIgnoreCase))
{
cardHeaderClass += " bg-danger text-white";
cardClass += " border-danger";
}
if (string.Equals(Model.Style, "Warn", StringComparison.InvariantCultureIgnoreCase))
{
cardHeaderClass += " bg-warning";
cardClass += " border-warning";
}
}
@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>
}
<div class="@cardClass">
<div class="@cardHeaderClass">
<strong>Are you sure?</strong>
</div>
<div class="card-body">
@Html.Raw(@Model.Message)<br /><br />
<form action='@Model.ControllerURL' method="post">
@Html.HiddenFor(m => m.NoRedirectURL)
@Html.HiddenFor(m => m.YesRedirectURL)
@Html.HiddenFor(m => m.Message)
@Html.HiddenFor(m => m.Style)
@if (Model.Style == "Safe")
{
<div class="form-group"><button type="submit" class="btn btn-success rounded-0" name="UserSelection" value="true">Yes</button>&nbsp;&nbsp;<button type="submit" class="btn btn-success rounded-0" name="UserSelection" value="false">No</button></div>
}
else if (Model.Style == "Warn")
{
<div class="form-group"><button type="submit" class="btn btn-warning rounded-0" name="UserSelection" value="true">Yes</button>&nbsp;&nbsp;<button type="submit" class="btn btn-success rounded-0" name="UserSelection" value="false">No</button></div>
}
else
{
<div class="form-group"><button type="submit" class="btn btn-danger rounded-0" name="UserSelection" value="true">Yes</button>&nbsp;&nbsp;<button type="submit" class="btn btn-success rounded-0" name="UserSelection" value="false">No</button></div>
}
</form>
</div>
</div>

View File

@@ -0,0 +1,91 @@
@model TightWiki.Models.ViewModels.Utility.NotifyViewModel
@{
Layout = "/Views/Shared/_Layout.cshtml";
var sessionState = ViewData["SessionState"] as TightWiki.SessionState ?? throw new Exception("Wiki State Context cannot be null.");
bool _isFirst = true;
}
@if (string.IsNullOrEmpty(@Model.RedirectURL) == false && string.IsNullOrEmpty(Model.ErrorMessage) == true && Model.RedirectTimeout > 0)
{
<script>
document.addEventListener("DOMContentLoaded", function () {
var countdownElement = document.getElementById('countdown');
var countdownTime = @(Model.RedirectTimeout - 1);
var interval = setInterval(function () {
countdownElement.textContent = countdownTime;
countdownTime--;
if (countdownTime < 0) {
clearInterval(interval);
window.location.href = "@Model.RedirectURL";
}
}, 1000);
});
</script>
}
@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
<div class="alert alert-danger">
@{
var errorMessages = @Model.ErrorMessage.Replace("\r\n", "\n").Split("\n");
}
@foreach (var message in errorMessages)
{
if (_isFirst)
{
<strong>@message</strong>
if (errorMessages.Count() > 1)
{
<br />
<hr />
}
_isFirst = false;
}
else
{
@message
<br />
}
}
@if (string.IsNullOrEmpty(@Model.RedirectURL) == false)
{
<br />
<a href="@Model.RedirectURL">Continue</a>
}
</div>
}
@if (!string.IsNullOrEmpty(Model.SuccessMessage))
{
<div class="alert alert-success">
@{
var successMessages = @Model.SuccessMessage.Replace("\r\n", "\n").Split("\n");
}
@foreach (var message in successMessages)
{
if (_isFirst)
{
<strong>@message</strong>
if (successMessages.Count() > 1)
{
<br />
<hr />
}
_isFirst = false;
}
else
{
@message
<br />
}
}
@if (string.IsNullOrEmpty(@Model.RedirectURL) == false && Model.RedirectTimeout > 0)
{
<p>
<a href="@Model.RedirectURL">Continue now</a> or you will be redirected in <span id="countdown">@Model.RedirectTimeout</span> seconds.
</p>
}
</div>
}