Exemple #1
0
 protected override void PrepareForUpdateChildrenMasterPages(bool isNew, BlogPost entity, BlogPostViewModel model, out IList<Guid> newMasterIds,
     out IList<Guid> oldMasterIds, out IList<Guid> childrenPageIds, out IList<MasterPage> existingChildrenMasterPages)
 {
     var modelExt = model as BlogPostViewModelExtender;
     if (!isNew && modelExt != null)
     {
         masterPageService.PrepareForUpdateChildrenMasterPages(entity, modelExt.MasterPageId, 
             out newMasterIds, out oldMasterIds, out childrenPageIds, out existingChildrenMasterPages);
     }
     else
     {
         newMasterIds = null;
         oldMasterIds = null;
         childrenPageIds = null;
         existingChildrenMasterPages = null; 
     }
 }
 protected virtual void PrepareForUpdateChildrenMasterPages(bool isNew, BlogPost entity, BlogPostViewModel model, out IList<Guid> newMasterIds,
     out IList<Guid> oldMasterIds, out IList<Guid> childrenPageIds, out IList<MasterPage> existingChildrenMasterPages)
 {
     newMasterIds = null;
     oldMasterIds = null;
     childrenPageIds = null;
     existingChildrenMasterPages = null;
 }
 protected virtual BlogPostContent SaveContentWithStatusUpdate(bool isNew, BlogPostContent newContent, BlogPostViewModel request, IPrincipal principal)
 {
     return (BlogPostContent)contentService.SaveContentWithStatusUpdate(newContent, request.DesirableStatus);
 }
        protected virtual IList<Tag> SaveTags(BlogPost blogPost, BlogPostViewModel request)
        {
            IList<Tag> newTags;
            tagService.SavePageTags(blogPost, request.Tags, out newTags);

            return newTags;
        }
        /// <summary>
        /// Saves the blog post.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="childContentOptionValues">The child content option values.</param>
        /// <param name="principal">The principal.</param>
        /// <param name="errorMessages">The error messages.</param>
        /// <returns>
        /// Saved blog post entity
        /// </returns>
        /// <exception cref="System.ComponentModel.DataAnnotations.ValidationException"></exception>
        /// <exception cref="SecurityException">Forbidden: Access is denied.</exception>
        public BlogPost SaveBlogPost(BlogPostViewModel request, IList<ContentOptionValuesViewModel> childContentOptionValues, IPrincipal principal, out string[] errorMessages)
        {
            errorMessages = new string[0];
            string[] roles;
            if (request.DesirableStatus == ContentStatus.Published)
            {
                accessControlService.DemandAccess(principal, RootModuleConstants.UserRoles.PublishContent);
                roles = new[] { RootModuleConstants.UserRoles.PublishContent };
            }
            else
            {
                accessControlService.DemandAccess(principal, RootModuleConstants.UserRoles.EditContent);
                roles = new[] { RootModuleConstants.UserRoles.EditContent };
            }

            var isNew = request.Id.HasDefaultValue();
            var userCanEdit = securityService.IsAuthorized(RootModuleConstants.UserRoles.EditContent);

            ValidateData(isNew, request);

            BlogPost blogPost;
            BlogPostContent content;
            PageContent pageContent;
            GetBlogPostAndContentEntities(request, principal, roles, ref isNew, out content, out pageContent, out blogPost);
            var beforeChange = new UpdatingBlogModel(blogPost);

            // Master page / layout
            Layout layout;
            Page masterPage;
            Region region;
            LoadDefaultLayoutAndRegion(out layout, out masterPage, out region);

            if (masterPage != null)
            {
                var level = accessControlService.GetAccessLevel(masterPage, principal);
                if (level < AccessLevel.Read)
                {
                    var message = BlogGlobalization.SaveBlogPost_FailedToSave_InaccessibleMasterPage;
                    const string logMessage = "Failed to save blog post. Selected master page for page layout is inaccessible.";
                    throw new ValidationException(() => message, logMessage);
                }
            }

            if (pageContent.Region == null)
            {
                pageContent.Region = region;
            }

            // Load master pages for updating page's master path and page's children master path
            IList<Guid> newMasterIds;
            IList<Guid> oldMasterIds;
            IList<Guid> childrenPageIds;
            IList<MasterPage> existingChildrenMasterPages;
            PrepareForUpdateChildrenMasterPages(isNew, blogPost, request, out newMasterIds, out oldMasterIds, out childrenPageIds, out existingChildrenMasterPages);

            // TODO: TEST AND TRY TO FIX IT: TRANSACTION HERE IS REQUIRED!
            // UnitOfWork.BeginTransaction(); // NOTE: this causes concurrent data exception.

            Redirect redirectCreated = null;
            if (!isNew && userCanEdit && !string.Equals(blogPost.PageUrl, request.BlogUrl) && !string.IsNullOrWhiteSpace(request.BlogUrl))
            {
                request.BlogUrl = urlService.FixUrl(request.BlogUrl);
                pageService.ValidatePageUrl(request.BlogUrl, request.Id);
                if (request.RedirectFromOldUrl)
                {
                    var redirect = redirectService.CreateRedirectEntity(blogPost.PageUrl, request.BlogUrl);
                    if (redirect != null)
                    {
                        repository.Save(redirect);
                        redirectCreated = redirect;
                    }
                }

                blogPost.PageUrl = urlService.FixUrl(request.BlogUrl);
            }

            // Push to change modified data each time.
            blogPost.ModifiedOn = DateTime.Now;

            if (userCanEdit)
            {
                blogPost.Title = request.Title;
                blogPost.Description = request.IntroText;
                blogPost.Author = request.AuthorId.HasValue ? repository.AsProxy<Author>(request.AuthorId.Value) : null;
                blogPost.Category = request.CategoryId.HasValue ? repository.AsProxy<Category>(request.CategoryId.Value) : null;
                blogPost.Image = (request.Image != null && request.Image.ImageId.HasValue) ? repository.AsProxy<MediaImage>(request.Image.ImageId.Value) : null;
                if (isNew || request.DesirableStatus == ContentStatus.Published)
                {
                    blogPost.ActivationDate = request.LiveFromDate;
                    blogPost.ExpirationDate = TimeHelper.FormatEndDate(request.LiveToDate);
                }
            }

            if (isNew)
            {
                if (!string.IsNullOrWhiteSpace(request.BlogUrl))
                {
                    blogPost.PageUrl = urlService.FixUrl(request.BlogUrl);
                    pageService.ValidatePageUrl(blogPost.PageUrl);
                }
                else
                {
                    blogPost.PageUrl = CreateBlogPermalink(request.Title, null, blogPost.Category != null ? (Guid?)blogPost.Category.Id : null);
                }

                blogPost.MetaTitle = request.MetaTitle ?? request.Title;
                if (masterPage != null)
                {
                    blogPost.MasterPage = masterPage;
                    masterPageService.SetPageMasterPages(blogPost, masterPage.Id);
                }
                else
                {
                    blogPost.Layout = layout;
                }
                UpdateStatus(blogPost, request.DesirableStatus);
                AddDefaultAccessRules(blogPost, principal, masterPage);
            }
            else if (request.DesirableStatus == ContentStatus.Published
                || blogPost.Status == PageStatus.Preview)
            {
                // Update only if publishing or current status is preview.
                // Else do not change, because it may change from published to draft status 
                UpdateStatus(blogPost, request.DesirableStatus);
            }

            // Create content.
            var newContent = new BlogPostContent
            {
                Id = content != null ? content.Id : Guid.Empty,
                Name = request.Title,
                Html = request.Content ?? string.Empty,
                EditInSourceMode = request.EditInSourceMode,
                ActivationDate = request.LiveFromDate,
                ExpirationDate = TimeHelper.FormatEndDate(request.LiveToDate)
            };

            // Preserve content if user is not authorized to change it.
            if (!userCanEdit)
            {
                if (content == null)
                {
                    throw new SecurityException("Forbidden: Access is denied."); // User has no rights to create new content.
                }

                var contentToPublish = (BlogPostContent)(content.History != null
                    ? content.History.FirstOrDefault(c => c.Status == ContentStatus.Draft) ?? content
                    : content);

                newContent.Name = contentToPublish.Name;
                newContent.Html = contentToPublish.Html;
            }

            content = SaveContentWithStatusUpdate(isNew, newContent, request, principal);
            pageContent.Content = content;
            optionService.SaveChildContentOptions(content, childContentOptionValues, request.DesirableStatus);

            blogPost.PageUrlHash = blogPost.PageUrl.UrlHash();
            blogPost.UseCanonicalUrl = request.UseCanonicalUrl;

            MapExtraProperties(isNew, blogPost, content, pageContent, request, principal);

            // Notify about page properties changing.
            var cancelEventArgs = Events.BlogEvents.Instance.OnBlogChanging(beforeChange, new UpdatingBlogModel(blogPost));
            if (cancelEventArgs.Cancel)
            {
                errorMessages = cancelEventArgs.CancellationErrorMessages.ToArray();
                return null;
            }

            repository.Save(blogPost);
            repository.Save(content);
            repository.Save(pageContent);

            masterPageService.UpdateChildrenMasterPages(existingChildrenMasterPages, oldMasterIds, newMasterIds, childrenPageIds);

            pageContent.Content = content;
            blogPost.PageContents = new [] {pageContent};

            

            IList<Tag> newTags = null;
            if (userCanEdit)
            {
                newTags = SaveTags(blogPost, request);
            }

            // Commit
            unitOfWork.Commit();

            // Notify about new created tags.
            Events.RootEvents.Instance.OnTagCreated(newTags);

            // Notify about new or updated blog post.
            if (isNew)
            {
                Events.BlogEvents.Instance.OnBlogCreated(blogPost);
            }
            else
            {
                Events.BlogEvents.Instance.OnBlogUpdated(blogPost);
            }

            // Notify about redirect creation.
            if (redirectCreated != null)
            {
                Events.PageEvents.Instance.OnRedirectCreated(redirectCreated);
            }

            return blogPost;
        }
