Skip to content

Commit 4fcc670

Browse files
committed
Theming (facets): active filters & facet url helper
1 parent 0899d89 commit 4fcc670

8 files changed

Lines changed: 304 additions & 33 deletions

File tree

src/Libraries/SmartStore.Core/Collections/Querystring.cs

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,18 @@ public QueryString(string queryString)
1919
FillFromString(queryString);
2020
}
2121

22-
public static QueryString Current
22+
public QueryString(NameValueCollection queryString)
23+
: base(queryString)
24+
{
25+
}
26+
27+
public static QueryString Current
2328
{
2429
get { return new QueryString().FromCurrent(); }
2530
}
2631

2732
/// <summary>
28-
/// extracts a querystring from a full URL
33+
/// Extracts a querystring from a full URL
2934
/// </summary>
3035
/// <param name="s">the string to extract the querystring from</param>
3136
/// <returns>a string representing only the querystring</returns>
@@ -94,7 +99,7 @@ public QueryString FromCurrent()
9499
}
95100

96101
/// <summary>
97-
/// adds a name value pair to the collection
102+
/// Adds a name value pair to the collection
98103
/// </summary>
99104
/// <param name="name">the name</param>
100105
/// <param name="value">the value associated to the name</param>
@@ -115,15 +120,16 @@ public QueryString Add(string name, string value, bool isUnique)
115120
{
116121
base[name] += "," + HttpUtility.UrlEncode(value);
117122
}
123+
118124
return this;
119125
}
120126

121-
/// <summary>
122-
/// removes a name value pair from the querystring collection
123-
/// </summary>
124-
/// <param name="name">name of the querystring value to remove</param>
125-
/// <returns>the QueryString object</returns>
126-
public new QueryString Remove(string name)
127+
/// <summary>
128+
/// Removes a name value pair from the querystring collection
129+
/// </summary>
130+
/// <param name="name">name of the querystring value to remove</param>
131+
/// <returns>the QueryString object</returns>
132+
public new QueryString Remove(string name)
127133
{
128134
string existingValue = base[name];
129135
if (!string.IsNullOrEmpty(existingValue))
@@ -175,24 +181,46 @@ public bool Contains(string name)
175181
}
176182

177183
/// <summary>
178-
/// outputs the querystring object to a string
184+
/// Outputs the querystring object to a string
179185
/// </summary>
180186
/// <returns>the encoded querystring as it would appear in a browser</returns>
181187
public override string ToString()
182188
{
183-
var builder = new StringBuilder();
184-
for (var i = 0; i < base.Keys.Count; i++)
185-
{
186-
if (!string.IsNullOrEmpty(base.Keys[i]))
187-
{
188-
foreach (string val in base[base.Keys[i]].EmptyNull().Split(','))
189-
{
190-
builder.Append((builder.Length == 0) ? "?" : "&").Append(
191-
HttpUtility.UrlEncode(base.Keys[i])).Append("=").Append(val);
192-
}
193-
}
194-
}
195-
return builder.ToString();
189+
return ToString(true);
196190
}
197-
}
191+
192+
/// <summary>
193+
/// Outputs the querystring object to a string
194+
/// </summary>
195+
/// <param name="splitValues">Whether to create entries for each comma-separated value</param>
196+
/// <returns>the encoded querystring as it would appear in a browser</returns>
197+
public string ToString(bool splitValues)
198+
{
199+
var builder = new StringBuilder();
200+
for (var i = 0; i < base.Keys.Count; i++)
201+
{
202+
var key = base.Keys[i];
203+
var value = base[key];
204+
205+
if (!string.IsNullOrEmpty(key))
206+
{
207+
builder.Append((builder.Length == 0) ? "?" : "&");
208+
209+
if (splitValues)
210+
{
211+
foreach (string val in value.EmptyNull().Split(','))
212+
{
213+
builder.Append(HttpUtility.UrlEncode(key)).Append("=").Append(val);
214+
}
215+
}
216+
else
217+
{
218+
builder.Append(HttpUtility.UrlEncode(key)).Append("=").Append(value);
219+
}
220+
}
221+
}
222+
223+
return builder.ToString();
224+
}
225+
}
198226
}

src/Libraries/SmartStore.Core/Search/Facets/FacetGroup.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,22 @@
33

