|
13 | 13 | using Nop.Core.Domain.Localization; |
14 | 14 | using Nop.Core.Domain.News; |
15 | 15 | using Nop.Core.Domain.Seo; |
| 16 | +using Nop.Core.Domain.Stores; |
16 | 17 | using Nop.Core.Domain.Topics; |
17 | 18 | using Nop.Core.Events; |
| 19 | +using Nop.Core.Http; |
18 | 20 | using Nop.Core.Infrastructure; |
19 | 21 | using Nop.Services.Blogs; |
20 | 22 | using Nop.Services.Catalog; |
@@ -522,7 +524,7 @@ protected virtual async Task WriteSitemapUrlAsync(XmlWriter writer, SitemapUrlMo |
522 | 524 | /// <param name="fullPath">The path and name of the sitemap file</param> |
523 | 525 | /// <param name="id">Sitemap identifier</param> |
524 | 526 | /// <returns>A task that represents the asynchronous operation</returns> |
525 | | - protected virtual async Task GenerateAsync(string fullPath, int id = 0) |
| 527 | + protected virtual async Task GenerateAsync(string fullPath, int id = 0) |
526 | 528 | { |
527 | 529 | //generate all URLs for the sitemap |
528 | 530 | var sitemapUrls = await GenerateUrlsAsync(); |
@@ -603,6 +605,22 @@ protected string GetLocalizedUrl(string currentUrl, Language lang) |
603 | 605 | return new Uri(new Uri(scheme), localizedPath).ToString(); |
604 | 606 | } |
605 | 607 |
|
| 608 | + /// <summary> |
| 609 | + /// Retrieves the list of languages for the given store that are not excluded in the sitemap XML settings, |
| 610 | + /// if SEO-friendly URLs for languages are enabled. |
| 611 | + /// </summary> |
| 612 | + /// <param name="store">The store to retrieve allowed languages for.</param> |
| 613 | + /// <returns> |
| 614 | + /// A list of <see cref="Language"/> if SEO-friendly URLs are enabled; otherwise, <c>null</c>. |
| 615 | + /// </returns> |
| 616 | + protected async Task<List<Language>> GetAllowedLanguagesAsync(Store store) |
| 617 | + { |
| 618 | + return _localizationSettings.SeoFriendlyUrlsForLanguagesEnabled |
| 619 | + ? (await _languageService.GetAllLanguagesAsync(storeId: store.Id)) |
| 620 | + .Where(lang => !_sitemapXmlSettings.DisallowLanguages.Contains(lang.Id)) |
| 621 | + .ToList() |
| 622 | + : null; |
| 623 | + } |
606 | 624 | #endregion |
607 | 625 |
|
608 | 626 | #region Methods |
@@ -823,27 +841,43 @@ public virtual async Task<SitemapModel> PrepareSitemapModelAsync(SitemapPageMode |
823 | 841 | /// </returns> |
824 | 842 | public virtual async Task<SitemapXmlModel> PrepareSitemapXmlModelAsync(int id = 0) |
825 | 843 | { |
826 | | - var language = await _workContext.GetWorkingLanguageAsync(); |
| 844 | + var workingLanguage = await _workContext.GetWorkingLanguageAsync(); |
827 | 845 | var store = await _storeContext.GetCurrentStoreAsync(); |
| 846 | + |
| 847 | + // get list of allowed languages (null if multilingual URLs are disabled) |
| 848 | + var languages = await GetAllowedLanguagesAsync(store); |
828 | 849 |
|
829 | | - var fileName = string.Format(NopSeoDefaults.SitemapXmlFilePattern, store.Id, language.Id, id); |
830 | | - var fullPath = _nopFileProvider.GetAbsolutePath(NopSeoDefaults.SitemapXmlDirectory, fileName); |
| 850 | + // select current language if allowed, fallback to first allowed if needed |
| 851 | + var language = languages?.FirstOrDefault(lang => lang.Id == workingLanguage?.Id) ?? languages?.FirstOrDefault() ?? workingLanguage; |
831 | 852 |
|
832 | | - if (_nopFileProvider.FileExists(fullPath) && _nopFileProvider.GetLastWriteTimeUtc(fullPath) > DateTime.UtcNow.AddHours(-_sitemapXmlSettings.RebuildSitemapXmlAfterHours)) |
| 853 | + if (language.Id != workingLanguage.Id) |
| 854 | + _actionContextAccessor.ActionContext.HttpContext.Items[NopHttpDefaults.ForcedSitemapXmlLanguage] = language.UniqueSeoCode.ToLowerInvariant(); |
| 855 | + |
| 856 | + try |
833 | 857 | { |
| 858 | + var fileName = string.Format(NopSeoDefaults.SitemapXmlFilePattern, store.Id, language.Id, id); |
| 859 | + var fullPath = _nopFileProvider.GetAbsolutePath(NopSeoDefaults.SitemapXmlDirectory, fileName); |
| 860 | + |
| 861 | + if (_nopFileProvider.FileExists(fullPath) && _nopFileProvider.GetLastWriteTimeUtc(fullPath) > DateTime.UtcNow.AddHours(-_sitemapXmlSettings.RebuildSitemapXmlAfterHours)) |
| 862 | + { |
| 863 | + return new SitemapXmlModel { SitemapXmlPath = fullPath }; |
| 864 | + } |
| 865 | + |
| 866 | + //execute task with lock |
| 867 | + if (!await _locker.PerformActionWithLockAsync( |
| 868 | + fullPath, |
| 869 | + TimeSpan.FromSeconds(_sitemapXmlSettings.SitemapBuildOperationDelay), |
| 870 | + async () => await GenerateAsync(fullPath, id))) |
| 871 | + { |
| 872 | + throw new InvalidOperationException(); |
| 873 | + } |
| 874 | + |
834 | 875 | return new SitemapXmlModel { SitemapXmlPath = fullPath }; |
835 | 876 | } |
836 | | - |
837 | | - //execute task with lock |
838 | | - if (!await _locker.PerformActionWithLockAsync( |
839 | | - fullPath, |
840 | | - TimeSpan.FromSeconds(_sitemapXmlSettings.SitemapBuildOperationDelay), |
841 | | - async () => await GenerateAsync(fullPath, id))) |
| 877 | + finally |
842 | 878 | { |
843 | | - throw new InvalidOperationException(); |
| 879 | + _actionContextAccessor.ActionContext.HttpContext.Items.Remove(NopHttpDefaults.ForcedSitemapXmlLanguage); |
844 | 880 | } |
845 | | - |
846 | | - return new SitemapXmlModel { SitemapXmlPath = fullPath }; |
847 | 881 | } |
848 | 882 |
|
849 | 883 | /// <summary> |
@@ -886,10 +920,7 @@ var name when name.Equals(nameof(ProductTag), StringComparison.InvariantCultureI |
886 | 920 | var store = await _storeContext.GetCurrentStoreAsync(); |
887 | 921 |
|
888 | 922 | var updatedOn = dateTimeUpdatedOn ?? DateTime.UtcNow; |
889 | | - var languages = _localizationSettings.SeoFriendlyUrlsForLanguagesEnabled |
890 | | - ? (await _languageService.GetAllLanguagesAsync(storeId: store.Id)) |
891 | | - .Where(lang => !_sitemapXmlSettings.DisallowLanguages.Contains(lang.Id)).ToList() |
892 | | - : null; |
| 923 | + var languages = await GetAllowedLanguagesAsync(store); |
893 | 924 |
|
894 | 925 | if (languages == null || languages.Count == 1) |
895 | 926 | return new SitemapUrlModel(url, new List<string>(), updateFreq, updatedOn); |
|
0 commit comments