예제 #1
0
        protected ICommandContext GetMockedCommandContext(IMessagesIndicator messagesIndicator = null)
        {
            Mock <ICommandContext> commandContext = new Mock <ICommandContext>();

            commandContext.Setup(f => f.Messages).Returns(() => messagesIndicator ?? GetMockedMessagesIndicator());
            return(commandContext.Object);
        }
예제 #2
0
        public static IHtmlString CustomMessagesBox(this HtmlHelper html, IMessagesIndicator messages, string id = null,
                                                    IDictionary <string, string> attributes = null)
        {
            if (string.IsNullOrWhiteSpace(id))
            {
                id = string.Format("bcms-custom-messages-{0}", Guid.NewGuid());
            }

            return(MessagesBox(html, id, attributes, cssClassMessages + " " + cssClassCustomMessages, messages));
        }
예제 #3
0
        public static IHtmlString CustomMessagesBox(this HtmlHelper html, IMessagesIndicator messages, string id = null,
            IDictionary<string, string> attributes = null)
        {
            if (string.IsNullOrWhiteSpace(id))
            {
                id = string.Format("bcms-custom-messages-{0}", Guid.NewGuid());
            }

            return MessagesBox(html, id, attributes, cssClassMessages + " " + cssClassCustomMessages, messages);
        }
예제 #4
0
        /// <summary>
        /// Renders messages box with given id.
        /// </summary>
        /// <param name="html">The HTML helper.</param>
        /// <param name="id">The messages box id.</param>
        /// <param name="attributes">The attributes.</param>
        /// <param name="cssClass">The CSS class.</param>
        /// <param name="messages">The messages.</param>
        /// <returns>
        /// Html string with rendered messages box.
        /// </returns>
        /// <exception cref="CmsException">Unable to generate messages box.;Controller should inherit CmsControllerBase class.</exception>
        /// <exception cref="System.NotSupportedException">Controller should inherit CmsControllerBase class.</exception>
        private static IHtmlString MessagesBox(this HtmlHelper html, string id,
            IDictionary<string, string> attributes, string cssClass, IMessagesIndicator messages = null)
        {
            var controller = html.ViewContext.Controller as CmsControllerBase;
            if (controller == null)
            {
                throw new CmsException("Unable to generate messages box.", new NotSupportedException("Controller should inherit CmsControllerBase class."));
            }

            string customCssClass = null;
            if (attributes != null)
            {
                customCssClass = attributes
                    .Where(a => a.Key == "class" && !string.IsNullOrWhiteSpace(a.Value))
                    .Select(a => string.Format(" {0}", a.Value))
                    .FirstOrDefault();
            }

            var sb = new StringBuilder();
            sb.AppendFormat("<div class=\"{0} {1}\"", cssClass, customCssClass);
            if (!string.IsNullOrEmpty(id))
            {
                sb.Append(" id=\"" + id + "\"");
            }
            if (attributes != null)
            {
                foreach (var pair in attributes)
                {
                    if (pair.Key != "class")
                    {
                        sb.AppendFormat(" {0}=\"{1}\"", pair.Key, pair.Value);
                    }
                }
            }
            sb.AppendLine(">");

            if (messages == null)
            {
                messages = controller.Messages;
            }

            AddMessagesBoxBlock(sb, "bcms-success-messages", messages.Success);
            AddMessagesBoxBlock(sb, "bcms-info-messages", messages.Info);
            AddMessagesBoxBlock(sb, "bcms-warning-messages", messages.Warn);
            AddMessagesBoxBlock(sb, "bcms-error-messages", messages.Error);

            sb.AppendLine("</div>");

            return new MvcHtmlString(sb.ToString());
        }
