Ejemplo n.º 1
0
        /// <summary>
        /// Saves the given page model
        /// </summary>
        /// <param name="model">The page model</param>
        private async Task <IEnumerable <Guid> > Save <T>(T model, bool isDraft) where T : Models.PageBase
        {
            var type         = App.PageTypes.GetById(model.TypeId);
            var affected     = new List <Guid>();
            var isNew        = false;
            var lastModified = DateTime.MinValue;

            if (type != null)
            {
                // Set content type
                model.ContentType = type.ContentTypeId;

                IQueryable <Page> pageQuery = _db.Pages;
                if (isDraft)
                {
                    pageQuery = pageQuery.AsNoTracking();
                }

                var page = await pageQuery
                           .Include(p => p.Blocks).ThenInclude(b => b.Block).ThenInclude(b => b.Fields)
                           .Include(p => p.Fields)
                           .FirstOrDefaultAsync(p => p.Id == model.Id)
                           .ConfigureAwait(false);

                if (page == null)
                {
                    isNew = true;
                }
                else
                {
                    lastModified = page.LastModified;
                }

                if (model.OriginalPageId.HasValue)
                {
                    var originalPageIsCopy = (await _db.Pages.AsNoTracking().FirstOrDefaultAsync(p => p.Id == model.OriginalPageId).ConfigureAwait(false))?.OriginalPageId.HasValue ?? false;
                    if (originalPageIsCopy)
                    {
                        throw new InvalidOperationException("Can not set copy of a copy");
                    }

                    var originalPageType = (await _db.Pages.AsNoTracking().FirstOrDefaultAsync(p => p.Id == model.OriginalPageId).ConfigureAwait(false))?.PageTypeId;
                    if (originalPageType != model.TypeId)
                    {
                        throw new InvalidOperationException("Copy can not have a different content type");
                    }

                    // Transform the model
                    if (page == null)
                    {
                        page = new Page()
                        {
                            Id = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(),
                        };

                        if (!isDraft)
                        {
                            _db.Pages.Add(page);

                            // Make room for the new page
                            var dest = await _db.Pages.Where(p => p.SiteId == model.SiteId && p.ParentId == model.ParentId).ToListAsync().ConfigureAwait(false);

                            affected.AddRange(MovePages(dest, page.Id, model.SiteId, model.ParentId, model.SortOrder, true));
                        }
                    }
                    else
                    {
                        // Check if the page has been moved
                        if (!isDraft && (page.ParentId != model.ParentId || page.SortOrder != model.SortOrder))
                        {
                            var source = await _db.Pages.Where(p => p.SiteId == page.SiteId && p.ParentId == page.ParentId && p.Id != model.Id).ToListAsync().ConfigureAwait(false);

                            var dest = page.ParentId == model.ParentId ? source : await _db.Pages.Where(p => p.SiteId == model.SiteId && p.ParentId == model.ParentId).ToListAsync().ConfigureAwait(false);

                            // Remove the old position for the page
                            affected.AddRange(MovePages(source, page.Id, page.SiteId, page.ParentId, page.SortOrder + 1, false));
                            // Add room for the new position of the page
                            affected.AddRange(MovePages(dest, page.Id, model.SiteId, model.ParentId, model.SortOrder, true));
                        }
                    }

                    if (!isDraft && (isNew || page.Title != model.Title || page.NavigationTitle != model.NavigationTitle))
                    {
                        // If this is new page or title has been updated it means
                        // the global sitemap changes. Notify the service.
                        affected.Add(page.Id);
                    }

                    page.PageTypeId      = model.TypeId;
                    page.OriginalPageId  = model.OriginalPageId;
                    page.SiteId          = model.SiteId;
                    page.Title           = model.Title;
                    page.NavigationTitle = model.NavigationTitle;
                    page.Slug            = model.Slug;
                    page.ParentId        = model.ParentId;
                    page.SortOrder       = model.SortOrder;
                    page.IsHidden        = model.IsHidden;
                    page.Route           = model.Route;
                    page.Published       = model.Published;

                    if (!isDraft)
                    {
                        await _db.SaveChangesAsync().ConfigureAwait(false);
                    }
                    else
                    {
                        var draft = await _db.PageRevisions
                                    .FirstOrDefaultAsync(r => r.PageId == page.Id && r.Created > lastModified)
                                    .ConfigureAwait(false);

                        if (draft == null)
                        {
                            draft = new PageRevision
                            {
                                Id     = Guid.NewGuid(),
                                PageId = page.Id
                            };
                            await _db.PageRevisions
                            .AddAsync(draft)
                            .ConfigureAwait(false);
                        }

                        draft.Data    = JsonConvert.SerializeObject(page);
                        draft.Created = page.LastModified;

                        await _db.SaveChangesAsync().ConfigureAwait(false);
                    }
                    return(affected);
                }

                // Transform the model
                if (page == null)
                {
                    page = new Page
                    {
                        Id           = model.Id != Guid.Empty ? model.Id : Guid.NewGuid(),
                        ParentId     = model.ParentId,
                        SortOrder    = model.SortOrder,
                        PageTypeId   = model.TypeId,
                        Created      = DateTime.Now,
                        LastModified = DateTime.Now
                    };
                    model.Id = page.Id;

                    if (!isDraft)
                    {
                        await _db.Pages.AddAsync(page);

                        // Make room for the new page
                        var dest = await _db.Pages.Where(p => p.SiteId == model.SiteId && p.ParentId == model.ParentId).ToListAsync().ConfigureAwait(false);

                        affected.AddRange(MovePages(dest, page.Id, model.SiteId, model.ParentId, model.SortOrder, true));
                    }
                }
                else
                {
                    // Check if the page has been moved
                    if (!isDraft && (page.ParentId != model.ParentId || page.SortOrder != model.SortOrder))
                    {
                        var source = await _db.Pages.Where(p => p.SiteId == page.SiteId && p.ParentId == page.ParentId && p.Id != model.Id).ToListAsync().ConfigureAwait(false);

                        var dest = page.ParentId == model.ParentId ? source : await _db.Pages.Where(p => p.SiteId == model.SiteId && p.ParentId == model.ParentId).ToListAsync().ConfigureAwait(false);

                        // Remove the old position for the page
                        affected.AddRange(MovePages(source, page.Id, page.SiteId, page.ParentId, page.SortOrder + 1, false));
                        // Add room for the new position of the page
                        affected.AddRange(MovePages(dest, page.Id, model.SiteId, model.ParentId, model.SortOrder, true));
                    }
                    page.LastModified = DateTime.Now;
                }

                if (isNew || page.Title != model.Title || page.NavigationTitle != model.NavigationTitle)
                {
                    // If this is new page or title has been updated it means
                    // the global sitemap changes. Notify the service.
                    affected.Add(page.Id);
                }

                page = _contentService.Transform <T>(model, type, page);

                // 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 = page.Blocks
                                  .Where(b => !current.Contains(b.BlockId) && !b.Block.IsReusable && b.Block.ParentId == null)
                                  .Select(b => b.Block);
                    var removedItems = page.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
                    page.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 = await blockQuery
                                    .Include(b => b.Fields)
                                    .FirstOrDefaultAsync(b => b.Id == blocks[n].Id)
                                    .ConfigureAwait(false);

                        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 page block
                        page.Blocks.Add(new PageBlock
                        {
                            Id        = Guid.NewGuid(),
                            BlockId   = block.Id,
                            Block     = block,
                            PageId    = page.Id,
                            SortOrder = n
                        });
                    }
                }
                if (!isDraft)
                {
                    await _db.SaveChangesAsync().ConfigureAwait(false);
                }
                else
                {
                    var draft = await _db.PageRevisions
                                .FirstOrDefaultAsync(r => r.PageId == page.Id && r.Created > lastModified)
                                .ConfigureAwait(false);

                    if (draft == null)
                    {
                        draft = new PageRevision
                        {
                            Id     = Guid.NewGuid(),
                            PageId = page.Id
                        };
                        await _db.PageRevisions
                        .AddAsync(draft)
                        .ConfigureAwait(false);
                    }

                    draft.Data    = JsonConvert.SerializeObject(page);
                    draft.Created = page.LastModified;

                    await _db.SaveChangesAsync().ConfigureAwait(false);
                }
            }
            return(affected);
        }