Exemple #6
0
        protected override void ValidateData(bool isNew, BlogPostViewModel model)
        {
            base.ValidateData(isNew, model);

            var modelExt = model as BlogPostViewModelExtender;
            if (modelExt != null)
            {
                // Validate technical info: if at least one of techincal fields are not null and at least one is null, throw an exception
                if (!model.ContentId.HasDefaultValue() || modelExt.PageContentId.HasValue || modelExt.RegionId.HasValue)
                {
                    if (model.ContentId.HasDefaultValue())
                    {
                        const string message = "Blog post content ID should be specified if technical info is set.";
                        throw new ValidationException(() => message, message);
                    }
                    if (!modelExt.PageContentId.HasValue || modelExt.PageContentId.Value.HasDefaultValue())
                    {
                        const string message = "Page content ID should be specified if technical info is set.";
                        throw new ValidationException(() => message, message);
                    }
                    if (!modelExt.RegionId.HasValue || modelExt.RegionId.Value.HasDefaultValue())
                    {
                        const string message = "Region ID should be specified if technical info is set.";
                        throw new ValidationException(() => message, message);
                    }
                }

                // Validate
                if (!isNew)
                {
                    if (!modelExt.LayoutId.HasValue && !modelExt.MasterPageId.HasValue)
                    {
                        const string message = "Master page id or layout id should be set when updating blog post.";
                        throw new ValidationException(() => message, message);
                    }

                    if (string.IsNullOrWhiteSpace(model.BlogUrl))
                    {
                        const string message = "Blog post URL cannot be null when updating blog post.";
                        throw new ValidationException(() => message, message);
                    }
                }
            }
        }
 private void AssertBlogPostUrl(BlogPostViewModel blog)
 {
     Assert.IsFalse(blog.BlogUrl.Contains(".aspx"));
     Assert.IsFalse(blog.BlogUrl.Contains(".asp"));
     Assert.IsFalse(blog.BlogUrl.Contains(".html"));
     Assert.IsFalse(blog.BlogUrl.Contains(".htm"));
     Assert.IsFalse(blog.BlogUrl.Contains(".php"));
 }
 protected virtual void ValidateData(bool isNew, BlogPostViewModel model)
 {
     // Do nothing
 }
        private BlogPostViewModel MapViewModel(BlogMLPost blogML, bool useOriginalUrls, BlogPostImportResult modification = null, List<string> unsavedUrls = null)
        {
            var model = new BlogPostViewModel
                    {
                        Title = blogML.PostName ?? blogML.Title,
                        IntroText = blogML.Excerpt != null ? blogML.Excerpt.UncodedText : null,
                        LiveFromDate = blogML.DateCreated.Date,
                        LiveToDate = null,
                        DesirableStatus = ContentStatus.Published,
                        Content = blogML.Content != null ? blogML.Content.UncodedText : null
                    };

            if (modification != null)
            {
                model.BlogUrl = modification.PageUrl;
                model.Title = modification.Title;
            }
            else if (useOriginalUrls)
            {
                model.BlogUrl = blogService.CreateBlogPermalink(FixUrl(blogML.PostUrl), unsavedUrls);
            }
            else
            {
                model.BlogUrl = blogService.CreateBlogPermalink(blogML.Title, unsavedUrls);
            }

            return model;
        }
