Skip to content

Commit be8dc7f

Browse files
committed
SEO: added XML sitemap publisher for forums & other sitemap enhancements
1 parent 9b95ac6 commit be8dc7f

19 files changed

Lines changed: 231 additions & 74 deletions

File tree

src/Libraries/SmartStore.Services/Blogs/BlogService.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,17 +193,17 @@ public virtual void UpdateCommentTotals(BlogPost blogPost)
193193

194194
#region XML Sitemap
195195

196-
public XmlSitemapResult PublishXmlSitemap(XmlSitemapBuildContext context)
196+
public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context)
197197
{
198-
if (!context.LoadSetting<SeoSettings>().XmlSitemapIncludesBlog)
198+
if (!context.LoadSetting<SeoSettings>().XmlSitemapIncludesBlog || !context.LoadSetting<BlogSettings>().Enabled)
199199
return null;
200200

201201
var query = GetAllBlogPosts(context.RequestStoreId, 0, null, null, 0, int.MaxValue).SourceQuery;
202202

203203
return new BlogPostXmlSitemapResult { Query = query };
204204
}
205205

206-
class BlogPostXmlSitemapResult : XmlSitemapResult
206+
class BlogPostXmlSitemapResult : XmlSitemapProvider
207207
{
208208
public IQueryable<BlogPost> Query { get; set; }
209209

src/Libraries/SmartStore.Services/Catalog/CategoryService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,7 @@ private void AddChildTreeNodes(TreeNode<ICategoryNode> parentNode, int parentIte
848848

849849
#region XML Sitemap
850850

851-
public XmlSitemapResult PublishXmlSitemap(XmlSitemapBuildContext context)
851+
public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context)
852852
{
853853
if (!context.LoadSetting<SeoSettings>().XmlSitemapIncludesCategories)
854854
return null;
@@ -857,7 +857,7 @@ public XmlSitemapResult PublishXmlSitemap(XmlSitemapBuildContext context)
857857
return new CategoryXmlSitemapResult { Query = query };
858858
}
859859

860-
class CategoryXmlSitemapResult : XmlSitemapResult
860+
class CategoryXmlSitemapResult : XmlSitemapProvider
861861
{
862862
public IQueryable<Category> Query { get; set; }
863863

src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ public virtual void UpdateProductManufacturer(ProductManufacturer productManufac
345345

346346
#region XML Sitemap
347347

348-
public XmlSitemapResult PublishXmlSitemap(XmlSitemapBuildContext context)
348+
public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context)
349349
{
350350
if (!context.LoadSetting<SeoSettings>().XmlSitemapIncludesManufacturers)
351351
return null;
@@ -354,7 +354,7 @@ public XmlSitemapResult PublishXmlSitemap(XmlSitemapBuildContext context)
354354
return new ManufacturerXmlSitemapResult { Query = query };
355355
}
356356

357-
class ManufacturerXmlSitemapResult : XmlSitemapResult
357+
class ManufacturerXmlSitemapResult : XmlSitemapProvider
358358
{
359359
public IQueryable<Manufacturer> Query { get; set; }
360360

src/Libraries/SmartStore.Services/Forums/ForumService.cs

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@
1010
using SmartStore.Data.Caching;
1111
using SmartStore.Services.Common;
1212
using SmartStore.Services.Customers;
13+
using SmartStore.Services.Seo;
14+
using SmartStore.Core.Domain.Seo;
15+
using SmartStore.Core.Domain.Localization;
16+
using System.Web.Mvc;
17+
using System;
1318

1419
namespace SmartStore.Services.Forums
1520
{
16-
public partial class ForumService : IForumService
21+
public partial class ForumService : IForumService, IXmlSitemapPublisher
1722
{
1823
private readonly IRepository<ForumGroup> _forumGroupRepository;
1924
private readonly IRepository<Forum> _forumRepository;
@@ -27,6 +32,8 @@ public partial class ForumService : IForumService
2732
private readonly IRepository<Customer> _customerRepository;
2833
private readonly IGenericAttributeService _genericAttributeService;
2934
private readonly ICustomerService _customerService;
35+
private readonly Lazy<UrlHelper> _urlHelper;
36+
private readonly Lazy<IUrlRecordService> _urlRecordService;
3037
private readonly ICommonServices _services;
3138

3239
public ForumService(
@@ -42,6 +49,8 @@ public ForumService(
4249
IRepository<Customer> customerRepository,
4350
IGenericAttributeService genericAttributeService,
4451
ICustomerService customerService,
52+
Lazy<UrlHelper> urlHelper,
53+
Lazy<IUrlRecordService> urlRecordService,
4554
ICommonServices services)
4655
{
4756
_forumGroupRepository = forumGroupRepository;
@@ -56,6 +65,8 @@ public ForumService(
5665
_customerRepository = customerRepository;
5766
_genericAttributeService = genericAttributeService;
5867
_customerService = customerService;
68+
_urlHelper = urlHelper;
69+
_urlRecordService = urlRecordService;
5970
_services = services;
6071
}
6172

@@ -983,5 +994,134 @@ public virtual bool IsCustomerAllowedToSubscribe(Customer customer)
983994
}
984995

985996
#endregion
997+
998+
#region XML Sitemap
999+
1000+
public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context)
1001+
{
1002+
if (!context.LoadSetting<SeoSettings>().XmlSitemapIncludesForum || !context.LoadSetting<ForumSettings>().ForumsEnabled)
1003+
return null;
1004+
1005+
return new ForumXmlSitemapResult(context, this, _urlHelper.Value, _urlRecordService.Value);
1006+
}
1007+
1008+
class ForumXmlSitemapResult : XmlSitemapProvider
1009+
{
1010+
private readonly IForumService _forumService;
1011+
private readonly XmlSitemapBuildContext _context;
1012+
private readonly UrlHelper _urlHelper;
1013+
private readonly IUrlRecordService _urlRecordService;
1014+
1015+
private readonly IList<ForumGroup> _groups;
1016+
private readonly IList<Forum> _forums;
1017+
private readonly IQueryable<ForumTopic> _topicsQuery;
1018+
1019+
public ForumXmlSitemapResult(
1020+
XmlSitemapBuildContext context,
1021+
IForumService forumService,
1022+
UrlHelper urlHelper,
1023+
IUrlRecordService urlRecordService)
1024+
{
1025+
_forumService = forumService;
1026+
_context = context;
1027+
_urlHelper = urlHelper;
1028+
_urlRecordService = urlRecordService;
1029+
1030+
_groups = _forumService.GetAllForumGroups(context.RequestStoreId, false);
1031+
_forums = _groups.SelectMany(x => x.Forums).ToList();
1032+
_topicsQuery = _forumService.GetAllTopics(0, 0, int.MaxValue, false).SourceQuery;
1033+
}
1034+
1035+
public override int GetTotalCount()
1036+
{
1037+
// INFO: we gonna create nodes for all groups, forums within groups and all topics
1038+
return _groups.Count + _forums.Count + _topicsQuery.Count();
1039+
}
1040+
1041+
public override XmlSitemapNode CreateNode(UrlHelper urlHelper, string baseUrl, NamedEntity entity, UrlRecordCollection slugs, Language language)
1042+
{
1043+
var path = string.Empty;
1044+
1045+
switch (entity.EntityName)
1046+
{
1047+
case nameof(ForumGroup):
1048+
path = urlHelper.RouteUrl("ForumGroupSlug", new { id = entity.Id, slug = slugs.GetSlug(language.Id, entity.Id, true) });
1049+
break;
1050+
case nameof(Forum):
1051+
path = urlHelper.RouteUrl("ForumSlug", new { id = entity.Id, slug = slugs.GetSlug(language.Id, entity.Id, true) });
1052+
break;
1053+
case nameof(ForumTopic):
1054+
path = urlHelper.RouteUrl("TopicSlug", new { id = entity.Id, slug = entity.Slug });
1055+
break;
1056+
}
1057+
1058+
if (path.HasValue())
1059+
{
1060+
return new XmlSitemapNode
1061+
{
1062+
LastMod = entity.LastMod,
1063+
Loc = baseUrl + path.TrimStart('/')
1064+
};
1065+
}
1066+
1067+
return null;
1068+
}
1069+
1070+
public override IEnumerable<NamedEntity> Enlist()
1071+
{
1072+
// Enlist forum groups
1073+
foreach (var group in _groups)
1074+
{
1075+
yield return new NamedEntity { EntityName = nameof(ForumGroup), Id = group.Id, LastMod = group.UpdatedOnUtc };
1076+
}
1077+
1078+
// Enlist forums
1079+
foreach (var forum in _forums)
1080+
{
1081+
yield return new NamedEntity { EntityName = nameof(Forum), Id = forum.Id, LastMod = forum.UpdatedOnUtc };
1082+
}
1083+
1084+
// Enlist topics
1085+
var query = _topicsQuery.AsNoTracking();
1086+
var maxId = int.MaxValue;
1087+
1088+
while (maxId > 1)
1089+
{
1090+
if (_context.CancellationToken.IsCancellationRequested)
1091+
{
1092+
break;
1093+
}
1094+
1095+
var topics = query
1096+
.Where(x => x.Id < maxId)
1097+
.OrderByDescending(x => x.Id)
1098+
.Take(() => _context.MaximumNodeCount)
1099+
.Select(x => new { x.Id, x.UpdatedOnUtc, x.Subject })
1100+
.ToList();
1101+
1102+
if (topics.Count == 0)
1103+
{
1104+
break;
1105+
}
1106+
1107+
maxId = topics.Last().Id;
1108+
1109+
foreach (var x in topics)
1110+
{
1111+
yield return new NamedEntity
1112+
{
1113+
EntityName = nameof(ForumTopic),
1114+
Slug = (new ForumTopic { Subject = x.Subject }).GetSeName(),
1115+
Id = x.Id,
1116+
LastMod = x.UpdatedOnUtc
1117+
};
1118+
}
1119+
}
1120+
}
1121+
1122+
public override int Order => 1000;
1123+
}
1124+
1125+
#endregion
9861126
}
9871127
}

src/Libraries/SmartStore.Services/News/NewsService.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,16 +141,16 @@ public virtual void UpdateCommentTotals(NewsItem newsItem)
141141

142142
#region XML Sitemap
143143

144-
public XmlSitemapResult PublishXmlSitemap(XmlSitemapBuildContext context)
144+
public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context)
145145
{
146-
if (!context.LoadSetting<SeoSettings>().XmlSitemapIncludesNews)
146+
if (!context.LoadSetting<SeoSettings>().XmlSitemapIncludesNews || !context.LoadSetting<NewsSettings>().Enabled)
147147
return null;
148148

149149
var query = GetAllNews(0, context.RequestStoreId, 0, int.MaxValue).SourceQuery;
150150
return new NewsXmlSitemapResult { Query = query };
151151
}
152152

153-
class NewsXmlSitemapResult : XmlSitemapResult
153+
class NewsXmlSitemapResult : XmlSitemapProvider
154154
{
155155
public IQueryable<NewsItem> Query { get; set; }
156156

src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchService.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ public IQueryable<Product> PrepareQuery(CatalogSearchQuery searchQuery, IQueryab
255255

256256
#region XML Sitemap
257257

258-
public XmlSitemapResult PublishXmlSitemap(XmlSitemapBuildContext context)
258+
public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context)
259259
{
260260
if (!context.LoadSetting<SeoSettings>().XmlSitemapIncludesProducts)
261261
return null;
@@ -270,7 +270,7 @@ public XmlSitemapResult PublishXmlSitemap(XmlSitemapBuildContext context)
270270
return new ProductXmlSitemapResult { Query = query, Context = context };
271271
}
272272

273-
class ProductXmlSitemapResult : XmlSitemapResult
273+
class ProductXmlSitemapResult : XmlSitemapProvider
274274
{
275275
public IQueryable<Product> Query { get; set; }
276276
public XmlSitemapBuildContext Context { get; set; }
@@ -288,6 +288,11 @@ public override IEnumerable<NamedEntity> Enlist()
288288
//var limit = 0;
289289
while (maxId > 1)
290290
{
291+
if (Context.CancellationToken.IsCancellationRequested)
292+
{
293+
break;
294+
}
295+
291296
var products = query
292297
.Where(x => x.Id < maxId)
293298
.OrderByDescending(x => x.Id)

src/Libraries/SmartStore.Services/Search/Forum/ForumSearchService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
namespace SmartStore.Services.Search
1313
{
1414
public partial class ForumSearchService : SearchServiceBase, IForumSearchService
15-
{
15+
{
1616
private readonly ICommonServices _services;
1717
private readonly IIndexManager _indexManager;
1818
private readonly Lazy<IForumService> _forumService;

src/Libraries/SmartStore.Services/Seo/IXmlSitemapPublisher.cs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,47 @@
33
using System.Linq;
44
using System.Text;
55
using System.Threading.Tasks;
6+
using System.Web.Mvc;
7+
using SmartStore.Core.Domain.Localization;
8+
using SmartStore.Core.Domain.Seo;
69

710
namespace SmartStore.Services.Seo
811
{
912
public partial interface IXmlSitemapPublisher
1013
{
11-
XmlSitemapResult PublishXmlSitemap(XmlSitemapBuildContext context);
14+
XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context);
1215
}
1316

14-
public abstract class XmlSitemapResult
17+
public class XmlSitemapProvider
1518
{
16-
public abstract int GetTotalCount();
17-
public abstract IEnumerable<NamedEntity> Enlist();
19+
public virtual int GetTotalCount()
20+
{
21+
return 0;
22+
}
23+
24+
public virtual IEnumerable<NamedEntity> Enlist()
25+
{
26+
return Enumerable.Empty<NamedEntity>();
27+
}
28+
29+
public virtual IEnumerable<XmlSitemapNode> EnlistNodes(Language language)
30+
{
31+
return Enumerable.Empty<XmlSitemapNode>();
32+
}
33+
34+
public virtual XmlSitemapNode CreateNode(UrlHelper urlHelper, string baseUrl, NamedEntity entity, UrlRecordCollection slugs, Language language)
35+
{
36+
var slug = slugs.GetSlug(language.Id, entity.Id, true);
37+
var path = urlHelper.RouteUrl(entity.EntityName, new { SeName = slug }).TrimStart('/');
38+
var loc = baseUrl + path;
39+
40+
return new XmlSitemapNode
41+
{
42+
LastMod = entity.LastMod,
43+
Loc = loc
44+
};
45+
}
46+
1847
public virtual int Order { get; }
1948
}
2049
}

src/Libraries/SmartStore.Services/Seo/NamedEntity.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ namespace SmartStore.Services.Seo
77
public class NamedEntity : BaseEntity, ISlugSupported
88
{
99
public string EntityName { get; set; }
10+
public string DisplayName { get; set; }
11+
public string Slug { get; set; }
1012
public DateTime LastMod { get; set; }
1113
public int? LanguageId { get; set; }
1214

0 commit comments

Comments
 (0)