Fixed: #U4-1810, pretty much finished the examine management dashboard.
This commit is contained in:
@@ -456,6 +456,41 @@ namespace Umbraco.Core.Models
|
||||
return ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the full xml representation for the <see cref="IContent"/> object and all of it's descendants
|
||||
/// </summary>
|
||||
/// <param name="content"><see cref="IContent"/> to generate xml for</param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
|
||||
internal static XElement ToDeepXml(this IContent content)
|
||||
{
|
||||
var xml = content.ToXml();
|
||||
|
||||
var descendants = content.Descendants().ToArray();
|
||||
var currentChildren = descendants.Where(x => x.ParentId == content.Id);
|
||||
AddChildXml(descendants, currentChildren, xml);
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
private static void AddChildXml(
|
||||
IContent[] originalDescendants,
|
||||
IEnumerable<IContent> currentChildren,
|
||||
XElement currentXml)
|
||||
{
|
||||
foreach (var child in currentChildren)
|
||||
{
|
||||
//add the child's xml
|
||||
var childXml = child.ToXml();
|
||||
currentXml.Add(childXml);
|
||||
//copy local (out of closure)
|
||||
var c = child;
|
||||
//get this item's children
|
||||
var children = originalDescendants.Where(x => x.ParentId == c.Id);
|
||||
//recurse and add it's children to the child xml element
|
||||
AddChildXml(originalDescendants, children, childXml);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IContent"/> object
|
||||
/// </summary>
|
||||
|
||||
@@ -45,15 +45,19 @@
|
||||
<div data-bind="visible: showProperties">
|
||||
|
||||
<div class="propertyPane index-tools">
|
||||
<a href="#" data-bind="click: toggleIndexTools, css: {expanded:showIndexTools() }">Index info & tools</a>
|
||||
<div data-bind="visible: showIndexTools() && IsLuceneIndex">
|
||||
<a href="#" data-bind="click: toggleTools, css: {expanded:showTools() }">Index info & tools</a>
|
||||
<div data-bind="visible: showTools() && IsLuceneIndex">
|
||||
<div class="index-actions">
|
||||
<button data-bind="click: rebuildIndex, disable: isProcessing">Rebuild index</button>
|
||||
<button data-bind="click: optimizeIndex, disable: isProcessing">Optimize index</button>
|
||||
<br/>
|
||||
<div data-bind="visible: processingAttempts() < 100">
|
||||
<button data-bind="click: rebuildIndex, disable: isProcessing">Rebuild index</button>
|
||||
<button data-bind="click: optimizeIndex, disable: isProcessing">Optimize index</button>
|
||||
</div>
|
||||
<div data-bind="visible: isProcessing()">
|
||||
<cc1:ProgressBar runat="server" ID="ProgressBar1" Text="Loading..." />
|
||||
</div>
|
||||
<div class="error" data-bind="visible: processingAttempts() >= 100">
|
||||
The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
@@ -152,13 +156,57 @@
|
||||
<div data-bind="foreach: searcherDetails, visible: !loading()">
|
||||
<div class="provider">
|
||||
<a href="#" data-bind="text: Name, click: toggleProperties"></a>
|
||||
|
||||
<div data-bind="visible: showProperties">
|
||||
|
||||
<div class="propertyPane search-tools">
|
||||
<a href="#" data-bind="click: toggleTools, css: {expanded:showTools() }">Search tools</a>
|
||||
<div data-bind="visible: showTools()">
|
||||
<a class="hide" href="#" data-bind="click: closeSearch, visible: isSearching">Hide search results</a>
|
||||
<input type="text" data-bind="value: searchText, event: {keyup: handleEnter}"/>
|
||||
<button data-bind="click: search, disable: isProcessing">Search</button>
|
||||
<input type="radio" name="searchType" id="textSearch" value="text" data-bind="checked: searchType" />
|
||||
<label for="textSearch">Text Search</label>
|
||||
<input type="radio" name="searchType" id="luceneSearch" value="lucene" data-bind="checked: searchType" />
|
||||
<label for="luceneSearch">Lucene Search</label>
|
||||
<div class="search-results" data-bind="visible: isSearching">
|
||||
<div data-bind="visible: isProcessing()">
|
||||
<cc1:ProgressBar runat="server" ID="ProgressBar2" Text="Loading..." />
|
||||
</div>
|
||||
<table data-bind="visible: !isProcessing()">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="score">Score</th>
|
||||
<th class="id">Id</th>
|
||||
<th>Values</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-bind="foreach: searchResults">
|
||||
<tr>
|
||||
<td data-bind="text: Score"></td>
|
||||
<td data-bind="text: Id"></td>
|
||||
<td data-bind="foreach: Fields">
|
||||
<span class="key" data-bind="text: key"></span>
|
||||
<span class="value" data-bind="text: value"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="propertyPane" data-bind="foreach: ProviderProperties, visible: showProperties">
|
||||
<tr>
|
||||
<th data-bind="text: key"></th>
|
||||
<td data-bind="text: value"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="propertyPane">
|
||||
<a href="#" data-bind="click: toggleProviderProperties, css: {expanded:showProviderProperties() }">Provider properties</a>
|
||||
<table data-bind="visible: showProviderProperties, foreach: ProviderProperties">
|
||||
<tr>
|
||||
<th data-bind="text: key"></th>
|
||||
<td data-bind="text: value"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,5 +47,14 @@ namespace Umbraco.Web.UI.Umbraco.Dashboard {
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::Umbraco.Web.UI.Controls.ProgressBar ProgressBar1;
|
||||
|
||||
/// <summary>
|
||||
/// ProgressBar2 control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::Umbraco.Web.UI.Controls.ProgressBar ProgressBar2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#examineManagement .provider {
|
||||
#examineManagement .provider
|
||||
{
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
#examineManagement .propertyPane {
|
||||
#examineManagement .propertyPane
|
||||
{
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
@@ -12,46 +14,111 @@
|
||||
color: #333;
|
||||
width: 200px;
|
||||
}
|
||||
#examineManagement table td {
|
||||
|
||||
#examineManagement table td
|
||||
{
|
||||
color: #999;
|
||||
}
|
||||
|
||||
#examineManagement table td.highlight {
|
||||
font-style: italic;
|
||||
}
|
||||
#examineManagement table td.highlight
|
||||
{
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#examineManagement .propertyPane > a
|
||||
{
|
||||
{
|
||||
padding: 3px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#examineManagement .propertyPane > a.expanded
|
||||
{
|
||||
border-bottom: 1px solid #CCC;
|
||||
margin-bottom: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
#examineManagement .propertyPane > a.expanded
|
||||
{
|
||||
border-bottom: 1px solid #CCC;
|
||||
margin-bottom: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
#examineManagement .provider > a {
|
||||
#examineManagement .provider > a
|
||||
{
|
||||
padding: 7px;
|
||||
display: block;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
#examineManagement .provider > div {
|
||||
#examineManagement .provider > div
|
||||
{
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#examineManagement a {
|
||||
#examineManagement a
|
||||
{
|
||||
text-decoration: none;
|
||||
}
|
||||
#examineManagement a:hover {
|
||||
text-decoration: underline;
|
||||
|
||||
#examineManagement a:hover
|
||||
{
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#examineManagement .index-tools table
|
||||
{
|
||||
width: 350px;
|
||||
}
|
||||
#examineManagement .index-tools table {
|
||||
width:350px;
|
||||
}
|
||||
#examineManagement .index-tools .index-actions {
|
||||
|
||||
#examineManagement .index-tools .index-actions
|
||||
{
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
#examineManagement .index-tools .index-actions .error
|
||||
{
|
||||
width: 560px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#examineManagement .search-tools input[type="text"]
|
||||
{
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
#examineManagement .search-tools a.hide
|
||||
{
|
||||
float: right;
|
||||
}
|
||||
|
||||
#examineManagement .search-tools .search-results
|
||||
{
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#examineManagement .search-tools .search-results table
|
||||
{
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
#examineManagement .search-tools .search-results th.score
|
||||
{
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#examineManagement .search-tools .search-results th.id
|
||||
{
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#examineManagement .search-tools .search-results tbody tr td
|
||||
{
|
||||
border-bottom: 1px solid #DDD;
|
||||
padding: 3px;
|
||||
}
|
||||
#examineManagement .search-tools .search-results tbody tr td span.key {
|
||||
display: inline-block;
|
||||
color: rgb(0, 64, 201);
|
||||
font-style: italic;
|
||||
}
|
||||
#examineManagement .search-tools .search-results tbody tr td span.value {
|
||||
font-weight: normal;
|
||||
display: inline-block;
|
||||
padding-right: 5px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,40 @@
|
||||
_opts: null,
|
||||
_koViewModel: null,
|
||||
|
||||
_mapSearcherModelProperties : function(indexerModel) {
|
||||
_mapSearcherModelProperties: function (indexerModel) {
|
||||
var self = this;
|
||||
|
||||
var viewModel = self._mapBaseProviderModelProperties(indexerModel);
|
||||
|
||||
//add custom searcher props
|
||||
viewModel.searchText = ko.observable("");
|
||||
viewModel.searchType = ko.observable("text");
|
||||
viewModel.isSearching = ko.observable(false);
|
||||
viewModel.closeSearch = function() {
|
||||
this.isSearching(false);
|
||||
};
|
||||
//add flag properties to determine if either button has been pressed
|
||||
viewModel.isProcessing = ko.observable(false);
|
||||
//don't need an observable array since it does not change, just need an observable to hold an array.
|
||||
viewModel.searchResults = ko.observable([]);
|
||||
viewModel.search = function () {
|
||||
//NOTE: 'this' is the ko view model
|
||||
this.isSearching(true);
|
||||
self._doSearch(this);
|
||||
};
|
||||
viewModel.handleEnter = function(vm, event) {
|
||||
var keyCode = (event.which ? event.which : event.keyCode);
|
||||
if (keyCode === 13) {
|
||||
vm.search();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
return viewModel;
|
||||
},
|
||||
|
||||
_mapBaseProviderModelProperties : function(indexerModel) {
|
||||
var self = this;
|
||||
|
||||
//do the ko auto-mapping
|
||||
@@ -24,6 +57,14 @@
|
||||
viewModel.toggleProperties = function () {
|
||||
this.showProperties(!this.showProperties());
|
||||
};
|
||||
viewModel.showProviderProperties = ko.observable(false);
|
||||
viewModel.toggleProviderProperties = function () {
|
||||
this.showProviderProperties(!this.showProviderProperties());
|
||||
};
|
||||
viewModel.showTools = ko.observable(false);
|
||||
viewModel.toggleTools = function () {
|
||||
this.showTools(!this.showTools());
|
||||
};
|
||||
|
||||
return viewModel;
|
||||
},
|
||||
@@ -37,8 +78,10 @@
|
||||
if (!isUpdate) {
|
||||
//do the ko auto-mapping to the new object and create additional properties
|
||||
|
||||
viewModel = self._mapSearcherModelProperties(indexerModel);
|
||||
viewModel = self._mapBaseProviderModelProperties(indexerModel);
|
||||
|
||||
//property to track how many attempts have been made to check if the index is optimized or rebuilt
|
||||
viewModel.processingAttempts = ko.observable(0);
|
||||
//add a hasDeletions prop
|
||||
viewModel.hasDeletions = ko.observable(indexerModel.DeletionCount > 0);
|
||||
//add toggle and show properties
|
||||
@@ -54,15 +97,7 @@
|
||||
viewModel.showNodeTypes = ko.observable(false);
|
||||
viewModel.toggleNodeTypes = function () {
|
||||
this.showNodeTypes(!this.showNodeTypes());
|
||||
};
|
||||
viewModel.showProviderProperties = ko.observable(false);
|
||||
viewModel.toggleProviderProperties = function () {
|
||||
this.showProviderProperties(!this.showProviderProperties());
|
||||
};
|
||||
viewModel.showIndexTools = ko.observable(false);
|
||||
viewModel.toggleIndexTools = function () {
|
||||
this.showIndexTools(!this.showIndexTools());
|
||||
};
|
||||
};
|
||||
//add flag properties to determine if either button has been pressed
|
||||
viewModel.isProcessing = ko.observable(false);
|
||||
//add the button methods
|
||||
@@ -71,7 +106,8 @@
|
||||
"Depending on how much content there is in your site this could take a while. " +
|
||||
"It is not recommended to rebuild an index during times of high website traffic " +
|
||||
"or when editors are editing content.")) {
|
||||
|
||||
//NOTE: 'this' is the knockoutjs model that is bound
|
||||
self._doProcessing(this.Name(), this, "PostRebuildIndex", "PostCheckRebuildIndex");
|
||||
}
|
||||
};
|
||||
viewModel.optimizeIndex = function () {
|
||||
@@ -79,7 +115,7 @@
|
||||
"It is not recommended to optimize an index during times of high website traffic " +
|
||||
"or when editors are editing content.")) {
|
||||
//NOTE: 'this' is the knockoutjs model that is bound
|
||||
self._optimizeIndex(indexerModel.Name, this);
|
||||
self._doProcessing(this.Name(), this, "PostOptimizeIndex", "PostCheckOptimizeIndex");
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -100,18 +136,34 @@
|
||||
|
||||
return viewModel;
|
||||
},
|
||||
|
||||
_doSearch: function(viewModel) {
|
||||
var self = this;
|
||||
viewModel.isProcessing(true);
|
||||
$.get(self._opts.restServiceLocation + "GetSearchResults?searcherName=" + viewModel.Name() + "&query=" + viewModel.searchText() + "&queryType=" + viewModel.searchType(),
|
||||
function(searchResults) {
|
||||
viewModel.isProcessing(false);
|
||||
//re-map the fields dictionary to array
|
||||
for (var s in searchResults) {
|
||||
searchResults[s].Fields = self._mapDictionaryToArray(searchResults[s].Fields);
|
||||
}
|
||||
viewModel.searchResults(searchResults);
|
||||
}).fail(function(a, b, c) {
|
||||
alert(b + ": " + a.responseText);
|
||||
});
|
||||
},
|
||||
|
||||
_optimizeIndex: function (name, viewModel) {
|
||||
_doProcessing: function (name, viewModel, processingActionName, pollActionName) {
|
||||
var self = this;
|
||||
viewModel.isProcessing(true); //set the model processing
|
||||
|
||||
$.post(self._opts.restServiceLocation + "PostOptimizeIndex?indexerName=" + name,
|
||||
$.post(self._opts.restServiceLocation + processingActionName + "?indexerName=" + name,
|
||||
function (data) {
|
||||
|
||||
//optimization has started, nothing is returned accept a 200 status code.
|
||||
//lets poll to see if it is done.
|
||||
setTimeout(function() {
|
||||
self._checkOptimizeIndex(name, viewModel);
|
||||
self._checkProcessing(name, viewModel, pollActionName);
|
||||
}, 1000);
|
||||
|
||||
}).fail(function (a, b, c) {
|
||||
@@ -119,10 +171,10 @@
|
||||
});
|
||||
},
|
||||
|
||||
_checkOptimizeIndex: function (name, viewModel) {
|
||||
_checkProcessing: function (name, viewModel, actionName) {
|
||||
var self = this;
|
||||
|
||||
$.post(self._opts.restServiceLocation + "PostCheckOptimizeIndex?indexerName=" + name,
|
||||
$.post(self._opts.restServiceLocation + actionName + "?indexerName=" + name,
|
||||
function (data) {
|
||||
if (data) {
|
||||
//success! now, we need to re-update the whole indexer model
|
||||
@@ -130,8 +182,20 @@
|
||||
viewModel.isProcessing(false);
|
||||
}
|
||||
else {
|
||||
//copy local from closure
|
||||
var vm = viewModel;
|
||||
var an = actionName;
|
||||
setTimeout(function () {
|
||||
self._checkOptimizeIndex(name);
|
||||
//don't continue if we've tried 100 times
|
||||
if (vm.processingAttempts() < 100) {
|
||||
self._checkProcessing(name, vm, an);
|
||||
//add an attempt
|
||||
vm.processingAttempts(vm.processingAttempts() + 1);
|
||||
}
|
||||
else {
|
||||
//we've exceeded 100 attempts, stop processing
|
||||
viewModel.isProcessing(false);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}).fail(function (a, b, c) {
|
||||
|
||||
@@ -237,25 +237,10 @@ namespace Umbraco.Web.Search
|
||||
/// <param name="node"></param>
|
||||
/// <param name="cacheOnly">true if data is going to be returned from cache</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If the type of node is not a Document, the cacheOnly has no effect, it will use the API to return
|
||||
/// the xml.
|
||||
/// </remarks>
|
||||
[SecuritySafeCritical]
|
||||
[Obsolete("This method is no longer used and will be removed from the core in future versions, the cacheOnly parameter has no effect. Use the other ToXDocument overload instead")]
|
||||
public static XDocument ToXDocument(Content node, bool cacheOnly)
|
||||
{
|
||||
if (cacheOnly && node.GetType().Equals(typeof(Document)))
|
||||
{
|
||||
var umbXml = library.GetXmlNodeById(node.Id.ToString());
|
||||
if (umbXml != null)
|
||||
{
|
||||
return umbXml.ToXDocument();
|
||||
}
|
||||
}
|
||||
|
||||
//this will also occur if umbraco hasn't cached content yet....
|
||||
|
||||
//if it's not a using cache and it's not cacheOnly, then retrieve the Xml using the API
|
||||
{
|
||||
return ToXDocument(node);
|
||||
}
|
||||
|
||||
@@ -267,6 +252,16 @@ namespace Umbraco.Web.Search
|
||||
[SecuritySafeCritical]
|
||||
private static XDocument ToXDocument(Content node)
|
||||
{
|
||||
if (TypeHelper.IsTypeAssignableFrom<Document>(node))
|
||||
{
|
||||
return new XDocument(((Document) node).Content.ToXml());
|
||||
}
|
||||
|
||||
if (TypeHelper.IsTypeAssignableFrom<global::umbraco.cms.businesslogic.media.Media>(node))
|
||||
{
|
||||
return new XDocument(((global::umbraco.cms.businesslogic.media.Media) node).MediaItem.ToXml());
|
||||
}
|
||||
|
||||
var xDoc = new XmlDocument();
|
||||
var xNode = xDoc.CreateNode(XmlNodeType.Element, "node", "");
|
||||
node.XmlPopulate(xDoc, ref xNode, false);
|
||||
|
||||
@@ -61,6 +61,20 @@ namespace Umbraco.Web.Search
|
||||
return indexer.GetSearcherForIndexer().GetIndexReaderForSearcher().IsOptimized();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the index is locked
|
||||
/// </summary>
|
||||
/// <param name="indexer"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If the index does not exist we'll consider it locked
|
||||
/// </remarks>
|
||||
public static bool IsIndexLocked(this LuceneIndexer indexer)
|
||||
{
|
||||
return !indexer.IndexExists()
|
||||
|| IndexWriter.IsLocked(indexer.GetSearcherForIndexer().GetIndexReaderForSearcher().Directory());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of documents deleted in the index
|
||||
/// </summary>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http;
|
||||
using Examine;
|
||||
using Examine.LuceneEngine;
|
||||
using Examine.LuceneEngine.Providers;
|
||||
using Examine.Providers;
|
||||
using Lucene.Net.Search;
|
||||
@@ -19,7 +21,6 @@ namespace Umbraco.Web.WebServices
|
||||
/// Get the details for indexers
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public IEnumerable<ExamineIndexerModel> GetIndexerDetails()
|
||||
{
|
||||
return ExamineManager.Instance.IndexProviderCollection.Select(CreateModel);
|
||||
@@ -29,7 +30,6 @@ namespace Umbraco.Web.WebServices
|
||||
/// Get the details for searchers
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public IEnumerable<ExamineSearcherModel> GetSearcherDetails()
|
||||
{
|
||||
var model = new List<ExamineSearcherModel>(
|
||||
@@ -52,10 +52,34 @@ namespace Umbraco.Web.WebServices
|
||||
return model;
|
||||
}
|
||||
|
||||
public ISearchResults GetSearchResults(string searcherName, string query, string queryType)
|
||||
{
|
||||
if (queryType == null)
|
||||
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
|
||||
|
||||
if (query.IsNullOrWhiteSpace())
|
||||
return SearchResults.Empty();
|
||||
|
||||
LuceneSearcher searcher;
|
||||
var msg = ValidateLuceneSearcher(searcherName, out searcher);
|
||||
if (msg.IsSuccessStatusCode)
|
||||
{
|
||||
if (queryType.InvariantEquals("text"))
|
||||
{
|
||||
return searcher.Search(query, false);
|
||||
}
|
||||
if (queryType.InvariantEquals("lucene"))
|
||||
{
|
||||
return searcher.Search(searcher.CreateSearchCriteria().RawQuery(query));
|
||||
}
|
||||
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
|
||||
}
|
||||
throw new HttpResponseException(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Optimizes an index
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
public HttpResponseMessage PostOptimizeIndex(string indexerName)
|
||||
{
|
||||
LuceneIndexer indexer;
|
||||
@@ -78,12 +102,61 @@ namespace Umbraco.Web.WebServices
|
||||
return msg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds the index
|
||||
/// </summary>
|
||||
/// <param name="indexerName"></param>
|
||||
/// <returns></returns>
|
||||
public HttpResponseMessage PostRebuildIndex(string indexerName)
|
||||
{
|
||||
LuceneIndexer indexer;
|
||||
var msg = ValidateLuceneIndexer(indexerName, out indexer);
|
||||
if (msg.IsSuccessStatusCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
indexer.RebuildIndex();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
return new HttpResponseMessage(HttpStatusCode.Conflict)
|
||||
{
|
||||
Content = new StringContent(string.Format("The index could not be rebuilt at this time, most likely there is another thread currently writing to the index. Error: {0}", ex)),
|
||||
ReasonPhrase = "Could Not Rebuild"
|
||||
};
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the index has been rebuilt
|
||||
/// </summary>
|
||||
/// <param name="indexerName"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is kind of rudementary since there's no way we can know that the index has rebuilt, we'll just check
|
||||
/// if the index is locked based on Lucene apis
|
||||
/// </remarks>
|
||||
public ExamineIndexerModel PostCheckRebuildIndex(string indexerName)
|
||||
{
|
||||
LuceneIndexer indexer;
|
||||
var msg = ValidateLuceneIndexer(indexerName, out indexer);
|
||||
if (msg.IsSuccessStatusCode)
|
||||
{
|
||||
var isLocked = indexer.IsIndexLocked();
|
||||
return isLocked
|
||||
? null
|
||||
: CreateModel(indexer);
|
||||
}
|
||||
throw new HttpResponseException(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the index is optimized
|
||||
/// </summary>
|
||||
/// <param name="indexerName"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public ExamineIndexerModel PostCheckOptimizeIndex(string indexerName)
|
||||
{
|
||||
LuceneIndexer indexer;
|
||||
@@ -126,6 +199,31 @@ namespace Umbraco.Web.WebServices
|
||||
return indexerModel;
|
||||
}
|
||||
|
||||
private HttpResponseMessage ValidateLuceneSearcher(string searcherName, out LuceneSearcher searcher)
|
||||
{
|
||||
if (ExamineManager.Instance.SearchProviderCollection.Cast<BaseSearchProvider>().Any(x => x.Name == searcherName))
|
||||
{
|
||||
searcher = ExamineManager.Instance.SearchProviderCollection[searcherName] as LuceneSearcher;
|
||||
if (searcher == null)
|
||||
{
|
||||
return new HttpResponseMessage(HttpStatusCode.BadRequest)
|
||||
{
|
||||
Content = new StringContent(string.Format("The searcher {0} is not of type {1}", searcherName, typeof(LuceneSearcher))),
|
||||
ReasonPhrase = "Wrong Searcher Type"
|
||||
};
|
||||
}
|
||||
//return Ok!
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
searcher = null;
|
||||
return new HttpResponseMessage(HttpStatusCode.BadRequest)
|
||||
{
|
||||
Content = new StringContent(string.Format("No searcher found with name = {0}", searcherName)),
|
||||
ReasonPhrase = "Searcher Not Found"
|
||||
};
|
||||
}
|
||||
|
||||
private HttpResponseMessage ValidateLuceneIndexer(string indexerName, out LuceneIndexer indexer)
|
||||
{
|
||||
if (ExamineManager.Instance.IndexProviderCollection.Any(x => x.Name == indexerName))
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace UmbracoExamine.DataServices
|
||||
var xmlContent = XDocument.Parse("<content></content>");
|
||||
foreach (var c in _applicationContext.Services.ContentService.GetRootContent())
|
||||
{
|
||||
xmlContent.Root.Add(c.ToXml());
|
||||
xmlContent.Root.Add(c.ToDeepXml());
|
||||
}
|
||||
var result = ((IEnumerable)xmlContent.XPathEvaluate(xpath)).Cast<XElement>();
|
||||
return result.ToXDocument();
|
||||
|
||||
@@ -208,8 +208,16 @@ namespace UmbracoExamine
|
||||
if (!SupportedTypes.Contains(type))
|
||||
return;
|
||||
|
||||
DataService.LogService.AddVerboseLog((int)node.Attribute("id"), string.Format("ReIndexNode with type: {0}", type));
|
||||
base.ReIndexNode(node, type);
|
||||
if (node.Attribute("id") != null)
|
||||
{
|
||||
DataService.LogService.AddVerboseLog((int) node.Attribute("id"), string.Format("ReIndexNode with type: {0}", type));
|
||||
base.ReIndexNode(node, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
DataService.LogService.AddErrorLog(-1, string.Format("ReIndexNode cannot proceed, the format of the XElement is invalid, the xml has no 'id' attribute. {0}", node));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user