Adding some admin UI for search index management. Not yet working end-to-end but it looks like it does *

* Waiting on some pieces of the index manager/provider are being added and/or reworked then we'll get it all hooked up.

--HG--
branch : dev
This commit is contained in:
Nathan Heskew
2010-06-04 14:45:18 -07:00
parent 69e32f3d6b
commit 9ef73f84b0
12 changed files with 174 additions and 15 deletions

View File

@@ -0,0 +1,16 @@
using Orchard.Localization;
using Orchard.UI.Navigation;
namespace Orchard.Search {
public class AdminMenu : INavigationProvider {
public Localizer T { get; set; }
public string MenuName { get { return "admin"; } }
public void GetNavigation(NavigationBuilder builder) {
builder.Add(T("Site"), "11",
menu => menu
.Add(T("Search Index"), "10.0", item => item.Action("Index", "Admin", new {area = "Orchard.Search"})
.Permission(Permissions.ManageSearchIndex)));
}
}
}

View File

@@ -0,0 +1,49 @@
using System.Web.Mvc;
using Orchard.Localization;
using Orchard.Search.Services;
using Orchard.Search.ViewModels;
using Orchard.UI.Notify;
namespace Orchard.Search.Controllers {
public class AdminController : Controller {
private readonly ISearchService _searchService;
public AdminController(ISearchService searchService, IOrchardServices services) {
_searchService = searchService;
Services = services;
T = NullLocalizer.Instance;
}
public IOrchardServices Services { get; private set; }
public Localizer T { get; set; }
public ActionResult Index() {
var viewModel = new SearchIndexViewModel {HasIndexToManage = _searchService.HasIndexToManage};
if (!viewModel.HasIndexToManage)
Services.Notifier.Information(T("There is not search index to manage for this site."));
return View(viewModel);
}
[HttpPost]
public ActionResult Update() {
if (!Services.Authorizer.Authorize(Permissions.ManageSearchIndex, T("Not allowed to manage the search index.")))
return new HttpUnauthorizedResult();
_searchService.UpdateIndex();
return RedirectToAction("Index");
}
[HttpPost]
public ActionResult Rebuild() {
if (!Services.Authorizer.Authorize(Permissions.ManageSearchIndex, T("Not allowed to manage the search index.")))
return new HttpUnauthorizedResult();
_searchService.RebuildIndex();
return RedirectToAction("Index");
}
}
}

View File

@@ -65,11 +65,15 @@
<Reference Include="System.EnterpriseServices" /> <Reference Include="System.EnterpriseServices" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Controllers\SearchController.cs" /> <Compile Include="Controllers\SearchController.cs" />
<Compile Include="Filters\SearchFilter.cs" /> <Compile Include="Filters\SearchFilter.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Routes.cs" /> <Compile Include="Routes.cs" />
<Compile Include="Services\ISearchService.cs" /> <Compile Include="Services\ISearchService.cs" />
<Compile Include="Services\SearchService.cs" /> <Compile Include="Services\SearchService.cs" />
<Compile Include="ViewModels\SearchIndexViewModel.cs" />
<Compile Include="ViewModels\SearchResultViewModel.cs" /> <Compile Include="ViewModels\SearchResultViewModel.cs" />
<Compile Include="ViewModels\SearchViewModel.cs" /> <Compile Include="ViewModels\SearchViewModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
@@ -82,7 +86,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Module.txt" /> <Content Include="Module.txt" />
<Content Include="Styles\admin.css" />
<Content Include="Styles\search.css" /> <Content Include="Styles\search.css" />
<Content Include="Views\Admin\Index.ascx" />
<Content Include="Views\SearchForm.ascx" /> <Content Include="Views\SearchForm.ascx" />
<Content Include="Views\Search\Index.ascx" /> <Content Include="Views\Search\Index.ascx" />
<Content Include="Views\Web.config" /> <Content Include="Views\Web.config" />

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using Orchard.Security.Permissions;
namespace Orchard.Search {
public class Permissions : IPermissionProvider {
public static readonly Permission ManageSearchIndex = new Permission { Description = "Manage Search Index", Name = "ManageSearchIndex" };
public string ModuleName {
get {
return "Search";
}
}
public IEnumerable<Permission> GetPermissions() {
return new Permission[] {
ManageSearchIndex,
};
}
public IEnumerable<PermissionStereotype> GetDefaultStereotypes() {
return new[] {
new PermissionStereotype {
Name = "Administrator",
Permissions = new[] {ManageSearchIndex}
},
};
}
}
}

