/// <summary> /// Transforms the given blocks to the internal data model. /// </summary> /// <param name="models">The blocks</param> /// <param name="languageId">The language id</param> /// <returns>The data model</returns> public IList <ContentBlock> TransformContentBlocks(IList <Extend.Block> models, Guid languageId) { var blocks = new List <ContentBlock>(); if (models != null) { for (var n = 0; n < models.Count; n++) { var type = App.Blocks.GetByType(models[n].GetType().FullName); if (type != null) { // Make sure we generate an id if it's empty if (models[n].Id == Guid.Empty) { models[n].Id = Guid.NewGuid(); } var block = new ContentBlock() { Id = models[n].Id, CLRType = models[n].GetType().FullName }; foreach (var prop in models[n].GetType().GetProperties(App.PropertyBindings)) { if (typeof(Extend.IField).IsAssignableFrom(prop.PropertyType)) { var blockValue = prop.GetValue(models[n]); // Only save fields to the database var field = new ContentBlockField() { Id = Guid.NewGuid(), BlockId = block.Id, FieldId = prop.Name, SortOrder = 0, CLRType = prop.PropertyType.FullName, }; // Set value if (blockValue is Extend.ITranslatable) { var translation = new ContentBlockFieldTranslation { FieldId = field.Id, LanguageId = languageId, Value = App.SerializeObject(blockValue, prop.PropertyType) }; field.Translations.Add(translation); } else { field.Value = App.SerializeObject(blockValue, prop.PropertyType); } block.Fields.Add(field); } } blocks.Add(block); if (typeof(Extend.BlockGroup).IsAssignableFrom(models[n].GetType())) { var blockItems = TransformContentBlocks(((Extend.BlockGroup)models[n]).Items, languageId); if (blockItems.Count() > 0) { foreach (var item in blockItems) { item.ParentId = block.Id; } blocks.AddRange(blockItems); } } } } } return(blocks); }
/// <summary> /// Saves the given content model /// </summary> /// <param name="model">The content model</param> /// <param name="languageId">The selected language id</param> public async Task Save <T>(T model, Guid languageId) where T : Models.GenericContent { var type = App.ContentTypes.GetById(model.TypeId); var lastModified = DateTime.MinValue; if (type != null) { // Ensure category if (type.UseCategory) { if (model is Models.ICategorizedContent categorized) { var category = await _db.Taxonomies .FirstOrDefaultAsync(c => c.Id == categorized.Category.Id) .ConfigureAwait(false); if (category == null) { if (!string.IsNullOrWhiteSpace(categorized.Category.Slug)) { category = await _db.Taxonomies .FirstOrDefaultAsync(c => c.GroupId == type.Group && c.Slug == categorized.Category.Slug && c.Type == TaxonomyType.Category) .ConfigureAwait(false); } if (category == null && !string.IsNullOrWhiteSpace(categorized.Category.Title)) { category = await _db.Taxonomies .FirstOrDefaultAsync(c => c.GroupId == type.Group && c.Title == categorized.Category.Title && c.Type == TaxonomyType.Category) .ConfigureAwait(false); } if (category == null) { category = new Taxonomy { Id = categorized.Category.Id != Guid.Empty ? categorized.Category.Id : Guid.NewGuid(), GroupId = type.Group, Type = TaxonomyType.Category, Title = categorized.Category.Title, Slug = Utils.GenerateSlug(categorized.Category.Title), Created = DateTime.Now, LastModified = DateTime.Now }; await _db.Taxonomies.AddAsync(category).ConfigureAwait(false); } categorized.Category.Id = category.Id; categorized.Category.Title = category.Title; categorized.Category.Slug = category.Slug; } } } // Ensure tags if (type.UseTags) { if (model is Models.ITaggedContent tagged) { foreach (var t in tagged.Tags) { var tag = await _db.Taxonomies .FirstOrDefaultAsync(tg => tg.Id == t.Id) .ConfigureAwait(false); if (tag == null) { if (!string.IsNullOrWhiteSpace(t.Slug)) { tag = await _db.Taxonomies .FirstOrDefaultAsync(tg => tg.GroupId == type.Group && tg.Slug == t.Slug && tg.Type == TaxonomyType.Tag) .ConfigureAwait(false); } if (tag == null && !string.IsNullOrWhiteSpace(t.Title)) { tag = await _db.Taxonomies .FirstOrDefaultAsync(tg => tg.GroupId == type.Group && tg.Title == t.Title && tg.Type == TaxonomyType.Tag) .ConfigureAwait(false); } if (tag == null) { tag = new Taxonomy { Id = t.Id != Guid.Empty ? t.Id : Guid.NewGuid(), GroupId = type.Group, Type = TaxonomyType.Tag, Title = t.Title, Slug = Utils.GenerateSlug(t.Title), Created = DateTime.Now, LastModified = DateTime.Now }; await _db.Taxonomies.AddAsync(tag).ConfigureAwait(false); } t.Id = tag.Id; } t.Title = tag.Title; t.Slug = tag.Slug; } } } var content = await _db.Content .Include(c => c.Translations) .Include(c => c.Blocks).ThenInclude(b => b.Fields).ThenInclude(f => f.Translations) .Include(c => c.Fields).ThenInclude(f => f.Translations) .Include(c => c.Category) .Include(c => c.Tags).ThenInclude(t => t.Taxonomy) .AsSplitQuery() .FirstOrDefaultAsync(p => p.Id == model.Id) .ConfigureAwait(false); // If not, create new content if (content == null) { content = new Content { Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(), Created = DateTime.Now, LastModified = DateTime.Now }; model.Id = content.Id; await _db.Content.AddAsync(content).ConfigureAwait(false); } else { content.LastModified = DateTime.Now; } content = _service.Transform <T>(model, type, content, languageId); // Process fields foreach (var field in content.Fields) { // Ensure foreign key for new fields if (field.ContentId == Guid.Empty) { field.ContentId = content.Id; await _db.ContentFields.AddAsync(field).ConfigureAwait(false); } } if (type.UseTags) { if (model is Models.ITaggedContent taggedModel) { // Remove tags var removedTags = new List <ContentTaxonomy>(); foreach (var tag in content.Tags) { if (!taggedModel.Tags.Any(t => t.Id == tag.TaxonomyId)) { removedTags.Add(tag); } } foreach (var removed in removedTags) { content.Tags.Remove(removed); } // Add tags foreach (var tag in taggedModel.Tags) { if (!content.Tags.Any(t => t.ContentId == content.Id && t.TaxonomyId == tag.Id)) { var contentTaxonomy = new ContentTaxonomy { ContentId = content.Id, TaxonomyId = tag.Id }; content.Tags.Add(contentTaxonomy); } } } } // Transform blocks if (model is Models.IBlockContent blockModel) { var blockModels = blockModel.Blocks; if (blockModels != null) { var blocks = _service.TransformContentBlocks(blockModels, languageId); var current = blocks.Select(b => b.Id).ToArray(); // Delete removed blocks var removed = content.Blocks .Where(b => !current.Contains(b.Id) && b.ParentId == null) .ToList(); var removedItems = content.Blocks .Where(b => !current.Contains(b.Id) && b.ParentId != null) // && removed.Select(p => p.Id).ToList().Contains(b.ParentId.Value)) .ToList(); _db.ContentBlocks.RemoveRange(removed); _db.ContentBlocks.RemoveRange(removedItems); // Map the new block for (var n = 0; n < blocks.Count; n++) { var block = content.Blocks.FirstOrDefault(b => b.Id == blocks[n].Id); if (block == null) { block = new ContentBlock { Id = blocks[n].Id != Guid.Empty ? blocks[n].Id : Guid.NewGuid() }; await _db.ContentBlocks.AddAsync(block).ConfigureAwait(false); } block.ParentId = blocks[n].ParentId; block.SortOrder = n; block.CLRType = blocks[n].CLRType; var currentFields = blocks[n].Fields.Select(f => f.FieldId).Distinct(); var removedFields = block.Fields.Where(f => !currentFields.Contains(f.FieldId)); _db.ContentBlockFields.RemoveRange(removedFields); foreach (var newField in blocks[n].Fields) { var field = block.Fields.FirstOrDefault(f => f.FieldId == newField.FieldId); if (field == null) { field = new ContentBlockField { Id = newField.Id != Guid.Empty ? newField.Id : Guid.NewGuid(), BlockId = block.Id, FieldId = newField.FieldId }; await _db.ContentBlockFields.AddAsync(field).ConfigureAwait(false); block.Fields.Add(field); } field.SortOrder = newField.SortOrder; field.CLRType = newField.CLRType; field.Value = newField.Value; foreach (var newTranslation in newField.Translations) { var translation = field.Translations.FirstOrDefault(t => t.LanguageId == newTranslation.LanguageId); if (translation == null) { translation = new ContentBlockFieldTranslation { FieldId = field.Id, LanguageId = languageId }; await _db.ContentBlockFieldTranslations.AddAsync(translation).ConfigureAwait(false); field.Translations.Add(translation); } translation.Value = newTranslation.Value; } } content.Blocks.Add(block); } } } await _db.SaveChangesAsync().ConfigureAwait(false); /* * TODO * * await DeleteUnusedCategories(model.BlogId).ConfigureAwait(false); * await DeleteUnusedTags(model.BlogId).ConfigureAwait(false); */ } }