/// <summary> /// Generates a local url for the given post block. /// </summary> /// <param name="app">The application service</param> /// <param name="block">The block</param> /// <returns>The url</returns> public static string Url(this IApplicationService app, PostBlock block) { if (block != null) { return(Url(app, block.Body)); } return(""); }
/// <summary> /// Generates an absolute url for the given post block. /// </summary> /// <param name="app">The application service</param> /// <param name="block">The block</param> /// <returns>The url</returns> public static string AbsoluteUrl(this IApplicationService app, PostBlock block) { return($"{ AbsoluteUrlStart(app) }{ Url(app, block) }"); }
/// <summary> /// Saves the given post model /// </summary> /// <param name="model">The post model</param> /// <param name="isDraft">If the model should be saved as a draft</param> private async Task Save <T>(T model, bool isDraft) where T : Models.PostBase { var type = App.PostTypes.GetById(model.TypeId); var lastModified = DateTime.MinValue; if (type != null) { // Ensure category var category = await _db.Categories .FirstOrDefaultAsync(c => c.Id == model.Category.Id) .ConfigureAwait(false); if (category == null) { if (!string.IsNullOrWhiteSpace(model.Category.Slug)) { category = await _db.Categories .FirstOrDefaultAsync(c => c.BlogId == model.BlogId && c.Slug == model.Category.Slug) .ConfigureAwait(false); } if (category == null && !string.IsNullOrWhiteSpace(model.Category.Title)) { category = await _db.Categories .FirstOrDefaultAsync(c => c.BlogId == model.BlogId && c.Title == model.Category.Title) .ConfigureAwait(false); } if (category == null) { category = new Category { Id = model.Category.Id != Guid.Empty ? model.Category.Id : Guid.NewGuid(), BlogId = model.BlogId, Title = model.Category.Title, Slug = Utils.GenerateSlug(model.Category.Title), Created = DateTime.Now, LastModified = DateTime.Now }; await _db.Categories.AddAsync(category).ConfigureAwait(false); } model.Category.Id = category.Id; model.Category.Title = category.Title; model.Category.Slug = category.Slug; } // Ensure tags foreach (var t in model.Tags) { var tag = await _db.Tags .FirstOrDefaultAsync(tg => tg.Id == t.Id) .ConfigureAwait(false); if (tag == null) { if (!string.IsNullOrWhiteSpace(t.Slug)) { tag = await _db.Tags .FirstOrDefaultAsync(tg => tg.BlogId == model.BlogId && tg.Slug == t.Slug) .ConfigureAwait(false); } if (tag == null && !string.IsNullOrWhiteSpace(t.Title)) { tag = await _db.Tags .FirstOrDefaultAsync(tg => tg.BlogId == model.BlogId && tg.Title == t.Title) .ConfigureAwait(false); } if (tag == null) { tag = new Tag { Id = t.Id != Guid.Empty ? t.Id : Guid.NewGuid(), BlogId = model.BlogId, Title = t.Title, Slug = Utils.GenerateSlug(t.Title), Created = DateTime.Now, LastModified = DateTime.Now }; await _db.Tags.AddAsync(tag).ConfigureAwait(false); } t.Id = tag.Id; } t.Title = tag.Title; t.Slug = tag.Slug; } // Ensure that we have a slug if (string.IsNullOrWhiteSpace(model.Slug)) { model.Slug = Utils.GenerateSlug(model.Title, false); } else { model.Slug = Utils.GenerateSlug(model.Slug, false); } IQueryable <Post> postQuery = _db.Posts; if (isDraft) { postQuery = postQuery.AsNoTracking(); } // FirstOrDefaultAsync(p => p.Id ... postQuery = postQuery.OrderBy(p => p.Id); var post = await postQuery .Include(p => p.Permissions) .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields) .Include(p => p.Fields) .Include(p => p.Tags).ThenInclude(t => t.Tag) .AsSplitQuery() .FirstOrDefaultAsync(p => p.Id == model.Id) .ConfigureAwait(false); // If not, create a new post if (post == null) { post = new Post { Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(), Created = DateTime.Now, LastModified = DateTime.Now }; model.Id = post.Id; if (!isDraft) { await _db.Posts.AddAsync(post).ConfigureAwait(false); } } else { post.LastModified = DateTime.Now; } post = _contentService.Transform <T>(model, type, post); // Set if comments should be enabled post.EnableComments = model.EnableComments; post.CloseCommentsAfterDays = model.CloseCommentsAfterDays; // Update permissions post.Permissions.Clear(); foreach (var permission in model.Permissions) { post.Permissions.Add(new PostPermission { PostId = post.Id, Permission = permission }); } // Make sure foreign key is set for fields if (!isDraft) { foreach (var field in post.Fields) { if (field.PostId == Guid.Empty) { field.PostId = post.Id; await _db.PostFields.AddAsync(field).ConfigureAwait(false); } } } if (isDraft) { post.Category = new Category { Id = model.Category.Id, BlogId = model.BlogId, Title = model.Category.Title, Slug = model.Category.Slug }; } // Transform blocks var blockModels = model.Blocks; if (blockModels != null) { var blocks = _contentService.TransformBlocks(blockModels); var current = blocks.Select(b => b.Id).ToArray(); // Delete removed blocks var removed = post.Blocks .Where(b => !current.Contains(b.BlockId) && !b.Block.IsReusable && b.Block.ParentId == null) .Select(b => b.Block); var removedItems = post.Blocks .Where(b => !current.Contains(b.BlockId) && b.Block.ParentId != null && removed.Select(p => p.Id).ToList().Contains(b.Block.ParentId.Value)) .Select(b => b.Block); if (!isDraft) { _db.Blocks.RemoveRange(removed); _db.Blocks.RemoveRange(removedItems); } // Delete the old page blocks post.Blocks.Clear(); // Now map the new block for (var n = 0; n < blocks.Count; n++) { IQueryable <Block> blockQuery = _db.Blocks; if (isDraft) { blockQuery = blockQuery.AsNoTracking(); } var block = blockQuery .Include(b => b.Fields) .FirstOrDefault(b => b.Id == blocks[n].Id); if (block == null) { block = new Block { Id = blocks[n].Id != Guid.Empty ? blocks[n].Id : Guid.NewGuid(), Created = DateTime.Now }; if (!isDraft) { await _db.Blocks.AddAsync(block).ConfigureAwait(false); } } block.ParentId = blocks[n].ParentId; block.CLRType = blocks[n].CLRType; block.IsReusable = blocks[n].IsReusable; block.Title = blocks[n].Title; block.LastModified = DateTime.Now; var currentFields = blocks[n].Fields.Select(f => f.FieldId).Distinct(); var removedFields = block.Fields.Where(f => !currentFields.Contains(f.FieldId)); if (!isDraft) { _db.BlockFields.RemoveRange(removedFields); } foreach (var newField in blocks[n].Fields) { var field = block.Fields.FirstOrDefault(f => f.FieldId == newField.FieldId); if (field == null) { field = new BlockField { Id = newField.Id != Guid.Empty ? newField.Id : Guid.NewGuid(), BlockId = block.Id, FieldId = newField.FieldId }; if (!isDraft) { await _db.BlockFields.AddAsync(field).ConfigureAwait(false); } block.Fields.Add(field); } field.SortOrder = newField.SortOrder; field.CLRType = newField.CLRType; field.Value = newField.Value; } // Create the post block var postBlock = new PostBlock { Id = Guid.NewGuid(), BlockId = block.Id, Block = block, PostId = post.Id, SortOrder = n }; if (!isDraft) { await _db.PostBlocks.AddAsync(postBlock).ConfigureAwait(false); } post.Blocks.Add(postBlock); } } // Remove tags var removedTags = new List <PostTag>(); foreach (var tag in post.Tags) { if (!model.Tags.Any(t => t.Id == tag.TagId)) { removedTags.Add(tag); } } foreach (var removed in removedTags) { post.Tags.Remove(removed); } // Add tags foreach (var tag in model.Tags) { if (!post.Tags.Any(t => t.PostId == post.Id && t.TagId == tag.Id)) { var postTag = new PostTag { PostId = post.Id, TagId = tag.Id }; if (isDraft) { postTag.Tag = new Tag { Id = tag.Id, BlogId = post.BlogId, Title = tag.Title, Slug = tag.Slug }; } post.Tags.Add(postTag); } } if (!isDraft) { await _db.SaveChangesAsync().ConfigureAwait(false); await DeleteUnusedCategories(model.BlogId).ConfigureAwait(false); await DeleteUnusedTags(model.BlogId).ConfigureAwait(false); } else { var draft = await _db.PostRevisions .FirstOrDefaultAsync(r => r.PostId == post.Id && r.Created > lastModified) .ConfigureAwait(false); if (draft == null) { draft = new PostRevision { Id = Guid.NewGuid(), PostId = post.Id }; await _db.PostRevisions .AddAsync(draft) .ConfigureAwait(false); } draft.Data = JsonConvert.SerializeObject(post); draft.Created = post.LastModified; await _db.SaveChangesAsync().ConfigureAwait(false); } } }