In this series of posts I will start detailing the components of the MVC forum and how you can modify it to suit your own site. ASP.NET MVC does not include anything that directly corresponds to a page. In an ASP.NET MVC application, there is not a page on disk that corresponds to the path in the URL that you type into the address bar of your browser. The closest thing to a page in an ASP.NET MVC application is something called a view.
The look and feel of the forum is controlled by a set of template files which you can find in the Views folder, and css files which are located in the Content folder. If all you want to do is change colours and fonts etc, then you just need to edit the css files. Under the Views folder you will find several subfolders, each subfolder contains template files specific to a certain area/function of the site, Home, Forum,Topic etc. You will also see a subfolder called Shared, this subfolder contains the main base layout template together with common include templates for formatting specific items on a page, I will go into this as we go along.
All public/member web pages are based on a main _Layout.cshtml template (admin pages use a different _layout.cshtml), this template can be found in the Shared folder.
Main Layout template
The layout template contains elements common to every page (menu etc) as well as defining the basic page layout, header, footer columns etc. The file also contains placeholders where you can inject code from the other views, these are defined as sections and may or may not be required, a required section means you must define it in the view. The code sample below shows the forums _layout page to illustrate the layout and sections, other code/css/javascript has been removed for clarity.
Code:
@using System.Globalization
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using SnitzCore.Service.MiddleWare
@using SnitzCore.Service.TagHelpers
@{
string[] rtlLangs = new string[] { "ar", "arc", "dv", "fa", "ha", "khw", "ks", "ku", "ps", "ur", "yi", "he" };
//check the culture so we can set the page direction and language
var cookielang = SnitzCookie.GetCookieValue("CookieLang");
CultureInfo ci = CultureInfo.CurrentUICulture;
string clang = ci.TwoLetterISOLanguageName;
if (cookielang != null)
{
var cultureInfo = new CultureInfo(cookielang);
CultureInfo.CurrentUICulture = cultureInfo;
clang = cultureInfo.TwoLetterISOLanguageName;
}
Context.Session.SetString("culture", clang);
bool isRighToLeft = rtlLangs.Contains(clang);
MemberService.SetLastHere(User);
}
<!DOCTYPE html>
<html lang="@clang" dir="@(isRighToLeft ? "rtl" : "ltr" )">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Forum</title>
@await RenderSectionAsync("MetaData", required: false)
@if (isRighToLeft)
{
<link rel="stylesheet" href="~/css/bootstrap.rtl.min.css">
}else{
<link rel="stylesheet" href="~/css/bootstrap.min.css" />
}
<link rel="stylesheet" href="~/lib/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="~/css/site.min.css">
@await Component.InvokeAsync("SnitzThemeCss")
<link rel="stylesheet" href="~/css/busy.min.css">
@await RenderSectionAsync("Styles", required: false)
</head>
<body>
<partial name="_BusyPartial" />
<div class="d-flex flex-column" style="min-height: 100vh;">
<partial name="MainNav"/>
<div class="container flex-grow-1">
<partial snitz-if="@(SnitzConfig.GetIntValue("STRSHOWANNOUNCE") == 1)" name="Announcement" />
<partial snitz-if="@(SnitzConfig.GetIntValue("INTREQUIRECONSENT") == 1)" name="CookieConsentPartial" />
<main role="main" class="pb-3 mt-6">
@RenderBody()
</main>
</div>
<partial name="Footer"/>
</div>
<script type="text/javascript" src="~/js/bootstrap.bundle.min.js"></script>
<script type="text/javascript" src="~/js/jquery.min.js"></script>
<script type="text/javascript" src="~/lib/jquery/dist/jquery-ui.min.js"></script>
<script type="text/javascript" src="~/lib/jquery/jquery.timeago.min.js"></script>
<partial name="GlobalJs" />
<partial name="ValidationScriptsPartial" />
<script type="text/javascript" src="~/js/site.min.js" asp-append-version="true"></script>
<script type="text/javascript" src="~/js/jquery.cookie.js"></script>
@if (!clang.Contains("en"))
{
var src = $"timeago.{clang}.js";
<script type="text/javascript" src="~/js/timeago/@src"></script>
}
@await RenderSectionAsync("Scripts", required: false)
<renderstoredcontent asp-key="cookie-consent"></renderstoredcontent>
<renderstoredcontent asp-key="password-toggle"></renderstoredcontent>
@if (User.IsInRole("Administrator"))
{
<div id="confirmRestart" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger">
<a href="#" data-bs-dismiss="modal" aria-hidden="true" class="btn btn-danger">X</a>
<h4 class="text-bg-danger">Application Restart!</h4>
</div>
<div class="modal-body">
<p>You are about to Restart the Application.</p>
<p>Do you wish to proceed?</p>
</div>
<div class="modal-footer">
<a href="#" id="btnRestartYes" class="btn btn-danger">@Localizer["btnOk"]</a>
<a href="#" data-bs-dismiss="modal" aria-hidden="true" class="btn btn-secondary">@Localizer["btnCancel"]</a>
</div>
</div>
</div>
</div>
<div id="forumAlert" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger">
<a href="#" data-bs-dismiss="modal" aria-hidden="true" class="btn btn-danger">X</a>
<h4 class="text-bg-danger">Alert!</h4>
</div>
<div class="modal-body">
<p>There are pending member registrations.</p>
</div>
</div>
</div>
</div>
}
<script type="text/javascript">
$(document).on('click', '.confirm-restart', function (e) {
e.preventDefault();
var href = $(this).attr('href');
$('#confirmRestart').data('url', href).modal('show');
$('#confirmRestart').on('click', '#btnRestartYes', function (e) {
e.preventDefault();
$.post(href,'',
function(data, status){
if (!data) {
alert("There was a problem!");
} else {
$('#confirmRestart .modal-body').html("<p>Application is restarting, please wait ...</p>");
$('#btnRestartYes').hide();
setTimeout(function () {
$('#confirmRestart').modal('hide');
location.reload(true);
}, 25000);
}
});
});
});
$(document).ready(function () {
if (SnitzVars.pending > 0) {
$('#forumAlert').modal('show');
setTimeout(function () {
$('#forumAlert').modal('hide');
}, 3000);
}
});
$(window).on('beforeunload', function(){
displayBusyIndicator();
});
window.addEventListener( "pageshow", function ( event ) {
var historyTraversal = event.persisted ||
( typeof window.performance != "undefined" &&
window.performance.navigation.type === 2 );
if ( historyTraversal ) {
// Handle page restore.
window.location.reload();
}
});
$(document).on('submit', 'form', function () {
displayBusyIndicator();
});
function displayBusyIndicator() {
$('.loading').show();
}
$(document).ajaxComplete(function(event, xhr, settings) {
$('.loading').hide();
});
if(SnitzVars.showPageTimer == '1'){
window.addEventListener('load', () => {
//debugger;
const [pageNav] = performance.getEntriesByType('navigation');
const footer = document.getElementById('loadTime');
let workerTime = 0;
if (pageNav.responseEnd > 0) {
workerTime = (pageNav.responseEnd - pageNav.workerStart)/1000;
if (footer) {
var test = (workerTime).toLocaleString(
undefined, // leave undefined to use the visitor's browser
// locale or a string like 'en-US' to override it.
{ minimumFractionDigits: 2 }
);
footer.textContent = `Page loaded in ${test} s`;
}
}
});
}
</script>
</body>
</html>
You will also see a few lines like @Html.Partial("_LoginPartial") in the actual _layout file, these lines are including other view templates much like the classic asp include files. In general these 'partial views' will start with an underscore and will either be in the same subfolder as the view template, or in the shared folder if included in multiple views.