diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec
index 270d6a08f8..fc8f52d1fa 100644
--- a/build/NuSpecs/UmbracoCms.nuspec
+++ b/build/NuSpecs/UmbracoCms.nuspec
@@ -17,7 +17,7 @@
-
+
diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs
index a760ebb717..c7e8a5c328 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs
@@ -183,17 +183,17 @@ namespace Umbraco.Core.Persistence.Repositories
}
public IEnumerable GetMemberGroupsForMember(string username)
- {
- var sql = new Sql()
+ {
+ var sql = new Sql()
.Select("un.*")
.From("umbracoNode AS un")
.InnerJoin("cmsMember2MemberGroup")
- .On("un.id = cmsMember2MemberGroup.MemberGroup")
- .LeftJoin("(SELECT umbracoNode.id, cmsMember.LoginName FROM umbracoNode INNER JOIN cmsMember ON umbracoNode.id = cmsMember.nodeId) AS member")
- .On("member.id = cmsMember2MemberGroup.Member")
- .Where("un.nodeObjectType=@objectType", new {objectType = NodeObjectTypeId })
- .Where("member.LoginName=@loginName", new {loginName = username});
-
+ .On("cmsMember2MemberGroup.MemberGroup = un.id")
+ .InnerJoin("cmsMember")
+ .On("cmsMember.nodeId = cmsMember2MemberGroup.Member")
+ .Where("un.nodeObjectType=@objectType", new { objectType = NodeObjectTypeId })
+ .Where("cmsMember.LoginName=@loginName", new { loginName = username });
+
return Database.Fetch(sql)
.DistinctBy(dto => dto.NodeId)
.Select(x => _modelFactory.BuildEntity(x));
diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs
index ae2f7a3dc6..9465cf2ad0 100644
--- a/src/Umbraco.Core/Services/ContentService.cs
+++ b/src/Umbraco.Core/Services/ContentService.cs
@@ -993,8 +993,7 @@ namespace Umbraco.Core.Services
FROM umbracoNode
JOIN cmsDocument ON umbracoNode.id=cmsDocument.nodeId AND cmsDocument.published=@0
WHERE umbracoNode.trashed=@1 AND umbracoNode.id IN (@2)",
- true, false, ids);
- Console.WriteLine(sql.SQL);
+ true, false, ids);
var x = uow.Database.Fetch(sql);
return ids.Length == x.Count;
}
@@ -2007,7 +2006,7 @@ namespace Umbraco.Core.Services
}
return true;
- }
+ }
///
/// Sorts a collection of objects by updating the SortOrder according
@@ -2029,7 +2028,7 @@ namespace Umbraco.Core.Services
using (new WriteLock(Locker))
{
var allContent = GetByIds(ids).ToDictionary(x => x.Id, x => x);
- var items = ids.Select(x => allContent[x]);
+ var items = ids.Select(x => allContent[x]);
using (var uow = UowProvider.GetUnitOfWork())
{
@@ -2838,4 +2837,4 @@ namespace Umbraco.Core.Services
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs
index 1bf847648c..c33a5fa21f 100644
--- a/src/Umbraco.Core/Services/UserService.cs
+++ b/src/Umbraco.Core/Services/UserService.cs
@@ -228,9 +228,9 @@ namespace Umbraco.Core.Services
}
///
- /// Deletes an
+ /// Disables an
///
- /// to Delete
+ /// to disable
public void Delete(IUser membershipUser)
{
//disable
@@ -753,7 +753,7 @@ namespace Umbraco.Core.Services
/// Id of the User to retrieve
///
public IProfile GetProfileById(int id)
- {
+ {
//This is called a TON. Go get the full user from cache which should already be IProfile
var fullUser = GetUserById(id);
var asProfile = fullUser as IProfile;
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js
index 6b43eddae3..09d218f85e 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js
@@ -20,7 +20,7 @@
tours = [];
return tourResource.getTours().then(function(tourFiles) {
angular.forEach(tourFiles, function (tourFile) {
- angular.forEach(tourFile, function(newTour) {
+ angular.forEach(tourFile.tours, function(newTour) {
validateTour(newTour);
validateTourRegistration(newTour);
tours.push(newTour);
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js
index 5401ae5387..91c74311b3 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js
@@ -22,6 +22,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
selectedSearchResults: []
};
+ $scope.showTarget = $scope.model.hideTarget !== true;
+
if (dialogOptions.currentTarget) {
$scope.model.target = dialogOptions.currentTarget;
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html
index 13bcfd9c3e..e1b13206df 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html
+++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html
@@ -18,7 +18,7 @@
ng-model="model.target.name" />
-
+
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index 49bc478461..dc9939c422 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -333,8 +333,8 @@
umbraco.providers
-
- ..\packages\Umbraco.ModelsBuilder.3.0.7\lib\Umbraco.ModelsBuilder.dll
+
+ ..\packages\Umbraco.ModelsBuilder.3.0.8\lib\Umbraco.ModelsBuilder.dll
diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config
index eb6e198935..8653059b89 100644
--- a/src/Umbraco.Web.UI/packages.config
+++ b/src/Umbraco.Web.UI/packages.config
@@ -36,5 +36,5 @@
-
+
\ 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 1c1f03f140..29068057b1 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
@@ -1485,7 +1485,7 @@ Mange hilsner fra Umbraco robotten
Sæt rettigheder for specifikke noderProfilSøg alle 'børn'
- Start node
+ StartnodeAktivAlleDeaktiveret
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml
index a3a562d152..8561b419f0 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml
@@ -32,11 +32,6 @@
מייןתרגםעדכן
- מחק
- בצע
- תזמן
- ייבא נתונים
- צור הגדרות ילדיםיומן משימות מתוזמנות
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml
index f291aab7ea..49160fc662 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml
@@ -12,9 +12,11 @@
KopiujUtwórzStwórz zbiór
+ Stwórz grupęUsuńDeaktywujOpróżnij kosz
+ AktywujEksportuj typ dokumentuImportuj typ dokumentuImportuj zbiór
@@ -27,16 +29,49 @@
Cofnij publikacjęOdśwież węzełOpublikuj ponownie całą stronę
+ Zmień nazwęPrzywróćUstaw uprawnienia dla strony %0%
+ Wybierz dokąd przenieść
+ W strukturze drzewa poniżejUprawnieniaCofnijWyślij do publikacjiWyślij do tłumaczenia
+ Ustaw grupęSortujPrzetłumaczAktualizuj
+ Ustaw uprawnienia
+ Odblokuj
+ Stwórz Szablon Zawartości
+
+ Zawartość
+ Administracja
+ Struktura
+ Inne
+
+
+ Zezwól na dostęp do przydzielenia języka i hostów
+ Zezwól na dostęp do wglądu w historię logów węzła
+ Zezwól na dostęp do widoku węzła
+ Zezwól na dostęp do zmiany typu dokumentu dla węzła
+ Zezwól na dostęp do skopiowania węzła
+ Zezwól na dostęp do stworzenia węzłów
+ Zezwól na dostęp do usunięcia węzłóws
+ Zezwól na dostęp do przeniesienia węzła
+ Zezwól na dostęp do ustawienia i zmiany publicznego dostępu węzła
+ Zezwól na dostęp do publikacji węzła
+ Zezwól na dostęp do zmiany uprawnień węzła
+ Zezwól na dostęp do cofnięcia węzła do poprzedniego stanu
+ Zezwól na dostęp do wysłania węzła do akceptacji przed publikacją
+ Zezwól na dostęp do wysłania węzła do tłumaczenia
+ Zezwól na dostęp do zmiany kolejności sortowania węzłów
+ Zezwól na dostęp do tłumaczenia węzła
+ Zezwól na dostęp do zapisania węzła
+ Zezwól na dostęp do utworzenia Szablonu Zawartości
+
Brak odpowiednich uprawnieńDodaj nową domenę
@@ -50,14 +85,18 @@
Domena '%0%' została skasowanaDomena '%0%' jest aktualnie przypisanaDomena '%0%' została zaktualizowana
-
- Edycja aktualnych domen
+ Edytuj Aktualne Domeny
+
+
+ OdziedziczonaJęzyk
- lub wybierz dziedziczenie języka z węzła rodzica. Zostanie to zastosowane
-także do obecnego węzła, o ile poniższa domena również do niego należy.]]>
+
+ lub wybierz dziedziczenie języka z węzła rodzica. Zostanie to zastosowane
+ także do obecnego węzła, o ile poniższa domena również do niego należy.]]>
+ Domeny
@@ -96,6 +135,7 @@ także do obecnego węzła, o ile poniższa domena również do niego należy.]]
Pokaż styleWstaw tabelęWygeneruj modele
+ Zapisz i wygeneruj modeleCofnijPowtórz
@@ -139,6 +179,8 @@ także do obecnego węzła, o ile poniższa domena również do niego należy.]]
OpublikowaneNie ma żadnych elementów do wyświetleniaNie ma żadnych elementów do wyświetlenia w liście.
+ Nie dodano żadnej zawartości
+ Nie dodano żadnych członkówTyp mediówLink do elementu(ów) mediówCzłonek grupy
@@ -173,16 +215,30 @@ także do obecnego węzła, o ile poniższa domena również do niego należy.]]
CelOznacza to następującą godzinę na serwerze:Co to oznacza?]]>
+ Czy na pewno chcesz usunąć ten element?
+ Właściwość %0% używa edytora %1%, który nie jest wspierany przez Nested Content.Dodaj kolejne pole tekstoweUsuń te pole tekstowe
+ Korzeń zawartości
+
+ Stwórz nowy Szablon Zawartości z '%0%'
+ Pusty
+ Wybierz Szablon Zawartości
+ Szablon Zawartości został stworzony
+ Szablon Zawartości został stworzony z '%0%'
+ Szablon Zawartości o tej samej nazwie już istnieje
+ Szablon Zawartości to predefiniowana zawartość, którą edytor może wybrać, aby użyć jej jako podstawę do stworzenia nowej zawartości
+
Kliknij, aby załadować plikPrzerzuć swoje pliki tutaj...
- Link do mediów
+ Link do mediówlub kliknij tutaj, aby wybrać plikiJedyne dozwolone typy plików to
+ Nie można załadować pliku, typ pliku nie jest akceptowanyMaksymalny rozmiar pliku to
+ Korzeń mediówStwórz nowego członka
@@ -191,6 +247,7 @@ także do obecnego węzła, o ile poniższa domena również do niego należy.]]
Gdzie chcesz stworzyć nowy %0%?Utwórz w
+ Wybierz typ dokumentu, dla którego chcesz stworzyć szablon zawartościWybierz rodzaj oraz tytuł"typy dokumentów".]]>"typy mediów".]]>
@@ -249,6 +306,8 @@ także do obecnego węzła, o ile poniższa domena również do niego należy.]]
Skopiowano %0% z %1% elementów
+ Tytuł linku
+ LinkNazwaZarządzaj nazwami hostówZamknij to okno
@@ -276,6 +335,9 @@ także do obecnego węzła, o ile poniższa domena również do niego należy.]]
To makro nie posiada żadnych właściwości, które można edytowaćWklejEdytuj Uprawnienia dla
+ Ustaw uprawnienia dla
+ Ustaw uprawnienia dla %0% dla grupy użytkownika %1%
+ Wybierz grupy użytkowników, dla których chcesz ustawić uprawnieniaZawartość kosza jest teraz usuwana. Proszę nie zamykać tego okna do momentu zakończenia procesu.Zawartość kosza została usuniętaUsunięcie elementów z kosza powoduje ich trwałe i nieodwracalne skasowanie
@@ -288,10 +350,14 @@ także do obecnego węzła, o ile poniższa domena również do niego należy.]]
Cache strony zostanie odświeżone. Cała zawartość opublikowana będzie aktualna, lecz nieopublikowana zawartość pozostanie niewidocznaLiczba kolumnLiczba wierszy
- Ustaw zastępczy ID Ustawiając ID na tym elemencie możesz później łączyć treść z podrzędnych szablonów,
- ustawiając dowiązanie do tego ID na elemencie <asp:treści />]]>
- Wybierz zastępczy ID z poniższej listy. Możesz wybierać tylko
- spośród ID na szablonie nadrzędnym tego formularza.]]>
+
+ Ustaw zastępczy ID Ustawiając ID na tym elemencie możesz później łączyć treść z podrzędnych szablonów,
+ ustawiając dowiązanie do tego ID na elemencie <asp:treści />]]>
+
+
+ Wybierz zastępczy ID z poniższej listy. Możesz wybierać tylko
+ spośród ID na szablonie nadrzędnym tego formularza.]]>
+ Kliknij na obrazie, aby zobaczyć go w pełnym rozmiarzeWybierz elementPodgląd elementów Cache
@@ -303,14 +369,19 @@ także do obecnego węzła, o ile poniższa domena również do niego należy.]]
Otwórz zlinkowany dokument w nowym oknie lub zakładceLink do mediówLink do plików
+ Wybierz węzeł początkowy zawartościWybierz mediaWybierz ikonęWybierz elementWybierz linkWybierz makroWybierz zawartość
+ Wybierz węzeł początkowy mediówWybierz członkaWybierz członka grupy
+ Wybierz węzeł
+ Wybierz sekcje
+ Wybierz użytkownikówNie znaleziono ikonTe makro nie ma żadnych właściwościBrak dostępnych makro do wstawienia
@@ -325,9 +396,11 @@ także do obecnego węzła, o ile poniższa domena również do niego należy.]]
Wybierz snippet
- %0%' poniżej.
-Możesz dodać dodatkowe języki w menu "Języki" po lewej stronie.]]>
+
+ %0%' poniżej.
+ Możesz dodać dodatkowe języki w menu "Języki" po lewej stronie.]]>
+ Nazwa językaEdytuj klucz elementu słownika.
@@ -342,12 +415,15 @@ Możesz dodać dodatkowe języki w menu "Języki" po lewej stronie.]]>Potwierdź hasłoNazwij %0%...Wpisz nazwę...
+ Wpisz adres e-mail...
+ Wpisz nazwę użytkownika...Etykieta...Wpisz opis...Wpisz, aby wyszukać...Wpisz, aby filtrować...Wpisz, aby dodać tagi (naciśnij enter po każdym tagu)...Wpisz adres e-mail
+ Wpisz wiadomość...Twoja nazwa użytkownika to przeważnie Twój adres e-mail
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs
index 6b3a73f5df..a61ca7e249 100644
--- a/src/Umbraco.Web/Editors/ContentController.cs
+++ b/src/Umbraco.Web/Editors/ContentController.cs
@@ -532,6 +532,7 @@ namespace Umbraco.Web.Editors
///
[FileUploadCleanupFilter]
[ContentPostValidate]
+ [OutgoingEditorModelEvent]
public ContentItemDisplay PostSave(
[ModelBinder(typeof(ContentItemBinder))]
ContentItemSave contentItem)
@@ -847,6 +848,7 @@ namespace Umbraco.Web.Editors
///
///
[EnsureUserPermissionForContent("id", 'U')]
+ [OutgoingEditorModelEvent]
public ContentItemDisplay PostUnPublish(int id)
{
var foundContent = GetObjectFromRequest(() => Services.ContentService.GetById(id));
diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs
index e8b0550741..72af1c4c70 100644
--- a/src/Umbraco.Web/Editors/MediaController.cs
+++ b/src/Umbraco.Web/Editors/MediaController.cs
@@ -461,6 +461,7 @@ namespace Umbraco.Web.Editors
///
[FileUploadCleanupFilter]
[MediaPostValidate]
+ [OutgoingEditorModelEvent]
public MediaItemDisplay PostSave(
[ModelBinder(typeof(MediaItemBinder))]
MediaItemSave contentItem)
diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs
index 5921b9c61c..5aad40aa70 100644
--- a/src/Umbraco.Web/Editors/MemberController.cs
+++ b/src/Umbraco.Web/Editors/MemberController.cs
@@ -256,6 +256,7 @@ namespace Umbraco.Web.Editors
///
///
[FileUploadCleanupFilter]
+ [OutgoingEditorModelEvent]
public MemberDisplay PostSave(
[ModelBinder(typeof(MemberBinder))]
MemberSave contentItem)
diff --git a/src/Umbraco.Web/Editors/TourController.cs b/src/Umbraco.Web/Editors/TourController.cs
index ed1ed4c9c8..fa1f2a1d2d 100644
--- a/src/Umbraco.Web/Editors/TourController.cs
+++ b/src/Umbraco.Web/Editors/TourController.cs
@@ -2,11 +2,11 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text.RegularExpressions;
using Newtonsoft.Json;
-using Umbraco.Core;
+using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
-using Umbraco.Core.Logging;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi.Filters;
@@ -18,29 +18,35 @@ namespace Umbraco.Web.Editors
[UmbracoApplicationAuthorize(Constants.Applications.Content)]
public class TourController : UmbracoAuthorizedJsonController
{
- public IEnumerable GetTours()
+ public IEnumerable GetTours()
{
- var tours = new List();
+ var result = new List();
if (UmbracoConfig.For.UmbracoSettings().BackOffice.Tours.EnableTours == false)
- return tours;
+ return result;
var toursPath = Path.Combine(IOHelper.MapPath(SystemDirectories.Config), "BackOfficeTours");
if (Directory.Exists(toursPath) == false)
- return tours;
+ return result;
var tourFiles = Directory.GetFiles(toursPath, "*.json")
.OrderBy(x => x, StringComparer.InvariantCultureIgnoreCase);
var disabledTours = TourFilterResolver.Current.DisabledTours;
+
+ var coreTourFiles = Directory.GetFiles(
+ Path.Combine(IOHelper.MapPath(SystemDirectories.Config), "BackOfficeTours"), "*.json");
- foreach (var tourFile in tourFiles)
+ foreach (var tourFile in coreTourFiles)
{
- try
+ try
+ {
+ var contents = File.ReadAllText(tourFile);
+
+ result.Add(new BackOfficeTourFile
{
- var contents = File.ReadAllText(tourFile);
- var tourArray = JsonConvert.DeserializeObject(contents);
- tours.Add(tourArray.Where(x =>
- disabledTours.Contains(x.Alias, StringComparer.InvariantCultureIgnoreCase) == false).ToArray());
+ FileName = Path.GetFileNameWithoutExtension(tourFile),
+ Tours = JsonConvert.DeserializeObject(contents)
+ });
}
catch (IOException e)
{
@@ -52,9 +58,33 @@ namespace Umbraco.Web.Editors
Logger.Error("Error while trying to parse content as tour data: " + tourFile, e);
throw new JsonReaderException("Error while trying to parse content as tour data: " + tourFile, e);
}
+
}
- return tours;
+ //collect all tour files in packges
+ foreach (var plugin in Directory.EnumerateDirectories(IOHelper.MapPath(SystemDirectories.AppPlugins)))
+ {
+ var pluginName = Path.GetFileName(plugin.TrimEnd('\\'));
+
+ foreach (var backofficeDir in Directory.EnumerateDirectories(plugin, "backoffice"))
+ {
+ foreach (var tourDir in Directory.EnumerateDirectories(backofficeDir, "tours"))
+ {
+ foreach (var tourFile in Directory.EnumerateFiles(tourDir, "*.json"))
+ {
+ var contents = File.ReadAllText(tourFile);
+ result.Add(new BackOfficeTourFile
+ {
+ FileName = Path.GetFileNameWithoutExtension(tourFile),
+ PluginName = pluginName,
+ Tours = JsonConvert.DeserializeObject(contents)
+ });
+ }
+ }
+ }
+ }
+
+ return result.OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase);
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/ExamineStartup.cs b/src/Umbraco.Web/ExamineStartup.cs
index 0a7cab9522..eac3e20d87 100644
--- a/src/Umbraco.Web/ExamineStartup.cs
+++ b/src/Umbraco.Web/ExamineStartup.cs
@@ -22,6 +22,7 @@ namespace Umbraco.Web
private readonly List _indexesToRebuild = new List();
private readonly ApplicationContext _appCtx;
private readonly ProfilingLogger _profilingLogger;
+ private static bool _isConfigured = false;
//this is used if we are not the MainDom, in which case we need to ensure that if indexes need rebuilding that this
//doesn't occur since that should only occur when we are MainDom
@@ -89,24 +90,7 @@ namespace Umbraco.Web
///
public void Complete()
{
- //We now need to disable waiting for indexing for Examine so that the appdomain is shutdown immediately and doesn't wait for pending
- //indexing operations. We used to wait for indexing operations to complete but this can cause more problems than that is worth because
- //that could end up halting shutdown for a very long time causing overlapping appdomains and many other problems.
- foreach (var luceneIndexer in ExamineManager.Instance.IndexProviderCollection.OfType())
- {
- luceneIndexer.WaitForIndexQueueOnShutdown = false;
-
- //we should check if the index is locked ... it shouldn't be! We are using simple fs lock now and we are also ensuring that
- //the indexes are not operational unless MainDom is true so if _disableExamineIndexing is false then we should be in charge
- if (_disableExamineIndexing == false)
- {
- var dir = luceneIndexer.GetLuceneDirectory();
- if (IndexWriter.IsLocked(dir))
- {
- IndexWriter.Unlock(dir);
- }
- }
- }
+ EnsureUnlockedAndConfigured();
//Ok, now that everything is complete we'll check if we've stored any references to index that need rebuilding and run them
// (see the initialize method for notes) - we'll ensure we remove the event handler too in case examine manager doesn't actually
@@ -131,6 +115,8 @@ namespace Umbraco.Web
//don't do anything if we have disabled this
if (_disableExamineIndexing) return;
+ EnsureUnlockedAndConfigured();
+
//If the developer has explicitly opted out of rebuilding indexes on startup then we
// should adhere to that and not do it, this means that if they are load balancing things will be
// out of sync if they are auto-scaling but there's not much we can do about that.
@@ -168,6 +154,40 @@ namespace Umbraco.Web
yield return index;
}
+ ///
+ /// Must be called to configure each index and ensure it's unlocked before any indexing occurs
+ ///
+ ///
+ /// Indexing rebuilding can occur on a normal boot if the indexes are empty or on a cold boot by the database server messenger. Before
+ /// either of these happens, we need to configure the indexes.
+ ///
+ private void EnsureUnlockedAndConfigured()
+ {
+ if (_isConfigured) return;
+
+ _isConfigured = true;
+
+ foreach (var luceneIndexer in ExamineManager.Instance.IndexProviderCollection.OfType())
+ {
+ //We now need to disable waiting for indexing for Examine so that the appdomain is shutdown immediately and doesn't wait for pending
+ //indexing operations. We used to wait for indexing operations to complete but this can cause more problems than that is worth because
+ //that could end up halting shutdown for a very long time causing overlapping appdomains and many other problems.
+ luceneIndexer.WaitForIndexQueueOnShutdown = false;
+
+ //we should check if the index is locked ... it shouldn't be! We are using simple fs lock now and we are also ensuring that
+ //the indexes are not operational unless MainDom is true so if _disableExamineIndexing is false then we should be in charge
+ if (_disableExamineIndexing == false)
+ {
+ var dir = luceneIndexer.GetLuceneDirectory();
+ if (IndexWriter.IsLocked(dir))
+ {
+ _profilingLogger.Logger.Info("Forcing index " + luceneIndexer.IndexSetName + " to be unlocked since it was left in a locked state");
+ IndexWriter.Unlock(dir);
+ }
+ }
+ }
+ }
+
private void OnInstanceOnBuildingEmptyIndexOnStartup(object sender, BuildingEmptyIndexOnStartupEventArgs args)
{
//store the indexer that needs rebuilding because it's empty for when the boot process
diff --git a/src/Umbraco.Web/Models/BackOfficeTour.cs b/src/Umbraco.Web/Models/BackOfficeTour.cs
new file mode 100644
index 0000000000..1e0f345b52
--- /dev/null
+++ b/src/Umbraco.Web/Models/BackOfficeTour.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Umbraco.Web.Models
+{
+ [DataContract(Name = "tour", Namespace = "")]
+ public class BackOfficeTour
+ {
+ [DataMember(Name = "name")]
+ public string Name { get; set; }
+ [DataMember(Name = "alias")]
+ public string Alias { get; set; }
+ [DataMember(Name = "group")]
+ public string Group { get; set; }
+ [DataMember(Name = "allowDisable")]
+ public bool AllowDisable { get; set; }
+ [DataMember(Name = "steps")]
+ public BackOfficeTourStep[] Steps { get; set; }
+ }
+}
diff --git a/src/Umbraco.Web/Models/BackOfficeTourFile.cs b/src/Umbraco.Web/Models/BackOfficeTourFile.cs
new file mode 100644
index 0000000000..69b35c8088
--- /dev/null
+++ b/src/Umbraco.Web/Models/BackOfficeTourFile.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace Umbraco.Web.Models
+{
+ [DataContract(Name = "tourFile", Namespace = "")]
+ public class BackOfficeTourFile
+ {
+ ///
+ /// The file name for the tour
+ ///
+ [DataMember(Name = "fileName")]
+ public string FileName { get; set; }
+
+ ///
+ /// The plugin folder that the tour comes from
+ ///
+ ///
+ /// If this is null it means it's a Core tour
+ ///
+ [DataMember(Name = "pluginName")]
+ public string PluginName { get; set; }
+
+ [DataMember(Name = "tours")]
+ public IEnumerable Tours { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Models/BackOfficeTourStep.cs b/src/Umbraco.Web/Models/BackOfficeTourStep.cs
new file mode 100644
index 0000000000..e0371bf4b5
--- /dev/null
+++ b/src/Umbraco.Web/Models/BackOfficeTourStep.cs
@@ -0,0 +1,23 @@
+using System.Runtime.Serialization;
+
+namespace Umbraco.Web.Models
+{
+ [DataContract(Name = "step", Namespace = "")]
+ public class BackOfficeTourStep
+ {
+ [DataMember(Name = "title")]
+ public string Title { get; set; }
+ [DataMember(Name = "content")]
+ public string Content { get; set; }
+ [DataMember(Name = "type")]
+ public string Type { get; set; }
+ [DataMember(Name = "element")]
+ public string Element { get; set; }
+ [DataMember(Name = "elementPreventClick")]
+ public bool ElementPreventClick { get; set; }
+ [DataMember(Name = "backdropOpacity")]
+ public float BackdropOpacity { get; set; }
+ [DataMember(Name = "event")]
+ public string Event { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs
index c8ddd77afd..ae612afe09 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs
@@ -251,7 +251,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
// move to parent node
e = (XmlElement) e.ParentNode;
- id = int.Parse(e.GetAttribute("id"));
+ id = int.Parse(e.GetAttribute("id"), CultureInfo.InvariantCulture);
hasDomains = id != -1 && domainHelper.NodeHasDomains(id);
}
diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs
index f0f2461ad8..bfe8fb5260 100644
--- a/src/Umbraco.Web/PublishedContentQuery.cs
+++ b/src/Umbraco.Web/PublishedContentQuery.cs
@@ -230,7 +230,7 @@ namespace Umbraco.Web
{
// todo: in v8, implement in a more efficient way
var legacyXml = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema;
- var xpath = legacyXml ? "//node [@key=$guid]" : "//* [@isDoc and @key=$guid]";
+ var xpath = legacyXml ? "//node [@key=$guid]" : "//* [@key=$guid]";
var doc = cache.GetSingleByXPath(xpath, new XPathVariable("guid", id.ToString()));
return doc;
}
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 956071aa40..089f024021 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -372,6 +372,8 @@
+
+
@@ -430,6 +432,7 @@
+