Restrict Person by library & Age rating
This commit is contained in:
parent
d261eaa98f
commit
b03b5bfdfc
4 changed files with 116 additions and 14 deletions
|
|
@ -185,7 +185,7 @@ public class PersonController : BaseApiController
|
||||||
[HttpGet("series-known-for")]
|
[HttpGet("series-known-for")]
|
||||||
public async Task<ActionResult<IEnumerable<SeriesDto>>> GetKnownSeries(int personId)
|
public async Task<ActionResult<IEnumerable<SeriesDto>>> GetKnownSeries(int personId)
|
||||||
{
|
{
|
||||||
return Ok(await _unitOfWork.PersonRepository.GetSeriesKnownFor(personId));
|
return Ok(await _unitOfWork.PersonRepository.GetSeriesKnownFor(personId, User.GetUserId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -206,6 +206,7 @@ public class PersonController : BaseApiController
|
||||||
/// <param name="dto"></param>
|
/// <param name="dto"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("merge")]
|
[HttpPost("merge")]
|
||||||
|
[Authorize("RequireAdminRole")]
|
||||||
public async Task<ActionResult<PersonDto>> MergePeople(PersonMergeDto dto)
|
public async Task<ActionResult<PersonDto>> MergePeople(PersonMergeDto dto)
|
||||||
{
|
{
|
||||||
var dst = await _unitOfWork.PersonRepository.GetPersonById(dto.DestId, PersonIncludes.All);
|
var dst = await _unitOfWork.PersonRepository.GetPersonById(dto.DestId, PersonIncludes.All);
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ public interface IPersonRepository
|
||||||
Task<Person?> GetPersonByNameOrAliasAsync(string name, PersonIncludes includes = PersonIncludes.Aliases);
|
Task<Person?> GetPersonByNameOrAliasAsync(string name, PersonIncludes includes = PersonIncludes.Aliases);
|
||||||
Task<bool> IsNameUnique(string name);
|
Task<bool> IsNameUnique(string name);
|
||||||
|
|
||||||
Task<IEnumerable<SeriesDto>> GetSeriesKnownFor(int personId);
|
Task<IEnumerable<SeriesDto>> GetSeriesKnownFor(int personId, int userId);
|
||||||
Task<IEnumerable<StandaloneChapterDto>> GetChaptersForPersonByRole(int personId, int userId, PersonRole role);
|
Task<IEnumerable<StandaloneChapterDto>> GetChaptersForPersonByRole(int personId, int userId, PersonRole role);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns all people with a matching name, or alias
|
/// Returns all people with a matching name, or alias
|
||||||
|
|
@ -179,20 +179,25 @@ public class PersonRepository : IPersonRepository
|
||||||
public async Task<IEnumerable<PersonRole>> GetRolesForPersonByName(int personId, int userId)
|
public async Task<IEnumerable<PersonRole>> GetRolesForPersonByName(int personId, int userId)
|
||||||
{
|
{
|
||||||
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||||
|
var userLibs = await _context.Library.GetUserLibraries(userId).ToListAsync();
|
||||||
|
|
||||||
// Query roles from ChapterPeople
|
// Query roles from ChapterPeople
|
||||||
var chapterRoles = await _context.Person
|
var chapterRoles = await _context.Person
|
||||||
.Where(p => p.Id == personId)
|
.Where(p => p.Id == personId)
|
||||||
|
.SelectMany(p => p.ChapterPeople)
|
||||||
.RestrictAgainstAgeRestriction(ageRating)
|
.RestrictAgainstAgeRestriction(ageRating)
|
||||||
.SelectMany(p => p.ChapterPeople.Select(cp => cp.Role))
|
.RestrictByLibrary(userLibs)
|
||||||
|
.Select(cp => cp.Role)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
// Query roles from SeriesMetadataPeople
|
// Query roles from SeriesMetadataPeople
|
||||||
var seriesRoles = await _context.Person
|
var seriesRoles = await _context.Person
|
||||||
.Where(p => p.Id == personId)
|
.Where(p => p.Id == personId)
|
||||||
|
.SelectMany(p => p.SeriesMetadataPeople)
|
||||||
.RestrictAgainstAgeRestriction(ageRating)
|
.RestrictAgainstAgeRestriction(ageRating)
|
||||||
.SelectMany(p => p.SeriesMetadataPeople.Select(smp => smp.Role))
|
.RestrictByLibrary(userLibs)
|
||||||
|
.Select(smp => smp.Role)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
|
@ -204,26 +209,33 @@ public class PersonRepository : IPersonRepository
|
||||||
{
|
{
|
||||||
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||||
|
|
||||||
var query = CreateFilteredPersonQueryable(userId, filter, ageRating);
|
var query = await CreateFilteredPersonQueryable(userId, filter, ageRating);
|
||||||
|
|
||||||
return await PagedList<BrowsePersonDto>.CreateAsync(query, userParams.PageNumber, userParams.PageSize);
|
return await PagedList<BrowsePersonDto>.CreateAsync(query, userParams.PageNumber, userParams.PageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IQueryable<BrowsePersonDto> CreateFilteredPersonQueryable(int userId, BrowsePersonFilterDto filter, AgeRestriction ageRating)
|
private async Task<IQueryable<BrowsePersonDto>> CreateFilteredPersonQueryable(int userId, BrowsePersonFilterDto filter, AgeRestriction ageRating)
|
||||||
{
|
{
|
||||||
|
var libs = await _context.Library.Includes(LibraryIncludes.AppUser).ToListAsync();
|
||||||
|
var libIds = libs.Select(l => l.Id).ToList();
|
||||||
|
var userLibs = libs.Where(lib => lib.AppUsers.Any(user => user.Id == userId))
|
||||||
|
.Select(lib => lib.Id).ToList();
|
||||||
|
|
||||||
var query = _context.Person.AsNoTracking();
|
var query = _context.Person.AsNoTracking();
|
||||||
|
|
||||||
// Apply filtering based on statements
|
// Apply filtering based on statements
|
||||||
query = BuildPersonFilterQuery(userId, filter, query);
|
query = BuildPersonFilterQuery(userId, filter, query);
|
||||||
|
|
||||||
// Apply age restriction
|
// Apply restrictions
|
||||||
query = query.RestrictAgainstAgeRestriction(ageRating);
|
query = query.RestrictAgainstAgeRestriction(ageRating);
|
||||||
|
query = query.RestrictByLibrary(userLibs, libIds);
|
||||||
|
|
||||||
// Apply sorting and limiting
|
// Apply sorting and limiting
|
||||||
var sortedQuery = query.SortBy(filter.SortOptions);
|
var sortedQuery = query.SortBy(filter.SortOptions);
|
||||||
|
|
||||||
var limitedQuery = ApplyPersonLimit(sortedQuery, filter.LimitTo);
|
var limitedQuery = ApplyPersonLimit(sortedQuery, filter.LimitTo);
|
||||||
|
|
||||||
|
// I cannot get the counting to work without errors...
|
||||||
// Project to DTO
|
// Project to DTO
|
||||||
var projectedQuery = limitedQuery.Select(p => new BrowsePersonDto
|
var projectedQuery = limitedQuery.Select(p => new BrowsePersonDto
|
||||||
{
|
{
|
||||||
|
|
@ -232,11 +244,17 @@ public class PersonRepository : IPersonRepository
|
||||||
Description = p.Description,
|
Description = p.Description,
|
||||||
CoverImage = p.CoverImage,
|
CoverImage = p.CoverImage,
|
||||||
SeriesCount = p.SeriesMetadataPeople
|
SeriesCount = p.SeriesMetadataPeople
|
||||||
.Select(smp => smp.SeriesMetadata.SeriesId)
|
.Select(smp => smp.SeriesMetadata)
|
||||||
|
//.RestrictByLibrary(userLibs, libIds)
|
||||||
|
//.RestrictAgainstAgeRestriction(ageRating)
|
||||||
|
.Select(smp => smp.SeriesId)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.Count(),
|
.Count(),
|
||||||
ChapterCount = p.ChapterPeople
|
ChapterCount = p.ChapterPeople
|
||||||
.Select(cp => cp.Chapter.Id)
|
.Select(chp => chp.Chapter)
|
||||||
|
//.RestrictByLibrary(userLibs, libIds)
|
||||||
|
//.RestrictAgainstAgeRestriction(ageRating)
|
||||||
|
.Select(cp => cp.Id)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.Count()
|
.Count()
|
||||||
});
|
});
|
||||||
|
|
@ -287,11 +305,13 @@ public class PersonRepository : IPersonRepository
|
||||||
{
|
{
|
||||||
var normalized = name.ToNormalized();
|
var normalized = name.ToNormalized();
|
||||||
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||||
|
var userLibs = await _context.Library.GetUserLibraries(userId).ToListAsync();
|
||||||
|
|
||||||
return await _context.Person
|
return await _context.Person
|
||||||
.Where(p => p.NormalizedName == normalized)
|
.Where(p => p.NormalizedName == normalized)
|
||||||
.Includes(includes)
|
.Includes(includes)
|
||||||
.RestrictAgainstAgeRestriction(ageRating)
|
.RestrictAgainstAgeRestriction(ageRating)
|
||||||
|
.RestrictByLibrary(userLibs)
|
||||||
.ProjectTo<PersonDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<PersonDto>(_mapper.ConfigurationProvider)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
}
|
}
|
||||||
|
|
@ -313,13 +333,18 @@ public class PersonRepository : IPersonRepository
|
||||||
.AnyAsync(p => p.Name == name || p.Aliases.Any(pa => pa.Alias == name)));
|
.AnyAsync(p => p.Name == name || p.Aliases.Any(pa => pa.Alias == name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<SeriesDto>> GetSeriesKnownFor(int personId)
|
public async Task<IEnumerable<SeriesDto>> GetSeriesKnownFor(int personId, int userId)
|
||||||
{
|
{
|
||||||
|
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||||
|
var userLibs = await _context.Library.GetUserLibraries(userId).ToListAsync();
|
||||||
|
|
||||||
return await _context.Person
|
return await _context.Person
|
||||||
.Where(p => p.Id == personId)
|
.Where(p => p.Id == personId)
|
||||||
.SelectMany(p => p.SeriesMetadataPeople)
|
.SelectMany(p => p.SeriesMetadataPeople)
|
||||||
.Select(smp => smp.SeriesMetadata)
|
.Select(smp => smp.SeriesMetadata)
|
||||||
.Select(sm => sm.Series)
|
.Select(sm => sm.Series)
|
||||||
|
.RestrictAgainstAgeRestriction(ageRating)
|
||||||
|
.Where(s => userLibs.Contains(s.LibraryId))
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.OrderByDescending(s => s.ExternalSeriesMetadata.AverageExternalRating)
|
.OrderByDescending(s => s.ExternalSeriesMetadata.AverageExternalRating)
|
||||||
.Take(20)
|
.Take(20)
|
||||||
|
|
@ -330,11 +355,13 @@ public class PersonRepository : IPersonRepository
|
||||||
public async Task<IEnumerable<StandaloneChapterDto>> GetChaptersForPersonByRole(int personId, int userId, PersonRole role)
|
public async Task<IEnumerable<StandaloneChapterDto>> GetChaptersForPersonByRole(int personId, int userId, PersonRole role)
|
||||||
{
|
{
|
||||||
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||||
|
var userLibs = await _context.Library.GetUserLibraries(userId).ToListAsync();
|
||||||
|
|
||||||
return await _context.ChapterPeople
|
return await _context.ChapterPeople
|
||||||
.Where(cp => cp.PersonId == personId && cp.Role == role)
|
.Where(cp => cp.PersonId == personId && cp.Role == role)
|
||||||
.Select(cp => cp.Chapter)
|
.Select(cp => cp.Chapter)
|
||||||
.RestrictAgainstAgeRestriction(ageRating)
|
.RestrictAgainstAgeRestriction(ageRating)
|
||||||
|
.RestrictByLibrary(userLibs)
|
||||||
.OrderBy(ch => ch.SortOrder)
|
.OrderBy(ch => ch.SortOrder)
|
||||||
.Take(20)
|
.Take(20)
|
||||||
.ProjectTo<StandaloneChapterDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<StandaloneChapterDto>(_mapper.ConfigurationProvider)
|
||||||
|
|
@ -385,27 +412,31 @@ public class PersonRepository : IPersonRepository
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IList<PersonDto>> GetAllPersonDtosAsync(int userId, PersonIncludes includes = PersonIncludes.Aliases)
|
public async Task<IList<PersonDto>> GetAllPersonDtosAsync(int userId, PersonIncludes includes = PersonIncludes.None)
|
||||||
{
|
{
|
||||||
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||||
|
var userLibs = await _context.Library.GetUserLibraries(userId).ToListAsync();
|
||||||
|
|
||||||
return await _context.Person
|
return await _context.Person
|
||||||
.Includes(includes)
|
.Includes(includes)
|
||||||
.OrderBy(p => p.Name)
|
|
||||||
.RestrictAgainstAgeRestriction(ageRating)
|
.RestrictAgainstAgeRestriction(ageRating)
|
||||||
|
.RestrictByLibrary(userLibs)
|
||||||
|
.OrderBy(p => p.Name)
|
||||||
.ProjectTo<PersonDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<PersonDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IList<PersonDto>> GetAllPersonDtosByRoleAsync(int userId, PersonRole role, PersonIncludes includes = PersonIncludes.Aliases)
|
public async Task<IList<PersonDto>> GetAllPersonDtosByRoleAsync(int userId, PersonRole role, PersonIncludes includes = PersonIncludes.None)
|
||||||
{
|
{
|
||||||
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
var ageRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||||
|
var userLibs = await _context.Library.GetUserLibraries(userId).ToListAsync();
|
||||||
|
|
||||||
return await _context.Person
|
return await _context.Person
|
||||||
.Where(p => p.SeriesMetadataPeople.Any(smp => smp.Role == role) || p.ChapterPeople.Any(cp => cp.Role == role)) // Filter by role in both series and chapters
|
.Where(p => p.SeriesMetadataPeople.Any(smp => smp.Role == role) || p.ChapterPeople.Any(cp => cp.Role == role)) // Filter by role in both series and chapters
|
||||||
.Includes(includes)
|
.Includes(includes)
|
||||||
.OrderBy(p => p.Name)
|
|
||||||
.RestrictAgainstAgeRestriction(ageRating)
|
.RestrictAgainstAgeRestriction(ageRating)
|
||||||
|
.RestrictByLibrary(userLibs)
|
||||||
|
.OrderBy(p => p.Name)
|
||||||
.ProjectTo<PersonDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<PersonDto>(_mapper.ConfigurationProvider)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,19 @@ public static class RestrictByAgeExtensions
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IQueryable<SeriesMetadataPeople> RestrictAgainstAgeRestriction(this IQueryable<SeriesMetadataPeople> queryable, AgeRestriction restriction)
|
||||||
|
{
|
||||||
|
if (restriction.AgeRating == AgeRating.NotApplicable) return queryable;
|
||||||
|
var q = queryable.Where(s => s.SeriesMetadata.AgeRating <= restriction.AgeRating);
|
||||||
|
|
||||||
|
if (!restriction.IncludeUnknowns)
|
||||||
|
{
|
||||||
|
return q.Where(s => s.SeriesMetadata.AgeRating != AgeRating.Unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static IQueryable<Chapter> RestrictAgainstAgeRestriction(this IQueryable<Chapter> queryable, AgeRestriction restriction)
|
public static IQueryable<Chapter> RestrictAgainstAgeRestriction(this IQueryable<Chapter> queryable, AgeRestriction restriction)
|
||||||
{
|
{
|
||||||
|
|
@ -41,6 +54,19 @@ public static class RestrictByAgeExtensions
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IQueryable<ChapterPeople> RestrictAgainstAgeRestriction(this IQueryable<ChapterPeople> queryable, AgeRestriction restriction)
|
||||||
|
{
|
||||||
|
if (restriction.AgeRating == AgeRating.NotApplicable) return queryable;
|
||||||
|
var q = queryable.Where(cp => cp.Chapter.Volume.Series.Metadata.AgeRating <= restriction.AgeRating);
|
||||||
|
|
||||||
|
if (!restriction.IncludeUnknowns)
|
||||||
|
{
|
||||||
|
return q.Where(cp => cp.Chapter.Volume.Series.Metadata.AgeRating != AgeRating.Unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static IQueryable<AppUserCollection> RestrictAgainstAgeRestriction(this IQueryable<AppUserCollection> queryable, AgeRestriction restriction)
|
public static IQueryable<AppUserCollection> RestrictAgainstAgeRestriction(this IQueryable<AppUserCollection> queryable, AgeRestriction restriction)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using API.Entities;
|
||||||
|
using API.Entities.Person;
|
||||||
|
|
||||||
|
namespace API.Extensions.QueryExtensions;
|
||||||
|
|
||||||
|
// TODO: Refactor with IQueryable userLibs? But then I can't do the allLibs check?
|
||||||
|
/// <summary>
|
||||||
|
/// Optionally pass ids of all libraries, will then be smart and not restrict if the person has access to all
|
||||||
|
/// </summary>
|
||||||
|
public static class RestrictByLibraryExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
public static IQueryable<Person> RestrictByLibrary(this IQueryable<Person> query, IList<int> userLibs, IList<int> allLibs = null)
|
||||||
|
{
|
||||||
|
if (allLibs != null && allLibs.Count == userLibs.Count) return query;
|
||||||
|
|
||||||
|
return query.Where(p =>
|
||||||
|
p.ChapterPeople.Any(cp => userLibs.Contains(cp.Chapter.Volume.Series.LibraryId)) ||
|
||||||
|
p.SeriesMetadataPeople.Any(sm => userLibs.Contains(sm.SeriesMetadata.Series.LibraryId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<Chapter> RestrictByLibrary(this IQueryable<Chapter> query, IList<int> userLibs, IList<int> allLibs = null)
|
||||||
|
{
|
||||||
|
if (allLibs != null && allLibs.Count == userLibs.Count) return query;
|
||||||
|
|
||||||
|
return query.Where(cp => userLibs.Contains(cp.Volume.Series.LibraryId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<SeriesMetadataPeople> RestrictByLibrary(this IQueryable<SeriesMetadataPeople> query, IList<int> userLibs, IList<int> allLibs = null)
|
||||||
|
{
|
||||||
|
if (allLibs != null && allLibs.Count == userLibs.Count) return query;
|
||||||
|
|
||||||
|
return query.Where(sm => userLibs.Contains(sm.SeriesMetadata.Series.LibraryId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IQueryable<ChapterPeople> RestrictByLibrary(this IQueryable<ChapterPeople> query, IList<int> userLibs, IList<int> allLibs = null)
|
||||||
|
{
|
||||||
|
if (allLibs != null && allLibs.Count == userLibs.Count) return query;
|
||||||
|
|
||||||
|
return query.Where(cp => userLibs.Contains(cp.Chapter.Volume.Series.LibraryId));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue