/// <summary> /// Applies date filtering to the list of media. /// </summary> private IReadOnlyList <Media> TryFilterMediaByDate(FolderContentsRequestVM request, IReadOnlyList <Media> source) { var dateFrom = TryParse(request.From); var dateTo = TryParse(request.To); if (dateFrom == null && dateTo == null) { return(source); } return(source.Select(x => new { Date = FuzzyDate.TryParse(x.Date), Media = x }) .Where(x => x.Date != null) .Where(x => (dateFrom == null || x.Date >= dateFrom) && (dateTo == null || x.Date <= dateTo)) .Select(x => x.Media) .ToList()); FuzzyDate?TryParse(string value) { var date = value?.TryParse <DateTime?>(); return(date == null ? (FuzzyDate?)null : new FuzzyDate(date.Value)); } }
/// <summary> /// Returns the details for a media. /// </summary> public async Task <MediaVM> GetMediaAsync(string key) { var id = PageHelper.GetMediaId(key); var media = await _db.Media .Include(x => x.Tags) .ThenInclude(x => x.Object) .Where(x => x.IsDeleted == false) .FirstOrDefaultAsync(x => x.Id == id); if (media == null) { throw new KeyNotFoundException(); } var descr = await _markdown.CompileAsync(media.Description); return(new MediaVM { Type = media.Type, IsProcessed = media.IsProcessed, Title = media.Title, Description = descr, Date = FuzzyDate.TryParse(media.Date), Tags = GetMediaTagsVMs(media.Tags).ToList(), Event = GetPageTitle(media.Tags.FirstOrDefault(x => x.Type == MediaTagType.Event)), Location = GetPageTitle(media.Tags.FirstOrDefault(x => x.Type == MediaTagType.Location)), OriginalPath = media.FilePath, PreviewPath = GetSizedMediaPath(media.FilePath, MediaSize.Large) }); }
/// <summary> /// Performs additional checks on the registration request. /// </summary> private async Task ValidateRegisterRequestAsync(RegisterUserVM vm, bool usePasswordAuth) { var val = new Validator(); if (FuzzyDate.TryParse(vm.Birthday) == null) { val.Add(nameof(vm.Birthday), "Дата рождения указана неверно."); } var emailExists = await _db.Users.AnyAsync(x => x.Email == vm.Email); if (emailExists) { val.Add(nameof(vm.Email), "Адрес электронной почты уже зарегистрирован."); } if (usePasswordAuth) { if (vm.Password == null || vm.Password.Length < 6) { val.Add(nameof(vm.Password), "Пароль должен содержать как минимум 6 символов."); } if (vm.Password != vm.PasswordCopy) { val.Add(nameof(vm.PasswordCopy), "Пароли не совпадают."); } } val.ThrowIfInvalid(); }
/// <summary> /// Uploads a new media file. /// </summary> public async Task <MediaUploadResultVM> UploadAsync(MediaUploadRequestVM vm, IFormFile file, ClaimsPrincipal principal) { var id = Guid.NewGuid(); var key = PageHelper.GetMediaKey(id); var handler = _mediaHandlers.FirstOrDefault(x => x.SupportedMimeTypes.Contains(file.ContentType)); if (handler == null) { throw new UploadException("Неизвестный тип файла!"); } var userId = _userMgr.GetUserId(principal); var user = await _db.Users.GetAsync(x => x.Id == userId, "Пользователь не найден"); var paths = await SaveUploadAsync(file, key, handler); var tags = await GetTagsForUploadedMedia(vm); var meta = await handler.ExtractMetadataAsync(paths.LocalPath, file.ContentType); var media = new Media { Id = id, Key = key, Type = handler.MediaType, MimeType = file.ContentType, Title = vm.Title, FilePath = paths.UrlPath, UploadDate = DateTimeOffset.Now, Uploader = user, IsProcessed = handler.IsImmediate, Date = FuzzyDate.TryParse(vm.Date) != null ? vm.Date : meta?.Date?.Date.ToString("yyyy.MM.dd", CultureInfo.InvariantCulture), Tags = tags }; _db.Media.Add(media); if (!handler.IsImmediate) { _db.MediaJobs.Add(new MediaEncodingJob { Id = Guid.NewGuid(), MediaId = media.Id }); } var changeset = await GetChangesetAsync(null, _mapper.Map <MediaEditorVM>(media), id, principal, null); _db.Changes.Add(changeset); return(_mapper.Map <MediaUploadResultVM>(media)); }
public void Configure(IProfileExpression profile) { profile.CreateMap <Data.Models.Media, MediaThumbnailExtendedVM>() .MapMember(x => x.Id, x => x.Id) .MapMember(x => x.Key, x => x.Key) .MapMember(x => x.Type, x => x.Type) .MapMember(x => x.Date, x => FuzzyDate.TryParse(x.Date)) .MapMember(x => x.UploadDate, x => x.UploadDate) .MapMember(x => x.Title, x => x.Title) .MapMember(x => x.MediaTagsCount, x => x.Tags.Count(y => y.Type == MediaTagType.DepictedEntity)) .MapMember(x => x.ThumbnailUrl, x => MediaPresenterService.GetSizedMediaPath(x.FilePath, MediaSize.Small)); }
/// <summary> /// Performs additional checks on the registration request. /// </summary> private async Task ValidateRegisterRequestAsync(RegisterUserVM vm) { var val = new Validator(); if (FuzzyDate.TryParse(vm.Birthday) == null) val.Add(nameof(vm.Birthday), "Дата рождения указана неверно."); var emailExists = await _db.Users.AnyAsync(x => x.Email == vm.Email); if (emailExists) val.Add(nameof(vm.Email), "Адрес электронной почты уже зарегистрирован."); val.ThrowIfInvalid(); }
/// <summary> /// Renders the property values. /// </summary> public async Task <IReadOnlyList <ChangePropertyValue> > RenderValuesAsync(string json) { var result = new List <ChangePropertyValue>(); var data = JsonConvert.DeserializeObject <RelationEditorVM>(StringHelper.Coalesce(json, "{}")); if (data.SourceIds == null) { data.SourceIds = Array.Empty <Guid>(); } var pageIds = data.SourceIds .Concat(new[] { data.DestinationId ?? Guid.Empty, data.EventId ?? Guid.Empty }) .ToList(); var namesLookup = await _db.Pages .Where(x => pageIds.Contains(x.Id)) .ToDictionaryAsync(x => x.Id, x => x.Title); Add(nameof(RelationEditorVM.DestinationId), "Основная страница", namesLookup.TryGetValue(data.DestinationId ?? Guid.Empty)); Add(nameof(RelationEditorVM.Type), "Тип связи", string.IsNullOrEmpty(json) ? null : data.Type.GetEnumDescription()); if (data.SourceIds.Length == 0) { Add(nameof(RelationEditorVM.SourceIds), "Связанная страница", null); } else if (data.SourceIds.Length == 1) { var name = namesLookup.TryGetValue(data.SourceIds[0]); Add(nameof(RelationEditorVM.SourceIds), "Связанная страница", name); } else { var pageNames = data.SourceIds .Select(x => namesLookup.TryGetValue(x)) .Where(x => !string.IsNullOrEmpty(x)); Add(nameof(RelationEditorVM.SourceIds), "Связанные страницы", ViewHelper.RenderBulletList(_html, pageNames)); } Add(nameof(RelationEditorVM.EventId), "Событие", namesLookup.TryGetValue(data.EventId ?? Guid.Empty)); Add(nameof(RelationEditorVM.DurationStart), "Начало", FuzzyDate.TryParse(data.DurationStart)?.ReadableDate); Add(nameof(RelationEditorVM.DurationEnd), "Конец", FuzzyDate.TryParse(data.DurationEnd)?.ReadableDate); return(result); void Add(string prop, string name, string value) { result.Add(new ChangePropertyValue(prop, name, value)); } }
/// <summary> /// Returns the last N uploaded images. /// </summary> public async Task <IReadOnlyList <MediaThumbnailVM> > GetLastUploadedMediaAsync(int count) { return(await _db.Media .Where(x => !x.IsDeleted) .OrderByDescending(x => x.UploadDate) .Take(count) .Select(x => new MediaThumbnailVM { Key = x.Key, Type = x.Type, ThumbnailUrl = GetSizedMediaPath(x.FilePath, MediaSize.Small), Date = FuzzyDate.TryParse(x.Date), }) .ToListAsync()); }
/// <summary> /// Returns the photo model. /// </summary> public static MediaThumbnailVM GetMediaThumbnail(Media media, MediaSize size = MediaSize.Small) { if (media == null) { return(null); } return(new MediaThumbnailVM { Type = media.Type, Key = media.Key, ThumbnailUrl = GetSizedMediaPath(media.FilePath, size), Date = FuzzyDate.TryParse(media.Date) }); }
/// <summary> /// Returns the media details. /// </summary> public async Task <MediaVM> GetMediaAsync(string key, UserContext ctx) { var errorKey = $"Media ({key})"; var media = await _db.Media .AsNoTracking() .Include(x => x.Tags) .ThenInclude(x => x.Tag) .Include(x => x.Folder) .GetAsync(x => x.Key == key, errorKey); if (ctx.Link != null) { // subfolders disallowed: only media immediately inside current folder are available if (ctx.Link.Scope == SearchScope.CurrentFolder && media.FolderKey != ctx.Link.Folder.Key) { throw new NotFoundException(errorKey); } // inside subfolder if (!media.Folder.Path.StartsWith(ctx.Link.Folder.Path)) { throw new NotFoundException(errorKey); } // must have tags if (!string.IsNullOrEmpty(ctx.Link.Tags)) { var tagIds = ctx.Link.Tags.TryParseList <int>(","); if (!media.Tags.Any(x => tagIds.Contains(x.TagId))) { throw new NotFoundException(errorKey); } } var mediaDate = FuzzyDate.TryParse(media.Date); var dateFrom = FuzzyDate.TryParse(ctx.Link.DateFrom); var dateTo = FuzzyDate.TryParse(ctx.Link.DateTo); if ((dateFrom != null && !(mediaDate >= dateFrom)) || (dateTo != null && !(mediaDate <= dateTo))) { throw new NotFoundException(errorKey); } } return(_mapper.Map <MediaVM>(media)); }
/// <summary> /// Checks if the update request contains valid data. /// </summary> private async Task ValidateRequestAsync(MediaEditorVM vm) { var val = new Validator(); if (!string.IsNullOrEmpty(vm.Date) && FuzzyDate.TryParse(vm.Date) == null) { val.Add(nameof(vm.Date), "Введите корректную дату."); } var depictedIds = JsonConvert.DeserializeObject <IEnumerable <MediaTagVM> >(vm.DepictedEntities ?? "[]") .Select(x => x.PageId) .ToList(); var locId = vm.Location.TryParse <Guid?>(); var evtId = vm.Event.TryParse <Guid?>(); var tagIds = depictedIds.Concat(new[] { locId, evtId }) .Where(x => x != null) .Select(x => x.Value) .ToList(); if (tagIds.Any()) { var existing = await _db.Pages .Where(x => tagIds.Contains(x.Id) && !x.IsDeleted) .ToHashSetAsync(x => x.Id); if (depictedIds.Any(x => x != null && !existing.Contains(x.Value))) { val.Add(nameof(vm.DepictedEntities), "Страница не существует!"); } if (locId != null && !existing.Contains(locId.Value)) { val.Add(nameof(vm.Location), "Страница не существует!"); } if (evtId != null && !existing.Contains(evtId.Value)) { val.Add(nameof(vm.Event), "Страница не существует!"); } } val.ThrowIfInvalid(); }
/// <summary> /// Checks if the create/update request contains valid data. /// </summary> private async Task ValidateRequestAsync(RelationEditorVM vm, bool isNew) { var val = new Validator(); vm.SourceIds = vm.SourceIds ?? new Guid[0]; var pageIds = vm.SourceIds .Concat(new [] { vm.DestinationId ?? Guid.Empty, vm.EventId ?? Guid.Empty }) .ToList(); var pages = await _db.Pages .Where(x => pageIds.Contains(x.Id)) .ToDictionaryAsync(x => x.Id, x => x.Type); var sourceTypes = vm.SourceIds.Select(x => pages.TryGetNullableValue(x)).ToList(); var destType = pages.TryGetNullableValue(vm.DestinationId ?? Guid.Empty); var eventType = pages.TryGetNullableValue(vm.EventId ?? Guid.Empty); if (vm.SourceIds == null || vm.SourceIds.Length == 0) { val.Add(nameof(vm.SourceIds), "Выберите страницу"); } else if (isNew == false && vm.SourceIds.Length > 1) { val.Add(nameof(vm.SourceIds), "При редактировании может быть указана только одна страница"); } else if (sourceTypes.Any(x => x == null)) { val.Add(nameof(vm.SourceIds), "Страница не найдена"); } if (vm.DestinationId == null) { val.Add(nameof(vm.DestinationId), "Выберите страницу"); } else if (destType == null) { val.Add(nameof(vm.DestinationId), "Страница не найдена"); } if (destType != null && sourceTypes.Any(x => x != null && !RelationHelper.IsRelationAllowed(x.Value, destType.Value, vm.Type))) { val.Add(nameof(vm.Type), "Тип связи недопустимм для данных страниц"); } if (vm.EventId != null) { if (eventType == null) { val.Add(nameof(vm.EventId), "Страница не найдена"); } else if (eventType != PageType.Event) { val.Add(nameof(vm.EventId), "Требуется страница события"); } else if (!RelationHelper.IsRelationEventReferenceAllowed(vm.Type)) { val.Add(nameof(vm.EventId), "Событие нельзя привязать к данному типу связи"); } } if (!string.IsNullOrEmpty(vm.DurationStart) || !string.IsNullOrEmpty(vm.DurationEnd)) { if (!RelationHelper.IsRelationDurationAllowed(vm.Type)) { val.Add(nameof(vm.DurationStart), "Дату нельзя указать для данного типа связи"); } else { var from = FuzzyDate.TryParse(vm.DurationStart); var to = FuzzyDate.TryParse(vm.DurationEnd); if (from > to) { val.Add(nameof(vm.DurationStart), "Дата начала не может быть больше даты конца"); } else if (FuzzyRange.TryParse(FuzzyRange.TryCombine(vm.DurationStart, vm.DurationEnd)) == null) { val.Add(nameof(vm.DurationStart), "Введите дату в корректном формате"); } } } var existingRelation = await _db.Relations .AnyAsync(x => vm.SourceIds.Contains(x.SourceId) && x.DestinationId == vm.DestinationId && x.Type == vm.Type && x.Id != vm.Id); if (existingRelation) { val.Add(nameof(vm.DestinationId), "Такая связь уже существует!"); } val.ThrowIfInvalid(); }