예제 #5
0
        /// <summary>
        /// Renders messages box with given id.
        /// </summary>
        /// <param name="html">The HTML helper.</param>
        /// <param name="id">The messages box id.</param>
        /// <param name="attributes">The attributes.</param>
        /// <param name="cssClass">The CSS class.</param>
        /// <param name="messages">The messages.</param>
        /// <returns>
        /// Html string with rendered messages box.
        /// </returns>
        /// <exception cref="CmsException">Unable to generate messages box.;Controller should inherit CmsControllerBase class.</exception>
        /// <exception cref="System.NotSupportedException">Controller should inherit CmsControllerBase class.</exception>
        private static IHtmlString MessagesBox(this HtmlHelper html, string id,
                                               IDictionary <string, string> attributes, string cssClass, IMessagesIndicator messages = null)
        {
            var controller = html.ViewContext.Controller as CmsControllerBase;

            if (controller == null)
            {
                throw new CmsException("Unable to generate messages box.", new NotSupportedException("Controller should inherit CmsControllerBase class."));
            }

            string customCssClass = null;

            if (attributes != null)
            {
                customCssClass = attributes
                                 .Where(a => a.Key == "class" && !string.IsNullOrWhiteSpace(a.Value))
                                 .Select(a => string.Format(" {0}", a.Value))
                                 .FirstOrDefault();
            }

            var sb = new StringBuilder();

            sb.AppendFormat("<div class=\"{0} {1}\"", cssClass, customCssClass);
            if (!string.IsNullOrEmpty(id))
            {
                sb.Append(" id=\"" + id + "\"");
            }
            if (attributes != null)
            {
                foreach (var pair in attributes)
                {
                    if (pair.Key != "class")
                    {
                        sb.AppendFormat(" {0}=\"{1}\"", pair.Key, pair.Value);
                    }
                }
            }
            sb.AppendLine(">");

            if (messages == null)
            {
                messages = controller.Messages;
            }

            AddMessagesBoxBlock(sb, "bcms-success-messages", messages.Success);
            AddMessagesBoxBlock(sb, "bcms-info-messages", messages.Info);
            AddMessagesBoxBlock(sb, "bcms-warning-messages", messages.Warn);
            AddMessagesBoxBlock(sb, "bcms-error-messages", messages.Error);

            sb.AppendLine("</div>");

            return(new MvcHtmlString(sb.ToString()));
        }
