mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-31 14:33:50 -04:00
Misc Bugfixes (#1015)
* Fixed some security issues in dev env * When deleting folders in bookmark cleanup, delete empty folders correctly. * When a new library is created and cards are added, cards can have a blank library name. Card library name code is reworked to be much lighter on memory. * Added a config for github issues to disable blank issues. * Skip any sort of directory iteration code if we haven't deleted any bookmarks. * Fixed a bug where some style overrides were duplicating. Now logic is much more targetted, only applying to the correct tags. * Applied sorting to the filtering apis. * Reverted one of my changes for a better version Robbie did.
This commit is contained in:
parent
c631395aae
commit
c6d1311560
@ -400,6 +400,78 @@ public class CleanupServiceTests
|
|||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
_context.AppUser.Add(new AppUser()
|
||||||
|
{
|
||||||
|
Bookmarks = new List<AppUserBookmark>()
|
||||||
|
{
|
||||||
|
new AppUserBookmark()
|
||||||
|
{
|
||||||
|
AppUserId = 1,
|
||||||
|
ChapterId = 1,
|
||||||
|
Page = 1,
|
||||||
|
FileName = "1/1/1/0001.jpg",
|
||||||
|
SeriesId = 1,
|
||||||
|
VolumeId = 1
|
||||||
|
},
|
||||||
|
new AppUserBookmark()
|
||||||
|
{
|
||||||
|
AppUserId = 1,
|
||||||
|
ChapterId = 1,
|
||||||
|
Page = 2,
|
||||||
|
FileName = "1/1/1/0002.jpg",
|
||||||
|
SeriesId = 1,
|
||||||
|
VolumeId = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
|
||||||
|
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), filesystem);
|
||||||
|
var cleanupService = new CleanupService(_logger, _unitOfWork, _messageHub,
|
||||||
|
ds);
|
||||||
|
|
||||||
|
await cleanupService.CleanupBookmarks();
|
||||||
|
|
||||||
|
Assert.Equal(2, ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories).Count());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CleanupBookmarks_LeavesOneFiles()
|
||||||
|
{
|
||||||
|
var filesystem = CreateFileSystem();
|
||||||
|
filesystem.AddFile($"{BookmarkDirectory}1/1/1/0001.jpg", new MockFileData(""));
|
||||||
|
filesystem.AddFile($"{BookmarkDirectory}1/1/2/0002.jpg", new MockFileData(""));
|
||||||
|
|
||||||
|
// Delete all Series to reset state
|
||||||
|
await ResetDB();
|
||||||
|
|
||||||
|
_context.Series.Add(new Series()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Library = new Library() {
|
||||||
|
Name = "Test LIb",
|
||||||
|
Type = LibraryType.Manga,
|
||||||
|
},
|
||||||
|
Volumes = new List<Volume>()
|
||||||
|
{
|
||||||
|
new Volume()
|
||||||
|
{
|
||||||
|
Chapters = new List<Chapter>()
|
||||||
|
{
|
||||||
|
new Chapter()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
_context.AppUser.Add(new AppUser()
|
_context.AppUser.Add(new AppUser()
|
||||||
{
|
{
|
||||||
Bookmarks = new List<AppUserBookmark>()
|
Bookmarks = new List<AppUserBookmark>()
|
||||||
@ -426,7 +498,7 @@ public class CleanupServiceTests
|
|||||||
await cleanupService.CleanupBookmarks();
|
await cleanupService.CleanupBookmarks();
|
||||||
|
|
||||||
Assert.Equal(1, ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories).Count());
|
Assert.Equal(1, ds.GetFiles(BookmarkDirectory, searchOption:SearchOption.AllDirectories).Count());
|
||||||
|
Assert.Equal(1, ds.FileSystem.Directory.GetDirectories($"{BookmarkDirectory}1/1/").Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -111,7 +111,7 @@ public class MetadataController : BaseApiController
|
|||||||
{
|
{
|
||||||
Title = t.ToDescription(),
|
Title = t.ToDescription(),
|
||||||
Value = t
|
Value = t
|
||||||
}));
|
}).OrderBy(t => t.Title));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -67,6 +67,7 @@ public class GenreRepository : IGenreRepository
|
|||||||
.Where(s => libraryIds.Contains(s.LibraryId))
|
.Where(s => libraryIds.Contains(s.LibraryId))
|
||||||
.SelectMany(s => s.Metadata.Genres)
|
.SelectMany(s => s.Metadata.Genres)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
|
.OrderBy(p => p.Title)
|
||||||
.ProjectTo<GenreTagDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<GenreTagDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,8 @@ public class PersonRepository : IPersonRepository
|
|||||||
.Where(s => libraryIds.Contains(s.LibraryId))
|
.Where(s => libraryIds.Contains(s.LibraryId))
|
||||||
.SelectMany(s => s.Metadata.People)
|
.SelectMany(s => s.Metadata.People)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
|
.OrderBy(p => p.Name)
|
||||||
|
.AsNoTracking()
|
||||||
.ProjectTo<PersonDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<PersonDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
@ -74,6 +76,7 @@ public class PersonRepository : IPersonRepository
|
|||||||
public async Task<IList<Person>> GetAllPeople()
|
public async Task<IList<Person>> GetAllPeople()
|
||||||
{
|
{
|
||||||
return await _context.Person
|
return await _context.Person
|
||||||
|
.OrderBy(p => p.Name)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -778,6 +778,7 @@ public class SeriesRepository : ISeriesRepository
|
|||||||
var ret = await _context.Series
|
var ret = await _context.Series
|
||||||
.Where(s => libraryIds.Contains(s.LibraryId))
|
.Where(s => libraryIds.Contains(s.LibraryId))
|
||||||
.Select(s => s.Metadata.Language)
|
.Select(s => s.Metadata.Language)
|
||||||
|
.AsNoTracking()
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
@ -787,7 +788,9 @@ public class SeriesRepository : ISeriesRepository
|
|||||||
{
|
{
|
||||||
Title = CultureInfo.GetCultureInfo(s).DisplayName,
|
Title = CultureInfo.GetCultureInfo(s).DisplayName,
|
||||||
IsoCode = s
|
IsoCode = s
|
||||||
}).ToList();
|
})
|
||||||
|
.OrderBy(s => s.Title)
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IList<PublicationStatusDto>> GetAllPublicationStatusesDtosForLibrariesAsync(List<int> libraryIds)
|
public async Task<IList<PublicationStatusDto>> GetAllPublicationStatusesDtosForLibrariesAsync(List<int> libraryIds)
|
||||||
@ -801,6 +804,7 @@ public class SeriesRepository : ISeriesRepository
|
|||||||
Value = s,
|
Value = s,
|
||||||
Title = s.ToDescription()
|
Title = s.ToDescription()
|
||||||
})
|
})
|
||||||
|
.OrderBy(s => s.Title)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +67,8 @@ public class TagRepository : ITagRepository
|
|||||||
.Where(s => libraryIds.Contains(s.LibraryId))
|
.Where(s => libraryIds.Contains(s.LibraryId))
|
||||||
.SelectMany(s => s.Metadata.Tags)
|
.SelectMany(s => s.Metadata.Tags)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
|
.OrderBy(t => t.Title)
|
||||||
|
.AsNoTracking()
|
||||||
.ProjectTo<TagDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<TagDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
@ -80,6 +82,7 @@ public class TagRepository : ITagRepository
|
|||||||
{
|
{
|
||||||
return await _context.Tag
|
return await _context.Tag
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
|
.OrderBy(t => t.Title)
|
||||||
.ProjectTo<TagDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<TagDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
@ -184,19 +184,23 @@ namespace API.Services.Tasks
|
|||||||
|
|
||||||
|
|
||||||
var filesToDelete = allBookmarkFiles.ToList().Except(bookmarks).ToList();
|
var filesToDelete = allBookmarkFiles.ToList().Except(bookmarks).ToList();
|
||||||
_logger.LogDebug("[Bookmarks] Bookmark cleanup wants to delete {Count} files", filesToDelete.Count());
|
_logger.LogDebug("[Bookmarks] Bookmark cleanup wants to delete {Count} files", filesToDelete.Count);
|
||||||
|
|
||||||
|
if (filesToDelete.Count == 0) return;
|
||||||
|
|
||||||
_directoryService.DeleteFiles(filesToDelete);
|
_directoryService.DeleteFiles(filesToDelete);
|
||||||
|
|
||||||
// Clear all empty directories
|
// Clear all empty directories
|
||||||
foreach (var directory in _directoryService.FileSystem.Directory.GetDirectories(bookmarkDirectory))
|
foreach (var directory in _directoryService.FileSystem.Directory.GetDirectories(bookmarkDirectory, "", SearchOption.AllDirectories))
|
||||||
{
|
{
|
||||||
if (_directoryService.FileSystem.Directory.GetFiles(directory).Length == 0 &&
|
if (_directoryService.FileSystem.Directory.GetFiles(directory, "", SearchOption.AllDirectories).Length == 0 &&
|
||||||
_directoryService.FileSystem.Directory.GetDirectories(directory).Length == 0)
|
_directoryService.FileSystem.Directory.GetDirectories(directory).Length == 0)
|
||||||
{
|
{
|
||||||
_directoryService.FileSystem.Directory.Delete(directory, false);
|
_directoryService.FileSystem.Directory.Delete(directory, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
36
UI/Web/package-lock.json
generated
36
UI/Web/package-lock.json
generated
@ -10645,9 +10645,33 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node-fetch": {
|
"node-fetch": {
|
||||||
"version": "2.6.1",
|
"version": "2.6.7",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||||
|
"requires": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||||
|
},
|
||||||
|
"webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||||
|
},
|
||||||
|
"whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||||
|
"requires": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node-forge": {
|
"node-forge": {
|
||||||
"version": "0.10.0",
|
"version": "0.10.0",
|
||||||
@ -12384,8 +12408,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
"resolved": "",
|
||||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"strip-ansi": {
|
"strip-ansi": {
|
||||||
@ -12590,8 +12613,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
"resolved": "",
|
||||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ansi-styles": {
|
"ansi-styles": {
|
||||||
|
@ -34,6 +34,21 @@ export class LibraryService {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLibraryName(libraryId: number) {
|
||||||
|
if (this.libraryNames != undefined && this.libraryNames.hasOwnProperty(libraryId)) {
|
||||||
|
return of(this.libraryNames[libraryId]);
|
||||||
|
}
|
||||||
|
return this.httpClient.get<Library[]>(this.baseUrl + 'library').pipe(map(l => {
|
||||||
|
this.libraryNames = {};
|
||||||
|
l.forEach(lib => {
|
||||||
|
if (this.libraryNames !== undefined) {
|
||||||
|
this.libraryNames[lib.id] = lib.name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return this.libraryNames[libraryId];
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
listDirectories(rootPath: string) {
|
listDirectories(rootPath: string) {
|
||||||
let query = '';
|
let query = '';
|
||||||
if (rootPath !== undefined && rootPath.length > 0) {
|
if (rootPath !== undefined && rootPath.length > 0) {
|
||||||
|
@ -43,6 +43,15 @@ const TOP_OFFSET = -50 * 1.5; // px the sticky header takes up
|
|||||||
const CHAPTER_ID_NOT_FETCHED = -2;
|
const CHAPTER_ID_NOT_FETCHED = -2;
|
||||||
const CHAPTER_ID_DOESNT_EXIST = -1;
|
const CHAPTER_ID_DOESNT_EXIST = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Styles that should be applied on the top level book-content tag
|
||||||
|
*/
|
||||||
|
const pageLevelStyles = ['margin-left', 'margin-right', 'font-size'];
|
||||||
|
/**
|
||||||
|
* Styles that should be applied on every element within book-content tag
|
||||||
|
*/
|
||||||
|
const elementLevelStyles = ['line-height', 'font-family'];
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-book-reader',
|
selector: 'app-book-reader',
|
||||||
templateUrl: './book-reader.component.html',
|
templateUrl: './book-reader.component.html',
|
||||||
@ -680,17 +689,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
page = 0;
|
page = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// BUG: Last page is not counting as read
|
|
||||||
if (!(page === 0 || page === this.maxPages - 1)) {
|
if (!(page === 0 || page === this.maxPages - 1)) {
|
||||||
page -= 1;
|
page -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Due to the fact that we start at image 0, but page 1, we need the last page to have progress as page + 1 to be completed
|
|
||||||
// let tempPageNum = this.pageNum;
|
|
||||||
// if (this.pageNum == this.maxPages - 1) {
|
|
||||||
// tempPageNum = this.pageNum + 1;
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.pageNum = page;
|
this.pageNum = page;
|
||||||
this.loadPage();
|
this.loadPage();
|
||||||
|
|
||||||
@ -903,31 +905,41 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this.updateReaderStyles();
|
this.updateReaderStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies styles onto the html of the book page
|
||||||
|
*/
|
||||||
updateReaderStyles() {
|
updateReaderStyles() {
|
||||||
if (this.readingHtml != undefined && this.readingHtml.nativeElement) {
|
if (this.readingHtml === undefined || !this.readingHtml.nativeElement) return;
|
||||||
Object.entries(this.pageStyles).forEach(item => {
|
|
||||||
if (item[1] == '100%' || item[1] == '0px' || item[1] == 'inherit') {
|
|
||||||
// Remove the style or skip
|
|
||||||
this.renderer.removeStyle(this.readingHtml.nativeElement, item[0]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.renderer.setStyle(this.readingHtml.nativeElement, item[0], item[1], RendererStyleFlags2.Important);
|
|
||||||
});
|
|
||||||
|
|
||||||
for(let i = 0; i < this.readingHtml.nativeElement.children.length; i++) {
|
// Line Height must be placed on each element in the page
|
||||||
const elem = this.readingHtml.nativeElement.children.item(i);
|
|
||||||
if (elem?.tagName === 'STYLE') continue;
|
// Apply page level overrides
|
||||||
Object.entries(this.pageStyles).forEach(item => {
|
Object.entries(this.pageStyles).forEach(item => {
|
||||||
if (item[1] == '100%' || item[1] == '0px' || item[1] == 'inherit') {
|
if (item[1] == '100%' || item[1] == '0px' || item[1] == 'inherit') {
|
||||||
// Remove the style or skip
|
// Remove the style or skip
|
||||||
this.renderer.removeStyle(elem, item[0]);
|
this.renderer.removeStyle(this.readingHtml.nativeElement, item[0]);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
this.renderer.setStyle(elem, item[0], item[1], RendererStyleFlags2.Important);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
if (pageLevelStyles.includes(item[0])) {
|
||||||
|
this.renderer.setStyle(this.readingHtml.nativeElement, item[0], item[1], RendererStyleFlags2.Important);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const individualElementStyles = Object.entries(this.pageStyles).filter(item => elementLevelStyles.includes(item[0]));
|
||||||
|
for(let i = 0; i < this.readingHtml.nativeElement.children.length; i++) {
|
||||||
|
const elem = this.readingHtml.nativeElement.children.item(i);
|
||||||
|
if (elem?.tagName === 'STYLE') continue;
|
||||||
|
individualElementStyles.forEach(item => {
|
||||||
|
if (item[1] == '100%' || item[1] == '0px' || item[1] == 'inherit') {
|
||||||
|
// Remove the style or skip
|
||||||
|
this.renderer.removeStyle(elem, item[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.renderer.setStyle(elem, item[0], item[1], RendererStyleFlags2.Important);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -115,12 +115,15 @@ export class CardItemComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.supressLibraryLink === false) {
|
if (this.supressLibraryLink === false) {
|
||||||
this.libraryService.getLibraryNames().pipe(takeUntil(this.onDestroy)).subscribe(names => {
|
if (this.entity !== undefined && this.entity.hasOwnProperty('libraryId')) {
|
||||||
if (this.entity !== undefined && this.entity.hasOwnProperty('libraryId')) {
|
this.libraryId = (this.entity as Series).libraryId;
|
||||||
this.libraryId = (this.entity as Series).libraryId;
|
}
|
||||||
this.libraryName = names[this.libraryId];
|
|
||||||
}
|
if (this.libraryId !== undefined && this.libraryId > 0) {
|
||||||
});
|
this.libraryService.getLibraryName(this.libraryId).pipe(takeUntil(this.onDestroy)).subscribe(name => {
|
||||||
|
this.libraryName = name;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.format = (this.entity as Series).format;
|
this.format = (this.entity as Series).format;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user