Exemple #10
0
        protected override void GetBlogPostAndContentEntities(BlogPostViewModel model, IPrincipal principal, string[] roles, 
            ref bool isNew, out BlogPostContent content, out PageContent pageContent , out BlogPost blogPost)
        {
            var modelExt = model as BlogPostViewModelExtender;
            if (!isNew && modelExt != null)
            {
                content = null;
                pageContent = null;

                blogPost = repository
                    .AsQueryable<BlogPost>(bp => bp.Id == model.Id)
                    .FetchMany(bp => bp.AccessRules)
                    .FetchMany(bp => bp.PageTags)
                    .ThenFetch(pt => pt.Tag)
                    .ToList()
                    .FirstOrDefault();

                if (blogPost != null)
                {
                    if (configuration.Security.AccessControlEnabled)
                    {
                        accessControlService.DemandAccess(blogPost, principal, AccessLevel.ReadWrite, roles);
                    }

                    if (modelExt.PageContentId.HasValue)
                    {
                        content = repository
                            .AsQueryable<BlogPostContent>(c => c.PageContents.Any(x => x.Page.Id == model.Id
                                && !x.IsDeleted && x.Id == modelExt.PageContentId.Value
                                && !x.IsDeleted && c.Id == model.ContentId))
                            .ToFuture()
                            .FirstOrDefault();

                        if (content == null)
                        {
                            const string message = "Cannot find a blog post content by specified blog post content and Id and page content Id.";
                            var logMessage = string.Format("{0} BlogId: {1}, BlogPostContentId: {2}, PageContentId: {3}", 
                                message, model.Id, model.ContentId, modelExt.PageContentId);
                            throw new ValidationException(() => message, logMessage);
                        }

                        pageContent = repository.First<PageContent>(pc => pc.Id == modelExt.PageContentId.Value);
                    }
                    else
                    {
                        content = repository
                            .AsQueryable<BlogPostContent>(c => c.PageContents.Any(x => x.Page.Id == model.Id && !x.IsDeleted) && !c.IsDeleted)
                            .ToFuture()
                            .FirstOrDefault();

                        if (content != null)
                        {
                            var contentRef = content;
                            pageContent = repository.FirstOrDefault<PageContent>(c => c.Page.Id == model.Id && !c.IsDeleted && c.Content == contentRef);
                        }
                    }
                }

                isNew = blogPost == null;
                if (isNew)
                {
                    blogPost = new BlogPost();
                    pageContent = new PageContent { Page = blogPost };
                }
            }
            else
            {
                base.GetBlogPostAndContentEntities(model, principal, roles, ref isNew, out content, out pageContent, out blogPost);
            }
        }