예제 #6
0
        /// <summary>
        /// Deletes the page.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="principal">The principal.</param>
        /// <param name="messages">The messages.</param>
        /// <returns>
        /// Delete result
        /// </returns>
        /// <exception cref="ConcurrentDataException"></exception>
        /// <exception cref="System.ComponentModel.DataAnnotations.ValidationException">
        /// </exception>
        public bool DeletePage(DeletePageViewModel model, IPrincipal principal, IMessagesIndicator messages = null)
        {
            var languagesFuture = repository.AsQueryable<Language>().ToFuture();

            var page =
                repository.AsQueryable<PageProperties>(p => p.Id == model.PageId)
                    .FetchMany(p => p.PageContents)
                    .ThenFetch(pc => pc.Content)
                    .ToFuture()
                    .ToList()
                    .FirstOne();

            // Unidentified problem where in some cases proxy is returned.
            if (page is INHibernateProxy)
            {
                page = repository.UnProxy(page);
            }

            if (model.Version > 0 && page.Version != model.Version)
            {
                throw new ConcurrentDataException(page);
            }

            if (page.IsMasterPage && repository.AsQueryable<MasterPage>(mp => mp.Master == page).Any())
            {
                var message = PagesGlobalization.DeletePageCommand_MasterPageHasChildren_Message;
                var logMessage = string.Format("Failed to delete page. Page is selected as master page. Id: {0} Url: {1}", page.Id, page.PageUrl);
                throw new ValidationException(() => message, logMessage);
            }

            var isRedirectInternal = false;
            if (!string.IsNullOrWhiteSpace(model.RedirectUrl))
            {
                isRedirectInternal = urlService.ValidateInternalUrl(model.RedirectUrl);
                if (!isRedirectInternal && urlService.ValidateInternalUrl(urlService.FixUrl(model.RedirectUrl)))
                {
                    isRedirectInternal = true;
                }
                if (isRedirectInternal)
                {
                    model.RedirectUrl = urlService.FixUrl(model.RedirectUrl);
                }
            }

            if (model.UpdateSitemap)
            {
                accessControlService.DemandAccess(principal, RootModuleConstants.UserRoles.EditContent);
            }

            var sitemaps = new Dictionary<Sitemap, bool>();
            var sitemapNodes = sitemapService.GetNodesByPage(page);
            if (model.UpdateSitemap)
            {
                sitemapNodes.Select(node => node.Sitemap)
                            .Distinct()
                            .ToList()
                            .ForEach(
                                sitemap =>
                                sitemaps.Add(
                                    sitemap,
                                    !cmsConfiguration.Security.AccessControlEnabled || accessControlService.GetAccessLevel(sitemap, principal) == AccessLevel.ReadWrite));

                foreach (var node in sitemapNodes)
                {
                    if (sitemaps[node.Sitemap] && node.ChildNodes.Count > 0)
                    {
                        var logMessage = string.Format("In {0} sitemap node {1} has {2} child nodes.", node.Sitemap.Id, node.Id, node.ChildNodes.Count);
                        throw new ValidationException(() => PagesGlobalization.DeletePageCommand_SitemapNodeHasChildNodes_Message, logMessage);
                    }
                }
            }

            unitOfWork.BeginTransaction();

            // Update sitemap nodes
            IList<SitemapNode> updatedNodes = new List<SitemapNode>();
            IList<SitemapNode> deletedNodes = new List<SitemapNode>();
            UpdateSitemapNodes(model, page, sitemapNodes, sitemaps, languagesFuture.ToList(), updatedNodes, deletedNodes);

            Redirect redirect;
            if (!string.IsNullOrWhiteSpace(model.RedirectUrl))
            {
                if (string.Equals(page.PageUrl, model.RedirectUrl, StringComparison.OrdinalIgnoreCase))
                {
                    var logMessage = string.Format("Circular redirect loop from url {0} to url {0}.", model.RedirectUrl);
                    throw new ValidationException(() => PagesGlobalization.ValidatePageUrlCommand_SameUrlPath_Message, logMessage);
                }

                // Validate url
                if (!urlService.ValidateExternalUrl(model.RedirectUrl))
                {
                    var logMessage = string.Format("Invalid redirect url {0}.", model.RedirectUrl);
                    throw new ValidationException(() => PagesGlobalization.ValidatePageUrlCommand_InvalidUrlPath_Message, logMessage);
                }

                string patternsValidationMessage;
                if (isRedirectInternal
                    && !urlService.ValidateUrlPatterns(model.RedirectUrl, out patternsValidationMessage, PagesGlobalization.DeletePage_RedirectUrl_Name))
                {
                    var logMessage = string.Format("{0}. URL: {1}.", patternsValidationMessage, model.RedirectUrl);
                    throw new ValidationException(() => patternsValidationMessage, logMessage);
                }

                redirect = redirectService.GetPageRedirect(page.PageUrl);
                if (redirect != null)
                {
                    redirect.RedirectUrl = model.RedirectUrl;
                }
                else
                {
                    redirect = redirectService.CreateRedirectEntity(page.PageUrl, model.RedirectUrl);
                }

                if (redirect != null)
                {
                    repository.Save(redirect);
                }
            }
            else
            {
                redirect = null;
            }

            // Delete child entities.            
            if (page.PageTags != null)
            {
                foreach (var pageTag in page.PageTags)
                {
                    repository.Delete(pageTag);
                }
            }

            var deletedPageContents = new List<PageContent>();
            var htmlContentsToDelete = new List<HtmlContent>();
            if (page.PageContents != null)
            {
                foreach (var pageContent in page.PageContents)
                {
                    // If content is HTML content, delete HTML content
                    var htmlContent = pageContent.Content as HtmlContent;
                    if (htmlContent != null)
                    {
                        var draft = pageContent.Content.History != null ? pageContent.Content.History.FirstOrDefault(c => c.Status == ContentStatus.Draft) : null;
                        if (draft != null)
                        {
                            repository.Delete(draft);
                        }

                        repository.Delete(htmlContent);
                        htmlContentsToDelete.Add(htmlContent);
                    }

                    repository.Delete(pageContent);
                    deletedPageContents.Add(pageContent);
                }
            }

            if (page.Options != null)
            {
                foreach (var option in page.Options)
                {
                    repository.Delete(option);
                }
            }

            if (page.AccessRules != null)
            {
                var rules = page.AccessRules.ToList();
                rules.ForEach(page.RemoveRule);
            }

            if (page.MasterPages != null)
            {
                foreach (var master in page.MasterPages)
                {
                    repository.Delete(master);
                }
            }

            // Delete page
            repository.Delete<Root.Models.Page>(page);

            // Commit
            unitOfWork.Commit();

            var updatedSitemaps = new List<Sitemap>();
            foreach (var node in updatedNodes)
            {
                Events.SitemapEvents.Instance.OnSitemapNodeUpdated(node);
                if (!updatedSitemaps.Contains(node.Sitemap))
                {
                    updatedSitemaps.Add(node.Sitemap);
                }
            }

            foreach (var node in deletedNodes)
            {
                Events.SitemapEvents.Instance.OnSitemapNodeDeleted(node);
                if (!updatedSitemaps.Contains(node.Sitemap))
                {
                    updatedSitemaps.Add(node.Sitemap);
                }
            }

            foreach (var updatedSitemap in updatedSitemaps)
            {
                Events.SitemapEvents.Instance.OnSitemapUpdated(updatedSitemap);
            }

            // Notifying about redirect created
            if (redirect != null)
            {
                Events.PageEvents.Instance.OnRedirectCreated(redirect);
            }

            // Notify about deleted page contents
            foreach (var deletedPageContent in deletedPageContents)
            {
                Events.PageEvents.Instance.OnPageContentDeleted(deletedPageContent);
            }

            // Notify about deleted html contents
            foreach (var htmlContent in htmlContentsToDelete)
            {
                Events.PageEvents.Instance.OnHtmlContentDeleted(htmlContent);
            }

            // Notifying, that page is deleted.
            Events.PageEvents.Instance.OnPageDeleted(page);

            if (sitemaps.Any(tuple => !tuple.Value) && messages != null)
            {
                // Some sitemaps where skipped, because user has no permission to edit.
                messages.AddSuccess(PagesGlobalization.DeletePage_SitemapSkipped_Message);
            }

            return true;
        }
 protected ICommandContext GetMockedCommandContext(IMessagesIndicator messagesIndicator = null)
 {
     Mock<ICommandContext> commandContext = new Mock<ICommandContext>();
     commandContext.Setup(f => f.Messages).Returns(() => messagesIndicator ?? GetMockedMessagesIndicator());
     return commandContext.Object;
 }
