Add support page, admin features, and auth hardening.
Rename public affiliate route to /support with localized nav labels, and include affiliate/social/wallpaper admin, 2FA login, and related site updates. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
202dc16a52
commit
fc45c35dbe
@@ -0,0 +1,3 @@
|
||||
@{
|
||||
Layout = "~/Views/Shared/_PortfolioLayout.cshtml";
|
||||
}
|
||||
@@ -5,34 +5,142 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>atakanozban.com Admin Panel</title>
|
||||
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" />
|
||||
<link rel="font-awesome" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" />
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
background-color: #212529;
|
||||
}
|
||||
|
||||
.admin-sidebar {
|
||||
width: 260px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.admin-sidebar .nav-link {
|
||||
color: rgba(255, 255, 255, .7);
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
.admin-sidebar .nav-link i {
|
||||
width: 1.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.admin-sidebar .nav-link:hover {
|
||||
color: #fff;
|
||||
background-color: rgba(255, 255, 255, .08);
|
||||
}
|
||||
|
||||
.admin-sidebar .nav-link.active {
|
||||
color: #fff;
|
||||
background-color: #0d6efd;
|
||||
}
|
||||
|
||||
.admin-content {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.admin-user-toggle {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.admin-user-toggle:hover {
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="w3-sidebar w3-bar-block bg-dark" style="width: 25%; color: #212529">
|
||||
<h3 class="w3-bar-item text-white">Menu</h3>
|
||||
<a href="/myprojects/index" class="w3-bar-item text-decoration-none text-white" target="_blank">Go to site</a>
|
||||
<a href="/login/logout" class="w3-bar-item text-decoration-none text-white">Sign out</a>
|
||||
</div>
|
||||
@{
|
||||
var currentPath = (Request.Url != null ? Request.Url.AbsolutePath : "").ToLowerInvariant();
|
||||
Func<string, bool> isActive = path => currentPath.StartsWith(path.ToLowerInvariant());
|
||||
var isDashboard = currentPath == "/admin" || currentPath == "/admin/" || currentPath.StartsWith("/admin/index");
|
||||
}
|
||||
<div class="d-flex">
|
||||
<div class="admin-sidebar d-flex flex-column flex-shrink-0 p-3 text-bg-dark">
|
||||
<a href="/admin" class="d-flex align-items-center mb-3 text-white text-decoration-none">
|
||||
<i class="fa-solid fa-shield-halved fs-4 me-2"></i>
|
||||
<span class="fs-4">Admin Panel</span>
|
||||
</a>
|
||||
<hr class="text-white-50" />
|
||||
|
||||
<!-- Page Content -->
|
||||
<div style="margin-left: 25%">
|
||||
<div class="w3-container w3-teal">
|
||||
<h1>atakanozban.com Admin Panel</h1>
|
||||
<ul class="nav nav-pills flex-column mb-auto">
|
||||
<li class="nav-item">
|
||||
<a href="/admin" class="nav-link @(isDashboard ? "active" : "")">
|
||||
<i class="fa-solid fa-gauge me-2"></i>Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/myprojects" class="nav-link @(isActive("/admin/myprojects") ? "active" : "")">
|
||||
<i class="fa-solid fa-diagram-project me-2"></i>My Projects
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/affiliatelinks" class="nav-link @(isActive("/admin/affiliatelinks") ? "active" : "")">
|
||||
<i class="fa-solid fa-link me-2"></i>Affiliate Links
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/socialicons" class="nav-link @(isActive("/admin/socialicons") || isActive("/admin/newsocialicon") || isActive("/admin/socialiconget") ? "active" : "")">
|
||||
<i class="fa-solid fa-share-nodes me-2"></i>Social Icons
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/wallpaper" class="nav-link @(isActive("/admin/wallpaper") ? "active" : "")">
|
||||
<i class="fa-solid fa-image me-2"></i>Wallpaper
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr class="text-white-50" />
|
||||
|
||||
<div class="dropdown">
|
||||
<a href="#" class="admin-user-toggle d-flex align-items-center dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa-solid fa-circle-user fs-3 me-2"></i>
|
||||
<strong>@(User?.Identity?.Name ?? "Admin")</strong>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-dark text-small shadow">
|
||||
<li><a class="dropdown-item" href="/admin/usersettings"><i class="fa-solid fa-user-gear me-2"></i>User Settings</a></li>
|
||||
<li><a class="dropdown-item" href="/" target="_blank"><i class="fa-solid fa-arrow-up-right-from-square me-2"></i>Go to site</a></li>
|
||||
<li><hr class="dropdown-divider" /></li>
|
||||
<li><a class="dropdown-item" href="/login/logout"><i class="fa-solid fa-right-from-bracket me-2"></i>Sign out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w3-container bg-dark">
|
||||
|
||||
<div class="admin-content p-4">
|
||||
@RenderBody()
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
body {
|
||||
background-color: #212529; /* Bootstrap's dark bg color */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="modal fade" id="pasteImageModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content bg-dark text-white border border-secondary">
|
||||
<div class="modal-header border-secondary">
|
||||
<h5 class="modal-title">Paste Image</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<div id="pasteImageTarget" contenteditable="true" spellcheck="false"
|
||||
class="border border-secondary rounded p-5 mb-3 text-white-50"
|
||||
style="outline:none;cursor:text;min-height:120px;display:flex;align-items:center;justify-content:center;">
|
||||
Click here, then press Ctrl+V to paste your image
|
||||
</div>
|
||||
<img id="pasteImageModalPreview" style="display:none;max-width:100%;max-height:220px;border-radius:6px;" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="@Url.Content("~/web_atakanozbancom/assets/js/paste-upload.js")"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
@using Resources
|
||||
|
||||
@{
|
||||
@{
|
||||
Layout = null;
|
||||
}
|
||||
|
||||
@@ -178,6 +176,8 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
@Html.Action("wallpaper", "main")
|
||||
|
||||
<link href="~/web_atakanozbancom/assets/css/style.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
@@ -196,9 +196,10 @@
|
||||
<div>
|
||||
<h3 class="float-md-start mb-0">Atakan</h3>
|
||||
<nav class="nav nav-masthead justify-content-center float-md-end">
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="Home" asp-action="ChangeLanguage" asp-route-lang="en" href="@Url.Action("index","main")">@Resource.NavHome</a>
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="Home" asp-action="ChangeLanguage" asp-route-lang="en" aria-current="page" href="@Url.Action("index","about")">@Resource.NavAboutMe</a>
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="Home" asp-action="ChangeLanguage" asp-route-lang="en" href="@Url.Action("index","myprojects")">@Resource.NavMyProjects</a> @{
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="main" asp-action="ChangeLanguage" asp-route-lang="en" href="@Url.Action("index","main")">@atakanozbancom.App_GlobalResources.Resource.NavHome</a>
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="main" asp-action="ChangeLanguage" asp-route-lang="en" aria-current="page" href="@Url.Action("index","about")">@atakanozbancom.App_GlobalResources.Resource.NavAboutMe</a>
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="main" asp-action="ChangeLanguage" asp-route-lang="en" href="@Url.Action("index","myprojects")">@atakanozbancom.App_GlobalResources.Resource.NavMyProjects</a>
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="main" asp-action="ChangeLanguage" asp-route-lang="en" href="@Url.Action("index","support")">@atakanozbancom.App_GlobalResources.Resource.NavAffiliateLinks</a> @{
|
||||
var currentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
|
||||
var back = Request.RawUrl;
|
||||
}
|
||||
@@ -219,32 +220,9 @@
|
||||
@RenderBody()
|
||||
|
||||
<footer class="mt-auto text-white-50">
|
||||
<a class="text-decoration-underline text-white-50" href="https://link.atakanozban.com/status">@Resource.Server</a>
|
||||
<a class="text-decoration-underline text-white-50" href="https://link.atakanozban.com/status">@atakanozbancom.App_GlobalResources.Resource.Server</a>
|
||||
<div class="icons mb-2 mt-2">
|
||||
<a href="https://link.atakanozban.com/youtube" class="text-decoration-none text-white-50">
|
||||
<i class="fa-brands fa-youtube"></i>
|
||||
</a>
|
||||
<a href="https://link.atakanozban.com/odysee" class="text-decoration-none text-white-50">
|
||||
<i class="fa-brands fa-odysee"></i>
|
||||
</a>
|
||||
<a href="https://link.atakanozban.com/dailymotion" class="text-decoration-none text-white-50">
|
||||
<i class="fa-brands fa-dailymotion"></i>
|
||||
</a>
|
||||
<a href="https://link.atakanozban.com/spotify" class="text-decoration-none text-white-50">
|
||||
<i class="fa-brands fa-spotify"></i>
|
||||
</a>
|
||||
<a href="https://link.atakanozban.com/instagram" class="text-decoration-none text-white-50">
|
||||
<i class="fa-brands fa-instagram"></i>
|
||||
</a>
|
||||
<a href="https://link.atakanozban.com/facebook" class="text-decoration-none text-white-50">
|
||||
<i class="fa-brands fa-facebook"></i>
|
||||
</a>
|
||||
<a href="https://link.atakanozban.com/github" class="text-decoration-none text-white-50">
|
||||
<i class="fa-brands fa-github"></i>
|
||||
</a>
|
||||
<a href="https://link.atakanozban.com/stream" class="text-decoration-none text-white-50">
|
||||
<i class="fa-solid fa-link"></i>
|
||||
</a>
|
||||
@Html.Action("socialicons", "main")
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
@{
|
||||
Layout = "~/Views/Shared/_PortfolioLayout.cshtml";
|
||||
}
|
||||
@@ -1,20 +1,18 @@
|
||||
@using Resources
|
||||
|
||||
@{
|
||||
@{
|
||||
ViewBag.Title = "index";
|
||||
Layout = "~/Views/Shared/_PortfolioLayout.cshtml";
|
||||
}
|
||||
|
||||
<main class="px-3">
|
||||
<h1 class="mb-3">@Resource.AboutMeTitle</h1>
|
||||
<h1 class="mb-3">@atakanozbancom.App_GlobalResources.Resource.AboutMeTitle</h1>
|
||||
<p class="mb-5 text-start">
|
||||
@Resource.About1
|
||||
@atakanozbancom.App_GlobalResources.Resource.About1
|
||||
<br /> <br />
|
||||
@Resource.About2
|
||||
@atakanozbancom.App_GlobalResources.Resource.About2
|
||||
<br /> <br />
|
||||
@Resource.About3
|
||||
@atakanozbancom.App_GlobalResources.Resource.About3
|
||||
<br /> <br />
|
||||
@Resource.About4
|
||||
@atakanozbancom.App_GlobalResources.Resource.About4
|
||||
</p>
|
||||
</main>
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
@model atakanozbancom.Models.classes.AdminAffiliateLinkVM
|
||||
@{
|
||||
ViewBag.Title = "affiliatelinkget";
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
}
|
||||
|
||||
<h2 class="mt-3 text-white">Edit Affiliate Link</h2>
|
||||
@if (TempData["ok"] != null)
|
||||
{<div class="alert alert-success">@TempData["ok"]</div>}
|
||||
<hr />
|
||||
|
||||
@using (Html.BeginForm("affiliatelinkget", "admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
@Html.HiddenFor(m => m.id)
|
||||
@Html.ValidationSummary(false, "", new { @class = "alert alert-danger" + (ViewData.ModelState.IsValid ? " d-none" : "") })
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="text-white">Current Logo</label><br />
|
||||
<img id="logoPreview" src="@Model.existingImage" alt="@Model.TitleEN" style="@(string.IsNullOrWhiteSpace(Model.existingImage) ? "display:none;" : "")width:64px;height:64px;object-fit:contain;background:#fff;border-radius:6px;padding:4px;" />
|
||||
<input id="logoFile" type="file" name="logoFile" class="form-control bg-dark text-white mt-2" accept="image/*" />
|
||||
<button type="button" class="btn btn-outline-light btn-sm mt-2" onclick="PasteImageModal.open('#logoFile', '#logoPreview')">
|
||||
<i class="fa-solid fa-paste me-1"></i>Paste image
|
||||
</button>
|
||||
<small class="text-muted d-block mt-1">Leave empty to keep the current logo.</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.url, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.url, new { @class = "form-control bg-dark text-white" })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.TitleEN, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.TitleEN, new { @class = "form-control bg-dark text-white" })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.DescEN, new { @class = "text-white" })
|
||||
@Html.TextAreaFor(p => p.DescEN, new { @class = "form-control bg-dark text-white", rows = 2 })
|
||||
</div>
|
||||
|
||||
<h3 class="mt-3 text-white">Translation (TR)</h3>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(m => m.TitleTR, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(m => m.TitleTR, new { @class = "form-control bg-dark text-white" })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(m => m.DescTR, new { @class = "text-white" })
|
||||
@Html.TextAreaFor(m => m.DescTR, new { @class = "form-control bg-dark text-white", rows = 2 })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.sort_order, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.sort_order, new { @class = "form-control bg-dark text-white", type = "number" })
|
||||
</div>
|
||||
|
||||
<button class="btn btn-success">Save</button>
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
@using atakanozbancom.Models.classes
|
||||
@model List<affiliatelink>
|
||||
|
||||
@{
|
||||
ViewBag.Title = "affiliatelinks";
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
}
|
||||
|
||||
<h2 class="text-white mt-3 mb-3">Affiliate Links</h2>
|
||||
@if (TempData["ok"] != null)
|
||||
{<div class="alert alert-success">@TempData["ok"]</div>}
|
||||
|
||||
<table class="table table-bordered mt-3">
|
||||
<tr class="bg-dark text-white">
|
||||
<th class="bg-dark text-white">Logo</th>
|
||||
<th class="bg-dark text-white">Title</th>
|
||||
<th class="bg-dark text-white">Description</th>
|
||||
<th class="bg-dark text-white">URL</th>
|
||||
<th class="bg-dark text-white">Order</th>
|
||||
<th class="bg-dark text-white">Edit</th>
|
||||
<th class="bg-dark text-white">Delete</th>
|
||||
</tr>
|
||||
@foreach (var x in Model)
|
||||
{
|
||||
<tr>
|
||||
<td class="bg-dark text-white"><img src="@x.image" alt="@x.title" style="width:40px;height:40px;object-fit:contain;background:#fff;border-radius:4px;padding:2px;" /></td>
|
||||
<td class="bg-dark text-white">@x.title</td>
|
||||
<td class="bg-dark text-white">@x.description</td>
|
||||
<td class="bg-dark text-white" style="word-break:break-all;">@x.url</td>
|
||||
<td class="bg-dark text-white">@x.sort_order</td>
|
||||
<td class="bg-dark text-white"><a href="/admin/affiliatelinkget/@x.id" class="btn btn-primary">Edit</a></td>
|
||||
<td class="bg-dark text-white">
|
||||
@using (Html.BeginForm("affiliatelinkdelete", "admin", new { id = x.id }, FormMethod.Post, new { onsubmit = "return confirm('Delete this affiliate link?');" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-danger">Delete</button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!Model.Any())
|
||||
{
|
||||
<tr>
|
||||
<td class="bg-dark text-white-50" colspan="7">No affiliate links yet.</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
<a href="/admin/newaffiliatepost" class="btn btn-primary">Create New Affiliate Link</a>
|
||||
+33
-28
@@ -1,33 +1,38 @@
|
||||
@using atakanozbancom.Models.classes
|
||||
@model List<myprojects>
|
||||
|
||||
|
||||
@{
|
||||
@{
|
||||
ViewBag.Title = "index";
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
}
|
||||
|
||||
<h2 class="text-white mt-3 mb-3">Posts</h2>
|
||||
|
||||
<table class="table table-bordered mt-3">
|
||||
<tr class="bg-dark text-white">
|
||||
<th class="bg-dark text-white">ID</th>
|
||||
<th class="bg-dark text-white">Title</th>
|
||||
<th class="bg-dark text-white">Description</th>
|
||||
<th class="bg-dark text-white">Edit</th>
|
||||
<th class="bg-dark text-white">Delete</th>
|
||||
</tr>
|
||||
@foreach (var x in Model)
|
||||
{
|
||||
<tr>
|
||||
<td class="bg-dark text-white">@x.id</td>
|
||||
<td class="bg-dark text-white">@x.title</td>
|
||||
<td class="bg-dark text-white">@x.description</td>
|
||||
<td class="bg-dark text-white"><a href="/admin/myprojectsget/@x.id" class="btn btn-primary">Edit</a></td>
|
||||
<td class="bg-dark text-white"><a href="/admin/myprojectsdelete/@x.id " class="btn btn-danger">Delete</a></td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
</table>
|
||||
<a href="/admin/newmppost" class="btn btn-primary">Create New Post</a>
|
||||
<h2 class="text-white mt-3 mb-3">Dashboard</h2>
|
||||
<p class="text-white-50">Welcome back! Choose a section from the menu on the left to get started.</p>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="bg-dark border border-secondary rounded p-4 text-white">
|
||||
<h4>My Projects</h4>
|
||||
<p class="text-white-50 mb-3">@ViewBag.MyProjectsCount post(s) published.</p>
|
||||
<a href="/admin/myprojects" class="btn btn-primary">Manage My Projects</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="bg-dark border border-secondary rounded p-4 text-white">
|
||||
<h4>Affiliate Links</h4>
|
||||
<p class="text-white-50 mb-3">@ViewBag.AffiliateLinksCount link(s) published.</p>
|
||||
<a href="/admin/affiliatelinks" class="btn btn-primary">Manage Affiliate Links</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="bg-dark border border-secondary rounded p-4 text-white">
|
||||
<h4>Social Icons</h4>
|
||||
<p class="text-white-50 mb-3">@ViewBag.SocialIconsCount icon(s) in the footer.</p>
|
||||
<a href="/admin/socialicons" class="btn btn-primary">Manage Social Icons</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="bg-dark border border-secondary rounded p-4 text-white">
|
||||
<h4>Wallpaper</h4>
|
||||
<p class="text-white-50 mb-3">Change the background image used across the site.</p>
|
||||
<a href="/admin/wallpaper" class="btn btn-primary">Manage Wallpaper</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
@using atakanozbancom.Models.classes
|
||||
@model List<myprojects>
|
||||
|
||||
|
||||
@{
|
||||
ViewBag.Title = "myprojects";
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
}
|
||||
|
||||
<h2 class="text-white mt-3 mb-3">My Projects</h2>
|
||||
|
||||
<table class="table table-bordered mt-3">
|
||||
<tr class="bg-dark text-white">
|
||||
<th class="bg-dark text-white">ID</th>
|
||||
<th class="bg-dark text-white">Title</th>
|
||||
<th class="bg-dark text-white">Description</th>
|
||||
<th class="bg-dark text-white">Edit</th>
|
||||
<th class="bg-dark text-white">Delete</th>
|
||||
</tr>
|
||||
@foreach (var x in Model)
|
||||
{
|
||||
<tr>
|
||||
<td class="bg-dark text-white">@x.id</td>
|
||||
<td class="bg-dark text-white">@x.title</td>
|
||||
<td class="bg-dark text-white">@x.description</td>
|
||||
<td class="bg-dark text-white"><a href="/admin/myprojectsget/@x.id" class="btn btn-primary">Edit</a></td>
|
||||
<td class="bg-dark text-white">
|
||||
@using (Html.BeginForm("myprojectsdelete", "admin", new { id = x.id }, FormMethod.Post, new { onsubmit = "return confirm('Delete this project?');" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-danger">Delete</button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
</table>
|
||||
<a href="/admin/newmppost" class="btn btn-primary">Create New Post</a>
|
||||
@@ -6,64 +6,138 @@
|
||||
}
|
||||
|
||||
<h2 class="mt-3 text-white">My Projects Edit Post</h2>
|
||||
|
||||
@if (TempData["ok"] != null)
|
||||
{
|
||||
<div class="alert alert-success">@TempData["ok"]</div>
|
||||
}
|
||||
|
||||
{<div class="alert alert-success">@TempData["ok"]</div>}
|
||||
<hr />
|
||||
|
||||
@using (Html.BeginForm("myprojectsget", "admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
|
||||
@using (Html.BeginForm("myprojectsget", "admin", FormMethod.Post,
|
||||
new { enctype = "multipart/form-data" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
@Html.HiddenFor(m => m.id)
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(m => m.id, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(m => m.id, new { @class = "form-control bg-dark text-white", @readonly = "readonly" })
|
||||
</div>
|
||||
@Html.LabelFor(x => x.id, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(x => x.id, new { @class = "form-control bg-dark text-white", @readonly = "readonly" })
|
||||
<br />
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(m => m.TitleEN, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(m => m.TitleEN, new { @class = "form-control bg-dark text-white required" })
|
||||
</div>
|
||||
@Html.LabelFor(p => p.TitleEN, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.TitleEN, new { @class = "form-control bg-dark text-white required" })
|
||||
<br />
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(m => m.DescEN, new { @class = "text-white" })
|
||||
@Html.TextAreaFor(m => m.DescEN, new { @class = "form-control bg-dark text-white required", rows = "6" })
|
||||
</div>
|
||||
@Html.LabelFor(p => p.DescEN, new { @class = "text-white" })
|
||||
@Html.TextAreaFor(p => p.DescEN, new { @class = "form-control bg-dark text-white required" })
|
||||
<small class="text-muted">To add a link, use: [Link text](https://example.com)</small>
|
||||
<br />
|
||||
|
||||
if (!string.IsNullOrEmpty(Model.image))
|
||||
{
|
||||
<div class="mb-3">
|
||||
<label class="text-white">Current Image</label>
|
||||
<br />
|
||||
<img src="@Model.image" style="max-width:200px;" class="mb-2" />
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="text-white">Change Image</label>
|
||||
<input type="file" name="file" class="form-control bg-dark text-white" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(m => m.iframe, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(m => m.iframe, new { @class = "form-control bg-dark text-white" })
|
||||
</div>
|
||||
|
||||
<h3 class="mt-3 text-white">Translation (TR)</h3>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(m => m.TitleTR, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(m => m.TitleTR, new { @class = "form-control bg-dark text-white required" })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(m => m.DescTR, new { @class = "text-white" })
|
||||
@Html.TextAreaFor(m => m.DescTR, new { @class = "form-control bg-dark text-white required", rows = "6" })
|
||||
</div>
|
||||
<h3 class="mt-3 text-white">Translation</h3>
|
||||
@Html.LabelFor(m => m.TitleTR, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(m => m.TitleTR, new { @class = "form-control bg-dark text-white required" })
|
||||
<br />
|
||||
@Html.LabelFor(m => m.DescTR, new { @class = "text-white" })
|
||||
@Html.TextAreaFor(m => m.DescTR, new { @class = "form-control bg-dark text-white required" })
|
||||
<small class="text-muted">To add a link, use: [Link text](https://example.com)</small>
|
||||
<br />
|
||||
|
||||
<button class="btn btn-success">Save</button>
|
||||
}
|
||||
}
|
||||
|
||||
<hr />
|
||||
<h3 class="mt-3 text-white">Media Manager</h3>
|
||||
|
||||
@if (Model.medias != null && Model.medias.Count > 0)
|
||||
{
|
||||
<div class="bg-dark p-3 rounded">
|
||||
@foreach (var m in Model.medias)
|
||||
{
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div class="text-white">
|
||||
<b>@m.media_type</b>
|
||||
<span class="text-muted"> (order: @m.sort_order)</span>
|
||||
<div style="word-break:break-all;">@m.url</div>
|
||||
@*@if (!string.IsNullOrWhiteSpace(m.title))
|
||||
{
|
||||
<div class="text-muted">@m.title</div>
|
||||
}*@
|
||||
</div>
|
||||
|
||||
@using (Html.BeginForm("deletemedia", "admin", new { id = m.id, projectId = Model.id }, FormMethod.Post, new { onsubmit = "return confirm('Delete this media?');" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
|
||||
}
|
||||
</div>
|
||||
<hr class="text-muted" />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-muted">No media for this project.</div>
|
||||
}
|
||||
|
||||
@using (Html.BeginForm("addmedia", "admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<input type="hidden" name="projectId" value="@Model.id" />
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="text-white">Type</label>
|
||||
<select id="mediaType" name="mediaType" class="form-control bg-dark text-white">
|
||||
<option value="1">Image</option>
|
||||
<option value="2">Iframe</option>
|
||||
@*<option value="3">Video</option>*@
|
||||
@*<option value="4">Document</option>*@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- IFRAME URL -->
|
||||
<div id="urlWrap" class="mb-2" style="display:none;">
|
||||
<label class="text-white">URL</label>
|
||||
<input id="iframeUrl" name="iframeUrl" class="form-control bg-dark text-white" placeholder="https://..." />
|
||||
<small class="text-muted">Only for iframe embed links.</small>
|
||||
</div>
|
||||
|
||||
<!-- FILE UPLOAD -->
|
||||
<div id="fileWrap" class="mb-2">
|
||||
<label class="text-white">Upload</label>
|
||||
<input id="mediaFile" type="file" name="mediaFile" class="form-control bg-dark text-white" />
|
||||
<button type="button" class="btn btn-outline-light btn-sm mt-2" onclick="PasteImageModal.open('#mediaFile')">
|
||||
<i class="fa-solid fa-paste me-1"></i>Paste image
|
||||
</button>
|
||||
<small class="text-muted d-block mt-1">Upload image/document files.</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="text-white">Sort Order</label>
|
||||
<input name="sortOrder" type="number" value="0" class="form-control bg-dark text-white" />
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary">Add Media</button>
|
||||
}
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var sel = document.getElementById("mediaType");
|
||||
var urlWrap = document.getElementById("urlWrap");
|
||||
var fileWrap = document.getElementById("fileWrap");
|
||||
var iframeUrl = document.getElementById("iframeUrl");
|
||||
var mediaFile = document.getElementById("mediaFile");
|
||||
|
||||
function sync() {
|
||||
var isIframe = sel.value === "2"; // Iframe
|
||||
urlWrap.style.display = isIframe ? "block" : "none";
|
||||
fileWrap.style.display = isIframe ? "none" : "block";
|
||||
|
||||
// optional: clear the hidden field so server doesn't get junk
|
||||
if (isIframe) {
|
||||
if (mediaFile) mediaFile.value = "";
|
||||
} else {
|
||||
if (iframeUrl) iframeUrl.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
sel.addEventListener("change", sync);
|
||||
sync();
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
@model atakanozbancom.Models.classes.AdminAffiliateLinkVM
|
||||
@{
|
||||
ViewBag.Title = "Add New Affiliate Link";
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
}
|
||||
|
||||
<h2 class="text-white mt-3">Add New Affiliate Link</h2>
|
||||
<hr />
|
||||
|
||||
@using (Html.BeginForm("newaffiliatepost", "admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
@Html.ValidationSummary(false, "", new { @class = "alert alert-danger" + (ViewData.ModelState.IsValid ? " d-none" : "") })
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="text-white">Logo</label>
|
||||
<img id="logoPreview" style="display:none;width:64px;height:64px;object-fit:contain;background:#fff;border-radius:6px;padding:4px;" class="mb-2 d-block" />
|
||||
<input id="logoFile" type="file" name="logoFile" class="form-control bg-dark text-white" accept="image/*" />
|
||||
<button type="button" class="btn btn-outline-light btn-sm mt-2" onclick="PasteImageModal.open('#logoFile', '#logoPreview')">
|
||||
<i class="fa-solid fa-paste me-1"></i>Paste image
|
||||
</button>
|
||||
<small class="text-muted d-block mt-1">PNG, JPG, WEBP or GIF.</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.url, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.url, new { @class = "form-control bg-dark text-white", placeholder = "https://..." })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.TitleEN, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.TitleEN, new { @class = "form-control bg-dark text-white", placeholder = "e.g. Hetzner" })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.DescEN, new { @class = "text-white" })
|
||||
@Html.TextAreaFor(p => p.DescEN, new { @class = "form-control bg-dark text-white", rows = 2, placeholder = "e.g. Get $20 free credit" })
|
||||
</div>
|
||||
|
||||
<h3 class="mt-3 text-white">Translation (TR)</h3>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.TitleTR, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.TitleTR, new { @class = "form-control bg-dark text-white" })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.DescTR, new { @class = "text-white" })
|
||||
@Html.TextAreaFor(p => p.DescTR, new { @class = "form-control bg-dark text-white", rows = 2 })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.sort_order, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.sort_order, new { @class = "form-control bg-dark text-white", type = "number" })
|
||||
<small class="text-muted">Lower numbers show first.</small>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-success">Save</button>
|
||||
}
|
||||
+102
-11
@@ -22,16 +22,7 @@
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.DescEN, new { @class = "text-white" })
|
||||
@Html.TextAreaFor(p => p.DescEN, new { @class = "form-control bg-dark text-white" })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="text-white">Project Image</label>
|
||||
<input type="file" name="file" class="form-control bg-dark text-white" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.iframe, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.iframe, new { @class = "form-control bg-dark text-white" })
|
||||
<small class="text-muted">To add a link, use: [Link text](https://example.com)</small>
|
||||
</div>
|
||||
|
||||
<h3 class="mt-3 text-white">Translation (TR)</h3>
|
||||
@@ -44,7 +35,107 @@
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.DescTR, new { @class = "text-white" })
|
||||
@Html.TextAreaFor(p => p.DescTR, new { @class = "form-control bg-dark text-white" })
|
||||
<small class="text-muted">To add a link, use: [Link text](https://example.com)</small>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-success">Save</button>
|
||||
}
|
||||
}
|
||||
|
||||
<hr />
|
||||
<h3 class="mt-3 text-white">Media Manager</h3>
|
||||
|
||||
@if (Model.medias != null && Model.medias.Count > 0)
|
||||
{
|
||||
<div class="bg-dark p-3 rounded">
|
||||
@foreach (var m in Model.medias)
|
||||
{
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div class="text-white">
|
||||
<b>@m.media_type</b>
|
||||
<span class="text-muted"> (order: @m.sort_order)</span>
|
||||
<div style="word-break:break-all;">@m.url</div>
|
||||
@*@if (!string.IsNullOrWhiteSpace(m.title))
|
||||
{
|
||||
<div class="text-muted">@m.title</div>
|
||||
}*@
|
||||
</div>
|
||||
|
||||
@using (Html.BeginForm("deletemedia", "admin", new { id = m.id, projectId = Model.id }, FormMethod.Post, new { onsubmit = "return confirm('Delete this media?');" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
|
||||
}
|
||||
</div>
|
||||
<hr class="text-muted" />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-muted">No media for this project.</div>
|
||||
}
|
||||
|
||||
@using (Html.BeginForm("addmedia", "admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<input type="hidden" name="projectId" value="@Model.id" />
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="text-white">Type</label>
|
||||
<select id="mediaType" name="mediaType" class="form-control bg-dark text-white">
|
||||
<option value="1">Image</option>
|
||||
<option value="2">Iframe</option>
|
||||
@*<option value="3">Video</option>*@
|
||||
@*<option value="4">Document</option>*@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- IFRAME URL -->
|
||||
<div id="urlWrap" class="mb-2" style="display:none;">
|
||||
<label class="text-white">URL</label>
|
||||
<input id="iframeUrl" name="iframeUrl" class="form-control bg-dark text-white" placeholder="https://..." />
|
||||
<small class="text-muted">Only for iframe embed links.</small>
|
||||
</div>
|
||||
|
||||
<!-- FILE UPLOAD -->
|
||||
<div id="fileWrap" class="mb-2">
|
||||
<label class="text-white">Upload</label>
|
||||
<input id="mediaFile" type="file" name="mediaFile" class="form-control bg-dark text-white" />
|
||||
<button type="button" class="btn btn-outline-light btn-sm mt-2" onclick="PasteImageModal.open('#mediaFile')">
|
||||
<i class="fa-solid fa-paste me-1"></i>Paste image
|
||||
</button>
|
||||
<small class="text-muted d-block mt-1">Upload image/document files.</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="text-white">Sort Order</label>
|
||||
<input name="sortOrder" type="number" value="0" class="form-control bg-dark text-white" />
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary">Add Media</button>
|
||||
}
|
||||
|
||||
<script>(function () {
|
||||
var sel = document.getElementById("mediaType");
|
||||
var urlWrap = document.getElementById("urlWrap");
|
||||
var fileWrap = document.getElementById("fileWrap");
|
||||
var iframeUrl = document.getElementById("iframeUrl");
|
||||
var mediaFile = document.getElementById("mediaFile");
|
||||
|
||||
function sync() {
|
||||
var isIframe = sel.value === "2"; // Iframe
|
||||
urlWrap.style.display = isIframe ? "block" : "none";
|
||||
fileWrap.style.display = isIframe ? "none" : "block";
|
||||
|
||||
// optional: clear the hidden field so server doesn't get junk
|
||||
if (isIframe) {
|
||||
if (mediaFile) mediaFile.value = "";
|
||||
} else {
|
||||
if (iframeUrl) iframeUrl.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
sel.addEventListener("change", sync);
|
||||
sync();
|
||||
})();
|
||||
</script>
|
||||
@@ -0,0 +1,48 @@
|
||||
@model atakanozbancom.Models.classes.AdminSocialIconVM
|
||||
@{
|
||||
ViewBag.Title = "Add New Social Icon";
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
}
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" />
|
||||
|
||||
<h2 class="text-white mt-3">Add New Social Icon</h2>
|
||||
<hr />
|
||||
|
||||
@using (Html.BeginForm("newsocialicon", "admin", FormMethod.Post))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
@Html.ValidationSummary(false, "", new { @class = "alert alert-danger" + (ViewData.ModelState.IsValid ? " d-none" : "") })
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.icon, new { @class = "text-white" })
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-dark text-white fs-4" style="width:3rem;justify-content:center;">
|
||||
<i id="icon-preview" class="@Model.icon"></i>
|
||||
</span>
|
||||
@Html.TextBoxFor(p => p.icon, new { @class = "form-control bg-dark text-white", placeholder = "e.g. fa-brands fa-youtube", id = "icon-input" })
|
||||
</div>
|
||||
<small class="text-muted">
|
||||
Any <a href="https://fontawesome.com/search?ic=brands" target="_blank" rel="noopener" class="text-info">Font Awesome</a> class name, e.g. <code>fa-brands fa-youtube</code> or <code>fa-solid fa-link</code>.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.url, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.url, new { @class = "form-control bg-dark text-white", placeholder = "https://..." })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.sort_order, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.sort_order, new { @class = "form-control bg-dark text-white", type = "number" })
|
||||
<small class="text-muted">Lower numbers show first.</small>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-success">Save</button>
|
||||
}
|
||||
|
||||
<script>
|
||||
document.getElementById('icon-input').addEventListener('input', function () {
|
||||
document.getElementById('icon-preview').className = this.value;
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,5 @@
|
||||
@{
|
||||
// Kept for old bookmarks; controller redirects to UserSettings.
|
||||
Layout = null;
|
||||
Response.Redirect(Url.Action("UserSettings", "admin"));
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
@model atakanozbancom.Models.classes.AdminSocialIconVM
|
||||
@{
|
||||
ViewBag.Title = "socialiconget";
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
}
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" />
|
||||
|
||||
<h2 class="mt-3 text-white">Edit Social Icon</h2>
|
||||
@if (TempData["ok"] != null)
|
||||
{<div class="alert alert-success">@TempData["ok"]</div>}
|
||||
<hr />
|
||||
|
||||
@using (Html.BeginForm("socialiconget", "admin", FormMethod.Post))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
@Html.HiddenFor(m => m.id)
|
||||
@Html.ValidationSummary(false, "", new { @class = "alert alert-danger" + (ViewData.ModelState.IsValid ? " d-none" : "") })
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.icon, new { @class = "text-white" })
|
||||
<div class="input-group">
|
||||
<span class="input-group-text bg-dark text-white fs-4" style="width:3rem;justify-content:center;">
|
||||
<i id="icon-preview" class="@Model.icon"></i>
|
||||
</span>
|
||||
@Html.TextBoxFor(p => p.icon, new { @class = "form-control bg-dark text-white", id = "icon-input" })
|
||||
</div>
|
||||
<small class="text-muted">
|
||||
Any <a href="https://fontawesome.com/search?ic=brands" target="_blank" rel="noopener" class="text-info">Font Awesome</a> class name, e.g. <code>fa-brands fa-youtube</code> or <code>fa-solid fa-link</code>.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.url, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.url, new { @class = "form-control bg-dark text-white" })
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@Html.LabelFor(p => p.sort_order, new { @class = "text-white" })
|
||||
@Html.TextBoxFor(p => p.sort_order, new { @class = "form-control bg-dark text-white", type = "number" })
|
||||
</div>
|
||||
|
||||
<button class="btn btn-success">Save</button>
|
||||
}
|
||||
|
||||
<script>
|
||||
document.getElementById('icon-input').addEventListener('input', function () {
|
||||
document.getElementById('icon-preview').className = this.value;
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,49 @@
|
||||
@using atakanozbancom.Models.classes
|
||||
@model List<socialicon>
|
||||
|
||||
@{
|
||||
ViewBag.Title = "socialicons";
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
}
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" />
|
||||
|
||||
<h2 class="text-white mt-3 mb-3">Social Icons</h2>
|
||||
<p class="text-white-50">These appear as the icon row at the bottom of every public page.</p>
|
||||
@if (TempData["ok"] != null)
|
||||
{<div class="alert alert-success">@TempData["ok"]</div>}
|
||||
|
||||
<table class="table table-bordered mt-3">
|
||||
<tr class="bg-dark text-white">
|
||||
<th class="bg-dark text-white">Icon</th>
|
||||
<th class="bg-dark text-white">Class</th>
|
||||
<th class="bg-dark text-white">URL</th>
|
||||
<th class="bg-dark text-white">Order</th>
|
||||
<th class="bg-dark text-white">Edit</th>
|
||||
<th class="bg-dark text-white">Delete</th>
|
||||
</tr>
|
||||
@foreach (var x in Model)
|
||||
{
|
||||
<tr>
|
||||
<td class="bg-dark text-white fs-4"><i class="@x.icon"></i></td>
|
||||
<td class="bg-dark text-white">@x.icon</td>
|
||||
<td class="bg-dark text-white" style="word-break:break-all;">@x.url</td>
|
||||
<td class="bg-dark text-white">@x.sort_order</td>
|
||||
<td class="bg-dark text-white"><a href="/admin/socialiconget/@x.id" class="btn btn-primary">Edit</a></td>
|
||||
<td class="bg-dark text-white">
|
||||
@using (Html.BeginForm("socialicondelete", "admin", new { id = x.id }, FormMethod.Post, new { onsubmit = "return confirm('Delete this icon?');" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-danger">Delete</button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@if (!Model.Any())
|
||||
{
|
||||
<tr>
|
||||
<td class="bg-dark text-white-50" colspan="6">No social icons yet.</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
<a href="/admin/newsocialicon" class="btn btn-primary">Create New Icon</a>
|
||||
@@ -0,0 +1,116 @@
|
||||
@{
|
||||
ViewBag.Title = "User Settings";
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
var enabled = ViewBag.TwoFactorEnabled == true;
|
||||
}
|
||||
|
||||
<h2 class="text-white mt-3 mb-3">User Settings</h2>
|
||||
<p class="text-white-50">Account: <strong class="text-white">@ViewBag.Username</strong></p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6 mb-4">
|
||||
<div class="bg-dark border border-secondary rounded p-4 text-white h-100">
|
||||
<h4 class="mb-3">Change password</h4>
|
||||
|
||||
@if (TempData["pwdOk"] != null)
|
||||
{
|
||||
<div class="alert alert-success py-2">@TempData["pwdOk"]</div>
|
||||
}
|
||||
@if (TempData["pwdError"] != null)
|
||||
{
|
||||
<div class="alert alert-danger py-2">@TempData["pwdError"]</div>
|
||||
}
|
||||
|
||||
@using (Html.BeginForm("ChangePassword", "admin", FormMethod.Post))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Current password</label>
|
||||
<input type="password" name="currentPassword" class="form-control bg-dark text-white" autocomplete="current-password" required />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">New password</label>
|
||||
<input type="password" name="newPassword" class="form-control bg-dark text-white" autocomplete="new-password" minlength="8" required />
|
||||
<small class="text-muted">At least 8 characters.</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Confirm new password</label>
|
||||
<input type="password" name="confirmPassword" class="form-control bg-dark text-white" autocomplete="new-password" minlength="8" required />
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Update password</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6 mb-4">
|
||||
<div class="bg-dark border border-secondary rounded p-4 text-white h-100">
|
||||
<h4 class="mb-3">
|
||||
Two-factor authentication
|
||||
@if (enabled)
|
||||
{
|
||||
<span class="badge text-bg-success ms-2">Enabled</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge text-bg-secondary ms-2">Disabled</span>
|
||||
}
|
||||
</h4>
|
||||
|
||||
@if (TempData["tfaOk"] != null)
|
||||
{
|
||||
<div class="alert alert-success py-2">@TempData["tfaOk"]</div>
|
||||
}
|
||||
@if (TempData["tfaError"] != null)
|
||||
{
|
||||
<div class="alert alert-danger py-2">@TempData["tfaError"]</div>
|
||||
}
|
||||
|
||||
@if (enabled)
|
||||
{
|
||||
<p class="text-white-50">2FA is active. Enter a current authenticator code to disable it.</p>
|
||||
using (Html.BeginForm("DisableTwoFactor", "admin", FormMethod.Post))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Authenticator code</label>
|
||||
<input type="text" name="code" class="form-control bg-dark text-white" maxlength="6" inputmode="numeric" pattern="[0-9]*" autocomplete="one-time-code" required />
|
||||
</div>
|
||||
<button type="submit" class="btn btn-danger" onclick="return confirm('Disable two-factor authentication?');">Disable 2FA</button>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<ol class="text-white-50">
|
||||
<li>Open your authenticator app and scan the QR code below.</li>
|
||||
<li>Or enter this secret manually: <code class="text-white">@ViewBag.SetupSecret</code></li>
|
||||
<li>Enter the 6-digit code to confirm and enable 2FA.</li>
|
||||
</ol>
|
||||
|
||||
<div class="text-center my-3">
|
||||
<div id="totpQr" class="d-inline-block bg-white p-2 rounded"></div>
|
||||
</div>
|
||||
|
||||
using (Html.BeginForm("EnableTwoFactor", "admin", FormMethod.Post))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Authenticator code</label>
|
||||
<input type="text" name="code" class="form-control bg-dark text-white" maxlength="6" inputmode="numeric" pattern="[0-9]*" autocomplete="one-time-code" required />
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success">Enable 2FA</button>
|
||||
}
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
var uri = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject((string)ViewBag.OtpAuthUri));
|
||||
var el = document.getElementById('totpQr');
|
||||
if (el && uri && typeof QRCode !== 'undefined') {
|
||||
new QRCode(el, { text: uri, width: 200, height: 200 });
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,37 @@
|
||||
@{
|
||||
ViewBag.Title = "wallpaper";
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
}
|
||||
|
||||
<h2 class="text-white mt-3 mb-3">Wallpaper</h2>
|
||||
<p class="text-white-50">This image is used as the background on every public page.</p>
|
||||
@if (TempData["ok"] != null)
|
||||
{<div class="alert alert-success">@TempData["ok"]</div>}
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="text-white d-block mb-2">Current wallpaper</label>
|
||||
<img src="@ViewBag.CurrentWallpaper" style="max-width:100%;max-height:300px;border-radius:8px;border:1px solid #444;" />
|
||||
</div>
|
||||
|
||||
@using (Html.BeginForm("Wallpaper", "admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="text-white">New wallpaper image</label>
|
||||
<img id="wallpaperPreview" style="display:none;max-width:100%;max-height:300px;border-radius:8px;" class="mb-2 d-block" />
|
||||
<input id="wallpaperFile" type="file" name="wallpaperFile" class="form-control bg-dark text-white" accept="image/*" />
|
||||
<button type="button" class="btn btn-outline-light btn-sm mt-2" onclick="PasteImageModal.open('#wallpaperFile', '#wallpaperPreview')">
|
||||
<i class="fa-solid fa-paste me-1"></i>Paste image
|
||||
</button>
|
||||
<small class="text-muted d-block mt-1">PNG, JPG, WEBP or GIF.</small>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-success">Upload New Wallpaper</button>
|
||||
}
|
||||
|
||||
@using (Html.BeginForm("WallpaperReset", "admin", FormMethod.Post, new { onsubmit = "return confirm('Reset wallpaper to the default image?');" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-secondary mt-2">Reset to Default</button>
|
||||
}
|
||||
+10
-21
@@ -1,19 +1,15 @@
|
||||
|
||||
@{
|
||||
@{
|
||||
Layout = null;
|
||||
}
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" data-bs-theme="dark">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Sign-in</title>
|
||||
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
|
||||
<style>
|
||||
.form-control {
|
||||
background-color: #212529 !important;
|
||||
@@ -30,16 +26,6 @@
|
||||
box-shadow: 0 0 5px rgba(13, 110, 253, 0.5);
|
||||
}
|
||||
|
||||
.form-check-input {
|
||||
background-color: #343a40 !important;
|
||||
border-color: #6c757d !important;
|
||||
}
|
||||
|
||||
.form-check-input:checked {
|
||||
background-color: #0d6efd !important;
|
||||
border-color: #0d6efd !important;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #0d6efd !important;
|
||||
border-color: #0d6efd !important;
|
||||
@@ -51,28 +37,31 @@
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-dark d-flex align-items-center justify-content-center vh-100">
|
||||
<main class="form-signin w-100" style="max-width: 400px;">
|
||||
@using (Html.BeginForm("index", "login", FormMethod.Post))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<h1 class="h3 mb-3 fw-normal text-white text-center">Please sign in</h1>
|
||||
|
||||
if (ViewBag.Error != null)
|
||||
{
|
||||
<div class="alert alert-danger py-2">@ViewBag.Error</div>
|
||||
}
|
||||
|
||||
<div class="form-floating">
|
||||
<input type="text" name="username" class="form-control" id="floatingInput" placeholder="Username">
|
||||
<input type="text" name="username" class="form-control" id="floatingInput" placeholder="Username" autocomplete="username" required>
|
||||
<label for="floatingInput">Username</label>
|
||||
</div>
|
||||
|
||||
<div class="form-floating mt-2 mb-2">
|
||||
<input type="password" name="password" class="form-control" id="floatingPassword" placeholder="Password">
|
||||
<div class="form-floating mt-2 mb-3">
|
||||
<input type="password" name="password" class="form-control" id="floatingPassword" placeholder="Password" autocomplete="current-password" required>
|
||||
<label for="floatingPassword">Password</label>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary w-100 py-2" type="submit">Sign in</button>
|
||||
}
|
||||
</main>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
@{
|
||||
Layout = null;
|
||||
}
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" data-bs-theme="dark">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Two-factor authentication</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.form-control {
|
||||
background-color: #212529 !important;
|
||||
color: white !important;
|
||||
border: 1px solid #6c757d;
|
||||
letter-spacing: 0.35rem;
|
||||
text-align: center;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #0d6efd !important;
|
||||
box-shadow: 0 0 5px rgba(13, 110, 253, 0.5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-dark d-flex align-items-center justify-content-center vh-100">
|
||||
<main class="w-100" style="max-width: 400px;">
|
||||
@using (Html.BeginForm("Verify", "login", FormMethod.Post))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<h1 class="h3 mb-2 fw-normal text-white text-center">Two-factor authentication</h1>
|
||||
<p class="text-white-50 text-center mb-3">Enter the 6-digit code from your authenticator app.</p>
|
||||
|
||||
if (ViewBag.Error != null)
|
||||
{
|
||||
<div class="alert alert-danger py-2">@ViewBag.Error</div>
|
||||
}
|
||||
|
||||
<div class="mb-3">
|
||||
<input type="text" name="code" class="form-control" maxlength="6" inputmode="numeric" pattern="[0-9]*" autocomplete="one-time-code" autofocus required />
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary w-100 py-2" type="submit">Verify</button>
|
||||
<a href="@Url.Action("Index", "login")" class="btn btn-link text-white-50 w-100 mt-2">Back to sign in</a>
|
||||
}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,13 +1,11 @@
|
||||
@using Resources
|
||||
|
||||
@{
|
||||
@{
|
||||
ViewBag.Title = "index";
|
||||
Layout = "~/Views/Shared/_PortfolioLayout.cshtml";
|
||||
}
|
||||
|
||||
<main class="px-3">
|
||||
<h1 class="index-title">@Resource.MainH1</h1>
|
||||
<h1 class="index-title">@atakanozbancom.App_GlobalResources.Resource.MainH1</h1>
|
||||
<div class="wrapper fs-5">
|
||||
@Resource.MainDiv
|
||||
@atakanozbancom.App_GlobalResources.Resource.MainDiv
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
@using atakanozbancom.Models.classes
|
||||
@model List<socialicon>
|
||||
|
||||
@foreach (var x in Model)
|
||||
{
|
||||
<a href="@x.url" class="text-decoration-none text-white-50">
|
||||
<i class="@x.icon"></i>
|
||||
</a>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<style>
|
||||
.background-image {
|
||||
background-image: url('@ViewBag.WallpaperUrl');
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,10 @@
|
||||
@using Resources
|
||||
@{
|
||||
@{
|
||||
ViewBag.Title = "index";
|
||||
Layout = "~/Views/Shared/_PortfolioLayout.cshtml";
|
||||
}
|
||||
<link href="@Url.Content("~/assets/css/style.css")" rel="stylesheet" />
|
||||
|
||||
<main class="px-3">
|
||||
<h1 class="m-4 text-white">@Resource.MyProjectsTitle</h1>
|
||||
<h1 class="m-4 text-white">@atakanozbancom.App_GlobalResources.Resource.MyProjectsTitle</h1>
|
||||
@Html.Action("projectposts", "myprojects")
|
||||
</main>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
@using Resources
|
||||
@model IEnumerable<atakanozbancom.Models.classes.MyProjectVM>
|
||||
@model IEnumerable<atakanozbancom.Models.classes.MyProjectsVM>
|
||||
|
||||
<p class="text-center">
|
||||
@(Resource.MyProjectsP1)
|
||||
<a href="https://link.atakanozban.com/github" style="text-decoration:none;color:darkgrey;" target="_blank">
|
||||
@Resource.GitHub
|
||||
@(atakanozbancom.App_GlobalResources.Resource.MyProjectsP1)
|
||||
<a href="https://git.atakanozban.com/gitea" style="text-decoration:none;color:darkgrey;" target="_blank">
|
||||
@atakanozbancom.App_GlobalResources.Resource.Gitea
|
||||
</a>
|
||||
@Resource.MyProjectsP1_1
|
||||
@atakanozbancom.App_GlobalResources.Resource.MyProjectsP1_1
|
||||
</p>
|
||||
|
||||
<style>
|
||||
@@ -38,6 +37,7 @@
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: contain;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.media-arrow {
|
||||
@@ -58,22 +58,100 @@
|
||||
.carousel-control-next {
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.carousel-indicators {
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
#projectLightboxModal .modal-dialog {
|
||||
width: auto;
|
||||
max-width: calc(100vw - 2rem);
|
||||
margin: 0.5rem auto;
|
||||
}
|
||||
|
||||
#projectLightboxModal .modal-content {
|
||||
background: transparent;
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#projectLightboxModal .modal-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#projectLightboxModal .lightbox-stage {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
max-width: calc(100vw - 2rem);
|
||||
line-height: 0;
|
||||
background: #000;
|
||||
border-radius: 0.35rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#projectLightboxModal .lightbox-stage img {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: calc(100vw - 2rem);
|
||||
max-height: 85vh;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
#projectLightboxModal .lightbox-arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
bottom: auto;
|
||||
transform: translateY(-50%);
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
opacity: 0.9;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#projectLightboxModal .lightbox-arrow.carousel-control-prev {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#projectLightboxModal .lightbox-arrow.carousel-control-next {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#projectLightboxModal .lightbox-indicators {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0.75rem;
|
||||
margin: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#projectLightboxModal .lightbox-indicators button {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(255, 255, 255, .5);
|
||||
border: 0;
|
||||
margin: 0 4px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#projectLightboxModal .lightbox-indicators button.active {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#projectLightboxModal .lightbox-close {
|
||||
position: absolute;
|
||||
top: 0.6rem;
|
||||
right: 0.6rem;
|
||||
z-index: 3;
|
||||
}
|
||||
</style>
|
||||
|
||||
@foreach (var p in Model)
|
||||
{
|
||||
var media = new List<(string Type, string Url)>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(p.iframe))
|
||||
{
|
||||
media.Add(("iframe", p.iframe));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(p.image))
|
||||
{
|
||||
media.Add(("image", p.image));
|
||||
}
|
||||
|
||||
var media = p.medias ?? new List<atakanozbancom.Models.classes.ProjectMediaVM>();
|
||||
var carouselId = $"projCarousel_{p.id}";
|
||||
var hasMultiple = media.Count > 1;
|
||||
|
||||
@@ -82,6 +160,22 @@
|
||||
@if (media.Count > 0)
|
||||
{
|
||||
<div id="@carouselId" class="carousel slide" data-bs-ride="carousel">
|
||||
|
||||
@if (hasMultiple)
|
||||
{
|
||||
<div class="carousel-indicators">
|
||||
@for (int i = 0; i < media.Count; i++)
|
||||
{
|
||||
<button type="button"
|
||||
data-bs-target="#@carouselId"
|
||||
data-bs-slide-to="@i"
|
||||
class="@(i == 0 ? "active" : "")"
|
||||
aria-current="@(i == 0 ? "true" : null)"
|
||||
aria-label="Slide @(i + 1)"></button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="carousel-inner">
|
||||
|
||||
@for (int i = 0; i < media.Count; i++)
|
||||
@@ -89,16 +183,17 @@
|
||||
var m = media[i];
|
||||
|
||||
<div class="carousel-item @(i == 0 ? "active" : "")"
|
||||
data-bs-interval="@(m.Type == "iframe" ? "false" : "5000")">
|
||||
data-bs-interval="false">
|
||||
|
||||
@if (m.Type == "iframe")
|
||||
@if (m.type == "iframe")
|
||||
{
|
||||
<div class="carousel-media-iframe">
|
||||
<iframe src="@m.Url"
|
||||
<iframe src="@m.url"
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer-when-downgrade"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowfullscreen></iframe>
|
||||
allowfullscreen
|
||||
referrerpolicy="no-referrer"
|
||||
sandbox="allow-scripts allow-same-origin allow-popups allow-forms"></iframe>
|
||||
|
||||
|
||||
@if (hasMultiple)
|
||||
{
|
||||
@@ -120,10 +215,14 @@
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
else if (m.type == "image")
|
||||
{
|
||||
<div class="carousel-media-image">
|
||||
<img src="@m.Url" alt="@p.title" />
|
||||
<img src="@m.url"
|
||||
alt="@p.title"
|
||||
class="project-lightbox-trigger"
|
||||
data-fullsrc="@m.url"
|
||||
data-gallery="@carouselId" />
|
||||
|
||||
@if (hasMultiple)
|
||||
{
|
||||
@@ -131,20 +230,33 @@
|
||||
type="button"
|
||||
data-bs-target="#@carouselId"
|
||||
data-bs-slide="prev">
|
||||
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">Previous</span>
|
||||
<span class="carousel-control-prev-icon"></span>
|
||||
</button>
|
||||
|
||||
<button class="carousel-control-next media-arrow"
|
||||
type="button"
|
||||
data-bs-target="#@carouselId"
|
||||
data-bs-slide="next">
|
||||
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">Next</span>
|
||||
<span class="carousel-control-next-icon"></span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@*else if (m.type == "video")
|
||||
{
|
||||
<video class="d-block w-100" controls preload="metadata">
|
||||
<source src="@m.url" type="video/mp4" />
|
||||
</video>
|
||||
}*@
|
||||
@*else if (m.type == "document")
|
||||
{
|
||||
<div class="ratio ratio-4x3">
|
||||
<iframe src="@m.url"></iframe>
|
||||
</div>
|
||||
<div class="text-center mt-2">
|
||||
<a href="@m.url" target="_blank">Open document</a>
|
||||
</div>
|
||||
}*@
|
||||
|
||||
</div>
|
||||
}
|
||||
@@ -154,7 +266,187 @@
|
||||
}
|
||||
|
||||
<h3 class="mt-3 text-center">@p.title</h3>
|
||||
<p class="lead mb-5 text-center">@Html.Raw(p.description)</p>
|
||||
<p class="lead mb-5 text-center">@Html.Raw(atakanozbancom.Models.classes.DescriptionFormatter.ToSafeHtml(p.description))</p>
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="modal fade" id="projectLightboxModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content border-0 shadow-none">
|
||||
<div class="modal-body">
|
||||
<div class="lightbox-stage">
|
||||
<button type="button" class="btn-close btn-close-white lightbox-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<img id="projectLightboxImg" src="" alt="" />
|
||||
<button type="button" id="lightboxPrev" class="carousel-control-prev lightbox-arrow" aria-label="Previous">
|
||||
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button type="button" id="lightboxNext" class="carousel-control-next lightbox-arrow" aria-label="Next">
|
||||
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||
</button>
|
||||
<div id="lightboxIndicators" class="carousel-indicators lightbox-indicators d-none"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var items = [];
|
||||
var index = 0;
|
||||
var modalEl = null;
|
||||
var imgEl = null;
|
||||
var stageEl = null;
|
||||
var dialogEl = null;
|
||||
var indicatorsEl = null;
|
||||
var prevBtn = null;
|
||||
var nextBtn = null;
|
||||
|
||||
function getEls() {
|
||||
modalEl = document.getElementById('projectLightboxModal');
|
||||
imgEl = document.getElementById('projectLightboxImg');
|
||||
indicatorsEl = document.getElementById('lightboxIndicators');
|
||||
prevBtn = document.getElementById('lightboxPrev');
|
||||
nextBtn = document.getElementById('lightboxNext');
|
||||
stageEl = modalEl ? modalEl.querySelector('.lightbox-stage') : null;
|
||||
dialogEl = modalEl ? modalEl.querySelector('.modal-dialog') : null;
|
||||
return modalEl && imgEl && indicatorsEl && prevBtn && nextBtn && stageEl && dialogEl;
|
||||
}
|
||||
|
||||
function fitToImage() {
|
||||
var maxW = Math.max(window.innerWidth - 32, 200);
|
||||
var maxH = Math.max(window.innerHeight * 0.85, 200);
|
||||
var nw = imgEl.naturalWidth || 0;
|
||||
var nh = imgEl.naturalHeight || 0;
|
||||
if (!nw || !nh) return;
|
||||
|
||||
var scale = Math.min(maxW / nw, maxH / nh, 1);
|
||||
var w = Math.round(nw * scale);
|
||||
var h = Math.round(nh * scale);
|
||||
|
||||
imgEl.style.width = w + 'px';
|
||||
imgEl.style.height = h + 'px';
|
||||
stageEl.style.width = w + 'px';
|
||||
stageEl.style.height = h + 'px';
|
||||
dialogEl.style.width = w + 'px';
|
||||
dialogEl.style.maxWidth = w + 'px';
|
||||
}
|
||||
|
||||
function showSlide(i) {
|
||||
if (!items.length) return;
|
||||
index = (i + items.length) % items.length;
|
||||
var item = items[index];
|
||||
|
||||
imgEl.onload = fitToImage;
|
||||
imgEl.src = item.src;
|
||||
imgEl.alt = item.alt;
|
||||
if (imgEl.complete && imgEl.naturalWidth) fitToImage();
|
||||
|
||||
var dots = indicatorsEl.querySelectorAll('button');
|
||||
for (var d = 0; d < dots.length; d++) {
|
||||
dots[d].classList.toggle('active', d === index);
|
||||
if (d === index) dots[d].setAttribute('aria-current', 'true');
|
||||
else dots[d].removeAttribute('aria-current');
|
||||
}
|
||||
}
|
||||
|
||||
function buildIndicators() {
|
||||
indicatorsEl.innerHTML = '';
|
||||
if (items.length < 2) {
|
||||
indicatorsEl.classList.add('d-none');
|
||||
prevBtn.classList.add('d-none');
|
||||
nextBtn.classList.add('d-none');
|
||||
return;
|
||||
}
|
||||
|
||||
indicatorsEl.classList.remove('d-none');
|
||||
prevBtn.classList.remove('d-none');
|
||||
nextBtn.classList.remove('d-none');
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
(function (slideIndex) {
|
||||
var btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.setAttribute('aria-label', 'Slide ' + (slideIndex + 1));
|
||||
if (slideIndex === index) {
|
||||
btn.classList.add('active');
|
||||
btn.setAttribute('aria-current', 'true');
|
||||
}
|
||||
btn.addEventListener('click', function () { showSlide(slideIndex); });
|
||||
indicatorsEl.appendChild(btn);
|
||||
})(i);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
var trigger = e.target.closest('.project-lightbox-trigger');
|
||||
if (!trigger) return;
|
||||
if (typeof bootstrap === 'undefined') return;
|
||||
if (!getEls()) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
var gallery = trigger.getAttribute('data-gallery');
|
||||
var galleryImgs = gallery
|
||||
? document.querySelectorAll('.project-lightbox-trigger[data-gallery="' + gallery + '"]')
|
||||
: [trigger];
|
||||
|
||||
items = [];
|
||||
index = 0;
|
||||
for (var i = 0; i < galleryImgs.length; i++) {
|
||||
var img = galleryImgs[i];
|
||||
items.push({
|
||||
src: img.getAttribute('data-fullsrc') || img.src,
|
||||
alt: img.alt || ''
|
||||
});
|
||||
if (img === trigger) index = i;
|
||||
}
|
||||
|
||||
buildIndicators();
|
||||
showSlide(index);
|
||||
bootstrap.Modal.getOrCreateInstance(modalEl).show();
|
||||
});
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
if (e.target.closest('#lightboxPrev')) {
|
||||
e.preventDefault();
|
||||
showSlide(index - 1);
|
||||
} else if (e.target.closest('#lightboxNext')) {
|
||||
e.preventDefault();
|
||||
showSlide(index + 1);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (!modalEl || !modalEl.classList.contains('show')) return;
|
||||
if (e.key === 'ArrowLeft') showSlide(index - 1);
|
||||
if (e.key === 'ArrowRight') showSlide(index + 1);
|
||||
});
|
||||
|
||||
window.addEventListener('resize', function () {
|
||||
if (modalEl && modalEl.classList.contains('show')) fitToImage();
|
||||
});
|
||||
|
||||
document.addEventListener('hidden.bs.modal', function (e) {
|
||||
if (e.target.id !== 'projectLightboxModal') return;
|
||||
if (!imgEl) getEls();
|
||||
if (imgEl) {
|
||||
imgEl.onload = null;
|
||||
imgEl.removeAttribute('src');
|
||||
imgEl.style.width = '';
|
||||
imgEl.style.height = '';
|
||||
}
|
||||
if (stageEl) {
|
||||
stageEl.style.width = '';
|
||||
stageEl.style.height = '';
|
||||
}
|
||||
if (dialogEl) {
|
||||
dialogEl.style.width = '';
|
||||
dialogEl.style.maxWidth = '';
|
||||
}
|
||||
items = [];
|
||||
index = 0;
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@@ -0,0 +1,35 @@
|
||||
@model List<atakanozbancom.Models.classes.AffiliateLinkVM>
|
||||
@{
|
||||
ViewBag.Title = "index";
|
||||
Layout = "~/Views/Shared/_PortfolioLayout.cshtml";
|
||||
}
|
||||
|
||||
<main class="px-3">
|
||||
<h1 class="mb-3">@atakanozbancom.App_GlobalResources.Resource.AffiliateLinksTitle</h1>
|
||||
<p class="mb-4 text-white">@atakanozbancom.App_GlobalResources.Resource.AffiliateLinksIntro</p>
|
||||
|
||||
@if (Model != null && Model.Any())
|
||||
{
|
||||
<div class="affiliate-list mb-5">
|
||||
@foreach (var link in Model)
|
||||
{
|
||||
<a class="affiliate-card" href="@link.url" target="_blank" rel="nofollow sponsored noopener">
|
||||
<img class="affiliate-logo" src="@link.image" alt="@link.title" loading="lazy" />
|
||||
<div class="affiliate-text">
|
||||
<div class="affiliate-title">@link.title</div>
|
||||
@if (!string.IsNullOrWhiteSpace(link.description))
|
||||
{
|
||||
<div class="affiliate-desc">@link.description</div>
|
||||
}
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="mb-5 text-start">
|
||||
@atakanozbancom.App_GlobalResources.Resource.AffiliateLinksComingSoon
|
||||
</p>
|
||||
}
|
||||
</main>
|
||||
Reference in New Issue
Block a user