Merge branch 'prerel-1.6.11' into 'main'

Prerel 1.6.11

See merge request personal/wedding-share!80
This commit is contained in:
Chris Collins
2025-07-29 22:03:24 +01:00
28 changed files with 350 additions and 211 deletions

View File

@@ -1,3 +1,4 @@
using System.Data.Common;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -50,6 +51,10 @@ namespace WeddingShare.UnitTests.Tests.Helpers
_database.GetGalleryId("blaa").Returns(Task.FromResult<int?>(mockData["blaa"].Id));
_database.GetGalleryId("missing").Returns(Task.FromResult<int?>(null));
_database.GetGalleryIdByName("default").Returns(Task.FromResult<int?>(mockData["default"].Id));
_database.GetGalleryIdByName("blaa").Returns(Task.FromResult<int?>(mockData["blaa"].Id));
_database.GetGalleryIdByName("missing").Returns(Task.FromResult<int?>(null));
_database.AddGallery(Arg.Any<GalleryModel>()).Returns(Task.FromResult<GalleryModel?>(new GalleryModel()
{
Id = 101,
@@ -86,10 +91,10 @@ namespace WeddingShare.UnitTests.Tests.Helpers
_localizer[Arg.Any<string>()].Returns(new LocalizedString("UnitTest", "UnitTest"));
}
[TestCase(DeviceType.Desktop, 1, "default", "password", ViewMode.Default, GalleryGroup.None, GalleryOrder.Descending, true)]
[TestCase(DeviceType.Mobile, 2, "blaa", "456789", ViewMode.Presentation, GalleryGroup.Date, GalleryOrder.Ascending, true)]
[TestCase(DeviceType.Tablet, 101, "missing", "123456", ViewMode.Slideshow, GalleryGroup.Uploader, GalleryOrder.Ascending, false)]
public async Task GalleryController_Index(DeviceType deviceType, int id, string name, string? key, ViewMode? mode, GalleryGroup group, GalleryOrder order, bool existing)
[TestCase(DeviceType.Desktop, 1, "default", "default", "password", ViewMode.Default, GalleryGroup.None, GalleryOrder.Descending, true)]
[TestCase(DeviceType.Mobile, 2, "blaa", "blaa", "456789", ViewMode.Presentation, GalleryGroup.Date, GalleryOrder.Ascending, true)]
[TestCase(DeviceType.Tablet, 101, "missing", "missing", "123456", ViewMode.Slideshow, GalleryGroup.Uploader, GalleryOrder.Ascending, false)]
public async Task GalleryController_Index(DeviceType deviceType, int id, string? identifier, string? name, string? key, ViewMode? mode, GalleryGroup group, GalleryOrder order, bool existing)
{
_deviceDetector.ParseDeviceType(Arg.Any<string>()).Returns(deviceType);
_settings.GetOrDefault(Settings.Basic.SingleGalleryMode, Arg.Any<bool>()).Returns(false);
@@ -100,23 +105,44 @@ namespace WeddingShare.UnitTests.Tests.Helpers
if (existing)
{
ViewResult actual = (ViewResult)await controller.Index(name, key, mode, group, order);
ViewResult actual = (ViewResult)await controller.Index(identifier, name, key, mode, group, order);
Assert.That(actual, Is.TypeOf<ViewResult>());
Assert.That(actual?.Model, Is.Not.Null);
PhotoGallery model = (PhotoGallery)actual.Model;
Assert.That(model?.GalleryId, Is.EqualTo(id));
Assert.That(model?.GalleryName, Is.EqualTo(name));
Assert.That(model?.Gallery?.Id, Is.EqualTo(id));
Assert.That(model?.Gallery?.Identifier, Is.EqualTo(identifier));
Assert.That(model?.Gallery?.Name, Is.EqualTo(name));
Assert.That(model?.SecretKey, Is.EqualTo(key));
Assert.That(model.ViewMode, Is.EqualTo(mode));
}
else
{
RedirectToActionResult actual = (RedirectToActionResult)await controller.Index(name, key, mode, group, order);
RedirectToActionResult actual = (RedirectToActionResult)await controller.Index(identifier, name, key, mode, group, order);
Assert.That(actual, Is.TypeOf<RedirectToActionResult>());
}
}
[TestCase(null, "default", "default")]
[TestCase("default", null, "default")]
[TestCase("default", "blaa", "blaa")]
public async Task GalleryController_Index_GetByIdentifier(string? id, string? identifier, string expected)
{
_deviceDetector.ParseDeviceType(Arg.Any<string>()).Returns(DeviceType.Desktop);
_settings.GetOrDefault(Settings.Basic.SingleGalleryMode, Arg.Any<bool>()).Returns(false);
_settings.GetOrDefault(Settings.Basic.GuestGalleryCreation, Arg.Any<bool>()).Returns(false);
var controller = new GalleryController(_env, _settings, _database, _file, _deviceDetector, _image, _notification, _encryption, _url, _logger, _localizer);
controller.ControllerContext.HttpContext = MockData.MockHttpContext();
ViewResult actual = (ViewResult)await controller.Index(id, identifier, "password", ViewMode.Default, GalleryGroup.None, GalleryOrder.Random);
Assert.That(actual, Is.TypeOf<ViewResult>());
Assert.That(actual?.Model, Is.Not.Null);
PhotoGallery model = (PhotoGallery)actual.Model;
Assert.That(model?.Gallery?.Identifier, Is.EqualTo(expected));
}
[TestCase(true, true)]
[TestCase(false, false)]
public async Task GalleryController_UploadDisabled(bool enabled, bool expected)
@@ -128,7 +154,7 @@ namespace WeddingShare.UnitTests.Tests.Helpers
var controller = new GalleryController(_env, _settings, _database, _file, _deviceDetector, _image, _notification, _encryption, _url, _logger, _localizer);
controller.ControllerContext.HttpContext = MockData.MockHttpContext();
ViewResult actual = (ViewResult)await controller.Index("default", "password", ViewMode.Default, GalleryGroup.None, GalleryOrder.Descending);
ViewResult actual = (ViewResult)await controller.Index("default", "default", "password", ViewMode.Default, GalleryGroup.None, GalleryOrder.Descending);
Assert.That(actual, Is.TypeOf<ViewResult>());
Assert.That(actual?.Model, Is.Not.Null);
@@ -150,7 +176,7 @@ namespace WeddingShare.UnitTests.Tests.Helpers
var controller = new GalleryController(_env, _settings, _database, _file, _deviceDetector, _image, _notification, _encryption, _url, _logger, _localizer);
controller.ControllerContext.HttpContext = MockData.MockHttpContext();
ViewResult actual = (ViewResult)await controller.Index("default", "password", ViewMode.Default, GalleryGroup.None, GalleryOrder.Descending);
ViewResult actual = (ViewResult)await controller.Index("default", "default", "password", ViewMode.Default, GalleryGroup.None, GalleryOrder.Descending);
Assert.That(actual, Is.TypeOf<ViewResult>());
Assert.That(actual?.Model, Is.Not.Null);
@@ -169,14 +195,15 @@ namespace WeddingShare.UnitTests.Tests.Helpers
var controller = new GalleryController(_env, _settings, _database, _file, _deviceDetector, _image, _notification, _encryption, _url, _logger, _localizer);
controller.ControllerContext.HttpContext = MockData.MockHttpContext();
ViewResult actual = (ViewResult)await controller.Index("default", "password", mode, group, order);
ViewResult actual = (ViewResult)await controller.Index("default", "default", "password", mode, group, order);
Assert.That(actual, Is.TypeOf<ViewResult>());
Assert.That(actual?.Model, Is.Not.Null);
PhotoGallery model = (PhotoGallery)actual.Model;
Assert.That(model?.GalleryId, Is.EqualTo(1));
Assert.That(model?.GalleryName, Is.EqualTo("default"));
Assert.That(model?.SecretKey, Is.EqualTo("password"));
Assert.That(model?.Gallery?.Id, Is.EqualTo(1));
Assert.That(model?.Gallery?.Identifier, Is.EqualTo("default"));
Assert.That(model?.Gallery?.Name, Is.EqualTo("default"));
Assert.That(model?.SecretKey, Is.EqualTo("password"));
Assert.That(model.ViewMode, Is.EqualTo(mode));
}
@@ -369,6 +396,7 @@ namespace WeddingShare.UnitTests.Tests.Helpers
"default", new GalleryModel()
{
Id = 1,
Identifier = "default",
Name = "default",
SecretKey = "password",
ApprovedItems = 32,
@@ -380,7 +408,8 @@ namespace WeddingShare.UnitTests.Tests.Helpers
"blaa", new GalleryModel()
{
Id = 2,
Name = "blaa",
Identifier = "blaa",
Name = "blaa",
SecretKey = "456789",
ApprovedItems = 2,
PendingItems = 1,
@@ -391,7 +420,8 @@ namespace WeddingShare.UnitTests.Tests.Helpers
"missing", new GalleryModel()
{
Id = 101,
Name = "missing",
Identifier = "missing",
Name = "missing",
SecretKey = "123456",
ApprovedItems = 0,
PendingItems = 0,

View File

@@ -13,47 +13,66 @@ namespace WeddingShare.Attributes
{
try
{
int? galleryId = null;
var request = filterContext.HttpContext.Request;
var databaseHelper = filterContext.HttpContext.RequestServices.GetService<IDatabaseHelper>();
var galleryName = (request.Query.ContainsKey("id") && !string.IsNullOrWhiteSpace(request.Query["id"])) ? request.Query["id"].ToString().ToLower() : "default";
var galleryId = (databaseHelper?.GetGalleryId(galleryName)?.Result) ?? 1;
var gallery = databaseHelper?.GetGallery(galleryId).Result;
if (gallery != null)
if (databaseHelper != null)
{
var encryptionHelper = filterContext.HttpContext.RequestServices.GetService<IEncryptionHelper>();
if (encryptionHelper != null)
var galleryIdentifier = (request.Query.ContainsKey("identifier") && !string.IsNullOrWhiteSpace(request.Query["identifier"])) ? request.Query["identifier"].ToString().ToLower() : null;
if (!string.IsNullOrWhiteSpace(galleryIdentifier))
{
galleryId = await databaseHelper.GetGalleryId(galleryIdentifier);
}
if (galleryId == null)
{
var key = request.Query.ContainsKey("key") ? request.Query["key"].ToString() : string.Empty;
var isEncrypted = request.Query.ContainsKey("enc") ? bool.Parse(request.Query["enc"].ToString().ToLower()) : false;
if (!isEncrypted && !string.IsNullOrWhiteSpace(key) && encryptionHelper.IsEncryptionEnabled())
{
var queryString = HttpUtility.ParseQueryString(request.QueryString.ToString());
queryString.Set("enc", "true");
queryString.Set("key", encryptionHelper.Encrypt(key));
filterContext.Result = new RedirectResult($"/Gallery?{queryString.ToString()}");
var galleryName = (request.Query.ContainsKey("id") && !string.IsNullOrWhiteSpace(request.Query["id"])) ? request.Query["id"].ToString().ToLower() : "default";
if (!string.IsNullOrWhiteSpace(galleryName))
{
galleryId = (databaseHelper?.GetGalleryIdByName(galleryName)?.Result) ?? 1;
}
else
{
var settingsHelper = filterContext.HttpContext.RequestServices.GetService<ISettingsHelper>();
if (settingsHelper != null)
{
var secretKey = settingsHelper.GetOrDefault(Settings.Gallery.SecretKey, string.Empty, galleryId).Result ?? string.Empty;
if (!string.IsNullOrWhiteSpace(secretKey))
{
secretKey = encryptionHelper.IsEncryptionEnabled() ? encryptionHelper.Encrypt(secretKey) : secretKey;
if (!string.IsNullOrWhiteSpace(secretKey) && !string.Equals(secretKey, key))
{
var logger = filterContext.HttpContext.RequestServices.GetService<ILogger<RequiresSecretKeyAttribute>>();
if (logger != null)
{
logger.LogWarning($"A request was made to an endpoint with an invalid secure key");
}
}
filterContext.Result = new RedirectToActionResult("Index", "Error", new { Reason = ErrorCode.InvalidSecretKey }, false);
if (galleryId != null)
{
var gallery = databaseHelper?.GetGallery(galleryId.Value).Result;
if (gallery != null)
{
var encryptionHelper = filterContext.HttpContext.RequestServices.GetService<IEncryptionHelper>();
if (encryptionHelper != null)
{
var key = request.Query.ContainsKey("key") ? request.Query["key"].ToString() : string.Empty;
var isEncrypted = request.Query.ContainsKey("enc") ? bool.Parse(request.Query["enc"].ToString().ToLower()) : false;
if (!isEncrypted && !string.IsNullOrWhiteSpace(key) && encryptionHelper.IsEncryptionEnabled())
{
var queryString = HttpUtility.ParseQueryString(request.QueryString.ToString());
queryString.Set("enc", "true");
queryString.Set("key", encryptionHelper.Encrypt(key));
filterContext.Result = new RedirectResult($"/Gallery?{queryString.ToString()}");
}
else
{
var settingsHelper = filterContext.HttpContext.RequestServices.GetService<ISettingsHelper>();
if (settingsHelper != null)
{
var secretKey = settingsHelper.GetOrDefault(Settings.Gallery.SecretKey, string.Empty, galleryId).Result ?? string.Empty;
if (!string.IsNullOrWhiteSpace(secretKey))
{
secretKey = encryptionHelper.IsEncryptionEnabled() ? encryptionHelper.Encrypt(secretKey) : secretKey;
if (!string.IsNullOrWhiteSpace(secretKey) && !string.Equals(secretKey, key))
{
var logger = filterContext.HttpContext.RequestServices.GetService<ILogger<RequiresSecretKeyAttribute>>();
if (logger != null)
{
logger.LogWarning($"A request was made to an endpoint with an invalid secure key");
}
filterContext.Result = new RedirectToActionResult("Index", "Error", new { Reason = ErrorCode.InvalidSecretKey }, false);
}
}
}
}
}

View File

@@ -59,15 +59,14 @@ namespace WeddingShare.BackgroundWorkers
var uploadsDirectory = Path.Combine(hostingEnvironment.WebRootPath, Directories.Uploads);
if (fileHelper.DirectoryExists(uploadsDirectory))
{
var searchPattern = !settingsHelper.GetOrDefault(Settings.Basic.SingleGalleryMode, false).Result ? "*" : "default";
var galleries = fileHelper.GetDirectories(uploadsDirectory, searchPattern, SearchOption.TopDirectoryOnly)?.Where(x => !Path.GetFileName(x).StartsWith("."));
if (galleries != null)
var galleryDirs = fileHelper.GetDirectories(uploadsDirectory, "*", SearchOption.TopDirectoryOnly)?.Where(x => !Path.GetFileName(x).StartsWith("."));
if (galleryDirs != null)
{
foreach (var gallery in galleries)
foreach (var galleryDir in galleryDirs)
{
try
{
var identifier = Path.GetFileName(gallery).ToLower();
var identifier = Path.GetFileName(galleryDir).ToLower();
var galleryId = await databaseHelper.GetGalleryId(identifier);
if (galleryId != null)
@@ -90,9 +89,9 @@ namespace WeddingShare.BackgroundWorkers
var allowedFileTypes = settingsHelper.GetOrDefault(Settings.Gallery.AllowedFileTypes, ".jpg,.jpeg,.png,.mp4,.mov", galleryItem?.Id).Result.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
var galleryItems = await databaseHelper.GetAllGalleryItems(galleryItem.Id);
if (Path.Exists(gallery))
if (Path.Exists(galleryDir))
{
var approvedFiles = fileHelper.GetFiles(gallery, "*.*", SearchOption.TopDirectoryOnly).Where(x => allowedFileTypes.Any(y => string.Equals(Path.GetExtension(x).Trim('.'), y.Trim('.'), StringComparison.OrdinalIgnoreCase)));
var approvedFiles = fileHelper.GetFiles(galleryDir, "*.*", SearchOption.TopDirectoryOnly).Where(x => allowedFileTypes.Any(y => string.Equals(Path.GetExtension(x).Trim('.'), y.Trim('.'), StringComparison.OrdinalIgnoreCase)));
if (approvedFiles != null)
{
foreach (var file in approvedFiles)
@@ -179,9 +178,9 @@ namespace WeddingShare.BackgroundWorkers
}
}
if (Path.Exists(Path.Combine(gallery, "Pending")))
if (Path.Exists(Path.Combine(galleryDir, "Pending")))
{
var pendingFiles = fileHelper.GetFiles(Path.Combine(gallery, "Pending"), "*.*", SearchOption.TopDirectoryOnly).Where(x => allowedFileTypes.Any(y => string.Equals(Path.GetExtension(x).Trim('.'), y.Trim('.'), StringComparison.OrdinalIgnoreCase)));
var pendingFiles = fileHelper.GetFiles(Path.Combine(galleryDir, "Pending"), "*.*", SearchOption.TopDirectoryOnly).Where(x => allowedFileTypes.Any(y => string.Equals(Path.GetExtension(x).Trim('.'), y.Trim('.'), StringComparison.OrdinalIgnoreCase)));
if (pendingFiles != null)
{
foreach (var file in pendingFiles)
@@ -216,7 +215,7 @@ namespace WeddingShare.BackgroundWorkers
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occurred while scanning directory '{gallery}'");
logger.LogError(ex, $"An error occurred while scanning directory '{galleryDir}'");
}
}
}

View File

@@ -218,25 +218,14 @@ namespace WeddingShare.Controllers
model.PendingRequests = await GetPendingReviews();
}
else if (tab == AccountTabs.Galleries)
{
if (!await _settings.GetOrDefault(Settings.Basic.SingleGalleryMode, false))
{
model.Galleries = (await _database.GetAllGalleries())?.Where(x => !x.Identifier.Equals("All", StringComparison.OrdinalIgnoreCase))?.ToList();
if (model.Galleries != null)
{
model.Galleries = await _database.GetAllGalleries();
if (model.Galleries != null)
var all = await _database.GetGallery(0);
if (all != null)
{
var all = await _database.GetGallery(0);
if (all != null)
{
model.Galleries.Add(all);
}
}
}
else
{
var gallery = await _database.GetGallery(1);
if (gallery != null)
{
model.Galleries = new List<GalleryModel>() { gallery };
model.Galleries.Add(all);
}
}
}
@@ -283,24 +272,13 @@ namespace WeddingShare.Controllers
var user = await _database.GetUser(User.Identity.GetUserId());
if (user != null)
{
if (!await _settings.GetOrDefault(Settings.Basic.SingleGalleryMode, false))
result = (await _database.GetAllGalleries())?.Where(x => !x.Identifier.Equals("All", StringComparison.OrdinalIgnoreCase))?.ToList();
if (result != null)
{
result = await _database.GetAllGalleries();
if (result != null)
var all = await _database.GetGallery(0);
if (all != null)
{
var all = await _database.GetGallery(0);
if (all != null)
{
result.Add(all);
}
}
}
else
{
var gallery = await _database.GetGallery(1);
if (gallery != null)
{
result = new List<GalleryModel>() { gallery };
result.Add(all);
}
}
}
@@ -481,7 +459,7 @@ namespace WeddingShare.Controllers
var gallery = await _database.GetGallery(review.GalleryId);
if (gallery != null)
{
var galleryDir = Path.Combine(UploadsDirectory, gallery.Name);
var galleryDir = Path.Combine(UploadsDirectory, gallery.Identifier);
var reviewFile = Path.Combine(galleryDir, "Pending", review.Title);
if (action == ReviewAction.APPROVED)
{
@@ -490,7 +468,7 @@ namespace WeddingShare.Controllers
review.State = GalleryItemState.Approved;
await _database.EditGalleryItem(review);
await _audit.LogAction(User?.Identity?.Name, $"'{review.Title}' {_localizer["Audit_ItemApprovedInGallery"].Value} '{gallery.Name}'");
await _audit.LogAction(User?.Identity?.Name, $"'{review.Title}' {_localizer["Audit_ItemApprovedInGallery"].Value} '{gallery.Identifier}'");
}
else if (action == ReviewAction.REJECTED)
{
@@ -508,7 +486,7 @@ namespace WeddingShare.Controllers
await _database.DeleteGalleryItem(review);
await _audit.LogAction(User?.Identity?.Name, $"'{review.Title}' {_localizer["Audit_ItemRejectedInGallery"].Value} '{gallery.Name}'");
await _audit.LogAction(User?.Identity?.Name, $"'{review.Title}' {_localizer["Audit_ItemRejectedInGallery"].Value} '{gallery.Identifier}'");
}
else if (action == ReviewAction.UNKNOWN)
{
@@ -550,7 +528,7 @@ namespace WeddingShare.Controllers
{
foreach (var review in galleryGroup)
{
var galleryDir = Path.Combine(UploadsDirectory, gallery.Name);
var galleryDir = Path.Combine(UploadsDirectory, gallery.Identifier);
var reviewFile = Path.Combine(galleryDir, "Pending", review.Title);
if (action == ReviewAction.APPROVED)
{
@@ -609,7 +587,7 @@ namespace WeddingShare.Controllers
{
try
{
var alreadyExists = (await _database.GetGalleryNames()).Any(x => x.Equals(model.Name, StringComparison.OrdinalIgnoreCase));
var alreadyExists = ((await _database.GetGalleryNames()).Any(x => x.Equals(model.Name, StringComparison.OrdinalIgnoreCase))) || ((await _database.GetGalleryId(model.Identifier)) != null);
if (!alreadyExists)
{
if (await _database.GetGalleryCount() < await _settings.GetOrDefault(Settings.Basic.MaxGalleryCount, 1000000))
@@ -701,12 +679,12 @@ namespace WeddingShare.Controllers
var gallery = await _database.GetGallery(id);
if (gallery != null)
{
var galleryDir = Path.Combine(UploadsDirectory, gallery.Name);
var galleryDir = Path.Combine(UploadsDirectory, gallery.Identifier);
if (_fileHelper.DirectoryExists(galleryDir))
{
foreach (var photo in _fileHelper.GetFiles(galleryDir, "*.*", SearchOption.AllDirectories))
{
var thumbnail = Path.Combine(ThumbnailsDirectory, gallery.Name, $"{Path.GetFileNameWithoutExtension(photo)}.webp");
var thumbnail = Path.Combine(ThumbnailsDirectory, gallery.Identifier, $"{Path.GetFileNameWithoutExtension(photo)}.webp");
_fileHelper.DeleteFileIfExists(thumbnail);
}
@@ -789,7 +767,7 @@ namespace WeddingShare.Controllers
var gallery = await _database.GetGallery(id);
if (gallery != null && gallery.Id > 1)
{
var galleryDir = Path.Combine(UploadsDirectory, gallery.Name);
var galleryDir = Path.Combine(UploadsDirectory, gallery.Identifier);
_fileHelper.DeleteDirectoryIfExists(galleryDir);
if (await _settings.GetOrDefault(Notifications.Alerts.DestructiveAction, true))
@@ -829,7 +807,7 @@ namespace WeddingShare.Controllers
var gallery = await _database.GetGallery(photo.GalleryId);
if (gallery != null)
{
var photoPath = Path.Combine(UploadsDirectory, gallery.Name, photo.Title);
var photoPath = Path.Combine(UploadsDirectory, gallery.Identifier, photo.Title);
_fileHelper.DeleteFileIfExists(photoPath);
await _audit.LogAction(User?.Identity?.Name, $"'{photo?.Title}' {_localizer["Audit_ItemDeletedInGallery"].Value} '{gallery?.Name}'");
@@ -1581,7 +1559,7 @@ namespace WeddingShare.Controllers
{
var galleries = new List<PhotoGallery>();
var items = !await _settings.GetOrDefault(Settings.Basic.SingleGalleryMode, false) ? await _database.GetPendingGalleryItems() : await _database.GetPendingGalleryItems(1);
var items = await _database.GetPendingGalleryItems();
if (items != null)
{
foreach (var galleryGroup in items.GroupBy(x => x.GalleryId))
@@ -1591,8 +1569,7 @@ namespace WeddingShare.Controllers
{
galleries.Add(new PhotoGallery()
{
GalleryId = gallery?.Id,
GalleryName = gallery?.Name,
Gallery = gallery,
Images = items?.Select(x => new PhotoGalleryImage()
{
Id = x.Id,
@@ -1600,8 +1577,8 @@ namespace WeddingShare.Controllers
Name = Path.GetFileName(x.Title),
UploadedBy = x.UploadedBy,
UploadDate = x.UploadedDate,
ImagePath = $"/{Path.Combine(UploadsDirectory, gallery.Name).Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}/Pending/{x.Title}",
ThumbnailPath = $"/{Path.Combine(ThumbnailsDirectory, gallery.Name).Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}/{Path.GetFileNameWithoutExtension(x.Title)}.webp",
ImagePath = $"/{Path.Combine(UploadsDirectory, gallery.Identifier).Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}/Pending/{x.Title}",
ThumbnailPath = $"/{Path.Combine(ThumbnailsDirectory, gallery.Identifier).Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}/{Path.GetFileNameWithoutExtension(x.Title)}.webp",
ThumbnailPathFallback = $"/{ThumbnailsDirectory.Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}/{Path.GetFileNameWithoutExtension(x.Title)}.webp",
MediaType = x.MediaType
})?.ToList(),

View File

@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using Mysqlx.Expr;
using WeddingShare.Attributes;
using WeddingShare.Constants;
using WeddingShare.Enums;
@@ -57,16 +58,20 @@ namespace WeddingShare.Controllers
}
[HttpPost]
public async Task<IActionResult> Login(string id = "default", string? key = null)
public async Task<IActionResult> Login(string? id, string? identifier, string? key = null)
{
id = id.Trim();
var append = new List<KeyValuePair<string, string>>()
int? galleryId = 0;
if (!string.IsNullOrWhiteSpace(identifier))
{
new KeyValuePair<string, string>("id", id)
};
galleryId = await _database.GetGalleryId(identifier);
}
else if (!string.IsNullOrWhiteSpace(id))
{
galleryId = await _database.GetGalleryIdByName(id);
}
var galleryId = await _database.GetGalleryId(id);
GalleryModel? gallery = await _database.GetGallery(galleryId.Value);
if (gallery == null)
{
@@ -74,9 +79,9 @@ namespace WeddingShare.Controllers
{
if (await _database.GetGalleryCount() < await _settings.GetOrDefault(Settings.Basic.MaxGalleryCount, 1000000))
{
await _database.AddGallery(new GalleryModel()
gallery = await _database.AddGallery(new GalleryModel()
{
Name = id.ToLower(),
Name = id?.ToLower() ?? GalleryHelper.GenerateGalleryIdentifier(),
SecretKey = key,
Owner = 0
});
@@ -92,6 +97,11 @@ namespace WeddingShare.Controllers
}
}
var append = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("identifier", gallery.Identifier)
};
if (!string.IsNullOrWhiteSpace(key))
{
var enc = _encryptionHelper.IsEncryptionEnabled();
@@ -107,10 +117,18 @@ namespace WeddingShare.Controllers
[HttpGet]
[RequiresSecretKey]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public async Task<IActionResult> Index(string id = "default", string? key = null, ViewMode? mode = null, GalleryGroup group = GalleryGroup.None, GalleryOrder order = GalleryOrder.Descending, GalleryFilter filter = GalleryFilter.All, string? culture = null, bool partial = false)
public async Task<IActionResult> Index(string? id, string? identifier, string? key = null, ViewMode? mode = null, GalleryGroup group = GalleryGroup.None, GalleryOrder order = GalleryOrder.Descending, GalleryFilter filter = GalleryFilter.All, string? culture = null, bool partial = false)
{
id = (!string.IsNullOrWhiteSpace(id) && !await _settings.GetOrDefault(Settings.Basic.SingleGalleryMode, false)) ? id.Trim().ToLower() : "default";
var galleryId = id.Equals("All", StringComparison.OrdinalIgnoreCase) ? 0 : await _database.GetGalleryId(id);
int? galleryId = null;
if (!string.IsNullOrWhiteSpace(identifier))
{
galleryId = await _database.GetGalleryId(identifier);
}
else if (!string.IsNullOrWhiteSpace(id))
{
galleryId = await _database.GetGalleryIdByName(id);
}
if (galleryId != null)
{
@@ -146,14 +164,14 @@ namespace WeddingShare.Controllers
ViewBag.IsMobile = !string.Equals("Desktop", deviceType, StringComparison.OrdinalIgnoreCase);
var galleryPath = Path.Combine(UploadsDirectory, id);
_fileHelper.CreateDirectoryIfNotExists(galleryPath);
_fileHelper.CreateDirectoryIfNotExists(Path.Combine(galleryPath, "Pending"));
GalleryModel? gallery = await _database.GetGallery(galleryId.Value);
if (gallery != null)
{
ViewBag.GalleryId = gallery.Name;
var galleryPath = Path.Combine(UploadsDirectory, gallery.Identifier);
_fileHelper.CreateDirectoryIfNotExists(galleryPath);
_fileHelper.CreateDirectoryIfNotExists(Path.Combine(galleryPath, "Pending"));
ViewBag.GalleryIdentifier = gallery.Identifier;
var secretKey = await _settings.GetOrDefault(Settings.Gallery.SecretKey, string.Empty, gallery.Id);
ViewBag.SecretKey = secretKey;
@@ -210,7 +228,7 @@ namespace WeddingShare.Controllers
var userPermissions = User?.Identity?.GetUserPermissions() ?? AccessPermissions.None;
var isGalleryAdmin = User?.Identity != null && User.Identity.IsAuthenticated && userPermissions.HasFlag(AccessPermissions.Gallery_Upload);
var uploadActvated = !string.Equals("All", gallery?.Name, StringComparison.OrdinalIgnoreCase) && (await _settings.GetOrDefault(Settings.Gallery.Upload, true, gallery?.Id) || isGalleryAdmin);
var uploadActvated = !gallery.Identifier.Equals("All", StringComparison.OrdinalIgnoreCase) && (await _settings.GetOrDefault(Settings.Gallery.Upload, true, gallery?.Id) || isGalleryAdmin);
if (uploadActvated)
{
try
@@ -255,8 +273,7 @@ namespace WeddingShare.Controllers
var itemCounts = await _database.GetGalleryItemCount(gallery?.Id, GalleryItemState.All, mediaType, orientation);
var model = new PhotoGallery()
{
GalleryId = gallery?.Id,
GalleryName = gallery?.Name,
Gallery = gallery,
SecretKey = secretKey,
Images = items?.Select(x => new PhotoGalleryImage()
{
@@ -265,8 +282,8 @@ namespace WeddingShare.Controllers
Name = Path.GetFileName(x.Title),
UploadedBy = x.UploadedBy,
UploadDate = x.UploadedDate,
ImagePath = $"/{Path.Combine(UploadsDirectory, gallery.Name).Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}/{x.Title}",
ThumbnailPath = $"/{Path.Combine(ThumbnailsDirectory, gallery.Name).Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}/{Path.GetFileNameWithoutExtension(x.Title)}.webp",
ImagePath = $"/{Path.Combine(UploadsDirectory, gallery.Identifier).Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}/{x.Title}",
ThumbnailPath = $"/{Path.Combine(ThumbnailsDirectory, gallery.Identifier).Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}/{Path.GetFileNameWithoutExtension(x.Title)}.webp",
ThumbnailPathFallback = $"/{ThumbnailsDirectory.Remove(_hostingEnvironment.WebRootPath).Replace('\\', '/').TrimStart('/')}/{Path.GetFileNameWithoutExtension(x.Title)}.webp",
MediaType = x.MediaType
})?.ToList(),
@@ -327,7 +344,7 @@ namespace WeddingShare.Controllers
var extension = Path.GetExtension(file.FileName);
var maxGallerySize = await _settings.GetOrDefault(Settings.Gallery.MaxSizeMB, 1024L, gallery.Id) * 1000000;
var maxFilesSize = await _settings.GetOrDefault(Settings.Gallery.MaxFileSizeMB, 50L, gallery.Id) * 1000000;
var galleryPath = Path.Combine(UploadsDirectory, gallery.Name);
var galleryPath = Path.Combine(UploadsDirectory, gallery.Identifier);
var allowedFileTypes = (await _settings.GetOrDefault(Settings.Gallery.AllowedFileTypes, ".jpg,.jpeg,.png,.mp4,.mov", gallery.Id)).Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
if (!allowedFileTypes.Any(x => string.Equals(x.Trim('.'), extension.Trim('.'), StringComparison.OrdinalIgnoreCase)))
@@ -370,7 +387,7 @@ namespace WeddingShare.Controllers
}
else
{
var gallerySavePath = Path.Combine(ThumbnailsDirectory, gallery.Name);
var gallerySavePath = Path.Combine(ThumbnailsDirectory, gallery.Identifier);
_fileHelper.CreateDirectoryIfNotExists(ThumbnailsDirectory);
_fileHelper.CreateDirectoryIfNotExists(gallerySavePath);
@@ -493,7 +510,7 @@ namespace WeddingShare.Controllers
if (await _settings.GetOrDefault(Settings.Gallery.Download, true, gallery?.Id) || (User?.Identity != null && User.Identity.IsAuthenticated))
{
var galleryDir = id > 0 ? Path.Combine(UploadsDirectory, gallery.Name) : UploadsDirectory;
var galleryDir = id > 0 ? Path.Combine(UploadsDirectory, gallery.Identifier) : UploadsDirectory;
if (_fileHelper.DirectoryExists(galleryDir))
{
var keepFiles = new List<string>();
@@ -553,7 +570,7 @@ namespace WeddingShare.Controllers
_fileHelper.CreateDirectoryIfNotExists(TempDirectory);
var tempZipFile = Path.Combine(TempDirectory, $"{gallery.Name}-{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.zip");
var tempZipFile = Path.Combine(TempDirectory, $"{gallery.Identifier}-{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.zip");
ZipFile.CreateFromDirectory(galleryDir, tempZipFile, CompressionLevel.Optimal, false);
if (User?.Identity == null || !User.Identity.IsAuthenticated)

View File

@@ -54,7 +54,7 @@ namespace WeddingShare.Controllers
}
}
model.GalleryNames = await _settings.GetOrDefault(Settings.GallerySelector.Dropdown, false) ? await _database.GetGalleryNames() : new List<string>() { "default" };
model.GalleryNames = await _settings.GetOrDefault(Settings.GallerySelector.Dropdown, false) ? (await _database.GetGalleryNames()).Where(x => !x.Equals("all", StringComparison.OrdinalIgnoreCase)) : new List<string>();
if (await _settings.GetOrDefault(Settings.GallerySelector.HideDefaultOption, false))
{
model.GalleryNames = model.GalleryNames.Where(x => !x.Equals("default", StringComparison.OrdinalIgnoreCase));

View File

@@ -8,7 +8,8 @@ namespace WeddingShare.Helpers.Database
Task<int> GetGalleryCount();
Task<IEnumerable<string>> GetGalleryNames();
Task<List<GalleryModel>> GetAllGalleries();
Task<int?> GetGalleryId(string name);
Task<int?> GetGalleryId(string identifier);
Task<int?> GetGalleryIdByName(string name);
Task<string?> GetGalleryName(int id);
Task<GalleryModel?> GetGallery(int id);
Task<GalleryModel?> AddGallery(GalleryModel model);

View File

@@ -166,7 +166,7 @@ namespace WeddingShare.Helpers.Database
return result;
}
public async Task<int?> GetGalleryId(string? name)
public async Task<int?> GetGalleryIdByName(string? name)
{
int? result = null;
@@ -187,6 +187,27 @@ namespace WeddingShare.Helpers.Database
return result;
}
public async Task<int?> GetGalleryId(string identifier)
{
int? result = null;
if (!string.IsNullOrWhiteSpace(identifier))
{
using (var conn = await GetConnection())
{
var cmd = CreateCommand($"SELECT g.`id` FROM `galleries` AS g WHERE UPPER(g.`identifier`)=UPPER(@Identifier);", conn);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("Identifier", identifier);
await conn.OpenAsync();
result = (int?)(long?)await cmd.ExecuteScalarAsync();
await conn.CloseAsync();
}
}
return result;
}
public async Task<string?> GetGalleryName(int id)
{
return (await this.GetGallery(id))?.Name;
@@ -220,12 +241,14 @@ namespace WeddingShare.Helpers.Database
result = new GalleryModel()
{
Id = 0,
Identifier = "all",
Name = "all",
SecretKey = null,
TotalItems = galleries?.Sum(x => x.TotalItems) ?? 0,
ApprovedItems = galleries?.Sum(x => x.ApprovedItems) ?? 0,
PendingItems = galleries?.Sum(x => x.PendingItems) ?? 0,
TotalGallerySize = galleries?.Sum(x => x.TotalGallerySize) ?? 0
TotalGallerySize = galleries?.Sum(x => x.TotalGallerySize) ?? 0,
Owner = 1
};
}
}
@@ -241,8 +264,9 @@ namespace WeddingShare.Helpers.Database
using (var conn = await GetConnection())
{
var cmd = CreateCommand($"INSERT INTO `galleries` (`name`, `secret_key`, `owner`) VALUES (@Name, @SecretKey, @Owner); SELECT g.*, COUNT(gi.`id`) AS `total`, SUM(CASE WHEN gi.`state`=@ApprovedState THEN 1 ELSE 0 END) AS `approved`, SUM(CASE WHEN gi.`state`=@PendingState THEN 1 ELSE 0 END) AS `pending`, SUM(gi.file_size) AS `total_gallery_size` FROM `galleries` AS g LEFT JOIN `gallery_items` AS gi ON g.`id` = gi.`gallery_id` WHERE g.`id`=LAST_INSERT_ID();", conn);
var cmd = CreateCommand($"INSERT INTO `galleries` (`identifier`, `name`, `secret_key`, `owner`) VALUES (@Identifier, @Name, @SecretKey, @Owner); SELECT g.*, COUNT(gi.`id`) AS `total`, SUM(CASE WHEN gi.`state`=@ApprovedState THEN 1 ELSE 0 END) AS `approved`, SUM(CASE WHEN gi.`state`=@PendingState THEN 1 ELSE 0 END) AS `pending`, SUM(gi.file_size) AS `total_gallery_size` FROM `galleries` AS g LEFT JOIN `gallery_items` AS gi ON g.`id` = gi.`gallery_id` WHERE g.`id`=LAST_INSERT_ID();", conn);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("Identifier", model.Identifier);
cmd.Parameters.AddWithValue("Name", model.Name.ToLower());
cmd.Parameters.AddWithValue("SecretKey", !string.IsNullOrWhiteSpace(model.SecretKey) ? model.SecretKey : DBNull.Value);
cmd.Parameters.AddWithValue("ApprovedState", (int)GalleryItemState.Approved);
@@ -1318,7 +1342,7 @@ namespace WeddingShare.Helpers.Database
public async Task<SettingModel?> GetSetting(string id, string gallery)
{
return await GetSetting(id, await this.GetGalleryId(gallery) ?? 0);
return await GetSetting(id, await this.GetGalleryIdByName(gallery) ?? 0);
}
public async Task<SettingModel?> GetSetting(string id, int galleryId)
@@ -1693,21 +1717,18 @@ namespace WeddingShare.Helpers.Database
{
try
{
var id = !await reader.IsDBNullAsync("id") ? reader.GetInt32("id") : 0;
if (id > 0)
items.Add(new GalleryModel()
{
items.Add(new GalleryModel()
{
Id = id,
Name = !await reader.IsDBNullAsync("name") ? reader.GetString("name") : "Unknown",
SecretKey = !await reader.IsDBNullAsync("secret_key") ? reader.GetString("secret_key") : null,
TotalItems = !await reader.IsDBNullAsync("total") ? reader.GetInt32("total") : 0,
ApprovedItems = !await reader.IsDBNullAsync("approved") ? reader.GetInt32("approved") : 0,
PendingItems = !await reader.IsDBNullAsync("pending") ? reader.GetInt32("pending") : 0,
TotalGallerySize = !await reader.IsDBNullAsync("total_gallery_size") ? reader.GetInt64("total_gallery_size") : 0,
Owner = !await reader.IsDBNullAsync("owner") ? reader.GetInt32("owner") : 0
});
}
Id = !await reader.IsDBNullAsync("id") ? reader.GetInt32("id") : 0,
Identifier = !await reader.IsDBNullAsync("identifier") ? reader.GetString("identifier") : GalleryHelper.GenerateGalleryIdentifier(),
Name = !await reader.IsDBNullAsync("name") ? reader.GetString("name") : "Unknown",
SecretKey = !await reader.IsDBNullAsync("secret_key") ? reader.GetString("secret_key") : null,
TotalItems = !await reader.IsDBNullAsync("total") ? reader.GetInt32("total") : 0,
ApprovedItems = !await reader.IsDBNullAsync("approved") ? reader.GetInt32("approved") : 0,
PendingItems = !await reader.IsDBNullAsync("pending") ? reader.GetInt32("pending") : 0,
TotalGallerySize = !await reader.IsDBNullAsync("total_gallery_size") ? reader.GetInt64("total_gallery_size") : 0,
Owner = !await reader.IsDBNullAsync("owner") ? reader.GetInt32("owner") : 0
});
}
catch (Exception ex)
{

View File

@@ -124,7 +124,7 @@ namespace WeddingShare.Helpers.Database
return result;
}
public async Task<int?> GetGalleryId(string? name)
public async Task<int?> GetGalleryIdByName(string? name)
{
int? result = null;
@@ -145,6 +145,27 @@ namespace WeddingShare.Helpers.Database
return result;
}
public async Task<int?> GetGalleryId(string identifier)
{
int? result = null;
if (!string.IsNullOrWhiteSpace(identifier))
{
using (var conn = await GetConnection())
{
var cmd = CreateCommand($"SELECT g.`id` FROM `galleries` AS g WHERE UPPER(g.`identifier`)=UPPER(@Identifier);", conn);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("Identifier", identifier);
await conn.OpenAsync();
result = (int?)(long?)await cmd.ExecuteScalarAsync();
await conn.CloseAsync();
}
}
return result;
}
public async Task<string?> GetGalleryName(int id)
{
return (await this.GetGallery(id))?.Name;
@@ -178,12 +199,14 @@ namespace WeddingShare.Helpers.Database
result = new GalleryModel()
{
Id = 0,
Identifier = "all",
Name = "all",
SecretKey = null,
TotalItems = galleries?.Sum(x => x.TotalItems) ?? 0,
ApprovedItems = galleries?.Sum(x => x.ApprovedItems) ?? 0,
PendingItems = galleries?.Sum(x => x.PendingItems) ?? 0,
TotalGallerySize = galleries?.Sum(x => x.TotalGallerySize) ?? 0
TotalGallerySize = galleries?.Sum(x => x.TotalGallerySize) ?? 0,
Owner = 1
};
}
}
@@ -205,8 +228,9 @@ namespace WeddingShare.Helpers.Database
using (var conn = await GetConnection())
{
var cmd = CreateCommand($"INSERT INTO `galleries` (`name`, `secret_key`, `owner`) VALUES (@Name, @SecretKey, @Owner); SELECT g.*, COUNT(gi.`id`) AS `total`, SUM(CASE WHEN gi.`state`=@ApprovedState THEN 1 ELSE 0 END) AS `approved`, SUM(CASE WHEN gi.`state`=@PendingState THEN 1 ELSE 0 END) AS `pending`, SUM(gi.file_size) AS `total_gallery_size` FROM `galleries` AS g LEFT JOIN `gallery_items` AS gi ON g.`id` = gi.`gallery_id` WHERE g.`id`=last_insert_rowid();", conn);
var cmd = CreateCommand($"INSERT INTO `galleries` (`identifier`, `name`, `secret_key`, `owner`) VALUES (@Identifier, @Name, @SecretKey, @Owner); SELECT g.*, COUNT(gi.`id`) AS `total`, SUM(CASE WHEN gi.`state`=@ApprovedState THEN 1 ELSE 0 END) AS `approved`, SUM(CASE WHEN gi.`state`=@PendingState THEN 1 ELSE 0 END) AS `pending`, SUM(gi.file_size) AS `total_gallery_size` FROM `galleries` AS g LEFT JOIN `gallery_items` AS gi ON g.`id` = gi.`gallery_id` WHERE g.`id`=last_insert_rowid();", conn);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("Identifier", model.Identifier);
cmd.Parameters.AddWithValue("Name", model.Name.ToLower());
cmd.Parameters.AddWithValue("SecretKey", !string.IsNullOrWhiteSpace(model.SecretKey) ? model.SecretKey : DBNull.Value);
cmd.Parameters.AddWithValue("ApprovedState", (int)GalleryItemState.Approved);
@@ -1700,21 +1724,18 @@ namespace WeddingShare.Helpers.Database
{
try
{
var id = !await reader.IsDBNullAsync("id") ? reader.GetInt32("id") : 0;
if (id > 0)
{
items.Add(new GalleryModel()
{
Id = id,
Name = !await reader.IsDBNullAsync("name") ? reader.GetString("name") : "Unknown",
SecretKey = !await reader.IsDBNullAsync("secret_key") ? reader.GetString("secret_key") : null,
TotalItems = !await reader.IsDBNullAsync("total") ? reader.GetInt32("total") : 0,
ApprovedItems = !await reader.IsDBNullAsync("approved") ? reader.GetInt32("approved") : 0,
PendingItems = !await reader.IsDBNullAsync("pending") ? reader.GetInt32("pending") : 0,
TotalGallerySize = !await reader.IsDBNullAsync("total_gallery_size") ? reader.GetInt64("total_gallery_size") : 0,
Owner = !await reader.IsDBNullAsync("owner") ? reader.GetInt32("owner") : 0
});
}
items.Add(new GalleryModel()
{
Id = !await reader.IsDBNullAsync("id") ? reader.GetInt32("id") : 0,
Identifier = !await reader.IsDBNullAsync("identifier") ? reader.GetString("identifier") : GalleryHelper.GenerateGalleryIdentifier(),
Name = !await reader.IsDBNullAsync("name") ? reader.GetString("name") : "Unknown",
SecretKey = !await reader.IsDBNullAsync("secret_key") ? reader.GetString("secret_key") : null,
TotalItems = !await reader.IsDBNullAsync("total") ? reader.GetInt32("total") : 0,
ApprovedItems = !await reader.IsDBNullAsync("approved") ? reader.GetInt32("approved") : 0,
PendingItems = !await reader.IsDBNullAsync("pending") ? reader.GetInt32("pending") : 0,
TotalGallerySize = !await reader.IsDBNullAsync("total_gallery_size") ? reader.GetInt64("total_gallery_size") : 0,
Owner = !await reader.IsDBNullAsync("owner") ? reader.GetInt32("owner") : 0
});
}
catch (Exception ex)
{

View File

@@ -32,7 +32,7 @@ namespace WeddingShare.Helpers.Dbup
catch { }
}
var galleries = await database.GetAllGalleries();
var galleries = (await database.GetAllGalleries())?.Where(x => !x.Identifier.Equals("All", StringComparison.OrdinalIgnoreCase));
if (galleries != null && galleries.Any())
{
var galleryKeys = GetKeys<Constants.Settings.Gallery>();

View File

@@ -0,0 +1,10 @@
namespace WeddingShare.Helpers
{
public class GalleryHelper
{
public static string GenerateGalleryIdentifier()
{
return Guid.NewGuid().ToString().Replace("-", string.Empty).ToLower();
}
}
}

View File

@@ -1,8 +1,11 @@
namespace WeddingShare.Models.Database
using WeddingShare.Helpers;
namespace WeddingShare.Models.Database
{
public class GalleryModel
{
public int Id { get; set; }
public string Identifier { get; set; } = GalleryHelper.GenerateGalleryIdentifier();
public string Name { get; set; } = "Unknown";
public string? SecretKey { get; set; }
public int TotalItems { get; set; }

View File

@@ -1,4 +1,5 @@
using WeddingShare.Enums;
using WeddingShare.Models.Database;
namespace WeddingShare.Models
{
@@ -10,14 +11,13 @@ namespace WeddingShare.Models
}
public PhotoGallery(ViewMode viewMode, GalleryGroup groupBy, GalleryOrder orderBy)
: this(1, "default", string.Empty, viewMode, groupBy, orderBy, new List<PhotoGalleryImage>(), false)
: this(null, string.Empty, viewMode, groupBy, orderBy, new List<PhotoGalleryImage>(), false)
{
}
public PhotoGallery(int id, string name, string secretKey, ViewMode viewMode, GalleryGroup groupBy, GalleryOrder orderBy, List<PhotoGalleryImage> images, bool uploadActivated)
public PhotoGallery(GalleryModel? gallery, string secretKey, ViewMode viewMode, GalleryGroup groupBy, GalleryOrder orderBy, List<PhotoGalleryImage> images, bool uploadActivated)
{
this.GalleryId = id;
this.GalleryName = name;
this.Gallery = gallery;
this.SecretKey = secretKey;
this.ViewMode = viewMode;
this.GroupBy = groupBy;
@@ -27,8 +27,7 @@ namespace WeddingShare.Models
this.UploadActivated = uploadActivated;
}
public int? GalleryId { get; set; }
public string? GalleryName { get; set; }
public GalleryModel? Gallery { get; set; }
public string? SecretKey { get; set; }
public ViewMode ViewMode { get; set; }
public GalleryGroup GroupBy { get; set; }

View File

@@ -21,6 +21,7 @@ CREATE TABLE `gallery_items` (
INSERT INTO `galleries`
(`id`, `name`, `secret_key`)
VALUES
(0, 'all', NULL),
(1, 'default', NULL);
--

View File

@@ -0,0 +1,6 @@
--
-- Add column `identifier` to `galleries`
--
ALTER TABLE `galleries` DROP `identifier`;
ALTER TABLE `galleries` ADD `identifier` VARCHAR(50) NOT NULL UNIQUE DEFAULT '';
UPDATE `galleries` SET `identifier`=`name`;

View File

@@ -0,0 +1,12 @@
--
-- DROP "all" gallery if exists
--
DELETE FROM `galleries` WHERE `id`=0;
--
-- INSERT "all" gallery
--
INSERT INTO `galleries`
(`id`, `identifier`, `name`, `secret_key`, `owner`)
VALUES
(0, 'all', 'all', NULL, 0);

View File

@@ -21,6 +21,7 @@ CREATE TABLE `gallery_items` (
INSERT INTO `galleries`
(`id`, `name`, `secret_key`)
VALUES
(0, 'all', NULL),
(1, 'default', NULL);
--

View File

@@ -0,0 +1,5 @@
--
-- Add column `identifier` to `galleries`
--
ALTER TABLE `galleries` ADD `identifier` TEXT NOT NULL DEFAULT '';
UPDATE `galleries` SET `identifier`=`name`;

View File

@@ -0,0 +1,12 @@
--
-- DROP "all" gallery if exists
--
DELETE FROM `galleries` WHERE `id`=0;
--
-- INSERT "all" gallery
--
INSERT INTO `galleries`
(`id`, `identifier`, `name`, `secret_key`, `owner`)
VALUES
(0, 'all', 'all', NULL, 0);

View File

@@ -22,11 +22,11 @@
<th class="col-2 d-none d-md-table-cell">@_localizer["Space_Percent_Used"].Value</th>
<th class="col-7 col-md-3">@_localizer["Actions"].Value</th>
</tr>
@foreach (var gallery in Model.OrderBy(x => string.Equals("All", x.Name, StringComparison.OrdinalIgnoreCase) ? 0 : string.Equals("default", x.Name, StringComparison.OrdinalIgnoreCase) ? 1 : 2))
@foreach (var gallery in Model.OrderBy(x => x.Identifier.Equals("All", StringComparison.OrdinalIgnoreCase) ? 0 : 1).ThenBy(x => x.Name.ToLower()))
{
var append = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("id", gallery.Name)
new KeyValuePair<string, string>("identifier", gallery.Identifier)
};
var secretKey = await _settings.GetOrDefault(Settings.Gallery.SecretKey, string.Empty, gallery.Id);
@@ -46,7 +46,7 @@
<i class="fa-solid @(!string.IsNullOrWhiteSpace(secretKey) ? "fa-lock" : "fa-lock-open") m-0"></i>
</td>
<td class="d-none d-md-table-cell text-center">
@(gallery.Name.Equals("all", StringComparison.OrdinalIgnoreCase) ? _file.BytesToHumanReadable(gallery.TotalGallerySize, 2) : $"{_file.BytesToHumanReadable(gallery.TotalGallerySize, 2)} ({gallery.CalculateUsage(await _settings.GetOrDefault(Settings.Gallery.MaxSizeMB, long.MaxValue, gallery?.Id))})")
@(gallery.Identifier.Equals("All", StringComparison.OrdinalIgnoreCase) ? _file.BytesToHumanReadable(gallery.TotalGallerySize, 2) : $"{_file.BytesToHumanReadable(gallery.TotalGallerySize, 2)} ({gallery.CalculateUsage(await _settings.GetOrDefault(Settings.Gallery.MaxSizeMB, long.MaxValue, gallery?.Id))})")
</td>
<td>
@if (userPermissions.HasFlag(AccessPermissions.Gallery_View))

View File

@@ -41,7 +41,7 @@
@if (identityEnabled)
{
<div class="col-12 col-lg-6 text-lg-center">
<b>@_localizer["Gallery"].Value:</b> @gallery.GalleryName
<b>@_localizer["Gallery"].Value:</b> @gallery.Gallery.Name
</div>
<div class="col-12 col-lg-6 text-lg-center">
<b>@_localizer["Uploader"].Value:</b> @(!string.IsNullOrWhiteSpace(review?.UploadedBy) ? review.UploadedBy : "Anonymous")
@@ -50,7 +50,7 @@
else
{
<div class="col-12">
<b>@_localizer["Gallery"].Value:</b> @gallery.GalleryName
<b>@_localizer["Gallery"].Value:</b> @gallery.Gallery.Name
</div>
}
</div>

View File

@@ -7,8 +7,8 @@
@{
var userPermissions = User?.Identity?.GetUserPermissions() ?? AccessPermissions.None;
var qrCodeEnabled = await _settings.GetOrDefault(Settings.Gallery.QRCode.Enabled, true, Model?.GalleryId);
var showFilters = await _settings.GetOrDefault(Settings.Gallery.ShowFilters, true, Model?.GalleryId);
var qrCodeEnabled = await _settings.GetOrDefault(Settings.Gallery.QRCode.Enabled, true, Model?.Gallery?.Id);
var showFilters = await _settings.GetOrDefault(Settings.Gallery.ShowFilters, true, Model?.Gallery?.Id);
}
@if (qrCodeEnabled)
@@ -36,10 +36,10 @@
{
<div class="row px-4 mb-4 presentation-hidden">
<div class="col-12 px-2 px-xl-0">
@if (User?.Identity != null && User.Identity.IsAuthenticated && userPermissions.HasFlag(AccessPermissions.Settings_Gallery_Update) && !(Model?.GalleryName ?? "Default").Equals("All", StringComparison.OrdinalIgnoreCase))
@if (User?.Identity != null && User.Identity.IsAuthenticated && userPermissions.HasFlag(AccessPermissions.Settings_Gallery_Update) && !Model.Gallery.Identifier.Equals("All", StringComparison.OrdinalIgnoreCase))
{
<div class="btn-group w-100 mb-2">
<button class="btnGallerySettings btn btn-danger btn-sm" type="button" data-gallery-name="@Model?.GalleryName" data-gallery-id="@Model?.GalleryId">
<button class="btnGallerySettings btn btn-danger btn-sm" type="button" data-gallery-name="@Model?.Gallery?.Name" data-gallery-id="@Model?.Gallery?.Id">
@_localizer["Settings"].Value
</button>
</div>
@@ -50,7 +50,7 @@
@if (qrCodeEnabled)
{
<div class="btn-group w-100 mb-2">
<button class="btnSaveQRCode btn btn-primary btn-sm" type="button" data-gallery-name="@Model?.GalleryName">
<button class="btnSaveQRCode btn btn-primary btn-sm" type="button" data-gallery-name="@Model?.Gallery?.Name">
@_localizer["Save_QR"].Value
</button>
</div>
@@ -120,10 +120,10 @@
</div>
</div>
@if (await _settings.GetOrDefault(Settings.Gallery.Download, true, Model?.GalleryId) || (User?.Identity != null && User.Identity.IsAuthenticated))
@if (await _settings.GetOrDefault(Settings.Gallery.Download, true, Model?.Gallery?.Id) || (User?.Identity != null && User.Identity.IsAuthenticated))
{
<div class="btn-group w-100 mb-2">
<button class="btnDownloadGallery btn btn-primary btn-sm" type="button" data-gallery-id="@Model?.GalleryId" data-gallery-key="@(await _settings.GetOrDefault(Settings.Gallery.SecretKey, string.Empty, Model?.GalleryId))">
<button class="btnDownloadGallery btn btn-primary btn-sm" type="button" data-gallery-id="@Model?.Gallery?.Id" data-gallery-key="@(await _settings.GetOrDefault(Settings.Gallery.SecretKey, string.Empty, Model?.Gallery?.Id))">
@_localizer["Download"].Value
</button>
</div>

View File

@@ -5,14 +5,14 @@
@if (Model.ViewMode != ViewMode.Slideshow)
{
var galleryName = (!string.IsNullOrWhiteSpace(Model?.GalleryName) && !Model.GalleryName.Equals("all", StringComparison.OrdinalIgnoreCase)) ? Model.GalleryName.ToLower() : "default";
var galleryName = (!string.IsNullOrWhiteSpace(Model?.Gallery?.Identifier) && !Model.Gallery.Identifier.Equals("All", StringComparison.OrdinalIgnoreCase)) ? Model.Gallery.Name.ToLower() : "default";
string banner;
try
{
var bannerGallery = _config.Get($"{Settings.Gallery.BannerImage}_{galleryName}") != null ? galleryName : "default";
banner = await _settings.GetOrDefault(Settings.Gallery.BannerImage, string.Empty, Model?.GalleryId);
banner = await _settings.GetOrDefault(Settings.Gallery.BannerImage, string.Empty, Model?.Gallery?.Id);
if (!string.IsNullOrWhiteSpace(banner))
{
if (!banner.StartsWith("/images", StringComparison.OrdinalIgnoreCase) && !banner.StartsWith("/banners", StringComparison.OrdinalIgnoreCase) && !banner.StartsWith("/custom_resources", StringComparison.OrdinalIgnoreCase))
@@ -32,7 +32,7 @@
}
catch { }
var quote = await _settings.GetOrDefault(Settings.Gallery.Quote, string.Empty, Model?.GalleryId);
var quote = await _settings.GetOrDefault(Settings.Gallery.Quote, string.Empty, Model?.Gallery?.Id);
if (!string.IsNullOrWhiteSpace(quote))
{
<div class="row mb-3 mb-lg-4 border rounded">
@@ -42,7 +42,7 @@
</div>
}
if (await _settings.GetOrDefault(Settings.Gallery.ReviewCounter, true, Model?.GalleryId))
if (await _settings.GetOrDefault(Settings.Gallery.ReviewCounter, true, Model?.Gallery?.Id))
{
<div class="row mb-3 mb-lg-4 review-counter">
<div class="col-4">

View File

@@ -7,19 +7,19 @@
@{
var qrAppend = new List<KeyValuePair<string, string>>();
var qrDefaultView = await _settings.GetOrDefault(Settings.Gallery.QRCode.DefaultView, string.Empty, Model?.GalleryId);
var qrDefaultView = await _settings.GetOrDefault(Settings.Gallery.QRCode.DefaultView, string.Empty, Model?.Gallery?.Id);
if (!string.IsNullOrWhiteSpace(qrDefaultView))
{
qrAppend.Add(new KeyValuePair<string, string>("mode", qrDefaultView));
}
var qrDefaultOrder = await _settings.GetOrDefault(Settings.Gallery.QRCode.DefaultSort, string.Empty, Model?.GalleryId);
var qrDefaultOrder = await _settings.GetOrDefault(Settings.Gallery.QRCode.DefaultSort, string.Empty, Model?.Gallery?.Id);
if (!string.IsNullOrWhiteSpace(qrDefaultOrder))
{
qrAppend.Add(new KeyValuePair<string, string>("order", qrDefaultOrder));
}
if (await _settings.GetOrDefault(Settings.Gallery.QRCode.IncludeCulture, true, Model?.GalleryId))
if (await _settings.GetOrDefault(Settings.Gallery.QRCode.IncludeCulture, true, Model?.Gallery?.Id))
{
try
{
@@ -44,7 +44,7 @@
@if (Model?.UploadActivated != null && Model.UploadActivated && Model.ViewMode != ViewMode.Presentation && Model.ViewMode != ViewMode.Slideshow)
{
var allowedFileTypes = (await _settings.GetOrDefault(Settings.Gallery.AllowedFileTypes, ".jpg,.jpeg,.png,.mp4,.mov", Model?.GalleryId))?.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)?.Select(x => $".{x.Trim('.')}")?.OrderBy(x => x)?.ToList() ?? new List<string>();
var allowedFileTypes = (await _settings.GetOrDefault(Settings.Gallery.AllowedFileTypes, ".jpg,.jpeg,.png,.mp4,.mov", Model?.Gallery?.Id))?.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)?.Select(x => $".{x.Trim('.')}")?.OrderBy(x => x)?.ToList() ?? new List<string>();
var identityRequired = await _settings.GetOrDefault(Settings.IdentityCheck.RequireIdentityForUpload, false);
<header class="file-uploader bg-dark py-1 py-lg-4">
@@ -61,7 +61,7 @@
<p class="small my-2">@(ViewBag.IsMobile ?? false ? _localizer["Click_To_Upload"].Value : _localizer["Drag_And_Drop"].Value)</p>
<p class="small my-2">(@string.Join(", ", allowedFileTypes).ToLower())</p>
<input class="position-absolute invisible upload-input" data-post-gallery-id="@Model?.GalleryId" data-post-url="/Gallery/UploadImage" data-post-key="@Model?.SecretKey" data-post-allow-camera="@(((ViewBag.IsMobile ?? false) && await _settings.GetOrDefault(Settings.Gallery.CameraUploads, false)).ToString().ToLower())" type="file" multiple accept="image/*,video/*" />
<input class="position-absolute invisible upload-input" data-post-gallery-id="@Model?.Gallery?.Id" data-post-url="/Gallery/UploadImage" data-post-key="@Model?.SecretKey" data-post-allow-camera="@(((ViewBag.IsMobile ?? false) && await _settings.GetOrDefault(Settings.Gallery.CameraUploads, false)).ToString().ToLower())" type="file" multiple accept="image/*,video/*" />
</fieldset>
</form>
</div>
@@ -78,7 +78,7 @@
<section class="py-1 py-lg-4">
<div class="row mt-3 mt-lg-0">
@if (await _settings.GetOrDefault(Settings.Gallery.FullWidth, false, Model?.GalleryId))
@if (await _settings.GetOrDefault(Settings.Gallery.FullWidth, false, Model?.Gallery?.Id))
{
<div id="main-gallery" class="col-12 px-3 px-lg-5">
<partial name="~/Views/Gallery/GalleryWrapper.cshtml" model="Model" />
@@ -97,10 +97,10 @@
</div>
</section>
@if (await _settings.GetOrDefault(Settings.Gallery.FullWidth, false, Model?.GalleryId) && User?.Identity != null && User.Identity.IsAuthenticated && !(Model?.GalleryName ?? "Default").Equals("All", StringComparison.OrdinalIgnoreCase))
@if (await _settings.GetOrDefault(Settings.Gallery.FullWidth, false, Model?.Gallery?.Id) && User?.Identity != null && User.Identity.IsAuthenticated && !Model.Gallery.Identifier.Equals("All", StringComparison.OrdinalIgnoreCase))
{
<!-- Settings Button -->
<i id="btn-full-width-settings" class="fa-solid fa-gear btnGallerySettings" data-gallery-name="@Model?.GalleryName" data-gallery-id="@Model?.GalleryId"></i>
<i id="btn-full-width-settings" class="fa-solid fa-gear btnGallerySettings" data-gallery-name="@Model?.Gallery?.Name" data-gallery-id="@Model?.Gallery?.Id"></i>
}
<script src="~/js/gallery.js" asp-append-version="true"></script>

View File

@@ -5,7 +5,7 @@
@{
var userPermissions = User?.Identity?.GetUserPermissions() ?? AccessPermissions.None;
var columnCount = await _settings.GetOrDefault(Settings.Gallery.Columns, 4, Model?.GalleryId);
var columnCount = await _settings.GetOrDefault(Settings.Gallery.Columns, 4, Model?.Gallery?.Id);
var identityEnabled = await _settings.GetOrDefault(Settings.IdentityCheck.Enabled, true);
try
@@ -102,7 +102,7 @@
<h1 class="display-7">@imageGroup.Key
<span class="header-actions">
<span class="action text-success pointer">
<i class="btnDownloadGroup fa-solid fa-download" data-gallery-id="@Model?.GalleryId" data-gallery-key="@(await _settings.GetOrDefault(Settings.Gallery.SecretKey, string.Empty, Model?.GalleryId))" data-group-name="@((int?)Model?.GroupBy)|@imageGroup.Key" alt="@_localizer["Download"].Value"></i>
<i class="btnDownloadGroup fa-solid fa-download" data-gallery-id="@Model?.Gallery?.Id" data-gallery-key="@(await _settings.GetOrDefault(Settings.Gallery.SecretKey, string.Empty, Model?.Gallery?.Id))" data-group-name="@((int?)Model?.GroupBy)|@imageGroup.Key" alt="@_localizer["Download"].Value"></i>
<span>@_localizer["Download"].Value</span>
</span>
</span>
@@ -121,7 +121,7 @@
var image = imageGroupImages.ElementAt(index);
<div class="image-tile mb-4">
<a href="@image.ImagePath" class="media-viewer-item" data-media-viewer-type="@image?.MediaType.ToString().ToLower()" data-media-viewer-collection="@image.GalleryName" data-media-viewer-author="@(identityEnabled ? $"{_localizer["Uploaded_By"].Value}: {(!string.IsNullOrWhiteSpace(image?.UploadedBy) ? image?.UploadedBy : "Anonymous")}" : string.Empty)" data-media-viewer-download="@((await _settings.GetOrDefault(Settings.Gallery.Download, true, Model?.GalleryId) || (User?.Identity != null && User.Identity.IsAuthenticated)).ToString().ToLower())">
<a href="@image.ImagePath" class="media-viewer-item" data-media-viewer-type="@image?.MediaType.ToString().ToLower()" data-media-viewer-collection="@image.GalleryName" data-media-viewer-author="@(identityEnabled ? $"{_localizer["Uploaded_By"].Value}: {(!string.IsNullOrWhiteSpace(image?.UploadedBy) ? image?.UploadedBy : "Anonymous")}" : string.Empty)" data-media-viewer-download="@((await _settings.GetOrDefault(Settings.Gallery.Download, true, Model?.Gallery?.Id) || (User?.Identity != null && User.Identity.IsAuthenticated)).ToString().ToLower())">
<img src="@image?.ThumbnailPath" class="w-100 shadow-1-strong" loading="lazy" onerror="this.onerror=null;this.src='@image?.ThumbnailPathFallback';" />
@if (image?.MediaType == MediaType.Video)
{
@@ -142,7 +142,7 @@
@if (Model?.LoadScripts ?? true)
{
var idleTimeout = await _settings.GetOrDefault(Settings.Gallery.IdleRefreshMins, 0, Model?.GalleryId);
var idleTimeout = await _settings.GetOrDefault(Settings.Gallery.IdleRefreshMins, 0, Model?.Gallery?.Id);
if (idleTimeout > 0)
{
<script>

View File

@@ -1,11 +1,12 @@
@using WeddingShare.Constants
@using WeddingShare.Helpers
@inject WeddingShare.Helpers.IConfigHelper _config
@inject WeddingShare.Helpers.ILanguageHelper _language
@inject WeddingShare.Helpers.Database.IDatabaseHelper _database
@{
var galleryName = (!string.IsNullOrWhiteSpace(ViewBag?.GalleryId) && !ViewBag.GalleryId.Equals("all", StringComparison.OrdinalIgnoreCase)) ? ViewBag.GalleryId.ToLower() : "default";
var galleryId = (_database?.GetGalleryId(galleryName)?.Result) ?? 1;
var galleryIdentifier = (!string.IsNullOrWhiteSpace(ViewBag?.GalleryIdentifier) && !ViewBag.GalleryIdentifier.Equals("All", StringComparison.OrdinalIgnoreCase)) ? ViewBag.GalleryIdentifier.ToLower() : "default";
var galleryId = (_database?.GetGalleryId(galleryIdentifier)?.Result) ?? 1;
var title = await _settings.GetOrDefault(Settings.Basic.Title, "WeddingShare", galleryId);
var isDemoMode = await _settings.GetOrDefault(Settings.IsDemoMode, false);
@@ -13,7 +14,7 @@
string logo;
try
{
var logoGallery = _config.Get($"{Settings.Basic.Logo}_{galleryName}") != null ? galleryName : "default";
var logoGallery = _config.Get($"{Settings.Basic.Logo}_{galleryIdentifier}") != null ? galleryIdentifier : "default";
logo = await _settings.GetOrDefault(Settings.Basic.Logo, "/images/logo.png", logoGallery);
if (!logo.StartsWith("/images", StringComparison.OrdinalIgnoreCase) && !logo.StartsWith("/logos", StringComparison.OrdinalIgnoreCase) && !logo.StartsWith("/custom_resources", StringComparison.OrdinalIgnoreCase))

View File

@@ -59,6 +59,8 @@
<EmbeddedResource Include="SqlScripts\MySQL\014 - Rename Background Service Settings Keys.sql" />
<EmbeddedResource Include="SqlScripts\MySQL\013 - Rename Account Settings Keys.sql" />
<EmbeddedResource Include="SqlScripts\MySQL\012 - Add Resource Owners.sql" />
<EmbeddedResource Include="SqlScripts\MySQL\016 - Add All Gallery.sql" />
<EmbeddedResource Include="SqlScripts\MySQL\015 - Add Gallery Identifier.sql" />
<EmbeddedResource Include="SqlScripts\SQLite\004 - Add 2FA.sql" />
<EmbeddedResource Include="SqlScripts\SQLite\003 - Add Checksum.sql" />
<EmbeddedResource Include="SqlScripts\SQLite\002 - Add MediaType.sql" />
@@ -75,7 +77,9 @@
<EmbeddedResource Include="SqlScripts\SQLite\010 - Add CustomResources Table.sql" />
<EmbeddedResource Include="SqlScripts\SQLite\012 - Add Resource Owners.sql" />
<EmbeddedResource Include="SqlScripts\SQLite\013 - Rename Account Settings Keys.sql" />
<EmbeddedResource Include="SqlScripts\SQLite\015 - Add Gallery Identifier.sql" />
<EmbeddedResource Include="SqlScripts\SQLite\014 - Rename Background Service Settings Keys.sql" />
<EmbeddedResource Include="SqlScripts\SQLite\016 - Add All Gallery.sql" />
</ItemGroup>
<ItemGroup>

View File

@@ -153,7 +153,7 @@
}
},
"Release": {
"Version": "1.6.10.8"
"Version": "1.6.11"
},
"Sponsors": {
"Url": "https://sponsors.wedding-share.org",