예제 #8
0
        /// <summary>
        /// Deletes the page.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="principal">The principal.</param>
        /// <param name="messages">The messages.</param>
        /// <returns>
        /// Delete result
        /// </returns>
        /// <exception cref="ConcurrentDataException"></exception>
        /// <exception cref="System.ComponentModel.DataAnnotations.ValidationException">
        /// </exception>
        public bool DeletePage(DeletePageViewModel model, IPrincipal principal, IMessagesIndicator messages = null)
        {
            var languagesFuture = repository.AsQueryable <Language>().ToFuture();

            var page = repository
                       .AsQueryable <PageProperties>(p => p.Id == model.PageId)
                       .FetchMany(p => p.PageContents)
                       .ThenFetch(pc => pc.Content)
                       .ToFuture()
                       .FirstOne();

            if (model.Version > 0 && page.Version != model.Version)
            {
                throw new ConcurrentDataException(page);
            }

            if (page.IsMasterPage && repository.AsQueryable <MasterPage>(mp => mp.Master == page).Any())
            {
                var message    = PagesGlobalization.DeletePageCommand_MasterPageHasChildren_Message;
                var logMessage = string.Format("Failed to delete page. Page is selected as master page. Id: {0} Url: {1}", page.Id, page.PageUrl);
                throw new ValidationException(() => message, logMessage);
            }

            var isRedirectInternal = false;

            if (!string.IsNullOrWhiteSpace(model.RedirectUrl))
            {
                isRedirectInternal = urlService.ValidateInternalUrl(model.RedirectUrl);
                if (!isRedirectInternal && urlService.ValidateInternalUrl(urlService.FixUrl(model.RedirectUrl)))
                {
                    isRedirectInternal = true;
                }
                if (isRedirectInternal)
                {
                    model.RedirectUrl = urlService.FixUrl(model.RedirectUrl);
                }
            }

            if (model.UpdateSitemap)
            {
                accessControlService.DemandAccess(principal, RootModuleConstants.UserRoles.EditContent);
            }

            var sitemaps     = new Dictionary <Sitemap, bool>();
            var sitemapNodes = sitemapService.GetNodesByPage(page);

            if (model.UpdateSitemap)
            {
                sitemapNodes.Select(node => node.Sitemap)
                .Distinct()
                .ToList()
                .ForEach(
                    sitemap =>
                    sitemaps.Add(
                        sitemap,
                        !cmsConfiguration.Security.AccessControlEnabled || accessControlService.GetAccessLevel(sitemap, principal) == AccessLevel.ReadWrite));

                foreach (var node in sitemapNodes)
                {
                    if (sitemaps[node.Sitemap] && node.ChildNodes.Count > 0)
                    {
                        var logMessage = string.Format("In {0} sitemap node {1} has {2} child nodes.", node.Sitemap.Id, node.Id, node.ChildNodes.Count);
                        throw new ValidationException(() => PagesGlobalization.DeletePageCommand_SitemapNodeHasChildNodes_Message, logMessage);
                    }
                }
            }

            unitOfWork.BeginTransaction();

            // Update sitemap nodes
            IList <SitemapNode> updatedNodes = new List <SitemapNode>();
            IList <SitemapNode> deletedNodes = new List <SitemapNode>();

            UpdateSitemapNodes(model, page, sitemapNodes, sitemaps, languagesFuture.ToList(), updatedNodes, deletedNodes);

            Redirect redirect;

            if (!string.IsNullOrWhiteSpace(model.RedirectUrl))
            {
                if (string.Equals(page.PageUrl, model.RedirectUrl, StringComparison.OrdinalIgnoreCase))
                {
                    var logMessage = string.Format("Circular redirect loop from url {0} to url {0}.", model.RedirectUrl);
                    throw new ValidationException(() => PagesGlobalization.ValidatePageUrlCommand_SameUrlPath_Message, logMessage);
                }

                // Validate url
                if (!urlService.ValidateExternalUrl(model.RedirectUrl))
                {
                    var logMessage = string.Format("Invalid redirect url {0}.", model.RedirectUrl);
                    throw new ValidationException(() => PagesGlobalization.ValidatePageUrlCommand_InvalidUrlPath_Message, logMessage);
                }

                string patternsValidationMessage;
                if (isRedirectInternal &&
                    !urlService.ValidateUrlPatterns(model.RedirectUrl, out patternsValidationMessage, PagesGlobalization.DeletePage_RedirectUrl_Name))
                {
                    var logMessage = string.Format("{0}. URL: {1}.", patternsValidationMessage, model.RedirectUrl);
                    throw new ValidationException(() => patternsValidationMessage, logMessage);
                }

                redirect = redirectService.GetPageRedirect(page.PageUrl);
                if (redirect != null)
                {
                    redirect.RedirectUrl = model.RedirectUrl;
                }
                else
                {
                    redirect = redirectService.CreateRedirectEntity(page.PageUrl, model.RedirectUrl);
                }

                if (redirect != null)
                {
                    repository.Save(redirect);
                }
            }
            else
            {
                redirect = null;
            }

            // Delete child entities.
            if (page.PageTags != null)
            {
                foreach (var pageTag in page.PageTags)
                {
                    repository.Delete(pageTag);
                }
            }

            var deletedPageContents  = new List <PageContent>();
            var htmlContentsToDelete = new List <HtmlContent>();

            if (page.PageContents != null)
            {
                foreach (var pageContent in page.PageContents)
                {
                    // If content is HTML content, delete HTML content
                    var htmlContent = pageContent.Content as HtmlContent;
                    if (htmlContent != null)
                    {
                        var draft = pageContent.Content.History != null?pageContent.Content.History.FirstOrDefault(c => c.Status == ContentStatus.Draft) : null;

                        if (draft != null)
                        {
                            repository.Delete(draft);
                        }

                        repository.Delete(htmlContent);
                        htmlContentsToDelete.Add(htmlContent);
                    }

                    repository.Delete(pageContent);
                    deletedPageContents.Add(pageContent);
                }
            }

            if (page.Options != null)
            {
                foreach (var option in page.Options)
                {
                    repository.Delete(option);
                }
            }

            if (page.AccessRules != null)
            {
                var rules = page.AccessRules.ToList();
                rules.ForEach(page.RemoveRule);
            }

            if (page.MasterPages != null)
            {
                foreach (var master in page.MasterPages)
                {
                    repository.Delete(master);
                }
            }

            // Delete page
            repository.Delete <Root.Models.Page>(page);

            // Commit
            unitOfWork.Commit();

            var updatedSitemaps = new List <Sitemap>();

            foreach (var node in updatedNodes)
            {
                Events.SitemapEvents.Instance.OnSitemapNodeUpdated(node);
                if (!updatedSitemaps.Contains(node.Sitemap))
                {
                    updatedSitemaps.Add(node.Sitemap);
                }
            }

            foreach (var node in deletedNodes)
            {
                Events.SitemapEvents.Instance.OnSitemapNodeDeleted(node);
                if (!updatedSitemaps.Contains(node.Sitemap))
                {
                    updatedSitemaps.Add(node.Sitemap);
                }
            }

            foreach (var updatedSitemap in updatedSitemaps)
            {
                Events.SitemapEvents.Instance.OnSitemapUpdated(updatedSitemap);
            }

            // Notifying about redirect created
            if (redirect != null)
            {
                Events.PageEvents.Instance.OnRedirectCreated(redirect);
            }

            // Notify about deleted page contents
            foreach (var deletedPageContent in deletedPageContents)
            {
                Events.PageEvents.Instance.OnPageContentDeleted(deletedPageContent);
            }

            // Notify about deleted html contents
            foreach (var htmlContent in htmlContentsToDelete)
            {
                Events.PageEvents.Instance.OnHtmlContentDeleted(htmlContent);
            }

            // Notifying, that page is deleted.
            Events.PageEvents.Instance.OnPageDeleted(page);

            if (sitemaps.Any(tuple => !tuple.Value) && messages != null)
            {
                // Some sitemaps where skipped, because user has no permission to edit.
                messages.AddSuccess(PagesGlobalization.DeletePage_SitemapSkipped_Message);
            }

            return(true);
        }
