Last PR before Release (#2692)
This commit is contained in:
parent
07e96389fb
commit
5cf6077dfd
38 changed files with 3801 additions and 2044 deletions
|
@ -76,7 +76,7 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||
<PackageReference Include="MimeTypeMapOfficial" Version="1.0.17" />
|
||||
|
|
|
@ -8,11 +8,13 @@ public class ScrobbleEventDto
|
|||
public int SeriesId { get; set; }
|
||||
public int LibraryId { get; set; }
|
||||
public bool IsProcessed { get; set; }
|
||||
public int? VolumeNumber { get; set; }
|
||||
public float? VolumeNumber { get; set; }
|
||||
public int? ChapterNumber { get; set; }
|
||||
public DateTime LastModifiedUtc { get; set; }
|
||||
public DateTime CreatedUtc { get; set; }
|
||||
public float? Rating { get; set; }
|
||||
public ScrobbleEventType ScrobbleEventType { get; set; }
|
||||
public bool IsErrored { get; set; }
|
||||
public string? ErrorDetails { get; set; }
|
||||
|
||||
}
|
||||
|
|
|
@ -38,4 +38,16 @@ public class UpdateNotificationDto
|
|||
/// Date of the publish
|
||||
/// </summary>
|
||||
public required string PublishDate { get; init; }
|
||||
/// <summary>
|
||||
/// Is the server on a nightly within this release
|
||||
/// </summary>
|
||||
public bool IsOnNightlyInRelease { get; set; }
|
||||
/// <summary>
|
||||
/// Is the server on an older version
|
||||
/// </summary>
|
||||
public bool IsReleaseNewer { get; set; }
|
||||
/// <summary>
|
||||
/// Is the server on this version
|
||||
/// </summary>
|
||||
public bool IsReleaseEqual { get; set; }
|
||||
}
|
||||
|
|
|
@ -18,50 +18,62 @@ public static class MigrateWantToReadExport
|
|||
{
|
||||
public static async Task Migrate(DataContext dataContext, IDirectoryService directoryService, ILogger<Program> logger)
|
||||
{
|
||||
var importFile = Path.Join(directoryService.ConfigDirectory, "want-to-read-migration.csv");
|
||||
if (File.Exists(importFile))
|
||||
{
|
||||
logger.LogCritical(
|
||||
"Running MigrateWantToReadExport migration - Completed. This is not an error");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateWantToReadExport migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
await using var command = dataContext.Database.GetDbConnection().CreateCommand();
|
||||
command.CommandText = "Select AppUserId, Id from Series WHERE AppUserId IS NOT NULL ORDER BY AppUserId;";
|
||||
|
||||
await dataContext.Database.OpenConnectionAsync();
|
||||
await using var result = await command.ExecuteReaderAsync();
|
||||
|
||||
await using var writer = new StreamWriter(Path.Join(directoryService.ConfigDirectory, "want-to-read-migration.csv"));
|
||||
await using var csvWriter = new CsvWriter(writer, CultureInfo.InvariantCulture);
|
||||
|
||||
// Write header
|
||||
csvWriter.WriteField("AppUserId");
|
||||
csvWriter.WriteField("Id");
|
||||
await csvWriter.NextRecordAsync();
|
||||
|
||||
// Write data
|
||||
while (await result.ReadAsync())
|
||||
{
|
||||
var appUserId = result["AppUserId"].ToString();
|
||||
var id = result["Id"].ToString();
|
||||
|
||||
csvWriter.WriteField(appUserId);
|
||||
csvWriter.WriteField(id);
|
||||
await csvWriter.NextRecordAsync();
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
await dataContext.Database.CloseConnectionAsync();
|
||||
writer.Close();
|
||||
} catch (Exception) {/* Swallow */}
|
||||
var importFile = Path.Join(directoryService.ConfigDirectory, "want-to-read-migration.csv");
|
||||
if (File.Exists(importFile))
|
||||
{
|
||||
logger.LogCritical(
|
||||
"Running MigrateWantToReadExport migration - Completed. This is not an error");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateWantToReadExport migration - Completed. This is not an error");
|
||||
logger.LogCritical(
|
||||
"Running MigrateWantToReadExport migration - Please be patient, this may take some time. This is not an error");
|
||||
|
||||
await using var command = dataContext.Database.GetDbConnection().CreateCommand();
|
||||
command.CommandText = "Select AppUserId, Id from Series WHERE AppUserId IS NOT NULL ORDER BY AppUserId;";
|
||||
|
||||
await dataContext.Database.OpenConnectionAsync();
|
||||
await using var result = await command.ExecuteReaderAsync();
|
||||
|
||||
await using var writer =
|
||||
new StreamWriter(Path.Join(directoryService.ConfigDirectory, "want-to-read-migration.csv"));
|
||||
await using var csvWriter = new CsvWriter(writer, CultureInfo.InvariantCulture);
|
||||
|
||||
// Write header
|
||||
csvWriter.WriteField("AppUserId");
|
||||
csvWriter.WriteField("Id");
|
||||
await csvWriter.NextRecordAsync();
|
||||
|
||||
// Write data
|
||||
while (await result.ReadAsync())
|
||||
{
|
||||
var appUserId = result["AppUserId"].ToString();
|
||||
var id = result["Id"].ToString();
|
||||
|
||||
csvWriter.WriteField(appUserId);
|
||||
csvWriter.WriteField(id);
|
||||
await csvWriter.NextRecordAsync();
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
await dataContext.Database.CloseConnectionAsync();
|
||||
writer.Close();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
/* Swallow */
|
||||
}
|
||||
|
||||
logger.LogCritical(
|
||||
"Running MigrateWantToReadExport migration - Completed. This is not an error");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// On new installs, the db isn't setup yet, so this has nothing to do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2880
API/Data/Migrations/20240205184724_ScrobbleEventError.Designer.cs
generated
Normal file
2880
API/Data/Migrations/20240205184724_ScrobbleEventError.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
57
API/Data/Migrations/20240205184724_ScrobbleEventError.cs
Normal file
57
API/Data/Migrations/20240205184724_ScrobbleEventError.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ScrobbleEventError : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<float>(
|
||||
name: "VolumeNumber",
|
||||
table: "ScrobbleEvent",
|
||||
type: "REAL",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ErrorDetails",
|
||||
table: "ScrobbleEvent",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsErrored",
|
||||
table: "ScrobbleEvent",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ErrorDetails",
|
||||
table: "ScrobbleEvent");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsErrored",
|
||||
table: "ScrobbleEvent");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "VolumeNumber",
|
||||
table: "ScrobbleEvent",
|
||||
type: "INTEGER",
|
||||
nullable: true,
|
||||
oldClrType: typeof(float),
|
||||
oldType: "REAL",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1522,9 +1522,15 @@ namespace API.Data.Migrations
|
|||
b.Property<DateTime>("CreatedUtc")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ErrorDetails")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Format")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsErrored")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsProcessed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
@ -1558,8 +1564,8 @@ namespace API.Data.Migrations
|
|||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("VolumeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
b.Property<float?>("VolumeNumber")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ using AutoMapper.QueryableExtensions;
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Data.Repositories;
|
||||
#nullable enable
|
||||
|
||||
public interface IAppUserSmartFilterRepository
|
||||
{
|
||||
|
@ -55,6 +56,7 @@ public class AppUserSmartFilterRepository : IAppUserSmartFilterRepository
|
|||
|
||||
public async Task<AppUserSmartFilter?> GetById(int smartFilterId)
|
||||
{
|
||||
return await _context.AppUserSmartFilter.FirstOrDefaultAsync(d => d.Id == smartFilterId);
|
||||
return await _context.AppUserSmartFilter
|
||||
.FirstOrDefaultAsync(d => d.Id == smartFilterId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ using AutoMapper.QueryableExtensions;
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Data.Repositories;
|
||||
#nullable enable
|
||||
|
||||
[Flags]
|
||||
public enum ChapterIncludes
|
||||
|
|
|
@ -5,12 +5,14 @@ using System.Threading.Tasks;
|
|||
using API.Constants;
|
||||
using API.DTOs;
|
||||
using API.DTOs.Recommendation;
|
||||
using API.DTOs.Scrobbling;
|
||||
using API.DTOs.SeriesDetail;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Metadata;
|
||||
using API.Extensions;
|
||||
using API.Extensions.QueryExtensions;
|
||||
using API.Services.Plus;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
@ -31,8 +33,9 @@ public interface IExternalSeriesMetadataRepository
|
|||
Task<SeriesDetailPlusDto> GetSeriesDetailPlusDto(int seriesId);
|
||||
Task LinkRecommendationsToSeries(Series series);
|
||||
Task<bool> IsBlacklistedSeries(int seriesId);
|
||||
Task CreateBlacklistedSeries(int seriesId);
|
||||
Task CreateBlacklistedSeries(int seriesId, bool saveChanges = true);
|
||||
Task RemoveFromBlacklist(int seriesId);
|
||||
Task<IList<int>> GetAllSeriesIdsWithoutMetadata(int limit);
|
||||
}
|
||||
|
||||
public class ExternalSeriesMetadataRepository : IExternalSeriesMetadataRepository
|
||||
|
@ -197,14 +200,19 @@ public class ExternalSeriesMetadataRepository : IExternalSeriesMetadataRepositor
|
|||
/// Creates a new instance against SeriesId and Saves to the DB
|
||||
/// </summary>
|
||||
/// <param name="seriesId"></param>
|
||||
public async Task CreateBlacklistedSeries(int seriesId)
|
||||
/// <param name="saveChanges"></param>
|
||||
public async Task CreateBlacklistedSeries(int seriesId, bool saveChanges = true)
|
||||
{
|
||||
if (seriesId <= 0) return;
|
||||
if (seriesId <= 0 || await _context.SeriesBlacklist.AnyAsync(s => s.SeriesId == seriesId)) return;
|
||||
|
||||
await _context.SeriesBlacklist.AddAsync(new SeriesBlacklist()
|
||||
{
|
||||
SeriesId = seriesId
|
||||
});
|
||||
await _context.SaveChangesAsync();
|
||||
if (saveChanges)
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -224,4 +232,16 @@ public class ExternalSeriesMetadataRepository : IExternalSeriesMetadataRepositor
|
|||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IList<int>> GetAllSeriesIdsWithoutMetadata(int limit)
|
||||
{
|
||||
return await _context.Series
|
||||
.Where(s => !ExternalMetadataService.NonEligibleLibraryTypes.Contains(s.Library.Type))
|
||||
.Where(s => s.ExternalSeriesMetadata == null || s.ExternalSeriesMetadata.ValidUntilUtc < DateTime.UtcNow)
|
||||
.OrderByDescending(s => s.Library.Type)
|
||||
.ThenBy(s => s.NormalizedName)
|
||||
.Select(s => s.Id)
|
||||
.Take(limit)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,348 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!-- utf-8 works for most cases -->
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!-- Forcing initial-scale shouldn't be necessary -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!-- Use the latest (edge) version of IE rendering engine -->
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<!-- Disable auto-scale in iOS 10 Mail entirely -->
|
||||
<title>Kavita - [Plain HTML]</title>
|
||||
<!-- The title tag shows in email notifications, like Android 4.4. -->
|
||||
<!-- Web Font / @font-face : BEGIN -->
|
||||
<!-- NOTE: If web fonts are not required, lines 10 - 27 can be safely removed. -->
|
||||
<!-- Desktop Outlook chokes on web font references and defaults to Times New Roman, so we force a safe fallback font. -->
|
||||
<!--[if mso]>
|
||||
<style>
|
||||
* {
|
||||
font-family: Arial, sans-serif !important;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!-- All other clients get the webfont reference; some will render the font and others will silently fail to the fallbacks. More on that here: http://stylecampaign.com/blog/2015/02/webfont-support-in-email/ -->
|
||||
<!--[if !mso]><!-->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Spartan:wght@500;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
||||
<!--<![endif]-->
|
||||
<!-- Web Font / @font-face : END -->
|
||||
<!-- CSS Reset -->
|
||||
<style>
|
||||
@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;
|
||||
padding: 0 !important;
|
||||
height: 100% !important;
|
||||
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;
|
||||
cursor: default !important;
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
font-size: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
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 {
|
||||
min-width: 375px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- Progressive Enhancements -->
|
||||
<style>
|
||||
/* What it does: Hover styles for buttons */
|
||||
|
||||
.button-td,
|
||||
.button-a {
|
||||
transition: all 100ms ease-in;
|
||||
}
|
||||
|
||||
.button-td:hover,
|
||||
.button-a:hover {
|
||||
background: #000000 !important;
|
||||
border-color: #000000 !important;
|
||||
color: white !important;
|
||||
}
|
||||
/* Media Queries */
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
/* What it does: Forces elements to resize to the full width of their container. Useful for resizing images beyond their max-width. */
|
||||
.fluid {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
/* What it does: Forces table cells into full-width rows. */
|
||||
.stack-column,
|
||||
.stack-column-center {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
direction: ltr !important;
|
||||
}
|
||||
/* And center justify these ones. */
|
||||
.stack-column-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
/* What it does: Generic utility class for centering. Useful for images, buttons, and nested tables. */
|
||||
.center-on-narrow {
|
||||
text-align: center !important;
|
||||
display: block !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
float: none !important;
|
||||
}
|
||||
table.center-on-narrow {
|
||||
display: inline-block !important;
|
||||
}
|
||||
/* What it does: Adjust typography on small screens to improve readability */
|
||||
.email-container p {
|
||||
font-size: 17px !important;
|
||||
line-height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- What it does: Makes background images in 72ppi Outlook render at correct size. -->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body width="100%" bgcolor="#F1F1F1" style="margin: 0; mso-line-height-rule: exactly;">
|
||||
<center style="width: 100%; background: #F1F1F1; text-align: left;">
|
||||
<!-- Visually Hidden Preheader Text : BEGIN -->
|
||||
<div style="display:none;font-size:1px;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;mso-hide:all;font-family: sans-serif;"> Your account's email has been updated on {{InvitingUser}}'s Kavita instance. Click the button to validate your email.</div>
|
||||
<!-- Visually Hidden Preheader Text : END -->
|
||||
<!--
|
||||
Set the email width. Defined in two places:
|
||||
1. max-width for all clients except Desktop Windows Outlook, allowing the email to squish on narrow but never go wider than 680px.
|
||||
2. MSO tags for Desktop Windows Outlook enforce a 680px width.
|
||||
Note: The Fluid and Responsive templates have a different width (600px). The hybrid grid is more "fragile", and I've found that 680px is a good width. Change with caution.
|
||||
-->
|
||||
<div style="max-width: 680px; margin: auto;" class="email-container">
|
||||
<!--[if mso]>
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="680" align="center">
|
||||
<tr>
|
||||
<td>
|
||||
<![endif]-->
|
||||
<!-- Email Body : BEGIN -->
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" width="100%" style="max-width: 680px;" class="email-container">
|
||||
<!-- HEADER : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#4AC694">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="margin-left: -10px;padding: 10px 40px 10px 40px; text-align: center;"> <img src="https://www.kavitareader.com/img/email/logo-256.png" alt="kavita_logo" width="75" style="height:auto;display:inline-block;vertical-align:middle;" />
|
||||
<div style="min-height:75px;line-height:75px;display:inline-block;vertical-align:middle;color:#fff;font-family: 'Spartan', sans-serif;font-size:2rem;font-weight:700;">Kavita</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HEADER : END -->
|
||||
<!-- HERO : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#fff" align="center" valign="top" style="text-align: center; background-position: center center !important; background-size: cover !important;">
|
||||
<!--[if gte mso 9]>
|
||||
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:680px; height:380px; background-position: center center !important;">
|
||||
<v:fill type="tile" src="background.png" color="#222222" />
|
||||
<v:textbox inset="0,0,0,0">
|
||||
<![endif]-->
|
||||
<div>
|
||||
<!--[if mso]>
|
||||
<table role="presentation" border="0" cellspacing="0" cellpadding="0" align="center" width="500">
|
||||
<tr>
|
||||
<td align="center" valign="middle" width="500">
|
||||
<![endif]-->
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="max-width:500px; margin: auto;">
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">Email Change Update</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Your account's email has been updated on {{InvitingUser}}'s Kavita instance.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Please click the following link to validate your email change. The email is not changed until you complete validation.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="center" style="text-align: center; padding: 15px 0px 20px 0px;">
|
||||
<!-- Button : BEGIN -->
|
||||
<center>
|
||||
<table role="presentation" align="center" cellspacing="0" cellpadding="0" border="0" class="center-on-narrow" style="text-align: center;">
|
||||
<tr>
|
||||
<td style="border-radius: 50px; background: #153643; text-align: center;" class="button-td">
|
||||
<a href="{{Link}}" style="background: #153643; border: 15px solid #153643; font-family: 'Montserrat', sans-serif; font-size: 14px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 50px; font-weight: bold;" class="button-a"> <span style="color:#ffffff;" class="button-link"> CONFIRM EMAIL </span> </a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<!-- Button : END -->
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If the button above does not work, please find the link here: <a style="color:inherit;margin: 0;width: 100%;word-break: break-all;" href="{{Link}}">{{Link}}</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
<!--[if gte mso 9]>
|
||||
</v:textbox>
|
||||
</v:rect>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HERO : END -->
|
||||
<!-- SOCIAL : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#292828">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 15px 30px; text-align: center;">
|
||||
<table align="center" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://discord.gg/b52wT37kt7"><img style="width:25px" src="https://www.kavitareader.com/img/email/discord-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Discord"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://www.reddit.com/r/KavitaManga/"><img style="width:25px" src="https://www.kavitareader.com/img/email/reddit-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Reddit"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://github.com/Kareadita/Kavita/"><img style="width:25px" src="https://www.kavitareader.com/img/email/github-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Github"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://opencollective.com/kavita"><img style="width:25px" src="https://www.kavitareader.com/img/email/open-collective-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Open Collective"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- SOCIAL : END -->
|
||||
</table>
|
||||
<!-- Email Body : END -->
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">Email Change Update</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Your account's email has been updated on {{InvitingUser}}'s Kavita instance.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Please click the following link to validate your email change. The email is not changed until you complete validation.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="center" style="text-align: center; padding: 15px 0px 20px 0px;">
|
||||
<!-- Button : BEGIN -->
|
||||
<center>
|
||||
<table role="presentation" align="center" cellspacing="0" cellpadding="0" border="0" class="center-on-narrow" style="text-align: center;">
|
||||
<tr>
|
||||
<td style="border-radius: 50px; background: #153643; text-align: center;" class="button-td">
|
||||
<a href="{{Link}}" style="background: #153643; border: 15px solid #153643; font-family: 'Montserrat', sans-serif; font-size: 14px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 50px; font-weight: bold;" class="button-a"> <span style="color:#ffffff;" class="button-link"> CONFIRM EMAIL </span> </a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<!-- Button : END -->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If the button above does not work, please find the link here: <a style="color:inherit;margin: 0;width: 100%;word-break: break-all;" href="{{Link}}">{{Link}}</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -1,348 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!-- utf-8 works for most cases -->
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!-- Forcing initial-scale shouldn't be necessary -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!-- Use the latest (edge) version of IE rendering engine -->
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<!-- Disable auto-scale in iOS 10 Mail entirely -->
|
||||
<title>Kavita - [Plain HTML]</title>
|
||||
<!-- The title tag shows in email notifications, like Android 4.4. -->
|
||||
<!-- Web Font / @font-face : BEGIN -->
|
||||
<!-- NOTE: If web fonts are not required, lines 10 - 27 can be safely removed. -->
|
||||
<!-- Desktop Outlook chokes on web font references and defaults to Times New Roman, so we force a safe fallback font. -->
|
||||
<!--[if mso]>
|
||||
<style>
|
||||
* {
|
||||
font-family: Arial, sans-serif !important;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!-- All other clients get the webfont reference; some will render the font and others will silently fail to the fallbacks. More on that here: http://stylecampaign.com/blog/2015/02/webfont-support-in-email/ -->
|
||||
<!--[if !mso]><!-->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Spartan:wght@500;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
||||
<!--<![endif]-->
|
||||
<!-- Web Font / @font-face : END -->
|
||||
<!-- CSS Reset -->
|
||||
<style>
|
||||
@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;
|
||||
padding: 0 !important;
|
||||
height: 100% !important;
|
||||
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;
|
||||
cursor: default !important;
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
font-size: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
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 {
|
||||
min-width: 375px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- Progressive Enhancements -->
|
||||
<style>
|
||||
/* What it does: Hover styles for buttons */
|
||||
|
||||
.button-td,
|
||||
.button-a {
|
||||
transition: all 100ms ease-in;
|
||||
}
|
||||
|
||||
.button-td:hover,
|
||||
.button-a:hover {
|
||||
background: #000000 !important;
|
||||
border-color: #000000 !important;
|
||||
color: white !important;
|
||||
}
|
||||
/* Media Queries */
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
/* What it does: Forces elements to resize to the full width of their container. Useful for resizing images beyond their max-width. */
|
||||
.fluid {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
/* What it does: Forces table cells into full-width rows. */
|
||||
.stack-column,
|
||||
.stack-column-center {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
direction: ltr !important;
|
||||
}
|
||||
/* And center justify these ones. */
|
||||
.stack-column-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
/* What it does: Generic utility class for centering. Useful for images, buttons, and nested tables. */
|
||||
.center-on-narrow {
|
||||
text-align: center !important;
|
||||
display: block !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
float: none !important;
|
||||
}
|
||||
table.center-on-narrow {
|
||||
display: inline-block !important;
|
||||
}
|
||||
/* What it does: Adjust typography on small screens to improve readability */
|
||||
.email-container p {
|
||||
font-size: 17px !important;
|
||||
line-height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- What it does: Makes background images in 72ppi Outlook render at correct size. -->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body width="100%" bgcolor="#F1F1F1" style="margin: 0; mso-line-height-rule: exactly;">
|
||||
<center style="width: 100%; background: #F1F1F1; text-align: left;">
|
||||
<!-- Visually Hidden Preheader Text : BEGIN -->
|
||||
<div style="display:none;font-size:1px;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;mso-hide:all;font-family: sans-serif;"> You have been invited to {{InvitingUser}}'s Kavita instance. Click the button to accept the invite. </div>
|
||||
<!-- Visually Hidden Preheader Text : END -->
|
||||
<!--
|
||||
Set the email width. Defined in two places:
|
||||
1. max-width for all clients except Desktop Windows Outlook, allowing the email to squish on narrow but never go wider than 680px.
|
||||
2. MSO tags for Desktop Windows Outlook enforce a 680px width.
|
||||
Note: The Fluid and Responsive templates have a different width (600px). The hybrid grid is more "fragile", and I've found that 680px is a good width. Change with caution.
|
||||
-->
|
||||
<div style="max-width: 680px; margin: auto;" class="email-container">
|
||||
<!--[if mso]>
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="680" align="center">
|
||||
<tr>
|
||||
<td>
|
||||
<![endif]-->
|
||||
<!-- Email Body : BEGIN -->
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" width="100%" style="max-width: 680px;" class="email-container">
|
||||
<!-- HEADER : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#4AC694">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="margin-left: -10px;padding: 10px 40px 10px 40px; text-align: center;"> <img src="https://www.kavitareader.com/img/email/logo-256.png" alt="kavita_logo" width="75" style="height:auto;display:inline-block;vertical-align:middle;" />
|
||||
<div style="min-height:75px;line-height:75px;display:inline-block;vertical-align:middle;color:#fff;font-family: 'Spartan', sans-serif;font-size:2rem;font-weight:700;">Kavita</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HEADER : END -->
|
||||
<!-- HERO : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#fff" align="center" valign="top" style="text-align: center; background-position: center center !important; background-size: cover !important;">
|
||||
<!--[if gte mso 9]>
|
||||
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:680px; height:380px; background-position: center center !important;">
|
||||
<v:fill type="tile" src="background.png" color="#222222" />
|
||||
<v:textbox inset="0,0,0,0">
|
||||
<![endif]-->
|
||||
<div>
|
||||
<!--[if mso]>
|
||||
<table role="presentation" border="0" cellspacing="0" cellpadding="0" align="center" width="500">
|
||||
<tr>
|
||||
<td align="center" valign="middle" width="500">
|
||||
<![endif]-->
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="max-width:500px; margin: auto;">
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">You've Been Invited</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">You have been invited to {{InvitingUser}}'s Kavita instance.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Please click the following link to setup an account for yourself and start reading.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="center" style="text-align: center; padding: 15px 0px 20px 0px;">
|
||||
<!-- Button : BEGIN -->
|
||||
<center>
|
||||
<table role="presentation" align="center" cellspacing="0" cellpadding="0" border="0" class="center-on-narrow" style="text-align: center;">
|
||||
<tr>
|
||||
<td style="border-radius: 50px; background: #153643; text-align: center;" class="button-td">
|
||||
<a href="{{Link}}" style="background: #153643; border: 15px solid #153643; font-family: 'Montserrat', sans-serif; font-size: 14px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 50px; font-weight: bold;" class="button-a"> <span style="color:#ffffff;" class="button-link"> ACCEPT INVITE </span> </a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<!-- Button : END -->
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If the button above does not work, please find the link here: <a style="color:inherit;margin: 0;width: 100%;word-break: break-all;" href="{{Link}}">{{Link}}</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
<!--[if gte mso 9]>
|
||||
</v:textbox>
|
||||
</v:rect>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HERO : END -->
|
||||
<!-- SOCIAL : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#292828">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 15px 30px; text-align: center;">
|
||||
<table align="center" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://discord.gg/b52wT37kt7"><img style="width:25px" src="https://www.kavitareader.com/img/email/discord-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Discord"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://www.reddit.com/r/KavitaManga/"><img style="width:25px" src="https://www.kavitareader.com/img/email/reddit-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Reddit"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://github.com/Kareadita/Kavita/"><img style="width:25px" src="https://www.kavitareader.com/img/email/github-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Github"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://opencollective.com/kavita"><img style="width:25px" src="https://www.kavitareader.com/img/email/open-collective-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Open Collective"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- SOCIAL : END -->
|
||||
</table>
|
||||
<!-- Email Body : END -->
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">You've Been Invited</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">You have been invited to {{InvitingUser}}'s Kavita instance.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Please click the following link to setup an account for yourself and start reading.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="center" style="text-align: center; padding: 15px 0px 20px 0px;">
|
||||
<!-- Button : BEGIN -->
|
||||
<center>
|
||||
<table role="presentation" align="center" cellspacing="0" cellpadding="0" border="0" class="center-on-narrow" style="text-align: center;">
|
||||
<tr>
|
||||
<td style="border-radius: 50px; background: #153643; text-align: center;" class="button-td">
|
||||
<a href="{{Link}}" style="background: #153643; border: 15px solid #153643; font-family: 'Montserrat', sans-serif; font-size: 14px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 50px; font-weight: bold;" class="button-a"> <span style="color:#ffffff;" class="button-link"> ACCEPT INVITE </span> </a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<!-- Button : END -->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If the button above does not work, please find the link here: <a style="color:inherit;margin: 0;width: 100%;word-break: break-all;" href="{{Link}}">{{Link}}</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -1,348 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!-- utf-8 works for most cases -->
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!-- Forcing initial-scale shouldn't be necessary -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!-- Use the latest (edge) version of IE rendering engine -->
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<!-- Disable auto-scale in iOS 10 Mail entirely -->
|
||||
<title>Kavita - [Plain HTML]</title>
|
||||
<!-- The title tag shows in email notifications, like Android 4.4. -->
|
||||
<!-- Web Font / @font-face : BEGIN -->
|
||||
<!-- NOTE: If web fonts are not required, lines 10 - 27 can be safely removed. -->
|
||||
<!-- Desktop Outlook chokes on web font references and defaults to Times New Roman, so we force a safe fallback font. -->
|
||||
<!--[if mso]>
|
||||
<style>
|
||||
* {
|
||||
font-family: Arial, sans-serif !important;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!-- All other clients get the webfont reference; some will render the font and others will silently fail to the fallbacks. More on that here: http://stylecampaign.com/blog/2015/02/webfont-support-in-email/ -->
|
||||
<!--[if !mso]><!-->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Spartan:wght@500;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
||||
<!--<![endif]-->
|
||||
<!-- Web Font / @font-face : END -->
|
||||
<!-- CSS Reset -->
|
||||
<style>
|
||||
@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;
|
||||
padding: 0 !important;
|
||||
height: 100% !important;
|
||||
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;
|
||||
cursor: default !important;
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
font-size: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
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 {
|
||||
min-width: 375px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- Progressive Enhancements -->
|
||||
<style>
|
||||
/* What it does: Hover styles for buttons */
|
||||
|
||||
.button-td,
|
||||
.button-a {
|
||||
transition: all 100ms ease-in;
|
||||
}
|
||||
|
||||
.button-td:hover,
|
||||
.button-a:hover {
|
||||
background: #000000 !important;
|
||||
border-color: #000000 !important;
|
||||
color: white !important;
|
||||
}
|
||||
/* Media Queries */
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
/* What it does: Forces elements to resize to the full width of their container. Useful for resizing images beyond their max-width. */
|
||||
.fluid {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
/* What it does: Forces table cells into full-width rows. */
|
||||
.stack-column,
|
||||
.stack-column-center {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
direction: ltr !important;
|
||||
}
|
||||
/* And center justify these ones. */
|
||||
.stack-column-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
/* What it does: Generic utility class for centering. Useful for images, buttons, and nested tables. */
|
||||
.center-on-narrow {
|
||||
text-align: center !important;
|
||||
display: block !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
float: none !important;
|
||||
}
|
||||
table.center-on-narrow {
|
||||
display: inline-block !important;
|
||||
}
|
||||
/* What it does: Adjust typography on small screens to improve readability */
|
||||
.email-container p {
|
||||
font-size: 17px !important;
|
||||
line-height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- What it does: Makes background images in 72ppi Outlook render at correct size. -->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body width="100%" bgcolor="#F1F1F1" style="margin: 0; mso-line-height-rule: exactly;">
|
||||
<center style="width: 100%; background: #F1F1F1; text-align: left;">
|
||||
<!-- Visually Hidden Preheader Text : BEGIN -->
|
||||
<div style="display:none;font-size:1px;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;mso-hide:all;font-family: sans-serif;"> Email confirmation is required for continued access. Click the button to confirm your email. </div>
|
||||
<!-- Visually Hidden Preheader Text : END -->
|
||||
<!--
|
||||
Set the email width. Defined in two places:
|
||||
1. max-width for all clients except Desktop Windows Outlook, allowing the email to squish on narrow but never go wider than 680px.
|
||||
2. MSO tags for Desktop Windows Outlook enforce a 680px width.
|
||||
Note: The Fluid and Responsive templates have a different width (600px). The hybrid grid is more "fragile", and I've found that 680px is a good width. Change with caution.
|
||||
-->
|
||||
<div style="max-width: 680px; margin: auto;" class="email-container">
|
||||
<!--[if mso]>
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="680" align="center">
|
||||
<tr>
|
||||
<td>
|
||||
<![endif]-->
|
||||
<!-- Email Body : BEGIN -->
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" width="100%" style="max-width: 680px;" class="email-container">
|
||||
<!-- HEADER : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#4AC694">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="margin-left: -10px;padding: 10px 40px 10px 40px; text-align: center;"> <img src="https://www.kavitareader.com/img/email/logo-256.png" alt="kavita_logo" width="75" style="height:auto;display:inline-block;vertical-align:middle;" />
|
||||
<div style="min-height:75px;line-height:75px;display:inline-block;vertical-align:middle;color:#fff;font-family: 'Spartan', sans-serif;font-size:2rem;font-weight:700;">Kavita</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HEADER : END -->
|
||||
<!-- HERO : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#fff" align="center" valign="top" style="text-align: center; background-position: center center !important; background-size: cover !important;">
|
||||
<!--[if gte mso 9]>
|
||||
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:680px; height:380px; background-position: center center !important;">
|
||||
<v:fill type="tile" src="background.png" color="#222222" />
|
||||
<v:textbox inset="0,0,0,0">
|
||||
<![endif]-->
|
||||
<div>
|
||||
<!--[if mso]>
|
||||
<table role="presentation" border="0" cellspacing="0" cellpadding="0" align="center" width="500">
|
||||
<tr>
|
||||
<td align="center" valign="middle" width="500">
|
||||
<![endif]-->
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="max-width:500px; margin: auto;">
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">Forgot your password?</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">That's okay, it happens! Click on the button below to reset your password.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="center" style="text-align: center; padding: 15px 0px 20px 0px;">
|
||||
<!-- Button : BEGIN -->
|
||||
<center>
|
||||
<table role="presentation" align="center" cellspacing="0" cellpadding="0" border="0" class="center-on-narrow" style="text-align: center;">
|
||||
<tr>
|
||||
<td style="border-radius: 50px; background: #153643; text-align: center;" class="button-td">
|
||||
<a href="{{Link}}" style="background: #153643; border: 15px solid #153643; font-family: 'Montserrat', sans-serif; font-size: 14px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 50px; font-weight: bold;" class="button-a"> <span style="color:#ffffff;" class="button-link"> RESET YOUR PASSWORD </span> </a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<!-- Button : END -->
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 0px 20px 0px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If you did not perform this action, ignore this email.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If the button above does not work, please find the link here: <a style="color:inherit;margin: 0;width: 100%;word-break: break-all;" href="{{Link}}">{{Link}}</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
<!--[if gte mso 9]>
|
||||
</v:textbox>
|
||||
</v:rect>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HERO : END -->
|
||||
<!-- SOCIAL : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#292828">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 15px 30px; text-align: center;">
|
||||
<table align="center" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://discord.gg/b52wT37kt7"><img style="width:25px" src="https://www.kavitareader.com/img/email/discord-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Discord"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://www.reddit.com/r/KavitaManga/"><img style="width:25px" src="https://www.kavitareader.com/img/email/reddit-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Reddit"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://github.com/Kareadita/Kavita/"><img style="width:25px" src="https://www.kavitareader.com/img/email/github-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Github"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://opencollective.com/kavita"><img style="width:25px" src="https://www.kavitareader.com/img/email/open-collective-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Open Collective"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- SOCIAL : END -->
|
||||
</table>
|
||||
<!-- Email Body : END -->
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">Forgot your password?</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">That's okay, it happens! Click on the button below to reset your password.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" align="center" style="text-align: center; padding: 15px 0px 20px 0px;">
|
||||
<!-- Button : BEGIN -->
|
||||
<center>
|
||||
<table role="presentation" align="center" cellspacing="0" cellpadding="0" border="0" class="center-on-narrow" style="text-align: center;">
|
||||
<tr>
|
||||
<td style="border-radius: 50px; background: #153643; text-align: center;" class="button-td">
|
||||
<a href="{{Link}}" style="background: #153643; border: 15px solid #153643; font-family: 'Montserrat', sans-serif; font-size: 14px; line-height: 1.1; text-align: center; text-decoration: none; display: block; border-radius: 50px; font-weight: bold;" class="button-a"> <span style="color:#ffffff;" class="button-link"> RESET YOUR PASSWORD </span> </a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</center>
|
||||
<!-- Button : END -->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 0px 20px 0px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If you did not perform this action, ignore this email.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 12px; line-height: 20px;">
|
||||
<p style="margin: 0;">If the button above does not work, please find the link here: <a style="color:inherit;margin: 0;width: 100%;word-break: break-all;" href="{{Link}}">{{Link}}</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -1,325 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!-- utf-8 works for most cases -->
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!-- Forcing initial-scale shouldn't be necessary -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!-- Use the latest (edge) version of IE rendering engine -->
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<!-- Disable auto-scale in iOS 10 Mail entirely -->
|
||||
<title>Event - [Plain HTML]</title>
|
||||
<!-- The title tag shows in email notifications, like Android 4.4. -->
|
||||
<!-- Web Font / @font-face : BEGIN -->
|
||||
<!-- NOTE: If web fonts are not required, lines 10 - 27 can be safely removed. -->
|
||||
<!-- Desktop Outlook chokes on web font references and defaults to Times New Roman, so we force a safe fallback font. -->
|
||||
<!--[if mso]>
|
||||
<style>
|
||||
* {
|
||||
font-family: Arial, sans-serif !important;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!-- All other clients get the webfont reference; some will render the font and others will silently fail to the fallbacks. More on that here: http://stylecampaign.com/blog/2015/02/webfont-support-in-email/ -->
|
||||
<!--[if !mso]><!-->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Spartan:wght@500;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
||||
<!--<![endif]-->
|
||||
<!-- Web Font / @font-face : END -->
|
||||
<!-- CSS Reset -->
|
||||
<style>
|
||||
@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;
|
||||
padding: 0 !important;
|
||||
height: 100% !important;
|
||||
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;
|
||||
cursor: default !important;
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
font-size: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
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 {
|
||||
min-width: 375px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- Progressive Enhancements -->
|
||||
<style>
|
||||
/* What it does: Hover styles for buttons */
|
||||
|
||||
.button-td,
|
||||
.button-a {
|
||||
transition: all 100ms ease-in;
|
||||
}
|
||||
|
||||
.button-td:hover,
|
||||
.button-a:hover {
|
||||
background: #000000 !important;
|
||||
border-color: #000000 !important;
|
||||
color: white !important;
|
||||
}
|
||||
/* Media Queries */
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
/* What it does: Forces elements to resize to the full width of their container. Useful for resizing images beyond their max-width. */
|
||||
.fluid {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
/* What it does: Forces table cells into full-width rows. */
|
||||
.stack-column,
|
||||
.stack-column-center {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
direction: ltr !important;
|
||||
}
|
||||
/* And center justify these ones. */
|
||||
.stack-column-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
/* What it does: Generic utility class for centering. Useful for images, buttons, and nested tables. */
|
||||
.center-on-narrow {
|
||||
text-align: center !important;
|
||||
display: block !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
float: none !important;
|
||||
}
|
||||
table.center-on-narrow {
|
||||
display: inline-block !important;
|
||||
}
|
||||
/* What it does: Adjust typography on small screens to improve readability */
|
||||
.email-container p {
|
||||
font-size: 17px !important;
|
||||
line-height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- What it does: Makes background images in 72ppi Outlook render at correct size. -->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body width="100%" bgcolor="#F1F1F1" style="margin: 0; mso-line-height-rule: exactly;">
|
||||
<center style="width: 100%; background: #F1F1F1; text-align: left;">
|
||||
<!-- Visually Hidden Preheader Text : BEGIN -->
|
||||
<div style="display:none;font-size:1px;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;mso-hide:all;font-family: sans-serif;">
|
||||
This is a Test Email
|
||||
</div>
|
||||
<!-- Visually Hidden Preheader Text : END -->
|
||||
<!--
|
||||
Set the email width. Defined in two places:
|
||||
1. max-width for all clients except Desktop Windows Outlook, allowing the email to squish on narrow but never go wider than 680px.
|
||||
2. MSO tags for Desktop Windows Outlook enforce a 680px width.
|
||||
Note: The Fluid and Responsive templates have a different width (600px). The hybrid grid is more "fragile", and I've found that 680px is a good width. Change with caution.
|
||||
-->
|
||||
<div style="max-width: 680px; margin: auto;" class="email-container">
|
||||
<!--[if mso]>
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="680" align="center">
|
||||
<tr>
|
||||
<td>
|
||||
<![endif]-->
|
||||
<!-- Email Body : BEGIN -->
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" width="100%" style="max-width: 680px;" class="email-container">
|
||||
<!-- HEADER : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#4AC694">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="margin-left: -10px;padding: 10px 40px 10px 40px; text-align: center;"> <img src="https://www.kavitareader.com/img/email/logo-256.png" alt="kavita_logo" width="75" style="height:auto;display:inline-block;vertical-align:middle;" />
|
||||
<div style="min-height:75px;line-height:75px;display:inline-block;vertical-align:middle;color:#fff;font-family: 'Spartan', sans-serif;font-size:2rem;font-weight:700;">Kavita</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HEADER : END -->
|
||||
<!-- HERO : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#fff" align="center" valign="top" style="text-align: center; background-position: center center !important; background-size: cover !important;">
|
||||
<!--[if gte mso 9]>
|
||||
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:680px; height:380px; background-position: center center !important;">
|
||||
<v:fill type="tile" src="background.png" color="#222222" />
|
||||
<v:textbox inset="0,0,0,0">
|
||||
<![endif]-->
|
||||
<div>
|
||||
<!--[if mso]>
|
||||
<table role="presentation" border="0" cellspacing="0" cellpadding="0" align="center" width="500">
|
||||
<tr>
|
||||
<td align="center" valign="middle" width="500">
|
||||
<![endif]-->
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="max-width:500px; margin: auto;">
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">This is a Test Email</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">If you've received this email, your KavitaEmail is setup</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
<!--[if gte mso 9]>
|
||||
</v:textbox>
|
||||
</v:rect>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HERO : END -->
|
||||
<!-- SOCIAL : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#292828">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 15px 30px; text-align: center;">
|
||||
<table align="center" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://discord.gg/b52wT37kt7"><img style="width:25px" src="https://www.kavitareader.com/img/email/discord-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Discord"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://www.reddit.com/r/KavitaManga/"><img style="width:25px" src="https://www.kavitareader.com/img/email/reddit-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Reddit"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://github.com/Kareadita/Kavita/"><img style="width:25px" src="https://www.kavitareader.com/img/email/github-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Github"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://opencollective.com/kavita"><img style="width:25px" src="https://www.kavitareader.com/img/email/open-collective-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Open Collective"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- SOCIAL : END -->
|
||||
</table>
|
||||
<!-- Email Body : END -->
|
||||
<!--[if mso]>
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">This is a Test Email</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">If you've received this email, your KavitaEmail is setup</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -1,323 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!-- utf-8 works for most cases -->
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!-- Forcing initial-scale shouldn't be necessary -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!-- Use the latest (edge) version of IE rendering engine -->
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<!-- Disable auto-scale in iOS 10 Mail entirely -->
|
||||
<title>Event - [Plain HTML]</title>
|
||||
<!-- The title tag shows in email notifications, like Android 4.4. -->
|
||||
<!-- Web Font / @font-face : BEGIN -->
|
||||
<!-- NOTE: If web fonts are not required, lines 10 - 27 can be safely removed. -->
|
||||
<!-- Desktop Outlook chokes on web font references and defaults to Times New Roman, so we force a safe fallback font. -->
|
||||
<!--[if mso]>
|
||||
<style>
|
||||
* {
|
||||
font-family: Arial, sans-serif !important;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!-- All other clients get the webfont reference; some will render the font and others will silently fail to the fallbacks. More on that here: http://stylecampaign.com/blog/2015/02/webfont-support-in-email/ -->
|
||||
<!--[if !mso]><!-->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Spartan:wght@500;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
||||
<!--<![endif]-->
|
||||
<!-- Web Font / @font-face : END -->
|
||||
<!-- CSS Reset -->
|
||||
<style>
|
||||
@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;
|
||||
padding: 0 !important;
|
||||
height: 100% !important;
|
||||
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;
|
||||
cursor: default !important;
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
font-size: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
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 {
|
||||
min-width: 375px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- Progressive Enhancements -->
|
||||
<style>
|
||||
/* What it does: Hover styles for buttons */
|
||||
|
||||
.button-td,
|
||||
.button-a {
|
||||
transition: all 100ms ease-in;
|
||||
}
|
||||
|
||||
.button-td:hover,
|
||||
.button-a:hover {
|
||||
background: #000000 !important;
|
||||
border-color: #000000 !important;
|
||||
color: white !important;
|
||||
}
|
||||
/* Media Queries */
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
/* What it does: Forces elements to resize to the full width of their container. Useful for resizing images beyond their max-width. */
|
||||
.fluid {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
/* What it does: Forces table cells into full-width rows. */
|
||||
.stack-column,
|
||||
.stack-column-center {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
direction: ltr !important;
|
||||
}
|
||||
/* And center justify these ones. */
|
||||
.stack-column-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
/* What it does: Generic utility class for centering. Useful for images, buttons, and nested tables. */
|
||||
.center-on-narrow {
|
||||
text-align: center !important;
|
||||
display: block !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
float: none !important;
|
||||
}
|
||||
table.center-on-narrow {
|
||||
display: inline-block !important;
|
||||
}
|
||||
/* What it does: Adjust typography on small screens to improve readability */
|
||||
.email-container p {
|
||||
font-size: 17px !important;
|
||||
line-height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- What it does: Makes background images in 72ppi Outlook render at correct size. -->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body width="100%" bgcolor="#F1F1F1" style="margin: 0; mso-line-height-rule: exactly;">
|
||||
<center style="width: 100%; background: #F1F1F1; text-align: left;">
|
||||
<!-- Visually Hidden Preheader Text : BEGIN -->
|
||||
<div style="display:none;font-size:1px;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;mso-hide:all;font-family: sans-serif;"> You've been sent a file from Kavita! </div>
|
||||
<!-- Visually Hidden Preheader Text : END -->
|
||||
<!--
|
||||
Set the email width. Defined in two places:
|
||||
1. max-width for all clients except Desktop Windows Outlook, allowing the email to squish on narrow but never go wider than 680px.
|
||||
2. MSO tags for Desktop Windows Outlook enforce a 680px width.
|
||||
Note: The Fluid and Responsive templates have a different width (600px). The hybrid grid is more "fragile", and I've found that 680px is a good width. Change with caution.
|
||||
-->
|
||||
<div style="max-width: 680px; margin: auto;" class="email-container">
|
||||
<!--[if mso]>
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="680" align="center">
|
||||
<tr>
|
||||
<td>
|
||||
<![endif]-->
|
||||
<!-- Email Body : BEGIN -->
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" width="100%" style="max-width: 680px;" class="email-container">
|
||||
<!-- HEADER : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#4AC694">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="margin-left: -10px;padding: 10px 40px 10px 40px; text-align: center;"> <img src="https://www.kavitareader.com/img/email/logo-256.png" alt="kavita_logo" width="75" style="height:auto;display:inline-block;vertical-align:middle;" />
|
||||
<div style="min-height:75px;line-height:75px;display:inline-block;vertical-align:middle;color:#fff;font-family: 'Spartan', sans-serif;font-size:2rem;font-weight:700;">Kavita</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HEADER : END -->
|
||||
<!-- HERO : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#fff" align="center" valign="top" style="text-align: center; background-position: center center !important; background-size: cover !important;">
|
||||
<!--[if gte mso 9]>
|
||||
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:680px; height:380px; background-position: center center !important;">
|
||||
<v:fill type="tile" src="background.png" color="#222222" />
|
||||
<v:textbox inset="0,0,0,0">
|
||||
<![endif]-->
|
||||
<div>
|
||||
<!--[if mso]>
|
||||
<table role="presentation" border="0" cellspacing="0" cellpadding="0" align="center" width="500">
|
||||
<tr>
|
||||
<td align="center" valign="middle" width="500">
|
||||
<![endif]-->
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="max-width:500px; margin: auto;">
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">You sent a file from Kavita</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Please find attached the file(s) you've sent.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
<!--[if gte mso 9]>
|
||||
</v:textbox>
|
||||
</v:rect>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HERO : END -->
|
||||
<!-- SOCIAL : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#292828">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 15px 30px; text-align: center;">
|
||||
<table align="center" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://discord.gg/b52wT37kt7"><img style="width:25px" src="https://www.kavitareader.com/img/email/discord-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Discord"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://www.reddit.com/r/KavitaManga/"><img style="width:25px" src="https://www.kavitareader.com/img/email/reddit-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Reddit"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://github.com/Kareadita/Kavita/"><img style="width:25px" src="https://www.kavitareader.com/img/email/github-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Github"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://opencollective.com/kavita"><img style="width:25px" src="https://www.kavitareader.com/img/email/open-collective-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Open Collective"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- SOCIAL : END -->
|
||||
</table>
|
||||
<!-- Email Body : END -->
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 20px 0 10px 20px;">
|
||||
<h1 style="margin: 0; font-family: 'Montserrat', sans-serif; font-size: 30px; line-height: 36px; font-weight: bold;">You sent a file from Kavita</h1> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" style="text-align: center; padding: 10px 20px 15px 20px; font-family: sans-serif; font-size: 18px; line-height: 20px;">
|
||||
<p style="margin: 0;">Please find attached the file(s) you've sent.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
320
API/EmailTemplates/base.html
Normal file
320
API/EmailTemplates/base.html
Normal file
|
@ -0,0 +1,320 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!-- utf-8 works for most cases -->
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!-- Forcing initial-scale shouldn't be necessary -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!-- Use the latest (edge) version of IE rendering engine -->
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<!-- Disable auto-scale in iOS 10 Mail entirely -->
|
||||
<title>Kavita - [Plain HTML]</title>
|
||||
<!-- The title tag shows in email notifications, like Android 4.4. -->
|
||||
<!-- Web Font / @font-face : BEGIN -->
|
||||
<!-- NOTE: If web fonts are not required, lines 10 - 27 can be safely removed. -->
|
||||
<!-- Desktop Outlook chokes on web font references and defaults to Times New Roman, so we force a safe fallback font. -->
|
||||
<!--[if mso]>
|
||||
<style>
|
||||
* {
|
||||
font-family: Arial, sans-serif !important;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!-- All other clients get the webfont reference; some will render the font and others will silently fail to the fallbacks. More on that here: http://stylecampaign.com/blog/2015/02/webfont-support-in-email/ -->
|
||||
<!--[if !mso]><!-->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Spartan:wght@500;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
||||
<!--<![endif]-->
|
||||
<!-- Web Font / @font-face : END -->
|
||||
<!-- CSS Reset -->
|
||||
<style>
|
||||
@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;
|
||||
padding: 0 !important;
|
||||
height: 100% !important;
|
||||
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;
|
||||
cursor: default !important;
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
font-size: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
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 {
|
||||
min-width: 375px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- Progressive Enhancements -->
|
||||
<style>
|
||||
/* What it does: Hover styles for buttons */
|
||||
|
||||
.button-td,
|
||||
.button-a {
|
||||
transition: all 100ms ease-in;
|
||||
}
|
||||
|
||||
.button-td:hover,
|
||||
.button-a:hover {
|
||||
background: #000000 !important;
|
||||
border-color: #000000 !important;
|
||||
color: white !important;
|
||||
}
|
||||
/* Media Queries */
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
/* What it does: Forces elements to resize to the full width of their container. Useful for resizing images beyond their max-width. */
|
||||
.fluid {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
/* What it does: Forces table cells into full-width rows. */
|
||||
.stack-column,
|
||||
.stack-column-center {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
direction: ltr !important;
|
||||
}
|
||||
/* And center justify these ones. */
|
||||
.stack-column-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
/* What it does: Generic utility class for centering. Useful for images, buttons, and nested tables. */
|
||||
.center-on-narrow {
|
||||
text-align: center !important;
|
||||
display: block !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
float: none !important;
|
||||
}
|
||||
table.center-on-narrow {
|
||||
display: inline-block !important;
|
||||
}
|
||||
/* What it does: Adjust typography on small screens to improve readability */
|
||||
.email-container p {
|
||||
font-size: 17px !important;
|
||||
line-height: 22px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<!-- What it does: Makes background images in 72ppi Outlook render at correct size. -->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body width="100%" bgcolor="#F1F1F1" style="margin: 0; mso-line-height-rule: exactly;">
|
||||
<center style="width: 100%; background: #F1F1F1; text-align: left;">
|
||||
<!-- Visually Hidden Preheader Text : BEGIN -->
|
||||
<div style="display:none;font-size:1px;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;mso-hide:all;font-family: sans-serif;"> Your account's email has been updated on {{InvitingUser}}'s Kavita instance. Click the button to validate your email.</div>
|
||||
<!-- Visually Hidden Preheader Text : END -->
|
||||
<!--
|
||||
Set the email width. Defined in two places:
|
||||
1. max-width for all clients except Desktop Windows Outlook, allowing the email to squish on narrow but never go wider than 680px.
|
||||
2. MSO tags for Desktop Windows Outlook enforce a 680px width.
|
||||
Note: The Fluid and Responsive templates have a different width (600px). The hybrid grid is more "fragile", and I've found that 680px is a good width. Change with caution.
|
||||
-->
|
||||
<div style="max-width: 680px; margin: auto;" class="email-container">
|
||||
<!--[if mso]>
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="680" align="center">
|
||||
<tr>
|
||||
<td>
|
||||
<![endif]-->
|
||||
<!-- Email Body : BEGIN -->
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" width="100%" style="max-width: 680px;" class="email-container">
|
||||
<!-- HEADER : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#4AC694">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="margin-left: -10px;padding: 10px 40px 10px 40px; text-align: center;"> <img src="https://www.kavitareader.com/img/email/logo-256.png" alt="kavita_logo" width="75" style="height:auto;display:inline-block;vertical-align:middle;" />
|
||||
<div style="min-height:75px;line-height:75px;display:inline-block;vertical-align:middle;color:#fff;font-family: 'Spartan', sans-serif;font-size:2rem;font-weight:700;">Kavita</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HEADER : END -->
|
||||
<!-- HERO : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#fff" align="center" valign="top" style="text-align: center; background-position: center center !important; background-size: cover !important;">
|
||||
<!--[if gte mso 9]>
|
||||
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:680px; height:380px; background-position: center center !important;">
|
||||
<v:fill type="tile" src="background.png" color="#222222" />
|
||||
<v:textbox inset="0,0,0,0">
|
||||
<![endif]-->
|
||||
<div>
|
||||
<!--[if mso]>
|
||||
<table role="presentation" border="0" cellspacing="0" cellpadding="0" align="center" width="500">
|
||||
<tr>
|
||||
<td align="center" valign="middle" width="500">
|
||||
<![endif]-->
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="max-width:500px; margin: auto;">
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
{{Body}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td height="20" style="font-size:20px; line-height:20px;"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- SOCIAL : BEGIN -->
|
||||
<tr>
|
||||
<td bgcolor="#292828">
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 15px 30px; text-align: center;">
|
||||
<table align="center" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://discord.gg/b52wT37kt7"><img style="width:25px" src="https://www.kavitareader.com/img/email/discord-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Discord"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://www.reddit.com/r/KavitaManga/"><img style="width:25px" src="https://www.kavitareader.com/img/email/reddit-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Reddit"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://github.com/Kareadita/Kavita/"><img style="width:25px" src="https://www.kavitareader.com/img/email/github-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Github"></a>
|
||||
</td>
|
||||
<td width="20"> </td>
|
||||
<td>
|
||||
<a href="https://opencollective.com/kavita"><img style="width:25px" src="https://www.kavitareader.com/img/email/open-collective-white.png" width="" height="" style="margin:0; padding:0; border:none; display:block;" border="0" alt="Open Collective"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- SOCIAL : END -->
|
||||
</tr>
|
||||
</table>
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
<!--[if gte mso 9]>
|
||||
</v:textbox>
|
||||
</v:rect>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<!-- HERO : END -->
|
||||
|
||||
</table>
|
||||
<!-- Email Body : END -->
|
||||
<!--[if mso]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -36,12 +36,20 @@ public class ScrobbleEvent : IEntityDate
|
|||
/// <summary>
|
||||
/// Depends on the ScrobbleEvent if filled in
|
||||
/// </summary>
|
||||
public int? VolumeNumber { get; set; } // TODO: Migrate this to float
|
||||
public float? VolumeNumber { get; set; }
|
||||
/// <summary>
|
||||
/// Has this event been processed and pushed to Provider
|
||||
/// </summary>
|
||||
public bool IsProcessed { get; set; }
|
||||
/// <summary>
|
||||
/// Was there an error processing this event
|
||||
/// </summary>
|
||||
public bool IsErrored { get; set; }
|
||||
/// <summary>
|
||||
/// The error details
|
||||
/// </summary>
|
||||
public string? ErrorDetails { get; set; }
|
||||
/// <summary>
|
||||
/// The date this was processed
|
||||
/// </summary>
|
||||
public DateTime? ProcessDateUtc { get; set; }
|
||||
|
|
|
@ -246,10 +246,15 @@ public class EmailService : IEmailService
|
|||
};
|
||||
email.From.Add(new MailboxAddress(smtpConfig.SenderDisplayName, smtpConfig.SenderAddress));
|
||||
|
||||
// Inject the body into the base template
|
||||
var fullBody = UpdatePlaceHolders(await GetEmailBody("base"), new List<KeyValuePair<string, string>>()
|
||||
{
|
||||
new ("{{Body}}", userEmailOptions.Body)
|
||||
});
|
||||
|
||||
var body = new BodyBuilder
|
||||
{
|
||||
HtmlBody = userEmailOptions.Body
|
||||
HtmlBody = fullBody
|
||||
};
|
||||
|
||||
if (userEmailOptions.Attachments != null)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
|
@ -14,6 +15,7 @@ using API.Entities.Metadata;
|
|||
using API.Extensions;
|
||||
using AutoMapper;
|
||||
using Flurl.Http;
|
||||
using Hangfire;
|
||||
using Kavita.Common;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Kavita.Common.Helpers;
|
||||
|
@ -49,6 +51,15 @@ public interface IExternalMetadataService
|
|||
Task<ExternalSeriesDetailDto?> GetExternalSeriesDetail(int? aniListId, long? malId, int? seriesId);
|
||||
Task<SeriesDetailPlusDto> GetSeriesDetailPlus(int seriesId, LibraryType libraryType);
|
||||
Task ForceKavitaPlusRefresh(int seriesId);
|
||||
Task FetchExternalDataTask();
|
||||
/// <summary>
|
||||
/// This is an entry point and provides a level of protection against calling upstream API. Will only allow 100 new
|
||||
/// series to fetch data within a day and enqueues background jobs at certain times to fetch that data.
|
||||
/// </summary>
|
||||
/// <param name="seriesId"></param>
|
||||
/// <param name="libraryType"></param>
|
||||
/// <returns></returns>
|
||||
Task GetNewSeriesData(int seriesId, LibraryType libraryType);
|
||||
}
|
||||
|
||||
public class ExternalMetadataService : IExternalMetadataService
|
||||
|
@ -58,6 +69,7 @@ public class ExternalMetadataService : IExternalMetadataService
|
|||
private readonly IMapper _mapper;
|
||||
private readonly ILicenseService _licenseService;
|
||||
private readonly TimeSpan _externalSeriesMetadataCache = TimeSpan.FromDays(30);
|
||||
public static readonly ImmutableArray<LibraryType> NonEligibleLibraryTypes = ImmutableArray.Create<LibraryType>(LibraryType.Comic);
|
||||
private readonly SeriesDetailPlusDto _defaultReturn = new()
|
||||
{
|
||||
Recommendations = null,
|
||||
|
@ -72,6 +84,8 @@ public class ExternalMetadataService : IExternalMetadataService
|
|||
_mapper = mapper;
|
||||
_licenseService = licenseService;
|
||||
|
||||
|
||||
|
||||
FlurlHttp.ConfigureClient(Configuration.KavitaPlusApiUrl, cli =>
|
||||
cli.Settings.HttpClientFactory = new UntrustedCertClientFactory());
|
||||
}
|
||||
|
@ -83,7 +97,34 @@ public class ExternalMetadataService : IExternalMetadataService
|
|||
/// <returns></returns>
|
||||
public static bool IsPlusEligible(LibraryType type)
|
||||
{
|
||||
return type != LibraryType.Comic;
|
||||
return !NonEligibleLibraryTypes.Contains(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a task that runs on a schedule and slowly fetches data from Kavita+ to keep
|
||||
/// data in the DB non-stale and fetched.
|
||||
/// </summary>
|
||||
/// <remarks>To avoid blasting Kavita+ API, this only processes a few records. The goal is to slowly build </remarks>
|
||||
/// <returns></returns>
|
||||
[DisableConcurrentExecution(60 * 60 * 60)]
|
||||
[AutomaticRetry(Attempts = 3, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
|
||||
public async Task FetchExternalDataTask()
|
||||
{
|
||||
// Find all Series that are eligible and limit
|
||||
var ids = await _unitOfWork.ExternalSeriesMetadataRepository.GetAllSeriesIdsWithoutMetadata(25);
|
||||
if (ids.Count == 0) return;
|
||||
|
||||
_logger.LogInformation("Started Refreshing {Count} series data from Kavita+", ids.Count);
|
||||
var count = 0;
|
||||
foreach (var seriesId in ids)
|
||||
{
|
||||
// TODO: Rewrite this so it's streamlined and not multiple DB calls
|
||||
var libraryType = await _unitOfWork.LibraryRepository.GetLibraryTypeBySeriesIdAsync(seriesId);
|
||||
await GetSeriesDetailPlus(seriesId, libraryType);
|
||||
await Task.Delay(1500);
|
||||
count++;
|
||||
}
|
||||
_logger.LogInformation("Finished Refreshing {Count} series data from Kavita+", count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -104,6 +145,15 @@ public class ExternalMetadataService : IExternalMetadataService
|
|||
await _unitOfWork.CommitAsync();
|
||||
}
|
||||
|
||||
[DisableConcurrentExecution(60 * 60 * 60)]
|
||||
[AutomaticRetry(Attempts = 3, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
|
||||
public Task GetNewSeriesData(int seriesId, LibraryType libraryType)
|
||||
{
|
||||
// TODO: Implement this task
|
||||
if (!IsPlusEligible(libraryType)) return Task.CompletedTask;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves Metadata about a Recommended External Series
|
||||
/// </summary>
|
||||
|
@ -153,6 +203,7 @@ public class ExternalMetadataService : IExternalMetadataService
|
|||
{
|
||||
var data = await _unitOfWork.SeriesRepository.GetPlusSeriesDto(seriesId);
|
||||
if (data == null) return _defaultReturn;
|
||||
_logger.LogDebug("Fetching Kavita+ Series Detail data for {SeriesName}", data.SeriesName);
|
||||
|
||||
var license = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.LicenseKey)).Value;
|
||||
var result = await (Configuration.KavitaPlusApiUrl + "/api/metadata/v2/series-detail")
|
||||
|
|
|
@ -89,6 +89,9 @@ public class ScrobblingService : IScrobblingService
|
|||
ScrobbleProvider.AniList
|
||||
};
|
||||
|
||||
private const string UnknownSeriesErrorMessage = "Series cannot be matched for Scrobbling";
|
||||
private const string AccessTokenErrorMessage = "Access Token needs to be rotated to continue scrobbling";
|
||||
|
||||
|
||||
public ScrobblingService(IUnitOfWork unitOfWork, ITokenService tokenService,
|
||||
IEventHub eventHub, ILogger<ScrobblingService> logger, ILicenseService licenseService,
|
||||
|
@ -374,7 +377,7 @@ public class ScrobblingService : IScrobblingService
|
|||
|
||||
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(series.LibraryId);
|
||||
if (library is not {AllowScrobbling: true}) return true;
|
||||
if (library.Type == LibraryType.Comic) return true;
|
||||
if (!ExternalMetadataService.IsPlusEligible(library.Type)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -424,14 +427,18 @@ public class ScrobblingService : IScrobblingService
|
|||
if (response.ErrorMessage != null && response.ErrorMessage.Contains("Too Many Requests"))
|
||||
{
|
||||
_logger.LogInformation("Hit Too many requests, sleeping to regain requests");
|
||||
await Task.Delay(TimeSpan.FromMinutes(5));
|
||||
await Task.Delay(TimeSpan.FromMinutes(10));
|
||||
} else if (response.ErrorMessage != null && response.ErrorMessage.Contains("Unauthorized"))
|
||||
{
|
||||
_logger.LogInformation("Kavita+ responded with Unauthorized. Please check your subscription");
|
||||
_logger.LogCritical("Kavita+ responded with Unauthorized. Please check your subscription");
|
||||
await _licenseService.HasActiveLicense(true);
|
||||
evt.IsErrored = true;
|
||||
evt.ErrorDetails = "Kavita+ subscription no longer active";
|
||||
throw new KavitaException("Kavita+ responded with Unauthorized. Please check your subscription");
|
||||
} else if (response.ErrorMessage != null && response.ErrorMessage.Contains("Access token is invalid"))
|
||||
{
|
||||
evt.IsErrored = true;
|
||||
evt.ErrorDetails = AccessTokenErrorMessage;
|
||||
throw new KavitaException("Access token is invalid");
|
||||
}
|
||||
else if (response.ErrorMessage != null && response.ErrorMessage.Contains("Unknown Series"))
|
||||
|
@ -442,12 +449,16 @@ public class ScrobblingService : IScrobblingService
|
|||
{
|
||||
_unitOfWork.ScrobbleRepository.Attach(new ScrobbleError()
|
||||
{
|
||||
Comment = "Unknown Series",
|
||||
Comment = UnknownSeriesErrorMessage,
|
||||
Details = data.SeriesName,
|
||||
LibraryId = evt.LibraryId,
|
||||
SeriesId = evt.SeriesId
|
||||
});
|
||||
await _unitOfWork.ExternalSeriesMetadataRepository.CreateBlacklistedSeries(evt.SeriesId, false);
|
||||
}
|
||||
|
||||
evt.IsErrored = true;
|
||||
evt.ErrorDetails = UnknownSeriesErrorMessage;
|
||||
} else if (response.ErrorMessage != null && response.ErrorMessage.StartsWith("Review"))
|
||||
{
|
||||
// Log the Series name and Id in ScrobbleErrors
|
||||
|
@ -462,8 +473,11 @@ public class ScrobblingService : IScrobblingService
|
|||
SeriesId = evt.SeriesId
|
||||
});
|
||||
}
|
||||
evt.IsErrored = true;
|
||||
evt.ErrorDetails = "Review was unable to be saved due to upstream requirements";
|
||||
}
|
||||
|
||||
evt.IsErrored = true;
|
||||
_logger.LogError("Scrobbling failed due to {ErrorMessage}: {SeriesName}", response.ErrorMessage, data.SeriesName);
|
||||
throw new KavitaException($"Scrobbling failed due to {response.ErrorMessage}: {data.SeriesName}");
|
||||
}
|
||||
|
@ -479,12 +493,14 @@ public class ScrobblingService : IScrobblingService
|
|||
{
|
||||
_unitOfWork.ScrobbleRepository.Attach(new ScrobbleError()
|
||||
{
|
||||
Comment = "Unknown Series",
|
||||
Comment = UnknownSeriesErrorMessage,
|
||||
Details = data.SeriesName,
|
||||
LibraryId = evt.LibraryId,
|
||||
SeriesId = evt.SeriesId
|
||||
});
|
||||
}
|
||||
evt.IsErrored = true;
|
||||
evt.ErrorDetails = "Bad payload from Scrobble Provider";
|
||||
throw new KavitaException("Bad payload from Scrobble Provider");
|
||||
}
|
||||
throw;
|
||||
|
@ -602,11 +618,10 @@ public class ScrobblingService : IScrobblingService
|
|||
.ToImmutableHashSet();
|
||||
|
||||
var errors = (await _unitOfWork.ScrobbleRepository.GetScrobbleErrors())
|
||||
.Where(e => e.Comment == "Unknown Series")
|
||||
.Where(e => e.Comment == "Unknown Series" || e.Comment == UnknownSeriesErrorMessage || e.Comment == AccessTokenErrorMessage)
|
||||
.Select(e => e.SeriesId)
|
||||
.ToList();
|
||||
|
||||
|
||||
var readEvents = (await _unitOfWork.ScrobbleRepository.GetByEvent(ScrobbleEventType.ChapterRead))
|
||||
.Where(e => librariesWithScrobbling.Contains(e.LibraryId))
|
||||
.Where(e => !errors.Contains(e.SeriesId))
|
||||
|
@ -674,7 +689,7 @@ public class ScrobblingService : IScrobblingService
|
|||
MALId = (int?) evt.MalId,
|
||||
ScrobbleEventType = evt.ScrobbleEventType,
|
||||
ChapterNumber = evt.ChapterNumber,
|
||||
VolumeNumber = evt.VolumeNumber,
|
||||
VolumeNumber = (int?) evt.VolumeNumber,
|
||||
AniListToken = evt.AppUser.AniListAccessToken,
|
||||
SeriesName = evt.Series.Name,
|
||||
LocalizedSeriesName = evt.Series.LocalizedName,
|
||||
|
@ -706,7 +721,7 @@ public class ScrobblingService : IScrobblingService
|
|||
MALId = (int?) evt.MalId,
|
||||
ScrobbleEventType = evt.ScrobbleEventType,
|
||||
ChapterNumber = evt.ChapterNumber,
|
||||
VolumeNumber = evt.VolumeNumber,
|
||||
VolumeNumber = (int?) evt.VolumeNumber,
|
||||
AniListToken = evt.AppUser.AniListAccessToken,
|
||||
SeriesName = evt.Series.Name,
|
||||
LocalizedSeriesName = evt.Series.LocalizedName,
|
||||
|
@ -770,6 +785,23 @@ public class ScrobblingService : IScrobblingService
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (await _unitOfWork.ExternalSeriesMetadataRepository.IsBlacklistedSeries(evt.SeriesId))
|
||||
{
|
||||
_unitOfWork.ScrobbleRepository.Attach(new ScrobbleError()
|
||||
{
|
||||
Comment = UnknownSeriesErrorMessage,
|
||||
Details = $"User: {evt.AppUser.UserName} Series: {evt.Series.Name}",
|
||||
LibraryId = evt.LibraryId,
|
||||
SeriesId = evt.SeriesId
|
||||
});
|
||||
evt.IsErrored = true;
|
||||
evt.ErrorDetails = "Series cannot be matched for Scrobbling";
|
||||
evt.ProcessDateUtc = DateTime.UtcNow;
|
||||
_unitOfWork.ScrobbleRepository.Update(evt);
|
||||
await _unitOfWork.CommitAsync();
|
||||
return 0;
|
||||
}
|
||||
|
||||
var count = await SetAndCheckRateLimit(userRateLimits, evt.AppUser, license.Value);
|
||||
userRateLimits[evt.AppUserId] = count;
|
||||
if (count == 0)
|
||||
|
@ -796,6 +828,9 @@ public class ScrobblingService : IScrobblingService
|
|||
if (ex.Message.Contains("Access token is invalid"))
|
||||
{
|
||||
_logger.LogCritical("Access Token for UserId: {UserId} needs to be rotated to continue scrobbling", evt.AppUser.Id);
|
||||
evt.IsErrored = true;
|
||||
evt.ErrorDetails = AccessTokenErrorMessage;
|
||||
_unitOfWork.ScrobbleRepository.Update(evt);
|
||||
return progressCounter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ public class TaskScheduler : ITaskScheduler
|
|||
private readonly IMediaConversionService _mediaConversionService;
|
||||
private readonly IScrobblingService _scrobblingService;
|
||||
private readonly ILicenseService _licenseService;
|
||||
private readonly IExternalMetadataService _externalMetadataService;
|
||||
|
||||
public static BackgroundJobServer Client => new ();
|
||||
public const string ScanQueue = "scan";
|
||||
|
@ -68,10 +69,11 @@ public class TaskScheduler : ITaskScheduler
|
|||
public const string BackupTaskId = "backup";
|
||||
public const string ScanLibrariesTaskId = "scan-libraries";
|
||||
public const string ReportStatsTaskId = "report-stats";
|
||||
public const string CheckScrobblingTokens = "check-scrobbling-tokens";
|
||||
public const string ProcessScrobblingEvents = "process-scrobbling-events";
|
||||
public const string ProcessProcessedScrobblingEvents = "process-processed-scrobbling-events";
|
||||
public const string LicenseCheck = "license-check";
|
||||
public const string CheckScrobblingTokensId = "check-scrobbling-tokens";
|
||||
public const string ProcessScrobblingEventsId = "process-scrobbling-events";
|
||||
public const string ProcessProcessedScrobblingEventsId = "process-processed-scrobbling-events";
|
||||
public const string LicenseCheckId = "license-check";
|
||||
public const string KavitaPlusDataRefreshId = "kavita+-data-refresh";
|
||||
|
||||
private static readonly ImmutableArray<string> ScanTasks =
|
||||
["ScannerService", "ScanLibrary", "ScanLibraries", "ScanFolder", "ScanSeries"];
|
||||
|
@ -88,7 +90,8 @@ public class TaskScheduler : ITaskScheduler
|
|||
IUnitOfWork unitOfWork, IMetadataService metadataService, IBackupService backupService,
|
||||
ICleanupService cleanupService, IStatsService statsService, IVersionUpdaterService versionUpdaterService,
|
||||
IThemeService themeService, IWordCountAnalyzerService wordCountAnalyzerService, IStatisticService statisticService,
|
||||
IMediaConversionService mediaConversionService, IScrobblingService scrobblingService, ILicenseService licenseService)
|
||||
IMediaConversionService mediaConversionService, IScrobblingService scrobblingService, ILicenseService licenseService,
|
||||
IExternalMetadataService externalMetadataService)
|
||||
{
|
||||
_cacheService = cacheService;
|
||||
_logger = logger;
|
||||
|
@ -105,6 +108,7 @@ public class TaskScheduler : ITaskScheduler
|
|||
_mediaConversionService = mediaConversionService;
|
||||
_scrobblingService = scrobblingService;
|
||||
_licenseService = licenseService;
|
||||
_externalMetadataService = externalMetadataService;
|
||||
}
|
||||
|
||||
public async Task ScheduleTasks()
|
||||
|
@ -121,7 +125,8 @@ public class TaskScheduler : ITaskScheduler
|
|||
}
|
||||
else
|
||||
{
|
||||
RecurringJob.AddOrUpdate(ScanLibrariesTaskId, () => ScanLibraries(false), Cron.Daily, RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(ScanLibrariesTaskId, () => ScanLibraries(false),
|
||||
Cron.Daily, RecurringJobOptions);
|
||||
}
|
||||
|
||||
setting = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.TaskBackup)).Value;
|
||||
|
@ -134,19 +139,24 @@ public class TaskScheduler : ITaskScheduler
|
|||
// Override daily and make 2am so that everything on system has cleaned up and no blocking
|
||||
schedule = Cron.Daily(2);
|
||||
}
|
||||
RecurringJob.AddOrUpdate(BackupTaskId, () => _backupService.BackupDatabase(), () => schedule, RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(BackupTaskId, () => _backupService.BackupDatabase(),
|
||||
() => schedule, RecurringJobOptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
RecurringJob.AddOrUpdate(BackupTaskId, () => _backupService.BackupDatabase(), Cron.Weekly, RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(BackupTaskId, () => _backupService.BackupDatabase(),
|
||||
Cron.Weekly, RecurringJobOptions);
|
||||
}
|
||||
|
||||
setting = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.TaskCleanup)).Value;
|
||||
_logger.LogDebug("Scheduling Cleanup Task for {Setting}", setting);
|
||||
RecurringJob.AddOrUpdate(CleanupTaskId, () => _cleanupService.Cleanup(), CronConverter.ConvertToCronNotation(setting), RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(CleanupTaskId, () => _cleanupService.Cleanup(),
|
||||
CronConverter.ConvertToCronNotation(setting), RecurringJobOptions);
|
||||
|
||||
RecurringJob.AddOrUpdate(RemoveFromWantToReadTaskId, () => _cleanupService.CleanupWantToRead(), Cron.Daily, RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(UpdateYearlyStatsTaskId, () => _statisticService.UpdateServerStatistics(), Cron.Monthly, RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(RemoveFromWantToReadTaskId, () => _cleanupService.CleanupWantToRead(),
|
||||
Cron.Daily, RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(UpdateYearlyStatsTaskId, () => _statisticService.UpdateServerStatistics(),
|
||||
Cron.Monthly, RecurringJobOptions);
|
||||
|
||||
await ScheduleKavitaPlusTasks();
|
||||
}
|
||||
|
@ -159,14 +169,23 @@ public class TaskScheduler : ITaskScheduler
|
|||
{
|
||||
return;
|
||||
}
|
||||
RecurringJob.AddOrUpdate(CheckScrobblingTokens, () => _scrobblingService.CheckExternalAccessTokens(), Cron.Daily, RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(CheckScrobblingTokensId, () => _scrobblingService.CheckExternalAccessTokens(),
|
||||
Cron.Daily, RecurringJobOptions);
|
||||
BackgroundJob.Enqueue(() => _scrobblingService.CheckExternalAccessTokens()); // We also kick off an immediate check on startup
|
||||
RecurringJob.AddOrUpdate(LicenseCheck, () => _licenseService.HasActiveLicense(true), LicenseService.Cron, RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(LicenseCheckId, () => _licenseService.HasActiveLicense(true),
|
||||
LicenseService.Cron, RecurringJobOptions);
|
||||
BackgroundJob.Enqueue(() => _licenseService.HasActiveLicense(true));
|
||||
|
||||
// KavitaPlus Scrobbling (every 4 hours)
|
||||
RecurringJob.AddOrUpdate(ProcessScrobblingEvents, () => _scrobblingService.ProcessUpdatesSinceLastSync(), "0 */4 * * *", RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(ProcessProcessedScrobblingEvents, () => _scrobblingService.ClearProcessedEvents(), Cron.Daily, RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(ProcessScrobblingEventsId, () => _scrobblingService.ProcessUpdatesSinceLastSync(),
|
||||
"0 */4 * * *", RecurringJobOptions);
|
||||
RecurringJob.AddOrUpdate(ProcessProcessedScrobblingEventsId, () => _scrobblingService.ClearProcessedEvents(),
|
||||
Cron.Daily, RecurringJobOptions);
|
||||
|
||||
// Backfilling/Freshening Reviews/Rating/Recommendations (TODO: This will come in v0.8.x)
|
||||
// RecurringJob.AddOrUpdate(KavitaPlusDataRefreshId,
|
||||
// () => _externalMetadataService.FetchExternalDataTask(), Cron.Hourly(Rnd.Next(0, 59)),
|
||||
// RecurringJobOptions);
|
||||
}
|
||||
|
||||
#region StatsTasks
|
||||
|
|
|
@ -13,6 +13,7 @@ using API.Entities.Enums;
|
|||
using API.Extensions;
|
||||
using API.Helpers;
|
||||
using API.Helpers.Builders;
|
||||
using API.Services.Plus;
|
||||
using API.Services.Tasks.Metadata;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using API.SignalR;
|
||||
|
@ -57,6 +58,7 @@ public class ProcessSeries : IProcessSeries
|
|||
private readonly IWordCountAnalyzerService _wordCountAnalyzerService;
|
||||
private readonly ICollectionTagService _collectionTagService;
|
||||
private readonly IReadingListService _readingListService;
|
||||
private readonly IExternalMetadataService _externalMetadataService;
|
||||
|
||||
private Dictionary<string, Genre> _genres;
|
||||
private IList<Person> _people;
|
||||
|
@ -66,7 +68,7 @@ public class ProcessSeries : IProcessSeries
|
|||
public ProcessSeries(IUnitOfWork unitOfWork, ILogger<ProcessSeries> logger, IEventHub eventHub,
|
||||
IDirectoryService directoryService, ICacheHelper cacheHelper, IReadingItemService readingItemService,
|
||||
IFileService fileService, IMetadataService metadataService, IWordCountAnalyzerService wordCountAnalyzerService,
|
||||
ICollectionTagService collectionTagService, IReadingListService readingListService)
|
||||
ICollectionTagService collectionTagService, IReadingListService readingListService, IExternalMetadataService externalMetadataService)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_logger = logger;
|
||||
|
@ -79,6 +81,7 @@ public class ProcessSeries : IProcessSeries
|
|||
_wordCountAnalyzerService = wordCountAnalyzerService;
|
||||
_collectionTagService = collectionTagService;
|
||||
_readingListService = readingListService;
|
||||
_externalMetadataService = externalMetadataService;
|
||||
|
||||
|
||||
_genres = new Dictionary<string, Genre>();
|
||||
|
@ -236,8 +239,9 @@ public class ProcessSeries : IProcessSeries
|
|||
|
||||
if (seriesAdded)
|
||||
{
|
||||
// See if any recommendations can link up to the series
|
||||
// See if any recommendations can link up to the series and pre-fetch external metadata for the series
|
||||
_logger.LogInformation("Linking up External Recommendations new series (if applicable)");
|
||||
await _externalMetadataService.GetNewSeriesData(series.Id, series.Library.Type);
|
||||
await _unitOfWork.ExternalSeriesMetadataRepository.LinkRecommendationsToSeries(series);
|
||||
await _eventHub.SendMessageAsync(MessageFactory.SeriesAdded,
|
||||
MessageFactory.SeriesAddedEvent(series.Id, series.Name, series.LibraryId), false);
|
||||
|
|
|
@ -47,7 +47,7 @@ public interface IVersionUpdaterService
|
|||
{
|
||||
Task<UpdateNotificationDto?> CheckForUpdate();
|
||||
Task PushUpdate(UpdateNotificationDto update);
|
||||
Task<IEnumerable<UpdateNotificationDto>> GetAllReleases();
|
||||
Task<IList<UpdateNotificationDto>> GetAllReleases();
|
||||
Task<int> GetNumberOfReleasesBehind();
|
||||
}
|
||||
|
||||
|
@ -82,10 +82,21 @@ public class VersionUpdaterService : IVersionUpdaterService
|
|||
return CreateDto(update);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<UpdateNotificationDto>> GetAllReleases()
|
||||
public async Task<IList<UpdateNotificationDto>> GetAllReleases()
|
||||
{
|
||||
var updates = await GetGithubReleases();
|
||||
return updates.Select(CreateDto).Where(d => d != null)!;
|
||||
var updateDtos = updates.Select(CreateDto)
|
||||
.Where(d => d != null)
|
||||
.OrderByDescending(d => d!.PublishDate)
|
||||
.Select(d => d!)
|
||||
.ToList();
|
||||
|
||||
// Find the latest dto
|
||||
var latestRelease = updateDtos[0]!;
|
||||
var isNightly = BuildInfo.Version > new Version(latestRelease.UpdateVersion);
|
||||
latestRelease.IsOnNightlyInRelease = isNightly;
|
||||
|
||||
return updateDtos;
|
||||
}
|
||||
|
||||
public async Task<int> GetNumberOfReleasesBehind()
|
||||
|
@ -108,7 +119,9 @@ public class VersionUpdaterService : IVersionUpdaterService
|
|||
UpdateTitle = update.Name,
|
||||
UpdateUrl = update.Html_Url,
|
||||
IsDocker = OsInfo.IsDocker,
|
||||
PublishDate = update.Published_At
|
||||
PublishDate = update.Published_At,
|
||||
IsReleaseEqual = BuildInfo.Version == updateVersion,
|
||||
IsReleaseNewer = BuildInfo.Version < updateVersion,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -142,8 +142,9 @@ public class TokenService : ITokenService
|
|||
return jwtClaim?.Value;
|
||||
}
|
||||
|
||||
public bool HasTokenExpired(string token)
|
||||
public bool HasTokenExpired(string? token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(token)) return true;
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var tokenContent = tokenHandler.ReadJwtToken(token);
|
||||
return tokenContent.ValidTo <= DateTime.UtcNow;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue