diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs
index 6a212a5a71..27ce6db3d8 100644
--- a/src/Umbraco.Core/Composing/TypeLoader.cs
+++ b/src/Umbraco.Core/Composing/TypeLoader.cs
@@ -47,7 +47,7 @@ namespace Umbraco.Core.Composing
/// The application runtime cache.
/// Files storage mode.
/// A profiling logger.
- public TypeLoader(IRuntimeCacheProvider runtimeCache, LocalTempStorage localTempStorage, ProfilingLogger logger)
+ public TypeLoader(IRuntimeCacheProvider runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger)
: this(runtimeCache, localTempStorage, logger, true)
{ }
@@ -58,7 +58,7 @@ namespace Umbraco.Core.Composing
/// Files storage mode.
/// A profiling logger.
/// Whether to detect changes using hashes.
- internal TypeLoader(IRuntimeCacheProvider runtimeCache, LocalTempStorage localTempStorage, ProfilingLogger logger, bool detectChanges)
+ internal TypeLoader(IRuntimeCacheProvider runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger, bool detectChanges)
{
_runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache));
_localTempStorage = localTempStorage == LocalTempStorage.Unknown ? LocalTempStorage.Default : localTempStorage;
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
index d2236bab70..91627edb8b 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class ContentElement : UmbracoConfigurationElement, IContentSection
{
- private const string DefaultPreviewBadge = @"In Preview Mode - click to end";
+ private const string DefaultPreviewBadge = @"In Preview Mode - click to end";
[ConfigurationProperty("imaging")]
internal ContentImagingElement Imaging => (ContentImagingElement) this["imaging"];
diff --git a/src/Umbraco.Core/Constants-Examine.cs b/src/Umbraco.Core/Constants-Examine.cs
deleted file mode 100644
index ddc3500066..0000000000
--- a/src/Umbraco.Core/Constants-Examine.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace Umbraco.Core
-{
- public static partial class Constants
- {
- public static class Examine
- {
- ///
- /// The alias of the internal member indexer
- ///
- public const string InternalMemberIndexer = "InternalMemberIndexer";
-
- ///
- /// The alias of the internal content indexer
- ///
- public const string InternalIndexer = "InternalIndexer";
-
- ///
- /// The alias of the external content indexer
- ///
- public const string ExternalIndexer = "ExternalIndexer";
-
- }
- }
-}
diff --git a/src/Umbraco.Core/RuntimeState.cs b/src/Umbraco.Core/RuntimeState.cs
index 4281ad7c42..4ea6efb860 100644
--- a/src/Umbraco.Core/RuntimeState.cs
+++ b/src/Umbraco.Core/RuntimeState.cs
@@ -225,14 +225,14 @@ namespace Umbraco.Core
protected virtual bool EnsureUmbracoUpgradeState(IUmbracoDatabaseFactory databaseFactory, ILogger logger)
{
- var umbracoPlan = new UmbracoPlan();
- var stateValueKey = Upgrader.GetStateValueKey(umbracoPlan);
+ var upgrader = new UmbracoUpgrader();
+ var stateValueKey = upgrader.StateValueKey;
// no scope, no service - just directly accessing the database
using (var database = databaseFactory.CreateDatabase())
{
CurrentMigrationState = KeyValueService.GetValue(database, stateValueKey);
- FinalMigrationState = umbracoPlan.FinalState;
+ FinalMigrationState = upgrader.Plan.FinalState;
}
logger.Debug("Final upgrade state is {FinalMigrationState}, database contains {DatabaseState}", CurrentMigrationState, FinalMigrationState ?? "");
diff --git a/src/Umbraco.Core/Services/IMemberGroupService.cs b/src/Umbraco.Core/Services/IMemberGroupService.cs
index 934bf1a480..6a554aad31 100644
--- a/src/Umbraco.Core/Services/IMemberGroupService.cs
+++ b/src/Umbraco.Core/Services/IMemberGroupService.cs
@@ -7,6 +7,7 @@ namespace Umbraco.Core.Services
{
IEnumerable GetAll();
IMemberGroup GetById(int id);
+ IEnumerable GetByIds(IEnumerable ids);
IMemberGroup GetByName(string name);
void Save(IMemberGroup memberGroup, bool raiseEvents = true);
void Delete(IMemberGroup memberGroup);
diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs
index cb2d90aa63..a926ce32aa 100644
--- a/src/Umbraco.Core/Services/IUserService.cs
+++ b/src/Umbraco.Core/Services/IUserService.cs
@@ -90,16 +90,6 @@ namespace Umbraco.Core.Services
string[] userGroups = null,
string filter = null);
- ///
- /// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method
- ///
- ///
- /// This method exists so that Umbraco developers can use one entry point to create/update users if they choose to.
- ///
- /// The user to save the password for
- /// The password to save
- void SavePassword(IUser user, string password);
-
///
/// Deletes or disables a User
///
diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs
index c5ad233a7a..3464823856 100644
--- a/src/Umbraco.Core/Services/Implement/ContentService.cs
+++ b/src/Umbraco.Core/Services/Implement/ContentService.cs
@@ -2708,11 +2708,6 @@ namespace Umbraco.Core.Services.Implement
{
scope.WriteLock(Constants.Locks.ContentTree);
- if (string.IsNullOrWhiteSpace(content.Name))
- {
- throw new ArgumentException("Cannot save content blueprint with empty name.");
- }
-
if (content.HasIdentity == false)
{
content.CreatorId = userId;
diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs
index b74abc03f7..be4f719bb1 100644
--- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs
+++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs
@@ -739,7 +739,7 @@ namespace Umbraco.Core.Services.Implement
try
{
- var container = new EntityContainer(Constants.ObjectTypes.DocumentType)
+ var container = new EntityContainer(ContainedObjectType)
{
Name = name,
ParentId = parentId,
diff --git a/src/Umbraco.Core/Services/Implement/MemberGroupService.cs b/src/Umbraco.Core/Services/Implement/MemberGroupService.cs
index e38c1530db..15b3101744 100644
--- a/src/Umbraco.Core/Services/Implement/MemberGroupService.cs
+++ b/src/Umbraco.Core/Services/Implement/MemberGroupService.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
@@ -60,6 +61,19 @@ namespace Umbraco.Core.Services.Implement
}
}
+ public IEnumerable GetByIds(IEnumerable ids)
+ {
+ if (ids == null || ids.Any() == false)
+ {
+ return new IMemberGroup[0];
+ }
+
+ using (var scope = ScopeProvider.CreateScope(autoComplete: true))
+ {
+ return _memberGroupRepository.GetMany(ids.ToArray());
+ }
+ }
+
public IMemberGroup GetById(int id)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
diff --git a/src/Umbraco.Core/Services/Implement/UserService.cs b/src/Umbraco.Core/Services/Implement/UserService.cs
index 44358caa84..188c6feb04 100644
--- a/src/Umbraco.Core/Services/Implement/UserService.cs
+++ b/src/Umbraco.Core/Services/Implement/UserService.cs
@@ -227,30 +227,6 @@ namespace Umbraco.Core.Services.Implement
Save(membershipUser);
}
- [Obsolete("ASP.NET Identity APIs like the BackOfficeUserManager should be used to manage passwords, this will not work with correct security practices because you would need the existing password")]
- [EditorBrowsable(EditorBrowsableState.Never)]
- public void SavePassword(IUser user, string password)
- {
- if (user == null) throw new ArgumentNullException(nameof(user));
-
- var provider = MembershipProviderExtensions.GetUsersMembershipProvider();
-
- if (provider.IsUmbracoMembershipProvider() == false)
- throw new NotSupportedException("When using a non-Umbraco membership provider you must change the user password by using the MembershipProvider.ChangePassword method");
-
- provider.ChangePassword(user.Username, "", password);
-
- //go re-fetch the member and update the properties that may have changed
- var result = GetByUsername(user.Username);
- if (result != null)
- {
- //should never be null but it could have been deleted by another thread.
- user.RawPasswordValue = result.RawPasswordValue;
- user.LastPasswordChangeDate = result.LastPasswordChangeDate;
- user.UpdateDate = result.UpdateDate;
- }
- }
-
///
/// Deletes or disables a User
///
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index c5319afb51..291242ff3f 100755
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -311,7 +311,6 @@
-
diff --git a/src/Umbraco.Examine/ExamineExtensions.cs b/src/Umbraco.Examine/ExamineExtensions.cs
index 71e3e65c21..525f0deaa1 100644
--- a/src/Umbraco.Examine/ExamineExtensions.cs
+++ b/src/Umbraco.Examine/ExamineExtensions.cs
@@ -2,9 +2,12 @@
using System.Linq;
using Examine;
using Examine.LuceneEngine.Providers;
+using Lucene.Net.Analysis;
using Lucene.Net.Index;
+using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;
+using Version = Lucene.Net.Util.Version;
using Umbraco.Core.Logging;
namespace Umbraco.Examine
@@ -14,6 +17,29 @@ namespace Umbraco.Examine
///
internal static class ExamineExtensions
{
+ public static bool TryParseLuceneQuery(string query)
+ {
+ //TODO: I'd assume there would be a more strict way to parse the query but not that i can find yet, for now we'll
+ // also do this rudimentary check
+ if (!query.Contains(":"))
+ return false;
+
+ try
+ {
+ //This will pass with a plain old string without any fields, need to figure out a way to have it properly parse
+ var parsed = new QueryParser(Version.LUCENE_30, "nodeName", new KeywordAnalyzer()).Parse(query);
+ return true;
+ }
+ catch (ParseException)
+ {
+ return false;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
///
/// Forcibly unlocks all lucene based indexes
///
diff --git a/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs
index 3647b57ae8..8d15613791 100644
--- a/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs
+++ b/src/Umbraco.Tests.Benchmarks/CtorInvokeBenchmarks.cs
@@ -167,7 +167,7 @@ namespace Umbraco.Tests.Benchmarks
[Benchmark]
public void EmitCtor()
{
- var ctor = ReflectionUtilities.EmitConstuctor>();
+ var ctor = ReflectionUtilities.EmitConstructor>();
var foo = ctor(_foo);
}
diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs
index ef50f0b48a..5237b92ab8 100644
--- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs
+++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs
@@ -110,7 +110,6 @@ namespace Umbraco.Tests.Cache.PublishedCache
}
[TestCase("id")]
- [TestCase("nodeId")]
[TestCase("__NodeId")]
public void DictionaryDocument_Id_Keys(string key)
{
@@ -127,7 +126,6 @@ namespace Umbraco.Tests.Cache.PublishedCache
}
[TestCase("nodeName")]
- [TestCase("__nodeName")]
public void DictionaryDocument_NodeName_Keys(string key)
{
var dicDoc = GetDictionaryDocument(nodeNameKey: key);
diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs
index 6165ecdac9..5148c7eb1b 100644
--- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs
+++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs
@@ -59,18 +59,17 @@ namespace Umbraco.Tests.Composing
// cleanup
var assDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
- foreach (var d in Directory.GetDirectories(Path.Combine(assDir.FullName, "TypeLoader")))
- Directory.Delete(d, true);
+ var tlDir = Path.Combine(assDir.FullName, "TypeLoader");
+ if (!Directory.Exists(tlDir))
+ return;
+ Directory.Delete(tlDir, true);
}
private DirectoryInfo PrepareFolder()
{
var assDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
- var dir = Directory.CreateDirectory(Path.Combine(assDir.FullName, "TypeLoader", Guid.NewGuid().ToString("N")));
- foreach (var f in dir.GetFiles())
- {
- f.Delete();
- }
+ var tlDir = Path.Combine(assDir.FullName, "TypeLoader");
+ var dir = Directory.CreateDirectory(Path.Combine(tlDir, Guid.NewGuid().ToString("N")));
return dir;
}
diff --git a/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs b/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs
index d588e19f07..d1470756f0 100644
--- a/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs
+++ b/src/Umbraco.Tests/Configurations/GlobalSettingsTests.cs
@@ -53,8 +53,7 @@ namespace Umbraco.Tests.Configurations
SystemDirectories.Root = rootPath;
Assert.AreEqual(outcome, Current.Config.Global().GetUmbracoMvcArea());
}
-
- [TestCase("/umbraco/umbraco.aspx")]
+
[TestCase("/umbraco/editContent.aspx")]
[TestCase("/install/default.aspx")]
[TestCase("/install/")]
diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs
index f1ac463305..962d6d13a9 100644
--- a/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs
+++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs
@@ -143,7 +143,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
[Test]
public void PreviewBadge()
{
- Assert.AreEqual(SettingsSection.Content.PreviewBadge, @"In Preview Mode - click to end");
+ Assert.AreEqual(SettingsSection.Content.PreviewBadge, @"In Preview Mode - click to end");
}
[Test]
public void ResolveUrlsFromTextString()
diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config
index a436dad9f5..4c64485503 100644
--- a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config
+++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config
@@ -77,7 +77,7 @@
In Preview Mode - click to end
+ In Preview Mode - click to end
]]>
diff --git a/src/Umbraco.Web.UI/Umbraco/ClientRedirect.aspx b/src/Umbraco.Web.UI/Umbraco/ClientRedirect.aspx
deleted file mode 100644
index 2a10d4d344..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/ClientRedirect.aspx
+++ /dev/null
@@ -1,64 +0,0 @@
-<%@ Page Language="C#" AutoEventWireup="true" Inherits="Umbraco.Web.UI.Pages.UmbracoEnsuredPage" %>
-<%--
- This page is required because we cannot reload the angular app with a changed Hash since it just detects the hash and doesn't reload.
- So this is used purely for a full reload of an angular app with a changed hash.
---%>
-
-
-
- Redirecting...
-
-
-
- Redirecting...
-
-
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml
index d2366b46f6..537558867f 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml
@@ -14,7 +14,7 @@
Vytvořit balíček
Odstranit
Deaktivovat
- Vyprázdnit koš
+ Vyprázdnit koš
Exportovat typ dokumentu
Importovat typ dokumentu
Importovat balíček
@@ -210,7 +210,6 @@
Zavřít toto okno
Jste si jistí. že chcete odstranit
Jste si jistí, že chcete deaktivovat
- Zatrhněte, prosím, tento box, abyste potvrdili odstranění %0% položek
Jste si jistí?
Jste si jistí?
Vyjmout
@@ -992,4 +991,4 @@
Vaše nedávná historie
Relace vyprší za
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
index 7864d3baae..5efb8c5649 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
@@ -15,7 +15,7 @@
Opret gruppe
Slet
Deaktivér
- Tøm papirkurv
+ Tøm papirkurv
Aktivér
Eksportér dokumenttype
Importér dokumenttype
@@ -333,7 +333,6 @@
Luk denne dialog
Er du sikker på at du vil slette
Er du sikker på du vil deaktivere
- Afkryds venligst denne boks for at bekræfte sletningen af %0% enhed(er)
Er du sikker på at du vil forlade Umbraco?
Er du sikker?
Klip
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml
index b2296b1ccf..53e87f8dfd 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml
@@ -14,7 +14,7 @@
Paket erstellen
Löschen
Deaktivieren
- Papierkorb leeren
+ Papierkorb leeren
Dokumenttyp exportieren
Dokumenttyp importieren
Paket importieren
@@ -222,7 +222,6 @@
Fenster schließen
Sind Sie sicher?
Deaktivieren bestätigen
- Löschen von %0% Element(en) bestätigen
Sind Sie sicher?
Sind Sie sicher?
Ausschneiden
@@ -881,7 +880,7 @@ das Dokument '%1%' wurde von '%2%' zur Übersetzung in '%5%' freigegeben.
Zum Bearbeiten verwenden Sie bitte diesen Link: http://%3%/translation/details.aspx?id=%4%.
-Sie können sich auch alle anstehenden Übersetzungen gesammelt im Umbraco-Verwaltungsbereich anzeigen lassen: http://%3%/Umbraco.aspx
+Sie können sich auch alle anstehenden Übersetzungen gesammelt im Umbraco-Verwaltungsbereich anzeigen lassen: http://%3%/umbraco
Einen schönen Tag wünscht
Ihr freundlicher Umbraco-Robot
@@ -977,4 +976,4 @@ Ihr freundlicher Umbraco-Robot
Ihr Verlauf
Sitzung läuft ab in
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
index 1b11522081..a1ab9c6e6c 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
@@ -16,7 +16,7 @@
Create group
Delete
Disable
- Empty recycle bin
+ Empty recycle bin
Enable
Export Document Type
Import Document Type
@@ -350,7 +350,6 @@
Close this window
Are you sure you want to delete
Are you sure you want to disable
- Please check this box to confirm deletion of %0% item(s)
Are you sure?
Are you sure?
Cut
@@ -1816,6 +1815,7 @@ To manage your website, simply open the Umbraco back office and start adding con
Validation
+ No validation
Validate as an email address
Validate as a number
Validate as a URL
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
index 775b40fd0c..028d8ef8ab 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
@@ -16,7 +16,7 @@
Create group
Delete
Disable
- Empty recycle bin
+ Empty recycle bin
Enable
Export Document Type
Import Document Type
@@ -147,8 +147,9 @@
Viewing for
Content deleted
Content unpublished
- Content saved and Published
- Content saved and published for languages: %0%
+ Content unpublished for languages: %0%
+ Content published
+ Content published for languages: %0%
Content saved
Content saved for languages: %0%
Content moved
@@ -165,10 +166,12 @@
Save
Delete
Unpublish
+ Unpublish
Rollback
Send To Publish
Send To Publish
Sort
+ History (all variants)
To change the document type for the selected content, first select from the list of valid types for this location.
@@ -373,7 +376,6 @@
Close this window
Are you sure you want to delete
Are you sure you want to disable
- Please check this box to confirm deletion of %0% item(s)
Are you sure?
Are you sure?
Cut
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml
index db94121405..7f35e90083 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/es.xml
@@ -15,7 +15,7 @@
Crear grupo
Borrar
Deshabilitar
- Vaciar Papelera
+ Vaciar Papelera
Activar
Exportar Documento (tipo)
Importar Documento (tipo)
@@ -296,7 +296,6 @@
Cerrar esta ventana
Estás seguro que quieres borrar
Estás seguro que quieres deshabilitar
- Por favor selecciona esta casilla para confirmar la eliminación de %0% entrada(s)
¿Estás seguro?
¿Estás seguro?
Cortar
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml
index b44da9ba04..581ce754e0 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml
@@ -16,7 +16,7 @@
Créer un package
Supprimer
Désactiver
- Vider la corbeille
+ Vider la corbeille
Activer
Exporter le type de document
Importer un type de document
@@ -310,7 +310,6 @@
Fermer cette fenêtre
Êtes-vous certain(e) de vouloir supprimer
Êtes-vous certain(e) de vouloir désactiver
- Cochez cette case pour confirmer la suppression de %0% élément(s)
Êtes-vous certain(e)?
Êtes-vous certain(e)?
Couper
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml
index 0fb00ef139..011745ac0f 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/he.xml
@@ -13,7 +13,7 @@
צור חבילה
מחק
נטרל
- רוקן סל מיחזור
+ רוקן סל מיחזור
ייצא סוג קובץ
ייבא סוג מסמך
ייבא חבילה
@@ -159,7 +159,6 @@
סגור חלון זה
האם הינך בטוח שברצונך למחוק זאת?
האם הינך בטוח שברצונך לכבות זאת?
- סמן תיבה זו לאשר מחיקה סופית של %0% פריט/ים
האם הינך בטוח?
האם אתה בטוח?
גזור
@@ -888,4 +887,4 @@ To manage your website, simply open the Umbraco back office and start adding con
סוגי משתמש
כותב
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml
index 7b8da34c7a..a55c626625 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/it.xml
@@ -13,7 +13,7 @@
Crea pacchetto
Cancella
Disabilita
- Svuota il cestino
+ Svuota il cestino
Esporta il tipo di documento
Importa il tipo di documento
Importa il pacchetto
@@ -164,7 +164,6 @@
Chiudi questa finestra
-
Taglia
@@ -877,4 +876,4 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i
Writer
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml
index e53abc2b53..3b23ccf877 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml
@@ -14,7 +14,7 @@
パッケージの作成
削除
無効
- ごみ箱を空にする
+ ごみ箱を空にする
ドキュメントタイプの書出
ドキュメントタイプの読込
パッケージの読み込み
@@ -224,7 +224,6 @@
このウィンドウを閉じる
削除しますか?
無効にしますか?
- これらの %0% 個の項目を削除する場合は、チェックボックスにチェックを入れてください
ログアウトしますか?
本当にいいですか?
切り取り
@@ -1132,4 +1131,4 @@ Runwayをインストールして作られた新しいウェブサイトがど
... またはカスタム検証を入力
必須フィールドです
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml
index f2d27094a8..7a46ea81c8 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/ko.xml
@@ -13,7 +13,7 @@
패키지 새로 만들기
삭제
비활성
- 휴지통 비우기
+ 휴지통 비우기
추출 문서 유형
등록 문서 유형
패키지 등록
@@ -158,7 +158,6 @@
이창 닫기
정말로 삭제하시겠습니까?
정말로 비활성화하시겠습니까?
- %0% 항목(들)을 삭제하시려면 이 박스를 체크하세요
로그아웃 하시겠습니까?
확실합니까?
TRANSLATE ME: 'Cut'
@@ -868,4 +867,4 @@
사용자 타입
작성자
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml
index 0a20fa07d3..d51d41e558 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml
@@ -14,7 +14,7 @@
Opprett pakke
Slett
Deaktiver
- Tøm papirkurv
+ Tøm papirkurv
Eksporter dokumenttype
Importer dokumenttype
Importer pakke
@@ -218,7 +218,6 @@
Lukk dette vinduet
Er du sikker på at du vil slette
Er du sikker på at du vil deaktivere
- Vennligst kryss av i denne boksen for å bekrefte sletting av %0% element(er)
Er du sikker på at du vil forlate Umbraco?
Er du sikker?
Klipp ut
@@ -961,4 +960,4 @@ Vennlig hilsen Umbraco roboten
Din historikk
Sesjonen utløper om
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml
index 395fc1a869..4aa3126701 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml
@@ -14,7 +14,7 @@
Nieuwe package
Verwijderen
Uitschakelen
- Prullenbak leegmaken
+ Prullenbak leegmaken
Documenttype exporteren
Documenttype importeren
Package importeren
@@ -249,7 +249,6 @@
Sluit dit venster
Weet je zeker dat je dit wilt verwijderen
Weet je zeker dat je dit wilt uitschakelen
- Vink aub dit keuzevak aan om het verwijderen van %0% item(s) te bevestigen
Weet je het zeker?
Weet je het zeker?
Knippen
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml
index d0f18119ef..e3e0fe8e7d 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml
@@ -15,7 +15,7 @@
Stwórz grupę
Usuń
Deaktywuj
- Opróżnij kosz
+ Opróżnij kosz
Aktywuj
Eksportuj typ dokumentu
Importuj typ dokumentu
@@ -298,7 +298,6 @@
Zamknij to okno
Jesteś pewny, że chcesz usunąć
Jesteś pewny, że chcesz wyłączyć
- Proszę zaznaczyć, aby potwierdzić usunięcie %0% elementów.
Jesteś pewny?
Jesteś pewny?
Wytnij
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml
index 98f557a78c..cff6009bb4 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/pt.xml
@@ -13,7 +13,7 @@
Criar Pacote
Remover
Desabilitar
- Esvaziar Lixeira
+ Esvaziar Lixeira
Exportar Tipo de Documento
Importar Tipo de Documento
Importar Pacote
@@ -158,7 +158,6 @@
Fechar esta janela
Certeza em remover
Certeza em desabilitar
- Favor selecionar esta caixa para confirmar a remoção de %0% item(s)
Tem certeza
Tem certeza?
Cortar
@@ -857,4 +856,4 @@ Você pode publicar esta página e todas suas sub-páginas ao selecionar pub
Tipos de usuários
Escrevente
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml
index 61e4ea2d06..fc8504fb13 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml
@@ -17,7 +17,7 @@
Значение по умолчанию
Удалить
Отключить
- Очистить корзину
+ Очистить корзину
Включить
Экспорт
Экспортировать
@@ -346,7 +346,6 @@
Закрыть это окно
Вы уверены, что хотите удалить
Вы уверены, что хотите запретить
- Пожалуйста, подтвердите удаление из корзины %0% элементов
Вы уверены?
Вы уверены?
Вырезать
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml
index 262df44eaf..77e213159b 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml
@@ -15,7 +15,7 @@
Standardvärde
Ta bort
Avaktivera
- Töm papperskorgen
+ Töm papperskorgen
Exportera dokumenttyp
Importera dokumenttyp
Importera paket
@@ -216,7 +216,6 @@
Stäng fönstret
Är du säker på att du vill ta bort
Är du säker på att du vill avaktivera
- Kryssa i denna ruta för att bekräfta att %0% objekt tas bort
Är du säker?
Är du säker?
Klipp ut
@@ -904,4 +903,4 @@
Din nuvarande historik
Din profil
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml
index 79304d86c2..ddf9aafaae 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml
@@ -14,7 +14,7 @@
Paket Oluştur
Sil
Devre Dışı Bırak
- Geri Dönüşümü Boşat
+ Geri Dönüşümü Boşat
Belge Türü Çıkart
Belge Türü Al
Paket Ekle
@@ -191,7 +191,6 @@
Bu pencereyi kapatın
Silmek istediğine emin misin
Eğer devre dışı bırakmak istediğinizden emin misiniz
- %0% öğe(lerin) silinmesi onaylamak için bu kutuyu kontrol edin
Emin misiniz?
Emin misiniz?
Kes
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml
index 0064653a69..05f8c3ecf7 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml
@@ -14,7 +14,7 @@
创建扩展包
删除
禁用
- 清空回收站
+ 清空回收站
导出文档类型
导入文档类型
导入扩展包
@@ -237,7 +237,6 @@
关闭窗口
您确定要删除吗
您确定要禁用吗
- 单击此框确定删除%0%项
您确定吗?
您确定吗?
剪切
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml
index 2579592cfe..0a87631329 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml
@@ -14,7 +14,7 @@
創建擴展包
刪除
禁用
- 清空回收站
+ 清空回收站
匯出文檔類型
導入文檔類型
導入擴展包
@@ -234,7 +234,6 @@
關閉窗口
您確定要刪除嗎
您確定要禁用嗎
- 按一下此框確定刪除%0%項
您確定嗎?
您確定嗎?
剪切
diff --git a/src/Umbraco.Web.UI/Umbraco/endPreview.aspx b/src/Umbraco.Web.UI/Umbraco/endPreview.aspx
deleted file mode 100644
index e55ca96618..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/endPreview.aspx
+++ /dev/null
@@ -1,3 +0,0 @@
-<%@ Page Language="C#" AutoEventWireup="true" Inherits="umbraco.presentation.endPreview" %>
-
-
diff --git a/src/Umbraco.Web.UI/Umbraco/ping.aspx b/src/Umbraco.Web.UI/Umbraco/ping.aspx
deleted file mode 100644
index ffb1631650..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/ping.aspx
+++ /dev/null
@@ -1,2 +0,0 @@
-<%@ Page language="c#" AutoEventWireup="True" %>
-I'm alive!
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI/Umbraco/umbraco.aspx b/src/Umbraco.Web.UI/Umbraco/umbraco.aspx
deleted file mode 100644
index 6e70513afd..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/umbraco.aspx
+++ /dev/null
@@ -1,2 +0,0 @@
-<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="umbraco.aspx.cs" Inherits="Umbraco.Web.UI.Umbraco.umbraco" %>
-
diff --git a/src/Umbraco.Web.UI/Umbraco/umbraco.aspx.cs b/src/Umbraco.Web.UI/Umbraco/umbraco.aspx.cs
deleted file mode 100644
index c4d7992db4..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/umbraco.aspx.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-using System.Web.UI;
-using System.Web.UI.WebControls;
-using Umbraco.Core.Composing;
-using Umbraco.Core.Configuration;
-
-namespace Umbraco.Web.UI.Umbraco
-{
- public partial class umbraco : System.Web.UI.Page
- {
- protected void Page_Load(object sender, EventArgs e)
- {
- Response.Status = "301 Moved Permanently";
- Response.AddHeader("Location", Current.Config.Global().Path);
- }
- }
-}
diff --git a/src/Umbraco.Web.UI/Umbraco/umbraco.aspx.designer.cs b/src/Umbraco.Web.UI/Umbraco/umbraco.aspx.designer.cs
deleted file mode 100644
index c9a577fb34..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/umbraco.aspx.designer.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace Umbraco.Web.UI.Umbraco {
-
-
- public partial class umbraco {
- }
-}
diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config
index 42c8b01e24..df6fe953fe 100644
--- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config
+++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config
@@ -41,7 +41,7 @@
In Preview Mode - click to end
+ In Preview Mode - click to end
]]>
In Preview Mode - click to end
+ In Preview Mode - click to end
]]>
diff --git a/src/Umbraco.Web.UI/default.aspx b/src/Umbraco.Web.UI/default.aspx
index 75b0e5d28c..a41d1eccf7 100644
--- a/src/Umbraco.Web.UI/default.aspx
+++ b/src/Umbraco.Web.UI/default.aspx
@@ -1,2 +1,2 @@
-<%@ Page language="c#" Codebehind="default.aspx.cs" AutoEventWireup="True" Inherits="umbraco.UmbracoDefault" trace="true" validateRequest="false" %>
+<%@ Page language="c#" AutoEventWireup="True" Inherits="umbraco.UmbracoDefault" trace="true" validateRequest="false" %>
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nb.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nb.xml
index 0a20fa07d3..d51d41e558 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/nb.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/nb.xml
@@ -14,7 +14,7 @@
Opprett pakke
Slett
Deaktiver
- Tøm papirkurv
+ Tøm papirkurv
Eksporter dokumenttype
Importer dokumenttype
Importer pakke
@@ -218,7 +218,6 @@
Lukk dette vinduet
Er du sikker på at du vil slette
Er du sikker på at du vil deaktivere
- Vennligst kryss av i denne boksen for å bekrefte sletting av %0% element(er)
Er du sikker på at du vil forlate Umbraco?
Er du sikker?
Klipp ut
@@ -961,4 +960,4 @@ Vennlig hilsen Umbraco roboten
Din historikk
Sesjonen utløper om
-
\ No newline at end of file
+
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/zh_tw.xml b/src/Umbraco.Web.UI/umbraco/config/lang/zh_tw.xml
index 2579592cfe..0a87631329 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/zh_tw.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/zh_tw.xml
@@ -14,7 +14,7 @@
創建擴展包
刪除
禁用
- 清空回收站
+ 清空回收站
匯出文檔類型
導入文檔類型
導入擴展包
@@ -234,7 +234,6 @@
關閉窗口
您確定要刪除嗎
您確定要禁用嗎
- 按一下此框確定刪除%0%項
您確定嗎?
您確定嗎?
剪切
diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs
index 4d7a21ffef..3cecdbe8e5 100644
--- a/src/Umbraco.Web/Editors/EntityController.cs
+++ b/src/Umbraco.Web/Editors/EntityController.cs
@@ -486,7 +486,10 @@ namespace Umbraco.Web.Editors
var pagedResult = new PagedResult(totalRecords, pageNumber, pageSize)
{
- Items = entities.Select(Mapper.Map)
+ Items = entities.Select(entity => Mapper.Map(entity, options =>
+ options.AfterMap((src, dest) => { dest.AdditionalData["hasChildren"] = src.HasChildren; })
+ )
+ )
};
return pagedResult;
diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs
index 67209c91bd..2f96ee3d45 100644
--- a/src/Umbraco.Web/Editors/ExamineManagementController.cs
+++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs
@@ -73,7 +73,7 @@ namespace Umbraco.Web.Editors
if (!msg.IsSuccessStatusCode)
throw new HttpResponseException(msg);
- var results = TryParseLuceneQuery(query)
+ var results = Examine.ExamineExtensions.TryParseLuceneQuery(query)
? searcher.Search(searcher.CreateCriteria().RawQuery(query), maxResults: pageSize * (pageIndex + 1))
: searcher.Search(query, true, maxResults: pageSize * (pageIndex + 1));
@@ -92,28 +92,7 @@ namespace Umbraco.Web.Editors
};
}
- private bool TryParseLuceneQuery(string query)
- {
- //TODO: I'd assume there would be a more strict way to parse the query but not that i can find yet, for now we'll
- // also do this rudimentary check
- if (!query.Contains(":"))
- return false;
-
- try
- {
- //This will pass with a plain old string without any fields, need to figure out a way to have it properly parse
- var parsed = new QueryParser(Version.LUCENE_30, "nodeName", new KeywordAnalyzer()).Parse(query);
- return true;
- }
- catch (ParseException)
- {
- return false;
- }
- catch (Exception)
- {
- return false;
- }
- }
+
///
/// Check if the index has been rebuilt
diff --git a/src/Umbraco.Web/Editors/KeepAliveController.cs b/src/Umbraco.Web/Editors/KeepAliveController.cs
new file mode 100644
index 0000000000..b067a5b67e
--- /dev/null
+++ b/src/Umbraco.Web/Editors/KeepAliveController.cs
@@ -0,0 +1,33 @@
+using System.Runtime.Serialization;
+using System.Web.Http;
+using Umbraco.Web.WebApi;
+
+namespace Umbraco.Web.Editors
+{
+ // fixme
+ // this is not authenticated, and therefore public, and therefore reveals we
+ // are running Umbraco - but, all requests should come from localhost really,
+ // so there should be a way to 404 when the request comes from the outside.
+
+ public class KeepAliveController : UmbracoApiController
+ {
+ [HttpGet]
+ public KeepAlivePingResult Ping()
+ {
+ return new KeepAlivePingResult
+ {
+ Success = true,
+ Message = "I'm alive!"
+ };
+ }
+ }
+
+ public class KeepAlivePingResult
+ {
+ [DataMember(Name = "success")]
+ public bool Success { get; set; }
+
+ [DataMember(Name = "message")]
+ public string Message { get; set; }
+ }
+}
diff --git a/src/Umbraco.Web/Editors/MemberGroupController.cs b/src/Umbraco.Web/Editors/MemberGroupController.cs
index 4ea823b25a..6feaab540b 100644
--- a/src/Umbraco.Web/Editors/MemberGroupController.cs
+++ b/src/Umbraco.Web/Editors/MemberGroupController.cs
@@ -36,6 +36,17 @@ namespace Umbraco.Web.Editors
return dto;
}
+ public IEnumerable GetByIds([FromUri]int[] ids)
+ {
+ if (_provider.IsUmbracoMembershipProvider())
+ {
+ return Services.MemberGroupService.GetByIds(ids)
+ .Select(Mapper.Map);
+ }
+
+ return Enumerable.Empty();
+ }
+
[HttpDelete]
[HttpPost]
public HttpResponseMessage DeleteById(int id)
diff --git a/src/Umbraco.Web/Editors/PreviewController.cs b/src/Umbraco.Web/Editors/PreviewController.cs
index 6a91d20ae0..6e119d68d9 100644
--- a/src/Umbraco.Web/Editors/PreviewController.cs
+++ b/src/Umbraco.Web/Editors/PreviewController.cs
@@ -88,5 +88,23 @@ namespace Umbraco.Web.Editors
// if (string.IsNullOrEmpty(editor)) throw new ArgumentNullException(nameof(editor));
// return View(_globalSettings.Path.EnsureEndsWith('/') + "Views/Preview/" + editor.Replace(".html", string.Empty) + ".cshtml");
//}
+
+ public ActionResult End(string redir = null)
+ {
+ var previewToken = Request.GetPreviewCookieValue();
+ var service = Current.PublishedSnapshotService;
+ service.ExitPreview(previewToken);
+
+ System.Web.HttpContext.Current.ExpireCookie(Constants.Web.PreviewCookieName);
+
+ if (Uri.IsWellFormedUriString(redir, UriKind.Relative)
+ && redir.StartsWith("//") == false
+ && Uri.TryCreate(redir, UriKind.Relative, out Uri url))
+ {
+ return Redirect(url.ToString());
+ }
+
+ return Redirect("/");
+ }
}
}
diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs
index 0d0438138a..00e124cb29 100644
--- a/src/Umbraco.Web/Editors/TemplateQueryController.cs
+++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs
@@ -18,26 +18,26 @@ namespace Umbraco.Web.Editors
[JsonCamelCaseFormatter]
public class TemplateQueryController : UmbracoAuthorizedJsonController
{
- private IEnumerable Terms
+ private IEnumerable Terms
{
get
{
- return new List()
+ return new List()
{
- new OperathorTerm(Services.TextService.Localize("template/is"), Operathor.Equals, new [] {"string"}),
- new OperathorTerm(Services.TextService.Localize("template/isNot"), Operathor.NotEquals, new [] {"string"}),
- new OperathorTerm(Services.TextService.Localize("template/before"), Operathor.LessThan, new [] {"datetime"}),
- new OperathorTerm(Services.TextService.Localize("template/beforeIncDate"), Operathor.LessThanEqualTo, new [] {"datetime"}),
- new OperathorTerm(Services.TextService.Localize("template/after"), Operathor.GreaterThan, new [] {"datetime"}),
- new OperathorTerm(Services.TextService.Localize("template/afterIncDate"), Operathor.GreaterThanEqualTo, new [] {"datetime"}),
- new OperathorTerm(Services.TextService.Localize("template/equals"), Operathor.Equals, new [] {"int"}),
- new OperathorTerm(Services.TextService.Localize("template/doesNotEqual"), Operathor.NotEquals, new [] {"int"}),
- new OperathorTerm(Services.TextService.Localize("template/contains"), Operathor.Contains, new [] {"string"}),
- new OperathorTerm(Services.TextService.Localize("template/doesNotContain"), Operathor.NotContains, new [] {"string"}),
- new OperathorTerm(Services.TextService.Localize("template/greaterThan"), Operathor.GreaterThan, new [] {"int"}),
- new OperathorTerm(Services.TextService.Localize("template/greaterThanEqual"), Operathor.GreaterThanEqualTo, new [] {"int"}),
- new OperathorTerm(Services.TextService.Localize("template/lessThan"), Operathor.LessThan, new [] {"int"}),
- new OperathorTerm(Services.TextService.Localize("template/lessThanEqual"), Operathor.LessThanEqualTo, new [] {"int"})
+ new OperatorTerm(Services.TextService.Localize("template/is"), Operator.Equals, new [] {"string"}),
+ new OperatorTerm(Services.TextService.Localize("template/isNot"), Operator.NotEquals, new [] {"string"}),
+ new OperatorTerm(Services.TextService.Localize("template/before"), Operator.LessThan, new [] {"datetime"}),
+ new OperatorTerm(Services.TextService.Localize("template/beforeIncDate"), Operator.LessThanEqualTo, new [] {"datetime"}),
+ new OperatorTerm(Services.TextService.Localize("template/after"), Operator.GreaterThan, new [] {"datetime"}),
+ new OperatorTerm(Services.TextService.Localize("template/afterIncDate"), Operator.GreaterThanEqualTo, new [] {"datetime"}),
+ new OperatorTerm(Services.TextService.Localize("template/equals"), Operator.Equals, new [] {"int"}),
+ new OperatorTerm(Services.TextService.Localize("template/doesNotEqual"), Operator.NotEquals, new [] {"int"}),
+ new OperatorTerm(Services.TextService.Localize("template/contains"), Operator.Contains, new [] {"string"}),
+ new OperatorTerm(Services.TextService.Localize("template/doesNotContain"), Operator.NotContains, new [] {"string"}),
+ new OperatorTerm(Services.TextService.Localize("template/greaterThan"), Operator.GreaterThan, new [] {"int"}),
+ new OperatorTerm(Services.TextService.Localize("template/greaterThanEqual"), Operator.GreaterThanEqualTo, new [] {"int"}),
+ new OperatorTerm(Services.TextService.Localize("template/lessThan"), Operator.LessThan, new [] {"int"}),
+ new OperatorTerm(Services.TextService.Localize("template/lessThanEqual"), Operator.LessThanEqualTo, new [] {"int"})
};
}
}
@@ -67,6 +67,7 @@ namespace Umbraco.Web.Editors
sb.Append("Model.Root()");
+ //fixme: This timer thing is not correct, it's definitely not timing the resulting query, the timer really isn't important and might as well be removed
var timer = new Stopwatch();
timer.Start();
diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs
index ea76293df5..178027857c 100644
--- a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs
+++ b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs
@@ -122,6 +122,8 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
.AfterMap((src, dest) =>
{
+ //TODO: Properly map this (not aftermap)
+
//get the icon if there is one
dest.Icon = src.Values.ContainsKey(UmbracoExamineIndex.IconFieldName)
? src.Values[UmbracoExamineIndex.IconFieldName]
diff --git a/src/Umbraco.Web/Models/TemplateQuery/Operathor.cs b/src/Umbraco.Web/Models/TemplateQuery/Operator.cs
similarity index 90%
rename from src/Umbraco.Web/Models/TemplateQuery/Operathor.cs
rename to src/Umbraco.Web/Models/TemplateQuery/Operator.cs
index 561caec362..135c43507e 100644
--- a/src/Umbraco.Web/Models/TemplateQuery/Operathor.cs
+++ b/src/Umbraco.Web/Models/TemplateQuery/Operator.cs
@@ -1,6 +1,6 @@
namespace Umbraco.Web.Models.TemplateQuery
{
- public enum Operathor
+ public enum Operator
{
Equals = 1,
NotEquals = 2,
diff --git a/src/Umbraco.Web/Models/TemplateQuery/OperathorTerm.cs b/src/Umbraco.Web/Models/TemplateQuery/OperatorTerm.cs
similarity index 56%
rename from src/Umbraco.Web/Models/TemplateQuery/OperathorTerm.cs
rename to src/Umbraco.Web/Models/TemplateQuery/OperatorTerm.cs
index c14e1854aa..086f0ff818 100644
--- a/src/Umbraco.Web/Models/TemplateQuery/OperathorTerm.cs
+++ b/src/Umbraco.Web/Models/TemplateQuery/OperatorTerm.cs
@@ -2,24 +2,24 @@
namespace Umbraco.Web.Models.TemplateQuery
{
- public class OperathorTerm
+ public class OperatorTerm
{
- public OperathorTerm()
+ public OperatorTerm()
{
Name = "is";
- Operathor = Operathor.Equals;
+ Operator = Operator.Equals;
AppliesTo = new [] { "string" };
}
- public OperathorTerm(string name, Operathor operathor, IEnumerable appliesTo)
+ public OperatorTerm(string name, Operator @operator, IEnumerable appliesTo)
{
Name = name;
- Operathor = operathor;
+ Operator = @operator;
AppliesTo = appliesTo;
}
public string Name { get; set; }
- public Operathor Operathor { get; set; }
+ public Operator Operator { get; set; }
public IEnumerable AppliesTo { get; set; }
}
}
diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs
index 8ba943756f..9c5f2c80c0 100644
--- a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs
+++ b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs
@@ -4,7 +4,7 @@
{
public PropertyModel Property { get; set; }
- public OperathorTerm Term { get; set; }
+ public OperatorTerm Term { get; set; }
public string ConstraintValue { get; set; }
}
@@ -53,30 +53,30 @@
}
- switch (condition.Term.Operathor)
+ switch (condition.Term.Operator)
{
- case Operathor.Equals:
+ case Operator.Equals:
operand = " == ";
break;
- case Operathor.NotEquals:
+ case Operator.NotEquals:
operand = " != ";
break;
- case Operathor.GreaterThan:
+ case Operator.GreaterThan:
operand = " > ";
break;
- case Operathor.GreaterThanEqualTo:
+ case Operator.GreaterThanEqualTo:
operand = " >= ";
break;
- case Operathor.LessThan:
+ case Operator.LessThan:
operand = " < ";
break;
- case Operathor.LessThanEqualTo:
+ case Operator.LessThanEqualTo:
operand = " <= ";
break;
- case Operathor.Contains:
+ case Operator.Contains:
value = string.Format("{0}{1}.Contains({2})", prefix, condition.Property.Alias, constraintValue);
break;
- case Operathor.NotContains:
+ case Operator.NotContains:
value = string.Format("!{0}{1}.Contains({2})", prefix, condition.Property.Alias, constraintValue);
break;
default :
diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs
index 07c5aac5bb..74c8744ead 100644
--- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs
+++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs
@@ -175,13 +175,17 @@ namespace Umbraco.Web.PropertyEditors
{
// create a temp property with the value
var tempProp = new Property(propType);
- tempProp.SetValue(propValues[propAlias] == null ? null : propValues[propAlias].ToString());
+ // if the property varies by culture, make sure we save using the current culture
+ var propCulture = propType.VariesByCulture() || propType.VariesByCultureAndSegment()
+ ? culture
+ : null;
+ tempProp.SetValue(propValues[propAlias] == null ? null : propValues[propAlias].ToString(), propCulture);
// convert that temp property, and store the converted value
var propEditor = _propertyEditors[propType.PropertyEditorAlias];
var tempConfig = dataTypeService.GetDataType(propType.DataTypeId).Configuration;
var valEditor = propEditor.GetValueEditor(tempConfig);
- var convValue = valEditor.ToEditor(tempProp, dataTypeService);
+ var convValue = valEditor.ToEditor(tempProp, dataTypeService, propCulture);
propValues[propAlias] = convValue == null ? null : JToken.FromObject(convValue);
}
catch (InvalidOperationException)
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs
index 4453fe7321..7c311236c0 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs
@@ -55,7 +55,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key");
//ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId");
ValidateAndSetProperty(valueDictionary, val => _sortOrder = Int32.Parse(val), "sortOrder");
- ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName", "__nodeName");
+ ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName");
ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName");
ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", LuceneIndex.ItemTypeFieldName);
ValidateAndSetProperty(valueDictionary, val => _documentTypeId = Int32.Parse(val), "nodeType");
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
index ac6b425e27..f203d5d2c9 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
@@ -240,9 +240,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
try
{
- if (eMgr.TryGetIndex(Constants.Examine.InternalIndexer, out var index))
+ if (eMgr.TryGetIndex(Constants.UmbracoIndexes.InternalIndexName, out var index))
return index.GetSearcher();
- throw new InvalidOperationException($"No index found by name {Constants.Examine.InternalIndexer}");
+ throw new InvalidOperationException($"No index found by name {Constants.UmbracoIndexes.InternalIndexName}");
}
catch (FileNotFoundException)
{
diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs
index 92edb88297..9a8f3ff7ac 100644
--- a/src/Umbraco.Web/PublishedContentExtensions.cs
+++ b/src/Umbraco.Web/PublishedContentExtensions.cs
@@ -270,7 +270,7 @@ namespace Umbraco.Web
{
//fixme: pass in the IExamineManager
- indexName = string.IsNullOrEmpty(indexName) ? Constants.Examine.ExternalIndexer : indexName;
+ indexName = string.IsNullOrEmpty(indexName) ? Constants.UmbracoIndexes.ExternalIndexName : indexName;
if (!ExamineManager.Instance.TryGetIndex(indexName, out var index))
throw new InvalidOperationException("No index found with name " + indexName);
@@ -290,7 +290,7 @@ namespace Umbraco.Web
{
//fixme: pass in the IExamineManager
- indexName = string.IsNullOrEmpty(indexName) ? Constants.Examine.ExternalIndexer : indexName;
+ indexName = string.IsNullOrEmpty(indexName) ? Constants.UmbracoIndexes.ExternalIndexName : indexName;
if (!ExamineManager.Instance.TryGetIndex(indexName, out var index))
throw new InvalidOperationException("No index found with name " + indexName);
@@ -312,8 +312,8 @@ namespace Umbraco.Web
if (searchProvider == null)
{
- if (!ExamineManager.Instance.TryGetIndex(Constants.Examine.ExternalIndexer, out var index))
- throw new InvalidOperationException("No index found with name " + Constants.Examine.ExternalIndexer);
+ if (!ExamineManager.Instance.TryGetIndex(Constants.UmbracoIndexes.ExternalIndexName, out var index))
+ throw new InvalidOperationException("No index found with name " + Constants.UmbracoIndexes.ExternalIndexName);
searchProvider = index.GetSearcher();
}
var results = searchProvider.Search(criteria);
diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs
index 04611000b9..774393f3de 100644
--- a/src/Umbraco.Web/PublishedContentQuery.cs
+++ b/src/Umbraco.Web/PublishedContentQuery.cs
@@ -192,7 +192,7 @@ namespace Umbraco.Web
//fixme: inject IExamineManager
indexName = string.IsNullOrEmpty(indexName)
- ? Constants.Examine.ExternalIndexer
+ ? Constants.UmbracoIndexes.ExternalIndexName
: indexName;
if (!ExamineManager.Instance.TryGetIndex(indexName, out var index))
@@ -220,8 +220,8 @@ namespace Umbraco.Web
//fixme: inject IExamineManager
if (searcher == null)
{
- if (!ExamineManager.Instance.TryGetIndex(Constants.Examine.ExternalIndexer, out var index))
- throw new InvalidOperationException($"No index found by name {Constants.Examine.ExternalIndexer}");
+ if (!ExamineManager.Instance.TryGetIndex(Constants.UmbracoIndexes.ExternalIndexName, out var index))
+ throw new InvalidOperationException($"No index found by name {Constants.UmbracoIndexes.ExternalIndexName}");
searcher = index.GetSearcher();
}
diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs
index 8c851d139f..4c879e931f 100644
--- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs
+++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs
@@ -31,7 +31,7 @@ namespace Umbraco.Web.Routing
#region GetUrl
///
- public string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
+ public UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
{
return null; // we have nothing to say
}
@@ -51,13 +51,14 @@ namespace Umbraco.Web.Routing
/// Other urls are those that GetUrl would not return in the current context, but would be valid
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).
///
- public IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
+ public IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
{
var node = umbracoContext.ContentCache.GetById(id);
- if (node == null) return Enumerable.Empty();
+ if (node == null)
+ yield break;
if (!node.HasProperty(Constants.Conventions.Content.UrlAlias))
- return Enumerable.Empty();
+ yield break;
var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper);
@@ -82,20 +83,19 @@ namespace Umbraco.Web.Routing
// the content finder may work, depending on the 'current' culture,
// but there's no way we can return something meaningful here
if (varies)
- return Enumerable.Empty();
+ yield break;
var umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias);
if (string.IsNullOrWhiteSpace(umbracoUrlName))
- return Enumerable.Empty();
+ yield break;
var path = "/" + umbracoUrlName;
var uri = new Uri(path, UriKind.Relative);
- return new[] { UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString() };
+ yield return UrlInfo.Url(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString());
}
else
{
// some domains: one url per domain, which is "/"
- var result = new List();
foreach(var domainUri in domainUris)
{
// if the property is invariant, get the invariant value, url is "/"
@@ -108,14 +108,13 @@ namespace Umbraco.Web.Routing
? node.Value(Constants.Conventions.Content.UrlAlias, culture: domainUri.Culture.Name)
: node.Value(Constants.Conventions.Content.UrlAlias);
- if (!string.IsNullOrWhiteSpace(umbracoUrlName))
- {
- var path = "/" + umbracoUrlName;
- var uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path));
- result.Add(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString());
- }
+ if (string.IsNullOrWhiteSpace(umbracoUrlName))
+ continue;
+
+ var path = "/" + umbracoUrlName;
+ var uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path));
+ yield return UrlInfo.Url(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString(), domainUri.Culture.Name);
}
- return result;
}
}
diff --git a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs
index ce1fc1ffab..ae17ff6258 100644
--- a/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs
+++ b/src/Umbraco.Web/Routing/CustomRouteUrlProvider.cs
@@ -15,7 +15,7 @@ namespace Umbraco.Web.Routing
///
/// This will return the URL that is returned by the assigned custom if this is a custom route
///
- public string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
+ public UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
{
if (umbracoContext?.PublishedRequest?.PublishedContent == null) return null;
if (umbracoContext.HttpContext?.Request?.RequestContext?.RouteData?.DataTokens == null) return null;
@@ -26,13 +26,15 @@ namespace Umbraco.Web.Routing
//NOTE: This looks like it might cause an infinite loop because PublishedContentBase.Url calls into UmbracoContext.Current.UrlProvider.GetUrl which calls back into the IUrlProvider pipeline
// but the specific purpose of this is that a developer is using their own IPublishedContent that returns a specific Url and doesn't go back into the UrlProvider pipeline.
//TODO: We could put a try/catch here just in case, else we could do some reflection checking to see if the implementation is PublishedContentBase and the Url property is not overridden.
- return content.Id == umbracoContext.PublishedRequest.PublishedContent.Id
- ? umbracoContext.PublishedRequest.PublishedContent.GetUrl(culture)
- : null;
+ return UrlInfo.Url(
+ content.Id == umbracoContext.PublishedRequest.PublishedContent.Id
+ ? umbracoContext.PublishedRequest.PublishedContent.GetUrl(culture)
+ : null,
+ culture);
}
///
- /// This always returns null because this url provider is used purely to deal with Umbraco custom routes with
+ /// This always returns an empty result because this url provider is used purely to deal with Umbraco custom routes with
/// UmbracoVirtualNodeRouteHandler, we really only care about the normal URL so that RedirectToCurrentUmbracoPage() works
/// with SurfaceControllers
///
@@ -40,9 +42,9 @@ namespace Umbraco.Web.Routing
///
///
///
- public IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
+ public IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
{
- return null;
+ yield break;
}
}
}
diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs
index 61437b6640..5045b1af96 100644
--- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs
+++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs
@@ -29,7 +29,7 @@ namespace Umbraco.Web.Routing
#region GetUrl
///
- public virtual string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
+ public virtual UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current)
{
if (!current.IsAbsoluteUri) throw new ArgumentException("Current url must be absolute.", nameof(current));
@@ -39,7 +39,7 @@ namespace Umbraco.Web.Routing
return GetUrlFromRoute(route, umbracoContext, content.Id, current, mode, culture);
}
- internal string GetUrlFromRoute(string route, UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture)
+ internal UrlInfo GetUrlFromRoute(string route, UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode, string culture)
{
if (string.IsNullOrWhiteSpace(route))
{
@@ -58,7 +58,9 @@ namespace Umbraco.Web.Routing
: domainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current, culture);
// assemble the url from domainUri (maybe null) and path
- return AssembleUrl(domainUri, path, current, mode).ToString();
+ var url = AssembleUrl(domainUri, path, current, mode).ToString();
+
+ return UrlInfo.Url(url, culture);
}
#endregion
@@ -76,10 +78,11 @@ namespace Umbraco.Web.Routing
/// Other urls are those that GetUrl would not return in the current context, but would be valid
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).
///
- public virtual IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
+ public virtual IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
{
var node = umbracoContext.ContentCache.GetById(id);
- if (node == null) return Enumerable.Empty();
+ if (node == null)
+ yield break;
var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper);
@@ -94,13 +97,14 @@ namespace Umbraco.Web.Routing
// no domains = exit
if (domainUris ==null)
- return Enumerable.Empty();
+ yield break;
- var result = new List();
foreach (var d in domainUris)
{
+ var culture = d?.Culture?.Name;
+
//although we are passing in culture here, if any node in this path is invariant, it ignores the culture anyways so this is ok
- var route = umbracoContext.ContentCache.GetRouteById(id, d?.Culture?.Name);
+ var route = umbracoContext.ContentCache.GetRouteById(id, culture);
if (route == null) continue;
//need to strip off the leading ID for the route if it exists (occurs if the route is for a node with a domain assigned)
@@ -109,9 +113,8 @@ namespace Umbraco.Web.Routing
var uri = new Uri(CombinePaths(d.Uri.GetLeftPart(UriPartial.Path), path));
uri = UriUtility.UriFromUmbraco(uri, _globalSettings, _requestSettings);
- result.Add(uri.ToString());
+ yield return UrlInfo.Url(uri.ToString(), culture);
}
- return result;
}
#endregion
@@ -146,7 +149,7 @@ namespace Umbraco.Web.Routing
uri = new Uri(path, UriKind.Relative);
break;
default:
- throw new ArgumentOutOfRangeException("mode");
+ throw new ArgumentOutOfRangeException(nameof(mode));
}
}
else // a domain was found
@@ -169,7 +172,7 @@ namespace Umbraco.Web.Routing
uri = new Uri(CombinePaths(domainUri.Uri.AbsolutePath, path), UriKind.Relative);
break;
default:
- throw new ArgumentOutOfRangeException("mode");
+ throw new ArgumentOutOfRangeException(nameof(mode));
}
}
diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs
index b6d79e788a..edfcb33aa5 100644
--- a/src/Umbraco.Web/Routing/DomainHelper.cs
+++ b/src/Umbraco.Web/Routing/DomainHelper.cs
@@ -30,10 +30,10 @@ namespace Umbraco.Web.Routing
/// The culture, or null.
/// The domain and its uri, if any, that best matches the specified uri and culture, else null.
///
- /// f at least a domain is set on the node then the method returns the domain that
+ /// If at least a domain is set on the node then the method returns the domain that
/// best matches the specified uri and culture, else it returns null.
- /// If culture is null, uses the default culture for the installation instead.
- /// fixme not exactly - if culture is !null, we MUST have a value for THAT culture, else we use default as hint
+ /// If culture is null, uses the default culture for the installation instead. Otherwise,
+ /// will try with the specified culture, else return null.
///
internal DomainAndUri DomainForNode(int nodeId, Uri current, string culture = null)
{
@@ -49,13 +49,9 @@ namespace Umbraco.Web.Routing
return null;
// else filter
- var domainAndUri = SelectDomain(domains, current, culture, _domainCache.DefaultCulture,
+ // it could be that none apply (due to culture)
+ return SelectDomain(domains, current, culture, _domainCache.DefaultCulture,
(cdomainAndUris, ccurrent, cculture, cdefaultCulture) => _siteDomainHelper.MapDomain(cdomainAndUris, ccurrent, cculture, cdefaultCulture));
-
- if (domainAndUri == null)
- throw new Exception("DomainForUri returned null.");
-
- return domainAndUri;
}
///
@@ -202,13 +198,12 @@ namespace Umbraco.Web.Routing
private static IReadOnlyCollection SelectByCulture(IReadOnlyCollection domainsAndUris, string culture, string defaultCulture)
{
+ // we try our best to match cultures, but may end with a bogus domain
+
if (culture != null) // try the supplied culture
{
var cultureDomains = domainsAndUris.Where(x => x.Culture.Name.InvariantEquals(culture)).ToList();
if (cultureDomains.Count > 0) return cultureDomains;
-
- // if a culture is supplied, we *want* a url for that culture, else fail
- throw new InvalidOperationException($"Could not find a domain for culture \"{culture}\".");
}
if (defaultCulture != null) // try the defaultCulture culture
@@ -224,13 +219,12 @@ namespace Umbraco.Web.Routing
{
DomainAndUri domainAndUri;
+ // we try our best to match cultures, but may end with a bogus domain
+
if (culture != null) // try the supplied culture
{
domainAndUri = domainsAndUris.FirstOrDefault(x => x.Culture.Name.InvariantEquals(culture));
if (domainAndUri != null) return domainAndUri;
-
- // if a culture is supplied, we *want* a url for that culture, else fail
- throw new InvalidOperationException($"Could not find a domain for culture \"{culture}\".");
}
if (defaultCulture != null) // try the defaultCulture culture
diff --git a/src/Umbraco.Web/Routing/IUrlProvider.cs b/src/Umbraco.Web/Routing/IUrlProvider.cs
index 0f102dceb8..55d39880d6 100644
--- a/src/Umbraco.Web/Routing/IUrlProvider.cs
+++ b/src/Umbraco.Web/Routing/IUrlProvider.cs
@@ -24,7 +24,7 @@ namespace Umbraco.Web.Routing
/// when no culture is specified, the current culture.
/// If the provider is unable to provide a url, it should return null.
///
- string GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current);
+ UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current);
///
/// Gets the other urls of a published content.
@@ -37,6 +37,6 @@ namespace Umbraco.Web.Routing
/// Other urls are those that GetUrl would not return in the current context, but would be valid
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).
///
- IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current);
+ IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current);
}
}
diff --git a/src/Umbraco.Web/Routing/UrlInfo.cs b/src/Umbraco.Web/Routing/UrlInfo.cs
index 01dbe4a0e1..ae5c4b412c 100644
--- a/src/Umbraco.Web/Routing/UrlInfo.cs
+++ b/src/Umbraco.Web/Routing/UrlInfo.cs
@@ -1,4 +1,5 @@
-using System.Runtime.Serialization;
+using System;
+using System.Runtime.Serialization;
namespace Umbraco.Web.Routing
{
@@ -6,13 +7,25 @@ namespace Umbraco.Web.Routing
/// Represents infos for a url.
///
[DataContract(Name = "urlInfo", Namespace = "")]
- public class UrlInfo
+ public class UrlInfo : IEquatable
{
+
+ ///
+ /// Creates a instance representing a true url.
+ ///
+ public static UrlInfo Url(string text, string culture = null) => new UrlInfo(text, true, culture);
+
+ ///
+ /// Creates a instance representing a message.
+ ///
+ public static UrlInfo Message(string text, string culture = null) => new UrlInfo(text, false, culture);
+
///
/// Initializes a new instance of the class.
///
public UrlInfo(string text, bool isUrl, string culture)
{
+ if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(text));
IsUrl = isUrl;
Text = text;
Culture = culture;
@@ -38,13 +51,47 @@ namespace Umbraco.Web.Routing
public string Text { get; }
///
- /// Creates a instance representing a true url.
+ /// Checks equality
///
- public static UrlInfo Url(string text, string culture = null) => new UrlInfo(text, true, culture);
+ ///
+ ///
+ ///
+ /// Compare both culture and Text as invariant strings since URLs are not case sensitive, nor are culture names within Umbraco
+ ///
+ public bool Equals(UrlInfo other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return string.Equals(Culture, other.Culture, StringComparison.InvariantCultureIgnoreCase) && IsUrl == other.IsUrl && string.Equals(Text, other.Text, StringComparison.InvariantCultureIgnoreCase);
+ }
- ///
- /// Creates a instance representing a message.
- ///
- public static UrlInfo Message(string text, string culture = null) => new UrlInfo(text, false, culture);
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((UrlInfo) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = (Culture != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(Culture) : 0);
+ hashCode = (hashCode * 397) ^ IsUrl.GetHashCode();
+ hashCode = (hashCode * 397) ^ (Text != null ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(Text) : 0);
+ return hashCode;
+ }
+ }
+
+ public static bool operator ==(UrlInfo left, UrlInfo right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(UrlInfo left, UrlInfo right)
+ {
+ return !Equals(left, right);
+ }
}
}
diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs
index b265d48923..7ed530093c 100644
--- a/src/Umbraco.Web/Routing/UrlProvider.cs
+++ b/src/Umbraco.Web/Routing/UrlProvider.cs
@@ -202,7 +202,7 @@ namespace Umbraco.Web.Routing
var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, content, mode, culture, current))
.FirstOrDefault(u => u != null);
- return url ?? "#"; // legacy wants this
+ return url?.Text ?? "#"; // legacy wants this
}
internal string GetUrlFromRoute(int id, string route, string culture)
@@ -210,7 +210,7 @@ namespace Umbraco.Web.Routing
var provider = _urlProviders.OfType().FirstOrDefault();
var url = provider == null
? route // what else?
- : provider.GetUrlFromRoute(route, UmbracoContext.Current, id, _umbracoContext.CleanedUmbracoUrl, Mode, culture);
+ : provider.GetUrlFromRoute(route, UmbracoContext.Current, id, _umbracoContext.CleanedUmbracoUrl, Mode, culture)?.Text;
return url ?? "#";
}
@@ -228,7 +228,7 @@ namespace Umbraco.Web.Routing
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).
/// The results depend on the current url.
///
- public IEnumerable GetOtherUrls(int id)
+ public IEnumerable GetOtherUrls(int id)
{
return GetOtherUrls(id, _umbracoContext.CleanedUmbracoUrl);
}
@@ -243,12 +243,9 @@ namespace Umbraco.Web.Routing
/// Other urls are those that GetUrl would not return in the current context, but would be valid
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).
///
- public IEnumerable GetOtherUrls(int id, Uri current)
+ public IEnumerable GetOtherUrls(int id, Uri current)
{
- // providers can return null or an empty list or a non-empty list, be prepared
- var urls = _urlProviders.SelectMany(provider => provider.GetOtherUrls(_umbracoContext, id, current) ?? Enumerable.Empty());
-
- return urls;
+ return _urlProviders.SelectMany(provider => provider.GetOtherUrls(_umbracoContext, id, current));
}
#endregion
diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs
index 20adf2626d..8db657ed30 100644
--- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs
+++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs
@@ -17,7 +17,7 @@ namespace Umbraco.Web.Routing
/// Use when displaying Urls. If errors occur when generating the Urls, they will show in the list.
/// Contains all the Urls that we can figure out (based upon domains, etc).
///
- public static IEnumerable GetContentUrls(this IContent content,
+ public static IEnumerable GetContentUrls(this IContent content,
PublishedRouter publishedRouter,
UmbracoContext umbracoContext,
ILocalizationService localizationService,
@@ -33,25 +33,71 @@ namespace Umbraco.Web.Routing
if (contentService == null) throw new ArgumentNullException(nameof(contentService));
if (logger == null) throw new ArgumentNullException(nameof(logger));
- var urls = new List();
-
if (content.Published == false)
{
- urls.Add(UrlInfo.Message(textService.Localize("content/itemNotPublished")));
- return urls;
+ yield return UrlInfo.Message(textService.Localize("content/itemNotPublished"));
+ yield break;
}
-
+
// build a list of urls, for the back-office
// which will contain
// - the 'main' urls, which is what .Url would return, for each culture
// - the 'other' urls we know (based upon domains, etc)
//
- // need to work on each culture.
- // on invariant trees, each culture return the same thing
- // but, we don't know if the tree to this content is invariant
+ // need to work through each installed culture:
+ // on invariant nodes, each culture returns the same url segment but,
+ // we don't know if the branch to this content is invariant, so we need to ask
+ // for URLs for all cultures.
+ // and, not only for those assigned to domains in the branch, because we want
+ // to show what GetUrl() would return, for every culture.
+ var urls = new HashSet();
var cultures = localizationService.GetAllLanguages().Select(x => x.IsoCode).ToList();
+ //get all URLs for all cultures
+ //in a HashSet, so de-duplicates too
+ foreach (var cultureUrl in GetContentUrlsByCulture(content, cultures, publishedRouter, umbracoContext, contentService, textService, logger))
+ {
+ urls.Add(cultureUrl);
+ }
+
+ //return the real urls first, then the messages
+ foreach (var urlGroup in urls.GroupBy(x => x.IsUrl).OrderByDescending(x => x.Key))
+ {
+ //in some cases there will be the same URL for multiple cultures:
+ // * The entire branch is invariant
+ // * If there are less domain/cultures assigned to the branch than the number of cultures/languages installed
+
+ foreach (var dUrl in urlGroup.DistinctBy(x => x.Text.ToUpperInvariant()).OrderBy(x => x.Text).ThenBy(x => x.Culture))
+ yield return dUrl;
+ }
+
+ // get the 'other' urls - ie not what you'd get with GetUrl() but urls that would route to the document, nevertheless.
+ // for these 'other' urls, we don't check whether they are routable, collide, anything - we just report them.
+ foreach (var otherUrl in umbracoContext.UrlProvider.GetOtherUrls(content.Id).OrderBy(x => x.Text).ThenBy(x => x.Culture))
+ if (urls.Add(otherUrl)) //avoid duplicates
+ yield return otherUrl;
+ }
+
+ ///
+ /// Tries to return a for each culture for the content while detecting collisions/errors
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static IEnumerable GetContentUrlsByCulture(IContent content,
+ IEnumerable cultures,
+ PublishedRouter publishedRouter,
+ UmbracoContext umbracoContext,
+ IContentService contentService,
+ ILocalizedTextService textService,
+ ILogger logger)
+ {
foreach (var culture in cultures)
{
// if content is variant, and culture is not published, skip
@@ -75,53 +121,26 @@ namespace Umbraco.Web.Routing
{
// deal with 'could not get the url'
case "#":
- HandleCouldNotGetUrl(content, culture, urls, contentService, textService);
+ yield return HandleCouldNotGetUrl(content, culture, contentService, textService);
break;
// deal with exceptions
case "#ex":
- urls.Add(UrlInfo.Message(textService.Localize("content/getUrlException"), culture));
+ yield return UrlInfo.Message(textService.Localize("content/getUrlException"), culture);
break;
// got a url, deal with collisions, add url
default:
- if (!DetectCollision(content, url, urls, culture, umbracoContext, publishedRouter, textService)) // detect collisions, etc
- urls.Add(UrlInfo.Url(url, culture));
+ if (DetectCollision(content, url, culture, umbracoContext, publishedRouter, textService, out var urlInfo)) // detect collisions, etc
+ yield return urlInfo;
+ else
+ yield return UrlInfo.Url(url, culture);
break;
}
}
-
- // prepare for de-duplication
- var durl = new Dictionary>();
- var dmsg = new Dictionary>();
- foreach (var url in urls)
- {
- var d = url.IsUrl ? durl : dmsg;
- if (!d.TryGetValue(url.Text, out var l))
- d[url.Text] = l = new List();
- l.Add(url);
- }
-
- // deduplicate, order urls first then messages, concatenate cultures (hide if 'all')
- var ret = new List();
- foreach (var (text, infos) in durl)
- ret.Add(UrlInfo.Url(text, infos.Count == cultures.Count ? null : string.Join(", ", infos.Select(x => x.Culture))));
- foreach (var (text, infos) in dmsg)
- ret.Add(UrlInfo.Message(text, infos.Count == cultures.Count ? null : string.Join(", ", infos.Select(x => x.Culture))));
-
- // get the 'other' urls - ie not what you'd get with GetUrl() but urls that would route to the document, nevertheless.
- // for these 'other' urls, we don't check whether they are routable, collide, anything - we just report them.
- // also, we are not dealing with cultures at all - that will have to wait
- foreach(var otherUrl in umbracoContext.UrlProvider.GetOtherUrls(content.Id))
- {
- if (urls.Any(x => x.IsUrl && x.Text == otherUrl)) continue;
- ret.Add(UrlInfo.Url(otherUrl));
- }
-
- return ret;
}
- private static void HandleCouldNotGetUrl(IContent content, string culture, List urls, IContentService contentService, ILocalizedTextService textService)
+ private static UrlInfo HandleCouldNotGetUrl(IContent content, string culture, IContentService contentService, ILocalizedTextService textService)
{
// document has a published version yet its url is "#" => a parent must be
// unpublished, walk up the tree until we find it, and report.
@@ -133,16 +152,16 @@ namespace Umbraco.Web.Routing
while (parent != null && parent.Published && (!parent.ContentType.VariesByCulture() || parent.IsCulturePublished(culture)));
if (parent == null) // oops, internal error
- urls.Add(UrlInfo.Message(textService.Localize("content/parentNotPublishedAnomaly"), culture));
+ return UrlInfo.Message(textService.Localize("content/parentNotPublishedAnomaly"), culture);
else if (!parent.Published) // totally not published
- urls.Add(UrlInfo.Message(textService.Localize("content/parentNotPublished", new[] { parent.Name }), culture));
+ return UrlInfo.Message(textService.Localize("content/parentNotPublished", new[] {parent.Name}), culture);
else // culture not published
- urls.Add(UrlInfo.Message(textService.Localize("content/parentCultureNotPublished", new[] { parent.Name }), culture));
+ return UrlInfo.Message(textService.Localize("content/parentCultureNotPublished", new[] {parent.Name}), culture);
}
- private static bool DetectCollision(IContent content, string url, List urls, string culture, UmbracoContext umbracoContext, PublishedRouter publishedRouter, ILocalizedTextService textService)
+ private static bool DetectCollision(IContent content, string url, string culture, UmbracoContext umbracoContext, PublishedRouter publishedRouter, ILocalizedTextService textService, out UrlInfo urlInfo)
{
// test for collisions on the 'main' url
var uri = new Uri(url.TrimEnd('/'), UriKind.RelativeOrAbsolute);
@@ -151,9 +170,11 @@ namespace Umbraco.Web.Routing
var pcr = publishedRouter.CreateRequest(umbracoContext, uri);
publishedRouter.TryRouteRequest(pcr);
+ urlInfo = null;
+
if (pcr.HasPublishedContent == false)
{
- urls.Add(UrlInfo.Message(textService.Localize("content/routeErrorCannotRoute"), culture));
+ urlInfo = UrlInfo.Message(textService.Localize("content/routeErrorCannotRoute"), culture);
return true;
}
@@ -172,7 +193,7 @@ namespace Umbraco.Web.Routing
l.Reverse();
var s = "/" + string.Join("/", l) + " (id=" + pcr.PublishedContent.Id + ")";
- urls.Add(UrlInfo.Message(textService.Localize("content/routeError", new[] { s }), culture));
+ urlInfo = UrlInfo.Message(textService.Localize("content/routeError", new[] { s }), culture);
return true;
}
diff --git a/src/Umbraco.Web/Scheduling/KeepAlive.cs b/src/Umbraco.Web/Scheduling/KeepAlive.cs
index 7ef1ec6453..572e4eb5a1 100644
--- a/src/Umbraco.Web/Scheduling/KeepAlive.cs
+++ b/src/Umbraco.Web/Scheduling/KeepAlive.cs
@@ -59,7 +59,7 @@ namespace Umbraco.Web.Scheduling
return true; // repeat
}
- var url = umbracoAppUrl + "/ping.aspx";
+ var url = umbracoAppUrl.TrimEnd('/') + "/api/keepalive/ping";
var request = new HttpRequestMessage(HttpMethod.Get, url);
var result = await _httpClient.SendAsync(request, token);
diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs
index 2faacf828d..b91b133ace 100644
--- a/src/Umbraco.Web/Search/ExamineComponent.cs
+++ b/src/Umbraco.Web/Search/ExamineComponent.cs
@@ -24,6 +24,8 @@ using System.Threading.Tasks;
using Examine.LuceneEngine.Directories;
using Umbraco.Core.Composing;
using Umbraco.Core.Strings;
+using Umbraco.Web.Models.ContentEditing;
+using Umbraco.Web.Trees;
namespace Umbraco.Web.Search
{
@@ -51,6 +53,7 @@ namespace Umbraco.Web.Search
// but greater that SafeXmlReaderWriter priority which is 60
private const int EnlistPriority = 80;
+
public override void Compose(Composition composition)
{
base.Compose(composition);
diff --git a/src/Umbraco.Web/Search/SearchableTreeCollection.cs b/src/Umbraco.Web/Search/SearchableTreeCollection.cs
index 86f4494353..38c329cafa 100644
--- a/src/Umbraco.Web/Search/SearchableTreeCollection.cs
+++ b/src/Umbraco.Web/Search/SearchableTreeCollection.cs
@@ -1,6 +1,8 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
+using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Services;
using Umbraco.Web.Trees;
@@ -19,15 +21,17 @@ namespace Umbraco.Web.Search
private Dictionary CreateDictionary(IApplicationTreeService treeService)
{
- var appTrees = treeService.GetAll().ToArray();
- var dictionary = new Dictionary();
+ var appTrees = treeService.GetAll()
+ .OrderBy(x => x.SortOrder)
+ .ToArray();
+ var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase);
var searchableTrees = this.ToArray();
- foreach (var searchableTree in searchableTrees)
+ foreach (var appTree in appTrees)
{
- var found = appTrees.FirstOrDefault(x => x.Alias == searchableTree.TreeAlias);
+ var found = searchableTrees.FirstOrDefault(x => x.TreeAlias.InvariantEquals(appTree.Alias));
if (found != null)
{
- dictionary[searchableTree.TreeAlias] = new SearchableApplicationTree(found.ApplicationAlias, found.Alias, searchableTree);
+ dictionary[found.TreeAlias] = new SearchableApplicationTree(appTree.ApplicationAlias, appTree.Alias, found);
}
}
return dictionary;
diff --git a/src/Umbraco.Web/Search/SearchableTreeCollectionBuilder.cs b/src/Umbraco.Web/Search/SearchableTreeCollectionBuilder.cs
index 51f4b13f0a..2d668257ef 100644
--- a/src/Umbraco.Web/Search/SearchableTreeCollectionBuilder.cs
+++ b/src/Umbraco.Web/Search/SearchableTreeCollectionBuilder.cs
@@ -6,5 +6,8 @@ namespace Umbraco.Web.Search
internal class SearchableTreeCollectionBuilder : LazyCollectionBuilderBase
{
protected override SearchableTreeCollectionBuilder This => this;
+
+ //per request because generally an instance of ISearchableTree is a controller
+ protected override Lifetime CollectionLifetime => Lifetime.Request;
}
}
diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
index 9aab30edae..c3ab7318a0 100644
--- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
+++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
@@ -10,6 +10,7 @@ using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
+using Umbraco.Examine;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Trees;
using SearchResult = Examine.SearchResult;
@@ -23,11 +24,18 @@ namespace Umbraco.Web.Search
{
private readonly IExamineManager _examineManager;
private readonly UmbracoHelper _umbracoHelper;
+ private readonly ILocalizationService _languageService;
+ private readonly IEntityService _entityService;
- public UmbracoTreeSearcher(IExamineManager examineManager, UmbracoHelper umbracoHelper)
+ public UmbracoTreeSearcher(IExamineManager examineManager,
+ UmbracoHelper umbracoHelper,
+ ILocalizationService languageService,
+ IEntityService entityService)
{
_examineManager = examineManager ?? throw new ArgumentNullException(nameof(examineManager));
_umbracoHelper = umbracoHelper ?? throw new ArgumentNullException(nameof(umbracoHelper));
+ _languageService = languageService;
+ _entityService = entityService;
}
///
@@ -51,16 +59,22 @@ namespace Umbraco.Web.Search
var sb = new StringBuilder();
string type;
- var indexName = Constants.Examine.InternalIndexer;
+ var indexName = Constants.UmbracoIndexes.InternalIndexName;
var fields = new[] { "id", "__NodeId" };
var umbracoContext = _umbracoHelper.UmbracoContext;
- //TODO: WE should really just allow passing in a lucene raw query
+ //TODO: WE should try to allow passing in a lucene raw query, however we will still need to do some manual string
+ // manipulation for things like start paths, member types, etc...
+ //if (Examine.ExamineExtensions.TryParseLuceneQuery(query))
+ //{
+
+ //}
+
switch (entityType)
{
case UmbracoEntityTypes.Member:
- indexName = Constants.Examine.InternalMemberIndexer;
+ indexName = Constants.UmbracoIndexes.MembersIndexName;
type = "member";
fields = new[] { "id", "__NodeId", "email", "loginName" };
if (searchFrom != null && searchFrom != Constants.Conventions.MemberTypes.AllMembersListId && searchFrom.Trim() != "-1")
@@ -72,13 +86,13 @@ namespace Umbraco.Web.Search
break;
case UmbracoEntityTypes.Media:
type = "media";
- var allMediaStartNodes = umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(Current.Services.EntityService);
- AppendPath(sb, UmbracoObjectTypes.Media, allMediaStartNodes, searchFrom, Current.Services.EntityService);
+ var allMediaStartNodes = umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService);
+ AppendPath(sb, UmbracoObjectTypes.Media, allMediaStartNodes, searchFrom, _entityService);
break;
case UmbracoEntityTypes.Document:
type = "content";
- var allContentStartNodes = umbracoContext.Security.CurrentUser.CalculateContentStartNodeIds(Current.Services.EntityService);
- AppendPath(sb, UmbracoObjectTypes.Document, allContentStartNodes, searchFrom, Current.Services.EntityService);
+ var allContentStartNodes = umbracoContext.Security.CurrentUser.CalculateContentStartNodeIds(_entityService);
+ AppendPath(sb, UmbracoObjectTypes.Document, allContentStartNodes, searchFrom, _entityService);
break;
default:
throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType);
@@ -89,11 +103,43 @@ namespace Umbraco.Web.Search
var internalSearcher = index.GetSearcher();
+ if (!BuildQuery(sb, query, searchFrom, fields, type))
+ {
+ totalFound = 0;
+ return Enumerable.Empty();
+ }
+
+ var raw = internalSearcher.CreateCriteria().RawQuery(sb.ToString());
+
+ var result = internalSearcher
+ //only return the number of items specified to read up to the amount of records to fill from 0 -> the number of items on the page requested
+ .Search(raw, Convert.ToInt32(pageSize * (pageIndex + 1)));
+
+ totalFound = result.TotalItemCount;
+
+ var pagedResult = result.Skip(Convert.ToInt32(pageIndex));
+
+ switch (entityType)
+ {
+ case UmbracoEntityTypes.Member:
+ return MemberFromSearchResults(pagedResult.ToArray());
+ case UmbracoEntityTypes.Media:
+ return MediaFromSearchResults(pagedResult);
+ case UmbracoEntityTypes.Document:
+ return ContentFromSearchResults(pagedResult);
+ default:
+ throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType);
+ }
+ }
+
+ private bool BuildQuery(StringBuilder sb, string query, string searchFrom, string[] fields, string type)
+ {
//build a lucene query:
- // the __nodeName will be boosted 10x without wildcards
- // then __nodeName will be matched normally with wildcards
+ // the nodeName will be boosted 10x without wildcards
+ // then nodeName will be matched normally with wildcards
// the rest will be normal without wildcards
+ var allLangs = _languageService.GetAllLanguages().Select(x => x.IsoCode.ToLowerInvariant()).ToList();
//check if text is surrounded by single or double quotes, if so, then exact match
var surroundedByQuotes = Regex.IsMatch(query, "^\".*?\"$")
@@ -102,15 +148,14 @@ namespace Umbraco.Web.Search
if (surroundedByQuotes)
{
//strip quotes, escape string, the replace again
- query = query.Trim(new[] { '\"', '\'' });
+ query = query.Trim('\"', '\'');
query = Lucene.Net.QueryParsers.QueryParser.Escape(query);
//nothing to search
if (searchFrom.IsNullOrWhiteSpace() && query.IsNullOrWhiteSpace())
{
- totalFound = 0;
- return new List();
+ return false;
}
//update the query with the query term
@@ -119,10 +164,9 @@ namespace Umbraco.Web.Search
//add back the surrounding quotes
query = string.Format("{0}{1}{0}", "\"", query);
- //node name exactly boost x 10
- sb.Append("+(__nodeName: (");
- sb.Append(query.ToLower());
- sb.Append(")^10.0 ");
+ sb.Append("+(");
+
+ AppendNodeNamePhraseWithBoost(sb, query, allLangs);
foreach (var f in fields)
{
@@ -143,8 +187,7 @@ namespace Umbraco.Web.Search
//nothing to search
if (searchFrom.IsNullOrWhiteSpace() && trimmed.IsNullOrWhiteSpace())
{
- totalFound = 0;
- return new List();
+ return false;
}
//update the query with the query term
@@ -154,24 +197,12 @@ namespace Umbraco.Web.Search
var querywords = query.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
- //node name exactly boost x 10
- sb.Append("+(__nodeName:");
- sb.Append("\"");
- sb.Append(query.ToLower());
- sb.Append("\"");
- sb.Append("^10.0 ");
-
- //node name normally with wildcards
- sb.Append(" __nodeName:");
- sb.Append("(");
- foreach (var w in querywords)
- {
- sb.Append(w.ToLower());
- sb.Append("* ");
- }
- sb.Append(") ");
+ sb.Append("+(");
+ AppendNodeNameExactWithBoost(sb, query, allLangs);
+ AppendNodeNameWithWildcards(sb, querywords, allLangs);
+
foreach (var f in fields)
{
//additional fields normally
@@ -195,26 +226,69 @@ namespace Umbraco.Web.Search
sb.Append("+__IndexType:");
sb.Append(type);
- var raw = internalSearcher.CreateCriteria().RawQuery(sb.ToString());
+ return true;
+ }
- var result = internalSearcher
- //only return the number of items specified to read up to the amount of records to fill from 0 -> the number of items on the page requested
- .Search(raw, Convert.ToInt32(pageSize * (pageIndex + 1)));
+ private void AppendNodeNamePhraseWithBoost(StringBuilder sb, string query, IEnumerable allLangs)
+ {
+ //node name exactly boost x 10
+ sb.Append("nodeName: (");
+ sb.Append(query.ToLower());
+ sb.Append(")^10.0 ");
- totalFound = result.TotalItemCount;
-
- var pagedResult = result.Skip(Convert.ToInt32(pageIndex));
-
- switch (entityType)
+ //also search on all variant node names
+ foreach (var lang in allLangs)
{
- case UmbracoEntityTypes.Member:
- return MemberFromSearchResults(pagedResult.ToArray());
- case UmbracoEntityTypes.Media:
- return MediaFromSearchResults(pagedResult);
- case UmbracoEntityTypes.Document:
- return ContentFromSearchResults(pagedResult);
- default:
- throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType);
+ //node name exactly boost x 10
+ sb.Append($"nodeName_{lang}: (");
+ sb.Append(query.ToLower());
+ sb.Append(")^10.0 ");
+ }
+ }
+
+ private void AppendNodeNameExactWithBoost(StringBuilder sb, string query, IEnumerable allLangs)
+ {
+ //node name exactly boost x 10
+ sb.Append("nodeName:");
+ sb.Append("\"");
+ sb.Append(query.ToLower());
+ sb.Append("\"");
+ sb.Append("^10.0 ");
+ //also search on all variant node names
+ foreach (var lang in allLangs)
+ {
+ //node name exactly boost x 10
+ sb.Append($"nodeName_{lang}:");
+ sb.Append("\"");
+ sb.Append(query.ToLower());
+ sb.Append("\"");
+ sb.Append("^10.0 ");
+ }
+ }
+
+ private void AppendNodeNameWithWildcards(StringBuilder sb, string[] querywords, IEnumerable allLangs)
+ {
+ //node name normally with wildcards
+ sb.Append("nodeName:");
+ sb.Append("(");
+ foreach (var w in querywords)
+ {
+ sb.Append(w.ToLower());
+ sb.Append("* ");
+ }
+ sb.Append(") ");
+ //also search on all variant node names
+ foreach (var lang in allLangs)
+ {
+ //node name normally with wildcards
+ sb.Append($"nodeName_{lang}:");
+ sb.Append("(");
+ foreach (var w in querywords)
+ {
+ sb.Append(w.ToLower());
+ sb.Append("* ");
+ }
+ sb.Append(") ");
}
}
@@ -278,32 +352,33 @@ namespace Umbraco.Web.Search
///
///
///
- private IEnumerable MemberFromSearchResults(ISearchResult[] results)
+ private IEnumerable MemberFromSearchResults(IEnumerable results)
{
- var mapped = Mapper.Map>(results).ToArray();
//add additional data
- foreach (var m in mapped)
+ foreach (var result in results)
{
+ var m = Mapper.Map(result);
+
//if no icon could be mapped, it will be set to document, so change it to picture
if (m.Icon == "icon-document")
{
m.Icon = "icon-user";
}
-
- var searchResult = results.First(x => x.Id == m.Id.ToString());
- if (searchResult.Values.ContainsKey("email") && searchResult.Values["email"] != null)
+
+ if (result.Values.ContainsKey("email") && result.Values["email"] != null)
{
- m.AdditionalData["Email"] = results.First(x => x.Id == m.Id.ToString()).Values["email"];
+ m.AdditionalData["Email"] = result.Values["email"];
}
- if (searchResult.Values.ContainsKey("__key") && searchResult.Values["__key"] != null)
+ if (result.Values.ContainsKey("__key") && result.Values["__key"] != null)
{
- if (Guid.TryParse(searchResult.Values["__key"], out var key))
+ if (Guid.TryParse(result.Values["__key"], out var key))
{
m.Key = key;
}
}
+
+ yield return m;
}
- return mapped;
}
///
@@ -313,17 +388,17 @@ namespace Umbraco.Web.Search
///
private IEnumerable MediaFromSearchResults(IEnumerable results)
{
- var mapped = Mapper.Map>(results).ToArray();
//add additional data
- foreach (var m in mapped)
+ foreach (var result in results)
{
+ var m = Mapper.Map(result);
//if no icon could be mapped, it will be set to document, so change it to picture
if (m.Icon == "icon-document")
{
m.Icon = "icon-picture";
}
+ yield return m;
}
- return mapped;
}
///
@@ -333,17 +408,28 @@ namespace Umbraco.Web.Search
///
private IEnumerable ContentFromSearchResults(IEnumerable results)
{
- var mapped = Mapper.Map>(results).ToArray();
- //add additional data
- foreach (var m in mapped)
+ var defaultLang = _languageService.GetDefaultLanguageIsoCode();
+
+ foreach (var result in results)
{
- var intId = m.Id.TryConvertTo();
+ var entity = Mapper.Map(result);
+
+ var intId = entity.Id.TryConvertTo();
if (intId.Success)
{
- m.AdditionalData["Url"] = _umbracoHelper.Url(intId.Result);
+ //if it varies by culture, return the default language URL
+ if (result.Values.TryGetValue(UmbracoContentIndex.VariesByCultureFieldName, out var varies) && varies == "1")
+ {
+ entity.AdditionalData["Url"] = _umbracoHelper.Url(intId.Result, defaultLang);
+ }
+ else
+ {
+ entity.AdditionalData["Url"] = _umbracoHelper.Url(intId.Result);
+ }
}
+
+ yield return entity;
}
- return mapped;
}
}
diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs
index d2b94c815b..a09c6fc039 100644
--- a/src/Umbraco.Web/Trees/ContentTreeController.cs
+++ b/src/Umbraco.Web/Trees/ContentTreeController.cs
@@ -36,10 +36,12 @@ namespace Umbraco.Web.Trees
public class ContentTreeController : ContentTreeControllerBase, ISearchableTree
{
private readonly UmbracoTreeSearcher _treeSearcher;
+ private readonly ActionCollection _actions;
- public ContentTreeController(UmbracoTreeSearcher treeSearcher)
+ public ContentTreeController(UmbracoTreeSearcher treeSearcher, ActionCollection actions)
{
_treeSearcher = treeSearcher;
+ _actions = actions;
}
protected override int RecycleBinId => Constants.System.RecycleBinContent;
@@ -61,14 +63,12 @@ namespace Umbraco.Web.Trees
//Special check to see if it ia a container, if so then we'll hide children.
var isContainer = entity.IsContainer; // && (queryStrings.Get("isDialog") != "true");
- var hasChildren = ShouldRenderChildrenOfContainer(entity);
-
var node = CreateTreeNode(
entity,
Constants.ObjectTypes.Document,
parentId,
queryStrings,
- hasChildren);
+ entity.HasChildren);
// set container style if it is one
if (isContainer)
@@ -127,7 +127,7 @@ namespace Umbraco.Web.Trees
// we need to get the default permissions as you can't set permissions on the very root node
var permission = Services.UserService.GetPermissions(Security.CurrentUser, Constants.System.Root).First();
- var nodeActions = Current.Actions.FromEntityPermission(permission)
+ var nodeActions = _actions.FromEntityPermission(permission)
.Select(x => new MenuItem(x));
//these two are the standard items
@@ -313,8 +313,7 @@ namespace Umbraco.Web.Trees
private void AddActionNode(IUmbracoEntity item, MenuItemCollection menu, bool hasSeparator = false, bool convert = false, bool opensDialog = false)
where TAction : IAction
{
- //fixme: Inject
- var menuItem = menu.Items.Add(Services.TextService.Localize("actions", Current.Actions.GetAction().Alias), hasSeparator);
+ var menuItem = menu.Items.Add(Services.TextService.Localize("actions", _actions.GetAction().Alias), hasSeparator);
if (convert) menuItem.ConvertLegacyMenuItem(item, "content", "content");
menuItem.OpensDialog = opensDialog;
}
diff --git a/src/Umbraco.Web/Trees/ISearchableTree.cs b/src/Umbraco.Web/Trees/ISearchableTree.cs
index 4146bfaf45..3d82d548c8 100644
--- a/src/Umbraco.Web/Trees/ISearchableTree.cs
+++ b/src/Umbraco.Web/Trees/ISearchableTree.cs
@@ -1,9 +1,10 @@
using System.Collections.Generic;
+using Umbraco.Core.Composing;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Trees
{
- public interface ISearchableTree
+ public interface ISearchableTree : IDiscoverable
{
///
/// The alias of the tree that the belongs to
diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs
index 8533081dde..47cd1cb693 100644
--- a/src/Umbraco.Web/Trees/MediaTreeController.cs
+++ b/src/Umbraco.Web/Trees/MediaTreeController.cs
@@ -59,18 +59,15 @@ namespace Umbraco.Web.Trees
///
protected override TreeNode GetSingleTreeNode(IEntitySlim entity, string parentId, FormDataCollection queryStrings)
{
- //Special check to see if it ia a container, if so then we'll hide children.
- var isContainer = entity.IsContainer; // && (queryStrings.Get("isDialog") != "true");
-
var node = CreateTreeNode(
entity,
Constants.ObjectTypes.Media,
parentId,
queryStrings,
- entity.HasChildren && !isContainer);
+ entity.HasChildren);
// entity is either a container, or a media
- if (isContainer)
+ if (entity.IsContainer)
{
node.SetContainerStyle();
node.AdditionalData.Add("isContainer", true);
diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs
index ae2ad1824d..39568e2efa 100644
--- a/src/Umbraco.Web/Trees/MemberTreeController.cs
+++ b/src/Umbraco.Web/Trees/MemberTreeController.cs
@@ -127,14 +127,14 @@ namespace Umbraco.Web.Trees
if (id == Constants.System.Root.ToInvariantString())
{
nodes.Add(
- CreateTreeNode(Constants.Conventions.MemberTypes.AllMembersListId, id, queryStrings, Services.TextService.Localize("member/allMembers"), "icon-users", false,
+ CreateTreeNode(Constants.Conventions.MemberTypes.AllMembersListId, id, queryStrings, Services.TextService.Localize("member/allMembers"), "icon-users", true,
queryStrings.GetValue("application") + TreeAlias.EnsureStartsWith('/') + "/list/" + Constants.Conventions.MemberTypes.AllMembersListId));
if (_isUmbracoProvider)
{
nodes.AddRange(Services.MemberTypeService.GetAll()
.Select(memberType =>
- CreateTreeNode(memberType.Alias, id, queryStrings, memberType.Name, "icon-users", false,
+ CreateTreeNode(memberType.Alias, id, queryStrings, memberType.Name, "icon-users", true,
queryStrings.GetValue("application") + TreeAlias.EnsureStartsWith('/') + "/list/" + memberType.Alias)));
}
}
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index ac6e40623d..d00ebaaeb6 100755
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -36,6 +36,7 @@
false
latest
+
@@ -115,6 +116,7 @@
+
@@ -529,9 +531,6 @@
-
- ASPXCodeBehind
-
@@ -690,8 +689,8 @@
-
-
+
+
@@ -1171,9 +1170,6 @@
ASPXCodeBehind
-
- ASPXCodeBehind
-
ASPXCodeBehind
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Default.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Default.aspx.cs
deleted file mode 100644
index f1a05c1185..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/Default.aspx.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System;
-using System.Collections;
-using System.ComponentModel;
-using System.Data;
-using System.Drawing;
-using System.Web;
-using System.Web.Mvc;
-using System.Web.SessionState;
-using System.Web.UI;
-using System.Web.UI.WebControls;
-using System.Web.UI.HtmlControls;
-
-namespace umbraco
-{
- ///
- /// Summary description for _Default.
- ///
- public partial class _Default : System.Web.UI.Page
- {
- protected void Page_Load(object sender, System.EventArgs e)
- {
- //var mvcHandler = new MvcHandler()
- //Server.TransferRequest();
- //Server.Transfer("~/Umbraco/Default");
- //Server.Transfer("umbraco.aspx");
- // Put user code to initialize the page here
- }
-
- ///
- /// Form1 control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::System.Web.UI.HtmlControls.HtmlForm Form1;
-
- }
-}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/endPreview.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/endPreview.aspx.cs
deleted file mode 100644
index 822f346705..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/endPreview.aspx.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.Web;
-using System.Web.UI;
-using Umbraco.Core;
-using Umbraco.Web;
-using Umbraco.Web.Composing;
-using Umbraco.Web.PublishedCache;
-
-namespace umbraco.presentation
-{
- public class endPreview : Page
- {
- protected void Page_Load(object sender, EventArgs e)
- {
- var request = (new HttpRequestWrapper(Request));
-
- var previewToken = request.GetPreviewCookieValue();
- var service = Current.PublishedSnapshotService;
- service.ExitPreview(previewToken);
-
- HttpContext.Current.ExpireCookie(Constants.Web.PreviewCookieName);
-
- var redir = Request.QueryString["redir"];
- Uri url = null;
-
- if (Uri.IsWellFormedUriString(redir, UriKind.Relative) == false
- || redir.StartsWith("//")
- || Uri.TryCreate(redir, UriKind.Relative, out url) == false)
- {
- Response.Redirect("/", true);
- }
-
- Response.Redirect(url.ToString(), true);
- }
- }
-}
diff --git a/src/umbraco.sln b/src/umbraco.sln
index 41bd8359c3..0bdcb53d99 100644
--- a/src/umbraco.sln
+++ b/src/umbraco.sln
@@ -14,13 +14,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{2849E9D4
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{FD962632-184C-4005-A5F3-E705D92FC645}"
ProjectSection(SolutionItems) = preProject
- ..\docs\BUILD.md = ..\docs\BUILD.md
- ..\docs\CODE_OF_CONDUCT.md = ..\docs\CODE_OF_CONDUCT.md
- ..\docs\CONTRIBUTING.md = ..\docs\CONTRIBUTING.md
- ..\LICENSE.md = ..\LICENSE.md
- ..\docs\PULL_REQUEST_TEMPLATE.md = ..\docs\PULL_REQUEST_TEMPLATE.md
- ..\docs\README.md = ..\docs\README.md
- ..\docs\V8_GETTING_STARTED.md = ..\docs\V8_GETTING_STARTED.md
+ ..\.github\BUILD.md = ..\.github\BUILD.md
+ ..\.github\CLEAR.md = ..\.github\CLEAR.md
+ ..\.github\CODE_OF_CONDUCT.md = ..\.github\CODE_OF_CONDUCT.md
+ ..\.github\CONTRIBUTING.md = ..\.github\CONTRIBUTING.md
+ ..\.github\CONTRIBUTING_DETAILED.md = ..\.github\CONTRIBUTING_DETAILED.md
+ ..\.github\CONTRIBUTION_GUIDELINES.md = ..\.github\CONTRIBUTION_GUIDELINES.md
+ ..\.github\PULL_REQUEST_TEMPLATE.md = ..\.github\PULL_REQUEST_TEMPLATE.md
+ ..\.github\README.md = ..\.github\README.md
+ ..\.github\REVIEW_PROCESS.md = ..\.github\REVIEW_PROCESS.md
+ ..\.github\V8_GETTING_STARTED.md = ..\.github\V8_GETTING_STARTED.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B5BD12C1-A454-435E-8A46-FF4A364C0382}"
@@ -92,6 +95,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DocTools", "DocTools", "{53
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Benchmarks", "Umbraco.Tests.Benchmarks\Umbraco.Tests.Benchmarks.csproj", "{3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IssueTemplates", "IssueTemplates", "{C7311C00-2184-409B-B506-52A5FAEA8736}"
+ ProjectSection(SolutionItems) = preProject
+ ..\.github\ISSUE_TEMPLATE\1_Bug.md = ..\.github\ISSUE_TEMPLATE\1_Bug.md
+ ..\.github\ISSUE_TEMPLATE\2_Feature_request.md = ..\.github\ISSUE_TEMPLATE\2_Feature_request.md
+ ..\.github\ISSUE_TEMPLATE\3_Support_question.md = ..\.github\ISSUE_TEMPLATE\3_Support_question.md
+ ..\.github\ISSUE_TEMPLATE\4_Documentation_issue.md = ..\.github\ISSUE_TEMPLATE\4_Documentation_issue.md
+ ..\.github\ISSUE_TEMPLATE\5_Security_issue.md = ..\.github\ISSUE_TEMPLATE\5_Security_issue.md
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -135,6 +147,7 @@ Global
{5B03EF4E-E0AC-4905-861B-8C3EC1A0D458} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D}
{53594E5B-64A2-4545-8367-E3627D266AE8} = {FD962632-184C-4005-A5F3-E705D92FC645}
{3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
+ {C7311C00-2184-409B-B506-52A5FAEA8736} = {FD962632-184C-4005-A5F3-E705D92FC645}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7A0F2E34-D2AF-4DAB-86A0-7D7764B3D0EC}