Exemple #11
0
        protected override IList<Tag> SaveTags(BlogPost blogPost, BlogPostViewModel request)
        {
            if (request.Tags != null)
            {
                return base.SaveTags(blogPost, request);
            }

            return null;
        }
Exemple #12
0
        protected override void MapExtraProperties(bool isNew, BlogPost entity, BlogPostContent content, PageContent pageContent, BlogPostViewModel model, IPrincipal principal)
        {
            var currentVersion = entity.Version;
            base.MapExtraProperties(isNew, entity, content, pageContent, model, principal);

            var modelExt = model as BlogPostViewModelExtender;
            if (modelExt != null)
            {
                // Restore version if not set from the extended model
                if (model.Version <= 0)
                {
                    entity.Version = currentVersion;
                }

                entity.SecondaryImage = modelExt.SecondaryImageId.HasValue ? repository.AsProxy<MediaImage>(modelExt.SecondaryImageId.Value) : null;
                entity.FeaturedImage = modelExt.FeaturedImageId.HasValue ? repository.AsProxy<MediaImage>(modelExt.FeaturedImageId.Value) : null;
                entity.IsArchived = modelExt.IsArchived;
                entity.UseNoFollow = modelExt.UseNoFollow;
                entity.UseNoIndex = modelExt.UseNoIndex;
                entity.MetaKeywords = modelExt.MetaKeywords;
                entity.MetaDescription = modelExt.MetaDescription;

                // If creating new and content / page content / region ids are set, enforce them to be set explicitly
                if (isNew && !model.ContentId.HasDefaultValue() && modelExt.PageContentId.HasValue && modelExt.RegionId.HasValue)
                {
                    pageContent.Id = modelExt.PageContentId.Value;
                    pageContent.Region = repository.AsProxy<Region>(modelExt.RegionId.Value);
                }

                // Set blog post Id, if it's set
                if (isNew && !model.Id.HasDefaultValue())
                {
                    entity.Id = model.Id;
                }

                // PublishedOn
                if (isNew && entity.Status == PageStatus.Published && modelExt.PublishedOn.HasValue)
                {
                    entity.PublishedOn = modelExt.PublishedOn.Value;
                }

                // Set layout / master page
                if (modelExt.MasterPageId.HasValue)
                {
                    entity.Layout = null;
                    if (isNew)
                    {
                        entity.MasterPage = repository
                            .AsQueryable<Page>(p => p.Id == modelExt.MasterPageId.Value)
                            .FetchMany(p => p.AccessRules)
                            .ToList()
                            .FirstOne();

                        if (modelExt.AccessRules == null)
                        {
                            AddDefaultAccessRules(entity, principal, entity.MasterPage);
                        }
                        masterPageService.SetPageMasterPages(entity, entity.MasterPage.Id);
                    }
                    else
                    {
                        entity.MasterPage = repository.AsProxy<Page>(modelExt.MasterPageId.Value);
                    }
                }
                else if (modelExt.LayoutId.HasValue)
                {
                    entity.Layout = repository.AsProxy<Layout>(modelExt.LayoutId.Value);
                    entity.MasterPage = null;
                    if (isNew && modelExt.AccessRules == null)
                    {
                        AddDefaultAccessRules(entity, principal, null);
                    }
                }

                // Add access rules from the request
                if (modelExt.AccessRules != null)
                {
                    if (entity.AccessRules == null)
                    {
                        entity.AccessRules = new List<AccessRule>();
                    }
                    else
                    {
                        entity.AccessRules.RemoveDuplicateEntities();
                    }
                    var accessRules = modelExt.AccessRules
                        .Select(r => (IAccessRule)new AccessRule
                        {
                            AccessLevel = (AccessLevel)(int)r.AccessLevel,
                            Identity = r.Identity,
                            IsForRole = r.IsForRole
                        }).ToList();

                    accessControlService.UpdateAccessControl(entity, accessRules);
                }
            }
        }
