Compare commits

...

85 Commits

Author SHA1 Message Date
Bjarke Berg 6190e2462a Review fixes 2019-08-07 10:10:46 +02:00
Bjarke Berg 69fb17a154 Added localizations of reasons 2019-08-07 09:57:37 +02:00
Bjarke Berg 90be3cb6c7 Fix test 2019-08-06 13:19:43 +02:00
Bjarke Berg c4b81a6273 Added localizations of reasons 2019-08-05 15:26:07 +02:00
Bjarke Berg 4b9c58f88f Added syntax based default isolation level + handle retries in UI 2019-08-05 07:09:43 +02:00
Bjarke Berg 4abb904389 Added spy functionality fix for Sql Ce 2019-07-31 10:08:47 +02:00
Bjarke Berg 291c30b5a1 Added spy functionality 2019-07-31 09:01:05 +02:00
Bjarke Berg 60183fe899 Added spy functionality 2019-07-31 08:50:18 +02:00
Bjarke Berg 61f27574b6 Just return the root, if the groups contains the root. 2019-07-31 08:14:24 +02:00
Bjarke Berg e2c8b55b33 AB#2083 - If the start nodes is set, but is set to the root only, then don't go to the database. 2019-07-29 14:36:39 +02:00
Shannon Deminick faef449750 Merge pull request #6009 from bielu/v8/dev
V8: Value Set builder shouldn't stop indexing when one field is empty in …
2019-07-29 11:36:01 +10:00
arkadiuszbiel 4a24064783 Value Set builder shouldn't stop indexing when one field is empty in value sets 2019-07-27 20:41:15 +01:00
Bjarke Berg fca74ad8d7 Merge pull request #5862 from umbraco/v8/bugfix/5789-PublishedContentQuery-Search
Fixes #5789 - PublishedContentQuery Search always searches on culture
2019-07-24 14:16:32 +02:00
Shannon 7bc7dba86d fixes timing issue 2019-07-24 21:46:19 +10:00
Sebastiaan Janssen ca75a25802 Makes checks a little more robust 2019-07-24 11:33:12 +02:00
Shannon ece31733a9 Merge branch 'v8/dev' of https://github.com/umbraco/Umbraco-CMS into v8/dev 2019-07-24 19:25:48 +10:00
Shannon 28a8027179 removes reference to old packages.umbraco.org 2019-07-24 19:25:32 +10:00
Steve Megson a97604d6c4 Handle CSV values in TagsPropertyEditor 2019-07-24 11:14:00 +02:00
Bjarke Berg 308f929f7b Refactored tests to avoid duplicate code 2019-07-24 11:13:21 +02:00
Shannon c8d7df1515 Merge branch 'v8/bugfix/5789-PublishedContentQuery-Search' of https://github.com/umbraco/Umbraco-CMS into v8/bugfix/5789-PublishedContentQuery-Search 2019-07-24 18:46:48 +10:00
Shannon 2c795662d2 Adds tests for PublishedContentQuery search 2019-07-24 18:46:14 +10:00
Kenn Jacobsen 8a43e3a87e Make sure save options are up to date with the content state 2019-07-24 10:46:00 +02:00
Bjarke Berg 210e43fcb0 Fix for build 2019-07-24 09:53:51 +02:00
Shannon Deminick beb8c7ac7c Update src/Umbraco.Web/PublishedContentQuery.cs
Co-Authored-By: Bjarke Berg <mail@bergmania.dk>
2019-07-24 17:30:29 +10:00
Shannon Deminick 2f8979bbc3 Update src/Umbraco.Web/PublishedContentQuery.cs
Co-Authored-By: Bjarke Berg <mail@bergmania.dk>
2019-07-24 17:29:00 +10:00
Shannon a8ed7f2c17 adds new method 2019-07-23 22:11:02 +10:00
Frans de Jong 1c6bf55e5d GetCurrentLoginStatus()
In the summary of MembershipHelper.GetCurrentLoginStatus() it states that null will be returned when no member is logged in but that isn't the case (anymore?).
2019-07-23 13:02:11 +02:00
Bjarke Berg c246044ac7 Merge pull request #5972 from stevemegson/v8/temp-checkboxlist
Content migration - Include CheckBoxList in ValueListPreValueMigrator
2019-07-23 12:47:13 +02:00
Shannon 5126521d37 Fixes #5789 2019-07-23 19:09:15 +10:00
Shannon 27223738c6 Merge branch 'v8/8.1' into v8/dev
# Conflicts:
#	src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
2019-07-23 19:05:06 +10:00
Steve Megson 968463912a Include CheckBoxList in ValueListPreValueMigrator 2019-07-21 14:47:37 +01:00
Shannon Deminick 05c6b856f9 Merge pull request #5897 from kjac/v8-fix-media-type-add-property
V8: Fix YSOD when editing media types
2019-07-19 15:57:01 +10:00
Shannon 6fe3089207 Merge branch 'v8/8.1' into v8/dev
# Conflicts:
#	src/SolutionInfo.cs
#	src/Umbraco.Web/HtmlHelperRenderExtensions.cs
#	src/Umbraco.Web/Mvc/HttpUmbracoFormRouteStringException.cs
#	src/Umbraco.Web/Mvc/ValidateUmbracoFormRouteStringAttribute.cs
2019-07-17 22:31:54 +10:00
Shannon Deminick 06b136fdf6 Merge pull request #5895 from ronaldbarendse/v8/feature/validate-umbracoformroutestring
Added attribute to validate Umbraco form route strings (ufprt)
2019-07-16 22:01:32 +10:00
Bjarke Berg c6246b5404 Merge remote-tracking branch 'origin/v8/dev' into v8/dev 2019-07-16 13:46:07 +02:00
Bjarke Berg 3062f40ef5 Missing file in project file 2019-07-16 13:43:10 +02:00
Bjarke Berg dcd0d38419 Merge remote-tracking branch 'origin/v8/8.1' into v8/dev 2019-07-16 13:41:30 +02:00
Bjarke Berg c01a8f8f13 Merge pull request #5887 from stevemegson/temp-content-migration
PR fixes 7 -> 8 migration bugs for #5852 and #5864
2019-07-16 13:37:08 +02:00
Kenn Jacobsen da71a94c39 Use IsCollectionEmpty() as per review 2019-07-16 12:45:27 +02:00
Shannon Deminick f6413af3f4 Merge pull request #5885 from hifi-phil/v8/dev
Change PropertyType.Alias to virtual
2019-07-16 20:31:58 +10:00
Bjarke Berg 440e649d6d Merge pull request #5861 from umbraco/v8/bugfix/cdf-upgrade-bug
Fixes issue during upgrade where umbraco can't clear cdf log files
2019-07-15 14:32:19 +02:00
Warren Buckley 47dcd37d1d Merge pull request #5784 from nathanwoulfe/5768-log-viewer-chart-naming
V8: Make log naming and colors consistent between Log Search and Log Overview
2019-07-15 11:47:20 +01:00
Kenn Jacobsen d60fc63e78 Fix YSOD when editing media types 2019-07-15 12:23:09 +02:00
Ronald Barendse b0c4042e87 Removed UmbracoAntiForgeryAdditionalDataProvider 2019-07-15 10:16:09 +02:00
Shannon da525513a3 Merge remote-tracking branch 'origin/v7/dev' into v8/dev
# Conflicts:
#	build/NuSpecs/tools/Web.config.install.xdt
#	src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.controller.js
#	src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js
#	src/Umbraco.Web/Editors/EntityController.cs
#	src/Umbraco.Web/UmbracoHelper.cs
2019-07-15 16:18:04 +10:00
Shannon Deminick 2ac5e61e44 Merge pull request #5881 from bjarnef/dev-v7-link-picker-url-anchors
v7: Fix link picker url and anchors
2019-07-15 13:02:47 +10:00
Ronald Barendse 101a8b6c78 Added ValidateUmbracoFormRouteStringAttribute to public controllers 2019-07-14 23:18:21 +02:00
Ronald Barendse f7a10e3a15 Added attribute to validate Umbraco form route strings (ufprt) 2019-07-14 23:18:06 +02:00
Steve Megson 4e0dd728a8 Fixes #5864 - migrate prevalues for Umbraco.DropDown.Flexible 2019-07-14 16:48:33 +01:00
Steve Megson 320ccf8105 Fixes #5852 - TablesForScheduledPublishing 2019-07-14 16:46:01 +01:00
hifi-phil 62f924a757 Change PropertyType.Alias to virtual
When unit testing PropertyType.Alias needs to be virtual to make it mockable. The underlying code on the set triggers IOC logic and makes it hard to test anything that uses Property Types.
2019-07-14 09:45:12 +01:00
Bjarne Fyrstenborg c47a08b553 Add overload for NiceUrl 2019-07-13 16:40:01 +02:00
Bjarne Fyrstenborg de9241bcf5 Add UmbracoHelper overload methods to get url by guid 2019-07-13 14:50:25 +02:00
Bjarne Fyrstenborg 7c03fe3c1f Adjust parameter 2019-07-13 14:49:35 +02:00
Bjarne Fyrstenborg 82eb241119 Add overload method for Udi 2019-07-13 14:49:05 +02:00
Bjarne Fyrstenborg 1073e6257c Adjust comments 2019-07-13 14:48:35 +02:00
Nathan Woulfe bc3e4847fe Merge branch '5768-log-viewer-chart-naming' of https://github.com/nathanwoulfe/umbraco-cms into 5768-log-viewer-chart-naming 2019-07-13 13:50:21 +10:00
Nathan Woulfe 1a7251909c fix empty array - was checking index against empty array, so condition was never met 2019-07-13 13:45:13 +10:00
Warren Buckley 61a18297d9 Make v8/dev now 8.2.0 2019-07-12 13:03:20 +01:00
Warren Buckley bdd413f9ee Merge branch 'v8/dev' into 5768-log-viewer-chart-naming 2019-07-12 12:30:32 +01:00
Bjarke Berg fd8ca35e63 Merge pull request #5869 from umbraco/v8/bugfix/logviewer-error-count
V8: Fixes LogViewer Error Count search result click
2019-07-12 12:56:08 +02:00
Warren Buckley fdd9aee773 Fixes LogViewer Error Count search result clickthrough to include errors & fatal without exception as well 2019-07-12 11:07:15 +01:00
Nathan Woulfe 3c2a92d3b6 map log values into logtypedata array - removes need to add these in the correct order. 2019-07-12 14:21:28 +10:00
Nathan Woulfe cf49e6160a critical => fatal 2019-07-12 14:20:28 +10:00
Shannon ec93030e21 Merge branch 'v8/dev' of https://github.com/umbraco/Umbraco-CMS into v8/dev 2019-07-12 11:23:51 +10:00
Shannon 1d49c8626e Revert "V8: Don't show multiple open menus (take two) (#5609)"
This reverts commit 9ce996cbba.
2019-07-12 11:23:36 +10:00
Shannon 89bb94aa1a Fixes issue during upgrade where umbraco can't clear cdf log files 2019-07-12 10:57:26 +10:00
Warren Buckley 03a4b75176 Ran .\build SetUmbracoVersion 8.1.1 to bump version 2019-07-11 15:11:37 +01:00
Sebastiaan Janssen 943b54dcf7 Update 1_Bug.md 2019-07-11 11:55:01 +02:00
Warren Buckley 39bd18ec1a At some point we have upgraded Serilog.Filters.Expressions package & the signature/method name got changed to ByIncludingOnly() 2019-07-11 10:45:41 +01:00
Shannon e93ddff46b Merge branch 'nathanwoulfe-temp-5147-grid-reorder' into v8/dev 2019-07-09 11:01:11 +10:00
Nathan Woulfe 3144b51c91 fix behaviour on page load - don't add active class if sticky-bar is in viewport 2019-07-09 10:49:34 +10:00
Nathan Woulfe e92f68d040 improve variable naming 2019-07-09 10:46:29 +10:00
Nathan Woulfe 469054e9b3 No longer need scrollable-container attribute 2019-07-09 10:46:29 +10:00
Nathan Woulfe d17022d87d make it look pretty 2019-07-09 10:46:29 +10:00
Nathan Woulfe 6865629090 use position:sticky and IntersectionObserver instead of element cloning and position:fixed - much simpler. 2019-07-09 10:46:29 +10:00
Shannon a4749f201d Ensures luceneIndexer.WaitForIndexQueueOnShutdown is called even when not in MainDom 2019-07-08 23:03:20 +10:00
Shannon Deminick ad90264ef1 Merge pull request #5695 from linuselander/issue/5218
Enable removal of unpublished content from ExternalIndex
2019-07-08 19:34:25 +10:00
Shannon Deminick 9ba7e7742a Merge pull request #5801 from bielu/v8/dev
Remove last casting to LuceneIndex
2019-07-08 18:39:40 +10:00
arkadiuszbiel 6473f600c7 Remove last casting to LucenIndex 2019-07-08 08:48:49 +01:00
Nathan Woulfe 6329dfa629 Merge Andy's changes 2019-07-06 07:28:32 +10:00
Andy Butland 2cd0173534 Fixed incorrect label for "Fatal" errors on the log viewer pie chart.
Made the log viewer title variable so it either says "Log Overview For Today" or "Log Overview For Selected Time Period" (previously it said the former even if you'd adjusted the dates you were looking at.
Fixed up the selected date period display so the conjunction "to" is used between the dates when the selected time period is changed and querystring is updated.
Minor code tidy-up.
2019-07-04 14:35:06 +02:00
Nathan Woulfe a0bcfeddcc fix date range formatting on flatpickr - 'to' wasnt displayed after choosing a range 2019-07-04 11:48:02 +10:00
Nathan Woulfe c4b5483d8c make log naming and colors consistent between views 2019-07-04 11:19:10 +10:00
Linus Elander 7557c584d8 Adjusts conditions for which indexes should be affected when a node is unpublished 2019-06-19 21:41:47 +02:00
75 changed files with 1050 additions and 2633 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ thoroughly. Then, proceed by filling out the rest of the details in the issue
template below. The more details you can give us, the easier it will be for us
to determine the cause of a problem.
See: https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md
See: https://github.com/umbraco/Umbraco-CMS/blob/v8/dev/.github/CONTRIBUTING.md
-->
+2 -2
View File
@@ -18,5 +18,5 @@ using System.Resources;
[assembly: AssemblyVersion("8.0.0")]
// these are FYI and changed automatically
[assembly: AssemblyFileVersion("8.1.1")]
[assembly: AssemblyInformationalVersion("8.1.1")]
[assembly: AssemblyFileVersion("8.2.0")]
[assembly: AssemblyInformationalVersion("8.2.0")]
@@ -0,0 +1,16 @@
using System;
namespace Umbraco.Core.Exceptions
{
/// <summary>
/// An exception that is thrown if the a query as waited for the lock too long
/// </summary>
public class LockTimeoutException : Exception
{
public LockTimeoutException(Exception inner)
: base(string.Empty, inner)
{ }
public short Reason { get; set; }
}
}
@@ -6,6 +6,7 @@ using Umbraco.Core.Migrations.Upgrade.Common;
using Umbraco.Core.Migrations.Upgrade.V_8_0_0;
using Umbraco.Core.Migrations.Upgrade.V_8_0_1;
using Umbraco.Core.Migrations.Upgrade.V_8_1_0;
using Umbraco.Core.Migrations.Upgrade.V_8_2_0;
namespace Umbraco.Core.Migrations.Upgrade
{
@@ -182,6 +183,8 @@ namespace Umbraco.Core.Migrations.Upgrade
To<RenameUserLoginDtoDateIndex>("{0372A42B-DECF-498D-B4D1-6379E907EB94}");
To<FixContentNuCascade>("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}");
// to 8.2.0....
To<AddLockTableColumns>("{8DC6A949-0FE4-4846-9C1D-61E49DDADFF9}");
//FINAL
}
}
@@ -0,0 +1,32 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.DataTypes
{
class DropDownFlexiblePreValueMigrator : IPreValueMigrator
{
public bool CanMigrate(string editorAlias)
=> editorAlias == "Umbraco.DropDown.Flexible";
public virtual string GetNewAlias(string editorAlias)
=> null;
public object GetConfiguration(int dataTypeId, string editorAlias, Dictionary<string, PreValueDto> preValues)
{
var config = new DropDownFlexibleConfiguration();
foreach (var preValue in preValues.Values)
{
if (preValue.Alias == "multiple")
{
config.Multiple = (preValue.Value == "1");
}
else
{
config.Items.Add(new ValueListConfiguration.ValueListItem { Id = preValue.Id, Value = preValue.Value });
}
}
return config;
}
}
}
@@ -19,6 +19,7 @@ public class PreValueMigratorComposer : ICoreComposer
.Append<NestedContentPreValueMigrator>()
.Append<DecimalPreValueMigrator>()
.Append<ListViewPreValueMigrator>()
.Append<DropDownFlexiblePreValueMigrator>()
.Append<ValueListPreValueMigrator>();
}
}
@@ -9,6 +9,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.DataTypes
private readonly string[] _editors =
{
"Umbraco.RadioButtonList",
"Umbraco.CheckBoxList",
"Umbraco.DropDown",
"Umbraco.DropdownlistPublishingKeys",
"Umbraco.DropDownMultiple",
@@ -0,0 +1,19 @@
using System.Linq;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Migrations.Upgrade.V_8_2_0
{
public class AddLockTableColumns : MigrationBase
{
public AddLockTableColumns(IMigrationContext context)
: base(context)
{ }
public override void Migrate()
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList();
AddColumnIfNotExists<LockDto>(columns, "writeLockReasonId");
}
}
}
+1 -1
View File
@@ -114,7 +114,7 @@ namespace Umbraco.Core.Models
/// Gets of sets the alias of the property type.
/// </summary>
[DataMember]
public string Alias
public virtual string Alias
{
get => _alias;
set => SetPropertyValueAndDetectChanges(SanitizeAlias(value), ref _alias, nameof(Alias));
+2 -1
View File
@@ -56,7 +56,7 @@ namespace Umbraco.Core.Models
/// </returns>
internal static string[] GetUserAvatarUrls(this IUser user, IAppCache cache)
{
// If FIPS is required, never check the Gravatar service as it only supports MD5 hashing.
// If FIPS is required, never check the Gravatar service as it only supports MD5 hashing.
// Unfortunately, if the FIPS setting is enabled on Windows, using MD5 will throw an exception
// and the website will not run.
// Also, check if the user has explicitly removed all avatars including a Gravatar, this will be possible and the value will be "none"
@@ -310,6 +310,7 @@ namespace Umbraco.Core.Models
// assume groupSn and userSn each don't contain duplicates
var asn = groupSn.Concat(userSn).Distinct().ToArray();
var paths = asn.Length > 0
? entityService.GetAllPaths(objectType, asn).ToDictionary(x => x.Id, x => x.Path)
: new Dictionary<int, string>();
+25 -11
View File
@@ -1,62 +1,76 @@
// ReSharper disable once CheckNamespace
namespace Umbraco.Core
{
static partial class Constants
{
/// <summary>
/// Defines lock objects.
/// Defines lock objects.
/// </summary>
public static class Locks
{
/// <summary>
/// All servers.
/// All servers.
/// </summary>
public const int Servers = -331;
/// <summary>
/// All content and media types.
/// All content and media types.
/// </summary>
public const int ContentTypes = -332;
/// <summary>
/// The entire content tree, i.e. all content items.
/// The entire content tree, i.e. all content items.
/// </summary>
public const int ContentTree = -333;
/// <summary>
/// The entire media tree, i.e. all media items.
/// The entire media tree, i.e. all media items.
/// </summary>
public const int MediaTree = -334;
/// <summary>
/// The entire member tree, i.e. all members.
/// The entire member tree, i.e. all members.
/// </summary>
public const int MemberTree = -335;
/// <summary>
/// All media types.
/// All media types.
/// </summary>
public const int MediaTypes = -336;
/// <summary>
/// All member types.
/// All member types.
/// </summary>
public const int MemberTypes = -337;
/// <summary>
/// All domains.
/// All domains.
/// </summary>
public const int Domains = -338;
/// <summary>
/// All key-values.
/// All key-values.
/// </summary>
public const int KeyValues = -339;
/// <summary>
/// All languages.
/// All languages.
/// </summary>
public const int Languages = -340;
public static class Reason
{
public const short Default = 1;
public const short MoveContentToRecycleBin = 10000;
public const short RestoreContentFromRecycleBin = 10001;
public const short EmptyRecycleBin = 10002;
public const short MoveContent = 10003;
public const short CopyContent = 10004;
public const short SortContent = 10005;
public const short DeleteContentOfTypes = 10006;
}
}
}
}
@@ -1,43 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Runtime.CompilerServices;
namespace Umbraco.Core.Persistence
{
internal static class DatabaseNodeLockExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ValidateDatabase(IUmbracoDatabase database)
{
if (database == null)
throw new ArgumentNullException("database");
if (database.GetCurrentTransactionIsolationLevel() < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
}
// updating a record within a repeatable-read transaction gets an exclusive lock on
// that record which will be kept until the transaction is ended, effectively locking
// out all other accesses to that record - thus obtaining an exclusive lock over the
// protected resources.
public static void AcquireLockNodeWriteLock(this IUmbracoDatabase database, int nodeId)
{
ValidateDatabase(database);
database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id",
new { @id = nodeId });
}
// reading a record within a repeatable-read transaction gets a shared lock on
// that record which will be kept until the transaction is ended, effectively preventing
// other write accesses to that record - thus obtaining a shared lock over the protected
// resources.
public static void AcquireLockNodeReadLock(this IUmbracoDatabase database, int nodeId)
{
ValidateDatabase(database);
database.ExecuteScalar<int>("SELECT value FROM umbracoLock WHERE id=@id",
new { @id = nodeId });
}
}
}
@@ -20,5 +20,9 @@ namespace Umbraco.Core.Persistence.Dtos
[NullSetting(NullSetting = NullSettings.NotNull)]
[Length(64)]
public string Name { get; set; }
[Column("writeLockReasonId")]
[NullSetting(NullSetting = NullSettings.Null)]
public short WriteLockReasonId { get; set; }
}
}
+2 -1
View File
@@ -1,4 +1,5 @@
using NPoco;
using System.Data;
using NPoco;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -0,0 +1,18 @@
using System;
using NPoco;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Persistence.Interceptors
{
public class LockTimeoutInterceptor : IExceptionInterceptor
{
public void OnException(IDatabase database, Exception exception)
{
if (database is IUmbracoDatabase umbracoDatabase && umbracoDatabase.SqlContext.SqlSyntax.IsLockTimeoutException(exception))
{
throw new LockTimeoutException(exception);
}
}
}
}
@@ -5,6 +5,7 @@ using NPoco;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using IsolationLevel = System.Data.IsolationLevel;
namespace Umbraco.Core.Persistence.SqlSyntax
{
@@ -76,6 +77,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax
string ConvertIntegerToOrderableString { get; }
string ConvertDateToOrderableString { get; }
string ConvertDecimalToOrderableString { get; }
bool IsReadUncommittedSupported { get; }
IsolationLevel DefaultIsolationLevel { get; }
IEnumerable<string> GetTablesInSchema(IDatabase db);
IEnumerable<ColumnInfo> GetColumnsInSchema(IDatabase db);
@@ -121,5 +124,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// unspecified.</para>
/// </remarks>
bool TryGetDefaultConstraint(IDatabase db, string tableName, string columnName, out string constraintName);
void ReadLock(IDatabase db, params int[] lockIds);
void WriteLock(IDatabase db, int? writeLockReasonId, params int[] lockIds);
bool IsLockTimeoutException(Exception exception);
}
}
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.Linq;
using System.Transactions;
using NPoco;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
@@ -12,11 +14,13 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// </summary>
public class SqlCeSyntaxProvider : MicrosoftSqlSyntaxProviderBase<SqlCeSyntaxProvider>
{
public override Sql<ISqlContext> SelectTop(Sql<ISqlContext> sql, int top)
{
return new Sql<ISqlContext>(sql.SqlContext, sql.SQL.Insert(sql.SQL.IndexOf(' '), " TOP " + top), sql.Arguments);
}
public override bool SupportsClustered()
{
return false;
@@ -47,6 +51,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return indexType;
}
public override bool IsReadUncommittedSupported => false;
public override System.Data.IsolationLevel DefaultIsolationLevel => System.Data.IsolationLevel.RepeatableRead;
public override string GetConcat(params string[] args)
{
return "(" + string.Join("+", args) + ")";
@@ -143,6 +150,45 @@ where table_name=@0 and column_name=@1", tableName, columnName).FirstOrDefault()
return hasDefault;
}
public override void WriteLock(IDatabase db, int? writeLockReasonId, params int[] lockIds)
{
// soon as we get Database, a transaction is started
if (db.Transaction.IsolationLevel < (System.Data.IsolationLevel) IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
db.Execute(@"SET LOCK_TIMEOUT 1800;");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = db.Execute(@"UPDATE umbracoLock SET value = value*-1, writeLockReasonId = @writeLockReasonId WHERE id=@id", new { id = lockId, writeLockReasonId = writeLockReasonId });
if (i == 0) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
}
public override bool IsLockTimeoutException(Exception exception)
{
return exception is SqlCeLockTimeoutException;
}
public override void ReadLock(IDatabase db, params int[] lockIds)
{
// soon as we get Database, a transaction is started
if (db.Transaction.IsolationLevel < (System.Data.IsolationLevel) IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = db.ExecuteScalar<int?>("SELECT value FROM umbracoLock WHERE id=@id", new { id = lockId });
if (i == null) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
}
public override bool DoesTableExist(IDatabase db, string tableName)
{
var result =
@@ -2,11 +2,12 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using NPoco;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Persistence.SqlSyntax
{
@@ -173,6 +174,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return items.Select(x => new Tuple<string, string, string, string>(x.TableName, x.ColumnName, x.Name, x.Definition));
}
public override bool IsReadUncommittedSupported => true;
public override System.Data.IsolationLevel DefaultIsolationLevel => System.Data.IsolationLevel.ReadCommitted;
public override IEnumerable<string> GetTablesInSchema(IDatabase db)
{
var items = db.Fetch<dynamic>("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = (SELECT SCHEMA_NAME())");
@@ -237,6 +241,44 @@ where tbl.[name]=@0 and col.[name]=@1;", tableName, columnName)
return !constraintName.IsNullOrWhiteSpace();
}
public override void WriteLock(IDatabase db, int? writeLockReasonId, params int[] lockIds)
{
// soon as we get Database, a transaction is started
if (db.Transaction.IsolationLevel < IsolationLevel.ReadCommitted)
throw new InvalidOperationException("A transaction with minimum ReadCommitted isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
db.Execute(@"SET LOCK_TIMEOUT 1800;");
var i = db.Execute(@"UPDATE umbracoLock WITH (REPEATABLEREAD) SET value = value*-1, writeLockReasonId = @writeLockReasonId WHERE id=@id", new { id = lockId, writeLockReasonId = writeLockReasonId });
if (i == 0) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
}
public override bool IsLockTimeoutException(Exception exception)
{
return exception is SqlException sqlException && sqlException.Number == 1222;
}
public override void ReadLock(IDatabase db, params int[] lockIds)
{
// soon as we get Database, a transaction is started
if (db.Transaction.IsolationLevel < IsolationLevel.ReadCommitted)
throw new InvalidOperationException("A transaction with minimum ReadCommitted isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = db.ExecuteScalar<int?>("SELECT value FROM umbracoLock WITH (REPEATABLEREAD) WHERE id=@id", new { id = lockId });
if (i == null) // ensure we are actually locking!
throw new ArgumentException($"LockObject with id={lockId} does not exist.", nameof(lockIds));
}
}
public override bool DoesTableExist(IDatabase db, string tableName)
{
var result =
@@ -200,7 +200,10 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return "NVARCHAR";
}
public abstract bool IsReadUncommittedSupported { get; }
public abstract IsolationLevel DefaultIsolationLevel { get; }
public virtual IEnumerable<string> GetTablesInSchema(IDatabase db)
{
return new List<string>();
@@ -224,6 +227,10 @@ namespace Umbraco.Core.Persistence.SqlSyntax
public abstract IEnumerable<Tuple<string, string, string, bool>> GetDefinedIndexes(IDatabase db);
public abstract bool TryGetDefaultConstraint(IDatabase db, string tableName, string columnName, out string constraintName);
public abstract void ReadLock(IDatabase db, params int[] lockIds);
public abstract void WriteLock(IDatabase db, int? writeLockReasonId, params int[] lockIds);
public abstract bool IsLockTimeoutException(Exception exception);
public virtual bool DoesTableExist(IDatabase db, string tableName)
{
@@ -559,5 +566,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
public virtual string ConvertIntegerToOrderableString => "REPLACE(STR({0}, 8), SPACE(1), '0')";
public virtual string ConvertDateToOrderableString => "CONVERT(nvarchar, {0}, 102)";
public virtual string ConvertDecimalToOrderableString => "REPLACE(STR({0}, 20, 9), SPACE(1), '0')";
}
}
@@ -20,9 +20,6 @@ namespace Umbraco.Core.Persistence
/// </remarks>
public class UmbracoDatabase : Database, IUmbracoDatabase
{
// Umbraco's default isolation level is RepeatableRead
private const IsolationLevel DefaultIsolationLevel = IsolationLevel.RepeatableRead;
private readonly ILogger _logger;
private readonly RetryPolicy _connectionRetryPolicy;
private readonly RetryPolicy _commandRetryPolicy;
@@ -38,7 +35,7 @@ namespace Umbraco.Core.Persistence
/// <para>Also used by DatabaseBuilder for creating databases and installing/upgrading.</para>
/// </remarks>
public UmbracoDatabase(string connectionString, ISqlContext sqlContext, DbProviderFactory provider, ILogger logger, RetryPolicy connectionRetryPolicy = null, RetryPolicy commandRetryPolicy = null)
: base(connectionString, sqlContext.DatabaseType, provider, DefaultIsolationLevel)
: base(connectionString, sqlContext.DatabaseType, provider, sqlContext.SqlSyntax.DefaultIsolationLevel)
{
SqlContext = sqlContext;
@@ -54,7 +51,7 @@ namespace Umbraco.Core.Persistence
/// </summary>
/// <remarks>Internal for unit tests only.</remarks>
internal UmbracoDatabase(DbConnection connection, ISqlContext sqlContext, ILogger logger)
: base(connection, sqlContext.DatabaseType, DefaultIsolationLevel)
: base(connection, sqlContext.DatabaseType, sqlContext.SqlSyntax.DefaultIsolationLevel)
{
SqlContext = sqlContext;
_logger = logger;
@@ -7,6 +7,7 @@ using NPoco.FluentMappings;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.FaultHandling;
using Umbraco.Core.Persistence.Interceptors;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -238,13 +239,15 @@ namespace Umbraco.Core.Persistence
// so that everything NPoco is properly cached for the lifetime of the application
_pocoMappers = new NPoco.MapperCollection { new PocoMapper() };
var factory = new FluentPocoDataFactory(GetPocoDataFactoryResolver);
_pocoDataFactory = factory;
var config = new FluentConfig(xmappers => factory);
// create the database factory
_npocoDatabaseFactory = DatabaseFactory.Config(x => x
.UsingDatabase(CreateDatabaseInstance) // creating UmbracoDatabase instances
.WithFluentConfig(config)); // with proper configuration
.WithFluentConfig(config) // with proper configuration
.WithInterceptor(new LockTimeoutInterceptor()));
if (_npocoDatabaseFactory == null)
throw new NullReferenceException("The call to UmbracoDatabaseFactory.Config yielded a null UmbracoDatabaseFactory instance.");
+25
View File
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Umbraco.Core.Cache;
using Umbraco.Core.Events;
using Umbraco.Core.Persistence;
@@ -57,6 +59,29 @@ namespace Umbraco.Core.Scoping
/// Write-locks some lock objects.
/// </summary>
/// <param name="lockIds">The lock object identifiers.</param>
[Obsolete("Use the overload with reason")]
[Browsable(false)]
void WriteLock(params int[] lockIds);
/// <summary>
/// Write-locks some lock objects.
/// </summary>
/// <param name="writeLockReasonId">The id of the reason to take the write lock. See Constants.Locks.Reason.*</param>
/// <param name="lockIds">The lock object identifiers.</param>
void WriteLock(short writeLockReasonId, params int[] lockIds);
/// <summary>
/// Bypasses all db-locks and reads the WriteLockReasonId column on the specified locks.
/// </summary>
/// <param name="lockIds">The lock ids from the Constants.Locks.*</param>
/// <returns>A dictionary with the lock is as key, and the last started work as value. Note value can be null if we don't know or use SqlCe.</returns>
IDictionary<int, short> TrySpyLock(params int[] lockIds);
/// <summary>
/// Bypasses all db-locks and reads the WriteLockReasonId column on the specified lock.
/// </summary>
/// <param name="lockId">The lock id from the Constants.Locks.*</param>
/// <returns>The last started work. Note this can be null if we don't know or use SqlCe.</returns>
short TrySpyLock(int lockId);
}
}
+35 -24
View File
@@ -1,11 +1,15 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
namespace Umbraco.Core.Scoping
{
@@ -33,8 +37,6 @@ namespace Umbraco.Core.Scoping
private ICompletable _fscope;
private IEventDispatcher _eventDispatcher;
private const IsolationLevel DefaultIsolationLevel = IsolationLevel.RepeatableRead;
// initializes a new scope
private Scope(ScopeProvider scopeProvider,
ILogger logger, FileSystems fileSystems, Scope parent, ScopeContext scopeContext, bool detachable,
@@ -205,7 +207,7 @@ namespace Umbraco.Core.Scoping
{
if (_isolationLevel != IsolationLevel.Unspecified) return _isolationLevel;
if (ParentScope != null) return ParentScope.IsolationLevel;
return DefaultIsolationLevel;
return Database.SqlContext.SqlSyntax.DefaultIsolationLevel;
}
}
@@ -235,6 +237,7 @@ namespace Umbraco.Core.Scoping
try
{
_database.BeginTransaction(IsolationLevel);
_database.Execute("SET LOCK_TIMEOUT 1800;");
return _database;
}
catch
@@ -490,35 +493,43 @@ namespace Umbraco.Core.Scoping
/// <inheritdoc />
public void ReadLock(params int[] lockIds)
{
// soon as we get Database, a transaction is started
Database.SqlContext.SqlSyntax.ReadLock(Database, lockIds);
}
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
/// <inheritdoc />
[Obsolete("Use the overload with reason")]
[Browsable(false)]
public void WriteLock(params int[] lockIds)
{
WriteLock(Constants.Locks.Reason.Default, lockIds);
}
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
/// <inheritdoc />
public void WriteLock(short writeLockReasonId, params int[] lockIds)
{
Database.SqlContext.SqlSyntax.WriteLock(Database, writeLockReasonId, lockIds);
}
/// <inheritdoc />
public IDictionary<int, short> TrySpyLock(params int[] lockIds)
{
using (var db = _scopeProvider.DatabaseFactory.CreateDatabase())
{
var i = Database.ExecuteScalar<int?>("SELECT value FROM umbracoLock WHERE id=@id", new { id = lockId });
if (i == null) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
if (!db.SqlContext.SqlSyntax.IsReadUncommittedSupported)
{
return lockIds.ToDictionary(x => x, x=> Constants.Locks.Reason.Default);
};
return db.Fetch<LockDto>("SELECT * FROM umbracoLock WITH (NOLOCK) WHERE id IN (@lockIds)", new { lockIds })
.ToDictionary(x => x.Id, x => x.WriteLockReasonId);
}
}
/// <inheritdoc />
public void WriteLock(params int[] lockIds)
public short TrySpyLock(int lockId)
{
// soon as we get Database, a transaction is started
if (Database.Transaction.IsolationLevel < IsolationLevel.RepeatableRead)
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
{
var i = Database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id", new { id = lockId });
if (i == 0) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
return TrySpyLock(new []{lockId})[lockId];
}
}
}
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Services.Implement
private IQuery<IContent> _queryNotTrashed;
//TODO: The non-lazy object should be injected
private readonly Lazy<PropertyValidationService> _propertyValidationService = new Lazy<PropertyValidationService>(() => new PropertyValidationService());
#region Constructors
@@ -1666,7 +1666,7 @@ namespace Umbraco.Core.Services.Implement
return OperationResult.Cancel(evtMsgs);
}
scope.WriteLock(Constants.Locks.ContentTree);
scope.WriteLock(Constants.Locks.Reason.EmptyRecycleBin, Constants.Locks.ContentTree);
// if it's not trashed yet, and published, we should unpublish
// but... Unpublishing event makes no sense (not going to cancel?) and no need to save
@@ -1790,7 +1790,7 @@ namespace Umbraco.Core.Services.Implement
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
scope.WriteLock(Constants.Locks.Reason.MoveContentToRecycleBin, Constants.Locks.ContentTree);
var originalPath = content.Path;
var moveEventInfo = new MoveEventInfo<IContent>(content, originalPath, Constants.System.RecycleBinContent);
@@ -1849,7 +1849,7 @@ namespace Umbraco.Core.Services.Implement
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
scope.WriteLock(Constants.Locks.Reason.MoveContent, Constants.Locks.ContentTree);
var parent = parentId == Constants.System.Root ? null : GetById(parentId);
if (parentId != Constants.System.Root && (parent == null || parent.Trashed))
@@ -1969,7 +1969,7 @@ namespace Umbraco.Core.Services.Implement
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
scope.WriteLock(Constants.Locks.Reason.EmptyRecycleBin, Constants.Locks.ContentTree);
// v7 EmptyingRecycleBin and EmptiedRecycleBin events are greatly simplified since
// each deleted items will have its own deleting/deleted events. so, files and such
@@ -2052,7 +2052,7 @@ namespace Umbraco.Core.Services.Implement
var copies = new List<Tuple<IContent, IContent>>();
scope.WriteLock(Constants.Locks.ContentTree);
scope.WriteLock(Constants.Locks.Reason.CopyContent, Constants.Locks.ContentTree);
// a copy is not published (but not really unpublishing either)
// update the create author and last edit author
@@ -2198,7 +2198,7 @@ namespace Umbraco.Core.Services.Implement
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
scope.WriteLock(Constants.Locks.Reason.SortContent, Constants.Locks.ContentTree);
var ret = Sort(scope, itemsA, userId, evtMsgs, raiseEvents);
scope.Complete();
@@ -2227,7 +2227,7 @@ namespace Umbraco.Core.Services.Implement
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
scope.WriteLock(Constants.Locks.Reason.SortContent, Constants.Locks.ContentTree);
var itemsA = GetByIds(idsA).ToArray();
var ret = Sort(scope, itemsA, userId, evtMsgs, raiseEvents);
@@ -2729,7 +2729,7 @@ namespace Umbraco.Core.Services.Implement
//
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
scope.WriteLock(Constants.Locks.Reason.DeleteContentOfTypes,Constants.Locks.ContentTree);
var query = Query<IContent>().WhereIn(x => x.ContentTypeId, contentTypeIdsA);
var contents = _documentRepository.Get(query).ToArray();
@@ -3028,6 +3028,6 @@ namespace Umbraco.Core.Services.Implement
#endregion
}
}
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
@@ -257,20 +258,38 @@ namespace Umbraco.Core.Services.Implement
/// <inheritdoc />
public virtual IEnumerable<IEntitySlim> GetChildren(int parentId)
{
using (ScopeProvider.CreateScope(autoComplete: true))
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
var query = Query<IUmbracoEntity>().Where(x => x.ParentId == parentId);
return _entityRepository.GetByQuery(query);
try
{
var query = Query<IUmbracoEntity>().Where(x => x.ParentId == parentId);
return _entityRepository.GetByQuery(query);
}
catch (LockTimeoutException e)
{
e.Reason = scope.TrySpyLock(Constants.Locks.ContentTree);
throw;
}
}
}
/// <inheritdoc />
public virtual IEnumerable<IEntitySlim> GetChildren(int parentId, UmbracoObjectTypes objectType)
{
using (ScopeProvider.CreateScope(autoComplete: true))
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
var query = Query<IUmbracoEntity>().Where(x => x.ParentId == parentId);
return _entityRepository.GetByQuery(query, objectType.GetGuid());
try
{
var query = Query<IUmbracoEntity>().Where(x => x.ParentId == parentId);
return _entityRepository.GetByQuery(query, objectType.GetGuid());
}
catch (LockTimeoutException e)
{
e.Reason = scope.TrySpyLock(Constants.Locks.ContentTree);
throw;
}
}
}
@@ -18,6 +18,11 @@ namespace Umbraco.Core.Services
return manager.Localize(fullKey, Thread.CurrentThread.CurrentUICulture);
}
public static string LocalizeLockReason(this ILocalizedTextService manager, short writeLockReasonId)
{
return manager.Localize($"writeLockReasons/Code_{writeLockReasonId}", Thread.CurrentThread.CurrentUICulture);
}
/// <summary>
/// Localize using the current thread culture
/// </summary>
+4 -1
View File
@@ -204,6 +204,7 @@
<Compile Include="CompositionExtensions_Essentials.cs" />
<Compile Include="CompositionExtensions_FileSystems.cs" />
<Compile Include="CompositionExtensions_Uniques.cs" />
<Compile Include="Exceptions\LockTimeoutException.cs" />
<Compile Include="FactoryExtensions.cs" />
<Compile Include="Composing\RegisterFactory.cs" />
<Compile Include="Composing\Current.cs" />
@@ -241,7 +242,9 @@
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\DefaultPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\RenamingPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\RichTextPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\DropDownFlexiblePreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\MergeDateAndDateTimePropertyEditor.cs" />
<Compile Include="Migrations\Upgrade\V_8_2_0\AddLockTableColumns.cs" />
<Compile Include="Models\Entities\EntityExtensions.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\PreValueMigratorBase.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\PreValueDto.cs" />
@@ -264,6 +267,7 @@
<Compile Include="Models\PublishedContent\ILivePublishedModelFactory.cs" />
<Compile Include="Models\PublishedContent\IPublishedContentType.cs" />
<Compile Include="Models\PublishedContent\IPublishedPropertyType.cs" />
<Compile Include="Persistence\Interceptors\LockTimeoutInterceptor.cs" />
<Compile Include="PropertyEditors\IIgnoreUserStartNodesConfig.cs" />
<Compile Include="PublishedContentExtensions.cs" />
<Compile Include="Models\PublishedContent\UrlMode.cs" />
@@ -978,7 +982,6 @@
<Compile Include="Persistence\DatabaseModelDefinitions\ModificationType.cs" />
<Compile Include="Persistence\DatabaseModelDefinitions\SystemMethods.cs" />
<Compile Include="Persistence\DatabaseModelDefinitions\TableDefinition.cs" />
<Compile Include="Persistence\DatabaseNodeLockExtensions.cs" />
<Compile Include="Persistence\DbCommandExtensions.cs" />
<Compile Include="Persistence\DbConnectionExtensions.cs" />
<Compile Include="Persistence\EntityNotFoundException.cs" />
+1 -1
View File
@@ -45,7 +45,7 @@ namespace Umbraco.Examine
continue;
case string strVal:
{
if (strVal.IsNullOrWhiteSpace()) return;
if (strVal.IsNullOrWhiteSpace()) continue;
var key = $"{keyVal.Key}{cultureSuffix}";
if (values.TryGetValue(key, out var v))
values[key] = new List<object>(v) { val }.ToArray();
+28 -1
View File
@@ -48,6 +48,31 @@ namespace Umbraco.Examine
}
}
/// <summary>
/// Returns all index fields that are culture specific (suffixed) or invariant
/// </summary>
/// <param name="index"></param>
/// <param name="culture"></param>
/// <returns></returns>
public static IEnumerable<string> GetCultureAndInvariantFields(this IUmbracoIndex index, string culture)
{
var allFields = index.GetFields();
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var field in allFields)
{
var match = CultureIsoCodeFieldNameMatchExpression.Match(field);
if (match.Success && match.Groups.Count == 3 && culture.InvariantEquals(match.Groups[2].Value))
{
yield return field; //matches this culture field
}
else if (!match.Success)
{
yield return field; //matches no culture field (invariant)
}
}
}
internal 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
@@ -77,7 +102,7 @@ namespace Umbraco.Examine
/// <remarks>
/// This is not thread safe, use with care
/// </remarks>
internal static void UnlockLuceneIndexes(this IExamineManager examineManager, ILogger logger)
internal static void ConfigureLuceneIndexes(this IExamineManager examineManager, ILogger logger, bool disableExamineIndexing)
{
foreach (var luceneIndexer in examineManager.Indexes.OfType<LuceneIndex>())
{
@@ -86,6 +111,8 @@ namespace Umbraco.Examine
//that could end up halting shutdown for a very long time causing overlapping appdomains and many other problems.
luceneIndexer.WaitForIndexQueueOnShutdown = false;
if (disableExamineIndexing) continue; //exit if not enabled, we don't need to unlock them if we're not maindom
//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
var dir = luceneIndexer.GetLuceneDirectory();
@@ -56,6 +56,8 @@ namespace Umbraco.Tests.Models
[TestCase("1,-1", "1", "1")] // was an issue
[TestCase("-1,1", "1", "1")] // was an issue
[TestCase("-1", "", "-1")]
[TestCase("", "-1", "-1")]
public void CombineStartNodes(string groupSn, string userSn, string expected)
{
+9 -8
View File
@@ -5,6 +5,7 @@ using System.Threading;
using NPoco;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Tests.TestHelpers;
@@ -37,7 +38,7 @@ namespace Umbraco.Tests.Persistence
{
using (var scope = ScopeProvider.CreateScope())
{
scope.Database.AcquireLockNodeReadLock(Constants.Locks.Servers);
scope.WriteLock(Constants.Locks.Reason.Default, Constants.Locks.Servers);
scope.Complete();
}
}
@@ -62,7 +63,7 @@ namespace Umbraco.Tests.Persistence
{
try
{
scope.Database.AcquireLockNodeReadLock(Constants.Locks.Servers);
scope.ReadLock(Constants.Locks.Servers);
lock (locker)
{
acquired++;
@@ -131,7 +132,7 @@ namespace Umbraco.Tests.Persistence
if (entered == threadCount) m1.Set();
}
ms[ic].WaitOne();
scope.Database.AcquireLockNodeWriteLock(Constants.Locks.Servers);
scope.WriteLock(Constants.Locks.Reason.Default,Constants.Locks.Servers);
lock (locker)
{
acquired++;
@@ -201,7 +202,7 @@ namespace Umbraco.Tests.Persistence
thread2.Join();
Assert.IsNotNull(e1);
Assert.IsInstanceOf<SqlCeLockTimeoutException>(e1);
Assert.IsInstanceOf<LockTimeoutException>(e1);
// the assertion below depends on timing conditions - on a fast enough environment,
// thread1 dies (deadlock) and frees thread2, which succeeds - however on a slow
@@ -210,7 +211,7 @@ namespace Umbraco.Tests.Persistence
//
//Assert.IsNull(e2);
if (e2 != null)
Assert.IsInstanceOf<SqlCeLockTimeoutException>(e2);
Assert.IsInstanceOf<LockTimeoutException>(e2);
}
private void DeadLockTestThread(int id1, int id2, EventWaitHandle myEv, WaitHandle otherEv, ref Exception exception)
@@ -221,7 +222,7 @@ namespace Umbraco.Tests.Persistence
{
otherEv.WaitOne();
Console.WriteLine($"[{id1}] WAIT {id1}");
scope.Database.AcquireLockNodeWriteLock(id1);
scope.WriteLock(Constants.Locks.Reason.Default,id1);
Console.WriteLine($"[{id1}] GRANT {id1}");
WriteLocks(scope.Database);
myEv.Set();
@@ -232,7 +233,7 @@ namespace Umbraco.Tests.Persistence
Thread.Sleep(200); // cannot wait due to deadlock... just give it a bit of time
Console.WriteLine($"[{id1}] WAIT {id2}");
scope.Database.AcquireLockNodeWriteLock(id2);
scope.WriteLock(Constants.Locks.Reason.Default,id2);
Console.WriteLine($"[{id1}] GRANT {id2}");
WriteLocks(scope.Database);
}
@@ -284,7 +285,7 @@ namespace Umbraco.Tests.Persistence
{
otherEv.WaitOne();
Console.WriteLine($"[{id}] WAIT {id}");
scope.Database.AcquireLockNodeWriteLock(id);
scope.WriteLock(Constants.Locks.Reason.Default, id);
Console.WriteLine($"[{id}] GRANT {id}");
WriteLocks(scope.Database);
myEv.Set();
@@ -0,0 +1,22 @@
using Lucene.Net.Store;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Umbraco.Tests.TestHelpers
{
/// <summary>
/// Used for tests with Lucene so that each RAM directory is unique
/// </summary>
public class RandomIdRAMDirectory : RAMDirectory
{
private readonly string _lockId = Guid.NewGuid().ToString();
public override string GetLockId()
{
return _lockId;
}
}
}
+2
View File
@@ -157,6 +157,7 @@
<Compile Include="Services\MemberGroupServiceTests.cs" />
<Compile Include="Services\MediaTypeServiceTests.cs" />
<Compile Include="Services\PropertyValidationServiceTests.cs" />
<Compile Include="TestHelpers\RandomIdRamDirectory.cs" />
<Compile Include="Testing\Objects\TestDataSource.cs" />
<Compile Include="Published\PublishedSnapshotTestObjects.cs" />
<Compile Include="Published\ModelTypeTests.cs" />
@@ -268,6 +269,7 @@
<Compile Include="Strings\StylesheetHelperTests.cs" />
<Compile Include="Strings\StringValidationTests.cs" />
<Compile Include="Web\Mvc\ValidateUmbracoFormRouteStringAttributeTests.cs" />
<Compile Include="Web\PublishedContentQueryTests.cs" />
<Compile Include="Web\UmbracoHelperTests.cs" />
<Compile Include="Membership\MembershipProviderBaseTests.cs" />
<Compile Include="Membership\UmbracoServiceMembershipProviderTests.cs" />
@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Examine;
using Examine.LuceneEngine.Providers;
using Lucene.Net.Store;
using Moq;
using NUnit.Framework;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Examine;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
namespace Umbraco.Tests.Web
{
[TestFixture]
public class PublishedContentQueryTests
{
private class TestIndex : LuceneIndex, IUmbracoIndex
{
private readonly string[] _fieldNames;
public TestIndex(string name, Directory luceneDirectory, string[] fieldNames)
: base(name, luceneDirectory, null, null, null, null)
{
_fieldNames = fieldNames;
}
public bool EnableDefaultEventHandler => throw new NotImplementedException();
public bool PublishedValuesOnly => throw new NotImplementedException();
public IEnumerable<string> GetFields() => _fieldNames;
}
private TestIndex CreateTestIndex(Directory luceneDirectory, string[] fieldNames)
{
var indexer = new TestIndex("TestIndex", luceneDirectory, fieldNames);
using (indexer.ProcessNonAsync())
{
//populate with some test data
indexer.IndexItem(new ValueSet("1", "content", new Dictionary<string, object>
{
[fieldNames[0]] = "Hello world, there are products here",
[UmbracoContentIndex.VariesByCultureFieldName] = "n"
}));
indexer.IndexItem(new ValueSet("2", "content", new Dictionary<string, object>
{
[fieldNames[1]] = "Hello world, there are products here",
[UmbracoContentIndex.VariesByCultureFieldName] = "y"
}));
indexer.IndexItem(new ValueSet("3", "content", new Dictionary<string, object>
{
[fieldNames[2]] = "Hello world, there are products here",
[UmbracoContentIndex.VariesByCultureFieldName] = "y"
}));
}
return indexer;
}
private PublishedContentQuery CreatePublishedContentQuery(IIndex indexer)
{
var examineManager = new Mock<IExamineManager>();
IIndex outarg = indexer;
examineManager.Setup(x => x.TryGetIndex("TestIndex", out outarg)).Returns(true);
var contentCache = new Mock<IPublishedContentCache>();
contentCache.Setup(x => x.GetById(It.IsAny<int>())).Returns((int intId) => Mock.Of<IPublishedContent>(x => x.Id == intId));
var snapshot = Mock.Of<IPublishedSnapshot>(x => x.Content == contentCache.Object);
var variationContext = new VariationContext();
var variationContextAccessor = Mock.Of<IVariationContextAccessor>(x => x.VariationContext == variationContext);
return new PublishedContentQuery(snapshot, variationContextAccessor, examineManager.Object);
}
[TestCase("fr-fr", ExpectedResult = "1, 3", TestName = "Search Culture: fr-fr. Must return both fr-fr and invariant results")]
[TestCase("en-us", ExpectedResult = "1, 2", TestName = "Search Culture: en-us. Must return both en-us and invariant results")]
[TestCase("*", ExpectedResult = "1, 2, 3", TestName = "Search Culture: *. Must return all cultures and all invariant results")]
[TestCase(null, ExpectedResult = "1", TestName = "Search Culture: null. Must return only invariant results")]
public string Search(string culture)
{
using (var luceneDir = new RandomIdRAMDirectory())
{
var fieldNames = new[] { "title", "title_en-us", "title_fr-fr" };
using (var indexer = CreateTestIndex(luceneDir, fieldNames))
{
var pcq = CreatePublishedContentQuery(indexer);
var results = pcq.Search("Products", culture, "TestIndex");
var ids = results.Select(x => x.Content.Id).ToArray();
return string.Join(", ", ids);
}
}
}
}
}
+7 -3
View File
@@ -1,5 +1,8 @@
using System;
using System.Text;
using Examine.LuceneEngine;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
@@ -13,18 +16,19 @@ using Umbraco.Web;
namespace Umbraco.Tests.Web
{
[TestFixture]
public class UmbracoHelperTests
{
{
[TearDown]
public void TearDown()
{
Current.Reset();
}
// ------- Int32 conversion tests
[Test]
public static void Converting_Boxed_34_To_An_Int_Returns_34()
+25 -44
View File
@@ -1222,7 +1222,7 @@
"arraybuffer.slice": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
"integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=",
"dev": true
},
"asap": {
@@ -1284,7 +1284,7 @@
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
"integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=",
"dev": true
},
"asynckit": {
@@ -1535,7 +1535,7 @@
},
"bl": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
"dev": true,
"requires": {
@@ -2516,7 +2516,7 @@
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=",
"dev": true
},
"continuable-cache": {
@@ -4045,7 +4045,7 @@
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
"dev": true,
"requires": {
"ms": "2.0.0"
@@ -4081,7 +4081,7 @@
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
"dev": true,
"requires": {
"ms": "2.0.0"
@@ -4476,7 +4476,7 @@
"eventemitter3": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
"integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==",
"integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=",
"dev": true
},
"exec-buffer": {
@@ -5309,8 +5309,7 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"aproba": {
"version": "1.2.0",
@@ -5331,14 +5330,12 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -5353,20 +5350,17 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"core-util-is": {
"version": "1.0.2",
@@ -5483,8 +5477,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"ini": {
"version": "1.3.5",
@@ -5496,7 +5489,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -5511,7 +5503,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -5519,14 +5510,12 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@@ -5545,7 +5534,6 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -5626,8 +5614,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"object-assign": {
"version": "4.1.1",
@@ -5639,7 +5626,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -5725,8 +5711,7 @@
"safe-buffer": {
"version": "5.1.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -5762,7 +5747,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -5782,7 +5766,6 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -5826,14 +5809,12 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"yallist": {
"version": "3.0.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
}
}
},
@@ -7575,7 +7556,7 @@
"has-binary2": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
"integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=",
"dev": true,
"requires": {
"isarray": "2.0.1"
@@ -7778,7 +7759,7 @@
"http-proxy": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz",
"integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==",
"integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=",
"dev": true,
"requires": {
"eventemitter3": "^3.0.0",
@@ -13616,7 +13597,7 @@
"qjobs": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
"integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
"integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=",
"dev": true
},
"qs": {
@@ -14787,7 +14768,7 @@
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
"dev": true,
"requires": {
"ms": "2.0.0"
@@ -15860,7 +15841,7 @@
"type-is": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
"integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=",
"dev": true,
"requires": {
"media-typer": "0.3.0",
@@ -15902,7 +15883,7 @@
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
"integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=",
"dev": true
},
"unc-path-regex": {
@@ -16505,7 +16486,7 @@
"ws": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
"integrity": "sha1-8c+E/i1ekB686U767OeF8YeiKPI=",
"dev": true,
"requires": {
"async-limiter": "~1.0.0",
@@ -94,6 +94,10 @@
content.apps[0].active = true;
$scope.appChanged(content.apps[0]);
}
// otherwise make sure the save options are up to date with the current content state
else {
createButtons($scope.content);
}
editorState.set(content);
@@ -101,14 +101,14 @@ angular.module('umbraco.directives')
var eventBindings = [];
function oneTimeClick(event) {
// ignore clicks on button groups toggles (i.e. the save and publish button)
var parents = $(event.target).closest("[data-element='button-group-toggle']");
if (parents.length > 0) {
return;
}
var el = event.target.nodeName;
//ignore link and button clicks
var els = ["INPUT", "A", "BUTTON"];
if (els.indexOf(el) >= 0) { return; }
// ignore clicks on new overlay
parents = $(event.target).parents(".umb-overlay,.umb-tour");
var parents = $(event.target).parents("a,button,.umb-overlay,.umb-tour");
if (parents.length > 0) {
return;
}
@@ -131,12 +131,6 @@ angular.module('umbraco.directives')
return;
}
// ignore clicks on dialog actions
var actions = $(event.target).parents(".umb-action");
if (actions.length === 1) {
return;
}
//ignore clicks inside this element
if ($(element).has($(event.target)).length > 0) {
return;
@@ -3,7 +3,7 @@
* @name umbraco.directives.directive:umbTree
* @restrict E
**/
function umbTreeDirective($q, $rootScope, treeService, notificationsService, userService) {
function umbTreeDirective($q, $rootScope, treeService, notificationsService, userService, $timeout) {
return {
restrict: 'E',
@@ -234,6 +234,8 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use
/** Method to load in the tree data */
function loadTree() {
if ($scope.section) {
//default args
@@ -243,33 +245,52 @@ function umbTreeDirective($q, $rootScope, treeService, notificationsService, use
if ($scope.customtreeparams) {
args["queryString"] = $scope.customtreeparams;
}
return treeService.getTree(args)
.then(function (data) {
//Only use the tree data, if we are still on the correct section
if(data.alias !== $scope.section){
return $q.reject();
}
//set the data once we have it
$scope.tree = data;
//set the root as the current active tree
$scope.activeTree = $scope.tree.root;
emitEvent("treeLoaded", { tree: $scope.tree });
emitEvent("treeNodeExpanded", { tree: $scope.tree, node: $scope.tree.root, children: $scope.tree.root.children });
return $q.when(data);
}, function (reason) {
notificationsService.error("Tree Error", reason);
return $q.reject(reason);
});
$scope.isLoading = true;
$scope.isLoadingReason = "";
return getTree(args);
}
else {
return $q.reject();
}
}
function getTree(args){
return treeService.getTree(args)
.then(function (data) {
//Only use the tree data, if we are still on the correct section
if(data.alias !== $scope.section){
return $q.reject();
}
//set the data once we have it
$scope.tree = data;
//set the root as the current active tree
$scope.activeTree = $scope.tree.root;
emitEvent("treeLoaded", { tree: $scope.tree });
emitEvent("treeNodeExpanded", { tree: $scope.tree, node: $scope.tree.root, children: $scope.tree.root.children });
$scope.isLoading = false;
$scope.isLoadingReason = "";
return $q.when(data);
}, function (reason) {
if(reason.status === 409){
$scope.isLoading = true;
$scope.isLoadingReason = reason.data;
$timeout(function() { $q.when(getTree(args));}, 5000);
}else{
notificationsService.error("Tree Error", reason);
return $q.reject(reason);
}
});
}
function loadChildren(node, forceReload) {
//emit treeNodeExpanding event, if a callback object is set on the tree
@@ -4,7 +4,7 @@
@restrict A
@description
Use this directive make an element sticky and follow the page when scrolling.
Use this directive make an element sticky and follow the page when scrolling. `umb-sticky-bar--active` class is applied when the element is stuck
<h3>Markup example</h3>
<pre>
@@ -12,140 +12,102 @@ Use this directive make an element sticky and follow the page when scrolling.
<div
class="my-sticky-bar"
umb-sticky-bar
scrollable-container=".container">
umb-sticky-bar>
</div>
</div>
</pre>
<h3>CSS example</h3>
<pre>
.my-sticky-bar {
padding: 15px 0;
background: #000000;
position: relative;
top: 0;
}
.my-sticky-bar.-umb-sticky-bar {
top: 100px;
}
</pre>
@param {string} scrollableContainer Set the class (".element") or the id ("#element") of the scrollable container element.
**/
(function () {
'use strict';
function StickyBarDirective($rootScope) {
function StickyBarDirective() {
/**
On initial load, the intersector fires if the grid editor is in the viewport
This flag is used to suppress the setClass behaviour on the initial load
**/
var initial = true;
/**
Toggle `umb-sticky-bar--active` class on the sticky-bar element
**/
function setClass(addClass, current) {
if (!initial) {
current.classList.toggle('umb-sticky-bar--active', addClass);
} else {
initial = false;
}
}
/**
Inserts two elements in the umbStickyBar parent element
These are used by the IntersectionObserve to calculate scroll position
**/
function addSentinels(current) {
['-top', '-bottom'].forEach(s => {
const sentinel = document.createElement('div');
sentinel.classList.add('umb-sticky-sentinel', s);
current.parentElement.appendChild(sentinel);
});
}
/**
Calls into setClass when the footer sentinel enters/exits the bottom of the container
Container is the parent element of the umbStickyBar element
**/
function observeFooter(current, container) {
const observer = new IntersectionObserver((records, observer) => {
let [target, rootBounds, intersected] = [records[0].boundingClientRect, records[0].rootBounds, records[0].intersectionRatio === 1];
if (target.bottom > rootBounds.top && intersected) {
setClass(true, current);
}
if (target.top < rootBounds.top && target.bottom < rootBounds.bottom) {
setClass(false, current);
}
}, {
threshold: [1],
root: container
});
observer.observe(current.parentElement.querySelector('.umb-sticky-sentinel.-bottom'));
}
/**
Calls into setClass when the header sentinel enters/exits the top of the container
Container is the parent element of the umbStickyBar element
**/
function observeHeader(current, container) {
const observer = new IntersectionObserver((records, observer) => {
let [target, rootBounds] = [records[0].boundingClientRect, records[0].rootBounds];
if (target.bottom < rootBounds.top) {
setClass(true, current);
}
if (target.bottom >= rootBounds.top && target.bottom < rootBounds.bottom) {
setClass(false, current);
}
}, {
threshold: [0],
root: container
});
observer.observe(current.parentElement.querySelector('.umb-sticky-sentinel.-top'));
}
function link(scope, el, attr, ctrl) {
var bar = $(el);
var scrollableContainer = null;
var clonedBar = null;
var cloneIsMade = false;
let current = el[0];
let container = current.closest('[data-element="editor-container"]');
function activate() {
if (bar.parents(".umb-property").length > 1) {
bar.addClass("nested");
return;
}
if (attr.scrollableContainer) {
scrollableContainer = bar.closest(attr.scrollableContainer);
} else {
scrollableContainer = $(window);
}
scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll");
$(window).on('resize.umbStickyBar', determineVisibility);
scope.$on('$destroy', function () {
scrollableContainer.off('.umbStickyBar');
$(window).off('.umbStickyBar');
});
}
function determineVisibility() {
var barTop = bar[0].offsetTop;
var scrollTop = scrollableContainer.scrollTop();
if (scrollTop > barTop) {
if (!cloneIsMade) {
createClone();
clonedBar.css({
'visibility': 'visible'
});
} else {
calculateSize();
}
} else {
if (cloneIsMade) {
//remove cloned element (switched places with original on creation)
bar.remove();
bar = clonedBar;
clonedBar = null;
bar.removeClass('-umb-sticky-bar');
bar.css({
position: 'relative',
'width': 'auto',
'height': 'auto',
'z-index': 'auto',
'visibility': 'visible'
});
cloneIsMade = false;
}
}
}
function calculateSize() {
var width = bar.innerWidth();
clonedBar.css({
width: width + 10 // + 10 (5*2) because we need to add border to avoid seeing the shadow beneath. Look at the CSS.
});
}
function createClone() {
//switch place with cloned element, to keep binding intact
clonedBar = bar;
bar = clonedBar.clone();
clonedBar.after(bar);
clonedBar.addClass('-umb-sticky-bar');
clonedBar.css({
'position': 'fixed',
// if you change this z-index value, make sure the sticky editor sub headers do not
// clash with umb-dropdown (e.g. the content actions dropdown in content list view)
'z-index': 99,
'visibility': 'hidden'
});
cloneIsMade = true;
calculateSize();
}
activate();
addSentinels(current);
observeHeader(current, container);
observeFooter(current, container);
}
var directive = {
@@ -172,7 +172,7 @@ function entityResource($q, $http, umbRequestHelper) {
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetUrlAndAnchors",
{ id: id })),
[{ id: id }])),
'Failed to retrieve url and anchors data for id ' + id);
},
@@ -32,15 +32,32 @@
border-radius: 3px;
}
.umb-editor-sub-header.-umb-sticky-bar {
box-shadow: 0 6px 3px -3px rgba(0,0,0,.16);
[umb-sticky-bar] {
transition: box-shadow 240ms;
margin-top: 0;
margin-top: 0;
margin-bottom: 0;
top: calc(@appHeaderHeight + @editorHeaderHeight);
position:sticky;
z-index: 99;
.umb-editor--infinityMode & {
top: calc(@editorHeaderHeight);
&.umb-sticky-bar--active {
box-shadow: 0 6px 3px -3px rgba(0,0,0,.16);
}
}
.umb-sticky-sentinel {
position: absolute;
left: 0;
width: 100%;
pointer-events: none;
&.-top {
top:0px;
height:1px;
}
&.-bottom {
bottom:50px;
height:10px;
}
}
@@ -5,6 +5,7 @@
// --------------------------------------------------
.umb-property-editor {
width: 100%;
position:relative;
}
.umb-property-editor-tiny {
@@ -165,8 +166,6 @@
.sp-replacer {
display: inline-flex;
margin-right: 18px;
border: solid 1px @gray-8;
border-radius: 3px;
}
label {
@@ -59,10 +59,10 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
if (dialogOptions.currentTarget) {
// clone the current target so we don't accidentally update the caller's model while manipulating $scope.model.target
$scope.model.target = angular.copy(dialogOptions.currentTarget);
//if we have a node ID, we fetch the current node to build the form data
// if we have a node ID, we fetch the current node to build the form data
if ($scope.model.target.id || $scope.model.target.udi) {
//will be either a udi or an int
// will be either a udi or an int
var id = $scope.model.target.udi ? $scope.model.target.udi : $scope.model.target.id;
if ($scope.model.target.udi) {
@@ -1,6 +1,5 @@
<div
class="umb-editor-sub-header umb-editor-sub-header--{{appearance}}"
umb-sticky-bar
scrollable-container=".umb-editor-container"
ng-transclude>
</div>
@@ -1,53 +1,62 @@
<ul class="umb-tree" ng-class="{'hide-options': hideoptions === 'true'}">
<div>
<div ng-if="isLoading">
<umb-load-indicator></umb-load-indicator>
<div style="position: absolute; top: 50%; padding-top: 30px; text-align: center; width: 76%; padding-left: 13%;">{{isLoadingReason}}</div>
</div>
<li ng-if="!tree.root.containsGroups">
<div class="umb-tree-root" data-element="tree-root" ng-class="getNodeCssClass(tree.root)" ng-hide="hideheader === 'true'" on-right-click="altSelect(tree.root, $event)">
<h5>
<a ng-href="#/{{section}}" ng-click="select(tree.root, $event)" class="umb-tree-root-link umb-outline" data-element="tree-root-link">
<i ng-if="enablecheckboxes === 'true'" ng-class="selectEnabledNodeClass(tree.root)"></i>
{{tree.name}}
</a>
</h5>
<button data-element="tree-item-options" class="umb-options btn-reset sr-only sr-only--focusable sr-only--hoverable" ng-hide="tree.root.isContainer || !tree.root.menuUrl" ng-click="options(tree.root, $event)" ng-swipe-right="options(tree.root, $event)">
<i></i><i></i><i></i>
</button>
</div>
<umb-tree-item class="umb-animated"
ng-repeat="child in tree.root.children"
enablelistviewexpand="{{enablelistviewexpand}}"
node="child"
current-node="currentNode"
tree="this"
is-dialog="isdialog"
section="{{section}}">
</umb-tree-item>
</li>
<div ng-if="!isLoading">
<ul class="umb-tree" ng-class="{'hide-options': hideoptions === 'true'}">
<!-- REPEAT for each group in the tree -->
<li ng-if="tree.root.containsGroups"
ng-repeat="group in tree.root.children">
<li ng-if="!tree.root.containsGroups">
<div class="umb-tree-root" data-element="tree-root" ng-class="getNodeCssClass(tree.root)" ng-hide="hideheader === 'true'" on-right-click="altSelect(tree.root, $event)">
<h5>
<a ng-href="#/{{section}}" ng-click="select(tree.root, $event)" class="umb-tree-root-link umb-outline" data-element="tree-root-link">
<i ng-if="enablecheckboxes === 'true'" ng-class="selectEnabledNodeClass(tree.root)"></i>
{{tree.name}}
</a>
</h5>
<button data-element="tree-item-options" class="umb-options btn-reset sr-only sr-only--focusable sr-only--hoverable" ng-hide="tree.root.isContainer || !tree.root.menuUrl" ng-click="options(tree.root, $event)" ng-swipe-right="options(tree.root, $event)">
<i></i><i></i><i></i>
</button>
</div>
<umb-tree-item class="umb-animated"
ng-repeat="child in tree.root.children"
enablelistviewexpand="{{enablelistviewexpand}}"
node="child"
current-node="currentNode"
tree="this"
is-dialog="isdialog"
section="{{section}}">
</umb-tree-item>
</li>
<div class="umb-tree-root" data-element="tree-root" ng-class="getNodeCssClass(group)" ng-hide="hideheader === 'true'" on-right-click="altSelect(group, $event)">
<h5>
<a ng-href="#/{{section}}" ng-click="select(group, $event)" class="umb-tree-root-link umb-outline" data-element="tree-root-link">
<i ng-if="enablecheckboxes === 'true'" ng-class="selectEnabledNodeClass(group)"></i>
{{group.name}}
</a>
</h5>
<button data-element="tree-item-options" class="umb-options umb-outline btn-reset sr-only sr-only--focusable sr-only--hoverable" ng-hide="group.isContainer || !group.menuUrl" ng-click="options(group, $event)" ng-swipe-right="options(group, $event)">
<i></i><i></i><i></i>
</button>
</div>
<!-- REPEAT for each group in the tree -->
<li ng-if="tree.root.containsGroups"
ng-repeat="group in tree.root.children">
<umb-tree-item class="umb-animated"
ng-repeat="child in group.children"
enablelistviewexpand="{{enablelistviewexpand}}"
node="child"
current-node="currentNode"
tree="this"
is-dialog="isdialog"
section="{{section}}">
</umb-tree-item>
</li>
<!-- END REPEAT -->
</ul>
<div class="umb-tree-root" data-element="tree-root" ng-class="getNodeCssClass(group)" ng-hide="hideheader === 'true'" on-right-click="altSelect(group, $event)">
<h5>
<a ng-href="#/{{section}}" ng-click="select(group, $event)" class="umb-tree-root-link umb-outline" data-element="tree-root-link">
<i ng-if="enablecheckboxes === 'true'" ng-class="selectEnabledNodeClass(group)"></i>
{{group.name}}
</a>
</h5>
<button data-element="tree-item-options" class="umb-options umb-outline btn-reset sr-only sr-only--focusable sr-only--hoverable" ng-hide="group.isContainer || !group.menuUrl" ng-click="options(group, $event)" ng-swipe-right="options(group, $event)">
<i></i><i></i><i></i>
</button>
</div>
<umb-tree-item class="umb-animated"
ng-repeat="child in group.children"
enablelistviewexpand="{{enablelistviewexpand}}"
node="child"
current-node="currentNode"
tree="this"
is-dialog="isdialog"
section="{{section}}">
</umb-tree-item>
</li>
<!-- END REPEAT -->
</ul>
</div>
</div>
@@ -10,11 +10,12 @@
vm.numberOfErrors = 0;
vm.commonLogMessages = [];
vm.commonLogMessagesCount = 10;
vm.dateRangeLabel = "";
// ChartJS Options - for count/overview of log distribution
vm.logTypeLabels = ["Info", "Debug", "Warning", "Error", "Critical"];
vm.logTypeLabels = ["Debug", "Info", "Warning", "Error", "Fatal"];
vm.logTypeData = [0, 0, 0, 0, 0];
vm.logTypeColors = [ '#dcdcdc', '#97bbcd', '#46bfbd', '#fdb45c', '#f7464a'];
vm.logTypeColors = ['#eaddd5', '#2bc37c', '#3544b1', '#ff9412', '#d42054'];
vm.chartOptions = {
legend: {
display: true,
@@ -23,35 +24,42 @@
};
let querystring = $location.search();
if(querystring.startDate){
if (querystring.startDate) {
vm.startDate = querystring.startDate;
}else{
vm.dateRangeLabel = getDateRangeLabel("Selected Time Period");
} else {
vm.startDate = new Date(Date.now());
vm.startDate.setDate(vm.startDate.getDate()-1);
vm.startDate.setDate(vm.startDate.getDate() - 1);
vm.startDate = vm.startDate.toIsoDateString();
vm.dateRangeLabel = getDateRangeLabel("Today");
}
if(querystring.endDate){
if (querystring.endDate) {
vm.endDate = querystring.endDate;
}else{
if (querystring.endDate === querystring.startDate) {
vm.dateRangeLabel = getDateRangeLabel("Selected Date");
}
} else {
vm.endDate = new Date(Date.now()).toIsoDateString();
}
vm.period = [vm.startDate, vm.endDate];
vm.period = [vm.startDate, vm.endDate];
//functions
vm.searchLogQuery = searchLogQuery;
vm.findMessageTemplate = findMessageTemplate;
vm.searchErrors = searchErrors;
function preFlightCheck(){
vm.loading = true;
//Do our pre-flight check (to see if we can view logs)
//IE the log file is NOT too big such as 1GB & crash the site
logViewerResource.canViewLogs(vm.startDate, vm.endDate).then(function(result){
logViewerResource.canViewLogs(vm.startDate, vm.endDate).then(function (result) {
vm.loading = false;
vm.canLoadLogs = result;
if(result){
if (result) {
//Can view logs - so initalise
init();
}
@@ -62,39 +70,39 @@
function init() {
vm.loading = true;
var savedSearches = logViewerResource.getSavedSearches().then(function (data) {
vm.searches = data;
},
// fallback to some defaults if error from API response
function () {
vm.searches = [
{
"name": "Find all logs where the Level is NOT Verbose and NOT Debug",
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
vm.searches = data;
},
// fallback to some defaults if error from API response
function () {
vm.searches = [
{
"name": "Find all logs where the Level is NOT Verbose and NOT Debug",
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
},
{
"name": "Find all logs that has an exception property (Warning, Error & Critical with Exceptions)",
"query": "Has(@Exception)"
{
"name": "Find all logs that has an exception property (Warning, Error & Fatal with Exceptions)",
"query": "Has(@Exception)"
},
{
"name": "Find all logs that have the property 'Duration'",
"query": "Has(Duration)"
{
"name": "Find all logs that have the property 'Duration'",
"query": "Has(Duration)"
},
{
"name": "Find all logs that have the property 'Duration' and the duration is greater than 1000ms",
"query": "Has(Duration) and Duration > 1000"
{
"name": "Find all logs that have the property 'Duration' and the duration is greater than 1000ms",
"query": "Has(Duration) and Duration > 1000"
},
{
"name": "Find all logs that are from the namespace 'Umbraco.Core'",
"query": "StartsWith(SourceContext, 'Umbraco.Core')"
{
"name": "Find all logs that are from the namespace 'Umbraco.Core'",
"query": "StartsWith(SourceContext, 'Umbraco.Core')"
},
{
"name": "Find all logs that use a specific log message template",
"query": "@MessageTemplate = '[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)'"
{
"name": "Find all logs that use a specific log message template",
"query": "@MessageTemplate = '[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)'"
}
]
});
});
var numOfErrors = logViewerResource.getNumberOfErrors(vm.startDate, vm.endDate).then(function (data) {
vm.numberOfErrors = data;
@@ -102,41 +110,56 @@
var logCounts = logViewerResource.getLogLevelCounts(vm.startDate, vm.endDate).then(function (data) {
vm.logTypeData = [];
vm.logTypeData.push(data.Information);
vm.logTypeData.push(data.Debug);
vm.logTypeData.push(data.Warning);
vm.logTypeData.push(data.Error);
vm.logTypeData.push(data.Fatal);
for (let [key, value] of Object.entries(data)) {
const index = vm.logTypeLabels.findIndex(x => key.startsWith(x));
if (index > -1) {
vm.logTypeData[index] = value;
}
}
});
var commonMsgs = logViewerResource.getMessageTemplates(vm.startDate, vm.endDate).then(function(data){
var commonMsgs = logViewerResource.getMessageTemplates(vm.startDate, vm.endDate).then(function (data) {
vm.commonLogMessages = data;
});
//Set loading indicatior to false when these 3 queries complete
$q.all([savedSearches, numOfErrors, logCounts, commonMsgs]).then(function(data) {
//Set loading indicator to false when these 3 queries complete
$q.all([savedSearches, numOfErrors, logCounts, commonMsgs]).then(function () {
vm.loading = false;
});
$timeout(function () {
navigationService.syncTree({ tree: "logViewer", path: "-1" });
navigationService.syncTree({
tree: "logViewer",
path: "-1"
});
});
}
function searchLogQuery(logQuery){
$location.path("/settings/logViewer/search").search({lq: logQuery, startDate: vm.startDate, endDate: vm.endDate});
function searchLogQuery(logQuery) {
$location.path("/settings/logViewer/search").search({
lq: logQuery,
startDate: vm.startDate,
endDate: vm.endDate
});
}
function findMessageTemplate(template){
function findMessageTemplate(template) {
var logQuery = "@MessageTemplate='" + template.MessageTemplate + "'";
searchLogQuery(logQuery);
}
function getDateRangeLabel(suffix) {
return "Log Overview for " + suffix;
}
function searchErrors(){
var logQuery = "@Level='Fatal' or @Level='Error' or Has(@Exception)";
searchLogQuery(logQuery);
}
preFlightCheck();
/////////////////////
vm.config = {
@@ -147,20 +170,21 @@
maxDate: "today",
conjunction: " to "
};
vm.dateRangeChange = function(selectedDates, dateStr, instance) {
if(selectedDates.length > 0){
vm.startDate = selectedDates[0].toIsoDateString();
vm.endDate = selectedDates[selectedDates.length-1].toIsoDateString(); // Take the last date as end
if(vm.startDate === vm.endDate){
vm.period = [vm.startDate];
}else{
vm.period = [vm.startDate, vm.endDate];
}
preFlightCheck();
vm.dateRangeChange = function (selectedDates, dateStr, instance) {
if (selectedDates.length > 0) {
// Update view by re-requesting route with updated querystring.
// By doing this we make sure the URL matches the selected time period, aiding sharing the link.
// Also resolves a minor layout issue where the " to " conjunction between the selected dates
// is collapsed to a comma.
const startDate = selectedDates[0].toIsoDateString();
const endDate = selectedDates[selectedDates.length - 1].toIsoDateString(); // Take the last date as end
$location.path("/settings/logViewer/overview").search({
startDate: startDate,
endDate: endDate
});
}
}
@@ -3,7 +3,7 @@
<umb-editor-view footer="false">
<umb-editor-header
name="'Log Overview for Today'"
name="vm.dateRangeLabel"
name-locked="true"
hide-icon="true"
hide-description="true"
@@ -74,17 +74,16 @@
<umb-box>
<umb-box-header title="Time Period"></umb-box-header>
<umb-flatpickr
class="datepicker"
ng-model="vm.period"
options="vm.config"
on-close="vm.dateRangeChange(selectedDates, dateStr, instance)">
<umb-flatpickr class="datepicker"
ng-model="vm.period"
options="vm.config"
on-close="vm.dateRangeChange(selectedDates, dateStr, instance)">
</umb-flatpickr>
</umb-box>
<div ng-show=" vm.canLoadLogs">
<!-- No of Errors -->
<umb-box ng-click="vm.searchLogQuery('Has(@Exception)')" style="cursor:pointer;">
<umb-box ng-click="vm.searchErrors()" style="cursor:pointer;">
<umb-box-header title="Number of Errors"></umb-box-header>
<umb-box-content class="block-form" style="font-size: 40px; font-weight:900; text-align:center; color:#fe6561;">
{{ vm.numberOfErrors }}
@@ -11,26 +11,28 @@
vm.showBackButton = true;
vm.page = {};
// this array is also used to map the logTypeColor param onto the log items
// in setLogTypeColors()
vm.logLevels = [
{
name: 'Verbose',
logTypeColor: 'gray'
logTypeColor: ''
},
{
name: 'Debug',
logTypeColor: 'secondary'
logTypeColor: 'gray'
},
{
name: 'Information',
logTypeColor: 'primary'
logTypeColor: 'success'
},
{
name: 'Warning',
logTypeColor: 'warning'
logTypeColor: 'primary'
},
{
name: 'Error',
logTypeColor: 'danger'
logTypeColor: 'warning'
},
{
name: 'Fatal',
@@ -118,7 +120,7 @@
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
},
{
"name": "Find all logs that has an exception property (Warning, Error & Critical with Exceptions)",
"name": "Find all logs that has an exception property (Warning, Error & Fatal with Exceptions)",
"query": "Has(@Exception)"
},
{
@@ -173,25 +175,8 @@
}
function setLogTypeColor(logItems) {
angular.forEach(logItems, function (log) {
switch (log.Level) {
case "Information":
log.logTypeColor = "primary";
break;
case "Debug":
log.logTypeColor = "secondary";
break;
case "Warning":
log.logTypeColor = "warning";
break;
case "Fatal":
case "Error":
log.logTypeColor = "danger";
break;
default:
log.logTypeColor = "gray";
}
});
logItems.forEach(logItem =>
logItem.logTypeColor = vm.logLevels.find(x => x.name === logItem.Level).logTypeColor);
}
function getFilterName(array) {
+2 -2
View File
@@ -345,9 +345,9 @@
<WebProjectProperties>
<UseIIS>False</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>8110</DevelopmentServerPort>
<DevelopmentServerPort>8200</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:8110</IISUrl>
<IISUrl>http://localhost:8200</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
+16 -6
View File
@@ -263,7 +263,7 @@
<key alias="scheduledPublishDocumentation"><![CDATA[<a href="https://our.umbraco.com/documentation/Getting-Started/Data/Scheduled-Publishing/#timezones" target="_blank">Hvad betyder det?</a>]]></key>
<key alias="nestedContentDeleteItem">Er du sikker på, at du vil slette dette element?</key>
<key alias="nestedContentEditorNotSupported">Egenskaben %0% anvender editoren %1% som ikke er understøttet af Nested Content.</key>
<key alias="nestedContentNoContentTypes">Der er ikke konfigureret nogen indholdstyper for denne egenskab.</key>
<key alias="nestedContentNoContentTypes">Der er ikke konfigureret nogen indholdstyper for denne egenskab.</key>
<key alias="addTextBox">Tilføj en ny tekstboks</key>
<key alias="removeTextBox">Fjern denne tekstboks</key>
<key alias="contentRoot">Indholdsrod</key>
@@ -284,8 +284,8 @@
<key alias="readyToPublish">Klar til at udgive?</key>
<key alias="readyToSave">Klar til at gemme?</key>
<key alias="sendForApproval">Send til godkendelse</key>
<key alias="schedulePublishHelp">Vælg dato og klokkeslæt for at udgive og/eller afpublicere indholdet.</key>
<key alias="createEmpty">Opret ny</key>
<key alias="schedulePublishHelp">Vælg dato og klokkeslæt for at udgive og/eller afpublicere indholdet.</key>
<key alias="createEmpty">Opret ny</key>
<key alias="createFromClipboard">Indsæt fra udklipsmappen</key>
</area>
<area alias="blueprints">
@@ -1571,8 +1571,8 @@ Mange hilsner fra Umbraco robotten
<area alias="emptyStates">
<key alias="emptyDictionaryTree">Ingen ordbog elementer at vælge imellem</key>
</area>
<area alias="textbox">
<key alias="characters_left"><![CDATA[<strong>%0%</strong> tegn tilbage.]]></key>
<area alias="textbox">
<key alias="characters_left"><![CDATA[<strong>%0%</strong> tegn tilbage.]]></key>
<key alias="characters_exceed"><![CDATA[Maksimum %0% tegn, <strong>%1%</strong> for mange.]]></key>
</area>
<area alias="recycleBin">
@@ -1606,8 +1606,18 @@ Mange hilsner fra Umbraco robotten
<key alias="settingsPublishedStatus">Published Cache</key>
<key alias="settingsModelsBuilder">Models Builder</key>
<key alias="settingsHealthCheck">Health Check</key>
<key alias="settingsProfiler">Profiling</key>
<key alias="settingsProfiler">Profiling</key>
<key alias="memberIntro">Kom godt i gang</key>
<key alias="formsInstall">Installer Umbraco Forms</key>
</area>
<area alias="writeLockReasons">
<key alias="Code_1">Langvaring operation</key>
<key alias="Code_10000">Flytter indhold til papirkurven</key>
<key alias="Code_10001">Genskaber indhold fra papirkurven</key>
<key alias="Code_10002">Sletter indhold fra papirkurven</key>
<key alias="Code_10003">Flytter indhold</key>
<key alias="Code_10004">Kopier indhold</key>
<key alias="Code_10005">Sorterer indhold</key>
<key alias="Code_10006">Sletter alt indhold af en type</key>
</area>
</language>
+14 -4
View File
@@ -334,7 +334,7 @@
<key alias="noDocumentTypesEditPermissions">Edit permissions for this document type</key>
<key alias="noMediaTypes" version="7.0"><![CDATA[There are no allowed media types available for creating media here. You must enable these in <strong>Media Types Types</strong> within the <strong>Settings</strong> section, by editing the <strong>Allowed child node types</strong> under <strong>Permissions</strong>.]]></key>
<key alias="noMediaTypesWithNoSettingsAccess">The selected media in the tree doesn't allow for any other media to be created below it.</key>
<key alias="noMediaTypesEditPermissions">Edit permissions for this media type</key>
<key alias="noMediaTypesEditPermissions">Edit permissions for this media type</key>
<key alias="documentTypeWithoutTemplate">Document Type without a template</key>
<key alias="newFolder">New folder</key>
<key alias="newDataType">New data type</key>
@@ -1130,7 +1130,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="cancelAndUploadAnother">Cancel and upload another package</key>
<key alias="accept">I accept</key>
<key alias="termsOfUse">terms of use</key>
<key alias="pathToFile">Path to file</key>
<key alias="pathToFileDescription">Absolute path to file (ie: /bin/umbraco.bin)</key>
<key alias="installed">Installed</key>
@@ -2121,8 +2121,18 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="settingsPublishedStatus">Published Status</key>
<key alias="settingsModelsBuilder">Models Builder</key>
<key alias="settingsHealthCheck">Health Check</key>
<key alias="settingsProfiler">Profiling</key>
<key alias="settingsProfiler">Profiling</key>
<key alias="memberIntro">Getting Started</key>
<key alias="formsInstall">Install Umbraco Forms</key>
</area>>
</area>
<area alias="writeLockReasons">
<key alias="Code_1">Langvaring operation</key>
<key alias="Code_10000">Move content to recycle bin</key>
<key alias="Code_10001">Restores content from recycle bin</key>
<key alias="Code_10002">Deletes content from recycle bin</key>
<key alias="Code_10003">Moves content</key>
<key alias="Code_10004">Copying content</key>
<key alias="Code_10005">Sort content</key>
<key alias="Code_10006">Deletes all content of a given type</key>
</area>
</language>
@@ -2135,8 +2135,18 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="settingsPublishedStatus">Published Status</key>
<key alias="settingsModelsBuilder">Models Builder</key>
<key alias="settingsHealthCheck">Health Check</key>
<key alias="settingsProfiler">Profiling</key>
<key alias="settingsProfiler">Profiling</key>
<key alias="memberIntro">Getting Started</key>
<key alias="formsInstall">Install Umbraco Forms</key>
</area>
<area alias="writeLockReasons">
<key alias="Code_1">Langvaring operation</key>
<key alias="Code_10000">Move content to recycle bin</key>
<key alias="Code_10001">Restores content from recycle bin</key>
<key alias="Code_10002">Deletes content from recycle bin</key>
<key alias="Code_10003">Moves content</key>
<key alias="Code_10004">Copying content</key>
<key alias="Code_10005">Sort content</key>
<key alias="Code_10006">Deletes all content of a given type</key>
</area>
</language>
@@ -4,7 +4,7 @@
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
},
{
"name": "Find all logs that has an exception property (Warning, Error & Critical with Exceptions)",
"name": "Find all logs that has an exception property (Warning, Error & Fatal with Exceptions)",
"query": "Has(@Exception)"
},
{
@@ -26,12 +26,13 @@
<add key="serilog:write-to:File.restrictedToMinimumLevel" value="Debug" />
<add key="serilog:write-to:File.retainedFileCountLimit" value="32" />--> <!-- Number of log files to keep (or remove value to keep all files) -->
<!--<add key="serilog:write-to:File.rollingInterval" value="Day" />--> <!-- Create a new log file every Minute/Hour/Day/Month/Year/infinite -->
<!--<add key="serilog:write-to:File.outputTemplate" value="{Timestamp:yyyy-MM-dd HH:mm:ss,fff} [P{ProcessId}/D{AppDomainId}/T{ThreadId}] {Log4NetLevel} {SourceContext} - {Message:lj}{NewLine}{Exception}" /> -->
<!-- Filters all above sink's to use this expression -->
<!-- Common use case is to include SourceType starting with your own namespace -->
<!--
<add key="serilog:using:FilterExpressions" value="Serilog.Filters.Expressions" />
<add key="serilog:filter:ByIncluding.expression" value="StartsWith(SourceContext, 'Umbraco.Core')" />
<add key="serilog:filter:ByIncludingOnly.expression" value="StartsWith(SourceContext, 'Umbraco.Core')" />
-->
</appSettings>
+11 -1
View File
@@ -74,6 +74,7 @@ namespace Umbraco.Web.Editors
//id is passed in eventually we'll probably want to support GUID + Udi too
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetPagedChildren", "id", typeof(int), typeof(string)),
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetPath", "id", typeof(int), typeof(Guid), typeof(Udi)),
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetUrlAndAnchors", "id", typeof(int), typeof(Guid), typeof(Udi)),
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi)),
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetByIds", "ids", typeof(int[]), typeof(Guid[]), typeof(Udi[]))));
}
@@ -288,7 +289,16 @@ namespace Umbraco.Web.Editors
}
[HttpGet]
public UrlAndAnchors GetUrlAndAnchors([FromUri]int id, [FromUri]string culture = "*")
public UrlAndAnchors GetUrlAndAnchors(Udi id, string culture = "*")
{
var intId = Services.EntityService.GetId(id);
if (!intId.Success)
throw new HttpResponseException(HttpStatusCode.NotFound);
return GetUrlAndAnchors(intId.Result, culture);
}
[HttpGet]
public UrlAndAnchors GetUrlAndAnchors(int id, string culture = "*")
{
var url = UmbracoContext.UrlProvider.GetUrl(id);
var anchorValues = Services.ContentService.GetAnchorValuesFromRTEs(id, culture);
@@ -85,7 +85,7 @@ namespace Umbraco.Web.Editors
};
}
/// <summary>
/// Check if the index has been rebuilt
@@ -250,7 +250,7 @@ namespace Umbraco.Web.Editors
private void Indexer_IndexOperationComplete(object sender, EventArgs e)
{
var indexer = (LuceneIndex)sender;
var indexer = (IIndex)sender;
_logger.Debug<ExamineManagementController>("Logging operation completed for index {IndexName}", indexer.Name);
@@ -259,7 +259,7 @@ namespace Umbraco.Web.Editors
_logger
.Info<ExamineManagementController
>($"Rebuilding index '{indexer.Name}' done, {indexer.CommitCount} items committed (can differ from the number of items in the index)");
>($"Rebuilding index '{indexer.Name}' done.");
var cacheKey = "temp_indexing_op_" + indexer.Name;
_runtimeCache.Clear(cacheKey);
@@ -15,11 +15,11 @@ namespace Umbraco.Web.Editors
[PluginController("UmbracoApi")]
public class LogViewerController : UmbracoAuthorizedJsonController
{
private ILogViewer _logViewer;
private readonly ILogViewer _logViewer;
public LogViewerController(ILogViewer logViewer)
{
_logViewer = logViewer;
_logViewer = logViewer ?? throw new ArgumentNullException(nameof(logViewer));
}
private bool CanViewLogs(LogTimePeriod logTimePeriod)
@@ -91,8 +91,6 @@ namespace Umbraco.Web.Editors
var direction = orderDirection == "Descending" ? Direction.Descending : Direction.Ascending;
return _logViewer.GetLogs(logTimePeriod, filterExpression: filterExpression, pageNumber: pageNumber, orderDirection: direction, logLevels: logLevels);
}
@@ -223,7 +223,6 @@ namespace Umbraco.Web
_method = method;
_controllerName = controllerName;
_encryptedString = UrlHelperRenderExtensions.CreateEncryptedRouteString(controllerName, controllerAction, area, additionalRouteVals);
}
+12 -4
View File
@@ -39,10 +39,14 @@ namespace Umbraco.Web
/// <param name="culture">Optional culture.</param>
/// <param name="indexName">Optional index name.</param>
/// <remarks>
/// <para>When the <paramref name="culture"/> is not specified, all cultures are searched.</para>
/// <para>
/// When the <paramref name="culture"/> is not specified or is *, all cultures are searched.
/// To search for only invariant documents and fields use null.
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents.
/// </para>
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
/// </remarks>
IEnumerable<PublishedSearchResult> Search(string term, string culture = null, string indexName = null);
IEnumerable<PublishedSearchResult> Search(string term, string culture = "*", string indexName = null);
/// <summary>
/// Searches content.
@@ -54,10 +58,14 @@ namespace Umbraco.Web
/// <param name="culture">Optional culture.</param>
/// <param name="indexName">Optional index name.</param>
/// <remarks>
/// <para>When the <paramref name="culture"/> is not specified, all cultures are searched.</para>
/// <para>
/// When the <paramref name="culture"/> is not specified or is *, all cultures are searched.
/// To search for only invariant documents and fields use null.
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents.
/// </para>
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
/// </remarks>
IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = null, string indexName = null);
IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = null);
/// <summary>
/// Executes the query and converts the results to PublishedSearchResult.
@@ -13,7 +13,7 @@ namespace Umbraco.Web.Media.TypeDetector
{
document = XDocument.Load(fileStream);
}
catch (System.Exception ex)
catch (System.Exception)
{
return false;
}
+1 -11
View File
@@ -12,7 +12,7 @@ namespace Umbraco.Web.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.2.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -50,15 +50,5 @@ namespace Umbraco.Web.Properties {
return ((string)(this["test"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.WebServiceUrl)]
[global::System.Configuration.DefaultSettingValueAttribute("https://our.umbraco.com/umbraco/webservices/api/repository.asmx")]
public string umbraco_org_umbraco_our_Repository {
get {
return ((string)(this["umbraco_org_umbraco_our_Repository"]));
}
}
}
}
@@ -11,8 +11,5 @@
<Setting Name="test" Type="System.String" Scope="Application">
<Value Profile="(Default)">Something</Value>
</Setting>
<Setting Name="umbraco_org_umbraco_our_Repository" Type="(Web Service URL)" Scope="Application">
<Value Profile="(Default)">https://our.umbraco.com/umbraco/webservices/api/repository.asmx</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Newtonsoft.Json.Linq;
@@ -38,9 +39,24 @@ namespace Umbraco.Web.PropertyEditors
/// <inheritdoc />
public override object FromEditor(ContentPropertyData editorValue, object currentValue)
{
return editorValue.Value is JArray json
? json.Select(x => x.Value<string>())
: null;
var value = editorValue?.Value?.ToString();
if (string.IsNullOrEmpty(value))
{
return null;
}
if (editorValue.Value is JArray json)
{
return json.Select(x => x.Value<string>());
}
if (string.IsNullOrWhiteSpace(value) == false)
{
return value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
}
return null;
}
/// <inheritdoc />
+28 -12
View File
@@ -20,14 +20,22 @@ namespace Umbraco.Web
{
private readonly IPublishedSnapshot _publishedSnapshot;
private readonly IVariationContextAccessor _variationContextAccessor;
private readonly IExamineManager _examineManager;
[Obsolete("Use the constructor with all parameters instead")]
public PublishedContentQuery(IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor)
: this (publishedSnapshot, variationContextAccessor, ExamineManager.Instance)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PublishedContentQuery"/> class.
/// </summary>
public PublishedContentQuery(IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor)
public PublishedContentQuery(IPublishedSnapshot publishedSnapshot, IVariationContextAccessor variationContextAccessor, IExamineManager examineManager)
{
_publishedSnapshot = publishedSnapshot ?? throw new ArgumentNullException(nameof(publishedSnapshot));
_variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor));
_examineManager = examineManager ?? throw new ArgumentNullException(nameof(examineManager));
}
#region Content
@@ -175,19 +183,19 @@ namespace Umbraco.Web
#region Search
/// <inheritdoc />
public IEnumerable<PublishedSearchResult> Search(string term, string culture = null, string indexName = null)
public IEnumerable<PublishedSearchResult> Search(string term, string culture = "*", string indexName = null)
{
return Search(term, 0, 0, out _, culture, indexName);
}
/// <inheritdoc />
public IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = null, string indexName = null)
public IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = null)
{
indexName = string.IsNullOrEmpty(indexName)
? Constants.UmbracoIndexes.ExternalIndexName
: indexName;
if (!ExamineManager.Instance.TryGetIndex(indexName, out var index) || !(index is IUmbracoIndex umbIndex))
if (!_examineManager.TryGetIndex(indexName, out var index) || !(index is IUmbracoIndex umbIndex))
throw new InvalidOperationException($"No index found by name {indexName} or is not of type {typeof(IUmbracoIndex)}");
var searcher = umbIndex.GetSearcher();
@@ -195,20 +203,28 @@ namespace Umbraco.Web
// default to max 500 results
var count = skip == 0 && take == 0 ? 500 : skip + take;
//set this to the specific culture or to the culture in the request
culture = culture ?? _variationContextAccessor.VariationContext.Culture;
ISearchResults results;
if (culture.IsNullOrWhiteSpace())
if (culture == "*")
{
//search everything
results = searcher.Search(term, count);
}
else if (culture.IsNullOrWhiteSpace())
{
//only search invariant
var qry = searcher.CreateQuery().Field(UmbracoContentIndex.VariesByCultureFieldName, "n"); //must not vary by culture
qry = qry.And().ManagedQuery(term);
results = qry.Execute(count);
}
else
{
//search only the specified culture
//get all index fields suffixed with the culture name supplied
var cultureFields = umbIndex.GetCultureFields(culture);
var qry = searcher.CreateQuery().Field(UmbracoContentIndex.VariesByCultureFieldName, "y"); //must vary by culture
qry = qry.And().ManagedQuery(term, cultureFields.ToArray());
var cultureFields = umbIndex.GetCultureAndInvariantFields(culture).ToArray();
var qry = searcher.CreateQuery().ManagedQuery(term, cultureFields);
results = qry.Execute(count);
}
@@ -304,7 +320,7 @@ namespace Umbraco.Web
}
}
#endregion
+19 -19
View File
@@ -30,8 +30,9 @@ namespace Umbraco.Web.Search
private readonly IValueSetBuilder<IMedia> _mediaValueSetBuilder;
private readonly IValueSetBuilder<IMember> _memberValueSetBuilder;
private static bool _disableExamineIndexing = false;
private static volatile bool _isConfigured = false;
private static readonly object IsConfiguredLocker = new object();
private static bool _isConfigured = false;
private static object _configuredInit = null;
private static object _isConfiguredLocker = new object();
private readonly IScopeProvider _scopeProvider;
private readonly ServiceContext _services;
private static BackgroundTaskRunner<IBackgroundTask> _rebuildOnStartupRunner;
@@ -91,7 +92,7 @@ namespace Umbraco.Web.Search
if (!examineShutdownRegistered)
{
_logger.Debug<ExamineComponent>("Examine shutdown not registered, this AppDomain is not the MainDom, Examine will be disabled");
_logger.Info<ExamineComponent>("Examine shutdown not registered, this AppDomain is not the MainDom, Examine will be disabled");
//if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled!
Suspendable.ExamineEvents.SuspendIndexers(_logger);
@@ -120,7 +121,7 @@ namespace Umbraco.Web.Search
MediaCacheRefresher.CacheUpdated += MediaCacheRefresherUpdated;
MemberCacheRefresher.CacheUpdated += MemberCacheRefresherUpdated;
EnsureUnlocked(_logger, _examineManager);
ConfigureIndexes(_logger, _examineManager);
// TODO: Instead of waiting 5000 ms, we could add an event handler on to fulfilling the first request, then start?
RebuildIndexes(_indexRebuilder, _logger, true, 5000);
@@ -161,25 +162,24 @@ namespace Umbraco.Web.Search
}
/// <summary>
/// Must be called to each index is unlocked before any indexing occurs
/// Called on startup to configure each index.
/// </summary>
/// <remarks>
/// 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.
/// Configuring also ensure the indexes are not locked.
/// </remarks>
private static void EnsureUnlocked(ILogger logger, IExamineManager examineManager)
private static void ConfigureIndexes(ILogger logger, IExamineManager examineManager)
{
if (_disableExamineIndexing) return;
if (_isConfigured) return;
lock (IsConfiguredLocker)
{
//double check
if (_isConfigured) return;
_isConfigured = true;
examineManager.UnlockLuceneIndexes(logger);
}
LazyInitializer.EnsureInitialized(
ref _configuredInit,
ref _isConfigured,
ref _isConfiguredLocker,
() =>
{
examineManager.ConfigureLuceneIndexes(logger, _disableExamineIndexing);
return null;
});
}
#region Cache refresher updated event handlers
@@ -737,7 +737,7 @@ namespace Umbraco.Web.Search
{
var strId = id.ToString(CultureInfo.InvariantCulture);
foreach (var index in examineComponent._examineManager.Indexes.OfType<IUmbracoIndex>()
.Where(x => (keepIfUnpublished && !x.PublishedValuesOnly) || !keepIfUnpublished)
.Where(x => x.PublishedValuesOnly || !keepIfUnpublished)
.Where(x => x.EnableDefaultEventHandler))
{
index.DeleteFromIndex(strId);
@@ -800,7 +800,7 @@ namespace Umbraco.Web.Search
if (_waitMilliseconds > 0)
Thread.Sleep(_waitMilliseconds);
EnsureUnlocked(_logger, _indexRebuilder.ExamineManager);
ConfigureIndexes(_logger, _indexRebuilder.ExamineManager);
_indexRebuilder.RebuildIndexes(_onlyEmptyIndexes);
}
}
+1 -1
View File
@@ -542,7 +542,7 @@ namespace Umbraco.Web.Security
}
/// <summary>
/// Returns the login status model of the currently logged in member, if no member is logged in it returns null;
/// Returns the login status model of the currently logged in member.
/// </summary>
/// <returns></returns>
public virtual LoginStatusModel GetCurrentLoginStatus()
+1 -22
View File
@@ -321,6 +321,7 @@
<Compile Include="WebApi\Filters\HttpQueryStringModelBinder.cs" />
<Compile Include="WebApi\HttpActionContextExtensions.cs" />
<Compile Include="Models\ContentEditing\IContentSave.cs" />
<Compile Include="WebApi\LockTimeoutExceptionHandlerAttribute.cs" />
<Compile Include="WebApi\SerializeVersionAttribute.cs" />
<Compile Include="WebApi\TrimModelBinder.cs" />
<Compile Include="Editors\CodeFileController.cs" />
@@ -906,11 +907,6 @@
<Compile Include="JavaScript\CssInitialization.cs" />
<Compile Include="JavaScript\DependencyPathRenderer.cs" />
<Compile Include="UmbracoComponentRenderer.cs" />
<Compile Include="Web References\org.umbraco.our\Reference.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Reference.map</DependentUpon>
</Compile>
<Compile Include="WebApi\AngularJsonMediaTypeFormatter.cs" />
<Compile Include="WebApi\AngularJsonOnlyConfigurationAttribute.cs" />
<Compile Include="Editors\Binders\MemberBinder.cs" />
@@ -1220,12 +1216,6 @@
<None Include="..\Umbraco.Web.UI\Views\web.config">
<Link>Mvc\web.config</Link>
</None>
<None Include="Web References\org.umbraco.our\Reference.map">
<Generator>MSDiscoCodeGenerator</Generator>
<LastGenOutput>Reference.cs</LastGenOutput>
</None>
<None Include="Web References\org.umbraco.our\repository.disco" />
<None Include="Web References\org.umbraco.our\repository.wsdl" />
<None Include="Web References\org.umbraco.update\UpgradeResult.datasource">
<DependentUpon>Reference.map</DependentUpon>
</None>
@@ -1238,17 +1228,6 @@
</None>
</ItemGroup>
<ItemGroup>
<WebReferenceUrl Include="http://our.umbraco.org/umbraco/webservices/api/repository.asmx">
<UrlBehavior>Dynamic</UrlBehavior>
<RelPath>Web References\org.umbraco.our\</RelPath>
<UpdateFromURL>http://our.umbraco.org/umbraco/webservices/api/repository.asmx</UpdateFromURL>
<ServiceLocationURL>
</ServiceLocationURL>
<CachedDynamicPropName>
</CachedDynamicPropName>
<CachedAppSettingsObjectName>Settings</CachedAppSettingsObjectName>
<CachedSettingsPropName>umbraco_org_umbraco_our_Repository</CachedSettingsPropName>
</WebReferenceUrl>
<WebReferenceUrl Include="http://update.umbraco.org/checkforupgrade.asmx">
<UrlBehavior>Dynamic</UrlBehavior>
<RelPath>Web References\org.umbraco.update\</RelPath>
+8
View File
@@ -53,7 +53,15 @@ namespace Umbraco.Web
{
// make sure we have a variation context
if (_variationContextAccessor.VariationContext == null)
{
// TODO: By using _defaultCultureAccessor.DefaultCulture this means that the VariationContext will always return a variant culture, it will never
// return an empty string signifying that the culture is invariant. But does this matter? Are we actually expecting this to return an empty string
// for invariant routes? From what i can tell throughout the codebase is that whenever we are checking against the VariationContext.Culture we are
// also checking if the content type varies by culture or not. This is fine, however the code in the ctor of VariationContext is then misleading
// since it's assuming that the Culture can be empty (invariant) when in reality of a website this will never be empty since a real culture is always set here.
_variationContextAccessor.VariationContext = new VariationContext(_defaultCultureAccessor.DefaultCulture);
}
var webSecurity = new WebSecurity(httpContext, _userService, _globalSettings);
File diff suppressed because it is too large Load Diff
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<DiscoveryClientResultsFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Results>
<DiscoveryClientResult referenceType="System.Web.Services.Discovery.ContractReference" url="https://our.umbraco.com/umbraco/webservices/api/repository.asmx?wsdl" filename="repository.wsdl" />
<DiscoveryClientResult referenceType="System.Web.Services.Discovery.DiscoveryDocumentReference" url="https://our.umbraco.com/umbraco/webservices/api/repository.asmx?disco" filename="repository.disco" />
</Results>
</DiscoveryClientResultsFile>
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<discovery xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/disco/">
<contractRef ref="https://our.umbraco.com/umbraco/webservices/api/repository.asmx?wsdl" docRef="https://our.umbraco.com/umbraco/webservices/api/repository.asmx" xmlns="http://schemas.xmlsoap.org/disco/scl/" />
<soap address="https://our.umbraco.com/umbraco/webservices/api/repository.asmx" xmlns:q1="http://packages.umbraco.org/webservices/" binding="q1:RepositorySoap" xmlns="http://schemas.xmlsoap.org/disco/soap/" />
<soap address="https://our.umbraco.com/umbraco/webservices/api/repository.asmx" xmlns:q2="http://packages.umbraco.org/webservices/" binding="q2:RepositorySoap12" xmlns="http://schemas.xmlsoap.org/disco/soap/" />
</discovery>
@@ -1,995 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://packages.umbraco.org/webservices/" xmlns:s1="http://microsoft.com/wsdl/types/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s2="http://packages.umbraco.org/webservices/AbstractTypes" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" targetNamespace="http://packages.umbraco.org/webservices/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://packages.umbraco.org/webservices/">
<s:import namespace="http://microsoft.com/wsdl/types/" />
<s:element name="Categories">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="repositoryGuid" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="CategoriesResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="CategoriesResult" type="tns:ArrayOfCategory" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="ArrayOfCategory">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="Category" nillable="true" type="tns:Category" />
</s:sequence>
</s:complexType>
<s:complexType name="Category">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="Text" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Description" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Url" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="Id" type="s:int" />
<s:element minOccurs="0" maxOccurs="1" name="Packages" type="tns:ArrayOfPackage" />
</s:sequence>
</s:complexType>
<s:complexType name="ArrayOfPackage">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="Package" nillable="true" type="tns:Package" />
</s:sequence>
</s:complexType>
<s:complexType name="Package">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="RepoGuid" type="s1:guid" />
<s:element minOccurs="0" maxOccurs="1" name="Text" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Description" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Icon" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Thumbnail" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Documentation" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Demo" type="s:string" />
<s:element minOccurs="1" maxOccurs="1" name="Accepted" type="s:boolean" />
<s:element minOccurs="1" maxOccurs="1" name="IsModule" type="s:boolean" />
<s:element minOccurs="1" maxOccurs="1" name="EditorsPick" type="s:boolean" />
<s:element minOccurs="1" maxOccurs="1" name="Protected" type="s:boolean" />
<s:element minOccurs="1" maxOccurs="1" name="HasUpgrade" type="s:boolean" />
<s:element minOccurs="0" maxOccurs="1" name="UpgradeVersion" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="UpgradeReadMe" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="Url" type="s:string" />
</s:sequence>
</s:complexType>
<s:element name="Modules">
<s:complexType />
</s:element>
<s:element name="ModulesResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="ModulesResult" type="tns:ArrayOfPackage" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="ModulesCategorized">
<s:complexType />
</s:element>
<s:element name="ModulesCategorizedResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="ModulesCategorizedResult" type="tns:ArrayOfCategory" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="Nitros">
<s:complexType />
</s:element>
<s:element name="NitrosResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="NitrosResult" type="tns:ArrayOfPackage" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="NitrosCategorized">
<s:complexType />
</s:element>
<s:element name="NitrosCategorizedResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="NitrosCategorizedResult" type="tns:ArrayOfCategory" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="authenticate">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="email" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="md5Password" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="authenticateResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="authenticateResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="fetchPackage">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="packageGuid" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="fetchPackageResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="fetchPackageResult" type="s:base64Binary" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="fetchPackageByVersion">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="packageGuid" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="repoVersion" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="fetchPackageByVersionResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="fetchPackageByVersionResult" type="s:base64Binary" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="fetchProtectedPackage">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="packageGuid" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="memberKey" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="fetchProtectedPackageResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="fetchProtectedPackageResult" type="s:base64Binary" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="SubmitPackage">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="repositoryGuid" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="authorGuid" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="packageGuid" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="packageFile" type="s:base64Binary" />
<s:element minOccurs="0" maxOccurs="1" name="packageDoc" type="s:base64Binary" />
<s:element minOccurs="0" maxOccurs="1" name="packageThumbnail" type="s:base64Binary" />
<s:element minOccurs="0" maxOccurs="1" name="name" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="author" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="authorUrl" type="s:string" />
<s:element minOccurs="0" maxOccurs="1" name="description" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="SubmitPackageResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="SubmitPackageResult" type="tns:SubmitStatus" />
</s:sequence>
</s:complexType>
</s:element>
<s:simpleType name="SubmitStatus">
<s:restriction base="s:string">
<s:enumeration value="Complete" />
<s:enumeration value="Exists" />
<s:enumeration value="NoAccess" />
<s:enumeration value="Error" />
</s:restriction>
</s:simpleType>
<s:element name="PackageByGuid">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="packageGuid" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="PackageByGuidResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="PackageByGuidResult" type="tns:Package" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="ArrayOfCategory" nillable="true" type="tns:ArrayOfCategory" />
<s:element name="ArrayOfPackage" nillable="true" type="tns:ArrayOfPackage" />
<s:element name="string" nillable="true" type="s:string" />
<s:element name="base64Binary" nillable="true" type="s:base64Binary" />
<s:element name="SubmitStatus" type="tns:SubmitStatus" />
<s:element name="Package" nillable="true" type="tns:Package" />
</s:schema>
<s:schema elementFormDefault="qualified" targetNamespace="http://microsoft.com/wsdl/types/">
<s:simpleType name="guid">
<s:restriction base="s:string">
<s:pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" />
</s:restriction>
</s:simpleType>
</s:schema>
<s:schema targetNamespace="http://packages.umbraco.org/webservices/AbstractTypes">
<s:import namespace="http://schemas.xmlsoap.org/soap/encoding/" />
<s:complexType name="StringArray">
<s:complexContent mixed="false">
<s:restriction base="soapenc:Array">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="String" type="s:string" />
</s:sequence>
</s:restriction>
</s:complexContent>
</s:complexType>
</s:schema>
</wsdl:types>
<wsdl:message name="CategoriesSoapIn">
<wsdl:part name="parameters" element="tns:Categories" />
</wsdl:message>
<wsdl:message name="CategoriesSoapOut">
<wsdl:part name="parameters" element="tns:CategoriesResponse" />
</wsdl:message>
<wsdl:message name="ModulesSoapIn">
<wsdl:part name="parameters" element="tns:Modules" />
</wsdl:message>
<wsdl:message name="ModulesSoapOut">
<wsdl:part name="parameters" element="tns:ModulesResponse" />
</wsdl:message>
<wsdl:message name="ModulesCategorizedSoapIn">
<wsdl:part name="parameters" element="tns:ModulesCategorized" />
</wsdl:message>
<wsdl:message name="ModulesCategorizedSoapOut">
<wsdl:part name="parameters" element="tns:ModulesCategorizedResponse" />
</wsdl:message>
<wsdl:message name="NitrosSoapIn">
<wsdl:part name="parameters" element="tns:Nitros" />
</wsdl:message>
<wsdl:message name="NitrosSoapOut">
<wsdl:part name="parameters" element="tns:NitrosResponse" />
</wsdl:message>
<wsdl:message name="NitrosCategorizedSoapIn">
<wsdl:part name="parameters" element="tns:NitrosCategorized" />
</wsdl:message>
<wsdl:message name="NitrosCategorizedSoapOut">
<wsdl:part name="parameters" element="tns:NitrosCategorizedResponse" />
</wsdl:message>
<wsdl:message name="authenticateSoapIn">
<wsdl:part name="parameters" element="tns:authenticate" />
</wsdl:message>
<wsdl:message name="authenticateSoapOut">
<wsdl:part name="parameters" element="tns:authenticateResponse" />
</wsdl:message>
<wsdl:message name="fetchPackageSoapIn">
<wsdl:part name="parameters" element="tns:fetchPackage" />
</wsdl:message>
<wsdl:message name="fetchPackageSoapOut">
<wsdl:part name="parameters" element="tns:fetchPackageResponse" />
</wsdl:message>
<wsdl:message name="fetchPackageByVersionSoapIn">
<wsdl:part name="parameters" element="tns:fetchPackageByVersion" />
</wsdl:message>
<wsdl:message name="fetchPackageByVersionSoapOut">
<wsdl:part name="parameters" element="tns:fetchPackageByVersionResponse" />
</wsdl:message>
<wsdl:message name="fetchProtectedPackageSoapIn">
<wsdl:part name="parameters" element="tns:fetchProtectedPackage" />
</wsdl:message>
<wsdl:message name="fetchProtectedPackageSoapOut">
<wsdl:part name="parameters" element="tns:fetchProtectedPackageResponse" />
</wsdl:message>
<wsdl:message name="SubmitPackageSoapIn">
<wsdl:part name="parameters" element="tns:SubmitPackage" />
</wsdl:message>
<wsdl:message name="SubmitPackageSoapOut">
<wsdl:part name="parameters" element="tns:SubmitPackageResponse" />
</wsdl:message>
<wsdl:message name="PackageByGuidSoapIn">
<wsdl:part name="parameters" element="tns:PackageByGuid" />
</wsdl:message>
<wsdl:message name="PackageByGuidSoapOut">
<wsdl:part name="parameters" element="tns:PackageByGuidResponse" />
</wsdl:message>
<wsdl:message name="CategoriesHttpGetIn">
<wsdl:part name="repositoryGuid" type="s:string" />
</wsdl:message>
<wsdl:message name="CategoriesHttpGetOut">
<wsdl:part name="Body" element="tns:ArrayOfCategory" />
</wsdl:message>
<wsdl:message name="ModulesHttpGetIn" />
<wsdl:message name="ModulesHttpGetOut">
<wsdl:part name="Body" element="tns:ArrayOfPackage" />
</wsdl:message>
<wsdl:message name="ModulesCategorizedHttpGetIn" />
<wsdl:message name="ModulesCategorizedHttpGetOut">
<wsdl:part name="Body" element="tns:ArrayOfCategory" />
</wsdl:message>
<wsdl:message name="NitrosHttpGetIn" />
<wsdl:message name="NitrosHttpGetOut">
<wsdl:part name="Body" element="tns:ArrayOfPackage" />
</wsdl:message>
<wsdl:message name="NitrosCategorizedHttpGetIn" />
<wsdl:message name="NitrosCategorizedHttpGetOut">
<wsdl:part name="Body" element="tns:ArrayOfCategory" />
</wsdl:message>
<wsdl:message name="authenticateHttpGetIn">
<wsdl:part name="email" type="s:string" />
<wsdl:part name="md5Password" type="s:string" />
</wsdl:message>
<wsdl:message name="authenticateHttpGetOut">
<wsdl:part name="Body" element="tns:string" />
</wsdl:message>
<wsdl:message name="fetchPackageHttpGetIn">
<wsdl:part name="packageGuid" type="s:string" />
</wsdl:message>
<wsdl:message name="fetchPackageHttpGetOut">
<wsdl:part name="Body" element="tns:base64Binary" />
</wsdl:message>
<wsdl:message name="fetchPackageByVersionHttpGetIn">
<wsdl:part name="packageGuid" type="s:string" />
<wsdl:part name="repoVersion" type="s:string" />
</wsdl:message>
<wsdl:message name="fetchPackageByVersionHttpGetOut">
<wsdl:part name="Body" element="tns:base64Binary" />
</wsdl:message>
<wsdl:message name="fetchProtectedPackageHttpGetIn">
<wsdl:part name="packageGuid" type="s:string" />
<wsdl:part name="memberKey" type="s:string" />
</wsdl:message>
<wsdl:message name="fetchProtectedPackageHttpGetOut">
<wsdl:part name="Body" element="tns:base64Binary" />
</wsdl:message>
<wsdl:message name="SubmitPackageHttpGetIn">
<wsdl:part name="repositoryGuid" type="s:string" />
<wsdl:part name="authorGuid" type="s:string" />
<wsdl:part name="packageGuid" type="s:string" />
<wsdl:part name="packageFile" type="s2:StringArray" />
<wsdl:part name="packageDoc" type="s2:StringArray" />
<wsdl:part name="packageThumbnail" type="s2:StringArray" />
<wsdl:part name="name" type="s:string" />
<wsdl:part name="author" type="s:string" />
<wsdl:part name="authorUrl" type="s:string" />
<wsdl:part name="description" type="s:string" />
</wsdl:message>
<wsdl:message name="SubmitPackageHttpGetOut">
<wsdl:part name="Body" element="tns:SubmitStatus" />
</wsdl:message>
<wsdl:message name="PackageByGuidHttpGetIn">
<wsdl:part name="packageGuid" type="s:string" />
</wsdl:message>
<wsdl:message name="PackageByGuidHttpGetOut">
<wsdl:part name="Body" element="tns:Package" />
</wsdl:message>
<wsdl:message name="CategoriesHttpPostIn">
<wsdl:part name="repositoryGuid" type="s:string" />
</wsdl:message>
<wsdl:message name="CategoriesHttpPostOut">
<wsdl:part name="Body" element="tns:ArrayOfCategory" />
</wsdl:message>
<wsdl:message name="ModulesHttpPostIn" />
<wsdl:message name="ModulesHttpPostOut">
<wsdl:part name="Body" element="tns:ArrayOfPackage" />
</wsdl:message>
<wsdl:message name="ModulesCategorizedHttpPostIn" />
<wsdl:message name="ModulesCategorizedHttpPostOut">
<wsdl:part name="Body" element="tns:ArrayOfCategory" />
</wsdl:message>
<wsdl:message name="NitrosHttpPostIn" />
<wsdl:message name="NitrosHttpPostOut">
<wsdl:part name="Body" element="tns:ArrayOfPackage" />
</wsdl:message>
<wsdl:message name="NitrosCategorizedHttpPostIn" />
<wsdl:message name="NitrosCategorizedHttpPostOut">
<wsdl:part name="Body" element="tns:ArrayOfCategory" />
</wsdl:message>
<wsdl:message name="authenticateHttpPostIn">
<wsdl:part name="email" type="s:string" />
<wsdl:part name="md5Password" type="s:string" />
</wsdl:message>
<wsdl:message name="authenticateHttpPostOut">
<wsdl:part name="Body" element="tns:string" />
</wsdl:message>
<wsdl:message name="fetchPackageHttpPostIn">
<wsdl:part name="packageGuid" type="s:string" />
</wsdl:message>
<wsdl:message name="fetchPackageHttpPostOut">
<wsdl:part name="Body" element="tns:base64Binary" />
</wsdl:message>
<wsdl:message name="fetchPackageByVersionHttpPostIn">
<wsdl:part name="packageGuid" type="s:string" />
<wsdl:part name="repoVersion" type="s:string" />
</wsdl:message>
<wsdl:message name="fetchPackageByVersionHttpPostOut">
<wsdl:part name="Body" element="tns:base64Binary" />
</wsdl:message>
<wsdl:message name="fetchProtectedPackageHttpPostIn">
<wsdl:part name="packageGuid" type="s:string" />
<wsdl:part name="memberKey" type="s:string" />
</wsdl:message>
<wsdl:message name="fetchProtectedPackageHttpPostOut">
<wsdl:part name="Body" element="tns:base64Binary" />
</wsdl:message>
<wsdl:message name="SubmitPackageHttpPostIn">
<wsdl:part name="repositoryGuid" type="s:string" />
<wsdl:part name="authorGuid" type="s:string" />
<wsdl:part name="packageGuid" type="s:string" />
<wsdl:part name="packageFile" type="s2:StringArray" />
<wsdl:part name="packageDoc" type="s2:StringArray" />
<wsdl:part name="packageThumbnail" type="s2:StringArray" />
<wsdl:part name="name" type="s:string" />
<wsdl:part name="author" type="s:string" />
<wsdl:part name="authorUrl" type="s:string" />
<wsdl:part name="description" type="s:string" />
</wsdl:message>
<wsdl:message name="SubmitPackageHttpPostOut">
<wsdl:part name="Body" element="tns:SubmitStatus" />
</wsdl:message>
<wsdl:message name="PackageByGuidHttpPostIn">
<wsdl:part name="packageGuid" type="s:string" />
</wsdl:message>
<wsdl:message name="PackageByGuidHttpPostOut">
<wsdl:part name="Body" element="tns:Package" />
</wsdl:message>
<wsdl:portType name="RepositorySoap">
<wsdl:operation name="Categories">
<wsdl:input message="tns:CategoriesSoapIn" />
<wsdl:output message="tns:CategoriesSoapOut" />
</wsdl:operation>
<wsdl:operation name="Modules">
<wsdl:input message="tns:ModulesSoapIn" />
<wsdl:output message="tns:ModulesSoapOut" />
</wsdl:operation>
<wsdl:operation name="ModulesCategorized">
<wsdl:input message="tns:ModulesCategorizedSoapIn" />
<wsdl:output message="tns:ModulesCategorizedSoapOut" />
</wsdl:operation>
<wsdl:operation name="Nitros">
<wsdl:input message="tns:NitrosSoapIn" />
<wsdl:output message="tns:NitrosSoapOut" />
</wsdl:operation>
<wsdl:operation name="NitrosCategorized">
<wsdl:input message="tns:NitrosCategorizedSoapIn" />
<wsdl:output message="tns:NitrosCategorizedSoapOut" />
</wsdl:operation>
<wsdl:operation name="authenticate">
<wsdl:input message="tns:authenticateSoapIn" />
<wsdl:output message="tns:authenticateSoapOut" />
</wsdl:operation>
<wsdl:operation name="fetchPackage">
<wsdl:input message="tns:fetchPackageSoapIn" />
<wsdl:output message="tns:fetchPackageSoapOut" />
</wsdl:operation>
<wsdl:operation name="fetchPackageByVersion">
<wsdl:input message="tns:fetchPackageByVersionSoapIn" />
<wsdl:output message="tns:fetchPackageByVersionSoapOut" />
</wsdl:operation>
<wsdl:operation name="fetchProtectedPackage">
<wsdl:input message="tns:fetchProtectedPackageSoapIn" />
<wsdl:output message="tns:fetchProtectedPackageSoapOut" />
</wsdl:operation>
<wsdl:operation name="SubmitPackage">
<wsdl:input message="tns:SubmitPackageSoapIn" />
<wsdl:output message="tns:SubmitPackageSoapOut" />
</wsdl:operation>
<wsdl:operation name="PackageByGuid">
<wsdl:input message="tns:PackageByGuidSoapIn" />
<wsdl:output message="tns:PackageByGuidSoapOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:portType name="RepositoryHttpGet">
<wsdl:operation name="Categories">
<wsdl:input message="tns:CategoriesHttpGetIn" />
<wsdl:output message="tns:CategoriesHttpGetOut" />
</wsdl:operation>
<wsdl:operation name="Modules">
<wsdl:input message="tns:ModulesHttpGetIn" />
<wsdl:output message="tns:ModulesHttpGetOut" />
</wsdl:operation>
<wsdl:operation name="ModulesCategorized">
<wsdl:input message="tns:ModulesCategorizedHttpGetIn" />
<wsdl:output message="tns:ModulesCategorizedHttpGetOut" />
</wsdl:operation>
<wsdl:operation name="Nitros">
<wsdl:input message="tns:NitrosHttpGetIn" />
<wsdl:output message="tns:NitrosHttpGetOut" />
</wsdl:operation>
<wsdl:operation name="NitrosCategorized">
<wsdl:input message="tns:NitrosCategorizedHttpGetIn" />
<wsdl:output message="tns:NitrosCategorizedHttpGetOut" />
</wsdl:operation>
<wsdl:operation name="authenticate">
<wsdl:input message="tns:authenticateHttpGetIn" />
<wsdl:output message="tns:authenticateHttpGetOut" />
</wsdl:operation>
<wsdl:operation name="fetchPackage">
<wsdl:input message="tns:fetchPackageHttpGetIn" />
<wsdl:output message="tns:fetchPackageHttpGetOut" />
</wsdl:operation>
<wsdl:operation name="fetchPackageByVersion">
<wsdl:input message="tns:fetchPackageByVersionHttpGetIn" />
<wsdl:output message="tns:fetchPackageByVersionHttpGetOut" />
</wsdl:operation>
<wsdl:operation name="fetchProtectedPackage">
<wsdl:input message="tns:fetchProtectedPackageHttpGetIn" />
<wsdl:output message="tns:fetchProtectedPackageHttpGetOut" />
</wsdl:operation>
<wsdl:operation name="SubmitPackage">
<wsdl:input message="tns:SubmitPackageHttpGetIn" />
<wsdl:output message="tns:SubmitPackageHttpGetOut" />
</wsdl:operation>
<wsdl:operation name="PackageByGuid">
<wsdl:input message="tns:PackageByGuidHttpGetIn" />
<wsdl:output message="tns:PackageByGuidHttpGetOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:portType name="RepositoryHttpPost">
<wsdl:operation name="Categories">
<wsdl:input message="tns:CategoriesHttpPostIn" />
<wsdl:output message="tns:CategoriesHttpPostOut" />
</wsdl:operation>
<wsdl:operation name="Modules">
<wsdl:input message="tns:ModulesHttpPostIn" />
<wsdl:output message="tns:ModulesHttpPostOut" />
</wsdl:operation>
<wsdl:operation name="ModulesCategorized">
<wsdl:input message="tns:ModulesCategorizedHttpPostIn" />
<wsdl:output message="tns:ModulesCategorizedHttpPostOut" />
</wsdl:operation>
<wsdl:operation name="Nitros">
<wsdl:input message="tns:NitrosHttpPostIn" />
<wsdl:output message="tns:NitrosHttpPostOut" />
</wsdl:operation>
<wsdl:operation name="NitrosCategorized">
<wsdl:input message="tns:NitrosCategorizedHttpPostIn" />
<wsdl:output message="tns:NitrosCategorizedHttpPostOut" />
</wsdl:operation>
<wsdl:operation name="authenticate">
<wsdl:input message="tns:authenticateHttpPostIn" />
<wsdl:output message="tns:authenticateHttpPostOut" />
</wsdl:operation>
<wsdl:operation name="fetchPackage">
<wsdl:input message="tns:fetchPackageHttpPostIn" />
<wsdl:output message="tns:fetchPackageHttpPostOut" />
</wsdl:operation>
<wsdl:operation name="fetchPackageByVersion">
<wsdl:input message="tns:fetchPackageByVersionHttpPostIn" />
<wsdl:output message="tns:fetchPackageByVersionHttpPostOut" />
</wsdl:operation>
<wsdl:operation name="fetchProtectedPackage">
<wsdl:input message="tns:fetchProtectedPackageHttpPostIn" />
<wsdl:output message="tns:fetchProtectedPackageHttpPostOut" />
</wsdl:operation>
<wsdl:operation name="SubmitPackage">
<wsdl:input message="tns:SubmitPackageHttpPostIn" />
<wsdl:output message="tns:SubmitPackageHttpPostOut" />
</wsdl:operation>
<wsdl:operation name="PackageByGuid">
<wsdl:input message="tns:PackageByGuidHttpPostIn" />
<wsdl:output message="tns:PackageByGuidHttpPostOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="RepositorySoap" type="tns:RepositorySoap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="Categories">
<soap:operation soapAction="http://packages.umbraco.org/webservices/Categories" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Modules">
<soap:operation soapAction="http://packages.umbraco.org/webservices/Modules" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="ModulesCategorized">
<soap:operation soapAction="http://packages.umbraco.org/webservices/ModulesCategorized" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Nitros">
<soap:operation soapAction="http://packages.umbraco.org/webservices/Nitros" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="NitrosCategorized">
<soap:operation soapAction="http://packages.umbraco.org/webservices/NitrosCategorized" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="authenticate">
<soap:operation soapAction="http://packages.umbraco.org/webservices/authenticate" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchPackage">
<soap:operation soapAction="http://packages.umbraco.org/webservices/fetchPackage" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchPackageByVersion">
<soap:operation soapAction="http://packages.umbraco.org/webservices/fetchPackageByVersion" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchProtectedPackage">
<soap:operation soapAction="http://packages.umbraco.org/webservices/fetchProtectedPackage" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="SubmitPackage">
<soap:operation soapAction="http://packages.umbraco.org/webservices/SubmitPackage" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="PackageByGuid">
<soap:operation soapAction="http://packages.umbraco.org/webservices/PackageByGuid" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="RepositorySoap12" type="tns:RepositorySoap">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="Categories">
<soap12:operation soapAction="http://packages.umbraco.org/webservices/Categories" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Modules">
<soap12:operation soapAction="http://packages.umbraco.org/webservices/Modules" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="ModulesCategorized">
<soap12:operation soapAction="http://packages.umbraco.org/webservices/ModulesCategorized" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Nitros">
<soap12:operation soapAction="http://packages.umbraco.org/webservices/Nitros" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="NitrosCategorized">
<soap12:operation soapAction="http://packages.umbraco.org/webservices/NitrosCategorized" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="authenticate">
<soap12:operation soapAction="http://packages.umbraco.org/webservices/authenticate" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchPackage">
<soap12:operation soapAction="http://packages.umbraco.org/webservices/fetchPackage" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchPackageByVersion">
<soap12:operation soapAction="http://packages.umbraco.org/webservices/fetchPackageByVersion" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchProtectedPackage">
<soap12:operation soapAction="http://packages.umbraco.org/webservices/fetchProtectedPackage" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="SubmitPackage">
<soap12:operation soapAction="http://packages.umbraco.org/webservices/SubmitPackage" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="PackageByGuid">
<soap12:operation soapAction="http://packages.umbraco.org/webservices/PackageByGuid" style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="RepositoryHttpGet" type="tns:RepositoryHttpGet">
<http:binding verb="GET" />
<wsdl:operation name="Categories">
<http:operation location="/Categories" />
<wsdl:input>
<http:urlEncoded />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Modules">
<http:operation location="/Modules" />
<wsdl:input>
<http:urlEncoded />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="ModulesCategorized">
<http:operation location="/ModulesCategorized" />
<wsdl:input>
<http:urlEncoded />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Nitros">
<http:operation location="/Nitros" />
<wsdl:input>
<http:urlEncoded />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="NitrosCategorized">
<http:operation location="/NitrosCategorized" />
<wsdl:input>
<http:urlEncoded />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="authenticate">
<http:operation location="/authenticate" />
<wsdl:input>
<http:urlEncoded />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchPackage">
<http:operation location="/fetchPackage" />
<wsdl:input>
<http:urlEncoded />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchPackageByVersion">
<http:operation location="/fetchPackageByVersion" />
<wsdl:input>
<http:urlEncoded />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchProtectedPackage">
<http:operation location="/fetchProtectedPackage" />
<wsdl:input>
<http:urlEncoded />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="SubmitPackage">
<http:operation location="/SubmitPackage" />
<wsdl:input>
<http:urlEncoded />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="PackageByGuid">
<http:operation location="/PackageByGuid" />
<wsdl:input>
<http:urlEncoded />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="RepositoryHttpPost" type="tns:RepositoryHttpPost">
<http:binding verb="POST" />
<wsdl:operation name="Categories">
<http:operation location="/Categories" />
<wsdl:input>
<mime:content type="application/x-www-form-urlencoded" />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Modules">
<http:operation location="/Modules" />
<wsdl:input>
<mime:content type="application/x-www-form-urlencoded" />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="ModulesCategorized">
<http:operation location="/ModulesCategorized" />
<wsdl:input>
<mime:content type="application/x-www-form-urlencoded" />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Nitros">
<http:operation location="/Nitros" />
<wsdl:input>
<mime:content type="application/x-www-form-urlencoded" />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="NitrosCategorized">
<http:operation location="/NitrosCategorized" />
<wsdl:input>
<mime:content type="application/x-www-form-urlencoded" />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="authenticate">
<http:operation location="/authenticate" />
<wsdl:input>
<mime:content type="application/x-www-form-urlencoded" />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchPackage">
<http:operation location="/fetchPackage" />
<wsdl:input>
<mime:content type="application/x-www-form-urlencoded" />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchPackageByVersion">
<http:operation location="/fetchPackageByVersion" />
<wsdl:input>
<mime:content type="application/x-www-form-urlencoded" />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="fetchProtectedPackage">
<http:operation location="/fetchProtectedPackage" />
<wsdl:input>
<mime:content type="application/x-www-form-urlencoded" />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="SubmitPackage">
<http:operation location="/SubmitPackage" />
<wsdl:input>
<mime:content type="application/x-www-form-urlencoded" />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="PackageByGuid">
<http:operation location="/PackageByGuid" />
<wsdl:input>
<mime:content type="application/x-www-form-urlencoded" />
</wsdl:input>
<wsdl:output>
<mime:mimeXml part="Body" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="Repository">
<wsdl:port name="RepositorySoap" binding="tns:RepositorySoap">
<soap:address location="https://our.umbraco.com/umbraco/webservices/api/repository.asmx" />
</wsdl:port>
<wsdl:port name="RepositorySoap12" binding="tns:RepositorySoap12">
<soap12:address location="https://our.umbraco.com/umbraco/webservices/api/repository.asmx" />
</wsdl:port>
<wsdl:port name="RepositoryHttpGet" binding="tns:RepositoryHttpGet">
<http:address location="https://our.umbraco.com/umbraco/webservices/api/repository.asmx" />
</wsdl:port>
<wsdl:port name="RepositoryHttpPost" binding="tns:RepositoryHttpPost">
<http:address location="https://our.umbraco.com/umbraco/webservices/api/repository.asmx" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
@@ -0,0 +1,33 @@
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Filters;
using Umbraco.Core;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
namespace Umbraco.Web.WebApi
{
public class LockTimeoutExceptionHandlerAttribute : FilterAttribute, IExceptionFilter
{
private readonly ILocalizedTextService _localizedTextService;
public LockTimeoutExceptionHandlerAttribute()
{
_localizedTextService = Current.Factory.GetInstance<ILocalizedTextService>();
}
public Task ExecuteExceptionFilterAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
if (actionExecutedContext.Exception is LockTimeoutException timeoutException)
{
var reason = _localizedTextService.LocalizeLockReason(timeoutException.Reason);
actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(HttpStatusCode.Conflict, reason);
}
return Task.CompletedTask;
}
}
}
@@ -25,6 +25,7 @@ namespace Umbraco.Web.WebApi
[UmbracoWebApiRequireHttps]
[CheckIfUserTicketDataIsStale]
[UnhandedExceptionLoggerConfiguration]
[LockTimeoutExceptionHandler]
[EnableDetailedErrors]
public abstract class UmbracoAuthorizedApiController : UmbracoApiController
{