View File

@@ -3,6 +3,9 @@ using Orchard.Indexing;
namespace Orchard.Search.Services { namespace Orchard.Search.Services {
public interface ISearchService : IDependency { public interface ISearchService : IDependency {
bool HasIndexToManage { get; }
IEnumerable<ISearchHit> Query(string term); IEnumerable<ISearchHit> Query(string term);
void RebuildIndex();
void UpdateIndex();
} }
} }

View File

@@ -1,25 +1,58 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Orchard.Indexing; using Orchard.Indexing;
using Orchard.Localization;
using Orchard.UI.Notify;
namespace Orchard.Search.Services namespace Orchard.Search.Services
{ {
public class SearchService : ISearchService public class SearchService : ISearchService
{ {
private const string SearchIndexName = "search";
private readonly IIndexManager _indexManager; private readonly IIndexManager _indexManager;
public SearchService(IIndexManager indexManager) { public SearchService(IOrchardServices services, IIndexManager indexManager) {
Services = services;
_indexManager = indexManager; _indexManager = indexManager;
T = NullLocalizer.Instance;
}
public IOrchardServices Services { get; set; }
public Localizer T { get; set; }
public bool HasIndexToManage {
get { return _indexManager.HasIndexProvider(); }
} }
public IEnumerable<ISearchHit> Query(string term) { public IEnumerable<ISearchHit> Query(string term) {
if (string.IsNullOrWhiteSpace(term) || !_indexManager.HasIndexProvider()) if (string.IsNullOrWhiteSpace(term) || !_indexManager.HasIndexProvider())
return Enumerable.Empty<ISearchHit>(); return Enumerable.Empty<ISearchHit>();
return _indexManager.GetSearchIndexProvider().CreateSearchBuilder("search") return _indexManager.GetSearchIndexProvider().CreateSearchBuilder(SearchIndexName)
.WithField("title", term) .WithField("title", term)
.WithField("body", term) .WithField("body", term)
.Search(); .Search();
} }
public void RebuildIndex() {
if (!_indexManager.HasIndexProvider()) {
Services.Notifier.Warning(T("There is no search index to rebuild."));
return;
}
var searchProvider = _indexManager.GetSearchIndexProvider();
if (searchProvider.Exists(SearchIndexName))
searchProvider.DeleteIndex(SearchIndexName);
searchProvider.CreateIndex(SearchIndexName); // or just reset the updated date and let the background process recreate the index
Services.Notifier.Information(T("The search index has been rebuilt."));
}
public void UpdateIndex() {
//todo: this
//if (_indexManager.HasIndexProvider())
// _indexManager.GetSearchIndexProvider().UpdateIndex(SearchIndexName);
Services.Notifier.Information(T("The search index has been updated."));
}
} }
} }

View File

@@ -0,0 +1,3 @@
#main button {
display:block;
}

View File