Exemple #13
0
        protected override BlogPostContent SaveContentWithStatusUpdate(bool isNew, BlogPostContent newContent, BlogPostViewModel model, IPrincipal principal)
        {
            var modelExt = model as BlogPostViewModelExtender;
            if (isNew && modelExt != null && !modelExt.ContentId.HasDefaultValue())
            {
                if (model.DesirableStatus == ContentStatus.Published)
                {
                    newContent.PublishedOn = modelExt.PublishedOn ?? DateTime.Now;
                    newContent.PublishedByUser = principal.Identity.Name;
                    // TODO: pass published by user newContent.PublishedByUser = !string.IsNullOrEmpty(request.Data.PublishedByUser) ? request.Data.PublishedByUser : securityService.CurrentPrincipalName;
                }

                newContent.Status = model.DesirableStatus;
                newContent.Id = modelExt.ContentId;
                repository.Save(newContent);

                return newContent;
            }

            return base.SaveContentWithStatusUpdate(isNew, newContent, model, principal);
        }
        protected virtual void GetBlogPostAndContentEntities(BlogPostViewModel request, IPrincipal principal, string[] roles, 
            ref bool isNew, out BlogPostContent content, out PageContent pageContent , out BlogPost blogPost)
        {
            content = null;
            pageContent = null;

            // Loading blog post and it's content, or creating new, if such not exists
            if (!isNew)
            {
                var blogPostFuture = repository
                    .AsQueryable<BlogPost>(b => b.Id == request.Id)
                    .ToFuture();

                content = repository
                    .AsQueryable<BlogPostContent>(c => c.PageContents.Any(x => x.Page.Id == request.Id && !x.IsDeleted))
                    .ToFuture()
                    .FirstOrDefault();

                blogPost = blogPostFuture.FirstOne();

                if (configuration.Security.AccessControlEnabled)
                {
                    accessControlService.DemandAccess(blogPost, principal, AccessLevel.ReadWrite, roles);
                }

                if (content != null)
                {
                    // Check if user has confirmed the deletion of content
                    if (!request.IsUserConfirmed && blogPost.IsMasterPage)
                    {
                        var hasAnyChildren = contentService.CheckIfContentHasDeletingChildren(blogPost.Id, content.Id, request.Content);
                        if (hasAnyChildren)
                        {
                            var message = PagesGlobalization.SaveContent_ContentHasChildrenContents_RegionDeleteConfirmationMessage;
                            var logMessage = string.Format("User is trying to delete content regions which has children contents. Confirmation is required. ContentId: {0}, PageId: {1}",
                                    content.Id, blogPost.Id);
                            throw new ConfirmationRequestException(() => message, logMessage);
                        }
                    }

                    var bpRef = blogPost;
                    var contentRef = content;
                    pageContent = repository.FirstOrDefault<PageContent>(c => c.Page == bpRef && !c.IsDeleted && c.Content == contentRef);
                }
            }
            else
            {
                blogPost = new BlogPost();
            }

            if (pageContent == null)
            {
                pageContent = new PageContent { Page = blogPost };
            }
        }
        private bool ValidateModel(BlogPostViewModel blogPostModel, BlogMLPost blogML, out BlogPostImportResult failedResult)
        {
            failedResult = null;

            if (string.IsNullOrWhiteSpace(blogML.ID))
            {
                failedResult = new BlogPostImportResult
                {
                    Title = blogML.Title,
                    PageUrl = blogML.PostUrl,
                    Success = false,
                    ErrorMessage = BlogGlobalization.ImportBlogPosts_ImportingBlogPostIdIsNotSet_Message,
                    Id = blogML.ID
                };
                return false;
            }

            var validationContext = new ValidationContext(blogPostModel, null, null);
            var validationResults = new List<ValidationResult>();
            if (!Validator.TryValidateObject(blogPostModel, validationContext, validationResults, true)
                && validationResults.Count > 0)
            {
                failedResult = new BlogPostImportResult
                    {
                        Title = blogML.Title,
                        PageUrl = blogML.PostUrl,
                        Success = false,
                        ErrorMessage = validationResults[0].ErrorMessage,
                        Id = blogML.ID
                    };
                return false;
            }

            try
            {
                pageService.ValidatePageUrl(blogPostModel.BlogUrl);
            }
            catch (Exception exc)
            {
                failedResult = new BlogPostImportResult
                {
                    Title = blogML.Title,
                    PageUrl = blogML.PostUrl,
                    Success = false,
                    ErrorMessage = exc.Message,
                    Id = blogML.ID
                };
                return false;
            }

            return true;
        }
 protected virtual void MapExtraProperties(bool isNew, BlogPost entity, BlogPostContent content, PageContent pageContent, BlogPostViewModel model, IPrincipal principal)
 {
     entity.Version = model.Version;
 }
