diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs index ab8c19d10..fc5e07378 100644 --- a/API/Controllers/AccountController.cs +++ b/API/Controllers/AccountController.cs @@ -363,7 +363,7 @@ public class AccountController : BaseApiController } // Validate no other users exist with this email - if (user.Email!.Equals(dto.Email)) return Ok(await _localizationService.Translate(User.GetUserId(), "nothing-to-do")); + if (user.Email!.Equals(dto.Email)) return BadRequest(await _localizationService.Translate(User.GetUserId(), "nothing-to-do")); // Check if email is used by another user var existingUserEmail = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email); @@ -386,8 +386,12 @@ public class AccountController : BaseApiController user.ConfirmationToken = token; await _userManager.UpdateAsync(user); + var emailLink = await _emailService.GenerateEmailLink(Request, user.ConfirmationToken, "confirm-email-update", dto.Email); + _logger.LogCritical("[Update Email]: Email Link for {UserName}: {Link}", user.UserName, emailLink); + if (!shouldEmailUser) { + _logger.LogInformation("Cannot email admin, email not setup or admin email invalid"); return Ok(new InviteUserResponse { EmailLink = string.Empty, @@ -399,9 +403,6 @@ public class AccountController : BaseApiController // Send a confirmation email try { - var emailLink = await _emailService.GenerateEmailLink(Request, user.ConfirmationToken, "confirm-email-update", dto.Email); - _logger.LogCritical("[Update Email]: Email Link for {UserName}: {Link}", user.UserName, emailLink); - if (!_emailService.IsValidEmail(user.Email)) { _logger.LogCritical("[Update Email]: User is trying to update their email, but their existing email ({Email}) isn't valid. No email will be send", user.Email); @@ -839,6 +840,7 @@ public class AccountController : BaseApiController return BadRequest(await _localizationService.Translate(user.Id, "generic-user-email-update")); } user.ConfirmationToken = null; + user.EmailConfirmed = true; await _unitOfWork.CommitAsync(); diff --git a/API/Controllers/OPDSController.cs b/API/Controllers/OPDSController.cs index fac50229e..c6c9fb425 100644 --- a/API/Controllers/OPDSController.cs +++ b/API/Controllers/OPDSController.cs @@ -1123,7 +1123,9 @@ public class OpdsController : BaseApiController Id = mangaFile.Id.ToString(), Title = title, Extent = fileSize, - Summary = $"{fileType.Split("/")[1]} - {fileSize}", + Summary = $"File Type: {fileType.Split("/")[1]} - {fileSize}" + (string.IsNullOrWhiteSpace(chapter.Summary) + ? string.Empty + : $" Summary: {chapter.Summary}"), Format = mangaFile.Format.ToString(), Links = new List() { @@ -1279,7 +1281,7 @@ public class OpdsController : BaseApiController }; } - private string SerializeXml(Feed feed) + private string SerializeXml(Feed? feed) { if (feed == null) return string.Empty; using var sm = new StringWriter(); diff --git a/API/Controllers/SettingsController.cs b/API/Controllers/SettingsController.cs index 08168ddc8..e7429a9b2 100644 --- a/API/Controllers/SettingsController.cs +++ b/API/Controllers/SettingsController.cs @@ -511,6 +511,7 @@ public class SettingsController : BaseApiController public async Task> TestEmailServiceUrl() { var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId()); + if (string.IsNullOrEmpty(user?.Email)) return BadRequest("Your account has no email on record. Cannot email."); return Ok(await _emailService.SendTestEmail(user!.Email)); } } diff --git a/API/Controllers/UsersController.cs b/API/Controllers/UsersController.cs index e6c5013e5..1a1f37637 100644 --- a/API/Controllers/UsersController.cs +++ b/API/Controllers/UsersController.cs @@ -112,7 +112,6 @@ public class UsersController : BaseApiController existingPreferences.GlobalPageLayoutMode = preferencesDto.GlobalPageLayoutMode; existingPreferences.BlurUnreadSummaries = preferencesDto.BlurUnreadSummaries; existingPreferences.LayoutMode = preferencesDto.LayoutMode; - existingPreferences.Theme = preferencesDto.Theme ?? await _unitOfWork.SiteThemeRepository.GetDefaultTheme(); existingPreferences.PromptForDownloadSize = preferencesDto.PromptForDownloadSize; existingPreferences.NoTransitions = preferencesDto.NoTransitions; existingPreferences.SwipeToPaginate = preferencesDto.SwipeToPaginate; @@ -124,6 +123,12 @@ public class UsersController : BaseApiController existingPreferences.PdfScrollMode = preferencesDto.PdfScrollMode; existingPreferences.PdfSpreadMode = preferencesDto.PdfSpreadMode; + if (existingPreferences.Theme.Id != preferencesDto.Theme?.Id) + { + existingPreferences.Theme = preferencesDto.Theme ?? await _unitOfWork.SiteThemeRepository.GetDefaultTheme(); + } + + if (_localizationService.GetLocales().Contains(preferencesDto.Locale)) { existingPreferences.Locale = preferencesDto.Locale; diff --git a/API/Data/Repositories/CollectionTagRepository.cs b/API/Data/Repositories/CollectionTagRepository.cs index b034eea50..5a45c117e 100644 --- a/API/Data/Repositories/CollectionTagRepository.cs +++ b/API/Data/Repositories/CollectionTagRepository.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Threading.Tasks; using API.Data.Misc; using API.DTOs.Collection; -using API.DTOs.CollectionTags; using API.Entities; using API.Entities.Enums; using API.Extensions; @@ -143,14 +142,6 @@ public class CollectionTagRepository : ICollectionTagRepository .ToListAsync(); } - [Obsolete("use TagExists with userId")] - public async Task TagExists(string title) - { - var normalized = title.ToNormalized(); - return await _context.CollectionTag - .AnyAsync(x => x.NormalizedTitle != null && x.NormalizedTitle.Equals(normalized)); - } - /// /// If any tag exists for that given user's collections /// @@ -176,10 +167,9 @@ public class CollectionTagRepository : ICollectionTagRepository public async Task> GetRandomCoverImagesAsync(int collectionId) { var random = new Random(); - var data = await _context.CollectionTag + var data = await _context.AppUserCollection .Where(t => t.Id == collectionId) - .SelectMany(t => t.SeriesMetadatas) - .Select(sm => sm.Series.CoverImage) + .SelectMany(uc => uc.Items.Select(series => series.CoverImage)) .Where(t => !string.IsNullOrEmpty(t)) .ToListAsync(); @@ -217,28 +207,6 @@ public class CollectionTagRepository : ICollectionTagRepository .ToListAsync(); } - public async Task> GetAllTagDtosAsync() - { - - return await _context.CollectionTag - .OrderBy(c => c.NormalizedTitle) - .AsNoTracking() - .ProjectTo(_mapper.ConfigurationProvider) - .ToListAsync(); - } - - public async Task> GetAllPromotedTagDtosAsync(int userId) - { - var userRating = await GetUserAgeRestriction(userId); - return await _context.CollectionTag - .Where(c => c.Promoted) - .RestrictAgainstAgeRestriction(userRating) - .OrderBy(c => c.NormalizedTitle) - .AsNoTracking() - .ProjectTo(_mapper.ConfigurationProvider) - .ToListAsync(); - } - public async Task GetCollectionAsync(int tagId, CollectionIncludes includes = CollectionIncludes.None) { diff --git a/API/I18N/en.json b/API/I18N/en.json index 9d10a5c55..bee5c0ee0 100644 --- a/API/I18N/en.json +++ b/API/I18N/en.json @@ -40,6 +40,8 @@ "invalid-username": "Invalid username", "critical-email-migration": "There was an issue during email migration. Contact support", "email-not-enabled": "Email is not enabled on this server. You cannot perform this action.", + "account-email-invalid": "The email on file for the admin account is not a valid email. Cannot send test email.", + "email-settings-invalid": "Email settings missing information. Ensure all email settings are saved.", "chapter-doesnt-exist": "Chapter does not exist", "file-missing": "File was not found in book", diff --git a/API/Services/EmailService.cs b/API/Services/EmailService.cs index 5e54e2170..0a8ba6404 100644 --- a/API/Services/EmailService.cs +++ b/API/Services/EmailService.cs @@ -51,16 +51,19 @@ public class EmailService : IEmailService private readonly IUnitOfWork _unitOfWork; private readonly IDirectoryService _directoryService; private readonly IHostEnvironment _environment; + private readonly ILocalizationService _localizationService; private const string TemplatePath = @"{0}.html"; private const string LocalHost = "localhost:4200"; - public EmailService(ILogger logger, IUnitOfWork unitOfWork, IDirectoryService directoryService, IHostEnvironment environment) + public EmailService(ILogger logger, IUnitOfWork unitOfWork, IDirectoryService directoryService, + IHostEnvironment environment, ILocalizationService localizationService) { _logger = logger; _unitOfWork = unitOfWork; _directoryService = directoryService; _environment = environment; + _localizationService = localizationService; } /// @@ -75,9 +78,18 @@ public class EmailService : IEmailService }; var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync(); - if (!IsValidEmail(adminEmail) || !settings.IsEmailSetup()) + if (!IsValidEmail(adminEmail)) { - result.ErrorMessage = "You need to fill in more information in settings and ensure your account has a valid email to send a test email"; + var defaultAdmin = await _unitOfWork.UserRepository.GetDefaultAdminUser(); + result.ErrorMessage = await _localizationService.Translate(defaultAdmin.Id, "account-email-invalid"); + result.Successful = false; + return result; + } + + if (!settings.IsEmailSetup()) + { + var defaultAdmin = await _unitOfWork.UserRepository.GetDefaultAdminUser(); + result.ErrorMessage = await _localizationService.Translate(defaultAdmin.Id, "email-settings-invalid"); result.Successful = false; return result; } diff --git a/API/Services/ReaderService.cs b/API/Services/ReaderService.cs index 945e55a45..f3adc93fc 100644 --- a/API/Services/ReaderService.cs +++ b/API/Services/ReaderService.cs @@ -77,7 +77,7 @@ public class ReaderService : IReaderService public static string FormatBookmarkFolderPath(string baseDirectory, int userId, int seriesId, int chapterId) { - return Tasks.Scanner.Parser.Parser.NormalizePath(Path.Join(baseDirectory, $"{userId}", $"{seriesId}", $"{chapterId}")); + return Parser.NormalizePath(Path.Join(baseDirectory, $"{userId}", $"{seriesId}", $"{chapterId}")); } /// diff --git a/API/config/appsettings.Development.json b/API/config/appsettings.Development.json index a72749400..0c6352d06 100644 --- a/API/config/appsettings.Development.json +++ b/API/config/appsettings.Development.json @@ -1,8 +1,8 @@ { "TokenKey": "super secret unguessable key that is longer because we require it", "Port": 5000, - "IpAddresses": "", + "IpAddresses": "0.0.0.0,::", "BaseUrl": "/", - "Cache": 90, + "Cache": 75, "AllowIFraming": false } \ No newline at end of file diff --git a/API/config/templates/EmailChange.html b/API/config/templates/EmailChange.html index f5d661294..7a960aea9 100644 --- a/API/config/templates/EmailChange.html +++ b/API/config/templates/EmailChange.html @@ -270,7 +270,7 @@ @@ -278,7 +278,7 @@ -

