using Nop.Core; using Nop.Core.Caching; using Nop.Core.Domain.Blogs; using Nop.Data; using Nop.Services.Stores; namespace Nop.Services.Blogs; /// /// Blog service /// public partial class BlogService : IBlogService { #region Fields protected readonly IRepository _blogCommentRepository; protected readonly IRepository _blogPostRepository; protected readonly IStaticCacheManager _staticCacheManager; protected readonly IStoreMappingService _storeMappingService; private static readonly char[] _separator = [',']; #endregion #region Ctor public BlogService( IRepository blogCommentRepository, IRepository blogPostRepository, IStaticCacheManager staticCacheManager, IStoreMappingService storeMappingService) { _blogCommentRepository = blogCommentRepository; _blogPostRepository = blogPostRepository; _staticCacheManager = staticCacheManager; _storeMappingService = storeMappingService; } #endregion #region Methods #region Blog posts /// /// Deletes a blog post /// /// Blog post /// A task that represents the asynchronous operation public virtual async Task DeleteBlogPostAsync(BlogPost blogPost) { await _blogPostRepository.DeleteAsync(blogPost); } /// /// Gets a blog post /// /// Blog post identifier /// /// A task that represents the asynchronous operation /// The task result contains the blog post /// public virtual async Task GetBlogPostByIdAsync(int blogPostId) { return await _blogPostRepository.GetByIdAsync(blogPostId, cache => default, useShortTermCache: true); } /// /// Gets all blog posts /// /// The store identifier; pass 0 to load all records /// Language identifier; 0 if you want to get all records /// Filter by created date; null if you want to get all records /// Filter by created date; null if you want to get all records /// Page index /// Page size /// A value indicating whether to show hidden records /// Filter by blog post title /// /// A task that represents the asynchronous operation /// The task result contains the blog posts /// public virtual async Task> GetAllBlogPostsAsync(int storeId = 0, int languageId = 0, DateTime? dateFrom = null, DateTime? dateTo = null, int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false, string title = null) { return await _blogPostRepository.GetAllPagedAsync(async query => { if (dateFrom.HasValue) query = query.Where(b => dateFrom.Value <= (b.StartDateUtc ?? b.CreatedOnUtc)); if (dateTo.HasValue) query = query.Where(b => dateTo.Value >= (b.StartDateUtc ?? b.CreatedOnUtc)); if (languageId > 0) query = query.Where(b => languageId == b.LanguageId); if (!string.IsNullOrEmpty(title)) query = query.Where(b => b.Title.Contains(title)); if (!showHidden || storeId > 0) { //apply store mapping constraints query = await _storeMappingService.ApplyStoreMapping(query, storeId); } if (!showHidden) { query = query.Where(b => !b.StartDateUtc.HasValue || b.StartDateUtc <= DateTime.UtcNow); query = query.Where(b => !b.EndDateUtc.HasValue || b.EndDateUtc >= DateTime.UtcNow); } query = query.OrderByDescending(b => b.StartDateUtc ?? b.CreatedOnUtc); return query; }, pageIndex, pageSize); } /// /// Gets all blog posts /// /// The store identifier; pass 0 to load all records /// Language identifier. 0 if you want to get all blog posts /// Tag /// Page index /// Page size /// A value indicating whether to show hidden records /// /// A task that represents the asynchronous operation /// The task result contains the blog posts /// public virtual async Task> GetAllBlogPostsByTagAsync(int storeId = 0, int languageId = 0, string tag = "", int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false) { tag = tag.Trim(); //we load all records and only then filter them by tag var blogPostsAll = await GetAllBlogPostsAsync(storeId: storeId, languageId: languageId, showHidden: showHidden); var taggedBlogPosts = new List(); foreach (var blogPost in blogPostsAll) { var tags = await ParseTagsAsync(blogPost); if (!string.IsNullOrEmpty(tags.FirstOrDefault(t => t.Equals(tag, StringComparison.InvariantCultureIgnoreCase)))) taggedBlogPosts.Add(blogPost); } //server-side paging var result = new PagedList(taggedBlogPosts, pageIndex, pageSize); return result; } /// /// Gets all blog post tags /// /// The store identifier; pass 0 to load all records /// Language identifier. 0 if you want to get all blog posts /// A value indicating whether to show hidden records /// /// A task that represents the asynchronous operation /// The task result contains the blog post tags /// public virtual async Task> GetAllBlogPostTagsAsync(int storeId, int languageId, bool showHidden = false) { var cacheKey = _staticCacheManager.PrepareKeyForDefaultCache(NopBlogsDefaults.BlogTagsCacheKey, languageId, storeId, showHidden); var blogPostTags = await _staticCacheManager.GetAsync(cacheKey, async () => { var rezBlogPostTags = new List(); var blogPosts = await GetAllBlogPostsAsync(storeId, languageId, showHidden: showHidden); foreach (var blogPost in blogPosts) { var tags = await ParseTagsAsync(blogPost); foreach (var tag in tags) { var foundBlogPostTag = rezBlogPostTags.Find(bpt => bpt.Name.Equals(tag, StringComparison.InvariantCultureIgnoreCase)); if (foundBlogPostTag == null) { foundBlogPostTag = new BlogPostTag { Name = tag, BlogPostCount = 1 }; rezBlogPostTags.Add(foundBlogPostTag); } else foundBlogPostTag.BlogPostCount++; } } return rezBlogPostTags; }); return blogPostTags; } /// /// Inserts a blog post /// /// Blog post /// A task that represents the asynchronous operation public virtual async Task InsertBlogPostAsync(BlogPost blogPost) { await _blogPostRepository.InsertAsync(blogPost); } /// /// Updates the blog post /// /// Blog post /// A task that represents the asynchronous operation public virtual async Task UpdateBlogPostAsync(BlogPost blogPost) { await _blogPostRepository.UpdateAsync(blogPost); } /// /// Returns all posts published between the two dates. /// /// Source /// Date from /// Date to /// /// A task that represents the asynchronous operation /// The task result contains the filtered posts /// public virtual async Task> GetPostsByDateAsync(IList blogPosts, DateTime dateFrom, DateTime dateTo) { ArgumentNullException.ThrowIfNull(blogPosts); var rez = await blogPosts .Where(p => dateFrom.Date <= (p.StartDateUtc ?? p.CreatedOnUtc) && (p.StartDateUtc ?? p.CreatedOnUtc).Date <= dateTo) .ToListAsync(); return rez; } /// /// Parse tags /// /// Blog post /// /// A task that represents the asynchronous operation /// The task result contains the ags /// public virtual async Task> ParseTagsAsync(BlogPost blogPost) { ArgumentNullException.ThrowIfNull(blogPost); if (blogPost.Tags == null) return new List(); var tags = await blogPost.Tags.Split(_separator, StringSplitOptions.RemoveEmptyEntries) .Select(tag => tag.Trim()) .Where(tag => !string.IsNullOrEmpty(tag)) .ToListAsync(); return tags; } /// /// Get a value indicating whether a blog post is available now (availability dates) /// /// Blog post /// Datetime to check; pass null to use current date /// Result public virtual bool BlogPostIsAvailable(BlogPost blogPost, DateTime? dateTime = null) { ArgumentNullException.ThrowIfNull(blogPost); if (blogPost.StartDateUtc.HasValue && blogPost.StartDateUtc.Value >= (dateTime ?? DateTime.UtcNow)) return false; if (blogPost.EndDateUtc.HasValue && blogPost.EndDateUtc.Value <= (dateTime ?? DateTime.UtcNow)) return false; return true; } #endregion #region Blog comments /// /// Gets all comments /// /// Customer identifier; 0 to load all records /// Store identifier; pass 0 to load all records /// Blog post ID; 0 or null to load all records /// A value indicating whether to content is approved; null to load all records /// Item creation from; null to load all records /// Item creation to; null to load all records /// Search comment text; null to load all records /// /// A task that represents the asynchronous operation /// The task result contains the comments /// public virtual async Task> GetAllCommentsAsync(int customerId = 0, int storeId = 0, int? blogPostId = null, bool? approved = null, DateTime? fromUtc = null, DateTime? toUtc = null, string commentText = null) { return await _blogCommentRepository.GetAllAsync(query => { if (approved.HasValue) query = query.Where(comment => comment.IsApproved == approved); if (blogPostId > 0) query = query.Where(comment => comment.BlogPostId == blogPostId); if (customerId > 0) query = query.Where(comment => comment.CustomerId == customerId); if (storeId > 0) query = query.Where(comment => comment.StoreId == storeId); if (fromUtc.HasValue) query = query.Where(comment => fromUtc.Value <= comment.CreatedOnUtc); if (toUtc.HasValue) query = query.Where(comment => toUtc.Value >= comment.CreatedOnUtc); if (!string.IsNullOrEmpty(commentText)) query = query.Where(c => c.CommentText.Contains(commentText)); query = query.OrderBy(comment => comment.CreatedOnUtc); return query; }); } /// /// Gets a blog comment /// /// Blog comment identifier /// /// A task that represents the asynchronous operation /// The task result contains the blog comment /// public virtual async Task GetBlogCommentByIdAsync(int blogCommentId) { return await _blogCommentRepository.GetByIdAsync(blogCommentId, cache => default, useShortTermCache: true); } /// /// Get blog comments by identifiers /// /// Blog comment identifiers /// /// A task that represents the asynchronous operation /// The task result contains the blog comments /// public virtual async Task> GetBlogCommentsByIdsAsync(int[] commentIds) { return await _blogCommentRepository.GetByIdsAsync(commentIds); } /// /// Get the count of blog comments /// /// Blog post /// Store identifier; pass 0 to load all records /// A value indicating whether to count only approved or not approved comments; pass null to get number of all comments /// /// A task that represents the asynchronous operation /// The task result contains the number of blog comments /// public virtual async Task GetBlogCommentsCountAsync(BlogPost blogPost, int storeId = 0, bool? isApproved = null) { var query = _blogCommentRepository.Table.Where(comment => comment.BlogPostId == blogPost.Id); if (storeId > 0) query = query.Where(comment => comment.StoreId == storeId); if (isApproved.HasValue) query = query.Where(comment => comment.IsApproved == isApproved.Value); var cacheKey = _staticCacheManager.PrepareKeyForDefaultCache(NopBlogsDefaults.BlogCommentsNumberCacheKey, blogPost, storeId, isApproved); return await _staticCacheManager.GetAsync(cacheKey, async () => await query.CountAsync()); } /// /// Deletes a blog comment /// /// Blog comment /// A task that represents the asynchronous operation public virtual async Task DeleteBlogCommentAsync(BlogComment blogComment) { await _blogCommentRepository.DeleteAsync(blogComment); } /// /// Deletes blog comments /// /// Blog comments /// A task that represents the asynchronous operation public virtual async Task DeleteBlogCommentsAsync(IList blogComments) { await _blogCommentRepository.DeleteAsync(blogComments); } /// /// Inserts a blog comment /// /// Blog comment /// A task that represents the asynchronous operation public virtual async Task InsertBlogCommentAsync(BlogComment blogComment) { await _blogCommentRepository.InsertAsync(blogComment); } /// /// Update a blog comment /// /// Blog comment /// A task that represents the asynchronous operation public virtual async Task UpdateBlogCommentAsync(BlogComment blogComment) { await _blogCommentRepository.UpdateAsync(blogComment); } #endregion #endregion }