@@ -0,0 +1,10 @@
using System;
using Orchard.Mvc.ViewModels;
namespace Orchard.Search.ViewModels {
public class SearchIndexViewModel : BaseViewModel {
public bool HasIndexToManage { get; set; }
//todo: hang the index updated date off here to show in the admin UI (e.g. -> index updated: June 4, 2010 [update index])
public DateTime IndexUpdatedUtc { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.Search.ViewModels.SearchIndexViewModel>" %>
<%@ Import Namespace="Orchard.Mvc.Html" %><%
Html.RegisterStyle("admin.css"); %>
<h1><%=Html.TitleForPage(T("Search Index Mangement").ToString()) %></h1><%
using (Html.BeginForm("update", "admin", FormMethod.Post, new {area = "Orchard.Search"})) { %>
<fieldset>
<p><%=T("The search index was last updated {0}. <button type=\"submit\" title=\"Update the search index.\" class=\"primaryAction\">Update</button>", Html.DateTimeRelative(Model.IndexUpdatedUtc))%></p>
<%=Html.AntiForgeryTokenOrchard() %>
</fieldset><%
}
using (Html.BeginForm("rebuild", "admin", FormMethod.Post, new {area = "Orchard.Search"})) { %>
<fieldset>
<p><%=T("Rebuild the search index for a fresh start. <button type=\"submit\" title=\"Rebuild the search index.\">Rebuld</button>") %></p>
<%=Html.AntiForgeryTokenOrchard() %>
</fieldset><%
} %>

View File

@@ -1,14 +1,11 @@
<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.Search.ViewModels.SearchViewModel>" %> <%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl<Orchard.Search.ViewModels.SearchViewModel>" %>
<%@ Import Namespace="Orchard.Mvc.Html" %><% <%@ Import Namespace="Orchard.Mvc.Html" %><%
Html.RegisterStyle("search.css"); %> Html.RegisterStyle("search.css"); %>
<h1><%=Html.TitleForPage(T("Search"))%></h1> <h1><%=Html.TitleForPage(T("Search"))%></h1><%
<% Html.Zone("search");
Html.Zone("search"); %><%
if (!string.IsNullOrWhiteSpace(Model.Query)) { %> if (!string.IsNullOrWhiteSpace(Model.Query)) { %>
<p class="search-summary"><%=T("<em>{0}</em> results", Model.Results.Count()) %></p><% <p class="search-summary"><%=T("<em>{0}</em> results", Model.Results.Count()) %></p><%
} }
if (Model.Results.Count() > 0) { %> if (Model.Results.Count() > 0) { %>
<%=Html.UnorderedList(Model.Results, (r, i) => Html.DisplayForItem(r.Content).ToHtmlString(), "search-results contentItems") %><% <%=Html.UnorderedList(Model.Results, (r, i) => Html.DisplayForItem(r.Content).ToHtmlString(), "search-results contentItems") %><%
} %> } %>

View File

@@ -57,7 +57,6 @@ body {
} }
button { button {
font-family:Segoe UI,Trebuchet,Arial,Sans-Serif; font-family:Segoe UI,Trebuchet,Arial,Sans-Serif;
font-size:1.01em;
} }
body#preview { body#preview {
min-width:0; min-width:0;
@@ -157,6 +156,7 @@ table.items th, table.items td, table.items caption { font-size:1.4em; line-heig
table.items p, table.items label, table.items input, table.items .button { font-size:1em; line-height:1em; } table.items p, table.items label, table.items input, table.items .button { font-size:1em; line-height:1em; }
p .button { font-size:inherit; } p .button { font-size:inherit; }
.meta, .hint { font-size:1.2em; } /* 12px */ .meta, .hint { font-size:1.2em; } /* 12px */
form.link button { font-size:1.01em; }
/* Links /* Links
----------------------------------------------------------*/ ----------------------------------------------------------*/
@@ -450,9 +450,6 @@ button, .button, .button:link, .button:visited {
text-align:center; text-align:center;
padding:0 .8em .1em; padding:0 .8em .1em;
} }
button {
padding-top:.08em;
}
form.link button { form.link button {
background:inherit; background:inherit;
border:0; border:0;

View File

@@ -111,7 +111,7 @@ namespace Orchard.Mvc.Html {
TimeSpan time = htmlHelper.Resolve<IClock>().UtcNow - value; TimeSpan time = htmlHelper.Resolve<IClock>().UtcNow - value;
if (time.TotalDays > 7) if (time.TotalDays > 7)
return "at " + htmlHelper.DateTime(value); return "on " + htmlHelper.DateTime(value, "MMM d yyyy 'at' h:mm tt");
if (time.TotalHours > 24) if (time.TotalHours > 24)
return string.Format("{0} day{1} ago", time.Days, time.Days == 1 ? "" : "s"); return string.Format("{0} day{1} ago", time.Days, time.Days == 1 ? "" : "s");
if (time.TotalMinutes > 60) if (time.TotalMinutes > 60)