Ejemplo n.º 2
0
        public async Task <IActionResult> Revert(int pageId, int revisionId)
        {
            try
            {
                Page page = await _Db.Pages
                            .Include(p => p.PageRevisions)
                            .ThenInclude(pr => pr.Template)
                            .Include(p => p.PageRevisions)
                            .ThenInclude(pr => pr.RevisionTextComponents)
                            .ThenInclude(rtc => rtc.TextComponent)
                            .Include(p => p.PageRevisions)
                            .ThenInclude(pr => pr.RevisionMediaComponents)
                            .ThenInclude(rmc => rmc.MediaComponent)
                            .Where(p => p.Id == pageId)
                            .FirstOrDefaultAsync();

                // Ensure page exists
                if (page == null)
                {
                    return(BadRequest("No page exists for given page ID"));
                }

                // Ensure that the revision belongs to the correct page
                if (!page.PageRevisions.Any(pr => pr.Id == revisionId))
                {
                    return(BadRequest("Given page does not contain a revision for the given revision ID"));
                }

                PageRevision revertTo = page.PageRevisions.Find(pr => pr.Id == revisionId);

                // Create a new revision for the page, that is a copy of the supplied revision
                PageRevision newRevision = new PageRevision
                {
                    Reason    = string.Format("Reverted page to revision: {0}", revertTo.Reason),
                    CreatedBy = await _Db.Accounts.FindAsync(User.AccountId()),
                    Page      = page,
                    Template  = revertTo.Template
                };

                await _Db.AddAsync(newRevision);

                // Create associations between the existing text components and the new revision
                foreach (RevisionTextComponent rtc in revertTo.RevisionTextComponents)
                {
                    await _Db.AddAsync(new RevisionTextComponent
                    {
                        PageRevisionId  = newRevision.Id,
                        TextComponentId = rtc.TextComponentId
                    });
                }

                // Do the same for existing media components
                foreach (RevisionMediaComponent rmc in revertTo.RevisionMediaComponents)
                {
                    await _Db.AddAsync(new RevisionMediaComponent
                    {
                        PageRevisionId   = newRevision.Id,
                        MediaComponentId = rmc.MediaComponentId
                    });
                }

                // Execute the database transaction
                await _Db.SaveChangesAsync();

                _Logger.LogDebug("{0} page reverted to revision {1} ({2}) by {3}",
                                 page.Name, revisionId, revertTo.Reason, User.AccountName());

                return(Ok());
            }
            catch (Exception ex)
            {
                _Logger.LogError("Error reverting page {0} to revision {1}: {2}", pageId, revisionId, ex.Message);
                _Logger.LogError(ex.StackTrace);
                return(BadRequest("Something went wrong, please try again later."));
            }
        }