If the button above does not work, please find the link here: {{Link}}

+

If the button above does not work, please find the link here: {{Link}}

@@ -312,19 +312,19 @@
- Discord + Discord   - Reddit + Reddit   - Github + Github   - Open Collective + Open Collective
diff --git a/API/config/templates/EmailConfirm.html b/API/config/templates/EmailConfirm.html index dff300dc6..4aa4f701c 100644 --- a/API/config/templates/EmailConfirm.html +++ b/API/config/templates/EmailConfirm.html @@ -35,7 +35,7 @@ @import url('https://fonts.googleapis.com/css2?family=Spartan:wght@500;700&display=swap'); /* What it does: Remove spaces around the email design added by some email clients. */ /* Beware: It can remove the padding / margin and add a background color to the compose a reply window. */ - + html, body { margin: 0 auto !important; @@ -44,53 +44,53 @@ width: 100% !important; } /* What it does: Stops email clients resizing small text. */ - + * { -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; } /* What it does: Centers email on Android 4.4 */ - + div[style*="margin: 16px 0"] { margin: 0 !important; } /* What it does: Stops Outlook from adding extra spacing to tables. */ - + table, td { mso-table-lspace: 0pt !important; mso-table-rspace: 0pt !important; } /* What it does: Fixes webkit padding issue. Fix for Yahoo mail table alignment bug. Applies table-layout to the first 2 tables then removes for anything nested deeper. */ - + table { border-spacing: 0 !important; border-collapse: collapse !important; table-layout: fixed !important; margin: 0 auto !important; } - + table table table { table-layout: auto; } - + i { color: #fff; font-size: 26px; } /* What it does: Uses a better rendering method when resizing images in IE. */ - + img { -ms-interpolation-mode: bicubic; } /* What it does: A work-around for email clients meddling in triggered links. */ - + *[x-apple-data-detectors], /* iOS */ - + .x-gmail-data-detectors, /* Gmail */ - + .x-gmail-data-detectors *, .aBn { border-bottom: 0 !important; @@ -103,25 +103,25 @@ line-height: inherit !important; } /* What it does: Prevents Gmail from displaying an download button on large, non-linked images. */ - + .a6S { display: none !important; opacity: 0.01 !important; } /* If the above doesn't work, add a .g-img class to any image in question. */ - + img.g-img + div { display: none !important; } /* What it does: Prevents underlining the button text in Windows 10 */ - + .button-link { text-decoration: none !important; } /* What it does: Removes right gutter in Gmail iOS app: https://github.com/TedGoas/Cerberus/issues/89 */ /* Create one of these media queries for each additional viewport size you'd like to fix */ /* Thanks to Eric Lepetit @ericlepetitsf) for help troubleshooting */ - + @media only screen and (min-device-width: 375px) and (max-device-width: 413px) { /* iPhone 6 and 6+ */ .email-container { @@ -132,12 +132,12 @@