using GanjooRazor.Utils;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OutputCaching;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RMuseum.Models.Ganjoor.ViewModels;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace GanjooRazor.Pages
{
///
/// The site's home page only. Used to also serve every poem/poet/category page (dispatching
/// internally on Request.Path == "/") - that responsibility moved to GanjoorPageModel, which now
/// owns the site's catch-all route (see Startup.cs). This page keeps its own automatic "/" route,
/// unaffected by that change.
///
[OutputCache(PolicyName = "GanjoorPublicPage")]
public class IndexModel : LoginPartialEnabledPageModel
{
private readonly PoetCacheService _poetCache;
///
/// constructor
///
public IndexModel(IConfiguration configuration,
HttpClient httpClient,
PoetCacheService poetCache
) : base(httpClient, configuration)
{
_poetCache = poetCache;
}
public bool OfflineMode => GetConfigFlag("OfflineMode");
public bool ReadOnlyMode => GetConfigFlag("ReadOnlyMode");
///
/// last error
///
public string LastError { get; set; }
///
/// If the response failed, stores the API's error message in and
/// returns true so the caller can short-circuit.
///
private async Task CaptureErrorIfFailedAsync(HttpResponseMessage response)
{
if (response.IsSuccessStatusCode)
{
return false;
}
LastError = await ReadErrorMessageAsync(response);
return true;
}
///
/// Poets (for the home page's own search-form author dropdown)
///
public List Poets { get; set; }
private async Task preparePoets()
{
var (success, poets, error) = await _poetCache.GetPoetsAsync(EditorCacheBypass);
if (!success)
{
LastError = error;
return false;
}
Poets = poets;
return true;
}
///
/// poets grouped by century, shown on the home page
///
public List PoetGroups { get; set; }
private async Task _PreparePoetGroups()
{
try
{
var response = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/centuries");
if (await CaptureErrorIfFailedAsync(response))
{
return false;
}
PoetGroups = JArray.Parse(await response.Content.ReadAsStringAsync()).ToObject>();
return true;
}
catch
{
LastError = "خطا در دسترسی به وب سرویس دیوان";
return false;
}
}
///
/// Handles the legacy "?p=<id>" query-string form by resolving it to the page's real
/// URL and redirecting there. Needed here too (not just on GanjoorPageModel): query strings
/// don't affect ASP.NET Core route matching, so a link like "/?p=123" always has
/// Path == "/" and is always routed to this page regardless of the split.
///
private async Task RedirectByPageIdAsync(string pageId)
{
var pageUrlResponse = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/pageurl?id={pageId}");
if (await CaptureErrorIfFailedAsync(pageUrlResponse))
{
return Page();
}
var pageUrl = JsonConvert.DeserializeObject(await pageUrlResponse.Content.ReadAsStringAsync());
return Redirect(pageUrl);
}
///
/// JSON endpoint for the home page's "مرور کتابها" horizontal book shelf (see bk.js's
/// initHomeBooksShelf()). Fetched client-side via "?Handler=BookCatalog" rather than hitting
/// the api/ganjoor/book-catalog API route directly from browser JS, same as every other
/// client-side data fetch on this site - the browser only ever talks to this page's own
/// handlers, which proxy to the API server-side.
///
public async Task OnGetBookCatalogAsync()
{
var response = await _httpClient.GetAsync($"{APIRoot.Url}/api/ganjoor/book-catalog");
if (!response.IsSuccessStatusCode)
{
return new BadRequestObjectResult(await ReadErrorMessageAsync(response));
}
var books = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
return new OkObjectResult(books);
}
public async Task OnGetAsync()
{
var maintenanceResult = TryGetMaintenanceModeResult();
if (maintenanceResult != null)
{
return maintenanceResult;
}
LastError = "";
InitializeCommonPageState();
if (!string.IsNullOrEmpty(Request.Query["p"]))
{
return await RedirectByPageIdAsync(Request.Query["p"]);
}
if (!await preparePoets())
{
return Page();
}
if (!await _PreparePoetGroups())
{
return Page();
}
// A fresh/forked install has an empty (or nearly empty) database: no poets, or no
// century grouping yet. The view below assumes at least the "popular poets" group
// (Id == 0) exists — PoetGroups.Where(g => g.Id == 0).Single() — and throws on an
// empty database instead of rendering something useful. Steer the visitor toward
// fixing that instead of letting them hit an unhandled exception: log in first if
// needed, then straight to the admin page that can seed real content.
if (IsDatabaseEffectivelyEmpty())
{
const string targetUrl = "/Admin/PublicDataImport";
return LoggedIn ? Redirect(targetUrl) : Redirect($"/login?redirect={targetUrl}");
}
ViewData["Title"] = "دیوان";
return Page();
}
///
/// true if there's nothing meaningful to show on the home page yet — no poets at all, or
/// no century grouping (the "popular poets" group with Id == 0 that the view relies on).
///
private bool IsDatabaseEffectivelyEmpty()
{
return Poets == null || Poets.Count == 0
|| PoetGroups == null || !PoetGroups.Any(g => g.Id == 0);
}
public async Task OnGetPoetInformationAsync(int id)
{
if (id == 0)
{
return new OkObjectResult(null);
}
var (success, poet, error) = await _poetCache.GetPoetAsync(id, EditorCacheBypass);
if (!success)
{
return BadRequest(error);
}
return new OkObjectResult(poet);
}
}
}