Initial work on configurable search widget.

This commit is contained in:
Sipke Schoorstra
2015-04-18 19:48:25 +02:00
parent 3e6043b6d7
commit b88d08b0ff
14 changed files with 155 additions and 68 deletions

View File

@@ -30,6 +30,7 @@ namespace Orchard.Search.Controllers {
IContentManager contentManager,
ISiteService siteService,
IShapeFactory shapeFactory) {
Services = services;
_searchService = searchService;
_contentManager = contentManager;
@@ -45,23 +46,24 @@ namespace Orchard.Search.Controllers {
public ILogger Logger { get; set; }
dynamic Shape { get; set; }
public ActionResult Index(PagerParameters pagerParameters, string q = "") {
public ActionResult Index(PagerParameters pagerParameters, string searchIndex = null, string q = "") {
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
var searchSettingPart = Services.WorkContext.CurrentSite.As<SearchSettingsPart>();
var index = !String.IsNullOrWhiteSpace(searchIndex) ? searchIndex.Trim() : searchSettingPart.SearchIndex;
if (String.IsNullOrEmpty(searchSettingPart.SearchIndex)) {
Services.Notifier.Error(T("Please define a default search index"));
if (String.IsNullOrEmpty(index)) {
Services.Notifier.Error(T("Please define a default search index."));
return HttpNotFound();
}
IPageOfItems<ISearchHit> searchHits = new PageOfItems<ISearchHit>(new ISearchHit[] { });
try {
searchHits = _searchService.Query(q, pager.Page, pager.PageSize,
Services.WorkContext.CurrentSite.As<SearchSettingsPart>().FilterCulture,
searchSettingPart.SearchIndex,
searchSettingPart.GetSearchFields(),
searchHit => searchHit);
searchHits = _searchService.Query(
q, pager.Page, pager.PageSize,
Services.WorkContext.CurrentSite.As<SearchSettingsPart>().FilterCulture,
index,
searchSettingPart.GetSearchFields(index),
searchHit => searchHit);
} catch(Exception exception) {
Logger.Error(T("Invalid search query: {0}", exception.Message).Text);
Services.Notifier.Error(T("Invalid search query: {0}", exception.Message));

View File

@@ -1,19 +1,49 @@
using Orchard.ContentManagement.Drivers;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Indexing;
using Orchard.Search.Models;
using Orchard.Search.ViewModels;
namespace Orchard.Search.Drivers {
public class SearchFormPartDriver : ContentPartDriver<SearchFormPart> {
private readonly IIndexManager _indexManager;
public SearchFormPartDriver(IIndexManager indexManager) {
_indexManager = indexManager;
}
protected override DriverResult Display(SearchFormPart part, string displayType, dynamic shapeHelper) {
var model = new SearchViewModel();
return ContentShape("Parts_Search_SearchForm",
() => {
var shape = shapeHelper.Parts_Search_SearchForm();
shape.ContentPart = part;
shape.ViewModel = model;
return shape;
});
return ContentShape("Parts_Search_SearchForm", () => {
var shape = shapeHelper.Parts_Search_SearchForm();
shape.AvailableIndexes = _indexManager.GetSearchIndexProvider().List().ToList();
shape.ContentPart = part;
shape.ViewModel = model;
return shape;
});
}
protected override DriverResult Editor(SearchFormPart part, dynamic shapeHelper) {
return Editor(part, null, shapeHelper);
}
protected override DriverResult Editor(SearchFormPart part, IUpdateModel updater, dynamic shapeHelper) {
return ContentShape("Parts_Search_SearchForm_Edit", () => {
var viewModel = new SearchFormViewModel {
OverrideIndex = part.OverrideIndex,
AvailableIndexes = _indexManager.GetSearchIndexProvider().List().ToList(),
SelectedIndex = part.SelectedIndex
};
if (updater != null) {
if (updater.TryUpdateModel(viewModel, Prefix, null, new[] {"AvailableIndexes"})) {
part.OverrideIndex = viewModel.OverrideIndex;
part.SelectedIndex = viewModel.SelectedIndex;
}
}
return shapeHelper.EditorTemplate(TemplateName: "Parts/Search.SearchForm", Model: viewModel, Prefix: Prefix);
});
}
}
}

View File

@@ -33,7 +33,11 @@ namespace Orchard.Search.Helpers {
}
public static string[] GetSearchFields(this SearchSettingsPart part) {
return part.SearchFields.ContainsKey(part.SearchIndex) ? part.SearchFields[part.SearchIndex] : new string[0];
return GetSearchFields(part, part.SearchIndex);
}
public static string[] GetSearchFields(this SearchSettingsPart part, string index) {
return part.SearchFields.ContainsKey(index) ? part.SearchFields[index] : new string[0];
}
}
}

View File

@@ -5,5 +5,14 @@ namespace Orchard.Search.Models {
/// Content part for the search form widget
/// </summary>
public class SearchFormPart : ContentPart {
public bool OverrideIndex {
get { return this.Retrieve(x => x.OverrideIndex); }
set { this.Store(x => x.OverrideIndex, value); }
}
public string SelectedIndex {
get { return this.Retrieve(x => x.SelectedIndex); }
set { this.Store(x => x.SelectedIndex, value); }
}
}
}

View File

@@ -88,6 +88,7 @@
<Compile Include="Services\SearchService.cs" />
<Compile Include="Settings\ContentPickerFieldEditorEvents.cs" />
<Compile Include="Settings\ContentPickerFieldSettings.cs" />
<Compile Include="ViewModels\SearchFormViewModel.cs" />
<Compile Include="ViewModels\SearchSettingsViewModel.cs" />
<Compile Include="ViewModels\SearchResultViewModel.cs" />
<Compile Include="ViewModels\SearchViewModel.cs" />
@@ -95,9 +96,8 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Project>{2d1d92bb-4555-4cbe-8d0e-63563d6ce4c6}</Project>
<Name>Orchard.Framework</Name>
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\Orchard.MediaLibrary\Orchard.MediaLibrary.csproj">
<Project>{73a7688a-5bd3-4f7e-adfa-ce36c5a10e3b}</Project>
@@ -160,6 +160,9 @@
<SubType>Designer</SubType>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Views\EditorTemplates\Parts\Search.SearchForm.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -2,10 +2,11 @@
<Place Parts_AdminSearch_SiteSettings="Content:1"/>
<Place Parts_Search_SiteSettings="Content:1"/>
<Place Parts_Search_SearchForm="Content:1"/>
<Place Parts_Search_SearchForm_Edit="Content:1"/>
<Place
Parts_Search_MediaLibrary_Actions="Actions:6"
Parts_Search_MediaLibrary_Navigation="Navigation:1"
<Place
Parts_Search_MediaLibrary_Actions="Actions:6"
Parts_Search_MediaLibrary_Navigation="Navigation:1"
/>
</Placement>

View File

@@ -3,7 +3,6 @@ using Orchard.UI.Resources;
namespace Orchard.Search {
public class ResourceManifest : IResourceManifestProvider {
public void BuildManifests(ResourceManifestBuilder builder) {
builder.Add().DefineStyle("SearchAdmin").SetUrl("orchard-search-admin.css"); // todo: this does not appear to be used anywhere
builder.Add().DefineStyle("Search").SetUrl("orchard-search-search.css");
}
}

View File

@@ -13,22 +13,22 @@ namespace Orchard.Search {
public IEnumerable<RouteDescriptor> GetRoutes() {
return new[] {
new RouteDescriptor {
Priority = 5,
Route = new Route(
"Search",
new RouteValueDictionary {
{"area", "Orchard.Search"},
{"controller", "search"},
{"action", "index"}
},
null,
new RouteValueDictionary {
{"area", "Orchard.Search"}
},
new MvcRouteHandler())
}
};
new RouteDescriptor {
Priority = 5,
Route = new Route("Search/{searchIndex}",
new RouteValueDictionary {
{"area", "Orchard.Search"},
{"controller", "Search"},
{"action", "Index"},
{"searchIndex", UrlParameter.Optional}
},
null,
new RouteValueDictionary {
{"area", "Orchard.Search"}
},
new MvcRouteHandler())
}
};
}
}
}

View File

@@ -1,3 +1,4 @@
#main button {
display:block;
.search-indexes > li {
float: left;
margin: 0 2em 2em 0;
}

View File

@@ -23,5 +23,4 @@ form.search input {
/*Search widget styles. More syles could be added here to target specific zones.*/
.widget-search-form {
float:right;
}
}

View File

@@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Orchard.Search.ViewModels {
public class SearchFormViewModel {
public bool OverrideIndex { get; set; }
public string SelectedIndex { get; set; }
public IList<string> AvailableIndexes { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
@model Orchard.Search.ViewModels.SearchFormViewModel
@{
Script.Require("ShapesBase");
}
@{
var indexOptions = Model.AvailableIndexes.Select(x => new SelectListItem { Text = x, Value = x, Selected = x == Model.SelectedIndex });
}
<fieldset>
<div class="form-group">
@Html.CheckBoxFor(m => m.OverrideIndex)
@Html.LabelFor(m => m.OverrideIndex, T("Override Search Index").Text, new { @class = "forcheckbox" })
@Html.Hint(T("Check this if you want to specify a different index for this widget to search."))
</div>
<div class="form-group" data-controllerid="@Html.FieldIdFor(m => m.OverrideIndex)">
@Html.LabelFor(m => m.SelectedIndex, T("Index"))
@Html.DropDownListFor(m => m.SelectedIndex, indexOptions)
@Html.Hint(T("The index to search."))
</div>
</fieldset>

View File

@@ -1,50 +1,52 @@
@using Orchard.Search.ViewModels
@model SearchSettingsViewModel
@{
Script.Require("jQuery");
Script.Include("orchard-search-settings.js");
Style.Include("orchard-search-admin.css");
}
<fieldset>
<legend>@T("Search")</legend>
<div>
<label>@T("Indexes")</label>
<div class="form-group">
<label>@T("Default Index")</label>
@if (Model.Entries != null && Model.Entries.Any()) {
if (String.IsNullOrWhiteSpace(Model.SelectedIndex)) {
Model.Entries.Insert(0, new IndexSettingsEntry { Index = "" });
}
<select id="selectIndex" name="@Html.NameFor(m => m.SelectedIndex)">
@foreach (var modelEntry in Model.Entries) {
@Html.SelectOption(Model.SelectedIndex, modelEntry.Index, modelEntry.Index)
}
</select>
<span class="hint">@T("Select which index to use in search queries.")</span>
@Html.Hint(T("Select which index to use by default in search queries."))
<label>@T("Fields")</label>
<ul class="search-fields" data-selected-index="@Model.SelectedIndex">
@{var entryIndex = 0;}
@foreach (var modelEntry in Model.Entries) {
@Html.HiddenFor(m => m.Entries[entryIndex].Index)
<li data-index="@modelEntry.Index">
@if (modelEntry.Fields != null && modelEntry.Fields.Any()) {
<ul class="search-indexes group">
@{
var indexes = Model.Entries.Select(x => x.Index);
var entryIndex = 0;
foreach (var index in indexes) {
var fieldEntries = Model.Entries.Single(x => x.Index == index).Fields;
var entryIndexClosure = entryIndex;
@Html.HiddenFor(m => m.Entries[entryIndex].Index)
<li>
<h3>@index</h3>
<ul>
@{ var fieldIndex = 0;}
@foreach (var fieldEntry in Model.Entries[entryIndex].Fields) {
@{ var fieldIndex = 0; }
@foreach (var fieldEntry in fieldEntries) {
var fieldIndexClosure = fieldIndex;
<li>
@Html.EditorFor(m => m.Entries[entryIndex].Fields[fieldIndex].Selected)
@Html.HiddenFor(m => m.Entries[entryIndex].Fields[fieldIndex].Field)
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.Entries[entryIndex].Fields[fieldIndex].Selected)">@Model.Entries[entryIndex].Fields[fieldIndex].Field</label>
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.Entries[entryIndexClosure].Fields[fieldIndexClosure].Selected)">@Model.Entries[entryIndex].Fields[fieldIndex].Field</label>
</li>
fieldIndex++;
}
</ul>
}
</li>
</li>
entryIndex++;
}
}
</ul>
<span class="hint">@T("Check any property which should be used for search queries.")</span>
<span class="hint">@T("Check any field for each index which should be used for search queries.")</span>
}
else {

View File

@@ -1,11 +1,20 @@
@using Orchard.Search.ViewModels;
@using Orchard.ContentManagement
@using Orchard.Search.Models
@using Orchard.Search.ViewModels;
@{
Style.Require("Search");
}
@{
var settings = WorkContext.CurrentSite.As<SearchSettingsPart>();
var part = (SearchFormPart)Model.ContentPart;
var index = part.OverrideIndex && part.SelectedIndex != settings.SearchIndex ? part.SelectedIndex : default(string);
var routeValues = new RouteValueDictionary { { "area", "Orchard.Search" } };
@using(Html.BeginForm("index", "search", new { area = "Orchard.Search" }, FormMethod.Get, new { @class = "search-form" })) {
if (!String.IsNullOrEmpty(index)) {
routeValues.Add("searchIndex", index);
}
}
@using (Html.BeginForm("Index", "Search", routeValues, FormMethod.Get, new RouteValueDictionary { { "class", "search-form" } })) {
<fieldset>
@Html.TextBox("q", (SearchViewModel)Model.ViewModel.Query)
@Html.Hidden("culture", WorkContext.CurrentCulture)