Try your search with a different keyword or use * as a wildcard.
using System.Globalization;
using System.Text;
using System.Xml;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Domain.Blogs;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Forums;
using Nop.Core.Domain.Localization;
using Nop.Core.Domain.News;
using Nop.Core.Domain.Seo;
using Nop.Core.Domain.Topics;
using Nop.Core.Events;
using Nop.Core.Infrastructure;
using Nop.Services.Blogs;
using Nop.Services.Catalog;
using Nop.Services.Customers;
using Nop.Services.Localization;
using Nop.Services.News;
using Nop.Services.Seo;
using Nop.Services.Topics;
using Nop.Web.Framework.Mvc.Routing;
using Nop.Web.Infrastructure.Cache;
using Nop.Web.Models.Sitemap;
namespace Nop.Web.Factories;
///
/// Represents the sitemap model factory implementation
///
public partial class SitemapModelFactory : ISitemapModelFactory
{
#region Fields
protected readonly BlogSettings _blogSettings;
protected readonly ForumSettings _forumSettings;
protected readonly IActionContextAccessor _actionContextAccessor;
protected readonly IBlogService _blogService;
protected readonly ICategoryService _categoryService;
protected readonly ICustomerService _customerService;
protected readonly IEventPublisher _eventPublisher;
protected readonly ILanguageService _languageService;
protected readonly ILocalizationService _localizationService;
protected readonly ILocker _locker;
protected readonly IManufacturerService _manufacturerService;
protected readonly INewsService _newsService;
protected readonly INopFileProvider _nopFileProvider;
protected readonly INopUrlHelper _nopUrlHelper;
protected readonly IProductService _productService;
protected readonly IProductTagService _productTagService;
protected readonly IStaticCacheManager _staticCacheManager;
protected readonly IStoreContext _storeContext;
protected readonly ITopicService _topicService;
protected readonly IUrlHelperFactory _urlHelperFactory;
protected readonly IUrlRecordService _urlRecordService;
protected readonly IWebHelper _webHelper;
protected readonly IWorkContext _workContext;
protected readonly LocalizationSettings _localizationSettings;
protected readonly NewsSettings _newsSettings;
protected readonly SitemapSettings _sitemapSettings;
protected readonly SitemapXmlSettings _sitemapXmlSettings;
#endregion
#region Ctor
public SitemapModelFactory(BlogSettings blogSettings,
ForumSettings forumSettings,
IActionContextAccessor actionContextAccessor,
IBlogService blogService,
ICategoryService categoryService,
ICustomerService customerService,
IEventPublisher eventPublisher,
ILanguageService languageService,
ILocalizationService localizationService,
ILocker locker,
IManufacturerService manufacturerService,
INewsService newsService,
INopFileProvider nopFileProvider,
INopUrlHelper nopUrlHelper,
IProductService productService,
IProductTagService productTagService,
IStaticCacheManager staticCacheManager,
IStoreContext storeContext,
ITopicService topicService,
IUrlHelperFactory urlHelperFactory,
IUrlRecordService urlRecordService,
IWebHelper webHelper,
IWorkContext workContext,
LocalizationSettings localizationSettings,
NewsSettings newsSettings,
SitemapSettings sitemapSettings,
SitemapXmlSettings sitemapXmlSettings)
{
_blogSettings = blogSettings;
_forumSettings = forumSettings;
_actionContextAccessor = actionContextAccessor;
_blogService = blogService;
_categoryService = categoryService;
_customerService = customerService;
_eventPublisher = eventPublisher;
_languageService = languageService;
_localizationService = localizationService;
_locker = locker;
_manufacturerService = manufacturerService;
_newsService = newsService;
_nopFileProvider = nopFileProvider;
_nopUrlHelper = nopUrlHelper;
_productService = productService;
_productTagService = productTagService;
_staticCacheManager = staticCacheManager;
_storeContext = storeContext;
_topicService = topicService;
_urlHelperFactory = urlHelperFactory;
_urlRecordService = urlRecordService;
_webHelper = webHelper;
_workContext = workContext;
_localizationSettings = localizationSettings;
_newsSettings = newsSettings;
_sitemapSettings = sitemapSettings;
_sitemapXmlSettings = sitemapXmlSettings;
}
#endregion
#region Utilities
///
/// Get UrlHelper
///
/// UrlHelper
protected virtual IUrlHelper GetUrlHelper()
{
return _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
}
///
/// Get HTTP protocol
///
///
/// A task that represents the asynchronous operation
/// The task result contains the protocol name as string
///
protected virtual async Task GetHttpProtocolAsync()
{
var store = await _storeContext.GetCurrentStoreAsync();
return store.SslEnabled ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
}
///
/// Generate URLs for the sitemap
///
///
/// A task that represents the asynchronous operation
/// The task result contains the list of sitemap URLs
///
protected virtual async Task> GenerateUrlsAsync()
{
var sitemapUrls = new List
{
//home page
await PrepareLocalizedSitemapUrlAsync("Homepage"),
//search products
await PrepareLocalizedSitemapUrlAsync("ProductSearch"),
//contact us
await PrepareLocalizedSitemapUrlAsync("ContactUs")
};
//news
if (_newsSettings.Enabled)
sitemapUrls.Add(await PrepareLocalizedSitemapUrlAsync("NewsArchive"));
//blog
if (_blogSettings.Enabled)
sitemapUrls.Add(await PrepareLocalizedSitemapUrlAsync("Blog"));
//forum
if (_forumSettings.ForumsEnabled)
sitemapUrls.Add(await PrepareLocalizedSitemapUrlAsync("Boards"));
//categories
if (_sitemapXmlSettings.SitemapXmlIncludeCategories)
sitemapUrls.AddRange(await GetCategoryUrlsAsync());
//manufacturers
if (_sitemapXmlSettings.SitemapXmlIncludeManufacturers)
sitemapUrls.AddRange(await GetManufacturerUrlsAsync());
//products
if (_sitemapXmlSettings.SitemapXmlIncludeProducts)
sitemapUrls.AddRange(await GetProductUrlsAsync());
//product tags
if (_sitemapXmlSettings.SitemapXmlIncludeProductTags)
sitemapUrls.AddRange(await GetProductTagUrlsAsync());
//news
if (_sitemapXmlSettings.SitemapXmlIncludeNews && _newsSettings.Enabled)
sitemapUrls.AddRange(await GetNewsItemUrlsAsync());
//blog posts
if (_sitemapXmlSettings.SitemapXmlIncludeBlogPosts && _blogSettings.Enabled)
sitemapUrls.AddRange(await GetBlogPostUrlsAsync());
//topics
if (_sitemapXmlSettings.SitemapXmlIncludeTopics)
sitemapUrls.AddRange(await GetTopicUrlsAsync());
//custom URLs
if (_sitemapXmlSettings.SitemapXmlIncludeCustomUrls)
sitemapUrls.AddRange(GetCustomUrls());
//event notification
await _eventPublisher.PublishAsync(new SitemapCreatedEvent(sitemapUrls));
return sitemapUrls;
}
///
/// Get news item URLs for the sitemap
///
///
/// A task that represents the asynchronous operation
/// The task result contains the sitemap URLs
///
protected virtual async Task> GetNewsItemUrlsAsync()
{
var store = await _storeContext.GetCurrentStoreAsync();
return await (await _newsService.GetAllNewsAsync(storeId: store.Id))
.SelectAwait(async news => await PrepareLocalizedSitemapUrlAsync("NewsItem",
async lang => new { SeName = await _urlRecordService.GetSeNameAsync(news, news.LanguageId, ensureTwoPublishedLanguages: false) },
news.CreatedOnUtc)).ToListAsync();
}
///
/// Get category URLs for the sitemap
///
///
/// A task that represents the asynchronous operation
/// The task result contains the sitemap URLs
///
protected virtual async Task> GetCategoryUrlsAsync()
{
var store = await _storeContext.GetCurrentStoreAsync();
return await (await _categoryService.GetAllCategoriesAsync(storeId: store.Id))
.SelectAwait(async category => await PrepareLocalizedSitemapUrlAsync("Category", GetSeoRouteParamsAwait(category), category.UpdatedOnUtc)).ToListAsync();
}
///
/// Get manufacturer URLs for the sitemap
///
///
/// A task that represents the asynchronous operation
/// The task result contains the sitemap URLs
///
protected virtual async Task> GetManufacturerUrlsAsync()
{
var store = await _storeContext.GetCurrentStoreAsync();
return await (await _manufacturerService.GetAllManufacturersAsync(storeId: store.Id))
.SelectAwait(async manufacturer => await PrepareLocalizedSitemapUrlAsync("Manufacturer", GetSeoRouteParamsAwait(manufacturer), manufacturer.UpdatedOnUtc)).ToListAsync();
}
///
/// Get product URLs for the sitemap
///
///
/// A task that represents the asynchronous operation
/// The task result contains the sitemap URLs
///
protected virtual async Task> GetProductUrlsAsync()
{
var store = await _storeContext.GetCurrentStoreAsync();
return await (await _productService.SearchProductsAsync(0, storeId: store.Id,
visibleIndividuallyOnly: true, orderBy: ProductSortingEnum.CreatedOn))
.SelectAwait(async product => await PrepareLocalizedSitemapUrlAsync("Product", GetSeoRouteParamsAwait(product), product.UpdatedOnUtc)).ToListAsync();
}
///
/// Get product tag URLs for the sitemap
///
///
/// A task that represents the asynchronous operation
/// The task result contains the sitemap URLs
///
protected virtual async Task> GetProductTagUrlsAsync()
{
return await (await _productTagService.GetAllProductTagsAsync())
.SelectAwait(async productTag => await PrepareLocalizedSitemapUrlAsync("ProductsByTag", GetSeoRouteParamsAwait(productTag))).ToListAsync();
}
///
/// Get topic URLs for the sitemap
///
///
/// A task that represents the asynchronous operation
/// The task result contains the sitemap URLs
///
protected virtual async Task> GetTopicUrlsAsync()
{
var store = await _storeContext.GetCurrentStoreAsync();
return await (await _topicService.GetAllTopicsAsync(store.Id)).Where(t => t.IncludeInSitemap)
.SelectAwait(async topic => await PrepareLocalizedSitemapUrlAsync("Topic", GetSeoRouteParamsAwait(topic))).ToListAsync();
}
///
/// Return localized blog post url
///
/// Blog post to generate URL
/// How often to update url
/// A task that represents the asynchronous operation
protected virtual async Task PrepareLocalizedBlogPostUrlModelAsync(BlogPost post, UpdateFrequency updateFreq = UpdateFrequency.Weekly)
{
//url for current language
var url = await _nopUrlHelper.RouteGenericUrlAsync(new
{
SeName = await _urlRecordService.GetSeNameAsync(post, null,
ensureTwoPublishedLanguages: false)
}, await GetHttpProtocolAsync());
var updatedOn = post.CreatedOnUtc;
if (!_localizationSettings.SeoFriendlyUrlsForLanguagesEnabled)
return new SitemapUrlModel(url, new List(), updateFreq, updatedOn);
var lang = await _languageService.GetLanguageByIdAsync(post.LanguageId);
if (lang == null || !lang.Published)
return new SitemapUrlModel(url, new List(), updateFreq, updatedOn);
url = await _nopUrlHelper.RouteGenericUrlAsync(new
{
SeName = await _urlRecordService.GetSeNameAsync(post, post.LanguageId,
ensureTwoPublishedLanguages: false)
}, await GetHttpProtocolAsync());
url = GetLocalizedUrl(url, lang);
return new SitemapUrlModel(url, new List(), updateFreq, updatedOn);
}
///
/// Get blog post URLs for the sitemap
///
///
/// A task that represents the asynchronous operation
/// The task result contains the sitemap URLs
///
protected virtual async Task> GetBlogPostUrlsAsync()
{
var store = await _storeContext.GetCurrentStoreAsync();
var urls = await (await _blogService.GetAllBlogPostsAsync(store.Id))
.Where(p => p.IncludeInSitemap)
.SelectAwait(async post =>
await PrepareLocalizedBlogPostUrlModelAsync(post)
).ToListAsync();
return urls;
}
///
/// Get custom URLs for the sitemap
///
/// Sitemap URLs
protected virtual IEnumerable GetCustomUrls()
{
var storeLocation = _webHelper.GetStoreLocation();
return _sitemapXmlSettings.SitemapCustomUrls.Select(customUrl =>
new SitemapUrlModel(string.Concat(storeLocation, customUrl), new List(), UpdateFrequency.Weekly, DateTime.UtcNow));
}
///
/// Get route params for URL localization
///
/// Model type
/// Model
///
/// A task that represents the asynchronous operation
/// The task result contains the lambda for route params
///
protected virtual Func> GetSeoRouteParamsAwait(T model)
where T : BaseEntity, ISlugSupported
{
return async lang => new { SeName = await _urlRecordService.GetSeNameAsync(model, lang) };
}
///
/// Write sitemap index file into the stream
///
/// Stream
/// The number of sitemaps
/// A task that represents the asynchronous operation
protected virtual async Task WriteSitemapIndexAsync(MemoryStream stream, int sitemapNumber)
{
await using var writer = XmlWriter.Create(stream, new XmlWriterSettings
{
Async = true,
Encoding = Encoding.UTF8,
Indent = true,
ConformanceLevel = ConformanceLevel.Auto
});
await writer.WriteStartDocumentAsync();
await writer.WriteStartElementAsync(prefix: null, localName: "sitemapindex", ns: "http://www.sitemaps.org/schemas/sitemap/0.9");
//write URLs of all available sitemaps
var urlHelper = GetUrlHelper();
for (var id = 1; id <= sitemapNumber; id++)
{
var url = urlHelper.RouteUrl("sitemap-indexed.xml", new { Id = id }, await GetHttpProtocolAsync());
var location = await XmlHelper.XmlEncodeAsync(url);
await writer.WriteStartElementAsync(null, "sitemap", null);
await writer.WriteElementStringAsync(null, "loc", null, location);
await writer.WriteElementStringAsync(null, "lastmod", null, DateTime.UtcNow.ToString(NopSeoDefaults.SitemapDateFormat));
await writer.WriteEndElementAsync();
}
await writer.WriteEndElementAsync();
}
///
/// Write sitemap file into the stream
///
/// Stream
/// List of sitemap URLs
/// A task that represents the asynchronous operation
protected virtual async Task WriteSitemapAsync(MemoryStream stream, IList sitemapUrls)
{
await using var writer = XmlWriter.Create(stream, new XmlWriterSettings
{
Async = true,
Encoding = Encoding.UTF8,
Indent = true,
ConformanceLevel = ConformanceLevel.Auto
});
await writer.WriteStartDocumentAsync();
await writer.WriteStartElementAsync(prefix: null, localName: "urlset", ns: "http://www.sitemaps.org/schemas/sitemap/0.9");
await writer.WriteAttributeStringAsync(prefix: "xsi", "schemaLocation",
"http://www.w3.org/2001/XMLSchema-instance",
"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.w3.org/1999/xhtml http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd");
await writer.WriteAttributeStringAsync(prefix: "xmlns", "xhtml", null, "http://www.w3.org/1999/xhtml");
//write URLs from list to the sitemap
foreach (var sitemapUrl in sitemapUrls)
{
//write base url
await WriteSitemapUrlAsync(writer, sitemapUrl);
//write all alternate url if exists
foreach (var alternate in sitemapUrl.AlternateLocations
.Where(p => !p.Equals(sitemapUrl.Location, StringComparison.InvariantCultureIgnoreCase)))
{
await WriteSitemapUrlAsync(writer, new SitemapUrlModel(alternate, sitemapUrl));
}
}
await writer.WriteEndElementAsync();
}
///
/// Write sitemap
///
/// XML stream writer
/// Sitemap URL
/// A task that represents the asynchronous operation
protected virtual async Task WriteSitemapUrlAsync(XmlWriter writer, SitemapUrlModel sitemapUrl)
{
if (string.IsNullOrEmpty(sitemapUrl.Location))
return;
await writer.WriteStartElementAsync(null, "url", null);
var loc = await XmlHelper.XmlEncodeAsync(sitemapUrl.Location);
await writer.WriteElementStringAsync(null, "loc", null, loc);
//write all related url
foreach (var alternate in sitemapUrl.AlternateLocations)
{
if (string.IsNullOrEmpty(alternate))
continue;
//extract seo code
var altLoc = await XmlHelper.XmlEncodeAsync(alternate);
var altLocPath = new Uri(altLoc).PathAndQuery;
var (_, lang) = await altLocPath.IsLocalizedUrlAsync(_actionContextAccessor.ActionContext.HttpContext.Request.PathBase, true);
if (string.IsNullOrEmpty(lang?.UniqueSeoCode))
continue;
await writer.WriteStartElementAsync("xhtml", "link", null);
await writer.WriteAttributeStringAsync(null, "rel", null, "alternate");
await writer.WriteAttributeStringAsync(null, "hreflang", null, lang.UniqueSeoCode);
await writer.WriteAttributeStringAsync(null, "href", null, altLoc);
await writer.WriteEndElementAsync();
}
await writer.WriteElementStringAsync(null, "changefreq", null, sitemapUrl.UpdateFrequency.ToString().ToLowerInvariant());
await writer.WriteElementStringAsync(null, "lastmod", null, sitemapUrl.UpdatedOn.ToString(NopSeoDefaults.SitemapDateFormat, CultureInfo.InvariantCulture));
await writer.WriteEndElementAsync();
}
///
/// This will build an XML sitemap for better index with search engines.
/// See http://en.wikipedia.org/wiki/Sitemaps for more information.
///
/// The path and name of the sitemap file
/// Sitemap identifier
/// A task that represents the asynchronous operation
protected virtual async Task GenerateAsync(string fullPath, int id = 0)
{
//generate all URLs for the sitemap
var sitemapUrls = await GenerateUrlsAsync();
//split URLs into separate lists based on the max size
var numberOfParts = (int)Math.Ceiling((decimal)sitemapUrls.Sum(x => (x.AlternateLocations?.Count ?? 0) + 1) / NopSeoDefaults.SitemapMaxUrlNumber);
var sitemaps = sitemapUrls
.Select((url, index) => new { Index = index, Value = url })
.GroupBy(group => group.Index % numberOfParts)
.Select(group => group
.Select(url => url.Value)
.ToList()).ToList();
if (!sitemaps.Any())
return;
await using var stream = new MemoryStream();
if (id > 0)
{
//requested sitemap does not exist
if (id > sitemaps.Count)
return;
//otherwise write a certain numbered sitemap file into the stream
await WriteSitemapAsync(stream, sitemaps.ElementAt(id - 1));
}
else
{
//URLs more than the maximum allowable, so generate a sitemap index file
if (numberOfParts > 1)
{
//write a sitemap index file into the stream
await WriteSitemapIndexAsync(stream, sitemaps.Count);
}
else
{
//otherwise generate a standard sitemap
await WriteSitemapAsync(stream, sitemaps.First());
}
}
if (_nopFileProvider.FileExists(fullPath))
_nopFileProvider.DeleteFile(fullPath);
using var fileStream = _nopFileProvider.GetOrCreateFile(fullPath);
stream.Position = 0;
await stream.CopyToAsync(fileStream, 81920);
}
///
/// Gets localized URL with SEO code
///
/// URL to add SEO code
/// Language for localization
/// Localized URL with SEO code
protected string GetLocalizedUrl(string currentUrl, Language lang)
{
if (string.IsNullOrEmpty(currentUrl))
return null;
if (_actionContextAccessor.ActionContext == null)
return null;
var pathBase = _actionContextAccessor.ActionContext.HttpContext.Request.PathBase;
//Extract server and path from url
var scheme = new Uri(currentUrl).GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped);
var path = new Uri(currentUrl).PathAndQuery;
//Replace seo code
var localizedPath = path
.RemoveLanguageSeoCodeFromUrl(pathBase, true)
.AddLanguageSeoCodeToUrl(pathBase, true, lang);
return new Uri(new Uri(scheme), localizedPath).ToString();
}
#endregion
#region Methods
///
/// Prepare the sitemap model
///
/// Sitemap page model
///
/// A task that represents the asynchronous operation
/// The task result contains the sitemap model
///
public virtual async Task PrepareSitemapModelAsync(SitemapPageModel pageModel)
{
ArgumentNullException.ThrowIfNull(pageModel);
var language = await _workContext.GetWorkingLanguageAsync();
var customer = await _workContext.GetCurrentCustomerAsync();
var customerRoleIds = await _customerService.GetCustomerRoleIdsAsync(customer);
var store = await _storeContext.GetCurrentStoreAsync();
var cacheKey = _staticCacheManager.PrepareKeyForDefaultCache(NopModelCacheDefaults.SitemapPageModelKey,
language, customerRoleIds, store);
var cachedModel = await _staticCacheManager.GetAsync(cacheKey, async () =>
{
//get URL helper
var urlHelper = GetUrlHelper();
var model = new SitemapModel();
//prepare common items
var commonGroupTitle = await _localizationService.GetResourceAsync("Sitemap.General");
//home page
model.Items.Add(new SitemapModel.SitemapItemModel
{
GroupTitle = commonGroupTitle,
Name = await _localizationService.GetResourceAsync("Homepage"),
Url = urlHelper.RouteUrl("Homepage")
});
//search
model.Items.Add(new SitemapModel.SitemapItemModel
{
GroupTitle = commonGroupTitle,
Name = await _localizationService.GetResourceAsync("Search"),
Url = urlHelper.RouteUrl("ProductSearch")
});
//news
if (_newsSettings.Enabled)
{
model.Items.Add(new SitemapModel.SitemapItemModel
{
GroupTitle = commonGroupTitle,
Name = await _localizationService.GetResourceAsync("News"),
Url = urlHelper.RouteUrl("NewsArchive")
});
}
//blog
if (_blogSettings.Enabled)
{
model.Items.Add(new SitemapModel.SitemapItemModel
{
GroupTitle = commonGroupTitle,
Name = await _localizationService.GetResourceAsync("Blog"),
Url = urlHelper.RouteUrl("Blog")
});
}
//forums
if (_forumSettings.ForumsEnabled)
{
model.Items.Add(new SitemapModel.SitemapItemModel
{
GroupTitle = commonGroupTitle,
Name = await _localizationService.GetResourceAsync("Forum.Forums"),
Url = urlHelper.RouteUrl("Boards")
});
}
//contact us
model.Items.Add(new SitemapModel.SitemapItemModel
{
GroupTitle = commonGroupTitle,
Name = await _localizationService.GetResourceAsync("ContactUs"),
Url = urlHelper.RouteUrl("ContactUs")
});
//customer info
model.Items.Add(new SitemapModel.SitemapItemModel
{
GroupTitle = commonGroupTitle,
Name = await _localizationService.GetResourceAsync("Account.MyAccount"),
Url = urlHelper.RouteUrl("CustomerInfo")
});
//at the moment topics are in general category too
if (_sitemapSettings.SitemapIncludeTopics)
{
var topics = (await _topicService.GetAllTopicsAsync(storeId: store.Id))
.Where(topic => topic.IncludeInSitemap);
model.Items.AddRange(await topics.SelectAwait(async topic => new SitemapModel.SitemapItemModel
{
GroupTitle = commonGroupTitle,
Name = await _localizationService.GetLocalizedAsync(topic, x => x.Title),
Url = await _nopUrlHelper.RouteGenericUrlAsync(new { SeName = await _urlRecordService.GetSeNameAsync(topic) })
}).ToListAsync());
}
//blog posts
if (_sitemapSettings.SitemapIncludeBlogPosts && _blogSettings.Enabled)
{
var blogPostsGroupTitle = await _localizationService.GetResourceAsync("Sitemap.BlogPosts");
var blogPosts = (await _blogService.GetAllBlogPostsAsync(storeId: store.Id))
.Where(p => p.IncludeInSitemap);
model.Items.AddRange(await blogPosts.SelectAwait(async post => new SitemapModel.SitemapItemModel
{
GroupTitle = blogPostsGroupTitle,
Name = post.Title,
Url = await _nopUrlHelper
.RouteGenericUrlAsync(new { SeName = await _urlRecordService.GetSeNameAsync(post, post.LanguageId, ensureTwoPublishedLanguages: false) })
}).ToListAsync());
}
//news
if (_sitemapSettings.SitemapIncludeNews && _newsSettings.Enabled)
{
var newsGroupTitle = await _localizationService.GetResourceAsync("Sitemap.News");
var news = await _newsService.GetAllNewsAsync(storeId: store.Id);
model.Items.AddRange(await news.SelectAwait(async newsItem => new SitemapModel.SitemapItemModel
{
GroupTitle = newsGroupTitle,
Name = newsItem.Title,
Url = await _nopUrlHelper
.RouteGenericUrlAsync(new { SeName = await _urlRecordService.GetSeNameAsync(newsItem, newsItem.LanguageId, ensureTwoPublishedLanguages: false) })
}).ToListAsync());
}
//categories
if (_sitemapSettings.SitemapIncludeCategories)
{
var categoriesGroupTitle = await _localizationService.GetResourceAsync("Sitemap.Categories");
var categories = await _categoryService.GetAllCategoriesAsync(storeId: store.Id);
model.Items.AddRange(await categories.SelectAwait(async category => new SitemapModel.SitemapItemModel
{
GroupTitle = categoriesGroupTitle,
Name = await _localizationService.GetLocalizedAsync(category, x => x.Name),
Url = await _nopUrlHelper.RouteGenericUrlAsync(new { SeName = await _urlRecordService.GetSeNameAsync(category) })
}).ToListAsync());
}
//manufacturers
if (_sitemapSettings.SitemapIncludeManufacturers)
{
var manufacturersGroupTitle = await _localizationService.GetResourceAsync("Sitemap.Manufacturers");
var manufacturers = await _manufacturerService.GetAllManufacturersAsync(storeId: store.Id);
model.Items.AddRange(await manufacturers.SelectAwait(async manufacturer => new SitemapModel.SitemapItemModel
{
GroupTitle = manufacturersGroupTitle,
Name = await _localizationService.GetLocalizedAsync(manufacturer, x => x.Name),
Url = await _nopUrlHelper.RouteGenericUrlAsync(new { SeName = await _urlRecordService.GetSeNameAsync(manufacturer) })
}).ToListAsync());
}
//products
if (_sitemapSettings.SitemapIncludeProducts)
{
var productsGroupTitle = await _localizationService.GetResourceAsync("Sitemap.Products");
var products = await _productService.SearchProductsAsync(0, storeId: store.Id, visibleIndividuallyOnly: true);
model.Items.AddRange(await products.SelectAwait(async product => new SitemapModel.SitemapItemModel
{
GroupTitle = productsGroupTitle,
Name = await _localizationService.GetLocalizedAsync(product, x => x.Name),
Url = await _nopUrlHelper.RouteGenericUrlAsync(new { SeName = await _urlRecordService.GetSeNameAsync(product) })
}).ToListAsync());
}
//product tags
if (_sitemapSettings.SitemapIncludeProductTags)
{
var productTagsGroupTitle = await _localizationService.GetResourceAsync("Sitemap.ProductTags");
var productTags = await _productTagService.GetAllProductTagsAsync();
model.Items.AddRange(await productTags.SelectAwait(async productTag => new SitemapModel.SitemapItemModel
{
GroupTitle = productTagsGroupTitle,
Name = await _localizationService.GetLocalizedAsync(productTag, x => x.Name),
Url = await _nopUrlHelper.RouteGenericUrlAsync(new { SeName = await _urlRecordService.GetSeNameAsync(productTag) })
}).ToListAsync());
}
return model;
});
//prepare model with pagination
pageModel.PageSize = Math.Max(pageModel.PageSize, _sitemapSettings.SitemapPageSize);
pageModel.PageNumber = Math.Max(pageModel.PageNumber, 1);
var pagedItems = new PagedList(cachedModel.Items, pageModel.PageNumber - 1, pageModel.PageSize);
var sitemapModel = new SitemapModel { Items = pagedItems };
sitemapModel.PageModel.LoadPagedList(pagedItems);
return sitemapModel;
}
///
/// Prepare sitemap model.
/// This will build an XML sitemap for better index with search engines.
/// See http://en.wikipedia.org/wiki/Sitemaps for more information.
///
/// Sitemap identifier
///
/// A task that represents the asynchronous operation
/// The task result contains the sitemap model with sitemap.xml as string
///
public virtual async Task PrepareSitemapXmlModelAsync(int id = 0)
{
var language = await _workContext.GetWorkingLanguageAsync();
var store = await _storeContext.GetCurrentStoreAsync();
var fileName = string.Format(NopSeoDefaults.SitemapXmlFilePattern, store.Id, language.Id, id);
var fullPath = _nopFileProvider.GetAbsolutePath(NopSeoDefaults.SitemapXmlDirectory, fileName);
if (_nopFileProvider.FileExists(fullPath) && _nopFileProvider.GetLastWriteTimeUtc(fullPath) > DateTime.UtcNow.AddHours(-_sitemapXmlSettings.RebuildSitemapXmlAfterHours))
{
return new SitemapXmlModel { SitemapXmlPath = fullPath };
}
//execute task with lock
if (!await _locker.PerformActionWithLockAsync(
fullPath,
TimeSpan.FromSeconds(_sitemapXmlSettings.SitemapBuildOperationDelay),
async () => await GenerateAsync(fullPath, id)))
{
throw new InvalidOperationException();
}
return new SitemapXmlModel { SitemapXmlPath = fullPath };
}
///
/// Return localized urls
///
/// Route name
/// Lambda for route params object
/// A time when URL was updated last time
/// How often to update url
/// A task that represents the asynchronous operation
public virtual async Task PrepareLocalizedSitemapUrlAsync(string routeName,
Func> getRouteParamsAwait = null,
DateTime? dateTimeUpdatedOn = null,
UpdateFrequency updateFreq = UpdateFrequency.Weekly)
{
async Task routeUrlAsync(string routeName, object values, string protocol) => routeName switch
{
var name when name.Equals(nameof(Product), StringComparison.InvariantCultureIgnoreCase)
=> await _nopUrlHelper.RouteGenericUrlAsync(values, protocol),
var name when name.Equals(nameof(Category), StringComparison.InvariantCultureIgnoreCase)
=> await _nopUrlHelper.RouteGenericUrlAsync(values, protocol),
var name when name.Equals(nameof(Manufacturer), StringComparison.InvariantCultureIgnoreCase)
=> await _nopUrlHelper.RouteGenericUrlAsync(values, protocol),
var name when name.Equals(nameof(BlogPost), StringComparison.InvariantCultureIgnoreCase)
=> await _nopUrlHelper.RouteGenericUrlAsync(values, protocol),
var name when name.Equals(nameof(NewsItem), StringComparison.InvariantCultureIgnoreCase)
=> await _nopUrlHelper.RouteGenericUrlAsync(values, protocol),
var name when name.Equals(nameof(Topic), StringComparison.InvariantCultureIgnoreCase)
=> await _nopUrlHelper.RouteGenericUrlAsync(values, protocol),
var name when name.Equals(nameof(ProductTag), StringComparison.InvariantCultureIgnoreCase)
=> await _nopUrlHelper.RouteGenericUrlAsync(values, protocol),
_ => GetUrlHelper().RouteUrl(routeName, values, protocol)
};
//url for current language
var url = await routeUrlAsync(routeName,
getRouteParamsAwait != null ? await getRouteParamsAwait(null) : null,
await GetHttpProtocolAsync());
var store = await _storeContext.GetCurrentStoreAsync();
var updatedOn = dateTimeUpdatedOn ?? DateTime.UtcNow;
var languages = _localizationSettings.SeoFriendlyUrlsForLanguagesEnabled
? await _languageService.GetAllLanguagesAsync(storeId: store.Id)
: null;
if (languages == null || languages.Count == 1)
return new SitemapUrlModel(url, new List(), updateFreq, updatedOn);
//return list of localized urls
var localizedUrls = await languages
.SelectAwait(async lang =>
{
var currentUrl = await routeUrlAsync(routeName,
getRouteParamsAwait != null ? await getRouteParamsAwait(lang.Id) : null,
await GetHttpProtocolAsync());
return GetLocalizedUrl(currentUrl, lang);
})
.Where(value => !string.IsNullOrEmpty(value))
.ToListAsync();
return new SitemapUrlModel(url, localizedUrls, updateFreq, updatedOn);
}
#endregion
}