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
+740
-129
@@ -13,12 +13,22 @@ namespace atakanozbancom.Controllers
|
||||
private readonly Context db = new Context();
|
||||
|
||||
[Authorize]
|
||||
public ActionResult Index() // shows what we have
|
||||
public ActionResult Index() // admin dashboard landing page
|
||||
{
|
||||
ViewBag.MyProjectsCount = db.MyProjects.Count();
|
||||
ViewBag.AffiliateLinksCount = db.AffiliateLinks.Count();
|
||||
ViewBag.SocialIconsCount = db.SocialIcons.Count();
|
||||
return View();
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public ActionResult MyProjects() // shows what we have
|
||||
{
|
||||
var value = db.MyProjects.ToList();
|
||||
return View(value);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet]
|
||||
public ActionResult myprojectsget(int id)
|
||||
{
|
||||
@@ -35,8 +45,6 @@ namespace atakanozbancom.Controllers
|
||||
var vm = new AdminMyProjectsEditVM
|
||||
{
|
||||
id = p.id,
|
||||
image = p.image,
|
||||
iframe = p.iframe,
|
||||
|
||||
TitleEN = p.title,
|
||||
DescEN = p.description,
|
||||
@@ -45,63 +53,39 @@ namespace atakanozbancom.Controllers
|
||||
DescTR = tr != null ? tr.description : ""
|
||||
};
|
||||
|
||||
// NEW:
|
||||
vm.medias = db.projectmedia
|
||||
.Where(m => m.project_id == p.id)
|
||||
.OrderBy(m => m.sort_order)
|
||||
.ThenBy(m => m.id)
|
||||
.ToList();
|
||||
|
||||
return View("myprojectsget", vm);
|
||||
}
|
||||
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult myprojectsget(AdminMyProjectsEditVM vm, HttpPostedFileBase file)
|
||||
public ActionResult myprojectsget(AdminMyProjectsEditVM vm)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
vm.medias = db.projectmedia
|
||||
.Where(m => m.project_id == vm.id)
|
||||
.OrderBy(m => m.sort_order)
|
||||
.ThenBy(m => m.id)
|
||||
.ToList();
|
||||
|
||||
return View("myprojectsget", vm);
|
||||
}
|
||||
|
||||
var p = db.MyProjects.FirstOrDefault(x => x.id == vm.id);
|
||||
if (p == null) return HttpNotFound();
|
||||
|
||||
var imageUrl = p.image;
|
||||
|
||||
if (file != null && file.ContentLength > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
|
||||
var allowedExtensions = new[] { ".jpg", ".jpeg", ".png", ".webp" };
|
||||
|
||||
if (!allowedExtensions.Contains(ext))
|
||||
{
|
||||
ModelState.AddModelError("", "You can just upload these formats: '.jpg', '.jpeg', '.png', '.webp'");
|
||||
return View("myprojectsget", vm);
|
||||
}
|
||||
|
||||
var maxFileSize = 5 * 1024 * 1024;
|
||||
if (file.ContentLength > maxFileSize)
|
||||
{
|
||||
ModelState.AddModelError("", "The size of file exceeds 5MB limit.");
|
||||
return View("myprojectsget", vm);
|
||||
}
|
||||
|
||||
var uploadsRoot = Server.MapPath("~/Content/uploads/projects");
|
||||
if (!Directory.Exists(uploadsRoot))
|
||||
Directory.CreateDirectory(uploadsRoot);
|
||||
|
||||
var fileName = Guid.NewGuid().ToString("N") + ext;
|
||||
var filePath = Path.Combine(uploadsRoot, fileName);
|
||||
file.SaveAs(filePath);
|
||||
|
||||
imageUrl = "/Content/uploads/projects/" + fileName;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModelState.AddModelError("", "An error occured while upload: " + ex.Message);
|
||||
return View("myprojectsget", vm);
|
||||
}
|
||||
}
|
||||
|
||||
// EN -> base
|
||||
p.title = vm.TitleEN;
|
||||
p.description = vm.DescEN;
|
||||
p.image = imageUrl;
|
||||
p.iframe = vm.iframe;
|
||||
|
||||
// TR -> translations
|
||||
var tr = db.myprojectstranslations
|
||||
@@ -121,76 +105,28 @@ namespace atakanozbancom.Controllers
|
||||
return RedirectToAction("myprojectsget", new { id = p.id });
|
||||
}
|
||||
|
||||
public ActionResult myprojectsupdate(myprojects x)
|
||||
{
|
||||
var ag = db.MyProjects.Find(x.id);
|
||||
ag.description = x.description;
|
||||
ag.title = x.title;
|
||||
ag.image = x.image;
|
||||
ag.iframe = x.iframe;
|
||||
db.SaveChanges();
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet]
|
||||
public ActionResult newmppost() // newmppost = New My Projects Post
|
||||
{
|
||||
return View(new AdminMyProjectsEditVM());
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult newmppost(AdminMyProjectsEditVM vm, HttpPostedFileBase file)
|
||||
public ActionResult newmppost(AdminMyProjectsEditVM vm)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return View(vm);
|
||||
|
||||
string imageUrl = vm.image;
|
||||
|
||||
try
|
||||
{
|
||||
if (file != null && file.ContentLength > 0)
|
||||
{
|
||||
var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
|
||||
var allowedExtensions = new[] { ".jpg", ".jpeg", ".png", ".webp" };
|
||||
|
||||
if (!allowedExtensions.Contains(ext))
|
||||
{
|
||||
ModelState.AddModelError("", "You can just upload these formats: '.jpg', '.jpeg', '.png', '.webp'");
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
var maxFileSize = 5 * 1024 * 1024;
|
||||
if (file.ContentLength > maxFileSize)
|
||||
{
|
||||
ModelState.AddModelError("", "The size of file exceeds 5MB limit.");
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
var uploadsRoot = Server.MapPath("~/Content/uploads/projects");
|
||||
if (!Directory.Exists(uploadsRoot))
|
||||
Directory.CreateDirectory(uploadsRoot);
|
||||
|
||||
var fileName = Guid.NewGuid().ToString("N") + ext;
|
||||
var filePath = Path.Combine(uploadsRoot, fileName);
|
||||
file.SaveAs(filePath);
|
||||
|
||||
imageUrl = "/Content/uploads/projects/" + fileName;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModelState.AddModelError("", "An errror occured while upload: " + ex.Message);
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
// EN -> base table
|
||||
var newProject = new myprojects
|
||||
{
|
||||
title = vm.TitleEN,
|
||||
description = vm.DescEN,
|
||||
image = imageUrl,
|
||||
iframe = vm.iframe
|
||||
description = vm.DescEN
|
||||
// image/iframe artık yok
|
||||
};
|
||||
|
||||
db.MyProjects.Add(newProject);
|
||||
@@ -201,17 +137,21 @@ namespace atakanozbancom.Controllers
|
||||
{
|
||||
project_id = newProject.id,
|
||||
culture = "tr",
|
||||
title = vm.TitleTR,
|
||||
description = vm.DescTR
|
||||
title = vm.TitleTR ?? "",
|
||||
description = vm.DescTR ?? ""
|
||||
};
|
||||
|
||||
db.myprojectstranslations.Add(tr);
|
||||
db.SaveChanges();
|
||||
|
||||
TempData["ok"] = "Project added successfully!";
|
||||
return RedirectToAction("Index");
|
||||
return RedirectToAction("myprojectsget", new { id = newProject.id }); // direkt edit sayfasına at, media eklesin
|
||||
}
|
||||
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult myprojectsdelete(int id)
|
||||
{
|
||||
var mpd = db.MyProjects.Find(id); // mpd = my projects delete
|
||||
@@ -220,42 +160,713 @@ namespace atakanozbancom.Controllers
|
||||
db.MyProjects.Remove(mpd);
|
||||
db.SaveChanges();
|
||||
}
|
||||
return RedirectToAction("Index");
|
||||
return RedirectToAction("MyProjects");
|
||||
}
|
||||
|
||||
//[HttpPost]
|
||||
//[ValidateAntiForgeryToken]
|
||||
//public ActionResult EditProject(AdminMyProjectsEditVM vm)
|
||||
//{
|
||||
// if (!ModelState.IsValid)
|
||||
// {
|
||||
// return View("myprojectsget", vm);
|
||||
// }
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult addmedia(
|
||||
int projectId,
|
||||
int mediaType,
|
||||
int sortOrder = 0,
|
||||
string iframeUrl = null,
|
||||
HttpPostedFileBase mediaFile = null)
|
||||
{
|
||||
var type = (MediaType)mediaType;
|
||||
|
||||
// var p = db.MyProjects.FirstOrDefault(x => x.id == vm.id);
|
||||
// if (p == null) return HttpNotFound();
|
||||
// 1) IFRAME -> URL
|
||||
if (type == MediaType.Iframe)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(iframeUrl))
|
||||
{
|
||||
TempData["ok"] = "Iframe URL boş olamaz.";
|
||||
return RedirectToAction("myprojectsget", new { id = projectId });
|
||||
}
|
||||
|
||||
// p.image = vm.image;
|
||||
// p.iframe = vm.iframe;
|
||||
// p.title = vm.TitleEN;
|
||||
// p.description = vm.DescEN;
|
||||
var trimmed = iframeUrl.Trim();
|
||||
if (!Uri.TryCreate(trimmed, UriKind.Absolute, out var embedUri)
|
||||
|| (embedUri.Scheme != Uri.UriSchemeHttps && embedUri.Scheme != Uri.UriSchemeHttp))
|
||||
{
|
||||
TempData["ok"] = "Iframe URL must be a valid http(s) address.";
|
||||
return RedirectToAction("myprojectsget", new { id = projectId });
|
||||
}
|
||||
|
||||
// var tr = db.myprojectstranslations
|
||||
// .FirstOrDefault(x => x.project_id == p.id && (x.culture == "tr" || x.culture == "tr-TR"));
|
||||
var row = new projectmedia
|
||||
{
|
||||
project_id = projectId,
|
||||
media_type = MediaType.Iframe,
|
||||
url = trimmed,
|
||||
sort_order = sortOrder
|
||||
};
|
||||
|
||||
// if (tr == null)
|
||||
// {
|
||||
// tr = new myprojectstranslation { project_id = p.id, culture = "tr" };
|
||||
// db.myprojectstranslations.Add(tr);
|
||||
// }
|
||||
db.projectmedia.Add(row);
|
||||
db.SaveChanges();
|
||||
|
||||
// tr.title = vm.TitleTR;
|
||||
// tr.description = vm.DescTR;
|
||||
TempData["ok"] = "Media eklendi!";
|
||||
return RedirectToAction("myprojectsget", new { id = projectId });
|
||||
}
|
||||
|
||||
// db.SaveChanges();
|
||||
// 2) IMAGE/VIDEO/DOCUMENT -> FILE
|
||||
if (mediaFile == null || mediaFile.ContentLength <= 0)
|
||||
{
|
||||
TempData["ok"] = "Dosya seçmelisin.";
|
||||
return RedirectToAction("myprojectsget", new { id = projectId });
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(mediaFile.FileName)?.ToLowerInvariant() ?? "";
|
||||
|
||||
// Tip bazlı uzantı whitelist
|
||||
string[] allowed;
|
||||
switch (type)
|
||||
{
|
||||
case MediaType.Image:
|
||||
allowed = new[] { ".jpg", ".jpeg", ".png", ".webp", ".gif" };
|
||||
break;
|
||||
//case MediaType.Video:
|
||||
// allowed = new[] { ".mp4", ".webm", ".mov" };
|
||||
// break;
|
||||
//case MediaType.Document:
|
||||
// allowed = new[] { ".pdf" };
|
||||
// break;
|
||||
default:
|
||||
TempData["ok"] = "Geçersiz media type.";
|
||||
return RedirectToAction("myprojectsget", new { id = projectId });
|
||||
}
|
||||
|
||||
if (!allowed.Contains(ext))
|
||||
{
|
||||
TempData["ok"] = "Bu dosya tipi kabul edilmiyor: " + ext;
|
||||
return RedirectToAction("myprojectsget", new { id = projectId });
|
||||
}
|
||||
|
||||
// Upload path
|
||||
var uploadsRoot = Server.MapPath("~/Content/uploads/projects-media");
|
||||
if (!Directory.Exists(uploadsRoot))
|
||||
Directory.CreateDirectory(uploadsRoot);
|
||||
|
||||
var fileName = Guid.NewGuid().ToString("N") + ext;
|
||||
var filePath = Path.Combine(uploadsRoot, fileName);
|
||||
mediaFile.SaveAs(filePath);
|
||||
|
||||
var publicUrl = "/Content/uploads/projects-media/" + fileName;
|
||||
|
||||
var m = new projectmedia
|
||||
{
|
||||
project_id = projectId,
|
||||
media_type = type,
|
||||
url = publicUrl,
|
||||
sort_order = sortOrder
|
||||
};
|
||||
|
||||
db.projectmedia.Add(m);
|
||||
db.SaveChanges();
|
||||
|
||||
TempData["ok"] = "Media eklendi!";
|
||||
return RedirectToAction("myprojectsget", new { id = projectId });
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult deletemedia(int id, int projectId)
|
||||
{
|
||||
var m = db.projectmedia.FirstOrDefault(x => x.id == id && x.project_id == projectId);
|
||||
if (m == null)
|
||||
return RedirectToAction("myprojectsget", new { id = projectId });
|
||||
|
||||
// Dosya silme: sadece Iframe dışındakiler + URL bizim uploads klasörümüzse
|
||||
if (m.media_type != MediaType.Iframe && !string.IsNullOrWhiteSpace(m.url))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Güvenlik: sadece bizim klasördeki dosyaları sil
|
||||
var prefix = "/Content/uploads/projects-media/";
|
||||
if (m.url.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var relative = "~" + m.url; // ~/Content/uploads/...
|
||||
var physicalPath = Server.MapPath(relative);
|
||||
|
||||
if (System.IO.File.Exists(physicalPath))
|
||||
System.IO.File.Delete(physicalPath);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// istersen log yazdırırız, şimdilik sessiz geçiyoruz
|
||||
}
|
||||
}
|
||||
|
||||
db.projectmedia.Remove(m);
|
||||
db.SaveChanges();
|
||||
|
||||
TempData["ok"] = "Media silindi.";
|
||||
return RedirectToAction("myprojectsget", new { id = projectId });
|
||||
}
|
||||
|
||||
// ================= AFFILIATE LINKS =================
|
||||
|
||||
// .svg intentionally excluded: SVGs can embed <script> and execute when opened directly.
|
||||
private static readonly string[] AllowedLogoExtensions = { ".jpg", ".jpeg", ".png", ".webp", ".gif" };
|
||||
private const string AffiliateLogosPrefix = "/Content/uploads/affiliate-logos/";
|
||||
|
||||
[Authorize]
|
||||
public ActionResult AffiliateLinks()
|
||||
{
|
||||
var value = db.AffiliateLinks
|
||||
.OrderBy(x => x.sort_order)
|
||||
.ThenBy(x => x.id)
|
||||
.ToList();
|
||||
return View(value);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet]
|
||||
public ActionResult newaffiliatepost()
|
||||
{
|
||||
return View(new AdminAffiliateLinkVM());
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult newaffiliatepost(AdminAffiliateLinkVM vm)
|
||||
{
|
||||
if (vm.logoFile == null || vm.logoFile.ContentLength <= 0)
|
||||
{
|
||||
ModelState.AddModelError("logoFile", "Logo image is required.");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return View(vm);
|
||||
|
||||
if (!IsSafeHttpUrl(vm.url))
|
||||
{
|
||||
ModelState.AddModelError("url", "URL must be a valid http(s) address.");
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
var logoUrl = SaveAffiliateLogo(vm.logoFile);
|
||||
if (logoUrl == null)
|
||||
{
|
||||
ModelState.AddModelError("logoFile", "This file type is not accepted.");
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
var link = new affiliatelink
|
||||
{
|
||||
title = vm.TitleEN,
|
||||
description = vm.DescEN,
|
||||
url = vm.url,
|
||||
sort_order = vm.sort_order,
|
||||
image = logoUrl
|
||||
};
|
||||
|
||||
db.AffiliateLinks.Add(link);
|
||||
db.SaveChanges();
|
||||
|
||||
var tr = new affiliatelinktranslation
|
||||
{
|
||||
affiliatelink_id = link.id,
|
||||
culture = "tr",
|
||||
title = vm.TitleTR ?? "",
|
||||
description = vm.DescTR ?? ""
|
||||
};
|
||||
|
||||
db.affiliatelinktranslations.Add(tr);
|
||||
db.SaveChanges();
|
||||
|
||||
TempData["ok"] = "Affiliate link added successfully!";
|
||||
return RedirectToAction("AffiliateLinks");
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet]
|
||||
public ActionResult affiliatelinkget(int id)
|
||||
{
|
||||
var a = db.AffiliateLinks
|
||||
.Include(x => x.Translations)
|
||||
.FirstOrDefault(x => x.id == id);
|
||||
if (a == null) return HttpNotFound();
|
||||
|
||||
var tr = a.Translations?.FirstOrDefault(t => t.culture == "tr" || t.culture == "tr-TR");
|
||||
|
||||
var vm = new AdminAffiliateLinkVM
|
||||
{
|
||||
id = a.id,
|
||||
TitleEN = a.title,
|
||||
DescEN = a.description,
|
||||
TitleTR = tr != null ? tr.title : "",
|
||||
DescTR = tr != null ? tr.description : "",
|
||||
url = a.url,
|
||||
sort_order = a.sort_order,
|
||||
existingImage = a.image
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult affiliatelinkget(AdminAffiliateLinkVM vm)
|
||||
{
|
||||
var a = db.AffiliateLinks.Find(vm.id);
|
||||
if (a == null) return HttpNotFound();
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
vm.existingImage = a.image;
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
if (!IsSafeHttpUrl(vm.url))
|
||||
{
|
||||
ModelState.AddModelError("url", "URL must be a valid http(s) address.");
|
||||
vm.existingImage = a.image;
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
if (vm.logoFile != null && vm.logoFile.ContentLength > 0)
|
||||
{
|
||||
var logoUrl = SaveAffiliateLogo(vm.logoFile);
|
||||
if (logoUrl == null)
|
||||
{
|
||||
ModelState.AddModelError("logoFile", "This file type is not accepted.");
|
||||
vm.existingImage = a.image;
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
DeleteAffiliateLogo(a.image);
|
||||
a.image = logoUrl;
|
||||
}
|
||||
|
||||
a.title = vm.TitleEN;
|
||||
a.description = vm.DescEN;
|
||||
a.url = vm.url;
|
||||
a.sort_order = vm.sort_order;
|
||||
|
||||
var tr = db.affiliatelinktranslations
|
||||
.FirstOrDefault(x => x.affiliatelink_id == a.id && (x.culture == "tr" || x.culture == "tr-TR"));
|
||||
|
||||
if (tr == null)
|
||||
{
|
||||
tr = new affiliatelinktranslation { affiliatelink_id = a.id, culture = "tr" };
|
||||
db.affiliatelinktranslations.Add(tr);
|
||||
}
|
||||
|
||||
tr.title = vm.TitleTR;
|
||||
tr.description = vm.DescTR;
|
||||
|
||||
db.SaveChanges();
|
||||
TempData["ok"] = "Affiliate link updated successfully!";
|
||||
return RedirectToAction("affiliatelinkget", new { id = a.id });
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult affiliatelinkdelete(int id)
|
||||
{
|
||||
var a = db.AffiliateLinks.Find(id);
|
||||
if (a != null)
|
||||
{
|
||||
var translations = db.affiliatelinktranslations.Where(x => x.affiliatelink_id == id);
|
||||
db.affiliatelinktranslations.RemoveRange(translations);
|
||||
|
||||
DeleteAffiliateLogo(a.image);
|
||||
db.AffiliateLinks.Remove(a);
|
||||
db.SaveChanges();
|
||||
}
|
||||
return RedirectToAction("AffiliateLinks");
|
||||
}
|
||||
|
||||
private string SaveAffiliateLogo(HttpPostedFileBase file)
|
||||
{
|
||||
var ext = Path.GetExtension(file.FileName)?.ToLowerInvariant() ?? "";
|
||||
if (!AllowedLogoExtensions.Contains(ext))
|
||||
return null;
|
||||
|
||||
var uploadsRoot = Server.MapPath("~/Content/uploads/affiliate-logos");
|
||||
if (!Directory.Exists(uploadsRoot))
|
||||
Directory.CreateDirectory(uploadsRoot);
|
||||
|
||||
var fileName = Guid.NewGuid().ToString("N") + ext;
|
||||
var filePath = Path.Combine(uploadsRoot, fileName);
|
||||
file.SaveAs(filePath);
|
||||
|
||||
return AffiliateLogosPrefix + fileName;
|
||||
}
|
||||
|
||||
private void DeleteAffiliateLogo(string publicUrl)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(publicUrl)) return;
|
||||
if (!publicUrl.StartsWith(AffiliateLogosPrefix, StringComparison.OrdinalIgnoreCase)) return;
|
||||
|
||||
try
|
||||
{
|
||||
var physicalPath = Server.MapPath("~" + publicUrl);
|
||||
if (System.IO.File.Exists(physicalPath))
|
||||
System.IO.File.Delete(physicalPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// silently ignore, matches existing media-delete behavior
|
||||
}
|
||||
}
|
||||
|
||||
// ================= SOCIAL ICONS (footer) =================
|
||||
|
||||
[Authorize]
|
||||
public ActionResult SocialIcons()
|
||||
{
|
||||
var value = db.SocialIcons
|
||||
.OrderBy(x => x.sort_order)
|
||||
.ThenBy(x => x.id)
|
||||
.ToList();
|
||||
return View(value);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet]
|
||||
public ActionResult newsocialicon()
|
||||
{
|
||||
return View(new AdminSocialIconVM());
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult newsocialicon(AdminSocialIconVM vm)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return View(vm);
|
||||
|
||||
if (!IsSafeHttpUrl(vm.url))
|
||||
{
|
||||
ModelState.AddModelError("url", "URL must be a valid http(s) address.");
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
var icon = new socialicon
|
||||
{
|
||||
icon = vm.icon,
|
||||
url = vm.url,
|
||||
sort_order = vm.sort_order
|
||||
};
|
||||
|
||||
db.SocialIcons.Add(icon);
|
||||
db.SaveChanges();
|
||||
|
||||
TempData["ok"] = "Social icon added successfully!";
|
||||
return RedirectToAction("SocialIcons");
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet]
|
||||
public ActionResult socialiconget(int id)
|
||||
{
|
||||
var s = db.SocialIcons.Find(id);
|
||||
if (s == null) return HttpNotFound();
|
||||
|
||||
var vm = new AdminSocialIconVM
|
||||
{
|
||||
id = s.id,
|
||||
icon = s.icon,
|
||||
url = s.url,
|
||||
sort_order = s.sort_order
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult socialiconget(AdminSocialIconVM vm)
|
||||
{
|
||||
var s = db.SocialIcons.Find(vm.id);
|
||||
if (s == null) return HttpNotFound();
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return View(vm);
|
||||
|
||||
if (!IsSafeHttpUrl(vm.url))
|
||||
{
|
||||
ModelState.AddModelError("url", "URL must be a valid http(s) address.");
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
s.icon = vm.icon;
|
||||
s.url = vm.url;
|
||||
s.sort_order = vm.sort_order;
|
||||
|
||||
db.SaveChanges();
|
||||
TempData["ok"] = "Social icon updated successfully!";
|
||||
return RedirectToAction("socialiconget", new { id = s.id });
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult socialicondelete(int id)
|
||||
{
|
||||
var s = db.SocialIcons.Find(id);
|
||||
if (s != null)
|
||||
{
|
||||
db.SocialIcons.Remove(s);
|
||||
db.SaveChanges();
|
||||
}
|
||||
return RedirectToAction("SocialIcons");
|
||||
}
|
||||
|
||||
// ================= WALLPAPER (site background) =================
|
||||
|
||||
private static readonly string[] AllowedWallpaperExtensions = { ".jpg", ".jpeg", ".png", ".webp", ".gif" };
|
||||
private const string WallpaperPrefix = "/Content/uploads/wallpaper/";
|
||||
private const string DefaultWallpaperUrl = "/web_atakanozbancom/assets/img/wp.jpg";
|
||||
|
||||
[Authorize]
|
||||
[HttpGet]
|
||||
public ActionResult Wallpaper()
|
||||
{
|
||||
var setting = db.SiteSettings.FirstOrDefault();
|
||||
ViewBag.CurrentWallpaper = !string.IsNullOrWhiteSpace(setting?.wallpaper) ? setting.wallpaper : DefaultWallpaperUrl;
|
||||
return View();
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Wallpaper(HttpPostedFileBase wallpaperFile)
|
||||
{
|
||||
if (wallpaperFile == null || wallpaperFile.ContentLength <= 0)
|
||||
{
|
||||
TempData["ok"] = "Please choose an image.";
|
||||
return RedirectToAction("Wallpaper");
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(wallpaperFile.FileName)?.ToLowerInvariant() ?? "";
|
||||
if (!AllowedWallpaperExtensions.Contains(ext))
|
||||
{
|
||||
TempData["ok"] = "This file type is not accepted.";
|
||||
return RedirectToAction("Wallpaper");
|
||||
}
|
||||
|
||||
var uploadsRoot = Server.MapPath("~/Content/uploads/wallpaper");
|
||||
if (!Directory.Exists(uploadsRoot))
|
||||
Directory.CreateDirectory(uploadsRoot);
|
||||
|
||||
var fileName = Guid.NewGuid().ToString("N") + ext;
|
||||
var filePath = Path.Combine(uploadsRoot, fileName);
|
||||
wallpaperFile.SaveAs(filePath);
|
||||
|
||||
var publicUrl = WallpaperPrefix + fileName;
|
||||
|
||||
var setting = db.SiteSettings.FirstOrDefault();
|
||||
if (setting == null)
|
||||
{
|
||||
setting = new sitesetting();
|
||||
db.SiteSettings.Add(setting);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteWallpaperFile(setting.wallpaper);
|
||||
}
|
||||
|
||||
setting.wallpaper = publicUrl;
|
||||
db.SaveChanges();
|
||||
|
||||
TempData["ok"] = "Wallpaper updated successfully!";
|
||||
return RedirectToAction("Wallpaper");
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult WallpaperReset()
|
||||
{
|
||||
var setting = db.SiteSettings.FirstOrDefault();
|
||||
if (setting != null && !string.IsNullOrWhiteSpace(setting.wallpaper))
|
||||
{
|
||||
DeleteWallpaperFile(setting.wallpaper);
|
||||
setting.wallpaper = null;
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
TempData["ok"] = "Wallpaper reset to default.";
|
||||
return RedirectToAction("Wallpaper");
|
||||
}
|
||||
|
||||
private void DeleteWallpaperFile(string publicUrl)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(publicUrl)) return;
|
||||
if (!publicUrl.StartsWith(WallpaperPrefix, StringComparison.OrdinalIgnoreCase)) return;
|
||||
|
||||
try
|
||||
{
|
||||
var physicalPath = Server.MapPath("~" + publicUrl);
|
||||
if (System.IO.File.Exists(physicalPath))
|
||||
System.IO.File.Delete(physicalPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public ActionResult UserSettings()
|
||||
{
|
||||
var user = GetCurrentAdmin();
|
||||
if (user == null)
|
||||
return RedirectToAction("Index", "login");
|
||||
|
||||
ViewBag.TwoFactorEnabled = user.two_factor_enabled;
|
||||
ViewBag.Username = user.username;
|
||||
|
||||
if (!user.two_factor_enabled)
|
||||
{
|
||||
var secret = Session["Pending2FaSetupSecret"] as string;
|
||||
if (string.IsNullOrWhiteSpace(secret))
|
||||
{
|
||||
secret = TotpHelper.GenerateSecret();
|
||||
Session["Pending2FaSetupSecret"] = secret;
|
||||
}
|
||||
|
||||
ViewBag.SetupSecret = secret;
|
||||
ViewBag.OtpAuthUri = TotpHelper.BuildOtpAuthUri("atakanozban.com", user.username, secret);
|
||||
}
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public ActionResult Security()
|
||||
{
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword)
|
||||
{
|
||||
var user = GetCurrentAdmin();
|
||||
if (user == null)
|
||||
return RedirectToAction("Index", "login");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(currentPassword)
|
||||
|| string.IsNullOrWhiteSpace(newPassword)
|
||||
|| string.IsNullOrWhiteSpace(confirmPassword))
|
||||
{
|
||||
TempData["pwdError"] = "All password fields are required.";
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
if (!PasswordHasher.Verify(currentPassword, user.password))
|
||||
{
|
||||
TempData["pwdError"] = "Current password is incorrect.";
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
if (newPassword.Length < 8)
|
||||
{
|
||||
TempData["pwdError"] = "New password must be at least 8 characters.";
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
if (!string.Equals(newPassword, confirmPassword, StringComparison.Ordinal))
|
||||
{
|
||||
TempData["pwdError"] = "New password and confirmation do not match.";
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
user.password = PasswordHasher.Hash(newPassword);
|
||||
db.SaveChanges();
|
||||
|
||||
TempData["pwdOk"] = "Password updated successfully.";
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult EnableTwoFactor(string code)
|
||||
{
|
||||
var user = GetCurrentAdmin();
|
||||
if (user == null)
|
||||
return RedirectToAction("Index", "login");
|
||||
|
||||
var secret = Session["Pending2FaSetupSecret"] as string;
|
||||
if (string.IsNullOrWhiteSpace(secret))
|
||||
{
|
||||
TempData["tfaError"] = "Setup expired. Please try again.";
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
if (!TotpHelper.VerifyCode(secret, code))
|
||||
{
|
||||
TempData["tfaError"] = "Invalid code. Scan the QR again and enter a fresh code.";
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
user.two_factor_secret = SecretProtector.Protect(secret);
|
||||
user.two_factor_enabled = true;
|
||||
db.SaveChanges();
|
||||
Session.Remove("Pending2FaSetupSecret");
|
||||
|
||||
TempData["tfaOk"] = "Two-factor authentication is now enabled.";
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult DisableTwoFactor(string code)
|
||||
{
|
||||
var user = GetCurrentAdmin();
|
||||
if (user == null)
|
||||
return RedirectToAction("Index", "login");
|
||||
|
||||
if (!user.two_factor_enabled || string.IsNullOrWhiteSpace(user.two_factor_secret))
|
||||
{
|
||||
TempData["tfaError"] = "Two-factor authentication is not enabled.";
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
var secret = SecretProtector.Unprotect(user.two_factor_secret) ?? user.two_factor_secret;
|
||||
if (!TotpHelper.VerifyCode(secret, code))
|
||||
{
|
||||
TempData["tfaError"] = "Invalid code. 2FA was not disabled.";
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
user.two_factor_enabled = false;
|
||||
user.two_factor_secret = null;
|
||||
db.SaveChanges();
|
||||
Session.Remove("Pending2FaSetupSecret");
|
||||
|
||||
TempData["tfaOk"] = "Two-factor authentication has been disabled.";
|
||||
return RedirectToAction("UserSettings");
|
||||
}
|
||||
|
||||
private admin GetCurrentAdmin()
|
||||
{
|
||||
var username = User?.Identity?.Name;
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
return null;
|
||||
|
||||
return db.admins.FirstOrDefault(x => x.username == username);
|
||||
}
|
||||
|
||||
private static bool IsSafeHttpUrl(string url)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
return false;
|
||||
|
||||
return Uri.TryCreate(url.Trim(), UriKind.Absolute, out var uri)
|
||||
&& (uri.Scheme == Uri.UriSchemeHttps || uri.Scheme == Uri.UriSchemeHttp);
|
||||
}
|
||||
|
||||
// TempData["ok"] = "Project updated.";
|
||||
// return RedirectToAction("myprojectsget", new { id = p.id });
|
||||
//}
|
||||
}
|
||||
}
|
||||
+169
-14
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Security;
|
||||
using atakanozbancom.Models.classes;
|
||||
@@ -10,33 +8,190 @@ namespace atakanozbancom.Controllers
|
||||
{
|
||||
public class loginController : Controller
|
||||
{
|
||||
// GET: Login
|
||||
Context c = new Context();
|
||||
private readonly Context c = new Context();
|
||||
private const int MaxPasswordFailures = 8;
|
||||
private const int MaxTotpFailures = 5;
|
||||
private const int LockMinutes = 15;
|
||||
|
||||
public ActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Index(admin ad)
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Index(admin ad)
|
||||
{
|
||||
var bilgiler = c.admins.FirstOrDefault(x => x.username == ad.username && x.password == ad.password);
|
||||
if (bilgiler != null)
|
||||
{
|
||||
FormsAuthentication.SetAuthCookie(bilgiler.username, false);
|
||||
Session["username"] = bilgiler.username.ToString();
|
||||
return RedirectToAction("Index", "admin");
|
||||
}
|
||||
else
|
||||
if (ad == null || string.IsNullOrWhiteSpace(ad.username) || string.IsNullOrWhiteSpace(ad.password))
|
||||
{
|
||||
ViewBag.Error = "Username and password are required.";
|
||||
return View();
|
||||
}
|
||||
|
||||
var user = c.admins.FirstOrDefault(x => x.username == ad.username);
|
||||
if (user == null)
|
||||
{
|
||||
ViewBag.Error = "Invalid username or password.";
|
||||
return View();
|
||||
}
|
||||
|
||||
if (user.login_lock_until.HasValue && user.login_lock_until.Value > DateTime.UtcNow)
|
||||
{
|
||||
ViewBag.Error = "Account temporarily locked. Try again later.";
|
||||
return View();
|
||||
}
|
||||
|
||||
if (!PasswordHasher.Verify(ad.password, user.password))
|
||||
{
|
||||
user.login_failed_count++;
|
||||
if (user.login_failed_count >= MaxPasswordFailures)
|
||||
{
|
||||
user.login_lock_until = DateTime.UtcNow.AddMinutes(LockMinutes);
|
||||
user.login_failed_count = 0;
|
||||
}
|
||||
c.SaveChanges();
|
||||
ViewBag.Error = "Invalid username or password.";
|
||||
return View();
|
||||
}
|
||||
|
||||
user.login_failed_count = 0;
|
||||
user.login_lock_until = null;
|
||||
|
||||
if (!PasswordHasher.IsHashed(user.password))
|
||||
{
|
||||
user.password = PasswordHasher.Hash(ad.password);
|
||||
}
|
||||
|
||||
c.SaveChanges();
|
||||
ClearPending2Fa();
|
||||
|
||||
if (user.two_factor_enabled && !string.IsNullOrWhiteSpace(user.two_factor_secret))
|
||||
{
|
||||
if (user.totp_lock_until.HasValue && user.totp_lock_until.Value > DateTime.UtcNow)
|
||||
{
|
||||
ViewBag.Error = "Too many failed 2FA attempts. Try again later.";
|
||||
return View();
|
||||
}
|
||||
|
||||
Session["Pending2FaUserId"] = user.id;
|
||||
Session["Pending2FaUsername"] = user.username;
|
||||
Session["Pending2FaExpiresUtc"] = DateTime.UtcNow.AddMinutes(10);
|
||||
return RedirectToAction("Verify");
|
||||
}
|
||||
|
||||
CompleteLogin(user.username);
|
||||
return RedirectToAction("Index", "admin");
|
||||
}
|
||||
|
||||
public ActionResult Verify()
|
||||
{
|
||||
if (!HasValidPending2Fa())
|
||||
{
|
||||
ClearPending2Fa();
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Verify(string code)
|
||||
{
|
||||
if (!HasValidPending2Fa())
|
||||
{
|
||||
ClearPending2Fa();
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var pendingId = Convert.ToInt32(Session["Pending2FaUserId"]);
|
||||
var pendingUsername = Session["Pending2FaUsername"].ToString();
|
||||
|
||||
var user = c.admins.FirstOrDefault(x => x.id == pendingId && x.username == pendingUsername);
|
||||
if (user == null || !user.two_factor_enabled || string.IsNullOrWhiteSpace(user.two_factor_secret))
|
||||
{
|
||||
ClearPending2Fa();
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
if (user.totp_lock_until.HasValue && user.totp_lock_until.Value > DateTime.UtcNow)
|
||||
{
|
||||
ViewBag.Error = "Too many failed attempts. Try again later.";
|
||||
return View();
|
||||
}
|
||||
|
||||
var secret = ResolveTotpSecret(user);
|
||||
if (string.IsNullOrWhiteSpace(secret) || !TotpHelper.VerifyCode(secret, code))
|
||||
{
|
||||
user.totp_failed_count++;
|
||||
if (user.totp_failed_count >= MaxTotpFailures)
|
||||
{
|
||||
user.totp_lock_until = DateTime.UtcNow.AddMinutes(LockMinutes);
|
||||
user.totp_failed_count = 0;
|
||||
ViewBag.Error = "Too many failed attempts. Try again later.";
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewBag.Error = "Invalid authentication code.";
|
||||
}
|
||||
c.SaveChanges();
|
||||
return View();
|
||||
}
|
||||
|
||||
user.totp_failed_count = 0;
|
||||
user.totp_lock_until = null;
|
||||
c.SaveChanges();
|
||||
|
||||
ClearPending2Fa();
|
||||
CompleteLogin(user.username);
|
||||
return RedirectToAction("Index", "admin");
|
||||
}
|
||||
|
||||
public ActionResult logout()
|
||||
{
|
||||
ClearPending2Fa();
|
||||
Session.Remove("username");
|
||||
Session.Abandon();
|
||||
FormsAuthentication.SignOut();
|
||||
return RedirectToAction("index", "login");
|
||||
}
|
||||
|
||||
private void CompleteLogin(string username)
|
||||
{
|
||||
FormsAuthentication.SetAuthCookie(username, false);
|
||||
Session["username"] = username;
|
||||
}
|
||||
|
||||
private bool HasValidPending2Fa()
|
||||
{
|
||||
if (Session["Pending2FaUserId"] == null || Session["Pending2FaUsername"] == null)
|
||||
return false;
|
||||
|
||||
var expires = Session["Pending2FaExpiresUtc"] as DateTime?;
|
||||
if (expires == null || expires.Value < DateTime.UtcNow)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string ResolveTotpSecret(admin user)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(user.two_factor_secret))
|
||||
return null;
|
||||
|
||||
var unprotected = SecretProtector.Unprotect(user.two_factor_secret);
|
||||
if (!string.IsNullOrWhiteSpace(unprotected))
|
||||
return unprotected;
|
||||
|
||||
return user.two_factor_secret;
|
||||
}
|
||||
|
||||
private void ClearPending2Fa()
|
||||
{
|
||||
Session.Remove("Pending2FaUserId");
|
||||
Session.Remove("Pending2FaUsername");
|
||||
Session.Remove("Pending2FaSetupSecret");
|
||||
Session.Remove("Pending2FaExpiresUtc");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,40 @@
|
||||
using System.Web.Mvc;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using atakanozbancom;
|
||||
using atakanozbancom.Models.classes;
|
||||
|
||||
namespace atakanozbancom.Controllers
|
||||
{
|
||||
public class MainController : languageController
|
||||
{
|
||||
private readonly Context db = new Context();
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public PartialViewResult socialicons()
|
||||
{
|
||||
var icons = db.SocialIcons
|
||||
.OrderBy(x => x.sort_order)
|
||||
.ThenBy(x => x.id)
|
||||
.ToList();
|
||||
|
||||
return PartialView(icons);
|
||||
}
|
||||
|
||||
public PartialViewResult wallpaper()
|
||||
{
|
||||
var setting = db.SiteSettings.FirstOrDefault();
|
||||
ViewBag.WallpaperUrl = !string.IsNullOrWhiteSpace(setting?.wallpaper)
|
||||
? setting.wallpaper
|
||||
: "/web_atakanozbancom/assets/img/wp.jpg";
|
||||
|
||||
return PartialView();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult ChangeLanguage(string lang, string returnUrl)
|
||||
{
|
||||
|
||||
@@ -17,64 +17,80 @@ namespace atakanozbancom.Controllers
|
||||
|
||||
public PartialViewResult projectposts()
|
||||
{
|
||||
var cultureFull = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
|
||||
var cultureShort = System.Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName;
|
||||
var cultureFull = Thread.CurrentThread.CurrentUICulture.Name;
|
||||
var cultureShort = Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName;
|
||||
var isEnglish = cultureShort == "en";
|
||||
|
||||
var model = c.MyProjects
|
||||
var projects = c.MyProjects
|
||||
.Include(p => p.Translations)
|
||||
.Select(p => new MyProjectsVM
|
||||
.ToList();
|
||||
|
||||
var ids = projects.Select(p => p.id).ToList();
|
||||
|
||||
var medias = c.projectmedia
|
||||
.Where(m => ids.Contains(m.project_id))
|
||||
.OrderBy(m => m.sort_order)
|
||||
.ThenBy(m => m.id)
|
||||
.ToList();
|
||||
|
||||
var mediaLookup = medias
|
||||
.GroupBy(m => m.project_id)
|
||||
.ToDictionary(g => g.Key, g => g.ToList());
|
||||
|
||||
var model = projects.Select(p =>
|
||||
{
|
||||
var title = !isEnglish
|
||||
? p.Translations
|
||||
.Where(t => t.culture == cultureFull || t.culture == cultureShort)
|
||||
.Select(t => t.title)
|
||||
.FirstOrDefault() ?? p.title
|
||||
: p.title;
|
||||
|
||||
var description = !isEnglish
|
||||
? p.Translations
|
||||
.Where(t => t.culture == cultureFull || t.culture == cultureShort)
|
||||
.Select(t => t.description)
|
||||
.FirstOrDefault() ?? p.description
|
||||
: p.description;
|
||||
|
||||
var vm = new MyProjectsVM
|
||||
{
|
||||
id = p.id,
|
||||
title = title,
|
||||
description = description,
|
||||
image = p.image,
|
||||
iframe = p.iframe,
|
||||
iframe = p.iframe
|
||||
};
|
||||
|
||||
title = !isEnglish
|
||||
? p.Translations
|
||||
.Where(t => t.culture == cultureFull || t.culture == cultureShort)
|
||||
.Select(t => t.title)
|
||||
.FirstOrDefault()
|
||||
?? p.title
|
||||
: p.title,
|
||||
// NEW MEDIA TABLE
|
||||
if (mediaLookup.TryGetValue(p.id, out var list))
|
||||
{
|
||||
vm.medias = list.Select(m => new ProjectMediaVM
|
||||
{
|
||||
url = m.url,
|
||||
order = m.sort_order,
|
||||
type =
|
||||
m.media_type == MediaType.Image ? "image" :
|
||||
m.media_type == MediaType.Iframe ? "iframe" :
|
||||
// m.media_type == MediaType.Video ? "video" :
|
||||
"document"
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
description = !isEnglish
|
||||
? p.Translations
|
||||
.Where(t => t.culture == cultureFull || t.culture == cultureShort)
|
||||
.Select(t => t.description)
|
||||
.FirstOrDefault()
|
||||
?? p.description
|
||||
: p.description
|
||||
})
|
||||
.ToList();
|
||||
// fallback legacy
|
||||
if (vm.medias == null || vm.medias.Count == 0)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(p.iframe))
|
||||
vm.medias.Add(new ProjectMediaVM { type = "iframe", url = p.iframe, order = 0 });
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(p.image))
|
||||
vm.medias.Add(new ProjectMediaVM { type = "image", url = p.image, order = 1 });
|
||||
}
|
||||
|
||||
return vm;
|
||||
}).ToList();
|
||||
|
||||
return PartialView(model);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult myprojectsget(int id)
|
||||
{
|
||||
|
||||
var p = c.MyProjects
|
||||
.Include(x => x.Translations)
|
||||
.FirstOrDefault(x => x.id == id);
|
||||
if (p == null) return HttpNotFound();
|
||||
|
||||
var tr = p.Translations?.FirstOrDefault(t => t.culture == "tr" || t.culture == "tr-TR");
|
||||
|
||||
var vm = new AdminMyProjectsEditVM
|
||||
{
|
||||
id = p.id,
|
||||
image = p.image,
|
||||
iframe = p.iframe,
|
||||
|
||||
TitleEN = p.title,
|
||||
DescEN = p.description,
|
||||
|
||||
TitleTR = tr?.title ?? "",
|
||||
DescTR = tr?.description ?? ""
|
||||
};
|
||||
|
||||
return View("~/Views/admin/myprojectsget.cshtml", vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using atakanozbancom;
|
||||
using atakanozbancom.Models.classes;
|
||||
|
||||
namespace atakanozbancom.Controllers
|
||||
{
|
||||
public class SupportController : Controller
|
||||
{
|
||||
private readonly Context db = new Context();
|
||||
|
||||
// GET: /Support
|
||||
[HttpGet]
|
||||
public ActionResult Index()
|
||||
{
|
||||
var cultureFull = Thread.CurrentThread.CurrentUICulture.Name;
|
||||
var cultureShort = Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName;
|
||||
var isEnglish = cultureShort == "en";
|
||||
|
||||
var links = db.AffiliateLinks
|
||||
.Include(x => x.Translations)
|
||||
.OrderBy(x => x.sort_order)
|
||||
.ThenBy(x => x.id)
|
||||
.ToList();
|
||||
|
||||
var model = links.Select(a =>
|
||||
{
|
||||
var title = !isEnglish
|
||||
? a.Translations
|
||||
.Where(t => t.culture == cultureFull || t.culture == cultureShort)
|
||||
.Select(t => t.title)
|
||||
.FirstOrDefault() ?? a.title
|
||||
: a.title;
|
||||
|
||||
var description = !isEnglish
|
||||
? a.Translations
|
||||
.Where(t => t.culture == cultureFull || t.culture == cultureShort)
|
||||
.Select(t => t.description)
|
||||
.FirstOrDefault() ?? a.description
|
||||
: a.description;
|
||||
|
||||
return new AffiliateLinkVM
|
||||
{
|
||||
id = a.id,
|
||||
title = title,
|
||||
description = description,
|
||||
url = a.url,
|
||||
image = a.image
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// POST: /Support/ChangeLanguage
|
||||
[HttpPost]
|
||||
public ActionResult ChangeLanguage(string lang)
|
||||
{
|
||||
new LanguageTR().SetLanguage(lang);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user