mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Merge
--HG-- branch : dev
This commit is contained in:
239
lib/lucene.net/LICENSE.txt
Normal file
239
lib/lucene.net/LICENSE.txt
Normal file
@@ -0,0 +1,239 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
|
||||
Some code in src/java/org/apache/lucene/util/UnicodeUtil.java was
|
||||
derived from unicode conversion examples available at
|
||||
http://www.unicode.org/Public/PROGRAMS/CVTUTF. Here is the copyright
|
||||
from those sources:
|
||||
|
||||
/*
|
||||
* Copyright 2001-2004 Unicode, Inc.
|
||||
*
|
||||
* Disclaimer
|
||||
*
|
||||
* This source code is provided as is by Unicode, Inc. No claims are
|
||||
* made as to fitness for any particular purpose. No warranties of any
|
||||
* kind are expressed or implied. The recipient agrees to determine
|
||||
* applicability of information provided. If this file has been
|
||||
* purchased on magnetic or optical media from Unicode, Inc., the
|
||||
* sole remedy for any claim will be exchange of defective media
|
||||
* within 90 days of receipt.
|
||||
*
|
||||
* Limitations on Rights to Redistribute This Code
|
||||
*
|
||||
* Unicode, Inc. hereby grants the right to freely use the information
|
||||
* supplied in this file in the creation of products supporting the
|
||||
* Unicode Standard, and to make copies of this file in any form
|
||||
* for internal or external distribution as long as this notice
|
||||
* remains attached.
|
||||
*/
|
||||
|
||||
|
||||
Some code in src/java/org/apache/lucene/util/ArrayUtil.java was
|
||||
derived from Python 2.4.2 sources available at
|
||||
http://www.python.org. Full license is here:
|
||||
|
||||
http://www.python.org/download/releases/2.4.2/license/
|
||||
|
||||
|
||||
BIN
lib/lucene.net/Lucene.Net.dll
Normal file
BIN
lib/lucene.net/Lucene.Net.dll
Normal file
Binary file not shown.
BIN
lib/lucene.net/Lucene.Net.pdb
Normal file
BIN
lib/lucene.net/Lucene.Net.pdb
Normal file
Binary file not shown.
27275
lib/lucene.net/Lucene.Net.xml
Normal file
27275
lib/lucene.net/Lucene.Net.xml
Normal file
File diff suppressed because it is too large
Load Diff
172
src/Orchard.Core.Tests/Indexing/DefaultIndexProviderTests.cs
Normal file
172
src/Orchard.Core.Tests/Indexing/DefaultIndexProviderTests.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Autofac;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.FileSystems.AppData;
|
||||
using Orchard.Indexing;
|
||||
using Orchard.Core.Indexing.Lucene;
|
||||
|
||||
namespace Orchard.Tests.Indexing {
|
||||
public class DefaultIndexProviderTests {
|
||||
private IContainer _container;
|
||||
private IIndexProvider _provider;
|
||||
private IAppDataFolder _appDataFolder;
|
||||
private ShellSettings _shellSettings;
|
||||
private readonly string _basePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
|
||||
[TestFixtureTearDown]
|
||||
public void Clean() {
|
||||
Directory.Delete(_basePath, true);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Setup() {
|
||||
if (Directory.Exists(_basePath)) {
|
||||
Directory.Delete(_basePath, true);
|
||||
}
|
||||
Directory.CreateDirectory(_basePath);
|
||||
|
||||
_appDataFolder = new AppDataFolder();
|
||||
_appDataFolder.SetBasePath(_basePath);
|
||||
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterType<DefaultIndexProvider>().As<IIndexProvider>();
|
||||
builder.RegisterInstance(_appDataFolder).As<IAppDataFolder>();
|
||||
|
||||
// setting up a ShellSettings instance
|
||||
_shellSettings = new ShellSettings { Name = "My Site" };
|
||||
builder.RegisterInstance(_shellSettings).As<ShellSettings>();
|
||||
|
||||
_container = builder.Build();
|
||||
_provider = _container.Resolve<IIndexProvider>();
|
||||
}
|
||||
|
||||
private string[] Indexes() {
|
||||
return new DirectoryInfo(Path.Combine(_basePath, "Sites", "My Site", "Indexes")).GetDirectories().Select(d => d.Name).ToArray();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexProviderShouldCreateNewIndex() {
|
||||
Assert.That(Indexes().Length, Is.EqualTo(0));
|
||||
|
||||
_provider.CreateIndex("default");
|
||||
Assert.That(Indexes().Length, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexProviderShouldOverwriteAlreadyExistingIndex() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.CreateIndex("default");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexProviderShouldDeleteExistingIndex() {
|
||||
Assert.That(Indexes().Length, Is.EqualTo(0));
|
||||
|
||||
_provider.CreateIndex("default");
|
||||
Assert.That(Indexes().Length, Is.EqualTo(1));
|
||||
|
||||
_provider.DeleteIndex("default");
|
||||
Assert.That(Indexes().Length, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexProviderShouldListExistingIndexes() {
|
||||
Assert.That(Indexes().Length, Is.EqualTo(0));
|
||||
|
||||
_provider.CreateIndex("default");
|
||||
Assert.That(Indexes().Length, Is.EqualTo(1));
|
||||
Assert.That(Indexes()[0], Is.EqualTo("default"));
|
||||
|
||||
_provider.CreateIndex("foo");
|
||||
Assert.That(Indexes().Length, Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ANewIndexShouldBeEmpty() {
|
||||
_provider.CreateIndex("default");
|
||||
var searchBuilder = _provider.CreateSearchBuilder("default");
|
||||
var hits = searchBuilder.Search();
|
||||
|
||||
Assert.That(hits.Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DocumentsShouldBeSearchableById() {
|
||||
_provider.CreateIndex("default");
|
||||
|
||||
_provider.Store("default", _provider.New(42));
|
||||
|
||||
var searchBuilder = _provider.CreateSearchBuilder("default");
|
||||
|
||||
var hit = searchBuilder.Get(42);
|
||||
Assert.IsNotNull(hit);
|
||||
Assert.That(hit.Id, Is.EqualTo(42));
|
||||
|
||||
hit = searchBuilder.Get(1);
|
||||
Assert.IsNull(hit);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PropertiesShouldNotBeLost() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(42).Add("prop1", "value1"));
|
||||
|
||||
var hit = _provider.CreateSearchBuilder("default").Get(42);
|
||||
|
||||
Assert.IsNotNull(hit);
|
||||
Assert.That(hit.Id, Is.EqualTo(42));
|
||||
Assert.That(hit.GetString("prop1"), Is.EqualTo("value1"));
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldHandleMultipleIndexes() {
|
||||
_provider.CreateIndex("default1");
|
||||
_provider.Store("default1", _provider.New(1));
|
||||
|
||||
_provider.CreateIndex("default2");
|
||||
_provider.Store("default2", _provider.New(2));
|
||||
|
||||
_provider.CreateIndex("default3");
|
||||
_provider.Store("default3", _provider.New(3));
|
||||
|
||||
Assert.IsNotNull(_provider.CreateSearchBuilder("default1").Get(1));
|
||||
Assert.IsNotNull(_provider.CreateSearchBuilder("default2").Get(2));
|
||||
Assert.IsNotNull(_provider.CreateSearchBuilder("default3").Get(3));
|
||||
|
||||
Assert.IsNull(_provider.CreateSearchBuilder("default1").Get(2));
|
||||
Assert.IsNull(_provider.CreateSearchBuilder("default2").Get(3));
|
||||
Assert.IsNull(_provider.CreateSearchBuilder("default3").Get(1));
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IdentifierShouldNotCollide() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("field", "value1"));
|
||||
_provider.Store("default", _provider.New(11).Add("field", "value11"));
|
||||
_provider.Store("default", _provider.New(111).Add("field", "value111"));
|
||||
|
||||
var searchBuilder = _provider.CreateSearchBuilder("default");
|
||||
|
||||
Assert.That(searchBuilder.Get(1).Id, Is.EqualTo(1));
|
||||
Assert.That(searchBuilder.Get(11).Id, Is.EqualTo(11));
|
||||
Assert.That(searchBuilder.Get(111).Id, Is.EqualTo(111));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TagsShouldBeRemoved() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("body", "<hr>some content</hr>"));
|
||||
_provider.Store("default", _provider.New(2).Add("body", "<hr>some content</hr>", true));
|
||||
|
||||
var searchBuilder = _provider.CreateSearchBuilder("default");
|
||||
|
||||
Assert.That(searchBuilder.WithField("body", "hr").Search().Count(), Is.EqualTo(1));
|
||||
Assert.That(searchBuilder.WithField("body", "hr").Search().First().Id, Is.EqualTo(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
181
src/Orchard.Core.Tests/Indexing/DefaultSearchBuilderTests.cs
Normal file
181
src/Orchard.Core.Tests/Indexing/DefaultSearchBuilderTests.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Autofac;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.FileSystems.AppData;
|
||||
using Orchard.Indexing;
|
||||
using Orchard.Core.Indexing.Lucene;
|
||||
|
||||
namespace Orchard.Tests.Indexing {
|
||||
public class DefaultSearchBuilderTests {
|
||||
private IContainer _container;
|
||||
private IIndexProvider _provider;
|
||||
private IAppDataFolder _appDataFolder;
|
||||
private ShellSettings _shellSettings;
|
||||
private readonly string _basePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
|
||||
[TestFixtureTearDown]
|
||||
public void Clean() {
|
||||
Directory.Delete(_basePath, true);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Setup() {
|
||||
if (Directory.Exists(_basePath)) {
|
||||
Directory.Delete(_basePath, true);
|
||||
}
|
||||
Directory.CreateDirectory(_basePath);
|
||||
|
||||
|
||||
_appDataFolder = new AppDataFolder();
|
||||
_appDataFolder.SetBasePath(_basePath);
|
||||
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterType<DefaultIndexProvider>().As<IIndexProvider>();
|
||||
builder.RegisterInstance(_appDataFolder).As<IAppDataFolder>();
|
||||
|
||||
// setting up a ShellSettings instance
|
||||
_shellSettings = new ShellSettings { Name = "My Site" };
|
||||
builder.RegisterInstance(_shellSettings).As<ShellSettings>();
|
||||
|
||||
_container = builder.Build();
|
||||
_provider = _container.Resolve<IIndexProvider>();
|
||||
}
|
||||
|
||||
private ISearchBuilder _searchBuilder { get { return _provider.CreateSearchBuilder("default"); } }
|
||||
|
||||
[Test]
|
||||
public void SearchTermsShouldBeFoundInMultipleFields() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default",
|
||||
_provider.New(42)
|
||||
.Add("title", "title1 title2 title3")
|
||||
.Add("date", new DateTime(2010, 05, 28, 14, 13, 56, 123))
|
||||
);
|
||||
|
||||
Assert.IsNotNull(_provider.CreateSearchBuilder("default").Get(42));
|
||||
|
||||
Assert.IsNotNull(_provider.CreateSearchBuilder("default").WithField("title", "title1").Search().FirstOrDefault());
|
||||
Assert.IsNotNull(_provider.CreateSearchBuilder("default").WithField("title", "title2").Search().FirstOrDefault());
|
||||
Assert.IsNotNull(_provider.CreateSearchBuilder("default").WithField("title", "title3").Search().FirstOrDefault());
|
||||
Assert.IsNull(_provider.CreateSearchBuilder("default").WithField("title", "title4").Search().FirstOrDefault());
|
||||
Assert.IsNotNull(_provider.CreateSearchBuilder("default").WithField("title", "title").Search().FirstOrDefault());
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSearchById() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1));
|
||||
_provider.Store("default", _provider.New(2));
|
||||
_provider.Store("default", _provider.New(3));
|
||||
|
||||
|
||||
Assert.That(_searchBuilder.Get(1).Id, Is.EqualTo(1));
|
||||
Assert.That(_searchBuilder.Get(2).Id, Is.EqualTo(2));
|
||||
Assert.That(_searchBuilder.Get(3).Id, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSearchWithField() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("title", "cat"));
|
||||
_provider.Store("default", _provider.New(2).Add("title", "dog"));
|
||||
_provider.Store("default", _provider.New(3).Add("title", "cat"));
|
||||
|
||||
|
||||
Assert.That(_searchBuilder.WithField("title", "cat").Search().Count(), Is.EqualTo(2));
|
||||
Assert.That(_searchBuilder.WithField("title", "cat").Search().Any(hit => new[] { 1, 3 }.Contains(hit.Id)), Is.True);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldCountResultsOnly() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("title", "cat"));
|
||||
_provider.Store("default", _provider.New(2).Add("title", "dog"));
|
||||
_provider.Store("default", _provider.New(3).Add("title", "cat"));
|
||||
|
||||
Assert.That(_searchBuilder.WithField("title", "dog").Count(), Is.EqualTo(1));
|
||||
Assert.That(_searchBuilder.WithField("title", "cat").Count(), Is.EqualTo(2));
|
||||
Assert.That(_searchBuilder.WithField("title", "c").Count(), Is.EqualTo(2));
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldFilterByDate() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("date", new DateTime(2010, 05, 28, 12, 30, 15)));
|
||||
_provider.Store("default", _provider.New(2).Add("date", new DateTime(2010, 05, 28, 12, 30, 30)));
|
||||
_provider.Store("default", _provider.New(3).Add("date", new DateTime(2010, 05, 28, 12, 30, 45)));
|
||||
|
||||
Assert.That(_searchBuilder.After("date", new DateTime(2010, 05, 28, 12, 30, 15)).Count(), Is.EqualTo(3));
|
||||
Assert.That(_searchBuilder.Before("date", new DateTime(2010, 05, 28, 12, 30, 45)).Count(), Is.EqualTo(3));
|
||||
Assert.That(_searchBuilder.After("date", new DateTime(2010, 05, 28, 12, 30, 15)).Before("date", new DateTime(2010, 05, 28, 12, 30, 45)).Count(), Is.EqualTo(3));
|
||||
Assert.That(_searchBuilder.After("date", new DateTime(2010, 05, 28, 12, 30, 16)).Before("date", new DateTime(2010, 05, 28, 12, 30, 44)).Count(), Is.EqualTo(1));
|
||||
Assert.That(_searchBuilder.After("date", new DateTime(2010, 05, 28, 12, 30, 46)).Count(), Is.EqualTo(0));
|
||||
Assert.That(_searchBuilder.Before("date", new DateTime(2010, 05, 28, 12, 30, 1)).Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSliceResults() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1));
|
||||
_provider.Store("default", _provider.New(22));
|
||||
_provider.Store("default", _provider.New(333));
|
||||
_provider.Store("default", _provider.New(4444));
|
||||
_provider.Store("default", _provider.New(55555));
|
||||
|
||||
|
||||
Assert.That(_searchBuilder.Count(), Is.EqualTo(5));
|
||||
Assert.That(_searchBuilder.Slice(0, 3).Count(), Is.EqualTo(3));
|
||||
Assert.That(_searchBuilder.Slice(1, 3).Count(), Is.EqualTo(3));
|
||||
Assert.That(_searchBuilder.Slice(3, 3).Count(), Is.EqualTo(2));
|
||||
|
||||
// Count() and Search() should return the same results
|
||||
Assert.That(_searchBuilder.Search().Count(), Is.EqualTo(5));
|
||||
Assert.That(_searchBuilder.Slice(0, 3).Search().Count(), Is.EqualTo(3));
|
||||
Assert.That(_searchBuilder.Slice(1, 3).Search().Count(), Is.EqualTo(3));
|
||||
Assert.That(_searchBuilder.Slice(3, 3).Search().Count(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSortByRelevance() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("body", "michaelson is in the kitchen"));
|
||||
_provider.Store("default", _provider.New(2).Add("body", "michael as a cousin named michael"));
|
||||
_provider.Store("default", _provider.New(3).Add("body", "speak inside the mic"));
|
||||
_provider.Store("default", _provider.New(4).Add("body", "a dog is pursuing a cat"));
|
||||
_provider.Store("default", _provider.New(5).Add("body", "the elephant can't catch up the dog"));
|
||||
|
||||
var michael = _searchBuilder.WithField("body", "mic").Search().ToList();
|
||||
Assert.That(michael.Count(), Is.EqualTo(3));
|
||||
Assert.That(michael[0].Score >= michael[1].Score, Is.True);
|
||||
|
||||
// Sorting on score is always descending
|
||||
michael = _searchBuilder.WithField("body", "mic").Ascending().Search().ToList();
|
||||
Assert.That(michael.Count(), Is.EqualTo(3));
|
||||
Assert.That(michael[0].Score >= michael[1].Score, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSortByDate() {
|
||||
_provider.CreateIndex("default");
|
||||
_provider.Store("default", _provider.New(1).Add("date", new DateTime(2010, 05, 28, 12, 30, 15)));
|
||||
_provider.Store("default", _provider.New(2).Add("date", new DateTime(2010, 05, 28, 12, 30, 30)));
|
||||
_provider.Store("default", _provider.New(3).Add("date", new DateTime(2010, 05, 28, 12, 30, 45)));
|
||||
|
||||
var date = _searchBuilder.SortBy("date").Search().ToList();
|
||||
Assert.That(date.Count(), Is.EqualTo(3));
|
||||
Assert.That(date[0].GetDateTime("date") > date[1].GetDateTime("date"), Is.True);
|
||||
Assert.That(date[1].GetDateTime("date") > date[2].GetDateTime("date"), Is.True);
|
||||
|
||||
date = _searchBuilder.SortBy("date").Ascending().Search().ToList();
|
||||
Assert.That(date.Count(), Is.EqualTo(3));
|
||||
Assert.That(date[0].GetDateTime("date") < date[1].GetDateTime("date"), Is.True);
|
||||
Assert.That(date[1].GetDateTime("date") < date[2].GetDateTime("date"), Is.True);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,8 @@
|
||||
<Compile Include="Common\Providers\CommonAspectProviderTests.cs" />
|
||||
<Compile Include="Common\Services\RoutableServiceTests.cs" />
|
||||
<Compile Include="Feeds\Controllers\FeedControllerTests.cs" />
|
||||
<Compile Include="Indexing\DefaultIndexProviderTests.cs" />
|
||||
<Compile Include="Indexing\DefaultSearchBuilderTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Scheduling\ScheduledTaskManagerTests.cs" />
|
||||
<Compile Include="Scheduling\ScheduledTaskExecutorTests.cs" />
|
||||
|
||||
2054
src/Orchard.Web/Core/App_Data/Localization/fr-FR/orchard.core.po
Normal file
2054
src/Orchard.Web/Core/App_Data/Localization/fr-FR/orchard.core.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@ namespace Orchard.Core.Common.Drivers {
|
||||
|
||||
private readonly IOrchardServices _services;
|
||||
private readonly IRoutableService _routableService;
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
protected override string Prefix {
|
||||
get { return "Routable"; }
|
||||
|
||||
@@ -8,6 +8,10 @@ namespace Orchard.Core.Common.Handlers {
|
||||
public class BodyAspectHandler : ContentHandler {
|
||||
public BodyAspectHandler(IRepository<BodyRecord> bodyRepository) {
|
||||
Filters.Add(StorageFilter.For(bodyRepository));
|
||||
|
||||
OnIndexing<BodyAspect>((context, bodyAspect) => context.IndexDocument
|
||||
.Add("body", bodyAspect.Record.Text, true).Store(false)
|
||||
.Add("format", bodyAspect.Record.Format).Analyze(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,14 @@ namespace Orchard.Core.Common.Handlers {
|
||||
//OnGetDisplayViewModel<CommonAspect>();
|
||||
OnGetEditorViewModel<CommonAspect>(GetEditor);
|
||||
OnUpdateEditorViewModel<CommonAspect>(UpdateEditor);
|
||||
|
||||
OnIndexing<CommonAspect>((context, commonAspect) => context.IndexDocument
|
||||
.Add("type", commonAspect.ContentItem.ContentType).Analyze(false)
|
||||
.Add("author", commonAspect.Owner.UserName).Analyze(false)
|
||||
.Add("created", commonAspect.CreatedUtc ?? _clock.UtcNow).Analyze(false)
|
||||
.Add("published", commonAspect.PublishedUtc ?? _clock.UtcNow).Analyze(false)
|
||||
.Add("modified", commonAspect.ModifiedUtc ?? _clock.UtcNow).Analyze(false)
|
||||
);
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
@@ -36,6 +36,10 @@ namespace Orchard.Core.Common.Handlers {
|
||||
|
||||
OnCreated<RoutableAspect>((context, ra) => routableService.ProcessSlug(ra));
|
||||
|
||||
OnIndexing<RoutableAspect>((context, part) => context.IndexDocument
|
||||
.Add("slug", part.Slug).Analyze(false)
|
||||
.Add("title", part.Title)
|
||||
);
|
||||
}
|
||||
|
||||
private static RouteValueDictionary GetRouteValues(IContentItemDriver driver, ContentItem contentItem) {
|
||||
|
||||
113
src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexDocument.cs
Normal file
113
src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexDocument.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web.Mvc;
|
||||
using Lucene.Net.Documents;
|
||||
using Orchard.Indexing;
|
||||
using Orchard.Mvc.Html;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
namespace Orchard.Core.Indexing.Lucene {
|
||||
|
||||
public class DefaultIndexDocument : IIndexDocument {
|
||||
|
||||
public List<AbstractField> Fields { get; private set; }
|
||||
private AbstractField _previousField;
|
||||
public int Id { get; private set; }
|
||||
|
||||
public DefaultIndexDocument(int documentId) {
|
||||
Fields = new List<AbstractField>();
|
||||
SetContentItemId(documentId);
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, string value) {
|
||||
return Add(name, value, false);
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, string value, bool removeTags) {
|
||||
AppendPreviousField();
|
||||
if(removeTags) {
|
||||
value = value.RemoveTags();
|
||||
}
|
||||
|
||||
_previousField = new Field(name, value, Field.Store.YES, Field.Index.ANALYZED);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, DateTime value) {
|
||||
AppendPreviousField();
|
||||
_previousField = new Field(name, DateTools.DateToString(value, DateTools.Resolution.SECOND), Field.Store.YES, Field.Index.NOT_ANALYZED);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, int value) {
|
||||
AppendPreviousField();
|
||||
_previousField = new NumericField(name, Field.Store.YES, true).SetIntValue(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, bool value) {
|
||||
AppendPreviousField();
|
||||
_previousField = new Field(name, value.ToString().ToLower(), Field.Store.YES, Field.Index.NOT_ANALYZED);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, float value) {
|
||||
AppendPreviousField();
|
||||
_previousField = new NumericField(name, Field.Store.YES, true).SetFloatValue(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Add(string name, object value) {
|
||||
AppendPreviousField();
|
||||
_previousField = new Field(name, value.ToString(), Field.Store.NO, Field.Index.NOT_ANALYZED);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Store(bool store) {
|
||||
EnsurePreviousField();
|
||||
if(store != _previousField.IsStored()) {
|
||||
var index = _previousField.IsTokenized() ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED;
|
||||
_previousField = new Field(_previousField.Name(), _previousField.StringValue(), store ? Field.Store.YES : Field.Store.NO, index);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument Analyze(bool analyze) {
|
||||
EnsurePreviousField();
|
||||
if (_previousField.IsTokenized() == analyze) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var index = analyze ? Field.Index.ANALYZED : Field.Index.NOT_ANALYZED;
|
||||
var store = _previousField.IsStored() ? Field.Store.YES : Field.Store.NO;
|
||||
_previousField = new Field(_previousField.Name(), _previousField.StringValue(), store, index);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IIndexDocument SetContentItemId(int id) {
|
||||
Id = id;
|
||||
Fields.Add(new Field("id", id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void AppendPreviousField() {
|
||||
if (_previousField == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Fields.Add(_previousField);
|
||||
_previousField = null;
|
||||
}
|
||||
|
||||
|
||||
public void PrepareForIndexing() {
|
||||
AppendPreviousField();
|
||||
}
|
||||
|
||||
private void EnsurePreviousField() {
|
||||
if(_previousField == null) {
|
||||
throw new ApplicationException("Operation can't be applied in this context.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
128
src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexProvider.cs
Normal file
128
src/Orchard.Web/Core/Indexing/Lucene/DefaultIndexProvider.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Lucene.Net.Analysis;
|
||||
using Lucene.Net.Analysis.Standard;
|
||||
using Lucene.Net.Documents;
|
||||
using Lucene.Net.Index;
|
||||
using Lucene.Net.Store;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.FileSystems.AppData;
|
||||
using Orchard.Indexing;
|
||||
using Directory = Lucene.Net.Store.Directory;
|
||||
using Version = Lucene.Net.Util.Version;
|
||||
using Orchard.Logging;
|
||||
|
||||
namespace Orchard.Core.Indexing.Lucene {
|
||||
/// <summary>
|
||||
/// Represents the default implementation of an IIndexProvider based on Lucene
|
||||
/// </summary>
|
||||
public class DefaultIndexProvider : IIndexProvider {
|
||||
private readonly IAppDataFolder _appDataFolder;
|
||||
private readonly ShellSettings _shellSettings;
|
||||
public static readonly Version LuceneVersion = Version.LUCENE_29;
|
||||
private readonly Analyzer _analyzer = new StandardAnalyzer(LuceneVersion);
|
||||
private readonly string _basePath;
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public DefaultIndexProvider(IAppDataFolder appDataFolder, ShellSettings shellSettings) {
|
||||
_appDataFolder = appDataFolder;
|
||||
_shellSettings = shellSettings;
|
||||
|
||||
// TODO: (sebros) Find a common way to get where tenant's specific files should go. "Sites/Tenant" is hard coded in multiple places
|
||||
_basePath = Path.Combine("Sites", _shellSettings.Name, "Indexes");
|
||||
|
||||
Logger = NullLogger.Instance;
|
||||
|
||||
// Ensures the directory exists
|
||||
var directory = new DirectoryInfo(_appDataFolder.MapPath(_basePath));
|
||||
if(!directory.Exists) {
|
||||
directory.Create();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Directory GetDirectory(string indexName) {
|
||||
var directoryInfo = new DirectoryInfo(_appDataFolder.MapPath(Path.Combine(_basePath, indexName)));
|
||||
return FSDirectory.Open(directoryInfo);
|
||||
}
|
||||
|
||||
private static Document CreateDocument(DefaultIndexDocument indexDocument) {
|
||||
var doc = new Document();
|
||||
|
||||
indexDocument.PrepareForIndexing();
|
||||
foreach(var field in indexDocument.Fields) {
|
||||
doc.Add(field);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
public bool Exists(string indexName) {
|
||||
return new DirectoryInfo(_appDataFolder.MapPath(Path.Combine(_basePath, indexName))).Exists;
|
||||
}
|
||||
|
||||
public void CreateIndex(string indexName) {
|
||||
var writer = new IndexWriter(GetDirectory(indexName), _analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED);
|
||||
writer.Close();
|
||||
|
||||
Logger.Information("Index [{0}] created", indexName);
|
||||
}
|
||||
|
||||
public void DeleteIndex(string indexName) {
|
||||
new DirectoryInfo(Path.Combine(_appDataFolder.MapPath(Path.Combine(_basePath, indexName))))
|
||||
.Delete(true);
|
||||
}
|
||||
|
||||
public void Store(string indexName, IIndexDocument indexDocument) {
|
||||
Store(indexName, (DefaultIndexDocument)indexDocument);
|
||||
}
|
||||
|
||||
public void Store(string indexName, DefaultIndexDocument indexDocument) {
|
||||
var writer = new IndexWriter(GetDirectory(indexName), _analyzer, false, IndexWriter.MaxFieldLength.UNLIMITED);
|
||||
|
||||
try {
|
||||
var doc = CreateDocument(indexDocument);
|
||||
writer.AddDocument(doc);
|
||||
Logger.Debug("Document [{0}] indexed", indexDocument.Id);
|
||||
}
|
||||
catch ( Exception ex ) {
|
||||
Logger.Error(ex, "An unexpected error occured while removing the document [{0}] from the index [{1}].", indexDocument.Id, indexName);
|
||||
}
|
||||
finally {
|
||||
writer.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Delete(string indexName, int id) {
|
||||
var reader = IndexReader.Open(GetDirectory(indexName), false);
|
||||
|
||||
try {
|
||||
var term = new Term("id", id.ToString());
|
||||
if ( reader.DeleteDocuments(term) != 0 ) {
|
||||
Logger.Error("The document [{0}] could not be removed from the index [{1}]", id, indexName);
|
||||
}
|
||||
else {
|
||||
Logger.Debug("Document [{0}] removed from index", id);
|
||||
}
|
||||
}
|
||||
catch ( Exception ex ) {
|
||||
Logger.Error(ex, "An unexpected error occured while removing the document [{0}] from the index [{1}].", id, indexName);
|
||||
}
|
||||
finally {
|
||||
reader.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public IIndexDocument New(int documentId) {
|
||||
return new DefaultIndexDocument(documentId);
|
||||
}
|
||||
|
||||
public ISearchBuilder CreateSearchBuilder(string indexName) {
|
||||
return new DefaultSearchBuilder(GetDirectory(indexName));
|
||||
}
|
||||
|
||||
public IIndexDocument Get(string indexName, int id) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
194
src/Orchard.Web/Core/Indexing/Lucene/DefaultSearchBuilder.cs
Normal file
194
src/Orchard.Web/Core/Indexing/Lucene/DefaultSearchBuilder.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Lucene.Net.Index;
|
||||
using Lucene.Net.Search;
|
||||
using Lucene.Net.Store;
|
||||
using Orchard.Logging;
|
||||
using Lucene.Net.Documents;
|
||||
using Orchard.Indexing;
|
||||
|
||||
namespace Orchard.Core.Indexing.Lucene {
|
||||
public class DefaultSearchBuilder : ISearchBuilder {
|
||||
|
||||
private const int MaxResults = Int16.MaxValue;
|
||||
|
||||
private readonly Directory _directory;
|
||||
|
||||
private readonly Dictionary<string, Query[]> _fields;
|
||||
private int _count;
|
||||
private int _skip;
|
||||
private readonly Dictionary<string, DateTime> _before;
|
||||
private readonly Dictionary<string, DateTime> _after;
|
||||
private string _sort;
|
||||
private bool _sortDescending;
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public DefaultSearchBuilder(Directory directory) {
|
||||
_directory = directory;
|
||||
Logger = NullLogger.Instance;
|
||||
|
||||
_count = MaxResults;
|
||||
_skip = 0;
|
||||
_before = new Dictionary<string, DateTime>();
|
||||
_after = new Dictionary<string, DateTime>();
|
||||
_fields = new Dictionary<string, Query[]>();
|
||||
_sort = String.Empty;
|
||||
_sortDescending = true;
|
||||
}
|
||||
|
||||
public ISearchBuilder Parse(string query) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ISearchBuilder WithField(string field, string value) {
|
||||
return WithField(field, value, true);
|
||||
}
|
||||
|
||||
public ISearchBuilder WithField(string field, string value, bool wildcardSearch) {
|
||||
|
||||
_fields[field] = value.Split(' ')
|
||||
.Where(k => !String.IsNullOrWhiteSpace(k))
|
||||
.Select(k => wildcardSearch ? (Query)new PrefixQuery(new Term(field, k)) : new TermQuery(new Term(k)))
|
||||
.ToArray();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ISearchBuilder After(string name, DateTime date) {
|
||||
_after[name] = date;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ISearchBuilder Before(string name, DateTime date) {
|
||||
_before[name] = date;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ISearchBuilder SortBy(string name) {
|
||||
_sort = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ISearchBuilder Ascending() {
|
||||
_sortDescending = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ISearchBuilder Slice(int skip, int count) {
|
||||
if ( skip < 0 ) {
|
||||
throw new ArgumentException("Skip must be greater or equal to zero");
|
||||
}
|
||||
|
||||
if ( count <= 0 ) {
|
||||
throw new ArgumentException("Count must be greater than zero");
|
||||
}
|
||||
|
||||
_skip = skip;
|
||||
_count = count;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private Query CreateQuery() {
|
||||
var query = new BooleanQuery();
|
||||
|
||||
if ( _fields.Keys.Count > 0 ) { // apply specific filters if defined
|
||||
foreach ( var filters in _fields.Values ) {
|
||||
foreach(var filter in filters)
|
||||
query.Add(filter, BooleanClause.Occur.SHOULD);
|
||||
}
|
||||
}
|
||||
|
||||
// apply date range filter ?
|
||||
foreach(string name in _before.Keys.Concat(_after.Keys)) {
|
||||
if ((_before.ContainsKey(name) && _before[name] != DateTime.MaxValue) || (_after.ContainsKey(name) && _after[name] != DateTime.MinValue)) {
|
||||
var filter = new TermRangeQuery("date",
|
||||
DateTools.DateToString(_after.ContainsKey(name) ? _after[name] : DateTime.MinValue, DateTools.Resolution.SECOND),
|
||||
DateTools.DateToString(_before.ContainsKey(name) ? _before[name] : DateTime.MaxValue, DateTools.Resolution.SECOND),
|
||||
true, true);
|
||||
query.Add(filter, BooleanClause.Occur.MUST);
|
||||
}
|
||||
}
|
||||
|
||||
if ( query.Clauses().Count == 0 ) { // get all documents ?
|
||||
query.Add(new TermRangeQuery("id", "0", "9", true, true), BooleanClause.Occur.SHOULD);
|
||||
}
|
||||
|
||||
Logger.Debug("New search query: {0}", query.ToString());
|
||||
return query;
|
||||
}
|
||||
|
||||
public IEnumerable<ISearchHit> Search() {
|
||||
var query = CreateQuery();
|
||||
|
||||
var searcher = new IndexSearcher(_directory, true);
|
||||
|
||||
try {
|
||||
var sort = String.IsNullOrEmpty(_sort)
|
||||
? Sort.RELEVANCE
|
||||
: new Sort(new SortField(_sort, CultureInfo.InvariantCulture, _sortDescending));
|
||||
var collector = TopFieldCollector.create(
|
||||
sort,
|
||||
_count + _skip,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true);
|
||||
|
||||
searcher.Search(query, collector);
|
||||
|
||||
var results = new List<DefaultSearchHit>();
|
||||
|
||||
foreach ( var scoreDoc in collector.TopDocs().scoreDocs.Skip(_skip) ) {
|
||||
results.Add(new DefaultSearchHit(searcher.Doc(scoreDoc.doc), scoreDoc.score));
|
||||
}
|
||||
|
||||
Logger.Information("Search results: {0}", results.Count);
|
||||
return results;
|
||||
}
|
||||
finally {
|
||||
searcher.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int Count() {
|
||||
var query = CreateQuery();
|
||||
|
||||
var searcher = new IndexSearcher(_directory, true);
|
||||
try {
|
||||
var hits = searcher.Search(query, Int16.MaxValue);
|
||||
Logger.Information("Search results: {0}", hits.scoreDocs.Length);
|
||||
var length = hits.scoreDocs.Length;
|
||||
return Math.Min(length - _skip, _count) ;
|
||||
}
|
||||
finally {
|
||||
searcher.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ISearchHit Get(int documentId) {
|
||||
var query = new TermQuery(new Term("id", documentId.ToString()));
|
||||
|
||||
var searcher = new IndexSearcher(_directory, true);
|
||||
try {
|
||||
var hits = searcher.Search(query, 1);
|
||||
Logger.Information("Search results: {0}", hits.scoreDocs.Length);
|
||||
if ( hits.scoreDocs.Length > 0 ) {
|
||||
return new DefaultSearchHit(searcher.Doc(hits.scoreDocs[0].doc), hits.scoreDocs[0].score);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
searcher.Close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
40
src/Orchard.Web/Core/Indexing/Lucene/DefaultSearchHit.cs
Normal file
40
src/Orchard.Web/Core/Indexing/Lucene/DefaultSearchHit.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Lucene.Net.Documents;
|
||||
using System.Globalization;
|
||||
using Lucene.Net.Util;
|
||||
using Orchard.Indexing;
|
||||
|
||||
namespace Orchard.Core.Indexing.Lucene {
|
||||
public class DefaultSearchHit : ISearchHit {
|
||||
private readonly Document _doc;
|
||||
private readonly float _score;
|
||||
|
||||
public float Score { get { return _score; } }
|
||||
|
||||
public DefaultSearchHit(Document document, float score) {
|
||||
_doc = document;
|
||||
_score = score;
|
||||
}
|
||||
|
||||
public int Id { get { return int.Parse(GetString("id")); } }
|
||||
|
||||
public int GetInt(string name) {
|
||||
return NumericUtils.PrefixCodedToInt(_doc.GetField(name).StringValue());
|
||||
}
|
||||
|
||||
public float GetFloat(string name) {
|
||||
return float.Parse(_doc.GetField(name).StringValue(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public bool GetBoolean(string name) {
|
||||
return bool.Parse(_doc.GetField(name).StringValue());
|
||||
}
|
||||
|
||||
public string GetString(string name) {
|
||||
return _doc.GetField(name).StringValue();
|
||||
}
|
||||
|
||||
public System.DateTime GetDateTime(string name) {
|
||||
return DateTools.StringToDate(_doc.GetField(name).StringValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace Orchard.Core.Indexing.Models {
|
||||
public class IndexingSettingsRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual DateTime? LatestIndexingUtc { get; set; }
|
||||
}
|
||||
}
|
||||
36
src/Orchard.Web/Core/Indexing/Models/IndexingTask.cs
Normal file
36
src/Orchard.Web/Core/Indexing/Models/IndexingTask.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Tasks.Indexing;
|
||||
|
||||
namespace Orchard.Core.Indexing.Models {
|
||||
public class IndexingTask : IIndexingTask {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IndexingTaskRecord _record;
|
||||
private ContentItem _item;
|
||||
private bool _itemInitialized;
|
||||
|
||||
public IndexingTask(IContentManager contentManager, IndexingTaskRecord record) {
|
||||
// in spite of appearances, this is actually a created class, not IoC,
|
||||
// but dependencies are passed in for lazy initialization purposes
|
||||
_contentManager = contentManager;
|
||||
_record = record;
|
||||
}
|
||||
|
||||
public DateTime? CreatedUtc {
|
||||
get { return _record.CreatedUtc; }
|
||||
}
|
||||
|
||||
public ContentItem ContentItem {
|
||||
get {
|
||||
if (!_itemInitialized) {
|
||||
if (_record.ContentItemRecord != null) {
|
||||
_item = _contentManager.Get(
|
||||
_record.ContentItemRecord.Id, VersionOptions.Published);
|
||||
}
|
||||
_itemInitialized = true;
|
||||
}
|
||||
return _item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/Orchard.Web/Core/Indexing/Models/IndexingTaskRecord.cs
Normal file
10
src/Orchard.Web/Core/Indexing/Models/IndexingTaskRecord.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using Orchard.ContentManagement.Records;
|
||||
|
||||
namespace Orchard.Core.Indexing.Models {
|
||||
public class IndexingTaskRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual DateTime? CreatedUtc { get; set; }
|
||||
public virtual ContentItemRecord ContentItemRecord { get; set; }
|
||||
}
|
||||
}
|
||||
10
src/Orchard.Web/Core/Indexing/Module.txt
Normal file
10
src/Orchard.Web/Core/Indexing/Module.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
name: Indexing
|
||||
antiforgery: enabled
|
||||
author: The Orchard Team
|
||||
website: http://orchardproject.net
|
||||
version: 0.1
|
||||
orchardversion: 0.1.2010.0312
|
||||
features:
|
||||
Indexing:
|
||||
Description: Indexing services based on Lucene.
|
||||
Category: Core
|
||||
@@ -0,0 +1,30 @@
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Tasks.Indexing;
|
||||
|
||||
namespace Orchard.Core.Indexing.Services {
|
||||
/// <summary>
|
||||
/// Intercepts the ContentHandler events to create indexing tasks when a content item
|
||||
/// is published, and to delete them when the content item is unpublished.
|
||||
/// </summary>
|
||||
public class CreateIndexingTaskHandler : ContentHandler {
|
||||
private readonly IIndexingTaskManager _indexingTaskManager;
|
||||
|
||||
public CreateIndexingTaskHandler(IIndexingTaskManager indexingTaskManager) {
|
||||
_indexingTaskManager = indexingTaskManager;
|
||||
|
||||
OnPublishing<ContentPart<CommonRecord>>(CreateIndexingTask);
|
||||
OnRemoved<ContentPart<CommonRecord>>(RemoveIndexingTask);
|
||||
}
|
||||
|
||||
void CreateIndexingTask(PublishContentContext context, ContentPart<CommonRecord> part) {
|
||||
_indexingTaskManager.CreateTask(context.ContentItem);
|
||||
}
|
||||
|
||||
void RemoveIndexingTask(RemoveContentContext context, ContentPart<CommonRecord> part) {
|
||||
_indexingTaskManager.DeleteTasks(context.ContentItem);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
108
src/Orchard.Web/Core/Indexing/Services/IndexingTaskExecutor.cs
Normal file
108
src/Orchard.Web/Core/Indexing/Services/IndexingTaskExecutor.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Data;
|
||||
using Orchard.Indexing;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Services;
|
||||
using Orchard.Tasks;
|
||||
using Orchard.Core.Indexing.Models;
|
||||
|
||||
namespace Orchard.Core.Indexing.Services {
|
||||
/// <summary>
|
||||
/// Contains the logic which is regularly executed to retrieve index information from multiple content handlers.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class IndexingTaskExecutor : IBackgroundTask {
|
||||
private readonly IClock _clock;
|
||||
private readonly IRepository<IndexingTaskRecord> _repository;
|
||||
private readonly IRepository<IndexingSettingsRecord> _settings;
|
||||
private readonly IEnumerable<IContentHandler> _handlers;
|
||||
private IIndexProvider _indexProvider;
|
||||
private IIndexManager _indexManager;
|
||||
private readonly IContentManager _contentManager;
|
||||
private const string SearchIndexName = "search";
|
||||
|
||||
public IndexingTaskExecutor(
|
||||
IClock clock,
|
||||
IRepository<IndexingTaskRecord> repository,
|
||||
IRepository<IndexingSettingsRecord> settings,
|
||||
IEnumerable<IContentHandler> handlers,
|
||||
IIndexManager indexManager,
|
||||
IContentManager contentManager) {
|
||||
_clock = clock;
|
||||
_repository = repository;
|
||||
_settings = settings;
|
||||
_indexManager = indexManager;
|
||||
_handlers = handlers;
|
||||
_contentManager = contentManager;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public void Sweep() {
|
||||
|
||||
if(!_indexManager.HasIndexProvider()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_indexProvider = _indexManager.GetSearchIndexProvider();
|
||||
|
||||
// retrieve last processed index time
|
||||
var settingsRecord = _settings.Table.FirstOrDefault();
|
||||
|
||||
if (settingsRecord == null) {
|
||||
_settings.Create(settingsRecord = new IndexingSettingsRecord { LatestIndexingUtc = new DateTime(1980, 1, 1)});
|
||||
}
|
||||
|
||||
var lastIndexing = settingsRecord.LatestIndexingUtc;
|
||||
settingsRecord.LatestIndexingUtc = _clock.UtcNow;
|
||||
|
||||
// retrieved not yet processed tasks
|
||||
var taskRecords = _repository.Fetch(x => x.CreatedUtc >= lastIndexing)
|
||||
.ToArray();
|
||||
|
||||
if (taskRecords.Length == 0)
|
||||
return;
|
||||
|
||||
Logger.Information("Processing {0} indexing tasks", taskRecords.Length);
|
||||
|
||||
|
||||
if(!_indexProvider.Exists(SearchIndexName)) {
|
||||
_indexProvider.CreateIndex(SearchIndexName);
|
||||
}
|
||||
|
||||
foreach (var taskRecord in taskRecords) {
|
||||
|
||||
try {
|
||||
var task = new IndexingTask(_contentManager, taskRecord);
|
||||
var context = new IndexContentContext {
|
||||
ContentItem = task.ContentItem,
|
||||
IndexDocument = _indexProvider.New(task.ContentItem.Id)
|
||||
};
|
||||
|
||||
// dispatch to handlers to retrieve index information
|
||||
foreach (var handler in _handlers) {
|
||||
handler.Indexing(context);
|
||||
}
|
||||
|
||||
_indexProvider.Store(SearchIndexName, context.IndexDocument);
|
||||
|
||||
foreach ( var handler in _handlers ) {
|
||||
handler.Indexed(context);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Warning(ex, "Unable to process indexing task #{0}", taskRecord.Id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_settings.Update(settingsRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
101
src/Orchard.Web/Core/Indexing/Services/IndexingTaskManager.cs
Normal file
101
src/Orchard.Web/Core/Indexing/Services/IndexingTaskManager.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Data;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Tasks.Scheduling;
|
||||
using Orchard.Utility.Extensions;
|
||||
using Orchard.Tasks.Indexing;
|
||||
using Orchard.Core.Indexing.Models;
|
||||
using Orchard.Services;
|
||||
|
||||
namespace Orchard.Core.Indexing.Services {
|
||||
[UsedImplicitly]
|
||||
public class IndexingTaskManager : IIndexingTaskManager {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IRepository<IndexingTaskRecord> _repository;
|
||||
private readonly IRepository<IndexingSettingsRecord> _settings;
|
||||
private readonly IClock _clock;
|
||||
|
||||
public IndexingTaskManager(
|
||||
IContentManager contentManager,
|
||||
IRepository<IndexingTaskRecord> repository,
|
||||
IRepository<IndexingSettingsRecord> settings,
|
||||
IClock clock) {
|
||||
_clock = clock;
|
||||
_repository = repository;
|
||||
_contentManager = contentManager;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public void CreateTask(ContentItem contentItem) {
|
||||
if (contentItem == null) {
|
||||
throw new ArgumentNullException("contentItem");
|
||||
}
|
||||
|
||||
// remove previous tasks for the same content item
|
||||
var tasks = _repository
|
||||
.Fetch(x => x.Id == contentItem.Id )
|
||||
.ToArray();
|
||||
|
||||
foreach (var task in tasks) {
|
||||
_repository.Delete(task);
|
||||
}
|
||||
|
||||
var taskRecord = new IndexingTaskRecord {
|
||||
CreatedUtc = _clock.UtcNow,
|
||||
ContentItemRecord = contentItem.Record
|
||||
};
|
||||
|
||||
_repository.Create(taskRecord);
|
||||
|
||||
Logger.Information("Indexing task created for [{0}:{1}]", contentItem.ContentType, contentItem.Id);
|
||||
|
||||
}
|
||||
|
||||
public IEnumerable<IIndexingTask> GetTasks(DateTime? createdAfter) {
|
||||
return _repository
|
||||
.Fetch(x => x.CreatedUtc > createdAfter)
|
||||
.Select(x => new IndexingTask(_contentManager, x))
|
||||
.Cast<IIndexingTask>()
|
||||
.ToReadOnlyCollection();
|
||||
}
|
||||
|
||||
public void DeleteTasks(DateTime? createdBefore) {
|
||||
Logger.Debug("Deleting Indexing tasks created before {0}", createdBefore);
|
||||
|
||||
var tasks = _repository
|
||||
.Fetch(x => x.CreatedUtc <= createdBefore);
|
||||
|
||||
foreach (var task in tasks) {
|
||||
_repository.Delete(task);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteTasks(ContentItem contentItem) {
|
||||
Logger.Debug("Deleting Indexing tasks for ContentItem [{0}:{1}]", contentItem.ContentType, contentItem.Id);
|
||||
|
||||
var tasks = _repository
|
||||
.Fetch(x => x.Id == contentItem.Id);
|
||||
|
||||
foreach (var task in tasks) {
|
||||
_repository.Delete(task);
|
||||
}
|
||||
}
|
||||
|
||||
public void RebuildIndex() {
|
||||
var settingsRecord = _settings.Table.FirstOrDefault();
|
||||
if (settingsRecord == null) {
|
||||
_settings.Create(settingsRecord = new IndexingSettingsRecord() );
|
||||
}
|
||||
|
||||
settingsRecord.LatestIndexingUtc = new DateTime(1980, 1, 1);
|
||||
_settings.Update(settingsRecord);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ActionResult Index(NavigationManagementViewModel model) {
|
||||
if (!_services.Authorizer.Authorize(Permissions.ManageMainMenu, T("Not allowed to manage the main menu")))
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Orchard.Core.Navigation.Drivers {
|
||||
}
|
||||
|
||||
public virtual IUser CurrentUser { get; set; }
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
protected override DriverResult Editor(MenuPart part) {
|
||||
if (!_authorizationService.TryCheckAccess(Permissions.ManageMainMenu, CurrentUser, part))
|
||||
|
||||
@@ -39,6 +39,10 @@
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Lucene.Net, Version=2.9.2.2, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\lib\lucene.net\Lucene.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
@@ -105,6 +109,16 @@
|
||||
<Compile Include="Feeds\Rss\RssResult.cs" />
|
||||
<Compile Include="HomePage\Controllers\HomeController.cs" />
|
||||
<Compile Include="HomePage\Routes.cs" />
|
||||
<Compile Include="Indexing\Lucene\DefaultIndexDocument.cs" />
|
||||
<Compile Include="Indexing\Lucene\DefaultIndexProvider.cs" />
|
||||
<Compile Include="Indexing\Lucene\DefaultSearchBuilder.cs" />
|
||||
<Compile Include="Indexing\Lucene\DefaultSearchHit.cs" />
|
||||
<Compile Include="Indexing\Models\IndexingSettingsRecord.cs" />
|
||||
<Compile Include="Indexing\Models\IndexingTask.cs" />
|
||||
<Compile Include="Indexing\Models\IndexingTaskRecord.cs" />
|
||||
<Compile Include="Indexing\Services\CreateIndexingTaskHandler.cs" />
|
||||
<Compile Include="Indexing\Services\IndexingTaskExecutor.cs" />
|
||||
<Compile Include="Indexing\Services\IndexingTaskManager.cs" />
|
||||
<Compile Include="Navigation\AdminMenu.cs" />
|
||||
<Compile Include="Navigation\Controllers\AdminController.cs" />
|
||||
<Compile Include="Navigation\Models\MenuItem.cs" />
|
||||
@@ -159,6 +173,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Common\Module.txt" />
|
||||
<Content Include="Indexing\Module.txt" />
|
||||
<Content Include="Settings\Module.txt" />
|
||||
<Content Include="Settings\Views\Admin\Index.ascx" />
|
||||
<Content Include="Web.config" />
|
||||
@@ -209,6 +224,7 @@
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<None Include="App_Data\Localization\en-US\orchard.core.po" />
|
||||
<None Include="App_Data\Localization\fr-FR\orchard.core.po" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Orchard.Blogs.Controllers {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
protected virtual ISite CurrentSite { get; [UsedImplicitly] private set; }
|
||||
public IOrchardServices Services { get; set; }
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Orchard.Blogs.Controllers {
|
||||
}
|
||||
|
||||
public IOrchardServices Services { get; set; }
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ActionResult Create() {
|
||||
if (!Services.Authorizer.Authorize(Permissions.EditBlogPost, T("Not allowed to create blog post")))
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Orchard.Blogs.Controllers {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
//TODO: (erikpo) Should think about moving the slug parameters and get calls and null checks up into a model binder or action filter
|
||||
public ActionResult Item(string blogSlug, string postSlug) {
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Orchard.Blogs.Drivers {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
protected override ContentType GetContentType() {
|
||||
return ContentType;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Orchard.Blogs.Drivers {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
protected override ContentType GetContentType() {
|
||||
return ContentType;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Orchard.MetaData.Controllers
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
//
|
||||
// GET: /ContentTypeList/
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Orchard.Modules.Controllers {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
public IOrchardServices Services { get; set; }
|
||||
|
||||
public ActionResult Index() {
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Orchard.Modules.Services {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
public IOrchardServices Services { get; set; }
|
||||
|
||||
public IModule GetModuleByName(string moduleName) {
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Orchard.MultiTenancy.Controllers {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
public IOrchardServices Services { get; set; }
|
||||
|
||||
public ActionResult Index() {
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Orchard.Pages.Controllers {
|
||||
|
||||
protected virtual ISite CurrentSite { get; [UsedImplicitly] private set; }
|
||||
public IOrchardServices Services { get; private set; }
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ActionResult List(PagesOptions options) {
|
||||
// Default options
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Orchard.Pages.Controllers {
|
||||
}
|
||||
|
||||
public IOrchardServices Services { get; set; }
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ActionResult Item(string slug) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.AccessFrontEnd, T("Couldn't view page")))
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Orchard.Pages.Drivers {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
protected override ContentType GetContentType() {
|
||||
return ContentType;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Orchard.Pages.Services {
|
||||
}
|
||||
|
||||
public IOrchardServices Services { get; private set; }
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public string GetProviderName() {
|
||||
return "PageHomePageProvider";
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Orchard.Setup.Controllers {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
private ActionResult IndexViewResult(SetupViewModel model) {
|
||||
string message;
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Orchard.Setup.Services {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ShellSettings Prime() {
|
||||
return _shellSettings;
|
||||
@@ -60,6 +60,7 @@ namespace Orchard.Setup.Services {
|
||||
"HomePage",
|
||||
"Navigation",
|
||||
"Scheduling",
|
||||
"Indexing",
|
||||
"Settings",
|
||||
"XmlRpc",
|
||||
"Orchard.Users",
|
||||
@@ -134,6 +135,7 @@ namespace Orchard.Setup.Services {
|
||||
// add default culture
|
||||
var cultureManager = environment.Resolve<ICultureManager>();
|
||||
cultureManager.AddCulture("en-US");
|
||||
cultureManager.AddCulture("fr-FR");
|
||||
|
||||
var contentManager = environment.Resolve<IContentManager>();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Orchard.Themes.Services {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public void ZoneRendering(ZoneRenderContext context) {
|
||||
#if DEBUG
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace Orchard.ContentManagement.Drivers {
|
||||
void IContentHandler.Published(PublishContentContext context) { }
|
||||
void IContentHandler.Removing(RemoveContentContext context) { }
|
||||
void IContentHandler.Removed(RemoveContentContext context) { }
|
||||
void IContentHandler.Indexing(IndexContentContext context) { }
|
||||
void IContentHandler.Indexed(IndexContentContext context) { }
|
||||
|
||||
|
||||
void IContentHandler.GetContentItemMetadata(GetContentItemMetadataContext context) {
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace Orchard.ContentManagement.Drivers {
|
||||
void IContentHandler.Published(PublishContentContext context) { }
|
||||
void IContentHandler.Removing(RemoveContentContext context) { }
|
||||
void IContentHandler.Removed(RemoveContentContext context) { }
|
||||
void IContentHandler.Indexing(IndexContentContext context) { }
|
||||
void IContentHandler.Indexed(IndexContentContext context) { }
|
||||
|
||||
void IContentHandler.GetContentItemMetadata(GetContentItemMetadataContext context) { }
|
||||
|
||||
|
||||
@@ -57,6 +57,14 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
Filters.Add(new InlineStorageFilter<TPart> { OnRemoved = handler });
|
||||
}
|
||||
|
||||
protected void OnIndexing<TPart>(Action<IndexContentContext, TPart> handler) where TPart : class, IContent {
|
||||
Filters.Add(new InlineStorageFilter<TPart> { OnIndexing = handler });
|
||||
}
|
||||
|
||||
protected void OnIndexed<TPart>(Action<IndexContentContext, TPart> handler) where TPart : class, IContent {
|
||||
Filters.Add(new InlineStorageFilter<TPart> { OnIndexed = handler });
|
||||
}
|
||||
|
||||
protected void OnGetContentItemMetadata<TPart>(Action<GetContentItemMetadataContext, TPart> handler) where TPart : class, IContent {
|
||||
Filters.Add(new InlineTemplateFilter<TPart> { OnGetItemMetadata = handler });
|
||||
}
|
||||
@@ -84,6 +92,8 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
public Action<PublishContentContext, TPart> OnPublished { get; set; }
|
||||
public Action<RemoveContentContext, TPart> OnRemoving { get; set; }
|
||||
public Action<RemoveContentContext, TPart> OnRemoved { get; set; }
|
||||
public Action<IndexContentContext, TPart> OnIndexing { get; set; }
|
||||
public Action<IndexContentContext, TPart> OnIndexed { get; set; }
|
||||
protected override void Activated(ActivatedContentContext context, TPart instance) {
|
||||
if (OnActivated != null) OnActivated(context, instance);
|
||||
}
|
||||
@@ -117,6 +127,15 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
protected override void Removed(RemoveContentContext context, TPart instance) {
|
||||
if (OnRemoved != null) OnRemoved(context, instance);
|
||||
}
|
||||
protected override void Indexing(IndexContentContext context, TPart instance) {
|
||||
if ( OnIndexing != null )
|
||||
OnIndexing(context, instance);
|
||||
}
|
||||
protected override void Indexed(IndexContentContext context, TPart instance) {
|
||||
if ( OnIndexed != null )
|
||||
OnIndexed(context, instance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class InlineTemplateFilter<TPart> : TemplateFilterBase<TPart> where TPart : class, IContent {
|
||||
@@ -214,6 +233,17 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
Removed(context);
|
||||
}
|
||||
|
||||
void IContentHandler.Indexing(IndexContentContext context) {
|
||||
foreach ( var filter in Filters.OfType<IContentStorageFilter>() )
|
||||
filter.Indexing(context);
|
||||
Indexing(context);
|
||||
}
|
||||
|
||||
void IContentHandler.Indexed(IndexContentContext context) {
|
||||
foreach ( var filter in Filters.OfType<IContentStorageFilter>() )
|
||||
filter.Indexed(context);
|
||||
Indexing(context);
|
||||
}
|
||||
|
||||
void IContentHandler.GetContentItemMetadata(GetContentItemMetadataContext context) {
|
||||
foreach (var filter in Filters.OfType<IContentTemplateFilter>())
|
||||
@@ -254,6 +284,9 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
protected virtual void Removing(RemoveContentContext context) { }
|
||||
protected virtual void Removed(RemoveContentContext context) { }
|
||||
|
||||
protected virtual void Indexing(IndexContentContext context) { }
|
||||
protected virtual void Indexed(IndexContentContext context) { }
|
||||
|
||||
protected virtual void GetItemMetadata(GetContentItemMetadataContext context) { }
|
||||
protected virtual void BuildDisplayModel(BuildDisplayModelContext context) { }
|
||||
protected virtual void BuildEditorModel(BuildEditorModelContext context) { }
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
void Published(PublishContentContext context);
|
||||
void Removing(RemoveContentContext context);
|
||||
void Removed(RemoveContentContext context);
|
||||
void Indexing(IndexContentContext context);
|
||||
void Indexed(IndexContentContext context);
|
||||
|
||||
void GetContentItemMetadata(GetContentItemMetadataContext context);
|
||||
void BuildDisplayModel(BuildDisplayModelContext context);
|
||||
|
||||
@@ -11,5 +11,7 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
void Published(PublishContentContext context);
|
||||
void Removing(RemoveContentContext context);
|
||||
void Removed(RemoveContentContext context);
|
||||
void Indexing(IndexContentContext context);
|
||||
void Indexed(IndexContentContext context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Orchard.Indexing;
|
||||
|
||||
namespace Orchard.ContentManagement.Handlers {
|
||||
public class IndexContentContext {
|
||||
public ContentItem ContentItem { get; set; }
|
||||
public IIndexDocument IndexDocument { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
protected virtual void Published(PublishContentContext context, TPart instance) { }
|
||||
protected virtual void Removing(RemoveContentContext context, TPart instance) { }
|
||||
protected virtual void Removed(RemoveContentContext context, TPart instance) { }
|
||||
protected virtual void Indexing(IndexContentContext context, TPart instance) { }
|
||||
protected virtual void Indexed(IndexContentContext context, TPart instance) { }
|
||||
|
||||
|
||||
void IContentStorageFilter.Activated(ActivatedContentContext context) {
|
||||
@@ -68,5 +70,16 @@ namespace Orchard.ContentManagement.Handlers {
|
||||
if (context.ContentItem.Is<TPart>())
|
||||
Removed(context, context.ContentItem.As<TPart>());
|
||||
}
|
||||
|
||||
void IContentStorageFilter.Indexing(IndexContentContext context) {
|
||||
if ( context.ContentItem.Is<TPart>() )
|
||||
Indexing(context, context.ContentItem.As<TPart>());
|
||||
}
|
||||
|
||||
void IContentStorageFilter.Indexed(IndexContentContext context) {
|
||||
if ( context.ContentItem.Is<TPart>() )
|
||||
Indexed(context, context.ContentItem.As<TPart>());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.IO;
|
||||
using System.Web.Hosting;
|
||||
using Autofac;
|
||||
using Autofac.Configuration;
|
||||
using Autofac.Integration.Web;
|
||||
using Orchard.Caching;
|
||||
using Orchard.Environment.AutofacUtil;
|
||||
using Orchard.Environment.Configuration;
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Orchard.Environment.Topology {
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
private Localizer T { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
#region Implementation of IShellDescriptorCache
|
||||
|
||||
|
||||
25
src/Orchard/Indexing/DefaultIndexManager.cs
Normal file
25
src/Orchard/Indexing/DefaultIndexManager.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.Indexing {
|
||||
public class DefaultIndexManager : IIndexManager {
|
||||
|
||||
private readonly IEnumerable<IIndexProvider> _indexProviders;
|
||||
|
||||
public DefaultIndexManager(IEnumerable<IIndexProvider> indexProviders) {
|
||||
_indexProviders = indexProviders;
|
||||
}
|
||||
|
||||
#region IIndexManager Members
|
||||
|
||||
public bool HasIndexProvider() {
|
||||
return _indexProviders.AsQueryable().Count() > 0;
|
||||
}
|
||||
|
||||
public IIndexProvider GetSearchIndexProvider() {
|
||||
return _indexProviders.AsQueryable().FirstOrDefault();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
29
src/Orchard/Indexing/IIndexDocument.cs
Normal file
29
src/Orchard/Indexing/IIndexDocument.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.Indexing {
|
||||
|
||||
public interface IIndexDocument {
|
||||
|
||||
IIndexDocument SetContentItemId(int documentId);
|
||||
|
||||
IIndexDocument Add(string name, string value);
|
||||
IIndexDocument Add(string name, string value, bool removeTags);
|
||||
IIndexDocument Add(string name, DateTime value);
|
||||
IIndexDocument Add(string name, int value);
|
||||
IIndexDocument Add(string name, bool value);
|
||||
IIndexDocument Add(string name, float value);
|
||||
|
||||
/// <summary>
|
||||
/// Whether to store the original value to the index.
|
||||
/// </summary>
|
||||
IIndexDocument Store(bool store);
|
||||
|
||||
/// <summary>
|
||||
/// Whether the content should be tokenized or not. If not, value will be taken as a whole.
|
||||
/// </summary>
|
||||
IIndexDocument Analyze(bool analyze);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
7
src/Orchard/Indexing/IIndexManager.cs
Normal file
7
src/Orchard/Indexing/IIndexManager.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Orchard.Indexing {
|
||||
public interface IIndexManager : IDependency {
|
||||
|
||||
bool HasIndexProvider();
|
||||
IIndexProvider GetSearchIndexProvider();
|
||||
}
|
||||
}
|
||||
45
src/Orchard/Indexing/IIndexProvider.cs
Normal file
45
src/Orchard/Indexing/IIndexProvider.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
namespace Orchard.Indexing {
|
||||
public interface IIndexProvider : IDependency {
|
||||
/// <summary>
|
||||
/// Creates a new index
|
||||
/// </summary>
|
||||
void CreateIndex(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an index is already existing or not
|
||||
/// </summary>
|
||||
bool Exists(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an existing index
|
||||
/// </summary>
|
||||
void DeleteIndex(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Loads an existing document
|
||||
/// </summary>
|
||||
IIndexDocument Get(string indexName, int documentId);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty document
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IIndexDocument New(int documentId);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new document to the index
|
||||
/// </summary>
|
||||
void Store(string indexName, IIndexDocument indexDocument);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an existing document from the index
|
||||
/// </summary>
|
||||
void Delete(string indexName, int id);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a search builder for this provider
|
||||
/// </summary>
|
||||
/// <returns>A search builder instance</returns>
|
||||
ISearchBuilder CreateSearchBuilder(string indexName);
|
||||
}
|
||||
}
|
||||
24
src/Orchard/Indexing/ISearchBuilder.cs
Normal file
24
src/Orchard/Indexing/ISearchBuilder.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.Indexing {
|
||||
public interface ISearchBuilder {
|
||||
|
||||
ISearchBuilder Parse(string query);
|
||||
|
||||
ISearchBuilder WithField(string field, string value);
|
||||
ISearchBuilder WithField(string field, string value, bool wildcardSearch);
|
||||
|
||||
ISearchBuilder After(string name, DateTime date);
|
||||
ISearchBuilder Before(string name, DateTime date);
|
||||
ISearchBuilder SortBy(string name);
|
||||
ISearchBuilder Ascending();
|
||||
|
||||
ISearchBuilder Slice(int skip, int count);
|
||||
IEnumerable<ISearchHit> Search();
|
||||
ISearchHit Get(int documentId);
|
||||
int Count();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
13
src/Orchard/Indexing/ISearchHit.cs
Normal file
13
src/Orchard/Indexing/ISearchHit.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
namespace Orchard.Indexing {
|
||||
public interface ISearchHit {
|
||||
int Id { get; }
|
||||
float Score { get; }
|
||||
|
||||
int GetInt(string name);
|
||||
float GetFloat(string name);
|
||||
bool GetBoolean(string name);
|
||||
string GetString(string name);
|
||||
DateTime GetDateTime(string name);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,114 @@
|
||||
namespace Orchard.Localization.Services {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Orchard.FileSystems.WebSite;
|
||||
|
||||
namespace Orchard.Localization.Services {
|
||||
public class DefaultResourceManager : IResourceManager {
|
||||
// This will use the .po files shortly.
|
||||
public string GetLocalizedString(string key, string cultureName) {
|
||||
if (cultureName.Equals("en-US")) {
|
||||
return key;
|
||||
private readonly IWebSiteFolder _webSiteFolder;
|
||||
private readonly ICultureManager _cultureManager;
|
||||
private readonly IList<CultureDictionary> _cultures;
|
||||
const string CoreLocalizationFilePathFormat = "/Core/App_Data/Localization/{0}/orchard.core.po";
|
||||
|
||||
public DefaultResourceManager(ICultureManager cultureManager, IWebSiteFolder webSiteFolder) {
|
||||
_cultureManager = cultureManager;
|
||||
_webSiteFolder = webSiteFolder;
|
||||
_cultures = new List<CultureDictionary>();
|
||||
}
|
||||
|
||||
public string GetLocalizedString(string scope, string text, string cultureName) {
|
||||
if (_cultures.Count == 0) {
|
||||
LoadCultures();
|
||||
}
|
||||
return string.Empty;
|
||||
|
||||
foreach (var culture in _cultures) {
|
||||
if (String.Equals(cultureName, culture.CultureName, StringComparison.OrdinalIgnoreCase)) {
|
||||
string scopedKey = scope + "|" + text;
|
||||
string genericKey = "|" + text;
|
||||
if (culture.Translations.ContainsKey(scopedKey)) {
|
||||
return culture.Translations[scopedKey];
|
||||
}
|
||||
if (culture.Translations.ContainsKey(genericKey)) {
|
||||
return culture.Translations[genericKey];
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private void LoadCultures() {
|
||||
foreach (var culture in _cultureManager.ListCultures()) {
|
||||
_cultures.Add(new CultureDictionary {
|
||||
CultureName = culture,
|
||||
Translations = LoadTranslationsForCulture(culture)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private IDictionary<string, string> LoadTranslationsForCulture(string culture) {
|
||||
string path = string.Format(CoreLocalizationFilePathFormat, culture);
|
||||
string text = _webSiteFolder.ReadFile(path);
|
||||
if (text != null) {
|
||||
return ParseLocalizationStream(text);
|
||||
}
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
private static IDictionary<string, string> ParseLocalizationStream(string text) {
|
||||
Dictionary<string, string> translations = new Dictionary<string, string>();
|
||||
StringReader reader = new StringReader(text);
|
||||
string poLine, id, scope;
|
||||
id = scope = String.Empty;
|
||||
while ((poLine = reader.ReadLine()) != null) {
|
||||
if (poLine.StartsWith("#:")) {
|
||||
scope = ParseScope(poLine);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (poLine.StartsWith("msgid")) {
|
||||
id = ParseId(poLine);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (poLine.StartsWith("msgstr")) {
|
||||
string translation = ParseTranslation(poLine);
|
||||
if (!String.IsNullOrEmpty(id)) {
|
||||
if (!String.IsNullOrEmpty(scope)) {
|
||||
string scopedKey = scope + "|" + id;
|
||||
if (!translations.ContainsKey(scopedKey)) {
|
||||
translations.Add(scopedKey, translation);
|
||||
}
|
||||
}
|
||||
string genericKey = "|" + id;
|
||||
if (!translations.ContainsKey(genericKey)) {
|
||||
translations.Add(genericKey, translation);
|
||||
}
|
||||
}
|
||||
id = scope = String.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return translations;
|
||||
}
|
||||
|
||||
private static string ParseTranslation(string poLine) {
|
||||
return poLine.Substring(6).Trim().Trim('"');
|
||||
}
|
||||
|
||||
private static string ParseId(string poLine) {
|
||||
return poLine.Substring(5).Trim().Trim('"');
|
||||
}
|
||||
|
||||
private static string ParseScope(string poLine) {
|
||||
return poLine.Substring(2).Trim().Trim('"');
|
||||
}
|
||||
|
||||
class CultureDictionary {
|
||||
public string CultureName { get; set; }
|
||||
public IDictionary<string, string> Translations { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Orchard.Localization.Services {
|
||||
public interface IResourceManager : IDependency {
|
||||
string GetLocalizedString(string key, string cultureName);
|
||||
string GetLocalizedString(string scope, string text, string cultureName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Orchard.Localization {
|
||||
Logger.Debug("{0} localizing '{1}'", _scope, textHint);
|
||||
|
||||
string currentCulture = _cultureManager.GetCurrentCulture(HttpContext.Current);
|
||||
var localizedFormat = _resourceManager.GetLocalizedString(textHint, currentCulture);
|
||||
var localizedFormat = _resourceManager.GetLocalizedString(_scope, textHint, currentCulture);
|
||||
|
||||
return args.Length < 1
|
||||
? new LocalizedString(localizedFormat)
|
||||
|
||||
@@ -94,10 +94,8 @@ namespace Orchard.Mvc.Html {
|
||||
#region Excerpt
|
||||
|
||||
public static MvcHtmlString Excerpt(this HtmlHelper html, string markup, int length) {
|
||||
var tagRegex = new Regex("<[^<>]*>", RegexOptions.Singleline);
|
||||
var text = tagRegex.Replace(markup, "");
|
||||
|
||||
return MvcHtmlString.Create(text.Ellipsize(length));
|
||||
return MvcHtmlString.Create(markup.RemoveTags().Ellipsize(length));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -145,6 +145,8 @@
|
||||
<Compile Include="IDependency.cs" />
|
||||
<Compile Include="Localization\Services\DefaultCultureManager.cs" />
|
||||
<Compile Include="Localization\Services\DefaultResourceManager.cs" />
|
||||
<Compile Include="Indexing\DefaultIndexManager.cs" />
|
||||
<Compile Include="Indexing\IIndexManager.cs" />
|
||||
<Compile Include="Localization\Services\ICultureManager.cs" />
|
||||
<Compile Include="Localization\Services\ICultureSelector.cs" />
|
||||
<Compile Include="Localization\Services\IResourceManager.cs" />
|
||||
@@ -163,6 +165,13 @@
|
||||
<Compile Include="Mvc\OrchardControllerFactory.cs" />
|
||||
<Compile Include="Environment\IOrchardHost.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Indexing\IIndexDocument.cs" />
|
||||
<Compile Include="Indexing\IIndexProvider.cs" />
|
||||
<Compile Include="Indexing\ISearchBuilder.cs" />
|
||||
<Compile Include="Indexing\ISearchHit.cs" />
|
||||
<Compile Include="Tasks\Indexing\IIndexingTask.cs" />
|
||||
<Compile Include="Tasks\Indexing\IIndexingTaskManager.cs" />
|
||||
<Compile Include="ContentManagement\Handlers\IndexContentContext.cs" />
|
||||
<Compile Include="Validation\Argument.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
9
src/Orchard/Tasks/Indexing/IIndexingTask.cs
Normal file
9
src/Orchard/Tasks/Indexing/IIndexingTask.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.Tasks.Indexing {
|
||||
public interface IIndexingTask {
|
||||
ContentItem ContentItem { get; }
|
||||
DateTime? CreatedUtc { get; }
|
||||
}
|
||||
}
|
||||
12
src/Orchard/Tasks/Indexing/IIndexingTaskManager.cs
Normal file
12
src/Orchard/Tasks/Indexing/IIndexingTaskManager.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.Tasks.Indexing {
|
||||
public interface IIndexingTaskManager : IDependency {
|
||||
void CreateTask(ContentItem contentItem);
|
||||
IEnumerable<IIndexingTask> GetTasks(DateTime? createdAfter);
|
||||
void DeleteTasks(DateTime? createdBefore);
|
||||
void DeleteTasks(ContentItem contentItem);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using Orchard.Logging;
|
||||
namespace Orchard.Tasks {
|
||||
public class SweepGenerator : IOrchardShellEvents {
|
||||
private readonly IContainer _container;
|
||||
private Timer _timer;
|
||||
private readonly Timer _timer;
|
||||
|
||||
public SweepGenerator(IContainer container) {
|
||||
_container = container;
|
||||
|
||||
@@ -29,5 +29,12 @@ namespace Orchard.Utility.Extensions {
|
||||
? defaultValue
|
||||
: text;
|
||||
}
|
||||
|
||||
public static string RemoveTags(this string html) {
|
||||
var tagRegex = new Regex("<[^<>]*>", RegexOptions.Singleline);
|
||||
var text = tagRegex.Replace(html, "");
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user