Ejemplo n.º 3
0
        //POST: /admin/pages/create
        public async Task <IActionResult> Create(IFormCollection request,
                                                 [Bind("Name", "Description", "Section")] Page page)
        {
            PageTemplate template = await _Db.PageTemplates.FindAsync(
                request.Int("templateId"));

            if (template == null)
            {
                return(NotFound($"The chosen template was not found."));
            }

            if (!ModelState.IsValid)
            {
                return(BadRequest("Server side validation failed."));
            }

            try
            {
                await _Db.AddAsync(page);

                // Create initial page revision
                PageRevision pageRevision = new PageRevision
                {
                    Page      = page,
                    Template  = template,
                    CreatedBy = await _Db.Accounts.FindAsync(User.AccountId())
                };
                await _Db.AddAsync(pageRevision);

                // Create empty text fields, and associate to new page
                for (int i = 0; i < template.TextAreas; i++)
                {
                    TextComponent textField = new TextComponent {
                        SlotNo = i
                    };
                    await _Db.AddAsync(textField);

                    await _Db.AddAsync(new RevisionTextComponent
                    {
                        TextComponent = textField,
                        PageRevision  = pageRevision,
                    });
                }

                // Create empty media fields, and associate to new page
                for (int i = 0; i < template.MediaAreas; i++)
                {
                    MediaComponent mediaComponent = new MediaComponent {
                        SlotNo = i
                    };
                    await _Db.AddAsync(mediaComponent);

                    await _Db.AddAsync(new RevisionMediaComponent
                    {
                        PageRevisionId   = pageRevision.Id,
                        MediaComponentId = mediaComponent.Id
                    });
                }

                // Save all to database in one transaction
                await _Db.SaveChangesAsync();

                // Return new page URL to the caller
                return(Ok(page.AbsoluteUrl));
            }
            catch (Exception ex)
            {
                _Logger.LogWarning("Error creating new page: {0}", ex.Message);
                _Logger.LogWarning(ex.StackTrace);
                return(BadRequest("There was an error creating the page. Please try again later."));
            }
        }
