Skip to content

Commit 2073fc7

Browse files
committed
2 parents 54c9e2a + e16fe3b commit 2073fc7

File tree

4 files changed

+98
-89
lines changed

4 files changed

+98
-89
lines changed

PLATFORM/GlobalAssemblyInfo.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
[assembly: AssemblyCompany("VirtoCommerce")]
1212
[assembly: AssemblyProduct("Virto Commerce 2.9")]
1313
[assembly: AssemblyCopyright("Copyright © VirtoCommerce 2011-2016")]
14-
[assembly: AssemblyFileVersion("2.9.1321.0")]
14+
[assembly: AssemblyFileVersion("2.9.1323.0")]
1515
[assembly: AssemblyVersion("2.9.0.0")]
1616
[assembly: AssemblyInformationalVersion("2.9")]
1717

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Virto Commerce 2.x
44
| Branch | Status |
55
| ------------- | ------------- |
66
| [Master - releases](https://github.com/VirtoCommerce/vc-community) | [![Build Status](http://ci.virtocommerce.com:8080/buildStatus/icon?job=VirtoCommerce 2.x CI Build - master)](http://ci.virtocommerce.com:8080/job/VirtoCommerce 2.x CI Build - master) |
7-
| [Development / Integration](https://github.com/VirtoCommerce/vc-community/tree/dev) | [![Build Status](http://ci.virtocommerce.com:8080/buildStatus/icon?job=VirtoCommerce 2.x CI Build)](http://ci.virtocommerce.com:8080/job/VirtoCommerce 2.x CI Build) |
7+
| [Development / Integration](https://github.com/VirtoCommerce/vc-community/tree/dev) | [![Build Status](http://ci.virtocommerce.com:8080/buildStatus/icon?job=VirtoCommerce 2.x CI Build - dev)](http://ci.virtocommerce.com:8080/job/VirtoCommerce 2.x CI Build - dev) |
88
| Feature (on going feature dev) | [![Build Status](http://ci.virtocommerce.com:8080/buildStatus/icon?job=VirtoCommerce 2.x CI Build - branches)](http://ci.virtocommerce.com:8080/job/VirtoCommerce 2.x CI Build - branches) |
99

1010
Virto Commerce is the second generation release and is the only enterprise level e-commerce product fully available under Open Source license. Virto Commerce is based on .NET 4.5 with extensive use of MVC, IoC, EF, Azure, Angular JS and many other cutting edge technologies. It can be deployed in Microsoft Cloud (Azure), Amazon Web Services (AWS) and on-premise. Mobile App Starter built using Ionic Framework is also available.

STOREFRONT/VirtoCommerce.Storefront/App_Start/RouteConfig.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace VirtoCommerce.Storefront
1212
{
1313
public class RouteConfig
1414
{
15-
15+
1616
public static void RegisterRoutes(RouteCollection routes, Func<WorkContext> workContextFactory, ICommerceCoreModuleApi commerceCoreApi, IStaticContentService staticContentService, ICacheManager<object> cacheManager)
1717
{
1818
routes.IgnoreRoute("favicon.ico");
@@ -56,7 +56,7 @@ public static void RegisterRoutes(RouteCollection routes, Func<WorkContext> work
5656
routes.MapLocalizedStorefrontRoute("API.Marketing.GetDynamicContent", "storefrontapi/marketing/dynamiccontent/{placeName}", defaults: new { controller = "ApiMarketing", action = "GetDynamicContent" });
5757
//Account API
5858
routes.MapLocalizedStorefrontRoute("API.Account.GetCurrentCustomer", "storefrontapi/account", defaults: new { controller = "ApiAccount", action = "GetCurrentCustomer" });
59-
59+
6060
//Quote requests API
6161
routes.MapLocalizedStorefrontRoute("API.QuoteRequest.GetItemsCount", "storefrontapi/quoterequests/{number}/itemscount", defaults: new { controller = "ApiQuoteRequest", action = "GetItemsCount" });
6262
routes.MapLocalizedStorefrontRoute("API.QuoteRequest.Get", "storefrontapi/quoterequests/{number}", defaults: new { controller = "ApiQuoteRequest", action = "Get" });
@@ -104,7 +104,7 @@ public static void RegisterRoutes(RouteCollection routes, Func<WorkContext> work
104104
routes.MapLocalizedStorefrontRoute("ShopifyCart.UpdateJs", "cart/update.js", defaults: new { controller = "ShopifyCompatibility", action = "UpdateJs" });
105105

106106
// QuoteRequest
107-
107+
108108
routes.MapLocalizedStorefrontRoute("QuoteRequest.CurrentQuoteRequest", "quoterequest", defaults: new { controller = "QuoteRequest", action = "CurrentQuoteRequest" });
109109
routes.MapLocalizedStorefrontRoute("Account.QuoteRequests", "account/quoterequests", defaults: new { controller = "QuoteRequest", action = "QuoteRequests" });
110110
routes.MapLocalizedStorefrontRoute("Account.QuoteRequestByNumber", "quoterequest/{number}", defaults: new { controller = "QuoteRequest", action = "QuoteRequestByNumber" });
@@ -119,7 +119,7 @@ public static void RegisterRoutes(RouteCollection routes, Func<WorkContext> work
119119
routes.MapLocalizedStorefrontRoute("Common.NoStore", "common/nostore", defaults: new { controller = "Common", action = "NoStore" });
120120
routes.MapLocalizedStorefrontRoute("Common.Maintenance", "maintenance", defaults: new { controller = "Common", action = "Maintenance" });
121121

122-
122+
123123
//Product routes
124124
routes.MapLocalizedStorefrontRoute("Product.GetProduct", "product/{productId}", defaults: new { controller = "Product", action = "ProductDetails" });
125125
routes.MapLocalizedStorefrontRoute("Product.GetProductJson", "product/{productId}/json", defaults: new { controller = "Product", action = "ProductDetailsJson" });
@@ -132,9 +132,9 @@ public static void RegisterRoutes(RouteCollection routes, Func<WorkContext> work
132132
routes.MapLocalizedStorefrontRoute("Blogs.GetBlog", "blogs/{blog}", defaults: new { controller = "Blog", action = "GetBlog" });
133133
routes.MapLocalizedStorefrontRoute("Blogs.GetBlogArticle", "blogs/{blog}/{article}", defaults: new { controller = "Blog", action = "GetBlogArticle" });
134134

135-
Func<string, Route> seoRouteFactory = url => new SeoRoute(url, new MvcRouteHandler(), workContextFactory, commerceCoreApi, staticContentService, cacheManager);
135+
Func<string, Route> seoRouteFactory = url => new SeoRoute(url, new MvcRouteHandler(), workContextFactory, commerceCoreApi, cacheManager);
136136
routes.MapLocalizedStorefrontRoute(name: "SeoRoute", url: "{*path}", defaults: new { controller = "StorefrontHome", action = "Index" }, constraints: null, routeFactory: seoRouteFactory);
137-
137+
138138
}
139139
}
140140
}

STOREFRONT/VirtoCommerce.Storefront/Routing/SeoRoute.cs

+90-81
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
using VirtoCommerce.Client.Model;
99
using VirtoCommerce.Storefront.Common;
1010
using VirtoCommerce.Storefront.Model;
11-
using VirtoCommerce.Storefront.Model.Catalog;
1211
using VirtoCommerce.Storefront.Model.Common;
13-
using VirtoCommerce.Storefront.Model.Services;
1412
using VirtoCommerce.Storefront.Model.StaticContent;
1513

1614
namespace VirtoCommerce.Storefront.Routing
@@ -19,15 +17,13 @@ public class SeoRoute : Route
1917
{
2018
private readonly Func<WorkContext> _workContextFactory;
2119
private readonly ICommerceCoreModuleApi _commerceCoreApi;
22-
private readonly IStaticContentService _contentService;
2320
private readonly ICacheManager<object> _cacheManager;
2421

25-
public SeoRoute(string url, IRouteHandler routeHandler, Func<WorkContext> workContextFactory, ICommerceCoreModuleApi commerceCoreApi, IStaticContentService staticContentService, ICacheManager<object> cacheManager)
22+
public SeoRoute(string url, IRouteHandler routeHandler, Func<WorkContext> workContextFactory, ICommerceCoreModuleApi commerceCoreApi, ICacheManager<object> cacheManager)
2623
: base(url, routeHandler)
2724
{
2825
_workContextFactory = workContextFactory;
2926
_commerceCoreApi = commerceCoreApi;
30-
_contentService = staticContentService;
3127
_cacheManager = cacheManager;
3228
}
3329

@@ -43,108 +39,124 @@ public override RouteData GetRouteData(HttpContextBase httpContext)
4339
var path = data.Values["path"] as string;
4440
var store = data.Values["store"] as string;
4541
//Special workaround for case when url contains only slug without store (one store case)
46-
if(string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(store) && workContext.AllStores != null)
42+
if (string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(store) && workContext.AllStores != null)
4743
{
48-
//use {store} as {path} if not exist any store with name {store}
49-
path = workContext.AllStores.Any(x => string.Equals(store, x.Id, StringComparison.InvariantCultureIgnoreCase)) ? null : store;
44+
//use {store} as {path} if not exist any store with name {store}
45+
path = workContext.AllStores.Any(x => string.Equals(store, x.Id, StringComparison.InvariantCultureIgnoreCase)) ? null : store;
5046
}
5147
//Get all seo records for requested slug and also all other seo records with different slug and languages but related to same object
5248
// GetSeoRecords('A') returns
5349
// { objectType: 'Product', objectId: '1', SemanticUrl: 'A', Language: 'en-us', active : false }
5450
// { objectType: 'Product', objectId: '1', SemanticUrl: 'AA', Language: 'en-us', active : true }
5551
var seoRecords = GetSeoRecords(path);
56-
var seoRecord = seoRecords.Where(x => path.Equals(x.SemanticUrl, StringComparison.OrdinalIgnoreCase))
57-
.FindBestSeoMatch(workContext.CurrentLanguage, workContext.CurrentStore);
58-
59-
if (seoRecord != null)
52+
if (seoRecords != null)
6053
{
61-
// Ensure the slug is active
62-
if (seoRecord.IsActive == null || !seoRecord.IsActive.Value)
54+
var seoRecord = seoRecords
55+
.Where(x => string.Equals(path, x.SemanticUrl, StringComparison.OrdinalIgnoreCase))
56+
.FindBestSeoMatch(workContext.CurrentLanguage, workContext.CurrentStore);
57+
58+
if (seoRecord != null)
6359
{
64-
// Slug is not active. Try to find the active one for the same entity and language.
65-
seoRecord = seoRecords.Where(x=>x.ObjectType == seoRecord.ObjectType && x.ObjectId == seoRecord.ObjectId && x.IsActive != null && x.IsActive.Value)
66-
.FindBestSeoMatch(workContext.CurrentLanguage, workContext.CurrentStore);
67-
68-
if (seoRecord == null)
60+
// Ensure the slug is active
61+
if (seoRecord.IsActive == null || !seoRecord.IsActive.Value)
6962
{
70-
// No active slug found
71-
data.Values["controller"] = "Error";
72-
data.Values["action"] = "Http404";
63+
// Slug is not active. Try to find the active one for the same entity and language.
64+
seoRecord = seoRecords.Where(x => x.ObjectType == seoRecord.ObjectType && x.ObjectId == seoRecord.ObjectId && x.IsActive != null && x.IsActive.Value)
65+
.FindBestSeoMatch(workContext.CurrentLanguage, workContext.CurrentStore);
66+
67+
if (seoRecord == null)
68+
{
69+
// No active slug found
70+
data.Values["controller"] = "Error";
71+
data.Values["action"] = "Http404";
72+
}
73+
else
74+
{
75+
// The active slug is found
76+
var response = httpContext.Response;
77+
response.Status = "301 Moved Permanently";
78+
response.RedirectLocation = string.Format("{0}{1}", workContext.CurrentStore.Url, seoRecord.SemanticUrl);
79+
response.End();
80+
data = null;
81+
}
7382
}
7483
else
7584
{
76-
// The active slug is found
77-
var response = httpContext.Response;
78-
response.Status = "301 Moved Permanently";
79-
response.RedirectLocation = string.Format("{0}{1}", workContext.CurrentStore.Url, seoRecord.SemanticUrl);
80-
response.End();
81-
data = null;
85+
// Redirect to the slug for the current language if it differs from the requested slug
86+
var actualActiveSeoRecord = seoRecords.Where(x => x.ObjectType == seoRecord.ObjectType && x.ObjectId == seoRecord.ObjectId && x.IsActive != null && x.IsActive.Value)
87+
.FindBestSeoMatch(workContext.CurrentLanguage, workContext.CurrentStore);
88+
//If actual seo different that requested need redirect 302
89+
if (!string.Equals(actualActiveSeoRecord.SemanticUrl, seoRecord.SemanticUrl, StringComparison.OrdinalIgnoreCase))
90+
{
91+
var response = httpContext.Response;
92+
response.Status = "302 Moved Temporarily";
93+
response.RedirectLocation = string.Concat(workContext.CurrentStore.Url, actualActiveSeoRecord.SemanticUrl);
94+
response.End();
95+
data = null;
96+
}
97+
else
98+
{
99+
// Process the URL
100+
switch (seoRecord.ObjectType)
101+
{
102+
case "CatalogProduct":
103+
data.Values["controller"] = "Product";
104+
data.Values["action"] = "ProductDetails";
105+
data.Values["productId"] = seoRecord.ObjectId;
106+
break;
107+
case "Category":
108+
data.Values["controller"] = "CatalogSearch";
109+
data.Values["action"] = "CategoryBrowsing";
110+
data.Values["categoryId"] = seoRecord.ObjectId;
111+
break;
112+
}
113+
}
82114
}
83115
}
84-
else
116+
else if (!string.IsNullOrEmpty(path))
85117
{
86-
// Redirect to the slug for the current language if it differs from the requested slug
87-
var actualActiveSeoRecord = seoRecords.Where(x => x.ObjectType == seoRecord.ObjectType && x.ObjectId == seoRecord.ObjectId && x.IsActive != null && x.IsActive.Value)
88-
.FindBestSeoMatch(workContext.CurrentLanguage, workContext.CurrentStore);
89-
//If actual seo different that requested need redirect 302
90-
if (!string.Equals(actualActiveSeoRecord.SemanticUrl, seoRecord.SemanticUrl, StringComparison.OrdinalIgnoreCase))
118+
var contentPage = TryToFindContentPageWithUrl(workContext, path);
119+
if (contentPage != null)
91120
{
92-
var response = httpContext.Response;
93-
response.Status = "302 Moved Temporarily";
94-
response.RedirectLocation = string.Format("{0}{1}", workContext.CurrentStore.Url, actualActiveSeoRecord.SemanticUrl);
95-
response.End();
96-
data = null;
121+
data.Values["controller"] = "Page";
122+
data.Values["action"] = "GetContentPage";
123+
data.Values["page"] = contentPage;
97124
}
98125
else
99126
{
100-
// Process the URL
101-
switch (seoRecord.ObjectType)
102-
{
103-
case "CatalogProduct":
104-
data.Values["controller"] = "Product";
105-
data.Values["action"] = "ProductDetails";
106-
data.Values["productId"] = seoRecord.ObjectId;
107-
break;
108-
case "Category":
109-
data.Values["controller"] = "CatalogSearch";
110-
data.Values["action"] = "CategoryBrowsing";
111-
data.Values["categoryId"] = seoRecord.ObjectId;
112-
break;
113-
}
127+
data.Values["controller"] = "Error";
128+
data.Values["action"] = "Http404";
114129
}
115130
}
116131
}
117-
else if(!String.IsNullOrEmpty(path))
118-
{
119-
var contentPage = TryToFindContentPageWithUrl(workContext, path);
120-
if(contentPage != null)
121-
{
122-
data.Values["controller"] = "Page";
123-
data.Values["action"] = "GetContentPage";
124-
data.Values["page"] = contentPage;
125-
}
126-
else
127-
{
128-
data.Values["controller"] = "Error";
129-
data.Values["action"] = "Http404";
130-
}
131-
}
132132
}
133133

134134
return data;
135135
}
136136

137-
private ContentItem TryToFindContentPageWithUrl(WorkContext workContext, string url)
137+
138+
private static ContentItem TryToFindContentPageWithUrl(WorkContext workContext, string url)
138139
{
139-
url = url.TrimStart('/');
140-
var pages = workContext.Pages.Where(x => string.Equals(x.Permalink, url, StringComparison.CurrentCultureIgnoreCase) || string.Equals(x.Url, url, StringComparison.InvariantCultureIgnoreCase));
141-
//Need return page with current or invariant language
142-
var retVal = pages.FirstOrDefault(x => x.Language == workContext.CurrentLanguage);
143-
if(retVal == null)
140+
ContentItem result = null;
141+
142+
if (workContext.Pages != null)
144143
{
145-
retVal = pages.FirstOrDefault(x => x.Language.IsInvariant);
144+
url = url.TrimStart('/');
145+
var pages = workContext.Pages
146+
.Where(x =>
147+
string.Equals(x.Permalink, url, StringComparison.CurrentCultureIgnoreCase) ||
148+
string.Equals(x.Url, url, StringComparison.InvariantCultureIgnoreCase))
149+
.ToList();
150+
151+
// Return page with current language or invariant language
152+
result = pages.FirstOrDefault(x => x.Language == workContext.CurrentLanguage);
153+
if (result == null)
154+
{
155+
result = pages.FirstOrDefault(x => x.Language.IsInvariant);
156+
}
146157
}
147-
return retVal;
158+
159+
return result;
148160
}
149161

150162
private List<VirtoCommerceDomainCommerceModelSeoInfo> GetSeoRecords(string path)
@@ -156,16 +168,13 @@ private List<VirtoCommerceDomainCommerceModelSeoInfo> GetSeoRecords(string path)
156168
var tokens = path.Split('/');
157169
// TODO: Store path tokens as breadcrumbs to the work context
158170
var slug = tokens.LastOrDefault();
159-
if (!String.IsNullOrEmpty(slug))
171+
if (!string.IsNullOrEmpty(slug))
160172
{
161-
seoRecords = _cacheManager.Get(string.Join(":", "CommerceGetSeoInfoBySlug", slug), "ApiRegion", () => { return _commerceCoreApi.CommerceGetSeoInfoBySlug(slug); });
173+
seoRecords = _cacheManager.Get(string.Join(":", "CommerceGetSeoInfoBySlug", slug), "ApiRegion", () => _commerceCoreApi.CommerceGetSeoInfoBySlug(slug));
162174
}
163175
}
164176

165177
return seoRecords;
166178
}
167-
168-
169-
170179
}
171180
}

0 commit comments

Comments
 (0)