diff --git a/src/NuGet.Config b/src/NuGet.Config
index 89e79db976..722f368692 100644
--- a/src/NuGet.Config
+++ b/src/NuGet.Config
@@ -3,5 +3,6 @@
+
\ No newline at end of file
diff --git a/src/Umbraco.Core/Constants-Examine.cs b/src/Umbraco.Core/Constants-Examine.cs
index 1356e55775..2f52e289c6 100644
--- a/src/Umbraco.Core/Constants-Examine.cs
+++ b/src/Umbraco.Core/Constants-Examine.cs
@@ -18,21 +18,7 @@
/// The alias of the external content indexer
///
public const string ExternalIndexer = "ExternalIndexer";
-
- ///
- /// The alias of the internal member searcher
- ///
- public const string InternalMemberSearcher = "InternalMemberSearcher";
-
- ///
- /// The alias of the internal content searcher
- ///
- public const string InternalSearcher = "InternalSearcher";
-
- ///
- /// The alias of the external content searcher
- ///
- public const string ExternalSearcher = "ExternalSearcher";
+
}
}
}
diff --git a/src/Umbraco.Core/IRuntimeState.cs b/src/Umbraco.Core/IRuntimeState.cs
index 5c0c01ef3b..5fcfab1518 100644
--- a/src/Umbraco.Core/IRuntimeState.cs
+++ b/src/Umbraco.Core/IRuntimeState.cs
@@ -57,6 +57,16 @@ namespace Umbraco.Core
///
RuntimeLevel Level { get; }
+ ///
+ /// Gets the current migration state.
+ ///
+ string CurrentMigrationState { get; }
+
+ ///
+ /// Gets the final migration state.
+ ///
+ string FinalMigrationState { get; }
+
///
/// Gets the exception that caused the boot to fail.
///
diff --git a/src/Umbraco.Core/Migrations/MigrationPlan.cs b/src/Umbraco.Core/Migrations/MigrationPlan.cs
index f686ff283e..733ff281e5 100644
--- a/src/Umbraco.Core/Migrations/MigrationPlan.cs
+++ b/src/Umbraco.Core/Migrations/MigrationPlan.cs
@@ -7,6 +7,9 @@ using Umbraco.Core.Scoping;
namespace Umbraco.Core.Migrations
{
+ ///
+ /// Represents a migration plan.
+ ///
public class MigrationPlan
{
private readonly IMigrationBuilder _migrationBuilder;
@@ -16,31 +19,67 @@ namespace Umbraco.Core.Migrations
private string _prevState;
private string _finalState;
- // initializes a non-executing plan
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the plan.
+ /// The plan cannot be executed. Use this constructor e.g. when only validating the plan,
+ /// or trying to get its final state, without actually needing to execute it.
public MigrationPlan(string name)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
Name = name;
+
+ // ReSharper disable once VirtualMemberCallInConstructor
+ // (accepted)
+ DefinePlan();
}
- // initializes a plan
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the plan.
+ /// A migration builder.
+ /// A logger.
+ /// The plan can be executed.
public MigrationPlan(string name, IMigrationBuilder migrationBuilder, ILogger logger)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
Name = name;
_migrationBuilder = migrationBuilder ?? throw new ArgumentNullException(nameof(migrationBuilder));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
+
+ // ReSharper disable once VirtualMemberCallInConstructor
+ // (accepted)
+ DefinePlan();
}
+ ///
+ /// Defines the plan.
+ ///
+ protected virtual void DefinePlan() { }
+
+ ///
+ /// Gets the name of the plan.
+ ///
public string Name { get; }
+ ///
+ /// Adds an empty migration from source to target state.
+ ///
public MigrationPlan Add(string sourceState, string targetState)
=> Add(sourceState, targetState);
+ ///
+ /// Adds a migration from source to target state.
+ ///
public MigrationPlan Add(string sourceState, string targetState)
where TMigration : IMigration
=> Add(sourceState, targetState, typeof(TMigration));
+ ///
+ /// Adds a migration from source to target state.
+ ///
public MigrationPlan Add(string sourceState, string targetState, Type migration)
{
if (sourceState == null) throw new ArgumentNullException(nameof(sourceState));
@@ -78,26 +117,50 @@ namespace Umbraco.Core.Migrations
return this;
}
+ ///
+ /// Chains an empty migration from chain to target state.
+ ///
public MigrationPlan Chain(string targetState)
=> Chain(targetState);
+ ///
+ /// Chains a migration from chain to target state.
+ ///
public MigrationPlan Chain(string targetState)
where TMigration : IMigration
=> Chain(targetState, typeof(TMigration));
+ ///
+ /// Chains a migration from chain to target state.
+ ///
public MigrationPlan Chain(string targetState, Type migration)
=> Add(_prevState, targetState, migration);
+ ///
+ /// Sets the chain state.
+ ///
public MigrationPlan From(string sourceState)
{
_prevState = sourceState ?? throw new ArgumentNullException(nameof(sourceState));
return this;
}
+ ///
+ /// Gets the initial state.
+ ///
+ /// The initial state is the state when no entry for the plan
+ /// could be found in the database (i.e. the plan has never run).
public virtual string InitialState => string.Empty;
+ ///
+ /// Gets the final state.
+ ///
public string FinalState => _finalState ?? (_finalState = Validate());
+ ///
+ /// Validates the plan.
+ ///
+ /// The plan's final state.
public string Validate()
{
// quick check for dead ends - a dead end is a transition that has a target state
@@ -134,6 +197,13 @@ namespace Umbraco.Core.Migrations
return finalState;
}
+ ///
+ /// Executes the plan.
+ ///
+ /// A scope.
+ /// The state to start execution at.
+ /// The final state.
+ /// The plan executes within the scope, which must then be completed.
public string Execute(IScope scope, string fromState)
{
Validate();
@@ -142,7 +212,7 @@ namespace Umbraco.Core.Migrations
throw new InvalidOperationException("Cannot execute a non-executing plan.");
_logger.Info("Starting \"{0}\"...", () => Name);
- var origState = fromState; //GetState();
+ var origState = fromState ?? string.Empty;
var info = "At " + (string.IsNullOrWhiteSpace(origState) ? "origin" : ("\"" + origState + "\"")) + ".";
info = info.Replace("{", "{{").Replace("}", "}}"); // stupid log4net
_logger.Info(info);
@@ -159,7 +229,7 @@ namespace Umbraco.Core.Migrations
var nextState = transition.TargetState;
origState = nextState;
-
+
_logger.Info("At \"{0}\".", origState);
if (!_transitions.TryGetValue(origState, out transition))
diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
index fb0b3fc670..f8cd4b0f78 100644
--- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
+++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
@@ -18,18 +18,29 @@ namespace Umbraco.Core.Migrations.Upgrade
///
public class UmbracoPlan : MigrationPlan
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
public UmbracoPlan()
: base(Constants.System.UmbracoUpgradePlanName)
- {
- DefinePlan();
- }
+ { }
+ ///
+ /// Initializes a new instance of the class.
+ ///
public UmbracoPlan(IMigrationBuilder migrationBuilder, ILogger logger)
: base(Constants.System.UmbracoUpgradePlanName, migrationBuilder, logger)
- {
- DefinePlan();
- }
+ { }
+ ///
+ ///
+ /// The default initial state in plans is string.Empty.
+ /// When upgrading from version 7, we want to use specific initial states
+ /// that are e.g. "{orig-7.9.3}", "{orig-7.11.1}", etc. so we can chain the proper
+ /// migrations.
+ /// This is also where we detect the current version, and reject invalid
+ /// upgrades (from a tool old version, or going back in time, etc).
+ ///
public override string InitialState
{
get
@@ -38,8 +49,8 @@ namespace Umbraco.Core.Migrations.Upgrade
if (!SemVersion.TryParse(ConfigurationManager.AppSettings["umbracoConfigurationStatus"], out var currentVersion))
throw new InvalidOperationException("Could not get current version from web.config umbracoConfigurationStatus appSetting.");
- // must be at least 7.?.? - fixme adjust when releasing
- if (currentVersion < new SemVersion(7, 999))
+ // must be at least 7.? - fixme adjust when releasing
+ if (currentVersion < new SemVersion(7, 9))
throw new InvalidOperationException($"Version {currentVersion} cannot be upgraded to {UmbracoVersion.SemanticVersion}.");
// cannot go back in time
@@ -62,64 +73,85 @@ namespace Umbraco.Core.Migrations.Upgrade
}
}
- private void DefinePlan()
+ ///
+ protected override void DefinePlan()
{
+ // NOTE: MODIFYING THE PLAN
+ //
+ // Please take great care when modifying the plan!
+ //
+ // * Creating a migration for version 8:
+ // Append the migration to the main version 8 chain, using a new guid.
+ // Update the final state (see end of file) to that guid.
+ // Append the migration to version 7 upgrade chains.
+ // * Porting a migration from version 7:
+ // Append the migration to the main version 8 chain, using a new guid.
+ // Update the final state (see end of file) to that guid.
+ // Update all init-7.x.y chains.
+
+
// UPGRADE FROM 7
//
- // when 8.0.0 is released, on the first upgrade, the state is automatically
- // set to {init-7.x.y} where 7.x.y is the version (see above), and then we define upgrades.
+ // When upgrading from version 7, the state is automatically set to {init-7.x.y} where
+ // 7.x.y is the version. We need to define a chain starting at that state and taking
+ // us to version 8. And we need such a chain for each 7.x.y version that can be upgraded
+ // to version 8, bearing in mind that new releases of version 7 will probably be
+ // created *after* the first released of version 8.
//
- // then, as more v7 and v8 versions are released, new chains needs to be defined to
- // support the upgrades (new v7 may backport some migrations and require their own
- // upgrade paths, etc).
- // fixme adjust when releasing
+ // fixme adjust when releasing the first public (alpha?) version
- From("{init-7.8.0}");
- Chain("{7C447271-CA3F-4A6A-A913-5D77015655CB}"); // add more lock objects
+ // at the moment we don't support upgrading from versions older than 7.9.0
+ //
+ From("{init-7.9.0}");
+ Chain("{7C447271-CA3F-4A6A-A913-5D77015655CB}");
Chain("{CBFF58A2-7B50-4F75-8E98-249920DB0F37}");
Chain("{3D18920C-E84D-405C-A06A-B7CEE52FE5DD}");
+
Chain("{FB0A5429-587E-4BD0-8A67-20F0E7E62FF7}");
Chain("{F0C42457-6A3B-4912-A7EA-F27ED85A2092}");
Chain("{8640C9E4-A1C0-4C59-99BB-609B4E604981}");
Chain("{DD1B99AF-8106-4E00-BAC7-A43003EA07F8}");
Chain("{9DF05B77-11D1-475C-A00A-B656AF7E0908}");
- Chain("{CA7DB949-3EF4-403D-8464-F9BA36A52E87}");
+ Chain("{6FE3EF34-44A0-4992-B379-B40BC4EF1C4D}");
+ Chain("{7F59355A-0EC9-4438-8157-EB517E6D2727}");
- // 7.8.1 = same as 7.8.0
- From("{init-7.8.1}");
- Chain("{init-7.8.0}");
+ // must chain to v8 final state (see at end of file)
+ Chain("{8918450B-3DA0-4BB7-886A-6FA8B7E4186E}");
- // 7.9.0 = requires its own chain
- From("{init-7.9.0}");
- // chain...
- Chain("{82C4BA1D-7720-46B1-BBD7-07F3F73800E6}");
+ // 7.9.1, .2, .3 = same as 7.9.0
+ // 7.10.0 = same as 7.9.0
+ From("{init-7.9.1}").Chain("{init-7.9.0}");
+ From("{init-7.9.2}").Chain("{init-7.9.0}");
+ From("{init-7.9.3}").Chain("{init-7.9.0}");
+ From("{init-7.10.0}").Chain("{init-7.9.0}");
- // UPGRADE 8
+ // VERSION 8 PLAN
//
- // starting from the original 8.0.0 final state, chain migrations to upgrade version 8,
- // defining new final states as more migrations are added to the chain.
+ // this is the master Umbraco migration plan, starting from the very first version 8
+ // release, which was a pre-pre-alpha, years ago. It contains migrations created
+ // for version 8, along with migrations ported from version 7 as version 7 evolves.
+ // It is therefore *normal* that some pure version 8 migrations are mixed with
+ // migrations merged from version 7.
//
- // before v8 is released, some sites may exist, and these "pre-8" versions require their
- // own upgrade plan. in other words, this is also the plan for sites that were on v8 before
- // v8 was released
+ // new migrations should always be *appended* to the *end* of the chain.
// 8.0.0
- From("{init-origin}");
+ From("{init-origin}"); // "origin" was 7.4.something
Chain("{98347B5E-65BF-4DD7-BB43-A09CB7AF4FCA}");
Chain("{1E8165C4-942D-40DC-AC76-C5FF8831E400}");
Chain("{39E15568-7AAD-4D54-81D0-758CCFC529F8}");
Chain("{55C3F97D-BDA7-4FB1-A743-B0456B56EAA3}");
- // 7.5.0
+ // merging from 7.5.0
Chain("{287F9E39-F673-42F7-908C-21659AB13B13}");
Chain("{2D08588A-AD90-479C-9F6E-A99B60BA7226}");
Chain("{2D917FF8-AC81-4C00-A407-1F4B1DF6089C}");
- // 7.5.5
+ // merging from 7.5.5
Chain("{44484C32-EEB3-4A12-B1CB-11E02CE22AB2}");
- // 7.6.0
+ // merging from 7.6.0
Chain("{3586E4E9-2922-49EB-8E2A-A530CE6DBDE0}");
Chain("{D4A5674F-654D-4CC7-85E5-CFDBC533A318}");
Chain("{7F828EDD-6622-4A8D-AD80-EEAF46C11680}");
@@ -130,7 +162,7 @@ namespace Umbraco.Core.Migrations.Upgrade
Chain("{5496C6CC-3AE0-4789-AF49-5BB4E28FA424}");
Chain("{8995332B-085E-4C0C-849E-9A77E79F4293}");
- // 7.7.0
+ // merging from 7.7.0
Chain("{74319856-7681-46B1-AA0D-F7E896FBE6A1}");
Chain("{0427B0A2-994A-4AB4-BFF3-31B20614F6C9}");
Chain("{F0D6F782-E432-46DE-A3A7-2AF06DB8853B}");
@@ -147,11 +179,7 @@ namespace Umbraco.Core.Migrations.Upgrade
Chain("{CA7DB949-3EF4-403D-8464-F9BA36A52E87}");
Chain("{7F0BF916-F64E-4B25-864A-170D6E6B68E5}");
- // at this point of the chain, people started to work on v8, so whenever we
- // merge stuff from v7, we have to chain the migrations here so they also
- // run for v8.
-
- // mergin from 7.8.0
+ // merging from 7.8.0
Chain("{FDCB727A-EFB6-49F3-89E4-A346503AB849}");
Chain("{2A796A08-4FE4-4783-A1A5-B8A6C8AA4A92}");
Chain("{1A46A98B-2AAB-4C8E-870F-A2D55A97FD1F}");
@@ -163,9 +191,9 @@ namespace Umbraco.Core.Migrations.Upgrade
Chain("{FD8631BC-0388-425C-A451-5F58574F6F05}");
Chain("{2821F53E-C58B-4812-B184-9CD240F990D7}");
Chain("{8918450B-3DA0-4BB7-886A-6FA8B7E4186E}");
- Chain("FIXGUID NEW FINAL");
// FINAL STATE - MUST MATCH LAST ONE ABOVE !
+ // whenever this changes, update all references in this file!
Add(string.Empty, "{8918450B-3DA0-4BB7-886A-6FA8B7E4186E}");
}
diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs
index 7d28ffcf57..44d7ad3f68 100644
--- a/src/Umbraco.Core/Runtime/CoreRuntime.cs
+++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs
@@ -138,7 +138,7 @@ namespace Umbraco.Core.Runtime
try
{
var dbfactory = container.GetInstance();
- SetRuntimeStateLevel(_state, dbfactory, Logger);
+ SetRuntimeStateLevel(dbfactory, Logger);
Logger.Debug($"Runtime level: {_state.Level}");
}
catch
@@ -233,38 +233,38 @@ namespace Umbraco.Core.Runtime
builder.AddCore();
}
- private void SetRuntimeStateLevel(RuntimeState runtimeState, IUmbracoDatabaseFactory databaseFactory, ILogger logger)
+ private void SetRuntimeStateLevel(IUmbracoDatabaseFactory databaseFactory, ILogger logger)
{
var localVersion = UmbracoVersion.Local; // the local, files, version
- var codeVersion = runtimeState.SemanticVersion; // the executing code version
+ var codeVersion = _state.SemanticVersion; // the executing code version
var connect = false;
// we don't know yet
- runtimeState.Level = RuntimeLevel.Unknown;
+ _state.Level = RuntimeLevel.Unknown;
if (localVersion == null)
{
// there is no local version, we are not installed
logger.Debug("No local version, need to install Umbraco.");
- runtimeState.Level = RuntimeLevel.Install;
+ _state.Level = RuntimeLevel.Install;
}
else if (localVersion != codeVersion)
{
// there *is* a local version, but it does not match the code version
// need to upgrade
logger.Debug($"Local version \"{localVersion}\" != code version \"{codeVersion}\", need to upgrade Umbraco.");
- runtimeState.Level = RuntimeLevel.Upgrade;
+ _state.Level = RuntimeLevel.Upgrade;
}
else if (databaseFactory.Configured == false)
{
// local version *does* match code version, but the database is not configured
// install (again? this is a weird situation...)
logger.Debug("Database is not configured, need to install Umbraco.");
- runtimeState.Level = RuntimeLevel.Install;
+ _state.Level = RuntimeLevel.Install;
}
// install? not going to test anything else
- if (runtimeState.Level == RuntimeLevel.Install)
+ if (_state.Level == RuntimeLevel.Install)
return;
// else, keep going,
@@ -284,14 +284,14 @@ namespace Umbraco.Core.Runtime
{
// cannot connect to configured database, this is bad, fail
logger.Debug("Could not connect to database.");
- runtimeState.Level = RuntimeLevel.BootFailed;
+ _state.Level = RuntimeLevel.BootFailed;
// in fact, this is bad enough that we want to throw
throw new BootFailedException("A connection string is configured but Umbraco could not connect to the database.");
}
// if we already know we want to upgrade, no need to look for migrations...
- if (runtimeState.Level == RuntimeLevel.Upgrade)
+ if (_state.Level == RuntimeLevel.Upgrade)
return;
// else
@@ -302,18 +302,19 @@ namespace Umbraco.Core.Runtime
{
exists = EnsureUmbracoUpgradeState(databaseFactory, logger);
}
- catch
+ catch (Exception e)
{
// can connect to the database but cannot access the migration table... need to install
+ logger.Warn(e, "Could not check the upgrade state.");
logger.Debug("Could not check the upgrade state, need to install Umbraco.");
- runtimeState.Level = RuntimeLevel.Install;
+ _state.Level = RuntimeLevel.Install;
return;
}
if (exists)
{
// the database version matches the code & files version, all clear, can run
- runtimeState.Level = RuntimeLevel.Run;
+ _state.Level = RuntimeLevel.Run;
return;
}
@@ -323,7 +324,7 @@ namespace Umbraco.Core.Runtime
// although the files version matches the code version, the database version does not
// which means the local files have been upgraded but not the database - need to upgrade
logger.Debug("Has not reached the final upgrade step, need to upgrade Umbraco.");
- runtimeState.Level = RuntimeLevel.Upgrade;
+ _state.Level = RuntimeLevel.Upgrade;
}
protected virtual bool EnsureUmbracoUpgradeState(IUmbracoDatabaseFactory databaseFactory, ILogger logger)
@@ -343,11 +344,12 @@ namespace Umbraco.Core.Runtime
state = database.FirstOrDefault(sql)?.Value;
}
- var finalState = umbracoPlan.FinalState;
+ _state.CurrentMigrationState = state;
+ _state.FinalMigrationState = umbracoPlan.FinalState;
- logger.Debug($"Final upgrade state is \"{finalState}\", database contains \"{state ?? ""}\".");
+ logger.Debug($"Final upgrade state is \"{_state.FinalMigrationState}\", database contains \"{state ?? ""}\".");
- return state == finalState;
+ return state == _state.FinalMigrationState;
}
#region Locals
diff --git a/src/Umbraco.Core/RuntimeState.cs b/src/Umbraco.Core/RuntimeState.cs
index 3c3ebdbd84..d740352649 100644
--- a/src/Umbraco.Core/RuntimeState.cs
+++ b/src/Umbraco.Core/RuntimeState.cs
@@ -85,6 +85,12 @@ namespace Umbraco.Core
/// This is either "/" or eg "/virtual".
public string ApplicationVirtualPath { get; } = HttpRuntime.AppDomainAppVirtualPath;
+ ///
+ public string CurrentMigrationState { get; internal set; }
+
+ ///
+ public string FinalMigrationState { get; internal set; }
+
///
/// Gets the runtime level of execution.
///
diff --git a/src/Umbraco.Examine/BaseUmbracoIndexer.cs b/src/Umbraco.Examine/BaseUmbracoIndexer.cs
deleted file mode 100644
index 24d3168240..0000000000
--- a/src/Umbraco.Examine/BaseUmbracoIndexer.cs
+++ /dev/null
@@ -1,459 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using Examine.LuceneEngine.Config;
-using Examine.LuceneEngine.Providers;
-using Examine.Providers;
-using Lucene.Net.Analysis;
-using Lucene.Net.Index;
-using Umbraco.Core;
-using Examine;
-using System.IO;
-using System.Xml;
-using System.Xml.Linq;
-using Examine.LuceneEngine;
-using Examine.LuceneEngine.Faceting;
-using Examine.LuceneEngine.Indexing;
-using Lucene.Net.Documents;
-using Lucene.Net.Store;
-using Umbraco.Core.Composing;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Xml;
-using Umbraco.Examine.LocalStorage;
-using Directory = Lucene.Net.Store.Directory;
-
-namespace Umbraco.Examine
-{
- ///
- /// An abstract provider containing the basic functionality to be able to query against
- /// Umbraco data.
- ///
- public abstract class BaseUmbracoIndexer : LuceneIndexer
- {
- // note
- // wrapping all operations that end up calling base.SafelyProcessQueueItems in a safe call
- // context because they will fork a thread/task/whatever which should *not* capture our
- // call context (and the database it can contain)! ideally we should be able to override
- // SafelyProcessQueueItems but that's not possible in the current version of Examine.
-
- ///
- /// Used to store the path of a content object
- ///
- public const string IndexPathFieldName = "__Path";
- public const string NodeKeyFieldName = "__Key";
- public const string IconFieldName = "__Icon";
- public const string PublishedFieldName = "__Published";
- ///
- /// The prefix added to a field when it is duplicated in order to store the original raw value.
- ///
- public const string RawFieldPrefix = "__Raw_";
-
- ///
- /// Default constructor
- ///
- protected BaseUmbracoIndexer()
- : base()
- {
- ProfilingLogger = Current.ProfilingLogger;
- _configBased = true;
- }
-
- protected BaseUmbracoIndexer(
- IEnumerable fieldDefinitions,
- Directory luceneDirectory,
- Analyzer defaultAnalyzer,
- ProfilingLogger profilingLogger,
- IValueSetValidator validator = null,
- FacetConfiguration facetConfiguration = null, IDictionary> indexValueTypes = null)
- : base(fieldDefinitions, luceneDirectory, defaultAnalyzer, validator, facetConfiguration, indexValueTypes)
- {
- if (profilingLogger == null) throw new ArgumentNullException("profilingLogger");
- ProfilingLogger = profilingLogger;
- }
-
- private readonly bool _configBased = false;
- private LocalTempStorageIndexer _localTempStorageIndexer;
-
- ///
- /// A type that defines the type of index for each Umbraco field (non user defined fields)
- /// Alot of standard umbraco fields shouldn't be tokenized or even indexed, just stored into lucene
- /// for retreival after searching.
- ///
- [Obsolete("IndexFieldPolicies is not really used apart for some legacy reasons - use FieldDefinition's instead")]
- internal static readonly List IndexFieldPolicies
- = new List
- {
- new StaticField("id", FieldIndexTypes.NOT_ANALYZED, false, string.Empty),
- new StaticField("key", FieldIndexTypes.NOT_ANALYZED, false, string.Empty),
- new StaticField( "version", FieldIndexTypes.NOT_ANALYZED, false, string.Empty),
- new StaticField( "parentID", FieldIndexTypes.NOT_ANALYZED, false, string.Empty),
- new StaticField( "level", FieldIndexTypes.NOT_ANALYZED, true, "NUMBER"),
- new StaticField( "writerID", FieldIndexTypes.NOT_ANALYZED, false, string.Empty),
- new StaticField( "creatorID", FieldIndexTypes.NOT_ANALYZED, false, string.Empty),
- new StaticField( "nodeType", FieldIndexTypes.NOT_ANALYZED, false, string.Empty),
- new StaticField( "template", FieldIndexTypes.NOT_ANALYZED, false, string.Empty),
- new StaticField( "sortOrder", FieldIndexTypes.NOT_ANALYZED, true, "NUMBER"),
- new StaticField( "createDate", FieldIndexTypes.NOT_ANALYZED, false, "DATETIME"),
- new StaticField( "updateDate", FieldIndexTypes.NOT_ANALYZED, false, "DATETIME"),
- new StaticField( "nodeName", FieldIndexTypes.ANALYZED, false, string.Empty),
- new StaticField( "urlName", FieldIndexTypes.NOT_ANALYZED, false, string.Empty),
- new StaticField( "writerName", FieldIndexTypes.ANALYZED, false, string.Empty),
- new StaticField( "creatorName", FieldIndexTypes.ANALYZED, false, string.Empty),
- new StaticField( "nodeTypeAlias", FieldIndexTypes.ANALYZED, false, string.Empty),
- new StaticField( "path", FieldIndexTypes.NOT_ANALYZED, false, string.Empty)
- };
-
- protected ProfilingLogger ProfilingLogger { get; private set; }
-
- ///
- /// Overridden to ensure that the umbraco system field definitions are in place
- ///
- ///
- ///
- protected override IEnumerable InitializeFieldDefinitions(IEnumerable originalDefinitions)
- {
- var fd = base.InitializeFieldDefinitions(originalDefinitions).ToList();
- fd.AddRange(new[]
- {
- new FieldDefinition("parentID", FieldDefinitionTypes.Integer),
- new FieldDefinition("level", FieldDefinitionTypes.Integer),
- new FieldDefinition("writerID", FieldDefinitionTypes.Integer),
- new FieldDefinition("creatorID", FieldDefinitionTypes.Integer),
- new FieldDefinition("sortOrder", FieldDefinitionTypes.Integer),
- new FieldDefinition("template", FieldDefinitionTypes.Integer),
-
- new FieldDefinition("createDate", FieldDefinitionTypes.DateTime),
- new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime),
-
- new FieldDefinition("key", FieldDefinitionTypes.Raw),
- new FieldDefinition("version", FieldDefinitionTypes.Raw),
- new FieldDefinition("nodeType", FieldDefinitionTypes.Raw),
- new FieldDefinition("template", FieldDefinitionTypes.Raw),
- new FieldDefinition("urlName", FieldDefinitionTypes.Raw),
- new FieldDefinition("path", FieldDefinitionTypes.Raw),
-
- new FieldDefinition(IndexPathFieldName, FieldDefinitionTypes.Raw),
- new FieldDefinition(NodeTypeAliasFieldName, FieldDefinitionTypes.Raw),
- new FieldDefinition(IconFieldName, FieldDefinitionTypes.Raw)
- });
- return fd;
- }
-
- public bool UseTempStorage
- {
- get { return _localTempStorageIndexer != null && _localTempStorageIndexer.LuceneDirectory != null; }
- }
-
- public string TempStorageLocation
- {
- get
- {
- if (UseTempStorage == false) return string.Empty;
- return _localTempStorageIndexer.TempPath;
- }
- }
-
- [Obsolete("This should not be used, it is used by the configuration based indexes but instead to disable Examine event handlers use the ExamineEvents class instead.")]
- [EditorBrowsable(EditorBrowsableState.Never)]
- public bool EnableDefaultEventHandler { get; protected set; }
-
- ///
- /// the supported indexable types
- ///
- protected abstract IEnumerable SupportedTypes { get; }
-
- #region Initialize
-
-
- ///
- /// Setup the properties for the indexer from the provider settings
- ///
- ///
- ///
- ///
- /// This is ONLY used for configuration based indexes
- ///
- public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
- {
- EnableDefaultEventHandler = true; //set to true by default
- bool enabled;
- if (bool.TryParse(config["enableDefaultEventHandler"], out enabled))
- {
- EnableDefaultEventHandler = enabled;
- }
-
- ProfilingLogger.Logger.Debug(GetType(), "{0} indexer initializing", () => name);
-
- base.Initialize(name, config);
-
- //NOTES: useTempStorage is obsolete, tempStorageDirectory is obsolete, both have been superceded by Examine Core's IDirectoryFactory
- // tempStorageDirectory never actually got finished in Umbraco Core but accidentally got shipped (it's only enabled on the searcher
- // and not the indexer). So this whole block is just legacy
-
- //detect if a dir factory has been specified, if so then useTempStorage will not be used (deprecated)
-
- if (config["directoryFactory"] == null && config["useTempStorage"] != null)
- {
- throw new NotImplementedException("Fix how local temp storage works and is synced with Examine v2.0 - since a writer is always open we cannot snapshot it, we need to use the same logic in AzureDirectory");
-
- _localTempStorageIndexer = new LocalTempStorageIndexer();
-
- var fsDir = base.GetLuceneDirectory() as FSDirectory;
- if (fsDir != null)
- {
- //Use the temp storage directory which will store the index in the local/codegen folder, this is useful
- // for websites that are running from a remove file server and file IO latency becomes an issue
- var attemptUseTempStorage = config["useTempStorage"].TryConvertTo();
- if (attemptUseTempStorage)
- {
-
- var indexSet = IndexSets.Instance.Sets[IndexSetName];
- var configuredPath = indexSet.IndexPath;
-
- _localTempStorageIndexer.Initialize(config, configuredPath, fsDir, IndexingAnalyzer, attemptUseTempStorage.Result);
- }
- }
-
- }
- }
-
- #endregion
-
-
- public override Directory GetLuceneDirectory()
- {
- //if temp local storage is configured use that, otherwise return the default
- if (UseTempStorage)
- {
- return _localTempStorageIndexer.LuceneDirectory;
- }
-
- return base.GetLuceneDirectory();
-
- }
-
- ///
- /// override to check if we can actually initialize.
- ///
- ///
- /// This check is required since the base examine lib will try to rebuild on startup
- ///
- public override void RebuildIndex()
- {
- if (CanInitialize())
- {
- ProfilingLogger.Logger.Debug(GetType(), "Rebuilding index");
- using (new SafeCallContext())
- {
- base.RebuildIndex();
- }
- }
- }
-
- ///
- /// override to check if we can actually initialize.
- ///
- ///
- /// This check is required since the base examine lib will try to rebuild on startup
- ///
- public override void IndexAll(string type)
- {
- if (CanInitialize())
- {
- using (new SafeCallContext())
- {
- base.IndexAll(type);
- }
- }
- }
-
- public override void IndexItems(IEnumerable nodes)
- {
- if (CanInitialize())
- {
- using (new SafeCallContext())
- {
- base.IndexItems(nodes);
- }
- }
- }
-
- [Obsolete("Use ValueSets with IndexItems instead")]
- public override void ReIndexNode(XElement node, string type)
- {
- if (CanInitialize())
- {
- if (SupportedTypes.Contains(type) == false)
- return;
-
- if (node.Attribute("id") != null)
- {
- ProfilingLogger.Logger.Debug(GetType(), "ReIndexNode {0} with type {1}", () => node.Attribute("id"), () => type);
- using (new SafeCallContext())
- {
- base.ReIndexNode(node, type);
- }
- }
- else
- {
- ProfilingLogger.Logger.Error(GetType(), "ReIndexNode cannot proceed, the format of the XElement is invalid",
- new XmlException("XElement is invalid, the xml has not id attribute"));
- }
- }
- }
-
- ///
- /// override to check if we can actually initialize.
- ///
- ///
- /// This check is required since the base examine lib will try to rebuild on startup
- ///
- public override void DeleteFromIndex(string nodeId)
- {
- if (CanInitialize())
- {
- using (new SafeCallContext())
- {
- base.DeleteFromIndex(nodeId);
- }
- }
- }
-
- ///
- /// Returns true if the Umbraco application is in a state that we can initialize the examine indexes
- ///
- protected bool CanInitialize()
- {
- // only affects indexers that are config file based, if an index was created via code then
- // this has no effect, it is assumed the index would not be created if it could not be initialized
- return _configBased == false || Current.RuntimeState.Level == RuntimeLevel.Run;
- }
-
- ///
- /// Reindexes all supported types
- ///
- protected override void PerformIndexRebuild()
- {
- foreach (var t in SupportedTypes)
- {
- IndexAll(t);
- }
- }
-
- ///
- /// overridden for logging
- ///
- ///
- protected override void OnIndexingError(IndexingErrorEventArgs e)
- {
- ProfilingLogger.Logger.Error(GetType(), e.Message, e.Exception);
- base.OnIndexingError(e);
- }
-
- ///
- /// Override for logging
- ///
- ///
- protected override void OnIgnoringIndexItem(IndexItemEventArgs e)
- {
- ProfilingLogger.Logger.Debug(GetType(), "OnIgnoringIndexItem {0} with type {1}", () => e.IndexItem.ValueSet.Id, () => e.IndexItem.ValueSet.IndexCategory);
- base.OnIgnoringIndexItem(e);
- }
-
- ///
- /// This ensures that the special __Raw_ fields are indexed
- ///
- ///
- protected override void OnDocumentWriting(DocumentWritingEventArgs docArgs)
- {
- var d = docArgs.Document;
-
- foreach (var f in docArgs.Values.Values.Where(x => x.Key.StartsWith(RawFieldPrefix)))
- {
- if (f.Value.Count > 0)
- {
- d.Add(new Field(
- f.Key,
- f.Value[0].ToString(),
- Field.Store.YES,
- Field.Index.NO, //don't index this field, we never want to search by it
- Field.TermVector.NO));
- }
- }
-
- ProfilingLogger.Logger.Debug(GetType(), "OnDocumentWriting {0} with type {1}", () => docArgs.Values.Id, () => docArgs.Values.ItemType);
-
- base.OnDocumentWriting(docArgs);
- }
-
- protected override void OnItemIndexed(IndexItemEventArgs e)
- {
- ProfilingLogger.Logger.Debug(GetType(), "Index created for node {0}", () => e.IndexItem.Id);
- base.OnItemIndexed(e);
- }
-
- protected override void OnIndexDeleted(DeleteIndexEventArgs e)
- {
- ProfilingLogger.Logger.Debug(GetType(), "Index deleted for term: {0} with value {1}", () => e.DeletedTerm.Key, () => e.DeletedTerm.Value);
- base.OnIndexDeleted(e);
- }
-
- [Obsolete("This is no longer used, index optimization is no longer managed with the LuceneIndexer")]
- protected override void OnIndexOptimizing(EventArgs e)
- {
- ProfilingLogger.Logger.Debug(GetType(), "Index is being optimized");
- base.OnIndexOptimizing(e);
- }
-
- ///
- /// Overridden for logging.
- ///
- ///
- protected override void AddDocument(ValueSet values)
- {
- ProfilingLogger.Logger.Debug(GetType(), "AddDocument {0} with type {1}", () => values.Id, () => values.ItemType);
- base.AddDocument(values);
- }
-
- protected override void OnTransformingIndexValues(TransformingIndexDataEventArgs e)
- {
- base.OnTransformingIndexValues(e);
-
- //ensure special __Path field
- if (e.OriginalValues.ContainsKey("path") && e.IndexItem.ValueSet.Values.ContainsKey(IndexPathFieldName) == false)
- {
- e.IndexItem.ValueSet.Values[IndexPathFieldName] = new List