예제 #9
0
        /// <summary>
        /// Deletes the page.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="principal">The principal.</param>
        /// <param name="messages">The messages.</param>
        /// <returns>
        /// Delete result
        /// </returns>
        /// <exception cref="ConcurrentDataException"></exception>
        /// <exception cref="System.ComponentModel.DataAnnotations.ValidationException">
        /// </exception>
        public bool DeletePage(DeletePageViewModel model, IPrincipal principal, IMessagesIndicator messages = null)
        {
            var page = repository.First<PageProperties>(model.PageId);
            if (model.Version > 0 && page.Version != model.Version)
            {
                throw new ConcurrentDataException(page);
            }

            if (page.IsMasterPage && repository.AsQueryable<MasterPage>(mp => mp.Master == page).Any())
            {
                var message = PagesGlobalization.DeletePageCommand_MasterPageHasChildren_Message;
                var logMessage = string.Format("Failed to delete page. Page is selected as master page. Id: {0} Url: {1}", page.Id, page.PageUrl);
                throw new ValidationException(() => message, logMessage);
            }

            var isRedirectInternal = false;
            if (!string.IsNullOrWhiteSpace(model.RedirectUrl))
            {
                isRedirectInternal = urlService.ValidateInternalUrl(model.RedirectUrl);
                if (!isRedirectInternal && urlService.ValidateInternalUrl(urlService.FixUrl(model.RedirectUrl)))
                {
                    isRedirectInternal = true;
                }
                if (isRedirectInternal)
                {
                    model.RedirectUrl = urlService.FixUrl(model.RedirectUrl);
                }
            }

            if (model.UpdateSitemap)
            {
                accessControlService.DemandAccess(principal, RootModuleConstants.UserRoles.EditContent);
            }

            var sitemaps = new Dictionary<Sitemap, bool>();
            var sitemapNodes = sitemapService.GetNodesByPage(page);
            if (model.UpdateSitemap)
            {
                sitemapNodes.Select(node => node.Sitemap)
                            .Distinct()
                            .ToList()
                            .ForEach(
                                sitemap =>
                                sitemaps.Add(
                                    sitemap,
                                    !cmsConfiguration.Security.AccessControlEnabled || accessControlService.GetAccessLevel(sitemap, principal) == AccessLevel.ReadWrite));

                foreach (var node in sitemapNodes)
                {
                    if (sitemaps[node.Sitemap] && node.ChildNodes.Count > 0)
                    {
                        var logMessage = string.Format("In {0} sitemap node {1} has {2} child nodes.", node.Sitemap.Id, node.Id, node.ChildNodes.Count);
                        throw new ValidationException(() => PagesGlobalization.DeletePageCommand_SitemapNodeHasChildNodes_Message, logMessage);
                    }
                }
            }

            unitOfWork.BeginTransaction();

            IList<SitemapNode> updatedNodes = new List<SitemapNode>();
            IList<SitemapNode> deletedNodes = new List<SitemapNode>();
            if (sitemapNodes != null)
            {
                // Archive sitemaps before update.
                sitemaps.Select(pair => pair.Key).ToList().ForEach(sitemap => sitemapService.ArchiveSitemap(sitemap.Id));
                foreach (var node in sitemapNodes)
                {
                    if (!node.IsDeleted)
                    {
                        if (model.UpdateSitemap && sitemaps[node.Sitemap])
                        {
                            // Delete sitemap node.
                            sitemapService.DeleteNode(node, ref deletedNodes);
                        }
                        else
                        {
                            // Unlink sitemap node.
                            if (node.Page != null && node.Page.Id == page.Id)
                            {
                                node.Page = null;
                                node.Title = node.UsePageTitleAsNodeTitle ? page.Title : node.Title;
                                node.Url = page.PageUrl;
                                node.UrlHash = page.PageUrlHash;
                                repository.Save(node);
                                updatedNodes.Add(node);
                            }
                        }
                    }
                }
            }

            Redirect redirect;
            if (!string.IsNullOrWhiteSpace(model.RedirectUrl))
            {
                if (string.Equals(page.PageUrl, model.RedirectUrl, StringComparison.OrdinalIgnoreCase))
                {
                    var logMessage = string.Format("Circular redirect loop from url {0} to url {0}.", model.RedirectUrl);
                    throw new ValidationException(() => PagesGlobalization.ValidatePageUrlCommand_SameUrlPath_Message, logMessage);
                }

                // Validate url
                if (!urlService.ValidateExternalUrl(model.RedirectUrl))
                {
                    var logMessage = string.Format("Invalid redirect url {0}.", model.RedirectUrl);
                    throw new ValidationException(() => PagesGlobalization.ValidatePageUrlCommand_InvalidUrlPath_Message, logMessage);
                }

                string patternsValidationMessage;
                if (isRedirectInternal
                    && !urlService.ValidateUrlPatterns(model.RedirectUrl, out patternsValidationMessage, PagesGlobalization.DeletePage_RedirectUrl_Name))
                {
                    var logMessage = string.Format("{0}. URL: {1}.", patternsValidationMessage, model.RedirectUrl);
                    throw new ValidationException(() => patternsValidationMessage, logMessage);
                }

                redirect = redirectService.GetPageRedirect(page.PageUrl);
                if (redirect != null)
                {
                    redirect.RedirectUrl = model.RedirectUrl;
                }
                else
                {
                    redirect = redirectService.CreateRedirectEntity(page.PageUrl, model.RedirectUrl);
                }

                if (redirect != null)
                {
                    repository.Save(redirect);
                }
            }
            else
            {
                redirect = null;
            }

            // Delete child entities.            
            if (page.PageTags != null)
            {
                foreach (var pageTag in page.PageTags)
                {
                    repository.Delete(pageTag);
                }
            }

            var deletedPageContents = new List<PageContent>();
            if (page.PageContents != null)
            {
                foreach (var pageContent in page.PageContents)
                {
                    repository.Delete(pageContent);
                    deletedPageContents.Add(pageContent);
                }
            }

            if (page.Options != null)
            {
                foreach (var option in page.Options)
                {
                    repository.Delete(option);
                }
            }

            if (page.AccessRules != null)
            {
                var rules = page.AccessRules.ToList();
                rules.ForEach(page.RemoveRule);
            }

            if (page.MasterPages != null)
            {
                foreach (var master in page.MasterPages)
                {
                    repository.Delete(master);
                }
            }

            // Delete page
            repository.Delete<Root.Models.Page>(page);

            // Commit
            unitOfWork.Commit();

            var updatedSitemaps = new List<Sitemap>();
            foreach (var node in updatedNodes)
            {
                Events.SitemapEvents.Instance.OnSitemapNodeUpdated(node);
                if (!updatedSitemaps.Contains(node.Sitemap))
                {
                    updatedSitemaps.Add(node.Sitemap);
                }
            }

            foreach (var node in deletedNodes)
            {
                Events.SitemapEvents.Instance.OnSitemapNodeDeleted(node);
                if (!updatedSitemaps.Contains(node.Sitemap))
                {
                    updatedSitemaps.Add(node.Sitemap);
                }
            }

            foreach (var updatedSitemap in updatedSitemaps)
            {
                Events.SitemapEvents.Instance.OnSitemapUpdated(updatedSitemap);
            }

            // Notifying about redirect created
            if (redirect != null)
            {
                Events.PageEvents.Instance.OnRedirectCreated(redirect);
            }

            // Notify about deleted page contents
            foreach (var deletedPageContent in deletedPageContents)
            {
                Events.PageEvents.Instance.OnPageContentDeleted(deletedPageContent);
            }

            // Notifying, that page is deleted.
            Events.PageEvents.Instance.OnPageDeleted(page);

            if (sitemaps.Any(tuple => !tuple.Value) && messages != null)
            {
                // Some sitemaps where skipped, because user has no permission to edit.
                messages.AddSuccess(PagesGlobalization.DeletePage_SitemapSkipped_Message);
            }

            return true;
        }