Ejemplo n.º 4
0
        public async Task <IActionResult> CreateRevision(int pageId, IFormCollection request)
        {
            /* To be supplied in request body -
             *
             * Reason for change - e.g. Updated pricing information
             * Template ID
             * Collection of text components
             * Collection of media components
             *
             */
            try
            {
                // Load page from database
                Page page = await _Db.Pages.FindAsync(pageId);

                if (page == null)
                {
                    return(BadRequest("No page exists for given page ID"));
                }

                List <TextComponent> newRevisionTextComponents =
                    request.Deserialize(typeof(List <TextComponent>), "textComponents");

                List <MediaComponent> newRevisionMediaComponents =
                    request.Deserialize(typeof(List <MediaComponent>), "imageComponents");

                // Todo: Do this after the view has had template switching enabled
                // Load template from database
                // PageTemplate template = await _Db.PageTemplates
                //    .FindAsync(request.Int("templateId"));

                // Fetch the original revision
                PageRevision old = await _Db.PageRevisions
                                   .Include(pr => pr.Template)
                                   .Include(pr => pr.RevisionMediaComponents)
                                   .ThenInclude(rmc => rmc.MediaComponent)
                                   .Include(pr => pr.RevisionTextComponents)
                                   .ThenInclude(rtc => rtc.TextComponent)
                                   .ThenInclude(tc => tc.CmsButton)
                                   .Where(pr => pr.Page == page)
                                   .OrderByDescending(pr => pr.CreatedAt)
                                   .FirstOrDefaultAsync();

                var oldRevision = new
                {
                    old.Template,
                    TextComponents = old.RevisionTextComponents
                                     .Select(rtc => rtc.TextComponent)
                                     .OrderBy(tc => tc.SlotNo)
                                     .ToList(),

                    MediaComponents = old.RevisionMediaComponents
                                      .Select(rmc => rmc.MediaComponent)
                                      .OrderBy(tc => tc.SlotNo)
                                      .ToList()
                };

                // Create the new page revision
                PageRevision newRevision = new PageRevision
                {
                    Page      = page,
                    Template  = oldRevision.Template,
                    Reason    = request.Str("reason"),
                    CreatedBy = await _Db.Accounts.FindAsync(User.AccountId()),
                };

                // Assign the new revision an ID
                await _Db.AddAsync(newRevision);

                for (int i = 0; i < newRevision.Template.TextAreas; i++)
                {
                    TextComponent textComponentToSave = null;

                    // Only save a new text component if it has changed
                    if (!newRevisionTextComponents[i].Equals(oldRevision.TextComponents[i]))
                    {
                        textComponentToSave = newRevisionTextComponents[i];

                        // Set ID to 0 so that EF Core assigns us a new one
                        textComponentToSave.Id = 0;

                        // Save a new button if the components button does not yet exist in database.
                        if (textComponentToSave.CmsButton != null &&
                            !textComponentToSave.CmsButton.Equals(oldRevision.TextComponents[i].CmsButton))
                        {
                            await _Db.AddAsync(textComponentToSave.CmsButton);
                        }

                        // Generate ID for the new TextComponent
                        await _Db.AddAsync(textComponentToSave);
                    }

                    // Add association between component and new revision
                    await _Db.AddAsync(new RevisionTextComponent
                    {
                        // Use the new components ID if it exists, other use existing (unchanged) component
                        // from previous revision
                        TextComponentId = textComponentToSave?.Id ?? oldRevision.TextComponents[i].Id,
                        PageRevisionId  = newRevision.Id
                    });
                }

                // Do the same for media components
                for (int i = 0; i < newRevision.Template.MediaAreas; i++)
                {
                    MediaComponent mediaComponentToSave = null;

                    // Only create new media component if the old one was modified
                    if (!newRevisionMediaComponents[i].Equals(oldRevision.MediaComponents[i]))
                    {
                        mediaComponentToSave = newRevisionMediaComponents[i];

                        // Generate new ID
                        mediaComponentToSave.Id = 0;
                        await _Db.AddAsync(mediaComponentToSave);
                    }

                    // Add association to new revision
                    await _Db.AddAsync(new RevisionMediaComponent
                    {
                        PageRevisionId   = newRevision.Id,
                        MediaComponentId = mediaComponentToSave?.Id ?? oldRevision.MediaComponents[i].Id
                    });
                }

                // Save changes
                await _Db.SaveChangesAsync();

                _Logger.LogDebug("New page revision created for page {0} ({1}): {2}", pageId, page.Name, newRevision.Reason);

                return(Ok());
            }
            catch (Exception ex)
            {
                _Logger.LogError("Error creating new revision for page {0}: {1}", pageId, ex.Message);
                _Logger.LogError(ex.StackTrace);
                return(BadRequest("Something went wrong, please try again later."));
            }
        }