44
namespace SmartStore.Core.Search.Facets
55
{
6+
public enum FacetGroupKind
7+
{
8+
Unknown = -1,
9+
Category,
10+
Brand,
11+
Price,
12+
Rating,
13+
DeliveryTime,
14+
Attribute,
15+
Variant
16+
}
17+
618
public class FacetGroup
719
{
820
private readonly Dictionary<string, Facet> _facets;
21+
private FacetGroupKind? _kind;
922

1023
public FacetGroup(
1124
string key,
@@ -78,5 +91,49 @@ public Facet GetFacet(string key)
7891

7992
return _facets.Get(key);
8093
}
94+
95+
public FacetGroupKind Kind
96+
{
97+
get
98+
{
99+
if (_kind == null)
100+
{
101+
if (Key.StartsWith("attrid"))
102+
{
103+
_kind = FacetGroupKind.Attribute;
104+
}
105+
else if (Key.StartsWith("variantid"))
106+
{
107+
_kind = FacetGroupKind.Variant;
108+
}
109+
else if (Key == "categoryid" || Key == "notfeaturedcategoryid")
110+
{
111+
_kind = FacetGroupKind.Category;
112+
}
113+
else if (Key == "manufacturerid")
114+
{
115+
_kind = FacetGroupKind.Brand;
116+
}
117+
else if (Key == "price")
118+
{
119+
_kind = FacetGroupKind.Price;
120+
}
121+
else if (Key == "rate")
122+
{
123+
_kind = FacetGroupKind.Rating;
124+
}
125+
else if (Key == "deliveryid")
126+
{
127+
_kind = FacetGroupKind.DeliveryTime;
128+
}
129+
else
130+
{
131+
_kind = FacetGroupKind.Unknown;
132+
}
133+
}
134+
135+
return _kind.Value;
136+
}
137+
}
81138
}
82139
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.Specialized;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Web;
7+
using SmartStore.Collections;
8+
using SmartStore.Core;
9+
using SmartStore.Core.Search.Facets;
10+
using SmartStore.Services.Search.Modelling;
11+
12+
namespace SmartStore.Services.Search.Extensions
13+
{
14+
public class FacetUrlHelper
15+
{
16+
private readonly ICatalogSearchQueryAliasMapper _mapper;
17+
private readonly IWorkContext _workContext;
18+
private readonly HttpRequestBase _httpRequest;
19+
20+
private readonly int _languageId;
21+
private readonly string _url;
22+
private readonly QueryString _initialQuery;
23+
24+
public FacetUrlHelper(
25+
ICatalogSearchQueryAliasMapper mapper,
26+
IWorkContext workContext,
27+
HttpRequestBase httpRequest)
28+
{
29+
_mapper = mapper;
30+
_workContext = workContext;
31+
_httpRequest = httpRequest;
32+
33+
_languageId = _workContext.WorkingLanguage.Id;
34+
_url = _httpRequest.CurrentExecutionFilePath;
35+
//_initialQuery = new QueryString().FillFromString(_httpRequest.QueryString.ToString(), false);
36+
_initialQuery = QueryString.Current;
37+
}
38+
39+
public string Toggle(Facet facet)
40+
{
41+
if (facet.Value.IsSelected)
42+
{
43+
return Remove(facet);
44+
}
45+
else
46+
{
47+
return Add(facet);
48+
}
49+
}
50+
51+
public string Add(params Facet[] facets)
52+
{
53+
var qs = new QueryString(_initialQuery);
54+
55+
foreach (var facet in facets)
56+
{
57+
var parts = GetQueryParts(facet);
58+
foreach (var name in parts.AllKeys)
59+
{
60+
qs.Add(name, parts[name], !facet.FacetGroup.IsMultiSelect);
61+
}
62+
}
63+
64+
return _url + qs.ToString(false);
65+
}
66+
67+
public string Remove(params Facet[] facets)
68+
{
69+
var qs = new QueryString(_initialQuery);
70+
71+
foreach (var facet in facets)
72+
{
73+
var parts = GetQueryParts(facet);
74+
foreach (var name in parts.AllKeys)
75+
{
76+
var currentValues = qs.Get(name).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToArray();
77+
qs.Remove(name);
78+
79+
if (currentValues != null)
80+
{
81+
var removeValues = parts.GetValues(name);
82+
var newValues = currentValues.Except(removeValues).ToArray();
83+
if (newValues.Length > 0)
84+
{
85+
newValues.Each(x => qs.Add(name, x, false));
86+
}
87+
}
88+
}
89+
}
90+
91+
return _url + qs.ToString(false);
92+
}
93+
94+
protected virtual NameValueCollection GetQueryParts(Facet facet)
95+
{
96+
var group = facet.FacetGroup;
97+
98+
// TODO: (mc) > (mg) Implement alias mapping for common facet groups like category, price etc.
99+
100+
string name = null;
101+
string value = null;
102+
int entityId;
103+
104+
var result = new NameValueCollection(2);
105+
106+
if (group.Kind == FacetGroupKind.Attribute)
107+
{
108+
// TODO: (mc) > (mh) Handle range type attributes also!
109+
entityId = facet.Value.Value.Convert<int>();
110+
name = _mapper.GetAttributeAliasById(facet.Value.ParentId, _languageId) ?? "attr" + facet.Value.ParentId;
111+
value = _mapper.GetAttributeOptionAliasById(entityId, _languageId) ?? "opt" + entityId;
112+
result.Add(name, value);
113+
}
114+
else if (group.Kind == FacetGroupKind.Variant)
115+
{
116+
entityId = facet.Value.Value.Convert<int>();
117+
name = _mapper.GetVariantAliasById(facet.Value.ParentId, _languageId) ?? "vari" + facet.Value.ParentId;
118+
value = _mapper.GetVariantOptionAliasById(entityId, _languageId) ?? "opt" + entityId;
119+
result.Add(name, value);
120+
}
121+
else if (group.Kind == FacetGroupKind.Category)
122+
{
123+
result.Add("c", facet.Value.Value.ToString());
124+
}
125+
else if (group.Kind == FacetGroupKind.Brand)
126+
{
127+
result.Add("m", facet.Value.Value.ToString());
128+
}
129+
else if (group.Kind == FacetGroupKind.Rating)
130+
{
131+
result.Add("r", facet.Value.Value.ToString());
132+
}
133+
else if (group.Kind == FacetGroupKind.DeliveryTime)
134+
{
135+
result.Add("d", facet.Value.Value.ToString());
136+
}
137+
else if (group.Kind == FacetGroupKind.Price)
138+
{
139+
// TODO: (mc) handle price properly
140+
result.Add("pt", facet.Value.UpperValue.ToString());
141+
}
142+
143+
return result;
144+
}
145+
}
146+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.Web.Mvc;
3+
using SmartStore.Core.Infrastructure;
4+
using SmartStore.Core.Search.Facets;
5+
6+
namespace SmartStore.Services.Search.Extensions
7+
{
8+
public static class UrlHelperExtensions
9+
{
10+
public static string FacetToggle(this UrlHelper urlHelper, Facet facet)
11+
{
12+
var facetUrlHelper = EngineContext.Current.Resolve<FacetUrlHelper>();
13+
return facetUrlHelper.Toggle(facet);
14+
}
15+
16+
public static string FacetAdd(this UrlHelper urlHelper, params Facet[] facets)
17+
{
18+
var facetUrlHelper = EngineContext.Current.Resolve<FacetUrlHelper>();
19+
return facetUrlHelper.Add(facets);
20+
}
21+
22+
public static string FacetRemove(this UrlHelper urlHelper, params Facet[] facets)
23+
{
24+
var facetUrlHelper = EngineContext.Current.Resolve<FacetUrlHelper>();
25+
return facetUrlHelper.Remove(facets);
26+
}
27+
}
28+
}

src/Libraries/SmartStore.Services/SmartStore.Services.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,11 +489,13 @@
489489
<Compile Include="Hooks\AclEntityHook.cs" />
490490
<Compile Include="ScopedServiceBase.cs" />
491491
<Compile Include="Search\CatalogSearchQuery.cs" />
492+
<Compile Include="Search\Extensions\UrlHelperExtensions.cs" />
492493
<Compile Include="Search\Modelling\CatalogSearchQueryAliasMapper.cs" />
493494
<Compile Include="Search\Modelling\CatalogSearchQueryFactory.cs" />
494495
<Compile Include="Search\CatalogSearchResult.cs" />
495496
<Compile Include="Search\CatalogSearchService.cs" />
496497
<Compile Include="Search\Modelling\CatalogSearchQueryModelBinder.cs" />
498+
<Compile Include="Search\Extensions\FacetUrlHelper.cs" />
497499
<Compile Include="Search\Modelling\ICatalogSearchQueryAliasMapper.cs" />
498500
<Compile Include="Search\Modelling\ICatalogSearchQueryFactory.cs" />
499501
<Compile Include="Search\ICatalogSearchService.cs" />

src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
using SmartStore.Services.Pdf;
6565
using SmartStore.Services.Polls;
6666
using SmartStore.Services.Search;
67+
using SmartStore.Services.Search.Extensions;
6768
using SmartStore.Services.Search.Modelling;
6869
using SmartStore.Services.Security;
6970
using SmartStore.Services.Seo;
@@ -265,6 +266,7 @@ protected override void Load(ContainerBuilder builder)
265266
builder.RegisterType<LinqCatalogSearchService>().Named<ICatalogSearchService>("linq").InstancePerRequest();
266267
builder.RegisterType<CatalogSearchQueryFactory>().As<ICatalogSearchQueryFactory>().InstancePerRequest();
267268
builder.RegisterType<CatalogSearchQueryAliasMapper>().As<ICatalogSearchQueryAliasMapper>().InstancePerRequest();
269+
builder.RegisterType<FacetUrlHelper>().InstancePerRequest();
268270
}
269271

270272
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)

0 commit comments

Comments
 (0)