/// <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); }
/// <summary> /// Executes the specified request. /// </summary> /// <param name="request">The request.</param> /// <returns></returns> /// <exception cref="ConcurrentDataException"></exception> /// <exception cref="System.ComponentModel.DataAnnotations.ValidationException"> /// </exception> public virtual bool Execute(DeletePageViewModel request) { var page = Repository.First <PageProperties>(request.PageId); if (page.Version != request.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); } request.RedirectUrl = urlService.FixUrl(request.RedirectUrl); if (request.UpdateSitemap) { AccessControlService.DemandAccess(Context.Principal, RootModuleConstants.UserRoles.EditContent); } var sitemaps = new Dictionary <Models.Sitemap, bool>(); var sitemapNodes = sitemapService.GetNodesByPage(page); if (request.UpdateSitemap) { sitemapNodes.Select(node => node.Sitemap) .Distinct() .ToList() .ForEach( sitemap => sitemaps.Add( sitemap, !cmsConfiguration.Security.AccessControlEnabled || AccessControlService.GetAccessLevel(sitemap, Context.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 (request.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.Url = page.PageUrl; node.UrlHash = page.PageUrlHash; Repository.Save(node); updatedNodes.Add(node); } } } } } if (!string.IsNullOrWhiteSpace(request.RedirectUrl)) { if (string.Equals(page.PageUrl, request.RedirectUrl, StringComparison.OrdinalIgnoreCase)) { var logMessage = string.Format("Circular redirect loop from url {0} to url {0}.", request.RedirectUrl); throw new ValidationException(() => PagesGlobalization.ValidatePageUrlCommand_SameUrlPath_Message, logMessage); } // Validate url if (!urlService.ValidateUrl(request.RedirectUrl)) { var logMessage = string.Format("Invalid redirect url {0}.", request.RedirectUrl); throw new ValidationException(() => PagesGlobalization.ValidatePageUrlCommand_InvalidUrlPath_Message, logMessage); } string patternsValidationMessage; if (!urlService.ValidateUrlPatterns(request.RedirectUrl, out patternsValidationMessage, PagesGlobalization.DeletePage_RedirectUrl_Name)) { var logMessage = string.Format("{0}. URL: {1}.", patternsValidationMessage, request.RedirectUrl); throw new ValidationException(() => patternsValidationMessage, logMessage); } var redirect = redirectService.GetPageRedirect(page.PageUrl); if (redirect != null) { redirect.RedirectUrl = request.RedirectUrl; } else { redirect = redirectService.CreateRedirectEntity(page.PageUrl, request.RedirectUrl); } if (redirect != null) { Repository.Save(redirect); } } // Delete child entities. if (page.PageTags != null) { foreach (var pageTag in page.PageTags) { Repository.Delete(pageTag); } } if (page.PageContents != null) { foreach (var pageContent in page.PageContents) { Repository.Delete(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>(request.PageId, request.Version); // Commit UnitOfWork.Commit(); var updatedSitemaps = new List <Models.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, that page is deleted. Events.PageEvents.Instance.OnPageDeleted(page); if (sitemaps.Any(tuple => !tuple.Value)) { // Some sitemaps where skipped, because user has no permission to edit. Context.Messages.AddSuccess(PagesGlobalization.DeletePage_SitemapSkipped_Message); } return(true); }