Exemple #17
0
        public ActionResult SaveBlogPost(BlogPostViewModel model)
        {
            var response = GetCommand<SaveBlogPostCommand>().ExecuteCommand(model);
            if (response != null)
            {
                if (model.DesirableStatus != ContentStatus.Preview && model.Id.HasDefaultValue())
                {
                    Messages.AddSuccess(BlogGlobalization.CreatePost_CreatedSuccessfully_Message);
                }
            }

            return WireJson(response != null, response);
        }
 private void AssertBlogPost(BlogPostViewModel blog)
 {
     Assert.AreEqual(blog.LiveFromDate, new DateTime(2006, 09, 05));
     Assert.AreEqual(blog.Title, "CS Dev Guide: Send Emails");
     Assert.AreEqual(blog.IntroText, "CS Dev Guide: Send Emails - Intro Text");
     Assert.AreEqual(blog.DesirableStatus, ContentStatus.Published);
     Assert.AreEqual(blog.Content, "<p>Any web application needs a way to send emails to different kinds of its users.&nbsp; This capability is provided in Community Server from early versions.&nbsp; Sending emails is one of easiest parts of Community Server development.</p><p>To send emails in Community Serve you have two options:&nbsp;using pre-defined email templates for some common situations&nbsp;or create an email manually.</p><p>First option is easy to implement.&nbsp; Community Server has provided several default templates for your emails and you can add your own templates via resource files as well.&nbsp; <em>CommunityServer.Components.Emails</em> namespace has many methods that get some parameters&nbsp;then create and send an email based on the template they have.</p><p>For example you can send a notification email to user to let him know his account is created.</p><div style=\"background: white none repeat scroll 0% 50%; font-size: 10pt; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: black; font-family: courier new\"><p style=\"margin: 0px\"><span style=\"color: blue\">private</span> <span style=\"color: blue\">void</span> SendEmail(<span style=\"color: blue\">string</span> username, <span style=\"color: blue\">string</span> password</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; , <span style=\"color: blue\">string</span> email)</p><p style=\"margin: 0px\">{</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; <span style=\"color: teal\">User</span> user = <span style=\"color: blue\">new</span> <span style=\"color: teal\">User</span>();</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; user.Username = username;</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; user.Password = password;</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; user.Email = email;</p><p style=\"margin: 0px\">&nbsp;</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; <span style=\"color: teal\">Emails</span>.UserCreate(user, password);</p><p style=\"margin: 0px\">}</p></div><p>These methods add emails to queue automatically.&nbsp; You can use <em>Emails.SendQueuedEmails()</em> method to send all queued emails to recipients.&nbsp; This method takes three parameters: an integer value for failure interval, an integer value that specifies the maximum number of tries of sending process&nbsp;failed and a <em>SiteSettings</em> object.</p><div style=\"background: white none repeat scroll 0% 50%; font-size: 10pt; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: black; font-family: courier new\"><p style=\"margin: 0px\"><span style=\"color: blue\">private</span> <span style=\"color: blue\">void</span> SendQueuedEmails()</p><p style=\"margin: 0px\">{</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; <span style=\"color: teal\">Emails</span>.SendQueuedEmails(5, 5,</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <span style=\"color: teal\">CSContext</span>.Current.SiteSettings);</p><p style=\"margin: 0px\">}</p></div><p>Second option to send emails is&nbsp;manual option.&nbsp; This is&nbsp;useful when you want to send an email but it doesn&#39;t fit to Community Server pre-defined templates.</p><p>This option is very similar to sending emails in ASP.NET 1.x.&nbsp; You need to create an instance of System.Web.Mail.MailMessage object, set its appropriate properties and add it to emails queue using Community Server APIs.&nbsp; You know MailMessage object is obsolete in ASP.NET 2.0 but Community Server accepts old objects to be able to work under ASP.NET 1.1.&nbsp; If you need more information about sending emails in ASP.NET 2.0, read my post about my SMTP component, Gopi, from <a href=\"http://nayyeri.net/archive/2006/05/20/Gopi-_2D00_-SMTP-component-for-.NET-2.0.asp\">here</a>.</p><p>Here is a sample of how to create a new MailMessage and add it to emails queue.</p><div style=\"background: white none repeat scroll 0% 50%; font-size: 10pt; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: black; font-family: courier new\"><p style=\"margin: 0px\"><span style=\"color: blue\">private</span> <span style=\"color: blue\">void</span> SendEmailManually()</p><p style=\"margin: 0px\">{</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; <span style=\"color: teal\">MailMessage</span> mail = <span style=\"color: blue\">new</span> <span style=\"color: teal\">MailMessage</span>();</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; mail.From = <span style=\"color: maroon\">&quot;[email protected]&quot;</span>;</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; mail.To = <span style=\"color: maroon\">&quot;[email protected]&quot;</span>;</p><p style=\"margin: 0px\">&nbsp;</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; mail.Priority = <span style=\"color: teal\">MailPriority</span>.Normal;</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; mail.BodyFormat = <span style=\"color: teal\">MailFormat</span>.Text;</p><p style=\"margin: 0px\">&nbsp;</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; mail.Body = <span style=\"color: maroon\">&quot;Long Live Community Server!&quot;</span>;</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; mail.Subject = <span style=\"color: maroon\">&quot;Test Email&quot;</span>;</p><p style=\"margin: 0px\">&nbsp;</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; <span style=\"color: teal\">EmailQueueProvider</span>.Instance().QueueEmail(mail);</p><p style=\"margin: 0px\">}</p></div><p>You saw I used <em>EmailQueueProvider.Instance().QueueEmail()</em> method to add my email to queue.&nbsp; You can use <em>EmailQueueProvider.Instance()</em> object to deal with emails queue in Community Server.&nbsp; For instance you can remove an email from emails queue by passing its Guid to <em>EmailQueueProvider.Instance().DeleteQueuedEmail()</em> method.</p><div style=\"background: white none repeat scroll 0% 50%; font-size: 10pt; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: black; font-family: courier new\"><p style=\"margin: 0px\"><span style=\"color: blue\">private</span> <span style=\"color: blue\">void</span> DealWithEmails()</p><p style=\"margin: 0px\">{</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; <span style=\"color: teal\">EmailQueueProvider</span>.Instance()</p><p style=\"margin: 0px\">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; .DeleteQueuedEmail(<span style=\"color: teal\">Guid</span>.NewGuid());&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; </p><p style=\"margin: 0px\">}</p></div><p><strong>Now playing: </strong>Modern Talking - You are not alone</p>");
     Assert.AreEqual(blog.AuthorId, new Guid("7D601C36-7130-4031-95BC-578C34328143"));
        // Assert.AreEqual(blog.CategoryId, new Guid("B81ACA8C-93CD-48C0-8B7F-F9ADE3D1BAC8"));
 }
        protected virtual void GetBlogPostAndContentEntities(BlogPostViewModel request, IPrincipal principal, string[] roles, 
            ref bool isNew, out BlogPostContent content, out PageContent pageContent , out BlogPost blogPost)
        {
            content = null;
            pageContent = null;

            // Loading blog post and it's content, or creating new, if such not exists
            if (!isNew)
            {
                var blogPostFuture = repository
                    .AsQueryable<BlogPost>(b => b.Id == request.Id)
                    .ToFuture();

                content = repository
                    .AsQueryable<BlogPostContent>(c => c.PageContents.Any(x => x.Page.Id == request.Id && !x.IsDeleted))
                    .ToFuture()
                    .FirstOrDefault();

                blogPost = blogPostFuture.FirstOne();

                if (configuration.Security.AccessControlEnabled)
                {
                    accessControlService.DemandAccess(blogPost, principal, AccessLevel.ReadWrite, roles);
                }

                if (content != null)
                {
                    // Check if user has confirmed the deletion of content
                    if (!request.IsUserConfirmed && blogPost.IsMasterPage)
                    {
                        contentService.CheckIfContentHasDeletingChildrenWithException(blogPost.Id, content.Id, request.Content);
                    }

                    var bpRef = blogPost;
                    var contentRef = content;
                    pageContent = repository.FirstOrDefault<PageContent>(c => c.Page == bpRef && !c.IsDeleted && c.Content == contentRef);
                }
            }
            else
            {
                blogPost = new BlogPost();
            }

            if (pageContent == null)
            {
                pageContent = new PageContent { Page = blogPost };
            }
        }
        /// <summary>
        /// Saves the blog post.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <param name="principal">The principal.</param>
        /// <returns>
        /// Saved blog post entity
        /// </returns>
        public BlogPost SaveBlogPost(BlogPostViewModel request, IPrincipal principal)
        {
            string[] roles;
            if (request.DesirableStatus == ContentStatus.Published)
            {
                accessControlService.DemandAccess(principal, RootModuleConstants.UserRoles.PublishContent);
                roles = new[] { RootModuleConstants.UserRoles.PublishContent };
            }
            else
            {
                accessControlService.DemandAccess(principal, RootModuleConstants.UserRoles.EditContent);
                roles = new[] { RootModuleConstants.UserRoles.EditContent };
            }

            Layout layout;
            Page masterPage;
            LoadLayout(out layout, out masterPage);

            if (masterPage != null)
            {
                var level = accessControlService.GetAccessLevel(masterPage, principal);
                if (level < AccessLevel.Read)
                {
                    var message = BlogGlobalization.SaveBlogPost_FailedToSave_InaccessibleMasterPage;
                    const string logMessage = "Failed to save blog post. Selected master page for page layout is inaccessible.";
                    throw new ValidationException(() => message, logMessage);
                }
            }

            var region = LoadRegion(layout, masterPage);
            var isNew = request.Id.HasDefaultValue();
            var userCanEdit = securityService.IsAuthorized(RootModuleConstants.UserRoles.EditContent);

            // UnitOfWork.BeginTransaction(); // NOTE: this causes concurrent data exception.

            BlogPost blogPost;
            BlogPostContent content = null;
            PageContent pageContent = null;
            Pages.Models.Redirect redirectCreated = null;

            // Loading blog post and it's content, or creating new, if such not exists
            if (!isNew)
            {
                var blogPostFuture = repository
                    .AsQueryable<BlogPost>(b => b.Id == request.Id)
                    .ToFuture();

                content = repository
                    .AsQueryable<BlogPostContent>(c => c.PageContents.Any(x => x.Page.Id == request.Id && !x.IsDeleted))
                    .ToFuture()
                    .FirstOrDefault();

                blogPost = blogPostFuture.FirstOne();

                if (cmsConfiguration.Security.AccessControlEnabled)
                {
                    accessControlService.DemandAccess(blogPost, principal, AccessLevel.ReadWrite, roles);
                }

                if (content != null)
                {
                    // Check if user has confirmed the deletion of content
                    if (!request.IsUserConfirmed && blogPost.IsMasterPage)
                    {
                        var hasAnyChildren = contentService.CheckIfContentHasDeletingChildren(blogPost.Id, content.Id, request.Content);
                        if (hasAnyChildren)
                        {
                            var message = PagesGlobalization.SaveContent_ContentHasChildrenContents_RegionDeleteConfirmationMessage;
                            var logMessage = string.Format("User is trying to delete content regions which has children contents. Confirmation is required. ContentId: {0}, PageId: {1}",
                                    content.Id, blogPost.Id);
                            throw new ConfirmationRequestException(() => message, logMessage);
                        }
                    }

                    pageContent = repository.FirstOrDefault<PageContent>(c => c.Page == blogPost && !c.IsDeleted && c.Content == content);
                }

                if (userCanEdit && !string.Equals(blogPost.PageUrl, request.BlogUrl) && request.BlogUrl != null)
                {
                    request.BlogUrl = urlService.FixUrl(request.BlogUrl);
                    pageService.ValidatePageUrl(request.BlogUrl, request.Id);
                    if (request.RedirectFromOldUrl)
                    {
                        var redirect = redirectService.CreateRedirectEntity(blogPost.PageUrl, request.BlogUrl);
                        if (redirect != null)
                        {
                            repository.Save(redirect);
                            redirectCreated = redirect;
                        }
                    }

                    blogPost.PageUrl = urlService.FixUrl(request.BlogUrl);
                }
            }
            else
            {
                blogPost = new BlogPost();
            }

            if (pageContent == null)
            {
                pageContent = new PageContent { Region = region, Page = blogPost };
            }

            // Push to change modified data each time.
            blogPost.ModifiedOn = DateTime.Now;
            blogPost.Version = request.Version;

            if (userCanEdit)
            {
                blogPost.Title = request.Title;
                blogPost.Description = request.IntroText;
                blogPost.Author = request.AuthorId.HasValue ? repository.AsProxy<Author>(request.AuthorId.Value) : null;
                blogPost.Category = request.CategoryId.HasValue ? repository.AsProxy<Category>(request.CategoryId.Value) : null;
                blogPost.Image = (request.Image != null && request.Image.ImageId.HasValue) ? repository.AsProxy<MediaImage>(request.Image.ImageId.Value) : null;
                if (isNew || request.DesirableStatus == ContentStatus.Published)
                {
                    blogPost.ActivationDate = request.LiveFromDate;
                    blogPost.ExpirationDate = TimeHelper.FormatEndDate(request.LiveToDate);
                }
            }

            if (isNew)
            {
                if (!string.IsNullOrWhiteSpace(request.BlogUrl))
                {
                    blogPost.PageUrl = urlService.FixUrl(request.BlogUrl);
                    pageService.ValidatePageUrl(blogPost.PageUrl);
                }
                else
                {
                    blogPost.PageUrl = CreateBlogPermalink(request.Title);
                }

                blogPost.MetaTitle = request.MetaTitle ?? request.Title;
                if (masterPage != null)
                {
                    blogPost.MasterPage = masterPage;
                    masterPageService.SetPageMasterPages(blogPost, masterPage.Id);
                }
                else
                {
                    blogPost.Layout = layout;
                }
                UpdateStatus(blogPost, request.DesirableStatus);
                AddDefaultAccessRules(blogPost, principal, masterPage);
            }
            else if (request.DesirableStatus == ContentStatus.Published
                || blogPost.Status == PageStatus.Preview)
            {
                // Update only if publishing or current status is preview.
                // Else do not change, because it may change from published to draft status 
                UpdateStatus(blogPost, request.DesirableStatus);
            }

            // Create content.
            var newContent = new BlogPostContent
            {
                Id = content != null ? content.Id : Guid.Empty,
                Name = request.Title,
                Html = request.Content ?? string.Empty,
                EditInSourceMode = request.EditInSourceMode,
                ActivationDate = request.LiveFromDate,
                ExpirationDate = TimeHelper.FormatEndDate(request.LiveToDate)
            };

            // Preserve content if user is not authorized to change it.
            if (!userCanEdit)
            {
                if (content == null)
                {
                    throw new SecurityException("Forbidden: Access is denied."); // User has no rights to create new content.
                }

                var contentToPublish = (BlogPostContent)(content.History != null
                    ? content.History.FirstOrDefault(c => c.Status == ContentStatus.Draft) ?? content
                    : content);

                newContent.Name = contentToPublish.Name;
                newContent.Html = contentToPublish.Html;
            }

            content = (BlogPostContent)contentService.SaveContentWithStatusUpdate(newContent, request.DesirableStatus);
            pageContent.Content = content;

            blogPost.PageUrlHash = blogPost.PageUrl.UrlHash();
            blogPost.UseCanonicalUrl = request.UseCanonicalUrl;

            repository.Save(blogPost);
            repository.Save(content);
            repository.Save(pageContent);

            pageContent.Content = content;
            blogPost.PageContents = new [] {pageContent};

            IList<Tag> newTags = null;
            if (userCanEdit)
            {
                tagService.SavePageTags(blogPost, request.Tags, out newTags);
            }

            // Commit
            unitOfWork.Commit();

            // Notify about new or updated blog post.
            if (isNew)
            {
                Events.BlogEvents.Instance.OnBlogCreated(blogPost);
            }
            else
            {
                Events.BlogEvents.Instance.OnBlogUpdated(blogPost);

            }

            // Notify about new created tags.
            Events.RootEvents.Instance.OnTagCreated(newTags);

            // Notify about redirect creation.
            if (redirectCreated != null)
            {
                Events.PageEvents.Instance.OnRedirectCreated(redirectCreated);
            }

            return blogPost;
        }
        public ActionResult SaveBlogPost(BlogPostViewModel model)
        {
            try
            {
                var response = GetCommand<SaveBlogPostCommand>().ExecuteCommand(model);
                if (response != null)
                {
                    if (model.DesirableStatus != ContentStatus.Preview && model.Id.HasDefaultValue())
                    {
                        Messages.AddSuccess(BlogGlobalization.CreatePost_CreatedSuccessfully_Message);
                    }
                }

                return WireJson(response != null, response);
            }
            catch (ConfirmationRequestException exc)
            {
                return WireJson(false, new { ConfirmationMessage = exc.Resource() });
            }
        }