Projects (GanjooRazor -> DiwanRazor, GanjoorService.sln -> DiwanService.sln), files, folders, classes, namespaces, settings, API routes (/api/diwan) and DB tables (Diwan*). External addresses (ganjoor.net, github.com/ganjoor) unchanged. Migrations renamed consistently (no pending model changes); requires a fresh database. Upstream RUNNING_LOCALLY/SEMANTIC_SEARCH docs archived unmodified under docs/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
504 lines
20 KiB
Plaintext
504 lines
20 KiB
Plaintext
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery
|
|
<!DOCTYPE html>
|
|
<html lang="ur-PK" dir="rtl">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="robots" content="noindex, nofollow" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<meta name="csrf-token" content="@Antiforgery.GetAndStoreTokens(Context).RequestToken" />
|
|
<title>
|
|
@Html.Raw(ViewData["Title"])
|
|
</title>
|
|
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
|
<link rel="stylesheet" href="~/css/site.css" />
|
|
<link href="/dist/skin/blue.monday/css/jplayer.blue.monday.min.css" rel="stylesheet" type="text/css" />
|
|
<link href="~/css/p8.css" asp-append-version="true" rel="stylesheet" type="text/css" />
|
|
<partial name="_DiwanHead" />
|
|
<link href="~/css/user-panel.css" asp-append-version="true" rel="stylesheet" type="text/css" />
|
|
<link rel="stylesheet" href="/css/leaflet.css?version=2">
|
|
<link rel="shortcut icon" href="/favicon.ico?version=1" type="image/x-icon" />
|
|
<link rel="search" type="application/opensearchdescription+xml" title="دیوان" href="/search.xml" />
|
|
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
|
<script src="~/js/leaflet.js"></script>
|
|
<script src="~/js/leaflet-providers.js"></script>
|
|
<script src="/lib/diff.js"></script>
|
|
<script src="/lib/tinymce/tinymce.min.js"></script>
|
|
<script>
|
|
tinymce.init({
|
|
selector: 'textarea.editor',
|
|
language: 'fa_IR',
|
|
menubar: false,
|
|
plugins: 'link paste image',
|
|
contextmenu: false,
|
|
toolbar: 'undo redo | alignleft aligncenter alignright alignjustify | link',
|
|
paste_auto_cleanup_on_paste: true,
|
|
paste_remove_styles: true,
|
|
paste_remove_styles_if_webkit: true,
|
|
paste_strip_class_attributes: true,
|
|
smart_paste: false,
|
|
a11y_advanced_options: true,
|
|
skin: (window.matchMedia("(prefers-color-scheme: dark)").matches && localStorage.getItem("scheme") != "light" ? "oxide-dark" : localStorage.getItem("scheme") == "dark" ? "oxide-dark" : "oxide"),
|
|
content_css: (window.matchMedia("(prefers-color-scheme: dark)").matches && localStorage.getItem("scheme") != "light" ? "dark" : localStorage.getItem("scheme") == "dark" ? "dark" : false),
|
|
});
|
|
</script>
|
|
|
|
<script src="~/js/bk.js" asp-append-version="true"></script>
|
|
@* person profile / people-explorer / family-tree inline modals - see _Layout.cshtml for the
|
|
same includes on the public site; needed here too since ReviewPersonEdits/
|
|
ReviewPersonRelationEdits link to a person's profile *@
|
|
<script src="~/js/peoplegraph.js" asp-append-version="true"></script>
|
|
<script src="~/js/familytree.js" asp-append-version="true"></script>
|
|
<script src="~/js/personwindow.js" asp-append-version="true"></script>
|
|
<script>
|
|
function editBanner(bannerId) {
|
|
var url = '?handler=Edit';
|
|
var altInput = '#alt-' + bannerId.toString();
|
|
var urlInput = '#url-' + bannerId.toString();
|
|
var activeInput = '#active-' + bannerId.toString();
|
|
|
|
$.ajax({
|
|
type: "PUT",
|
|
url: url,
|
|
data: {
|
|
id: bannerId,
|
|
alt: $(altInput).val(),
|
|
url: $(urlInput).val(),
|
|
active: $(activeInput).is(":checked")
|
|
},
|
|
error: function (e) {
|
|
upToast(e.responseText || 'خطا رخ داد.', 'error');
|
|
},
|
|
success: function() {
|
|
upToast('آگاهی به درستی ویرایش شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
async function deleteBanner(bannerId) {
|
|
|
|
var ok = await upConfirm('آیا از حذف این آگاهی اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
$.ajax({
|
|
type: "DELETE",
|
|
url: '',
|
|
data: {
|
|
id: bannerId
|
|
},
|
|
error: function (e) {
|
|
upToast(e.responseText || 'خطا رخ داد.', 'error');
|
|
},
|
|
success: function() {
|
|
var bannerIdBlockId = '#tr-' + bannerId;
|
|
$(bannerIdBlockId).remove();
|
|
},
|
|
});
|
|
}
|
|
|
|
async function rebuildDonationsPage() {
|
|
var ok = await upConfirm('آیا از بازسازی صفحهٔ کمکها اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
var url = '?handler=RebuildPage';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
url: url,
|
|
error: function() {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function() {
|
|
upToast('صفحه به درستی بازسازی شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
|
|
async function deleteItem(itemId, itemName) {
|
|
|
|
var ok = await upConfirm('آیا از حذف این ' + itemName + ' اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
$.ajax({
|
|
type: "DELETE",
|
|
url: '',
|
|
data: {
|
|
id: itemId
|
|
},
|
|
error: function() {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function() {
|
|
location.reload();
|
|
return false;
|
|
},
|
|
});
|
|
}
|
|
|
|
|
|
|
|
async function rebuildSitemap() {
|
|
var ok = await upConfirm('آیا از بازسازی نقشهٔ سایت اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
var url = '?handler=RebuildSitemap';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
url: url,
|
|
error: function() {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function() {
|
|
upToast('فرایند بازسازی نقشهٔ سایت شروع شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
|
|
|
|
async function rebuildStats() {
|
|
var ok = await upConfirm('آیا از بازسازی صفحات آمار اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
var url = '?handler=RebuildStats';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
url: url,
|
|
error: function() {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function() {
|
|
upToast('فرایند بازسازی صفحات آمار شروع شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
async function regeneratePublicData() {
|
|
var ok = await upConfirm('آیا از بازسازی و ارسال دادهٔ عمومی دیوان (مخزن گیت) اطمینان دارید؟ ممکن است مدتی طول بکشد.');
|
|
if (!ok) return;
|
|
|
|
var url = '?handler=RegeneratePublicData';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
url: url,
|
|
error: function () {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function () {
|
|
upToast('فرایند بازسازی دادهٔ عمومی در پسزمینه شروع شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
|
|
|
|
async function refillCoupletCounts() {
|
|
var ok = await upConfirm('آیا از بازشماری تعداد ابیات قطعات اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
var url = '?handler=RefillSectionsCoupletCounts';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
url: url,
|
|
error: function() {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function() {
|
|
upToast('فرایند بازشماری تعداد ابیات قطعات شروع شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
async function rebuildRelatedPages() {
|
|
var ok = await upConfirm('آیا از بازسازی محتوای صفحات استقبالها اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
var url = '?handler=RebuildRelatedPages';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
url: url,
|
|
error: function () {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function () {
|
|
upToast('فرایند بازسازی صفحات آمار شروع شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
async function rebuildDigitalSourcesStats() {
|
|
var ok = await upConfirm('آیا از بازسازی آمار منابع دیجیتال اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
var url = '?handler=RebuildDigitalSourcesStats';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
url: url,
|
|
error: function () {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function () {
|
|
upToast('فرایند بازسازی شروع شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
|
|
|
|
async function rebuildRebuildWordCounts(poetId) {
|
|
var onlyCurrentPoet = await upConfirm('فقط شاعر جاری؟', { danger: false });
|
|
if (!onlyCurrentPoet) {
|
|
poetId = 0;
|
|
}
|
|
var ok = await upConfirm('آیا از شمارش واژگان برای شاعر جاری با کد ' + poetId.toString() + ' اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
var url = '?handler=RebuildWordCounts';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
data: {
|
|
poetId: poetId
|
|
},
|
|
url: url,
|
|
error: function () {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function () {
|
|
upToast('فرایند بازسازی شروع شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
function discoverNaskbanPaperSources(poetId) {
|
|
var username = window.prompt('username:');
|
|
var password = window.prompt('password:');
|
|
|
|
var url = '?handler=ImportNaskbanPaperSources';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
data: {
|
|
poetId: poetId,
|
|
username: username,
|
|
password: password,
|
|
},
|
|
url: url,
|
|
error: function () {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function () {
|
|
upToast('فرایند بازسازی شروع شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
function discoverMusuemPaperSources(poetId) {
|
|
|
|
var url = '?handler=ImportMuseumPaperSources';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
data: {
|
|
poetId: poetId,
|
|
},
|
|
url: url,
|
|
error: function () {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function () {
|
|
upToast('فرایند بازسازی شروع شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function exportAll() {
|
|
var ok = await upConfirm('آیا از تولید مجدد همهٔ فایلهای دیوان رومیزی اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
var url = '?handler=ExportAll';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
url: url,
|
|
error: function() {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function() {
|
|
upToast('فرایند تولید مجدد همهٔ فایلهای دیوان رومیزی شروع شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
async function cleanCache(id) {
|
|
var ok = await upConfirm('آیا از حذف کش این صفحه اطمینان دارید؟');
|
|
if (!ok) return;
|
|
|
|
var url = '?handler=CleanCache';
|
|
|
|
$.ajax({
|
|
type: "POST",
|
|
url: url,
|
|
data: {
|
|
id: id
|
|
},
|
|
error: function() {
|
|
upToast('خطا رخ داد.', 'error');
|
|
},
|
|
success: function() {
|
|
upToast('کش صفحه پاک شد.', 'success');
|
|
},
|
|
|
|
});
|
|
}
|
|
|
|
|
|
|
|
function compareCorrectionBlocks(originalElemenyId, editedElementId, resultElementId) {
|
|
var a = document.getElementById(originalElemenyId);
|
|
var b = document.getElementById(editedElementId);
|
|
var result = document.getElementById(resultElementId);
|
|
var diff = JsDiff.diffChars(a.textContent, b.textContent);
|
|
var fragment = document.createDocumentFragment();
|
|
for (var i = 0; i < diff.length; i++) {
|
|
|
|
if (diff[i].added && diff[i + 1] && diff[i + 1].removed) {
|
|
var swap = diff[i];
|
|
diff[i] = diff[i + 1];
|
|
diff[i + 1] = swap;
|
|
}
|
|
|
|
var node;
|
|
if (diff[i].removed) {
|
|
node = document.createElement('del');
|
|
node.style.color = 'red';
|
|
node.appendChild(document.createTextNode(diff[i].value));
|
|
} else if (diff[i].added) {
|
|
node = document.createElement('span');
|
|
node.style.color = 'white';
|
|
node.style.backgroundColor = 'green';
|
|
node.appendChild(document.createTextNode(diff[i].value));
|
|
} else {
|
|
node = document.createTextNode(diff[i].value);
|
|
}
|
|
fragment.appendChild(node);
|
|
}
|
|
|
|
result.textContent = '';
|
|
result.appendChild(fragment);
|
|
}
|
|
|
|
|
|
</script>
|
|
</head>
|
|
|
|
<body class="admin-body">
|
|
<header>
|
|
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
|
<div class="container">
|
|
<a class="navbar-brand" asp-area="" asp-page="/Index" title="دیوان"><img src="/image/gdapk.png" alt="دیوان" style="width:auto;margin:auto;" /></a>
|
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
|
aria-expanded="false" aria-label="Toggle navigation">
|
|
<span class="navbar-toggler-icon"></span>
|
|
</button>
|
|
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
|
<ul class="navbar-nav flex-grow-1">
|
|
<li class="nav-item dropdown">
|
|
<a class="nav-link text-dark dropdown-toggle" href="#" id="adminReviewDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">بررسی و تأیید</a>
|
|
<div class="dropdown-menu" aria-labelledby="adminReviewDropdown">
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="ReviewEdits">ویرایشها</a>
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="ReviewPartEdits">ویرایشهای قطعات</a>
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="ReviewCatEdits">ویرایشهای زندگینامهها</a>
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="ReviewQuoteds">مشقها</a>
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="ReviewPersonEdits">ویرایشهای شخصیتها</a>
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="ReviewPersonRelationEdits">ویرایشهای نسبتهای خویشاوندی</a>
|
|
</div>
|
|
</li>
|
|
|
|
<li class="nav-item">
|
|
<a class="nav-link text-dark" asp-area="Admin" asp-page="Poets">سخنوران</a>
|
|
</li>
|
|
|
|
<li class="nav-item dropdown">
|
|
<a class="nav-link text-dark dropdown-toggle" href="#" id="adminSiteDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">مالی و سایت</a>
|
|
<div class="dropdown-menu" aria-labelledby="adminSiteDropdown">
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="Banners">آگاهیها</a>
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="Donations">کمکهای مالی</a>
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="Expenses">هزینهها</a>
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="LongRunningJobs">کارها</a>
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="PublicDataImport">درونریزی دادهٔ عمومی</a>
|
|
</div>
|
|
</li>
|
|
|
|
<li class="nav-item dropdown">
|
|
<a class="nav-link text-dark dropdown-toggle" href="#" id="adminUsersDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">کاربران و نقشها</a>
|
|
<div class="dropdown-menu" aria-labelledby="adminUsersDropdown">
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="Users">کاربران</a>
|
|
<a class="dropdown-item" asp-area="Admin" asp-page="Roles">نقشها</a>
|
|
</div>
|
|
</li>
|
|
|
|
<li class="nav-item">
|
|
<a class="nav-link text-dark" href="javascript:toggleColorScheme();" title="تغییر پوسته">
|
|
<span id="icon-sun"><svg height="24px" viewBox="0 -960 960 960" width="24px"><path d="M440-760v-160h80v160h-80Zm266 110-55-55 112-115 56 57-113 113Zm54 210v-80h160v80H760ZM440-40v-160h80v160h-80ZM254-652 140-763l57-56 113 113-56 54Zm508 512L651-255l54-54 114 110-57 59ZM40-440v-80h160v80H40Zm157 300-56-57 112-112 29 27 29 28-114 114Zm283-100q-100 0-170-70t-70-170q0-100 70-170t170-70q100 0 170 70t70 170q0 100-70 170t-170 70Z" /></svg></span>
|
|
<span id="icon-moon"><svg height="24px" viewBox="0 -960 960 960" width="24px"><path d="M480-120q-150 0-255-105T120-480q0-150 105-255t255-105q14 0 27.5 1t26.5 3q-41 29-65.5 75.5T444-660q0 90 63 153t153 63q55 0 101-24.5t75-65.5q2 13 3 26.5t1 27.5q0 150-105 255T480-120Z" /></svg></span>
|
|
</a>
|
|
</li>
|
|
|
|
<li class="nav-item">
|
|
<form method="post" action="/login?handler=Logout">
|
|
@Html.AntiForgeryToken()
|
|
<button type="submit" class="nav-link text-dark" title="خروج از حساب کاربری">خروج</button>
|
|
</form>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
<div class="container up-shell @(ViewData["WideShell"] is true ? "up-shell--wide" : "")">
|
|
<main role="main" class="pb-3">
|
|
@RenderBody()
|
|
</main>
|
|
</div>
|
|
|
|
<partial name="_Toasts" />
|
|
<partial name="_ConfirmModal" />
|
|
|
|
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
|
<script src="/js/user-panel.js?version=3"></script>
|
|
|
|
@await RenderSectionAsync("Scripts", required: false)
|
|
|
|
<script>
|
|
applyPreferredColorScheme(getPreferredColorScheme());
|
|
</script>
|
|
</body>
|
|
</html>
|