Prerel 1.2.0
This commit is contained in:
@@ -2,17 +2,18 @@
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using WeddingShare.Enums;
|
||||
using WeddingShare.Helpers;
|
||||
using WeddingShare.Helpers.Database;
|
||||
using WeddingShare.Models.Database;
|
||||
|
||||
namespace WeddingShare.Helpers
|
||||
namespace WeddingShare.BackgroundWorkers
|
||||
{
|
||||
public sealed class DirectoryScanner(IWebHostEnvironment hostingEnvironment, IConfigHelper configHelper, IDatabaseHelper databaseHelper, IImageHelper imageHelper, ILogger<DirectoryScanner> logger) : BackgroundService
|
||||
{
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
var cron = configHelper.GetOrDefault("BackgroundServices", "Directory_Scanner_Interval", "*/30 * * * *");
|
||||
var schedule = CrontabSchedule.Parse(cron);
|
||||
var schedule = CrontabSchedule.Parse(cron, new CrontabSchedule.ParseOptions() { IncludingSeconds = cron.Split(new[] { ' ' }, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).Length == 6 });
|
||||
|
||||
await Task.Delay((int)TimeSpan.FromSeconds(10).TotalMilliseconds, stoppingToken);
|
||||
await ScanForFiles();
|
||||
@@ -27,7 +28,7 @@ namespace WeddingShare.Helpers
|
||||
await ScanForFiles();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task ScanForFiles()
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
@@ -66,7 +67,7 @@ namespace WeddingShare.Helpers
|
||||
var galleryItems = await databaseHelper.GetAllGalleryItems(galleryItem.Id);
|
||||
|
||||
if (Path.Exists(gallery))
|
||||
{
|
||||
{
|
||||
var approvedFiles = Directory.GetFiles(gallery, "*.*", SearchOption.TopDirectoryOnly).Where(x => allowedFileTypes.Any(y => string.Equals(Path.GetExtension(x).Trim('.'), y.Trim('.'), StringComparison.OrdinalIgnoreCase)));
|
||||
if (approvedFiles != null)
|
||||
{
|
||||
@@ -113,7 +114,7 @@ namespace WeddingShare.Helpers
|
||||
}
|
||||
|
||||
if (Path.Exists(Path.Combine(gallery, "Pending")))
|
||||
{
|
||||
{
|
||||
var pendingFiles = Directory.GetFiles(Path.Combine(gallery, "Pending"), "*.*", SearchOption.TopDirectoryOnly).Where(x => allowedFileTypes.Any(y => string.Equals(Path.GetExtension(x).Trim('.'), y.Trim('.'), StringComparison.OrdinalIgnoreCase)));
|
||||
if (pendingFiles != null)
|
||||
{
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Composition;
|
||||
using System.IO.Compression;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
@@ -257,6 +258,81 @@ namespace WeddingShare.Controllers
|
||||
return Json(new { success = false });
|
||||
}
|
||||
|
||||
[HttpDelete]
|
||||
public async Task<IActionResult> WipeGallery(int id)
|
||||
{
|
||||
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
||||
{
|
||||
try
|
||||
{
|
||||
var gallery = await _database.GetGallery(id);
|
||||
if (gallery != null)
|
||||
{
|
||||
var galleryDir = Path.Combine(UploadsDirectory, gallery.Name);
|
||||
if (Directory.Exists(galleryDir))
|
||||
{
|
||||
foreach (var photo in Directory.GetFiles(galleryDir, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
var thumbnail = Path.Combine(ThumbnailsDirectory, $"{Path.GetFileNameWithoutExtension(photo)}.webp");
|
||||
if (System.IO.File.Exists(thumbnail))
|
||||
{
|
||||
System.IO.File.Delete(thumbnail);
|
||||
}
|
||||
}
|
||||
|
||||
Directory.Delete(galleryDir, true);
|
||||
Directory.CreateDirectory(galleryDir);
|
||||
}
|
||||
|
||||
return Json(new { success = await _database.WipeGallery(gallery) });
|
||||
}
|
||||
else
|
||||
{
|
||||
return Json(new { success = false, message = _localizer["Failed_Wipe_Gallery"].Value });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"{_localizer["Failed_Wipe_Gallery"].Value} - {ex?.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return Json(new { success = false });
|
||||
}
|
||||
|
||||
[HttpDelete]
|
||||
public async Task<IActionResult> WipeAllGalleries()
|
||||
{
|
||||
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(UploadsDirectory))
|
||||
{
|
||||
foreach (var gallery in Directory.GetDirectories(UploadsDirectory, "*", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
Directory.Delete(gallery, true);
|
||||
}
|
||||
|
||||
foreach (var thumbnail in Directory.GetFiles(ThumbnailsDirectory, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
System.IO.File.Delete(thumbnail);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.Combine(UploadsDirectory, "default"));
|
||||
}
|
||||
|
||||
return Json(new { success = await _database.WipeAllGalleries() });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"{_localizer["Failed_Wipe_Galleries"].Value} - {ex?.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return Json(new { success = false });
|
||||
}
|
||||
|
||||
[HttpDelete]
|
||||
public async Task<IActionResult> DeleteGallery(int id)
|
||||
{
|
||||
@@ -289,6 +365,42 @@ namespace WeddingShare.Controllers
|
||||
return Json(new { success = false });
|
||||
}
|
||||
|
||||
[HttpDelete]
|
||||
public async Task<IActionResult> DeletePhoto(int id)
|
||||
{
|
||||
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
||||
{
|
||||
try
|
||||
{
|
||||
var photo = await _database.GetGalleryItem(id);
|
||||
if (photo != null)
|
||||
{
|
||||
var gallery = await _database.GetGallery(photo.GalleryId);
|
||||
if (gallery != null)
|
||||
{
|
||||
var photoPath = Path.Combine(UploadsDirectory, gallery.Name, photo.Title);
|
||||
if (System.IO.File.Exists(photoPath))
|
||||
{
|
||||
System.IO.File.Delete(photoPath);
|
||||
}
|
||||
|
||||
return Json(new { success = await _database.DeleteGalleryItem(photo) });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Json(new { success = false, message = _localizer["Failed_Delete_Gallery"].Value });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"{_localizer["Failed_Delete_Gallery"].Value} - {ex?.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return Json(new { success = false });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> DownloadGallery(int id)
|
||||
{
|
||||
@@ -330,5 +442,139 @@ namespace WeddingShare.Controllers
|
||||
|
||||
return Json(new { success = false });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> ExportBackup()
|
||||
{
|
||||
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
||||
{
|
||||
var tempDir = $"Temp";
|
||||
var exportDir = Path.Combine(tempDir, "Export");
|
||||
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(UploadsDirectory))
|
||||
{
|
||||
if (!Directory.Exists(tempDir))
|
||||
{
|
||||
Directory.CreateDirectory(tempDir);
|
||||
}
|
||||
|
||||
if (Directory.Exists(exportDir))
|
||||
{
|
||||
Directory.Delete(exportDir, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(exportDir);
|
||||
|
||||
var dbExport = Path.Combine(exportDir, $"WeddingShare.bak");
|
||||
var exported = await _database.Export($"Data Source={dbExport}");
|
||||
if (exported)
|
||||
{
|
||||
var uploadsZip = Path.Combine(exportDir, $"Uploads.bak");
|
||||
ZipFile.CreateFromDirectory(UploadsDirectory, uploadsZip, CompressionLevel.Optimal, false);
|
||||
|
||||
var thumbnailsZip = Path.Combine(exportDir, $"Thumbnails.bak");
|
||||
ZipFile.CreateFromDirectory(ThumbnailsDirectory, thumbnailsZip, CompressionLevel.Optimal, false);
|
||||
|
||||
var exportZipFile = Path.Combine(tempDir, $"WeddingShare-{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.zip");
|
||||
if (System.IO.File.Exists(exportZipFile))
|
||||
{
|
||||
System.IO.File.Delete(exportZipFile);
|
||||
}
|
||||
|
||||
ZipFile.CreateFromDirectory(exportDir, exportZipFile, CompressionLevel.Optimal, false);
|
||||
System.IO.File.Delete(dbExport);
|
||||
System.IO.File.Delete(uploadsZip);
|
||||
System.IO.File.Delete(thumbnailsZip);
|
||||
|
||||
byte[] bytes = System.IO.File.ReadAllBytes(exportZipFile);
|
||||
System.IO.File.Delete(exportZipFile);
|
||||
|
||||
return Json(new { success = true, filename = Path.GetFileName(exportZipFile), content = Convert.ToBase64String(bytes, 0, bytes.Length) });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"{_localizer["Failed_Export"].Value} - {ex?.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(exportDir, true);
|
||||
}
|
||||
}
|
||||
|
||||
return Json(new { success = false });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> ImportBackup()
|
||||
{
|
||||
if (User?.Identity != null && User.Identity.IsAuthenticated)
|
||||
{
|
||||
var tempDir = $"Temp";
|
||||
var importDir = Path.Combine(tempDir, "Import");
|
||||
|
||||
try
|
||||
{
|
||||
var files = Request?.Form?.Files;
|
||||
if (files != null && files.Count > 0)
|
||||
{
|
||||
foreach (IFormFile file in files)
|
||||
{
|
||||
var extension = Path.GetExtension(file.FileName)?.Trim('.');
|
||||
if (string.Equals("zip", extension, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!Directory.Exists(tempDir))
|
||||
{
|
||||
Directory.CreateDirectory(tempDir);
|
||||
}
|
||||
|
||||
var filePath = Path.Combine(tempDir, "Import.zip");
|
||||
if (!string.IsNullOrWhiteSpace(filePath))
|
||||
{
|
||||
using (var fs = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(fs);
|
||||
}
|
||||
|
||||
if (Directory.Exists(importDir))
|
||||
{
|
||||
Directory.Delete(importDir, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(importDir);
|
||||
|
||||
ZipFile.ExtractToDirectory(filePath, importDir, true);
|
||||
System.IO.File.Delete(filePath);
|
||||
|
||||
var uploadsZip = Path.Combine(importDir, "Uploads.bak");
|
||||
ZipFile.ExtractToDirectory(uploadsZip, UploadsDirectory, true);
|
||||
|
||||
var thumbnailsZip = Path.Combine(importDir, "Thumbnails.bak");
|
||||
ZipFile.ExtractToDirectory(thumbnailsZip, ThumbnailsDirectory, true);
|
||||
|
||||
var dbImport = Path.Combine(importDir, "WeddingShare.bak");
|
||||
var imported = await _database.Import($"Data Source={dbImport}");
|
||||
|
||||
return Json(new { success = imported });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"{_localizer["Import_Failed"].Value} - {ex?.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(importDir, true);
|
||||
}
|
||||
}
|
||||
|
||||
return Json(new { success = false });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using WeddingShare.Attributes;
|
||||
using WeddingShare.Enums;
|
||||
@@ -20,19 +19,21 @@ namespace WeddingShare.Controllers
|
||||
private readonly IDatabaseHelper _database;
|
||||
private readonly ISecretKeyHelper _secretKey;
|
||||
private readonly IDeviceDetector _deviceDetector;
|
||||
private readonly IImageHelper _imageHelper;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IStringLocalizer<GalleryController> _localizer;
|
||||
|
||||
private readonly string UploadsDirectory;
|
||||
private readonly string ThumbnailsDirectory;
|
||||
|
||||
public GalleryController(IWebHostEnvironment hostingEnvironment, IConfigHelper config, IDatabaseHelper database, ISecretKeyHelper secretKey, IDeviceDetector deviceDetector, ILogger<GalleryController> logger, IStringLocalizer<GalleryController> localizer)
|
||||
public GalleryController(IWebHostEnvironment hostingEnvironment, IConfigHelper config, IDatabaseHelper database, ISecretKeyHelper secretKey, IDeviceDetector deviceDetector, IImageHelper imageHelper, ILogger<GalleryController> logger, IStringLocalizer<GalleryController> localizer)
|
||||
{
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_config = config;
|
||||
_database = database;
|
||||
_secretKey = secretKey;
|
||||
_deviceDetector = deviceDetector;
|
||||
_imageHelper = imageHelper;
|
||||
_logger = logger;
|
||||
_localizer = localizer;
|
||||
|
||||
@@ -43,7 +44,7 @@ namespace WeddingShare.Controllers
|
||||
[HttpGet]
|
||||
[RequiresSecretKey]
|
||||
[AllowGuestCreate]
|
||||
public async Task<IActionResult> Index(string id = "default", string? key = null)
|
||||
public async Task<IActionResult> Index(string id = "default", string? key = null, GalleryOrder order = GalleryOrder.None)
|
||||
{
|
||||
id = id.ToLower();
|
||||
if (string.IsNullOrWhiteSpace(id) || _config.GetOrDefault("Settings", "Single_Gallery_Mode", false))
|
||||
@@ -80,18 +81,38 @@ namespace WeddingShare.Controllers
|
||||
|
||||
if (gallery != null)
|
||||
{
|
||||
var allowedFileTypes = _config.GetOrDefault("Settings", "Allowed_File_Types", ".jpg,.jpeg,.png").Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||
var images = new PhotoGallery(_config.GetOrDefault("Settings", "Gallery_Columns", 4))
|
||||
var allowedFileTypes = _config.GetOrDefault("Settings", "Allowed_File_Types", ".jpg,.jpeg,.png").Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var images = (await _database.GetAllGalleryItems(gallery.Id, GalleryItemState.Approved))?.Where(x => allowedFileTypes.Any(y => string.Equals(Path.GetExtension(x.Title).Trim('.'), y.Trim('.'), StringComparison.OrdinalIgnoreCase)));
|
||||
switch (order)
|
||||
{
|
||||
case GalleryOrder.UploadedAsc:
|
||||
images = images?.OrderBy(x => x.Id);
|
||||
break;
|
||||
case GalleryOrder.UploadedDesc:
|
||||
images = images?.OrderByDescending(x => x.Id);
|
||||
break;
|
||||
case GalleryOrder.NameAsc:
|
||||
images = images?.OrderByDescending(x => x.Title);
|
||||
break;
|
||||
case GalleryOrder.NameDesc:
|
||||
images = images?.OrderBy(x => x.Title);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
var model = new PhotoGallery(_config.GetOrDefault("Settings", "Gallery_Columns", 4))
|
||||
{
|
||||
GalleryId = id,
|
||||
GalleryPath = $"/{galleryPath.Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}",
|
||||
ThumbnailsPath = $"/{ThumbnailsDirectory.Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}",
|
||||
Images = (await _database.GetAllGalleryItems(gallery.Id, GalleryItemState.Approved))?.Where(x => allowedFileTypes.Any(y => string.Equals(Path.GetExtension(x.Title).Trim('.'), y.Trim('.'), StringComparison.OrdinalIgnoreCase)))?.Select(x => Path.GetFileName(x.Title))?.ToList(),
|
||||
Images = images?.Select(x => new PhotoGalleryImage() { Id = x.Id, Name = Path.GetFileName(x.Title), Path = x.Title })?.ToList(),
|
||||
PendingCount = gallery?.PendingItems ?? 0,
|
||||
FileUploader = !_config.GetOrDefault("Settings", "Disable_Upload", false) || (User?.Identity != null && User.Identity.IsAuthenticated) ? new FileUploader(id, "/Gallery/UploadImage") : null
|
||||
};
|
||||
|
||||
return View(images);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
return View(new PhotoGallery());
|
||||
@@ -146,6 +167,11 @@ namespace WeddingShare.Controllers
|
||||
{
|
||||
var fileName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";
|
||||
var galleryPath = requiresReview ? Path.Combine(UploadsDirectory, gallery.Name, "Pending") : Path.Combine(UploadsDirectory, gallery.Name);
|
||||
if (!Directory.Exists(galleryPath))
|
||||
{
|
||||
Directory.CreateDirectory(galleryPath);
|
||||
}
|
||||
|
||||
var filePath = Path.Combine(galleryPath, fileName);
|
||||
if (!string.IsNullOrWhiteSpace(filePath))
|
||||
{
|
||||
@@ -154,6 +180,16 @@ namespace WeddingShare.Controllers
|
||||
await file.CopyToAsync(fs);
|
||||
}
|
||||
|
||||
if (!requiresReview)
|
||||
{
|
||||
if (!Directory.Exists(ThumbnailsDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(ThumbnailsDirectory);
|
||||
}
|
||||
|
||||
await _imageHelper.GenerateThumbnail(filePath, Path.Combine(ThumbnailsDirectory, $"{Path.GetFileNameWithoutExtension(filePath)}.webp"), _config.GetOrDefault("Settings", "Thumbnail_Size", 720));
|
||||
}
|
||||
|
||||
var item = await _database.AddGalleryItem(new GalleryItemModel()
|
||||
{
|
||||
GalleryId = gallery.Id,
|
||||
|
||||
11
WeddingShare/Enums/GalleryOrder.cs
Normal file
11
WeddingShare/Enums/GalleryOrder.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace WeddingShare.Enums
|
||||
{
|
||||
public enum GalleryOrder
|
||||
{
|
||||
None,
|
||||
UploadedAsc,
|
||||
UploadedDesc,
|
||||
NameAsc,
|
||||
NameDesc
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,9 @@ namespace WeddingShare.Helpers.Database
|
||||
Task<GalleryModel?> GetGallery(string name);
|
||||
Task<GalleryModel?> AddGallery(GalleryModel model);
|
||||
Task<GalleryModel?> EditGallery(GalleryModel model);
|
||||
Task<bool> WipeGallery(GalleryModel model);
|
||||
Task<bool> WipeAllGalleries();
|
||||
Task<bool> DeleteGallery(GalleryModel model);
|
||||
|
||||
Task<List<GalleryItemModel>> GetAllGalleryItems(int galleryId, GalleryItemState state = GalleryItemState.All);
|
||||
Task<int> GetPendingGalleryItemCount(int? galleryId = null);
|
||||
Task<List<PendingGalleryItemModel>> GetPendingGalleryItems(int? galleryId = null);
|
||||
@@ -20,5 +21,7 @@ namespace WeddingShare.Helpers.Database
|
||||
Task<GalleryItemModel?> AddGalleryItem(GalleryItemModel model);
|
||||
Task<GalleryItemModel?> EditGalleryItem(GalleryItemModel model);
|
||||
Task<bool> DeleteGalleryItem(GalleryItemModel model);
|
||||
Task<bool> Import(string path);
|
||||
Task<bool> Export(string path);
|
||||
}
|
||||
}
|
||||
@@ -141,6 +141,63 @@ namespace WeddingShare.Helpers.Database
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<bool> WipeGallery(GalleryModel model)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
using (var conn = new SqliteConnection(_connString))
|
||||
{
|
||||
var cmd = new SqliteCommand($"DELETE FROM `gallery_items` WHERE `gallery_id`=@Id;", conn);
|
||||
cmd.CommandType = CommandType.Text;
|
||||
cmd.Parameters.AddWithValue("Id", model.Id);
|
||||
|
||||
await conn.OpenAsync();
|
||||
var tran = await conn.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
cmd.Transaction = (SqliteTransaction)tran;
|
||||
result = (await cmd.ExecuteNonQueryAsync()) > 0;
|
||||
await tran.CommitAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await tran.RollbackAsync();
|
||||
}
|
||||
|
||||
await conn.CloseAsync();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<bool> WipeAllGalleries()
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
using (var conn = new SqliteConnection(_connString))
|
||||
{
|
||||
var cmd = new SqliteCommand($"DELETE FROM `gallery_items`; DELETE FROM `galleries` WHERE `id` > 1;", conn);
|
||||
cmd.CommandType = CommandType.Text;
|
||||
|
||||
await conn.OpenAsync();
|
||||
var tran = await conn.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
cmd.Transaction = (SqliteTransaction)tran;
|
||||
result = (await cmd.ExecuteNonQueryAsync()) > 0;
|
||||
await tran.CommitAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await tran.RollbackAsync();
|
||||
}
|
||||
|
||||
await conn.CloseAsync();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteGallery(GalleryModel model)
|
||||
{
|
||||
bool result = false;
|
||||
@@ -358,6 +415,62 @@ namespace WeddingShare.Helpers.Database
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Backups
|
||||
public async Task<bool> Import(string path)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
try
|
||||
{
|
||||
using (var backup = new SqliteConnection(path))
|
||||
using (var conn = new SqliteConnection(_connString))
|
||||
{
|
||||
await backup.OpenAsync();
|
||||
await conn.OpenAsync();
|
||||
|
||||
backup.BackupDatabase(conn);
|
||||
|
||||
await conn.CloseAsync();
|
||||
await backup.CloseAsync();
|
||||
|
||||
SqliteConnection.ClearPool(backup);
|
||||
}
|
||||
|
||||
result = true;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<bool> Export(string path)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
try
|
||||
{
|
||||
using (var conn = new SqliteConnection(_connString))
|
||||
using (var backup = new SqliteConnection(path))
|
||||
{
|
||||
await conn.OpenAsync();
|
||||
await backup.OpenAsync();
|
||||
|
||||
conn.BackupDatabase(backup);
|
||||
|
||||
await backup.CloseAsync();
|
||||
await conn.CloseAsync();
|
||||
|
||||
SqliteConnection.ClearPool(backup);
|
||||
}
|
||||
|
||||
result = true;
|
||||
}
|
||||
catch { }
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Data Parsers
|
||||
private async Task<List<GalleryModel>> ReadGalleries(SqliteDataReader? reader)
|
||||
{
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
}
|
||||
|
||||
public PhotoGallery(int columnCount)
|
||||
: this("default", columnCount, string.Empty, string.Empty, new List<string>())
|
||||
: this("default", columnCount, string.Empty, string.Empty, new List<PhotoGalleryImage>())
|
||||
{
|
||||
}
|
||||
|
||||
public PhotoGallery(string id, int columnCount, string galleryPath, string thumbnailPath, List<string> images)
|
||||
public PhotoGallery(string id, int columnCount, string galleryPath, string thumbnailPath, List<PhotoGalleryImage> images)
|
||||
{
|
||||
this.GalleryId = id;
|
||||
this.GalleryPath = galleryPath;
|
||||
@@ -42,7 +42,18 @@
|
||||
return this.ApprovedCount + this.PendingCount;
|
||||
}
|
||||
}
|
||||
public List<string>? Images { get; set; }
|
||||
public List<PhotoGalleryImage>? Images { get; set; }
|
||||
public FileUploader? FileUploader { get; set; }
|
||||
}
|
||||
|
||||
public class PhotoGalleryImage
|
||||
{
|
||||
public PhotoGalleryImage()
|
||||
{
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
public string? Path { get; set; }
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -129,12 +129,24 @@
|
||||
<data name="Failed_Edit_Gallery" xml:space="preserve">
|
||||
<value>Failed to update gallery</value>
|
||||
</data>
|
||||
<data name="Failed_Export" xml:space="preserve">
|
||||
<value>Failed to export data</value>
|
||||
</data>
|
||||
<data name="Failed_Finding_File" xml:space="preserve">
|
||||
<value>Failed to find file</value>
|
||||
</data>
|
||||
<data name="Failed_Import" xml:space="preserve">
|
||||
<value>Failed to import data</value>
|
||||
</data>
|
||||
<data name="Failed_Reviewing_Photo" xml:space="preserve">
|
||||
<value>Failed to review photo</value>
|
||||
</data>
|
||||
<data name="Failed_Wipe_Galleries" xml:space="preserve">
|
||||
<value>Failed to wipe galleries</value>
|
||||
</data>
|
||||
<data name="Failed_Wipe_Gallery" xml:space="preserve">
|
||||
<value>Failed to wipe gallery</value>
|
||||
</data>
|
||||
<data name="Gallery_Name_Already_Exists" xml:space="preserve">
|
||||
<value>Gallery name already exists</value>
|
||||
</data>
|
||||
|
||||
@@ -129,12 +129,24 @@
|
||||
<data name="Failed_Edit_Gallery" xml:space="preserve">
|
||||
<value>Échec de la mise à jour de la galerie</value>
|
||||
</data>
|
||||
<data name="Failed_Export" xml:space="preserve">
|
||||
<value>Échec de l'exportation des données</value>
|
||||
</data>
|
||||
<data name="Failed_Finding_File" xml:space="preserve">
|
||||
<value>Impossible de trouver le fichier</value>
|
||||
</data>
|
||||
<data name="Failed_Import" xml:space="preserve">
|
||||
<value>Échec de l'importation des données</value>
|
||||
</data>
|
||||
<data name="Failed_Reviewing_Photo" xml:space="preserve">
|
||||
<value>Impossible de vérifier la photo</value>
|
||||
</data>
|
||||
<data name="Failed_Wipe_Galleries" xml:space="preserve">
|
||||
<value>Impossible d'effacer la galerie</value>
|
||||
</data>
|
||||
<data name="Failed_Wipe_Gallery" xml:space="preserve">
|
||||
<value>Impossible d'effacer la galerie</value>
|
||||
</data>
|
||||
<data name="Gallery_Name_Already_Exists" xml:space="preserve">
|
||||
<value>Le nom de la galerie existe déjà</value>
|
||||
</data>
|
||||
|
||||
@@ -117,6 +117,18 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Clean" xml:space="preserve">
|
||||
<value>Clean</value>
|
||||
</data>
|
||||
<data name="Clean_Confirmation" xml:space="preserve">
|
||||
<value>Are you sure you want to clean this gallery?</value>
|
||||
</data>
|
||||
<data name="Wipe" xml:space="preserve">
|
||||
<value>Wipe</value>
|
||||
</data>
|
||||
<data name="Wipe_Confirmation" xml:space="preserve">
|
||||
<value>Are you sure you want to wipe this gallery?</value>
|
||||
</data>
|
||||
<data name="Close" xml:space="preserve">
|
||||
<value>Close</value>
|
||||
</data>
|
||||
@@ -141,4 +153,7 @@
|
||||
<data name="Update" xml:space="preserve">
|
||||
<value>Update</value>
|
||||
</data>
|
||||
<data name="Wipe_All_Confirmation" xml:space="preserve">
|
||||
<value>Are you sure you want to wipe all galleries?</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -117,6 +117,18 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Clean" xml:space="preserve">
|
||||
<value>Faire le ménage</value>
|
||||
</data>
|
||||
<data name="Clean_Confirmation" xml:space="preserve">
|
||||
<value>Etes-vous sûr de vouloir nettoyer cette galerie ?</value>
|
||||
</data>
|
||||
<data name="Wipe" xml:space="preserve">
|
||||
<value>Essuyer</value>
|
||||
</data>
|
||||
<data name="Wipe_Confirmation" xml:space="preserve">
|
||||
<value>Etes-vous sûr de vouloir effacer cette galerie ?</value>
|
||||
</data>
|
||||
<data name="Close" xml:space="preserve">
|
||||
<value>Fermer</value>
|
||||
</data>
|
||||
@@ -141,4 +153,7 @@
|
||||
<data name="Update" xml:space="preserve">
|
||||
<value>Mise à jour</value>
|
||||
</data>
|
||||
<data name="Wipe_All_Confirmation" xml:space="preserve">
|
||||
<value>Êtes-vous sûr de vouloir effacer toutes les galeries ?</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using WeddingShare.BackgroundWorkers;
|
||||
using WeddingShare.Helpers;
|
||||
using WeddingShare.Helpers.Database;
|
||||
using WeddingShare.Helpers.Dbup;
|
||||
@@ -52,6 +54,16 @@ namespace WeddingShare
|
||||
options.Limits.MaxRequestBodySize = int.MaxValue;
|
||||
});
|
||||
|
||||
services.Configure<FormOptions>(x =>
|
||||
{
|
||||
x.MultipartHeadersLengthLimit = Int32.MaxValue;
|
||||
x.MultipartBoundaryLengthLimit = Int32.MaxValue;
|
||||
x.MultipartBodyLengthLimit = Int64.MaxValue;
|
||||
x.ValueLengthLimit = Int32.MaxValue;
|
||||
x.BufferBodyLengthLimit = Int64.MaxValue;
|
||||
x.MemoryBufferThreshold = Int32.MaxValue;
|
||||
});
|
||||
|
||||
services.Configure<RequestLocalizationOptions>(options => {
|
||||
var supportedCultures = new[]
|
||||
{
|
||||
|
||||
@@ -11,13 +11,34 @@
|
||||
var ctx = Context.Request;
|
||||
var link = $"{(_config.GetOrDefault("Settings", "Force_Https", false) ? "https" : ctx.Scheme)}://{ctx.Host}/Gallery";
|
||||
|
||||
<h1 class="display-6">
|
||||
<span class="float-none">@_localizer["Available_Galleries"].Value: </span>
|
||||
@if (!_config.GetOrDefault("Settings", "Single_Gallery_Mode", false))
|
||||
{
|
||||
<span class="float-end"><i class="btnAddGallery fa-solid fa-calendar-plus fa-2x text-success pointer px-2" alt="Create"></i></span>
|
||||
}
|
||||
</h1>
|
||||
<div class="row pb-2 pb-lg-4">
|
||||
<div class="col-12 text-center">
|
||||
@if (!_config.GetOrDefault("Settings", "Single_Gallery_Mode", false))
|
||||
{
|
||||
<div class="mx-0 mx-sm-5 px-2 px-xl-5 text-success d-inline-block">
|
||||
<i class="btnAddGallery fa-solid fa-calendar-plus fa-2x pointer" alt="Create"></i>
|
||||
<h6>Create</h6>
|
||||
</div>
|
||||
}
|
||||
<div class="mx-0 mx-sm-5 px-2 px-xl-5 text-primary d-inline-block">
|
||||
<i class="btnImport fa-solid fa-upload fa-2x pointer" alt="Import"></i>
|
||||
<h6>Import</h6>
|
||||
</div>
|
||||
<div class="mx-0 mx-sm-5 px-2 px-xl-5 text-primary d-inline-block">
|
||||
<i class="btnExport fa-solid fa-download fa-2x pointer" alt="Export"></i>
|
||||
<h6>Export</h6>
|
||||
</div>
|
||||
<div class="mx-0 mx-sm-5 px-2 px-xl-5 text-danger d-inline-block">
|
||||
<i class="btnWipeAllGalleries fa-solid fa-broom fa-2x pointer" alt="Wipe"></i>
|
||||
<h6>Wipe</h6>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1 class="display-6">@_localizer["Available_Galleries"].Value: </h1>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th class="col-6 col-md-4">@_localizer["Name"].Value</th>
|
||||
@@ -43,17 +64,9 @@
|
||||
<td>
|
||||
<i class="btnOpenGallery btn btn-outline-primary fa-solid fa-up-right-from-square" data-url="@galleryLink" alt="Open"></i>
|
||||
<i class="btnDownloadGallery btn @(gallery.TotalItems > 0 ? "btn-outline-primary" : "btn-outline-disabled") fa-solid fa-download" alt="Download" @(gallery.TotalItems == 0 ? "disabled=disabled" : string.Empty)></i>
|
||||
|
||||
@if (gallery.Id > 1)
|
||||
{
|
||||
<i class="btnEditGallery btn btn-outline-success fa-solid fa-pen-to-square" alt="Rename"></i>
|
||||
<i class="btnDeleteGallery btn btn-outline-danger fa-solid fa-trash-can" alt="Delete"></i>
|
||||
}
|
||||
else
|
||||
{
|
||||
<i class="btn btn-outline-disabled fa-solid fa-pen-to-square" alt="Rename"></i>
|
||||
<i class="btn btn-outline-disabled fa-solid fa-trash-can" alt="Delete"></i>
|
||||
}
|
||||
<i class="btn @(gallery.Id > 1 ? "btnEditGallery btn-outline-success" : "btn-outline-disabled") fa-solid fa-pen-to-square" alt="Edit"></i>
|
||||
<i class="btn @(gallery.TotalItems > 0 ? "btnWipeGallery btn-outline-danger" : "btn-outline-disabled") fa-solid fa-broom" alt="Wipe" @(gallery.TotalItems == 0 ? "disabled=disabled" : string.Empty)></i>
|
||||
<i class="btn @(gallery.Id > 1 ? "btnDeleteGallery btn-outline-danger" : "btn-outline-disabled") fa-solid fa-trash-can" alt="Delete"></i>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@using System.Text
|
||||
@using WeddingShare.Enums
|
||||
@using WeddingShare.Views.Gallery
|
||||
@inject Microsoft.Extensions.Localization.IStringLocalizer<IndexModel> _localizer
|
||||
@inject WeddingShare.Helpers.IConfigHelper _config
|
||||
@@ -7,21 +8,21 @@
|
||||
@{
|
||||
var ctx = Context.Request;
|
||||
|
||||
var qrCodeLink = $"{(_config.GetOrDefault("Settings", "Force_Https", false) ? "https" : ctx.Scheme)}://{ctx.Host}{ctx.Path}";
|
||||
if (_config.GetOrDefault("Settings", "Hide_Key_From_QR_Code", false))
|
||||
var baseLink = $"{(_config.GetOrDefault("Settings", "Force_Https", false) ? "https" : ctx.Scheme)}://{ctx.Host}{ctx.Path}";
|
||||
|
||||
var queryString = new StringBuilder();
|
||||
foreach (var q in ctx.Query.Where(x => !string.Equals("order", x.Key, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var queryString = new StringBuilder();
|
||||
foreach (var q in ctx.Query.Where(x => !string.Equals("key", x.Key, StringComparison.OrdinalIgnoreCase)))
|
||||
if (string.Equals("key", q.Key, StringComparison.OrdinalIgnoreCase) && _config.GetOrDefault("Settings", "Hide_Key_From_QR_Code", false))
|
||||
{
|
||||
queryString.Append($"&{q.Key}={q.Value}");
|
||||
continue;
|
||||
}
|
||||
|
||||
qrCodeLink = $"{qrCodeLink}?{queryString.ToString().TrimStart('&')}";
|
||||
}
|
||||
else
|
||||
{
|
||||
qrCodeLink = $"{qrCodeLink}?{ctx.QueryString.ToString().TrimStart('?')}";
|
||||
queryString.Append($"&{q.Key}={q.Value}");
|
||||
}
|
||||
|
||||
baseLink = $"{baseLink}?{queryString.ToString().TrimStart('&')}".TrimEnd(new char[] { '?', '&' });
|
||||
var qrCodeLink = $"{baseLink}{(ctx.Query.ContainsKey("order") ? $"&order={ctx.Query["order"]}" : string.Empty)}";
|
||||
}
|
||||
|
||||
@if (Model?.FileUploader != null)
|
||||
@@ -53,6 +54,20 @@
|
||||
@if (!_config.GetOrDefault("Settings", "Disable_QR_Code", false))
|
||||
{
|
||||
<div class="qrcode-container d-none d-lg-block">
|
||||
<div class="btn-group w-100 mb-5">
|
||||
<button class="btn btn-primary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Sort
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
@foreach (GalleryOrder order in Enum.GetValues(typeof(GalleryOrder)))
|
||||
{
|
||||
if (order != GalleryOrder.None)
|
||||
{
|
||||
<a class="dropdown-item" href="@($"{baseLink}&order={(int)order}")">@order</a>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div id="qrcode"></div>
|
||||
<span>@_localizer["Share_Code"].Value</span>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
<link rel="stylesheet" href="~/css/fontawesome.solid.min.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/css/fontawesome.brands.min.css" asp-append-version="true" />
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/lib/jquery-lightbox/lightbox-plus-jquery.min.js"></script>
|
||||
@@ -35,6 +34,7 @@
|
||||
<script src="~/js/fontawesome.solid.min.js"></script>
|
||||
<script src="~/js/fontawesome.brands.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
<script src="~/js/popup.js" asp-append-version="true"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navigation -->
|
||||
|
||||
@@ -14,119 +14,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="add-gallery-modal" class="modal pt-lg-4" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@_localizer["Create"].Value</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row pb-4">
|
||||
<div class="col-12">
|
||||
<label for="gallery-name">@_localizer["Gallery_Name"].Value</label>
|
||||
<input type="text" id="gallery-name" class="form-control" aria-describedby="gallery-name-help" placeholder="" aria-label="@_localizer["Gallery_Name"].Value" />
|
||||
<div id="gallery-key-help" class="form-text">
|
||||
@_localizer["Gallery_Name_Help"].Value
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pb-4">
|
||||
<div class="col-12">
|
||||
<label for="gallery-name">@_localizer["Gallery_Key"].Value</label>
|
||||
<input type="text" id="gallery-key" class="form-control" aria-describedby="gallery-key-help" placeholder="" aria-label="@_localizer["Gallery_Key"].Value" />
|
||||
<div id="gallery-key-help" class="form-text">
|
||||
@_localizer["Gallery_Key_Help"].Value
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pt-3">
|
||||
<div class="col-6">
|
||||
<button type="button" class="btn btn-success col-12 btnCreate" data-dismiss="modal">@_localizer["Create"].Value</button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<button type="button" class="btn btn-secondary col-12 btnDismissPopup" data-dismiss="modal">@_localizer["Cancel"].Value</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="edit-gallery-modal" class="modal pt-lg-4" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@_localizer["Update"].Value</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="gallery-id" />
|
||||
|
||||
<div class="row pb-4">
|
||||
<div class="col-12">
|
||||
<label for="gallery-name">@_localizer["Gallery_Name"].Value</label>
|
||||
<input type="text" id="gallery-name" class="form-control" aria-describedby="gallery-name-help" placeholder="" aria-label="@_localizer["Gallery_Name"].Value" />
|
||||
<div id="gallery-key-help" class="form-text">
|
||||
@_localizer["Gallery_Name_Help"].Value
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pb-4">
|
||||
<div class="col-12">
|
||||
<label for="gallery-name">@_localizer["Gallery_Key"].Value</label>
|
||||
<input type="text" id="gallery-key" class="form-control" aria-describedby="gallery-key-help" placeholder="" aria-label="@_localizer["Gallery_Key"].Value" />
|
||||
<div id="gallery-key-help" class="form-text">
|
||||
@_localizer["Gallery_Key_Help"].Value
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pt-3">
|
||||
<div class="col-6">
|
||||
<button type="button" class="btn btn-success col-12 btnUpdate" data-dismiss="modal">@_localizer["Update"].Value</button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<button type="button" class="btn btn-secondary col-12 btnDismissPopup" data-dismiss="modal">@_localizer["Cancel"].Value</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="delete-gallery-modal" class="modal pt-lg-4" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@_localizer["Delete"].Value</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="gallery-id" />
|
||||
|
||||
<div class="row pb-4">
|
||||
<div class="col-12">
|
||||
<label for="gallery-name">@_localizer["Gallery_Name"].Value</label>
|
||||
<input type="text" id="gallery-name" class="form-control" aria-describedby="gallery-name-help" placeholder="" aria-label="@_localizer["Gallery_Name"].Value" disabled="disabled" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pb-4">
|
||||
<div class="col-12">@_localizer["Delete_Confirmation"].Value</div>
|
||||
</div>
|
||||
|
||||
<div class="row pt-3">
|
||||
<div class="col-6">
|
||||
<button type="button" class="btn btn-danger col-12 btnDelete" data-dismiss="modal">@_localizer["Delete"].Value</button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<button type="button" class="btn btn-secondary col-12 btnDismissPopup" data-dismiss="modal">@_localizer["Cancel"].Value</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -33,9 +33,15 @@
|
||||
var index = columnIndex;
|
||||
while (index < Model.Images.Count())
|
||||
{
|
||||
<a href="@Model.GalleryPath/@Model.Images[index]" data-lightbox="@Model.GalleryId">
|
||||
<img src="@Model.ThumbnailsPath/@(System.IO.Path.GetFileNameWithoutExtension(Model.Images[index].TrimStart('/'))).webp" class="w-100 shadow-1-strong rounded mb-4" loading="lazy" />
|
||||
</a>
|
||||
<div class="rounded mb-4 image-tile">
|
||||
<a href="@Model.GalleryPath/@Model.Images[index]?.Name?.TrimStart('/')" data-lightbox="@Model.GalleryId">
|
||||
<img src="@Model.ThumbnailsPath/@(System.IO.Path.GetFileNameWithoutExtension(Model.Images[index]?.Name?.TrimStart('/'))).webp" class="w-100 shadow-1-strong" loading="lazy" />
|
||||
</a>
|
||||
@if (User?.Identity != null && User.Identity.IsAuthenticated)
|
||||
{
|
||||
<button type="button" class="btn btn-danger btnDeletePhoto w-100 m-0" data-photo-id="@Model.Images[index]?.Id" data-photo-name="@Model.Images[index]?.Name">@_localizer["Delete"].Value</button>
|
||||
}
|
||||
</div>
|
||||
index += columnCount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"Directory_Scanner_Interval": "*/30 * * * *"
|
||||
},
|
||||
"Release": {
|
||||
"Version": "1.1.2"
|
||||
"Version": "1.2.0"
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -90,4 +90,17 @@ tr td i {
|
||||
|
||||
.pending-approval .btn-group .btn:last-child {
|
||||
border-radius: 0px 0px 5px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.image-tile {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.image-tile .btn {
|
||||
border-radius: 0px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.image-tile:hover .btn {
|
||||
display: block;
|
||||
}
|
||||
@@ -51,11 +51,224 @@
|
||||
$(document).off('click', 'i.btnAddGallery').on('click', 'i.btnAddGallery', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
displayPopup('add-gallery-modal');
|
||||
if ($(this).attr('disabled') == 'disabled') {
|
||||
return;
|
||||
}
|
||||
|
||||
displayPopup({
|
||||
Title: 'Create Gallery',
|
||||
Fields: [{
|
||||
Id: 'gallery-name',
|
||||
Name: 'Gallery Name',
|
||||
Hint: 'Please enter a new gallery name'
|
||||
}, {
|
||||
Id: 'gallery-key',
|
||||
Name: 'Secret Key',
|
||||
Hint: 'Please enter a new secret key'
|
||||
}],
|
||||
Buttons: [{
|
||||
Text: 'Create',
|
||||
Class: 'btn-success',
|
||||
Callback: function () {
|
||||
displayLoader('Loading...');
|
||||
|
||||
let name = $('#popup-modal-field-gallery-name').val();
|
||||
if (name == undefined || name.length == 0) {
|
||||
displayMessage(`Create Gallery`, `Gallery name cannot be empty`);
|
||||
return;
|
||||
}
|
||||
|
||||
let key = $('#popup-modal-field-gallery-key').val();
|
||||
|
||||
$.ajax({
|
||||
url: '/Admin/AddGallery',
|
||||
method: 'POST',
|
||||
data: { Id: 0, Name: name, SecretKey: key }
|
||||
})
|
||||
.done(data => {
|
||||
if (data.success === true) {
|
||||
displayMessage(`Create Gallery`, `Successfully created gallery`);
|
||||
} else if (data.message) {
|
||||
displayMessage(`Create Gallery`, `Create failed`, [data.message]);
|
||||
} else {
|
||||
displayMessage(`Create Gallery`, `Failed to create gallery`);
|
||||
}
|
||||
})
|
||||
.fail((xhr, error) => {
|
||||
displayMessage(`Create Gallery`, `Create failed`, [error]);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
Text: 'Close'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
$(document).off('click', 'i.btnImport').on('click', 'i.btnImport', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
if ($(this).attr('disabled') == 'disabled') {
|
||||
return;
|
||||
}
|
||||
|
||||
displayPopup({
|
||||
Title: 'Import Data',
|
||||
Fields: [{
|
||||
Id: 'import-file',
|
||||
Name: 'Backup File',
|
||||
Type: 'File',
|
||||
Hint: 'Please select a WeddingShare backup archive',
|
||||
Accept: '.zip'
|
||||
}],
|
||||
Buttons: [{
|
||||
Text: 'Import',
|
||||
Class: 'btn-success',
|
||||
Callback: function () {
|
||||
displayLoader('Loading...');
|
||||
|
||||
var files = $('#popup-modal-field-import-file')[0].files;
|
||||
if (files == undefined || files.length == 0) {
|
||||
displayMessage(`Import Data`, `Please select an import file`);
|
||||
return;
|
||||
}
|
||||
|
||||
var data = new FormData();
|
||||
data.append('file-0', files[0]);
|
||||
|
||||
$.ajax({
|
||||
url: '/Admin/ImportBackup',
|
||||
method: 'POST',
|
||||
data: data,
|
||||
contentType: false,
|
||||
processData: false
|
||||
})
|
||||
.done(data => {
|
||||
if (data.success === true) {
|
||||
displayMessage(`Import Data`, `Successfully imported data`);
|
||||
window.location.reload();
|
||||
} else if (data.message) {
|
||||
displayMessage(`Import Data`, `Import failed`, [data.message]);
|
||||
} else {
|
||||
displayMessage(`Import Data`, `Failed to import data`);
|
||||
}
|
||||
})
|
||||
.fail((xhr, error) => {
|
||||
displayMessage(`Import Data`, `Import failed`, [error]);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
Text: 'Close'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
$(document).off('click', 'i.btnExport').on('click', 'i.btnExport', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
if ($(this).attr('disabled') == 'disabled') {
|
||||
return;
|
||||
}
|
||||
|
||||
displayPopup({
|
||||
Title: 'Export Data',
|
||||
Message: 'Are you sure you want to continue?',
|
||||
Buttons: [{
|
||||
Text: 'Export',
|
||||
Class: 'btn-success',
|
||||
Callback: function () {
|
||||
displayLoader('Loading...');
|
||||
|
||||
$.ajax({
|
||||
url: '/Admin/ExportBackup',
|
||||
method: 'GET'
|
||||
})
|
||||
.done(data => {
|
||||
hideLoader();
|
||||
|
||||
if (data.success === true) {
|
||||
var s = window.atob(data.content);
|
||||
var bytes = new Uint8Array(s.length);
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
bytes[i] = s.charCodeAt(i);
|
||||
}
|
||||
|
||||
var blob = new Blob([bytes], { type: "application/octetstream" });
|
||||
|
||||
var isIE = false || !!document.documentMode;
|
||||
if (isIE) {
|
||||
window.navigator.msSaveBlob(blob, data.filename);
|
||||
} else {
|
||||
var url = window.URL || window.webkitURL;
|
||||
link = url.createObjectURL(blob);
|
||||
var a = $("<a />");
|
||||
a.attr("download", data.filename);
|
||||
a.attr("href", link);
|
||||
$("body").append(a);
|
||||
a[0].click();
|
||||
$("body").remove(a);
|
||||
}
|
||||
} else if (data.message) {
|
||||
displayMessage(`Export Data`, `Export failed`, [data.message]);
|
||||
} else {
|
||||
displayMessage(`Export Data`, `Failed to export data`);
|
||||
}
|
||||
})
|
||||
.fail((xhr, error) => {
|
||||
displayMessage(`Export Data`, `Export failed`, [error]);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
Text: 'Close'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
$(document).off('click', 'i.btnWipeAllGalleries').on('click', 'i.btnWipeAllGalleries', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
if ($(this).attr('disabled') == 'disabled') {
|
||||
return;
|
||||
}
|
||||
|
||||
displayPopup({
|
||||
Title: 'Wipe Data',
|
||||
Message: 'Are you sure you want to wipe all data?',
|
||||
Buttons: [{
|
||||
Text: 'Wipe',
|
||||
Class: 'btn-danger',
|
||||
Callback: function () {
|
||||
displayLoader('Loading...');
|
||||
|
||||
$.ajax({
|
||||
url: '/Admin/WipeAllGalleries',
|
||||
method: 'DELETE'
|
||||
})
|
||||
.done(data => {
|
||||
if (data.success === true) {
|
||||
displayMessage(`Wipe Data`, `Successfully wiped data`);
|
||||
} else if (data.message) {
|
||||
displayMessage(`Wipe Data`, `Wipe failed`, [data.message]);
|
||||
} else {
|
||||
displayMessage(`Wipe Data`, `Failed to wipe data`);
|
||||
}
|
||||
})
|
||||
.fail((xhr, error) => {
|
||||
displayMessage(`Wipe Data`, `Wipe failed`, [error]);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
Text: 'Close'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
$(document).off('click', 'i.btnOpenGallery').on('click', 'i.btnOpenGallery', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
if ($(this).attr('disabled') == 'disabled') {
|
||||
return;
|
||||
}
|
||||
|
||||
window.open($(this).data('url'), '_blank');
|
||||
});
|
||||
|
||||
@@ -116,119 +329,177 @@
|
||||
$(document).off('click', 'i.btnEditGallery').on('click', 'i.btnEditGallery', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
if ($(this).attr('disabled') == 'disabled') {
|
||||
return;
|
||||
}
|
||||
|
||||
let row = $(this).closest('tr');
|
||||
displayPopup({
|
||||
Title: 'Edit Gallery',
|
||||
Fields: [{
|
||||
Id: 'gallery-id',
|
||||
Value: row.data('gallery-id'),
|
||||
Type: 'hidden'
|
||||
}, {
|
||||
Id: 'gallery-name',
|
||||
Name: 'Gallery Name',
|
||||
Value: row.data('gallery-name'),
|
||||
Hint: 'Please enter a new gallery name'
|
||||
}, {
|
||||
Id: 'gallery-key',
|
||||
Name: 'Secret Key',
|
||||
Value: row.data('gallery-key'),
|
||||
Hint: 'Please enter a new secret key'
|
||||
}],
|
||||
Buttons: [{
|
||||
Text: 'Update',
|
||||
Class: 'btn-success',
|
||||
Callback: function () {
|
||||
displayLoader('Loading...');
|
||||
|
||||
$('#edit-gallery-modal #gallery-id').val(row.data('gallery-id'));
|
||||
$('#edit-gallery-modal #gallery-name').val(row.data('gallery-name'));
|
||||
$('#edit-gallery-modal #gallery-key').val(row.data('gallery-key'));
|
||||
let id = $('#popup-modal-field-gallery-id').val();
|
||||
if (id == undefined || id.length == 0) {
|
||||
displayMessage(`Edit Gallery`, `Gallery id cannot be empty`);
|
||||
return;
|
||||
}
|
||||
|
||||
displayPopup('edit-gallery-modal');
|
||||
let name = $('#popup-modal-field-gallery-name').val();
|
||||
if (name == undefined || name.length == 0) {
|
||||
displayMessage(`Edit Gallery`, `Gallery name cannot be empty`);
|
||||
return;
|
||||
}
|
||||
|
||||
let key = $('#popup-modal-field-gallery-key').val();
|
||||
|
||||
$.ajax({
|
||||
url: '/Admin/EditGallery',
|
||||
method: 'PUT',
|
||||
data: { Id: id, Name: name, SecretKey: key }
|
||||
})
|
||||
.done(data => {
|
||||
if (data.success === true) {
|
||||
$(`tr[data-gallery-id=${id}] .gallery-name`).text(name);
|
||||
displayMessage(`Edit Gallery`, `Successfully updated gallery`);
|
||||
} else if (data.message) {
|
||||
displayMessage(`Edit Gallery`, `Update failed`, [data.message]);
|
||||
} else {
|
||||
displayMessage(`Edit Gallery`, `Failed to update gallery`);
|
||||
}
|
||||
})
|
||||
.fail((xhr, error) => {
|
||||
displayMessage(`Edit Gallery`, `Update failed`, [error]);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
Text: 'Close'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
$(document).off('click', 'i.btnWipeGallery').on('click', 'i.btnWipeGallery', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
if ($(this).attr('disabled') == 'disabled') {
|
||||
return;
|
||||
}
|
||||
|
||||
let row = $(this).closest('tr');
|
||||
displayPopup({
|
||||
Title: 'Wipe Gallery',
|
||||
Message: `Are you sure you want to wipe gallery '${row.data('gallery-name') }'?`,
|
||||
Fields: [{
|
||||
Id: 'gallery-id',
|
||||
Value: row.data('gallery-id'),
|
||||
Type: 'hidden'
|
||||
}],
|
||||
Buttons: [{
|
||||
Text: 'Wipe',
|
||||
Class: 'btn-danger',
|
||||
Callback: function () {
|
||||
displayLoader('Loading...');
|
||||
|
||||
let id = $('#popup-modal-field-gallery-id').val();
|
||||
if (id == undefined || id.length == 0) {
|
||||
displayMessage(`Wipe Gallery`, `Gallery id cannot be empty`);
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/Admin/WipeGallery',
|
||||
method: 'DELETE',
|
||||
data: { id }
|
||||
})
|
||||
.done(data => {
|
||||
if (data.success === true) {
|
||||
$(`tr[data-gallery-id=${id}] .gallery-name`).text(name);
|
||||
displayMessage(`Wipe Gallery`, `Successfully wiped gallery`);
|
||||
} else if (data.message) {
|
||||
displayMessage(`Wipe Gallery`, `Wipe failed`, [data.message]);
|
||||
} else {
|
||||
displayMessage(`Wipe Gallery`, `Failed to wipe gallery`);
|
||||
}
|
||||
})
|
||||
.fail((xhr, error) => {
|
||||
displayMessage(`Wipe Gallery`, `Wipe failed`, [error]);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
Text: 'Close'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
$(document).off('click', 'i.btnDeleteGallery').on('click', 'i.btnDeleteGallery', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
if ($(this).attr('disabled') == 'disabled') {
|
||||
return;
|
||||
}
|
||||
|
||||
let row = $(this).closest('tr');
|
||||
displayPopup({
|
||||
Title: 'Delete Gallery',
|
||||
Message: `Are you sure you want to delete gallery '${row.data('gallery-name')}'?`,
|
||||
Fields: [{
|
||||
Id: 'gallery-id',
|
||||
Value: row.data('gallery-id'),
|
||||
Type: 'hidden'
|
||||
}],
|
||||
Buttons: [{
|
||||
Text: 'Delete',
|
||||
Class: 'btn-danger',
|
||||
Callback: function () {
|
||||
displayLoader('Loading...');
|
||||
|
||||
$('#delete-gallery-modal #gallery-id').val(row.data('gallery-id'));
|
||||
$('#delete-gallery-modal #gallery-name').val(row.data('gallery-name'));
|
||||
let id = $('#popup-modal-field-gallery-id').val();
|
||||
if (id == undefined || id.length == 0) {
|
||||
displayMessage(`Delete Gallery`, `Gallery id cannot be empty`);
|
||||
return;
|
||||
}
|
||||
|
||||
displayPopup('delete-gallery-modal');
|
||||
});
|
||||
|
||||
$(document).off('click', '#add-gallery-modal .btnCreate').on('click', '#add-gallery-modal .btnCreate', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
hidePopup('add-gallery-modal');
|
||||
displayLoader('Loading...');
|
||||
|
||||
let name = $('#add-gallery-modal #gallery-name').val();
|
||||
let key = $('#add-gallery-modal #gallery-key').val();
|
||||
|
||||
$.ajax({
|
||||
url: '/Admin/AddGallery',
|
||||
method: 'POST',
|
||||
data: { Id: 0, Name: name, SecretKey: key }
|
||||
})
|
||||
.done(data => {
|
||||
hideLoader();
|
||||
|
||||
if (data.success === true) {
|
||||
displayMessage(`Create`, `Successfully created gallery`);
|
||||
} else if (data.message) {
|
||||
displayMessage(`Create`, `Create failed`, [data.message]);
|
||||
} else {
|
||||
displayMessage(`Create`, `Failed to create gallery`);
|
||||
$.ajax({
|
||||
url: '/Admin/DeleteGallery',
|
||||
method: 'DELETE',
|
||||
data: { id }
|
||||
})
|
||||
.done(data => {
|
||||
if (data.success === true) {
|
||||
$(`tr[data-gallery-id=${id}]`).remove();
|
||||
displayMessage(`Delete Gallery`, `Successfully deleted gallery`);
|
||||
} else if (data.message) {
|
||||
displayMessage(`Delete Gallery`, `Delete failed`, [data.message]);
|
||||
} else {
|
||||
displayMessage(`Delete Gallery`, `Failed to delete gallery`);
|
||||
}
|
||||
})
|
||||
.fail((xhr, error) => {
|
||||
displayMessage(`Delete Gallery`, `Delete failed`, [error]);
|
||||
});
|
||||
}
|
||||
})
|
||||
.fail((xhr, error) => {
|
||||
hideLoader();
|
||||
displayMessage(`Create`, `Create failed`, [error]);
|
||||
});
|
||||
});
|
||||
|
||||
$(document).off('click', '#edit-gallery-modal .btnUpdate').on('click', '#edit-gallery-modal .btnUpdate', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
hidePopup('edit-gallery-modal');
|
||||
displayLoader('Loading...');
|
||||
|
||||
let id = $('#edit-gallery-modal #gallery-id').val();
|
||||
let name = $('#edit-gallery-modal #gallery-name').val();
|
||||
let key = $('#edit-gallery-modal #gallery-key').val();
|
||||
|
||||
$.ajax({
|
||||
url: '/Admin/EditGallery',
|
||||
method: 'PUT',
|
||||
data: { Id: id, Name: name, SecretKey: key }
|
||||
})
|
||||
.done(data => {
|
||||
hideLoader();
|
||||
|
||||
if (data.success === true) {
|
||||
$(`tr[data-gallery-id=${id}] .gallery-name`).text(name);
|
||||
displayMessage(`Update`, `Successfully updated gallery`);
|
||||
} else if (data.message) {
|
||||
displayMessage(`Update`, `Update failed`, [data.message]);
|
||||
} else {
|
||||
displayMessage(`Update`, `Failed to update gallery`);
|
||||
}
|
||||
})
|
||||
.fail((xhr, error) => {
|
||||
hideLoader();
|
||||
displayMessage(`Update`, `Update failed`, [error]);
|
||||
});
|
||||
});
|
||||
|
||||
$(document).off('click', '#delete-gallery-modal .btnDelete').on('click', '#delete-gallery-modal .btnDelete', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
hidePopup('delete-gallery-modal');
|
||||
displayLoader('Loading...');
|
||||
|
||||
let id = $('#delete-gallery-modal #gallery-id').val();
|
||||
|
||||
$.ajax({
|
||||
url: '/Admin/DeleteGallery',
|
||||
method: 'DELETE',
|
||||
data: { id }
|
||||
})
|
||||
.done(data => {
|
||||
hideLoader();
|
||||
|
||||
if (data.success === true) {
|
||||
$(`tr[data-gallery-id=${id}]`).remove();
|
||||
displayMessage(`Delete`, `Successfully deleted gallery`);
|
||||
} else if (data.message) {
|
||||
displayMessage(`Delete`, `Delete failed`, [data.message]);
|
||||
} else {
|
||||
displayMessage(`Delete`, `Failed to delete gallery`);
|
||||
}
|
||||
})
|
||||
.fail((xhr, error) => {
|
||||
hideLoader();
|
||||
displayMessage(`Delete`, `Delete failed`, [error]);
|
||||
});
|
||||
}, {
|
||||
Text: 'Close'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -148,5 +148,60 @@
|
||||
imageUpload(dataRefs);
|
||||
}
|
||||
|
||||
$(document).off('click', 'button.btnDeletePhoto').on('click', 'button.btnDeletePhoto', function (e) {
|
||||
preventDefaults(e);
|
||||
|
||||
if ($(this).attr('disabled') == 'disabled') {
|
||||
return;
|
||||
}
|
||||
|
||||
var id = $(this).data('photo-id');
|
||||
var name = $(this).data('photo-name');
|
||||
|
||||
displayPopup({
|
||||
Title: 'Delete Photo',
|
||||
Message: `Are you sure you want to delete photo '${name}'?`,
|
||||
Fields: [{
|
||||
Id: 'photo-id',
|
||||
Value: id,
|
||||
Type: 'hidden'
|
||||
}],
|
||||
Buttons: [{
|
||||
Text: 'Delete',
|
||||
Class: 'btn-danger',
|
||||
Callback: function () {
|
||||
displayLoader('Loading...');
|
||||
|
||||
let id = $('#popup-modal-field-photo-id').val();
|
||||
if (id == undefined || id.length == 0) {
|
||||
displayMessage(`Delete Photo`, `Photo id cannot be empty`);
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/Admin/DeletePhoto',
|
||||
method: 'DELETE',
|
||||
data: { id }
|
||||
})
|
||||
.done(data => {
|
||||
if (data.success === true) {
|
||||
$(`tr[data-gallery-id=${id}]`).remove();
|
||||
displayMessage(`Delete Photo`, `Successfully deleted photo`);
|
||||
} else if (data.message) {
|
||||
displayMessage(`Delete Photo`, `Delete failed`, [data.message]);
|
||||
} else {
|
||||
displayMessage(`Delete Photo`, `Failed to delete photo`);
|
||||
}
|
||||
})
|
||||
.fail((xhr, error) => {
|
||||
displayMessage(`Delete Photo`, `Delete failed`, [error]);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
Text: 'Close'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
})();
|
||||
65
WeddingShare/wwwroot/js/popup.js
Normal file
65
WeddingShare/wwwroot/js/popup.js
Normal file
@@ -0,0 +1,65 @@
|
||||
function displayPopup(options) {
|
||||
let fields = '';
|
||||
(options?.Fields ?? [])?.forEach((field, index) => {
|
||||
fields += `<div class="row pb-3 ${field?.Type?.toLowerCase() == 'hidden' ? "d-none" : ""}">
|
||||
<div class="col-12">
|
||||
<label>${field?.Name}</label>
|
||||
<input type="${field?.Type?.toLowerCase() ?? "text"}" id="popup-modal-field-${field?.Id}" class="form-control" aria-describedby="${field?.DescribedBy ?? ""}" placeholder="${field?.Placeholder ?? ""}" value="${field?.Value ?? ""}" aria-label="${field?.Name}" ${field?.Disabled ? "disabled=\"disabled\"" : ""} ${field?.Accept ? "accept=\"" + field?.Accept + "\"" : ""} />
|
||||
<div class="form-text">${field?.Hint ?? ""}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
let message = '';
|
||||
if (options?.Message != undefined && options?.Message.length > 0) {
|
||||
message = `<div class="row pb-3">
|
||||
<div class="col-12">${options?.Message ?? ""}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
let buttons = '';
|
||||
(options?.Buttons ?? [{ Text: 'Close' }])?.forEach((button, index) => {
|
||||
buttons += `<div class="col-${12 / options?.Buttons?.length ?? 1}">
|
||||
<button type="button" id="popup-modal-button-${index}" class="btn ${button?.Class ?? "btn-secondary"} col-12">${button?.Text ?? "Close"}</button>
|
||||
</div>`;
|
||||
|
||||
$(document).off('click', `#popup-modal-button-${index}`).on('click', `#popup-modal-button-${index}`, function () {
|
||||
hidePopup();
|
||||
|
||||
if (button?.Callback) {
|
||||
button?.Callback();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('body').loading({
|
||||
theme: 'dark',
|
||||
message: '',
|
||||
stoppable: false,
|
||||
start: true
|
||||
});
|
||||
|
||||
$('.popup-modal').remove();
|
||||
$('body').append(`<div class="modal popup-modal pt-lg-4" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">${options.Title}</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
${fields}
|
||||
${message}
|
||||
<div class="row ${(options?.Fields?.length ?? 0) > 0 ? "pt-3" : ""}">
|
||||
${buttons}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`);
|
||||
$('.popup-modal').show();
|
||||
}
|
||||
|
||||
function hidePopup() {
|
||||
$('body').loading('stop');
|
||||
$('.popup-modal').hide();
|
||||
}
|
||||
@@ -16,21 +16,6 @@ function hideLoader() {
|
||||
$('body').loading('stop');
|
||||
}
|
||||
|
||||
function displayPopup(id) {
|
||||
$('body').loading({
|
||||
theme: 'dark',
|
||||
message: '',
|
||||
stoppable: false,
|
||||
start: true
|
||||
});
|
||||
$(`#${id}`).show();
|
||||
}
|
||||
|
||||
function hidePopup(id) {
|
||||
$('body').loading('stop');
|
||||
$(`#${id}`).hide();
|
||||
}
|
||||
|
||||
function uuidv4() {
|
||||
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
|
||||
(+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16)
|
||||
@@ -38,6 +23,8 @@ function uuidv4() {
|
||||
}
|
||||
|
||||
function displayMessage(title, message, errors) {
|
||||
hideLoader();
|
||||
|
||||
$('#alert-message-modal .modal-title').text(title);
|
||||
$('#alert-message-modal .modal-message').html(message);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user