Compare commits

...

63 Commits

Author SHA1 Message Date
Sebastiaan Janssen 8baaae4da6 Merge branch '7.2.7' into master-v7 2015-07-15 12:58:48 +02:00
Sebastiaan Janssen ed05a3223f Update Examine NuGet dependency 2015-07-15 12:52:24 +02:00
Shannon 25e3134e35 Fixes unit tests 2015-07-15 12:33:47 +02:00
Shannon f1b9b98f5d Updates to latest examine 2015-07-15 12:32:11 +02:00
Sebastiaan Janssen d2f7eda074 Fixes unit test that failed because HostingEnvironment.ApplicationID is null in unit tests 2015-07-15 10:58:38 +02:00
Stephan 3062eedb4f content - ensure backward compatibility 2015-07-14 19:28:15 +02:00
Stephan 1e3dd4c5cd Cleanup new umbracoApplicationUrl config 2015-07-14 19:28:14 +02:00
Sebastiaan Janssen a7871abbd9 Update Examine and ClientDependency to latest version 2015-07-14 16:50:45 +02:00
Stephan bf3a08cf85 DisposableObject - cleanup & use 2015-07-14 15:56:23 +02:00
Sebastiaan Janssen 7bc208e490 U4-6825 Can't blank out values using the built-in EditProfile snippet
#U4-6825 Fixed
2015-07-14 15:32:08 +02:00
Shannon 6aa633c8c5 changes app timeout to 1.5 mins 2015-07-13 17:10:35 +02:00
Stephan 9be2f99e7b Scheduler - does not run tasks if not MainDom 2015-07-13 14:53:07 +02:00
Stephan 2d5bfc0fe2 Use MainDom status to protect umbraco.config
Conflicts:
	src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheFilePersister.cs
	src/Umbraco.Web/umbraco.presentation/content.cs
2015-07-13 14:52:55 +02:00
Stephan c01f864e37 ApplicationContext - manage MainDom
Conflicts:
	src/Umbraco.Core/ApplicationContext.cs
	src/Umbraco.Core/Umbraco.Core.csproj
2015-07-13 14:41:07 +02:00
Stephan a8be796d68 Bugfix content for LB 2015-07-13 14:17:00 +02:00
Stephan 4acbdbe896 Move WaitOneAsync from AsyncLock to WaitHandleExtensions
Conflicts:
	src/Umbraco.Core/AsyncLock.cs
	src/Umbraco.Core/Umbraco.Core.csproj
2015-07-13 14:16:43 +02:00
Stephan 34a9eef8b8 BackgroundTaskRunner - add Hosted option, refactor some tasks
Conflicts:
	src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs
2015-07-13 14:13:47 +02:00
Stephan 0f56fa4baa Bugfix content xml doctype issue 2015-07-13 14:08:50 +02:00
Shannon 82a8404b2e Updates TemplateRenderer to not use singletons and to ensure that the http context values are copied back if anything fails 2015-07-09 18:30:52 +02:00
Shannon bce8affe16 removes unused method 2015-07-09 15:19:47 +02:00
Shannon 4bbd8097a2 Merge branch '7.2.7' of https://github.com/umbraco/Umbraco-CMS into 7.2.7 2015-07-09 15:08:41 +02:00
Shannon da9c810d78 Updates examine initialization of its IIndexCriteria to be lazy, this will save a few db calls on startup and ensure they are excuted at the very last possible time. 2015-07-09 14:32:50 +02:00
Shannon 431d066300 Changes the ApplicationEventHandler to check if the db is configured instead of being connected to which can cause issues if SQLCE file is locked by another app domain on startup. Then we verify in the CoreBootManager that the db can be connected to with a retry policy. Changes the db context CanConnect method to not store a static value since it might later be able to be connected to. 2015-07-09 14:31:59 +02:00
Shannon 6d50bd891a moves the ApplicationContext.IsConfigured to a lazy initializer 2015-07-09 14:30:32 +02:00
Stephan 1d00e3c5d8 UmbracoApplication - log unhandled exceptions
Conflicts:
	src/Umbraco.Core/UmbracoApplicationBase.cs
2015-07-08 21:33:22 +02:00
Shannon e69422c8d8 Fixes tests (backports cache fix by name) 2015-07-08 18:14:31 +02:00
Shannon a277ac2fe4 udpates some logging stuff which will also make logging faster. 2015-07-08 17:55:08 +02:00
Shannon 1067b1e245 BackgroundTaskRunner - refactor recurring tasks, improve shutdown
Conflicts:
	src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheFilePersister.cs
	src/Umbraco.Web/Scheduling/LogScrubber.cs
	src/Umbraco.Web/Scheduling/Scheduler.cs
	src/Umbraco.Web/Umbraco.Web.csproj
2015-07-08 17:41:20 +02:00
Shannon 7987f13a11 U4-6788 - refactor OriginalRequestUrl into UmbracoApplicationUrl
and fixes merge issues.
2015-07-08 17:32:36 +02:00
Shannon ccda1bdd73 Refactor scheduled tasks & publishing cancellation 2015-07-08 17:23:15 +02:00
Shannon 76a6f2d2d0 Updates scheduled tasks to be async with cancellation token 2015-07-08 17:23:00 +02:00
Shannon 69428b2281 Scheduled publishing is executing request async with cancellation token 2015-07-08 17:22:44 +02:00
Shannon 7cd39918a0 another log entry 2015-07-08 16:54:38 +02:00
Shannon 9a626c9da6 Merge branch '7.2.7' of https://github.com/umbraco/Umbraco-CMS into 7.2.7 2015-07-08 16:45:42 +02:00
Shannon 0520797c09 more logging 2015-07-08 16:45:34 +02:00
Stephan 9b85ba5b90 Resolution - better logging when freezing 2015-07-08 16:32:43 +02:00
Shannon ca738979ce Adds some more detailed logging. 2015-07-08 16:27:06 +02:00
Shannon 8cfb56adf4 adds machine name logging to startup 2015-07-08 13:28:15 +02:00
Shannon 053346d79b fixes merge 2015-07-08 11:54:10 +02:00
Shannon 044c9c95f8 Merge branch 'dev-v7-U4-6677' of https://github.com/bjarnef/Umbraco-CMS into bjarnef-dev-v7-U4-6677
Conflicts:
	src/Umbraco.Web.UI.Client/src/less/panel.less

Conflicts:
	src/Umbraco.Web.UI.Client/src/less/panel.less
2015-07-08 11:49:09 +02:00
Shannon b23ea69b3e Fixes: U4-6636 Left navbar (section-bar) is unnecessary wide on mobile devices
Conflicts:
	src/Umbraco.Web.UI.Client/src/less/grid.less
2015-07-08 11:48:17 +02:00
Shannon 46f9c5ff05 Fixes: U4-5052 (U7) Previewing a published page changes icon in content tree to the little green plus indicating changes need to be published even though no changes have been made. 2015-07-08 11:46:03 +02:00
Shannon 3bc4b50d74 updates version 2015-07-08 11:36:50 +02:00
Shannon 1a2849dfb0 fixes merge issues 2015-07-08 11:35:55 +02:00
Shannon bcb5883230 Fixes: U4-3514 Add dropdown list of tabs, if more then 6 tabs
Conflicts:
	src/Umbraco.Web.UI.Client/bower.json
	src/Umbraco.Web.UI.Client/gruntFile.js
2015-07-08 11:16:46 +02:00
Shannon 18080c1cfb Fixes: U4-6796 BackOfficeIdentity gets assigned inadvertently for front-end requests if using ui.Text to localize 2015-07-08 11:13:49 +02:00
Shannon 2600a3887e Fixes issue with upgrading - potential file lock issues and also file check for access.config
Conflicts:
	src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MovePublicAccessXmlDataToDb.cs
2015-07-08 11:13:24 +02:00
Shannon 45bec2a67d Fixes date picker: U4-6715 i can't seem to remove a "publish-at" date? - date picker is more robust now and does proper validity checking/feedback 2015-07-08 11:12:19 +02:00
Shannon da7456fd2a Fixes up unit test for testing back office request to ensure that the GlobalSettings.Path is consistent with a vdir 2015-07-08 11:11:17 +02:00
Shannon 275ba00e24 Fix IsBackOfficeRequest returning false for virtual directory paths - U4-6346 2015-07-08 11:10:30 +02:00
Shannon d00bf4649d Fix for JsSource on legacy trees not supporting virtual paths (~/) - U4-6346
Conflicts:
	src/umbraco.cms/Actions/Action.cs
2015-07-08 11:10:18 +02:00
Shannon 04976f8063 U4-6202 - cache clearing by object type
Conflicts:
	src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs
2015-07-08 11:08:19 +02:00
Shannon 3e7c9fc81e Fixes: U4-6202 Cache not clearing by object type properly
Conflicts:
	src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs
2015-07-08 11:07:53 +02:00
Shannon a1f4d7b16f Fixes: U4-5798 The database configuration failed with the following message: The incoming request has too many parameters. The server supports a maximum of 2100 parameters. 2015-07-08 11:05:30 +02:00
Shannon 811ef4a444 Fixes: U4-5673 SQL Azure cannot drop clustered index on upgrade 2015-07-08 11:03:42 +02:00
Shannon 69ac621f5a Fixes: U4-5670 Umbraco.DropDown List pre value with alias ** already exists for this data type 2015-07-08 11:03:10 +02:00
Shannon 610582df38 U4-5162 - do not cache macro if previewing 2015-07-08 11:02:30 +02:00
Sebastiaan Janssen 181f7c1819 Adds close button to the preview 2015-06-09 18:19:26 +02:00
Sebastiaan Janssen 73fbe7d78c Merge branch 'dev-v7' into master-v7 2015-06-03 14:53:37 +02:00
Shannon 5d1340e864 Merge branch 'dev-v7' into master-v7 2015-05-13 10:15:11 +10:00
Sebastiaan Janssen c867f609f1 Merge branch '7.2.4' into master-v7 2015-03-20 11:05:00 +01:00
Sebastiaan Janssen 69879dc8f8 Merge branch 'dev-v7' into master-v7 2015-03-19 15:10:59 +01:00
hartvig f663a13391 Updates installer with a new number of tattoos
Kudos to Mike Perry for being #3: https://twitter.com/nvmy13t/status/574733012904099840
2015-03-09 08:52:21 +01:00
96 changed files with 2061 additions and 1319 deletions
+2 -2
View File
@@ -28,11 +28,11 @@
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
<dependency id="MySql.Data" version="[6.9.6, 7.0.0)" />
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
<dependency id="ClientDependency" version="[1.8.3.1, 2.0.0)" />
<dependency id="ClientDependency" version="[1.8.4, 2.0.0)" />
<dependency id="ClientDependency-Mvc" version="[1.8.0, 2.0.0)" />
<dependency id="AutoMapper" version="[3.0.0, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.5, 7.0.0)" />
<dependency id="Examine" version="[0.1.63, 1.0.0)" />
<dependency id="Examine" version="[0.1.65, 1.0.0)" />
<dependency id="ImageProcessor" version="[1.9.5, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[3.3.1, 5.0.0)" />
</dependencies>
+1 -1
View File
@@ -1,2 +1,2 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.2.6
7.2.7
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.2.6")]
[assembly: AssemblyInformationalVersion("7.2.6")]
[assembly: AssemblyFileVersion("7.2.7")]
[assembly: AssemblyInformationalVersion("7.2.7")]
+72 -63
View File
@@ -1,15 +1,12 @@
using System;
using System.Configuration;
using System.Threading;
using System.Web;
using System.Web.Caching;
using Umbraco.Core.Cache;
using System.Threading.Tasks;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
namespace Umbraco.Core
{
@@ -36,6 +33,8 @@ namespace Umbraco.Core
_databaseContext = dbContext;
_services = serviceContext;
ApplicationCache = cache;
Init();
}
/// <summary>
@@ -45,6 +44,8 @@ namespace Umbraco.Core
public ApplicationContext(CacheHelper cache)
{
ApplicationCache = cache;
Init();
}
/// <summary>
@@ -60,13 +61,13 @@ namespace Umbraco.Core
/// </remarks>
public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext)
{
if (ApplicationContext.Current != null)
if (Current != null)
{
if (!replaceContext)
return ApplicationContext.Current;
return Current;
}
ApplicationContext.Current = appContext;
return ApplicationContext.Current;
Current = appContext;
return Current;
}
/// <summary>
@@ -85,14 +86,14 @@ namespace Umbraco.Core
/// </remarks>
public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, bool replaceContext)
{
if (ApplicationContext.Current != null)
if (Current != null)
{
if (!replaceContext)
return ApplicationContext.Current;
return Current;
}
var ctx = new ApplicationContext(dbContext, serviceContext, cache);
ApplicationContext.Current = ctx;
return ApplicationContext.Current;
Current = ctx;
return Current;
}
/// <summary>
@@ -141,61 +142,75 @@ namespace Umbraco.Core
// GlobalSettings.CurrentVersion returns the hard-coded "current version"
// the system is configured if they match
// if they don't, install runs, updates web.config (presumably) and updates GlobalSettings.ConfiguredStatus
//
// then there is Application["umbracoNeedConfiguration"] which makes no sense... getting rid of it... SD: I have actually remove that now!
//
public bool IsConfigured
{
// todo - we should not do this - ok for now
get
{
return Configured;
}
get { return _configured.Value; }
}
/// <summary>
/// The original/first url that the web application executes
/// The application url.
/// </summary>
/// <remarks>
/// we need to set the initial url in our ApplicationContext, this is so our keep alive service works and this must
/// exist on a global context because the keep alive service doesn't run in a web context.
/// we are NOT going to put a lock on this because locking will slow down the application and we don't really care
/// if two threads write to this at the exact same time during first page hit.
/// see: http://issues.umbraco.org/issue/U4-2059
/// The application url is the url that should be used by services to talk to the application,
/// eg keep alive or scheduled publishing services. It must exist on a global context because
/// some of these services may not run within a web context.
/// The format of the application url is:
/// - has a scheme (http or https)
/// - has the SystemDirectories.Umbraco path
/// - does not end with a slash
/// It is initialized on the first request made to the server, by UmbracoModule.EnsureApplicationUrl:
/// - if umbracoSettings:settings/web.routing/@umbracoApplicationUrl is set, use the value (new setting)
/// - if umbracoSettings:settings/scheduledTasks/@baseUrl is set, use the value (backward compatibility)
/// - otherwise, use the url of the (first) request.
/// Not locking, does not matter if several threads write to this.
/// See also issues:
/// - http://issues.umbraco.org/issue/U4-2059
/// - http://issues.umbraco.org/issue/U4-6788
/// - http://issues.umbraco.org/issue/U4-5728
/// - http://issues.umbraco.org/issue/U4-5391
/// </remarks>
internal string OriginalRequestUrl { get; set; }
internal string UmbracoApplicationUrl
{
get
{
// if initialized, return
if (_umbracoApplicationUrl != null) return _umbracoApplicationUrl;
private bool _versionsDifferenceReported;
// try settings
ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(this, UmbracoConfig.For.UmbracoSettings());
/// <summary>
/// Checks if the version configured matches the assembly version
/// </summary>
private bool Configured
{
get
{
try
{
var configStatus = ConfigurationStatus;
var currentVersion = UmbracoVersion.Current.ToString(3);
var ok = configStatus == currentVersion;
// and return what we have, may be null
return _umbracoApplicationUrl;
}
set
{
_umbracoApplicationUrl = value;
}
}
if (ok == false && _versionsDifferenceReported == false)
{
// remember it's been reported so we don't flood the log
// no thread-safety so there may be a few log entries, doesn't matter
_versionsDifferenceReported = true;
LogHelper.Info<ApplicationContext>("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'");
}
return ok;
}
catch
{
return false;
}
}
}
internal string _umbracoApplicationUrl; // internal for tests
private Lazy<bool> _configured;
internal MainDom MainDom { get; private set; }
private void Init()
{
MainDom = new MainDom();
MainDom.Acquire();
//Create the lazy value to resolve whether or not the application is 'configured'
_configured = new Lazy<bool>(() =>
{
var configStatus = ConfigurationStatus;
var currentVersion = UmbracoVersion.Current.ToString(3);
var ok = configStatus == currentVersion;
if (ok == false)
{
LogHelper.Debug<ApplicationContext>("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'");
}
return ok;
});
}
private string ConfigurationStatus
{
@@ -212,12 +227,6 @@ namespace Umbraco.Core
}
}
private void AssertIsReady()
{
if (!this.IsReady)
throw new Exception("ApplicationContext is not ready yet.");
}
private void AssertIsNotReady()
{
if (this.IsReady)
+2 -2
View File
@@ -74,7 +74,7 @@ namespace Umbraco.Core
/// <returns></returns>
private bool ShouldExecute(ApplicationContext applicationContext)
{
if (applicationContext.IsConfigured && applicationContext.DatabaseContext.CanConnect)
if (applicationContext.IsConfigured && applicationContext.DatabaseContext.IsDatabaseConfigured)
{
return true;
}
@@ -84,7 +84,7 @@ namespace Umbraco.Core
return true;
}
if (!applicationContext.DatabaseContext.CanConnect && ExecuteWhenDatabaseNotConfigured)
if (!applicationContext.DatabaseContext.IsDatabaseConfigured && ExecuteWhenDatabaseNotConfigured)
{
return true;
}
+16 -38
View File
@@ -63,14 +63,14 @@ namespace Umbraco.Core
// for anonymous semaphore, use the unique releaser, else create a new one
return _semaphore != null
? _releaser // (IDisposable)new SemaphoreSlimReleaser(_semaphore)
: (IDisposable)new NamedSemaphoreReleaser(_semaphore2);
: new NamedSemaphoreReleaser(_semaphore2);
}
public Task<IDisposable> LockAsync()
{
var wait = _semaphore != null
? _semaphore.WaitAsync()
: WaitOneAsync(_semaphore2);
? _semaphore.WaitAsync()
: _semaphore2.WaitOneAsync();
return wait.IsCompleted
? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
@@ -79,6 +79,19 @@ namespace Umbraco.Core
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
public Task<IDisposable> LockAsync(int millisecondsTimeout)
{
var wait = _semaphore != null
? _semaphore.WaitAsync(millisecondsTimeout)
: _semaphore2.WaitOneAsync(millisecondsTimeout);
return wait.IsCompleted
? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
: wait.ContinueWith((_, state) => (((AsyncLock)state).CreateReleaser()),
this, CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
public IDisposable Lock()
{
if (_semaphore != null)
@@ -168,40 +181,5 @@ namespace Umbraco.Core
Dispose(false);
}
}
// http://stackoverflow.com/questions/25382583/waiting-on-a-named-semaphore-with-waitone100-vs-waitone0-task-delay100
// http://blog.nerdbank.net/2011/07/c-await-for-waithandle.html
// F# has a AwaitWaitHandle method that accepts a time out... and seems pretty complex...
// version below should be OK
private static Task WaitOneAsync(WaitHandle handle)
{
var tcs = new TaskCompletionSource<object>();
var callbackHandleInitLock = new object();
lock (callbackHandleInitLock)
{
RegisteredWaitHandle callbackHandle = null;
// ReSharper disable once RedundantAssignment
callbackHandle = ThreadPool.RegisterWaitForSingleObject(
handle,
(state, timedOut) =>
{
tcs.SetResult(null);
// we take a lock here to make sure the outer method has completed setting the local variable callbackHandle.
lock (callbackHandleInitLock)
{
// ReSharper disable once PossibleNullReferenceException
// ReSharper disable once AccessToModifiedClosure
callbackHandle.Unregister(null);
}
},
/*state:*/ null,
/*millisecondsTimeOutInterval:*/ Timeout.Infinite,
/*executeOnlyOnce:*/ true);
}
return tcs.Task;
}
}
}
@@ -90,7 +90,7 @@ namespace Umbraco.Core.Cache
{
foreach (var entry in GetDictionaryEntries()
.ToArray())
RemoveEntry((string) entry.Key);
RemoveEntry((string)entry.Key);
}
}
@@ -105,6 +105,9 @@ namespace Umbraco.Core.Cache
public virtual void ClearCacheObjectTypes(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
if (type == null) return;
var isInterface = type.IsInterface;
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
@@ -114,16 +117,20 @@ namespace Umbraco.Core.Cache
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
return value == null || value.GetType().ToString().InvariantEquals(typeName);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (type.IsInstanceOfType(value)) : (value.GetType() == type));
})
.ToArray())
RemoveEntry((string) entry.Key);
RemoveEntry((string)entry.Key);
}
}
public virtual void ClearCacheObjectTypes<T>()
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
using (WriteLock)
{
foreach (var entry in GetDictionaryEntries()
@@ -134,16 +141,20 @@ namespace Umbraco.Core.Cache
// compare on exact type, don't use "is"
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
return value == null || value.GetType() == typeOfT;
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (value is T) : (value.GetType() == typeOfT));
})
.ToArray())
RemoveEntry((string) entry.Key);
RemoveEntry((string)entry.Key);
}
}
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
var plen = CacheItemPrefix.Length + 1;
using (WriteLock)
{
@@ -156,11 +167,14 @@ namespace Umbraco.Core.Cache
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
if (value == null) return true;
return value.GetType() == typeOfT
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return (isInterface ? (value is T) : (value.GetType() == typeOfT))
// run predicate on the 'public key' part only, ie without prefix
&& predicate(((string)x.Key).Substring(plen), (T)value);
&& predicate(((string)x.Key).Substring(plen), (T)value);
}))
RemoveEntry((string) entry.Key);
RemoveEntry((string)entry.Key);
}
}
@@ -172,7 +186,7 @@ namespace Umbraco.Core.Cache
foreach (var entry in GetDictionaryEntries()
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
.ToArray())
RemoveEntry((string) entry.Key);
RemoveEntry((string)entry.Key);
}
}
@@ -184,7 +198,7 @@ namespace Umbraco.Core.Cache
foreach (var entry in GetDictionaryEntries()
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
.ToArray())
RemoveEntry((string) entry.Key);
RemoveEntry((string)entry.Key);
}
}
@@ -102,7 +102,7 @@ namespace Umbraco.Core.Cache
// cannot create value within the lock, so if result.IsValueCreated is false, just
// do nothing here - means that if creation throws, a race condition could cause
// more than one thread to reach the return statement below and throw - accepted.
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = GetSafeLazy(getCacheItem);
@@ -122,7 +122,7 @@ namespace Umbraco.Core.Cache
if (eh != null) throw eh.Exception; // throw once!
return value;
}
#endregion
#region Insert
@@ -20,16 +20,22 @@ namespace Umbraco.Core.Cache
private readonly System.Web.Caching.Cache _cache;
/// <summary>
/// Used for debugging
/// </summary>
internal Guid InstanceId { get; private set; }
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
{
_cache = cache;
InstanceId = Guid.NewGuid();
}
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
return _cache.Cast<DictionaryEntry>()
.Where(x => x.Key is string && ((string) x.Key).StartsWith(prefix));
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
}
protected override void RemoveEntry(string key)
@@ -134,6 +140,7 @@ namespace Umbraco.Core.Cache
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
lck.UpgradeToWriteLock();
//NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update!
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
}
}
@@ -184,13 +191,14 @@ namespace Umbraco.Core.Cache
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
if (value == null) return; // do not store null values (backward compat)
cacheKey = GetCacheKey(cacheKey);
cacheKey = GetCacheKey(cacheKey);
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
using (new WriteLock(_locker))
{
//NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update!
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
}
}
+38
View File
@@ -8,13 +8,51 @@ namespace Umbraco.Core.Cache
/// </summary>
public interface ICacheProvider
{
/// <summary>
/// Removes all items from the cache.
/// </summary>
void ClearAllCache();
/// <summary>
/// Removes an item from the cache, identified by its key.
/// </summary>
/// <param name="key">The key of the item.</param>
void ClearCacheItem(string key);
/// <summary>
/// Removes items from the cache, of a specified type.
/// </summary>
/// <param name="typeName">The name of the type to remove.</param>
/// <remarks>
/// <para>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</para>
/// <para>Performs a case-sensitive search.</para>
/// </remarks>
void ClearCacheObjectTypes(string typeName);
/// <summary>
/// Removes items from the cache, of a specified type.
/// </summary>
/// <typeparam name="T">The type of the items to remove.</typeparam>
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</remarks>
void ClearCacheObjectTypes<T>();
/// <summary>
/// Removes items from the cache, of a specified type, satisfying a predicate.
/// </summary>
/// <typeparam name="T">The type of the items to remove.</typeparam>
/// <param name="predicate">The predicate to satisfy.</param>
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</remarks>
void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate);
void ClearCacheByKeySearch(string keyStartsWith);
void ClearCacheByKeyExpression(string regexString);
IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith);
IEnumerable<object> GetCacheItemsByKeyExpression(string regexString);
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.Caching;
using System.Text.RegularExpressions;
using System.Threading;
@@ -16,12 +17,19 @@ namespace Umbraco.Core.Cache
/// </summary>
internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
{
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
internal ObjectCache MemoryCache;
/// <summary>
/// Used for debugging
/// </summary>
internal Guid InstanceId { get; private set; }
public ObjectCacheRuntimeCacheProvider()
{
MemoryCache = new MemoryCache("in-memory");
InstanceId = Guid.NewGuid();
}
#region Clear
@@ -41,11 +49,14 @@ namespace Umbraco.Core.Cache
{
if (MemoryCache[key] == null) return;
MemoryCache.Remove(key);
}
}
}
public virtual void ClearCacheObjectTypes(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
if (type == null) return;
var isInterface = type.IsInterface;
using (new WriteLock(_locker))
{
foreach (var key in MemoryCache
@@ -55,7 +66,10 @@ namespace Umbraco.Core.Cache
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
return value == null || value.GetType().ToString().InvariantEquals(typeName);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (type.IsInstanceOfType(value)) : (value.GetType() == type));
})
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
@@ -67,7 +81,8 @@ namespace Umbraco.Core.Cache
{
using (new WriteLock(_locker))
{
var typeOfT = typeof (T);
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
foreach (var key in MemoryCache
.Where(x =>
{
@@ -75,7 +90,11 @@ namespace Umbraco.Core.Cache
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
return value == null || value.GetType() == typeOfT;
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (value is T) : (value.GetType() == typeOfT));
})
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
@@ -88,6 +107,7 @@ namespace Umbraco.Core.Cache
using (new WriteLock(_locker))
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
foreach (var key in MemoryCache
.Where(x =>
{
@@ -96,8 +116,11 @@ namespace Umbraco.Core.Cache
// get non-created as NonCreatedValue & exceptions as null
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
if (value == null) return true;
return value.GetType() == typeOfT
&& predicate(x.Key, (T) value);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return (isInterface ? (value is T) : (value.GetType() == typeOfT))
&& predicate(x.Key, (T)value);
})
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
@@ -114,7 +137,7 @@ namespace Umbraco.Core.Cache
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
MemoryCache.Remove(key);
}
}
}
public virtual void ClearCacheByKeyExpression(string regexString)
@@ -126,7 +149,7 @@ namespace Umbraco.Core.Cache
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
MemoryCache.Remove(key);
}
}
}
#endregion
@@ -178,7 +201,7 @@ namespace Umbraco.Core.Cache
return GetCacheItem(cacheKey, getCacheItem, null);
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal,CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
// see notes in HttpRuntimeCacheProvider
@@ -193,6 +216,7 @@ namespace Umbraco.Core.Cache
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
lck.UpgradeToWriteLock();
//NOTE: This does an add or update
MemoryCache.Set(cacheKey, result, policy);
}
}
@@ -219,6 +243,7 @@ namespace Umbraco.Core.Cache
if (value == null) return; // do not store null values (backward compat)
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
//NOTE: This does an add or update
MemoryCache.Set(cacheKey, result, policy);
}
@@ -239,7 +264,7 @@ namespace Umbraco.Core.Cache
{
policy.ChangeMonitors.Add(new HostFileChangeMonitor(dependentFiles.ToList()));
}
if (removedCallback != null)
{
policy.RemovedCallback = arguments =>
@@ -270,6 +295,6 @@ namespace Umbraco.Core.Cache
}
return policy;
}
}
}
@@ -11,6 +11,8 @@
bool DisableFindContentByIdPath { get; }
string UrlProviderMode { get; }
string UmbracoApplicationUrl { get; }
}
}
@@ -30,8 +30,13 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
[ConfigurationProperty("urlProviderMode", DefaultValue = "AutoLegacy")]
public string UrlProviderMode
{
get { return (string)base["urlProviderMode"]; }
get { return (string) base["urlProviderMode"]; }
}
[ConfigurationProperty("umbracoApplicationUrl", DefaultValue = null)]
public string UmbracoApplicationUrl
{
get { return (string)base["umbracoApplicationUrl"]; }
}
}
}
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.2.6");
private static readonly Version Version = new Version("7.2.7");
/// <summary>
/// Gets the current version of Umbraco.
+93 -12
View File
@@ -2,10 +2,12 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Web;
using AutoMapper;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Mapping;
@@ -66,7 +68,9 @@ namespace Umbraco.Core
InitializeProfilerResolver();
_timer = DisposableTimer.TraceDuration<CoreBootManager>("Umbraco application starting", "Umbraco application startup complete");
_timer = DisposableTimer.TraceDuration<CoreBootManager>(
string.Format("Umbraco {0} application starting on {1}", UmbracoVersion.Current, NetworkHelper.MachineName),
"Umbraco application startup complete");
CreateApplicationCache();
@@ -95,9 +99,25 @@ namespace Umbraco.Core
InitializeModelMappers();
//now we need to call the initialize methods
ApplicationEventsResolver.Current.ApplicationEventHandlers
.ForEach(x => x.OnApplicationInitialized(UmbracoApplication, ApplicationContext));
using (DisposableTimer.DebugDuration<CoreBootManager>(
() => string.Format("Executing {0} IApplicationEventHandler.OnApplicationInitialized", ApplicationEventsResolver.Current.ApplicationEventHandlers.Count()),
() => "Finished executing IApplicationEventHandler.OnApplicationInitialized"))
{
//now we need to call the initialize methods
ApplicationEventsResolver.Current.ApplicationEventHandlers
.ForEach(x =>
{
try
{
x.OnApplicationInitialized(UmbracoApplication, ApplicationContext);
}
catch (Exception ex)
{
LogHelper.Error<CoreBootManager>("An error occurred running OnApplicationInitialized for handler " + x.GetType(), ex);
throw;
}
});
}
_isInitialized = true;
@@ -201,11 +221,27 @@ namespace Umbraco.Core
if (_isStarted)
throw new InvalidOperationException("The boot manager has already been initialized");
//call OnApplicationStarting of each application events handler
ApplicationEventsResolver.Current.ApplicationEventHandlers
.ForEach(x => x.OnApplicationStarting(UmbracoApplication, ApplicationContext));
using (DisposableTimer.DebugDuration<CoreBootManager>(
() => string.Format("Executing {0} IApplicationEventHandler.OnApplicationStarting", ApplicationEventsResolver.Current.ApplicationEventHandlers.Count()),
() => "Finished executing IApplicationEventHandler.OnApplicationStarting"))
{
//call OnApplicationStarting of each application events handler
ApplicationEventsResolver.Current.ApplicationEventHandlers
.ForEach(x =>
{
try
{
x.OnApplicationStarting(UmbracoApplication, ApplicationContext);
}
catch (Exception ex)
{
LogHelper.Error<CoreBootManager>("An error occurred running OnApplicationStarting for handler " + x.GetType(), ex);
throw;
}
});
}
if (afterStartup != null)
if (afterStartup != null)
{
afterStartup(ApplicationContext.Current);
}
@@ -226,10 +262,29 @@ namespace Umbraco.Core
throw new InvalidOperationException("The boot manager has already been completed");
FreezeResolution();
//call OnApplicationStarting of each application events handler
ApplicationEventsResolver.Current.ApplicationEventHandlers
.ForEach(x => x.OnApplicationStarted(UmbracoApplication, ApplicationContext));
//Here we need to make sure the db can be connected to
EnsureDatabaseConnection();
using (DisposableTimer.DebugDuration<CoreBootManager>(
() => string.Format("Executing {0} IApplicationEventHandler.OnApplicationStarted", ApplicationEventsResolver.Current.ApplicationEventHandlers.Count()),
() => "Finished executing IApplicationEventHandler.OnApplicationStarted"))
{
//call OnApplicationStarting of each application events handler
ApplicationEventsResolver.Current.ApplicationEventHandlers
.ForEach(x =>
{
try
{
x.OnApplicationStarted(UmbracoApplication, ApplicationContext);
}
catch (Exception ex)
{
LogHelper.Error<CoreBootManager>("An error occurred running OnApplicationStarted for handler " + x.GetType(), ex);
throw;
}
});
}
//Now, startup all of our legacy startup handler
ApplicationEventsResolver.Current.InstantiateLegacyStartupHandlers();
@@ -249,6 +304,32 @@ namespace Umbraco.Core
return this;
}
/// <summary>
/// We cannot continue if the db cannot be connected to
/// </summary>
private void EnsureDatabaseConnection()
{
if (ApplicationContext.IsConfigured == false) return;
if (ApplicationContext.DatabaseContext.IsDatabaseConfigured == false) return;
var currentTry = 0;
while (currentTry < 5)
{
if (ApplicationContext.DatabaseContext.CanConnect)
break;
//wait and retry
Thread.Sleep(1000);
currentTry++;
}
if (currentTry == 5)
{
throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but the Umbraco cannot connect to the database.");
}
}
/// <summary>
/// Freeze resolution to not allow Resolvers to be modified
/// </summary>
+3 -17
View File
@@ -26,8 +26,6 @@ namespace Umbraco.Core
{
private readonly IDatabaseFactory _factory;
private bool _configured;
private bool _canConnect;
private volatile bool _connectCheck = false;
private readonly object _locker = new object();
private string _connectionString;
private string _providerName;
@@ -67,21 +65,9 @@ namespace Umbraco.Core
get
{
if (IsDatabaseConfigured == false) return false;
//double check lock so that it is only checked once and is fast
if (_connectCheck == false)
{
lock (_locker)
{
if (_canConnect == false)
{
_canConnect = DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DatabaseProvider);
_connectCheck = true;
}
}
}
return _canConnect;
var canConnect = DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DatabaseProvider);
LogHelper.Info<DatabaseContext>("CanConnect = " + canConnect);
return canConnect;
}
}
+28 -39
View File
@@ -4,69 +4,58 @@ using System.Threading;
namespace Umbraco.Core
{
/// <summary>
/// Abstract implementation of logic commonly required to safely handle disposable unmanaged resources.
/// Abstract implementation of IDisposable.
/// </summary>
/// <remarks>
/// Can also be used as a pattern for when inheriting is not possible.
///
/// See also: https://msdn.microsoft.com/en-us/library/b1yfkh5e%28v=vs.110%29.aspx
/// See also: https://lostechies.com/chrispatterson/2012/11/29/idisposable-done-right/
///
/// Note: if an object's ctor throws, it will never be disposed, and so if that ctor
/// has allocated disposable objects, it should take care of disposing them.
/// </remarks>
public abstract class DisposableObject : IDisposable
{
private bool _disposed;
private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim();
private readonly object _locko = new object();
/// <summary>
/// Gets a value indicating whether this instance is disposed.
/// </summary>
/// <value>
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
/// </value>
public bool IsDisposed
{
get { return _disposed; }
}
// gets a value indicating whether this instance is disposed.
// for internal tests only (not thread safe)
//TODO make this internal + rename "Disposed" when we can break compatibility
public bool IsDisposed { get { return _disposed; } }
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <filterpriority>2</filterpriority>
// implements IDisposable
public void Dispose()
{
Dispose(true);
// Use SupressFinalize in case a subclass of this type implements a finalizer.
GC.SuppressFinalize(this);
}
// finalizer
~DisposableObject()
{
// Run dispose but let the class know it was due to the finalizer running.
Dispose(false);
}
protected virtual void Dispose(bool disposing)
//TODO make this private, non-virtual when we can break compatibility
protected virtual void Dispose(bool disposing)
{
// Only operate if we haven't already disposed
if (IsDisposed || !disposing) return;
lock (_locko)
{
if (_disposed) return;
_disposed = true;
}
using (new WriteLock(_disposalLocker))
{
// Check again now we're inside the lock
if (IsDisposed) return;
DisposeUnmanagedResources();
// Call to actually release resources. This method is only
// kept separate so that the entire disposal logic can be used as a VS snippet
DisposeResources();
// Indicate that the instance has been disposed.
_disposed = true;
}
if (disposing)
DisposeResources();
}
/// <summary>
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
/// </summary>
protected abstract void DisposeResources();
protected virtual void DisposeUnmanagedResources()
{
}
{ }
}
}
@@ -0,0 +1,18 @@
using System;
namespace Umbraco.Core.Exceptions
{
/// <summary>
/// An exception that is thrown if the umbraco application cannnot boot
/// </summary>
public class UmbracoStartupFailedException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Exception"/> class with a specified error message.
/// </summary>
/// <param name="message">The message that describes the error. </param>
public UmbracoStartupFailedException(string message) : base(message)
{
}
}
}
+6 -2
View File
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Web;
@@ -40,8 +41,11 @@ namespace Umbraco.Core.Logging
/// <returns></returns>
private static string PrefixThreadId(string generateMessageFormat)
{
return "[T" + Thread.CurrentThread.ManagedThreadId + "/D" + AppDomain.CurrentDomain.Id + "] " + generateMessageFormat;
}
return (_prefixThreadId ?? (_prefixThreadId = "[P" + Process.GetCurrentProcess().Id + "/T" + Thread.CurrentThread.ManagedThreadId + "/D" + AppDomain.CurrentDomain.Id + "] "))
+ generateMessageFormat;
}
private static string _prefixThreadId = null;
#region Error
/// <summary>
+185
View File
@@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.MemoryMappedFiles;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
namespace Umbraco.Core
{
// represents the main domain
class MainDom : IRegisteredObject
{
#region Vars
// our own lock for local consistency
private readonly object _locko = new object();
// async lock representing the main domain lock
private readonly AsyncLock _asyncLock;
private IDisposable _asyncLocker;
// event wait handle used to notify current main domain that it should
// release the lock because a new domain wants to be the main domain
private readonly EventWaitHandle _signal;
// indicates whether...
private volatile bool _isMainDom; // we are the main domain
private volatile bool _signaled; // we have been signaled
// actions to run before releasing the main domain
private readonly SortedList<int, Action> _callbacks = new SortedList<int, Action>();
private const int LockTimeoutMilliseconds = 90000; // (1.5 * 60 * 1000) == 1 min 30 seconds
#endregion
#region Ctor
// initializes a new instance of MainDom
public MainDom()
{
var appId = string.Empty;
// HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail
if (HostingEnvironment.ApplicationID != null)
appId = HostingEnvironment.ApplicationID.ReplaceNonAlphanumericChars(string.Empty);
var lockName = "UMBRACO-" + appId + "-MAINDOM-LCK";
_asyncLock = new AsyncLock(lockName);
var eventName = "UMBRACO-" + appId + "-MAINDOM-EVT";
_signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
}
#endregion
// register a main domain consumer
public bool Register(Action release, int weight = 100)
{
return Register(null, release, weight);
}
// register a main domain consumer
public bool Register(Action install, Action release, int weight = 100)
{
lock (_locko)
{
if (_signaled) return false;
if (install != null)
install();
if (release != null)
_callbacks.Add(weight, release);
return true;
}
}
// handles the signal requesting that the main domain is released
private void OnSignal(string source)
{
// once signaled, we stop waiting, but then there is the hosting environment
// so we have to make sure that we only enter that method once
lock (_locko)
{
LogHelper.Debug<MainDom>("Signaled" + (_signaled ? " (again)" : "") + " (" + source + ").");
if (_signaled) return;
if (_isMainDom == false) return; // probably not needed
_signaled = true;
}
try
{
LogHelper.Debug<MainDom>("Stopping...");
foreach (var callback in _callbacks.Values)
{
try
{
callback(); // no timeout on callbacks
}
catch (Exception e)
{
LogHelper.Error<MainDom>("Error while running callback, remaining callbacks will not run.", e);
throw;
}
}
LogHelper.Debug<MainDom>("Stopped.");
}
finally
{
// in any case...
_isMainDom = false;
_asyncLocker.Dispose();
LogHelper.Debug<MainDom>("Released MainDom.");
}
}
// acquires the main domain
public bool Acquire()
{
lock (_locko) // we don't want the hosting environment to interfere by signaling
{
// if signaled, too late to acquire, give up
// the handler is not installed so that would be the hosting environment
if (_signaled)
{
LogHelper.Debug<MainDom>("Cannot acquire MainDom (signaled).");
return false;
}
LogHelper.Debug<MainDom>("Acquiring MainDom...");
// signal other instances that we want the lock, then wait one the lock,
// which may timeout, and this is accepted - see comments below
// signal, then wait for the lock, then make sure the event is
// resetted (maybe there was noone listening..)
_signal.Set();
// if more than 1 instance reach that point, one will get the lock
// and the other one will timeout, which is accepted
_asyncLocker = _asyncLock.Lock(LockTimeoutMilliseconds);
_isMainDom = true;
// we need to reset the event, because otherwise we would end up
// signaling ourselves and commiting suicide immediately.
// only 1 instance can reach that point, but other instances may
// have started and be trying to get the lock - they will timeout,
// which is accepted
_signal.Reset();
_signal.WaitOneAsync()
.ContinueWith(_ => OnSignal("signal"));
HostingEnvironment.RegisterObject(this);
LogHelper.Debug<MainDom>("Acquired MainDom.");
return true;
}
}
// gets a value indicating whether we are the main domain
public bool IsMainDom
{
get { return _isMainDom; }
}
// IRegisteredObject
public void Stop(bool immediate)
{
try
{
OnSignal("environment"); // will run once
}
finally
{
HostingEnvironment.UnregisterObject(this);
}
}
}
}
+15 -1
View File
@@ -137,9 +137,23 @@ namespace Umbraco.Core.Models
new DelegateEqualityComparer<object>(
(o, o1) =>
{
//Custom comparer for enumerable if it is enumerable
if (o == null && o1 == null) return true;
//custom comparer for strings.
if (o is string || o1 is string)
{
//if one is null and another is empty then they are the same
if ((o as string).IsNullOrWhiteSpace() && (o1 as string).IsNullOrWhiteSpace())
{
return true;
}
if (o == null || o1 == null) return false;
return o.Equals(o1);
}
if (o == null || o1 == null) return false;
//Custom comparer for enumerable if it is enumerable
var enum1 = o as IEnumerable;
var enum2 = o1 as IEnumerable;
if (enum1 != null && enum2 != null)
@@ -112,7 +112,7 @@ namespace Umbraco.Core.ObjectResolution
/// <exception cref="InvalidOperationException">resolution is already frozen.</exception>
public static void Freeze()
{
LogHelper.Debug(typeof(Resolution), "Freezing resolution");
LogHelper.Debug(typeof (Resolution), "Freezing resolution");
using (new WriteLock(ConfigurationLock))
{
@@ -121,9 +121,20 @@ namespace Umbraco.Core.ObjectResolution
_isFrozen = true;
}
if (Frozen != null)
Frozen(null, null);
LogHelper.Debug(typeof(Resolution), "Resolution is frozen");
if (Frozen == null) return;
try
{
Frozen(null, null);
}
catch (Exception e)
{
LogHelper.Error(typeof (Resolution), "Exception in Frozen event handler.", e);
throw;
}
}
/// <summary>
@@ -34,15 +34,20 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
{
Delete.ForeignKey().FromTable("cmsTagRelationship").ForeignColumn("nodeId").ToTable("umbracoNode").PrimaryColumn("id");
//check for another strange really old one that might have existed
if (constraints.Any(x => x.Item1 == "cmsTagRelationship" && x.Item2 == "tagId"))
{
Delete.ForeignKey().FromTable("cmsTagRelationship").ForeignColumn("tagId").ToTable("cmsTags").PrimaryColumn("id");
}
}
else
{
//Before we try to delete this constraint, we'll see if it exists first, some older schemas never had it and some older schema's had this named
// differently than the default.
var constraint = constraints
.SingleOrDefault(x => x.Item1 == "cmsTagRelationship" && x.Item2 == "nodeId" && x.Item3.InvariantStartsWith("PK_") == false);
if (constraint != null)
var constraintMatches = constraints.Where(x => x.Item1 == "cmsTagRelationship" && x.Item2 == "nodeId" && x.Item3.InvariantStartsWith("PK_") == false);
foreach (var constraint in constraintMatches)
{
Delete.ForeignKey(constraint.Item3).OnTable("cmsTagRelationship");
}
@@ -42,11 +42,33 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
Create.Index("IX_cmsDocument_newest").OnTable("cmsDocument").OnColumn("newest").Ascending().WithOptions().NonClustered();
}
//TODO: We need to fix this for SQL Azure since it does not let you drop any clustered indexes
// Issue: http://issues.umbraco.org/issue/U4-5673
// Some work around notes:
// http://stackoverflow.com/questions/15872347/alter-clustered-index-column
// https://social.msdn.microsoft.com/Forums/azure/en-US/5cc4b302-fa42-4c62-956a-bbf79dbbd040/changing-clustered-index-in-azure?forum=ssdsgetstarted
//We need to do this for SQL Azure V2 since it does not let you drop any clustered indexes
// Issue: http://issues.umbraco.org/issue/U4-5673
if (Context.CurrentDatabaseProvider == DatabaseProviders.SqlServer || Context.CurrentDatabaseProvider == DatabaseProviders.SqlAzure)
{
var version = Context.Database.ExecuteScalar<string>("SELECT @@@@VERSION");
if (version.Contains("Microsoft SQL Azure"))
{
var parts = version.Split(new[] { '-' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToArray();
if (parts.Length > 1)
{
if (parts[1].StartsWith("11."))
{
//we want to drop the umbracoUserLogins_Index index since it is named incorrectly and then re-create it so
// it follows the standard naming convention
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("umbracoUserLogins_Index")))
{
//It's the old version that doesn't support dropping a clustered index on a table, so we need to do some manual work.
ExecuteSqlAzureSqlForChangingIndex();
}
return;
}
}
}
}
//we want to drop the umbracoUserLogins_Index index since it is named incorrectly and then re-create it so
// it follows the standard naming convention
@@ -67,5 +89,28 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
Delete.Index("IX_cmsDocument_published").OnTable("cmsDocument");
Delete.Index("IX_cmsDocument_newest").OnTable("cmsDocument");
}
private void ExecuteSqlAzureSqlForChangingIndex()
{
Context.Database.Execute(@"CREATE TABLE ""umbracoUserLogins_temp""
(
contextID uniqueidentifier NOT NULL,
userID int NOT NULL,
[timeout] bigint NOT NULL
);
CREATE CLUSTERED INDEX ""IX_umbracoUserLogins_Index"" ON ""umbracoUserLogins_temp"" (""contextID"");
INSERT INTO ""umbracoUserLogins_temp"" SELECT * FROM ""umbracoUserLogins""
DROP TABLE ""umbracoUserLogins""
CREATE TABLE ""umbracoUserLogins""
(
contextID uniqueidentifier NOT NULL,
userID int NOT NULL,
[timeout] bigint NOT NULL
);
CREATE CLUSTERED INDEX ""IX_umbracoUserLogins_Index"" ON ""umbracoUserLogins"" (""contextID"");
INSERT INTO ""umbracoUserLogins"" SELECT * FROM ""umbracoUserLogins_temp""
DROP TABLE ""umbracoUserLogins_temp""");
}
}
}
@@ -493,15 +493,10 @@ AND umbracoNode.id <> @id",
throw new InvalidOperationException("Cannot insert a pre value for a data type that has no identity");
}
//Cannot add a duplicate alias
var exists = Database.ExecuteScalar<int>(@"SELECT COUNT(*) FROM cmsDataTypePreValues
WHERE alias = @alias
AND datatypeNodeId = @dtdid",
new { alias = entity.Alias, dtdid = entity.DataType.Id });
if (exists > 0)
{
throw new DuplicateNameException("A pre value with the alias " + entity.Alias + " already exists for this data type");
}
//NOTE: We used to check that the Alias was unique for the given DataTypeNodeId prevalues list, BUT
// in reality there is no need to check the uniqueness of this alias because the only way that this code executes is
// based on an IDictionary<string, PreValue> dictionary being passed to this repository and a dictionary
// must have unique aliases by definition, so there is no need for this additional check
var dto = new DataTypePreValueDto
{
@@ -519,17 +514,12 @@ AND datatypeNodeId = @dtdid",
{
throw new InvalidOperationException("Cannot update a pre value for a data type that has no identity");
}
//Cannot change to a duplicate alias
var exists = Database.ExecuteScalar<int>(@"SELECT COUNT(*) FROM cmsDataTypePreValues
WHERE alias = @alias
AND datatypeNodeId = @dtdid
AND id <> @id",
new { id = entity.Id, alias = entity.Alias, dtdid = entity.DataType.Id });
if (exists > 0)
{
throw new DuplicateNameException("A pre value with the alias " + entity.Alias + " already exists for this data type");
}
//NOTE: We used to check that the Alias was unique for the given DataTypeNodeId prevalues list, BUT
// this causes issues when sorting the pre-values (http://issues.umbraco.org/issue/U4-5670) but in reality
// there is no need to check the uniqueness of this alias because the only way that this code executes is
// based on an IDictionary<string, PreValue> dictionary being passed to this repository and a dictionary
// must have unique aliases by definition, so there is no need for this additional check
var dto = new DataTypePreValueDto
{
+4 -8
View File
@@ -606,14 +606,10 @@ namespace Umbraco.Core
/// <returns></returns>
internal IEnumerable<T> CreateInstances<T>(IEnumerable<Type> types, bool throwException = false)
{
//Have removed logging because it doesn't really need to be logged since the time taken is generally 0ms.
//we want to know if it fails ever, not how long it took if it is only 0.
var typesAsArray = types.ToArray();
//using (DisposableTimer.DebugDuration<PluginManager>(
// String.Format("Starting instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName),
// String.Format("Completed instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName)))
//{
LogHelper.Debug<PluginManager>(string.Format("Instantiating {0} objects of type {1}", typesAsArray.Length, typeof (T).FullName));
var instances = new List<T>();
foreach (var t in typesAsArray)
{
@@ -634,7 +630,7 @@ namespace Umbraco.Core
}
}
return instances;
//}
}
/// <summary>
@@ -1,10 +1,9 @@
using System;
using System.Linq;
using System.Web;
using System.Xml;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Sync
{
@@ -13,58 +12,73 @@ namespace Umbraco.Core.Sync
/// </summary>
internal static class ServerEnvironmentHelper
{
/// <summary>
/// Returns the current umbraco base url for the current server depending on it's environment
/// status. This will attempt to determine the internal umbraco base url that can be used by the current
/// server to send a request to itself if it is in a load balanced environment.
/// </summary>
/// <returns>The full base url including schema (i.e. http://myserver:80/umbraco ) - or <c>null</c> if the url
/// cannot be determined at the moment (usually because the first request has not properly completed yet).</returns>
public static string GetCurrentServerUmbracoBaseUrl(ApplicationContext appContext, IUmbracoSettingsSection settings)
public static void TrySetApplicationUrlFromSettings(ApplicationContext appContext, IUmbracoSettingsSection settings)
{
// try umbracoSettings:settings/web.routing/@umbracoApplicationUrl
// which is assumed to:
// - end with SystemDirectories.Umbraco
// - contain a scheme
// - end or not with a slash, it will be taken care of
// eg "http://www.mysite.com/umbraco"
var url = settings.WebRouting.UmbracoApplicationUrl;
if (url.IsNullOrWhiteSpace() == false)
{
appContext.UmbracoApplicationUrl = url.TrimEnd('/');
LogHelper.Info(typeof(ServerEnvironmentHelper), "ApplicationUrl: " + appContext.UmbracoApplicationUrl + " (using web.routing/@umbracoApplicationUrl)");
return;
}
// try umbracoSettings:settings/scheduledTasks/@baseUrl
// which is assumed to:
// - end with SystemDirectories.Umbraco
// - NOT contain any scheme (because, legacy)
// - end or not with a slash, it will be taken care of
// eg "mysite.com/umbraco"
url = settings.ScheduledTasks.BaseUrl;
if (url.IsNullOrWhiteSpace() == false)
{
var ssl = GlobalSettings.UseSSL ? "s" : "";
url = "http" + ssl + "://" + url;
appContext.UmbracoApplicationUrl = url.TrimEnd('/');
LogHelper.Info(typeof(ServerEnvironmentHelper), "ApplicationUrl: " + appContext.UmbracoApplicationUrl + " (using scheduledTasks/@baseUrl)");
return;
}
// try servers
var status = GetStatus(settings);
if (status == CurrentServerEnvironmentStatus.Single)
{
// single install, return null if no config/original url, else use config/original url as base
// use http or https as appropriate
return GetBaseUrl(appContext, settings);
}
return;
// no server, nothing we can do
var servers = settings.DistributedCall.Servers.ToArray();
if (servers.Length == 0)
return;
if (servers.Any() == false)
{
// cannot be determined, return null if no config/original url, else use config/original url as base
// use http or https as appropriate
return GetBaseUrl(appContext, settings);
}
// we have servers, look for this server
foreach (var server in servers)
{
var appId = server.AppId;
var serverName = server.ServerName;
// skip if no data
if (appId.IsNullOrWhiteSpace() && serverName.IsNullOrWhiteSpace())
{
continue;
}
// if this server, build and return the url
if ((appId.IsNullOrWhiteSpace() == false && appId.Trim().InvariantEquals(HttpRuntime.AppDomainAppId))
|| (serverName.IsNullOrWhiteSpace() == false && serverName.Trim().InvariantEquals(NetworkHelper.MachineName)))
{
//match by appId or computer name! return the url configured
return string.Format("{0}://{1}:{2}/{3}",
// match by appId or computer name, return the url configured
url = string.Format("{0}://{1}:{2}/{3}",
server.ForceProtocol.IsNullOrWhiteSpace() ? "http" : server.ForceProtocol,
server.ServerAddress,
server.ForcePortnumber.IsNullOrWhiteSpace() ? "80" : server.ForcePortnumber,
IOHelper.ResolveUrl(SystemDirectories.Umbraco).TrimStart('/'));
appContext.UmbracoApplicationUrl = url.TrimEnd('/');
LogHelper.Info(typeof(ServerEnvironmentHelper), "ApplicationUrl: " + appContext.UmbracoApplicationUrl + " (using distributedCall/servers)");
}
}
// cannot be determined, return null if no config/original url, else use config/original url as base
// use http or https as appropriate
return GetBaseUrl(appContext, settings);
}
/// <summary>
@@ -113,21 +127,5 @@ namespace Umbraco.Core.Sync
return CurrentServerEnvironmentStatus.Slave;
}
private static string GetBaseUrl(ApplicationContext appContext, IUmbracoSettingsSection settings)
{
return (
// is config empty?
settings.ScheduledTasks.BaseUrl.IsNullOrWhiteSpace()
// is the orig req empty?
? appContext.OriginalRequestUrl.IsNullOrWhiteSpace()
// we've got nothing
? null
//the orig req url is not null, use that
: string.Format("http{0}://{1}", GlobalSettings.UseSSL ? "s" : "", appContext.OriginalRequestUrl)
// the config has been specified, use that
: string.Format("http{0}://{1}", GlobalSettings.UseSSL ? "s" : "", settings.ScheduledTasks.BaseUrl))
.EnsureEndsWith('/');
}
}
}
+19 -7
View File
@@ -137,7 +137,7 @@ namespace Umbraco.Core
}
return _allAssemblies;
}
}
}
/// <summary>
@@ -226,7 +226,7 @@ namespace Umbraco.Core
}
return LocalFilteredAssemblyCache;
}
}
}
/// <summary>
@@ -451,7 +451,7 @@ namespace Umbraco.Core
var allTypes = GetTypesWithFormattedException(a)
.ToArray();
var attributedTypes = new Type[] {};
var attributedTypes = new Type[] { };
try
{
//now filter the types based on the onlyConcreteClasses flag, not interfaces, not static classes but have
@@ -480,7 +480,8 @@ namespace Umbraco.Core
//now we need to include types that may be inheriting from sub classes of the attribute type being searched for
//so we will search in assemblies that reference those types too.
foreach (var subTypesInAssembly in allAttributeTypes.GroupBy(x => x.Assembly)){
foreach (var subTypesInAssembly in allAttributeTypes.GroupBy(x => x.Assembly))
{
//So that we are not scanning too much, we need to group the sub types:
// * if there is more than 1 sub type in the same assembly then we should only search on the 'lowest base' type.
@@ -610,7 +611,7 @@ namespace Umbraco.Core
catch (TypeLoadException ex)
{
LogHelper.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", a), ex);
continue;
continue;
}
//add the types to our list to return
@@ -618,7 +619,7 @@ namespace Umbraco.Core
{
foundAssignableTypes.Add(t);
}
//now we need to include types that may be inheriting from sub classes of the type being searched for
//so we will search in assemblies that reference those types too.
foreach (var subTypesInAssembly in allSubTypes.GroupBy(x => x.Assembly))
@@ -699,7 +700,18 @@ namespace Umbraco.Core
#endregion
//TODO: This isn't very elegant, and will have issues since the AppDomain.CurrentDomain
// doesn't actualy load in all assemblies, only the types that have been referenced so far.
// However, in a web context, the BuildManager will have executed which will force all assemblies
// to be loaded so it's fine for now.
internal static Type GetTypeByName(string typeName)
{
var type = Type.GetType(typeName);
if (type != null) return type;
return AppDomain.CurrentDomain.GetAssemblies()
.Select(x => x.GetType(typeName))
.FirstOrDefault(x => x != null);
}
}
}
+3
View File
@@ -312,10 +312,12 @@
<Compile Include="Events\SaveEventArgs.cs" />
<Compile Include="Events\SendToPublishEventArgs.cs" />
<Compile Include="Exceptions\InvalidCompositionException.cs" />
<Compile Include="Exceptions\UmbracoStartupFailedException.cs" />
<Compile Include="HideFromTypeFinderAttribute.cs" />
<Compile Include="HttpContextExtensions.cs" />
<Compile Include="IApplicationEventHandler.cs" />
<Compile Include="IDisposeOnRequestEnd.cs" />
<Compile Include="MainDom.cs" />
<Compile Include="Manifest\GridEditorConverter.cs" />
<Compile Include="Media\Exif\BitConverterEx.cs" />
<Compile Include="Media\Exif\ExifBitConverter.cs" />
@@ -1214,6 +1216,7 @@
<Compile Include="UriExtensions.cs" />
<Compile Include="SystemUtilities.cs" />
<Compile Include="UrlHelperExtensions.cs" />
<Compile Include="WaitHandleExtensions.cs" />
<Compile Include="WriteLock.cs" />
<Compile Include="XmlExtensions.cs" />
<Compile Include="XmlHelper.cs" />
+47 -3
View File
@@ -36,6 +36,19 @@ namespace Umbraco.Core
//don't output the MVC version header (security)
MvcHandler.DisableMvcResponseHeader = true;
//take care of unhandled exceptions - there is nothing we can do to
// prevent the entire w3wp process to go down but at least we can try
// and log the exception
AppDomain.CurrentDomain.UnhandledException += (_, args) =>
{
var exception = (Exception) args.ExceptionObject;
var isTerminating = args.IsTerminating; // always true?
var msg = "Unhandled exception in AppDomain";
if (isTerminating) msg += " (terminating)";
LogHelper.Error<UmbracoApplicationBase>(msg, exception);
};
//boot up the application
GetBootManager()
.Initialize()
@@ -73,7 +86,18 @@ namespace Umbraco.Core
protected virtual void OnApplicationStarting(object sender, EventArgs e)
{
if (ApplicationStarting != null)
ApplicationStarting(sender, e);
{
try
{
ApplicationStarting(sender, e);
}
catch (Exception ex)
{
LogHelper.Error<UmbracoApplicationBase>("An error occurred in an ApplicationStarting event handler", ex);
throw;
}
}
}
/// <summary>
@@ -84,7 +108,17 @@ namespace Umbraco.Core
protected virtual void OnApplicationStarted(object sender, EventArgs e)
{
if (ApplicationStarted != null)
ApplicationStarted(sender, e);
{
try
{
ApplicationStarted(sender, e);
}
catch (Exception ex)
{
LogHelper.Error<UmbracoApplicationBase>("An error occurred in an ApplicationStarted event handler", ex);
throw;
}
}
}
/// <summary>
@@ -95,7 +129,17 @@ namespace Umbraco.Core
private void OnApplicationInit(object sender, EventArgs e)
{
if (ApplicationInit != null)
ApplicationInit(sender, e);
{
try
{
ApplicationInit(sender, e);
}
catch (Exception ex)
{
LogHelper.Error<UmbracoApplicationBase>("An error occurred in an ApplicationInit event handler", ex);
throw;
}
}
}
/// <summary>
+1 -1
View File
@@ -49,7 +49,7 @@ namespace Umbraco.Core
var urlPath = fullUrlPath.TrimStart(appPath).EnsureStartsWith('/');
//check if this is in the umbraco back office
var isUmbracoPath = urlPath.InvariantStartsWith(GlobalSettings.Path.EnsureStartsWith('/'));
var isUmbracoPath = urlPath.InvariantStartsWith(GlobalSettings.Path.EnsureStartsWith('/').TrimStart(appPath.EnsureStartsWith('/')).EnsureStartsWith('/'));
//if not, then def not back office
if (isUmbracoPath == false) return false;
+44
View File
@@ -0,0 +1,44 @@
using System.Threading;
using System.Threading.Tasks;
namespace Umbraco.Core
{
internal static class WaitHandleExtensions
{
// http://stackoverflow.com/questions/25382583/waiting-on-a-named-semaphore-with-waitone100-vs-waitone0-task-delay100
// http://blog.nerdbank.net/2011/07/c-await-for-waithandle.html
// F# has a AwaitWaitHandle method that accepts a time out... and seems pretty complex...
// version below should be OK
public static Task WaitOneAsync(this WaitHandle handle, int millisecondsTimeout = Timeout.Infinite)
{
var tcs = new TaskCompletionSource<object>();
var callbackHandleInitLock = new object();
lock (callbackHandleInitLock)
{
RegisteredWaitHandle callbackHandle = null;
// ReSharper disable once RedundantAssignment
callbackHandle = ThreadPool.RegisterWaitForSingleObject(
handle,
(state, timedOut) =>
{
tcs.SetResult(null);
// we take a lock here to make sure the outer method has completed setting the local variable callbackHandle.
lock (callbackHandleInitLock)
{
// ReSharper disable once PossibleNullReferenceException
// ReSharper disable once AccessToModifiedClosure
callbackHandle.Unregister(null);
}
},
/*state:*/ null,
/*millisecondsTimeOutInterval:*/ millisecondsTimeout,
/*executeOnlyOnce:*/ true);
}
return tcs.Task;
}
}
}
@@ -581,15 +581,11 @@ namespace Umbraco.Tests.Scheduling
var waitHandle = new ManualResetEvent(false);
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
runner.TaskCompleted += (sender, args) => runCount++;
runner.TaskStarting += async (sender, args) =>
runner.TaskCompleted += (sender, args) =>
{
//wait for each task to finish once it's started
await sender.CurrentThreadingTask;
runCount++;
if (runCount > 3)
{
waitHandle.Set();
}
};
var task = new MyRecurringTask(runner, 200, 500);
@@ -604,7 +600,10 @@ namespace Umbraco.Tests.Scheduling
Assert.GreaterOrEqual(runCount, 4);
// stops recurring
runner.Shutdown(false, false);
runner.Shutdown(false, true);
// check that task has been disposed (timer has been killed, etc)
Assert.IsTrue(task.IsDisposed);
}
}
@@ -651,15 +650,12 @@ namespace Umbraco.Tests.Scheduling
var waitHandle = new ManualResetEvent(false);
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
runner.TaskCompleted += (sender, args) => runCount++;
runner.TaskStarting += async (sender, args) =>
runner.TaskCompleted += (sender, args) =>
{
//wait for each task to finish once it's started
await sender.CurrentThreadingTask;
runCount++;
if (runCount > 3)
{
waitHandle.Set();
}
};
var task = new MyDelayedRecurringTask(runner, 2000, 1000);
@@ -783,37 +779,31 @@ namespace Umbraco.Tests.Scheduling
}
}
private class MyDelayedRecurringTask : DelayedRecurringTaskBase<MyDelayedRecurringTask>
private class MyDelayedRecurringTask : RecurringTaskBase
{
public bool HasRun { get; private set; }
public MyDelayedRecurringTask(IBackgroundTaskRunner<MyDelayedRecurringTask> runner, int delayMilliseconds, int periodMilliseconds)
public MyDelayedRecurringTask(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds)
: base(runner, delayMilliseconds, periodMilliseconds)
{ }
private MyDelayedRecurringTask(MyDelayedRecurringTask source)
: base(source)
{ }
public override bool IsAsync
{
get { return false; }
}
public override void PerformRun()
public override bool PerformRun()
{
HasRun = true;
return true; // repeat
}
public override Task PerformRunAsync()
public override Task<bool> PerformRunAsync(CancellationToken token)
{
throw new NotImplementedException();
}
protected override MyDelayedRecurringTask GetRecurring()
{
return new MyDelayedRecurringTask(this);
}
public override bool RunsOnShutdown { get { return true; } }
}
private class MyDelayedTask : ILatchedBackgroundTask
@@ -867,29 +857,23 @@ namespace Umbraco.Tests.Scheduling
{ }
}
private class MyRecurringTask : RecurringTaskBase<MyRecurringTask>
private class MyRecurringTask : RecurringTaskBase
{
private readonly int _runMilliseconds;
public MyRecurringTask(IBackgroundTaskRunner<MyRecurringTask> runner, int runMilliseconds, int periodMilliseconds)
: base(runner, periodMilliseconds)
public MyRecurringTask(IBackgroundTaskRunner<RecurringTaskBase> runner, int runMilliseconds, int periodMilliseconds)
: base(runner, 0, periodMilliseconds)
{
_runMilliseconds = runMilliseconds;
}
private MyRecurringTask(MyRecurringTask source, int runMilliseconds)
: base(source)
{
_runMilliseconds = runMilliseconds;
}
public override void PerformRun()
public override bool PerformRun()
{
Thread.Sleep(_runMilliseconds);
return true; // repeat
}
public override Task PerformRunAsync()
public override Task<bool> PerformRunAsync(CancellationToken token)
{
throw new NotImplementedException();
}
@@ -899,10 +883,7 @@ namespace Umbraco.Tests.Scheduling
get { return false; }
}
protected override MyRecurringTask GetRecurring()
{
return new MyRecurringTask(this, _runMilliseconds);
}
public override bool RunsOnShutdown { get { return false; } }
}
private class MyTask : BaseTask
@@ -1,92 +1,98 @@
using System.Configuration;
using System.IO;
using System.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Profiling;
using Umbraco.Core.Sync;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests
{
[TestFixture]
public class ServerEnvironmentHelperTests
{
// note: in tests, read appContext._umbracoApplicationUrl and not the property,
// because reading the property does run some code, as long as the field is null.
[Test]
public void Get_Base_Url_Single_Server_Orig_Request_Url_No_SSL()
public void SetApplicationUrlWhenNoSettings()
{
var appContext = new ApplicationContext(null)
{
OriginalRequestUrl = "test.com"
UmbracoApplicationUrl = null // NOT set
};
ConfigurationManager.AppSettings.Set("umbracoUseSSL", "false");
ConfigurationManager.AppSettings.Set("umbracoUseSSL", "true"); // does not make a diff here
var result = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl(
appContext,
Mock.Of<IUmbracoSettingsSection>(
section =>
section.DistributedCall == Mock.Of<IDistributedCallSection>(callSection => callSection.Servers == Enumerable.Empty<IServer>())
&& section.ScheduledTasks == Mock.Of<IScheduledTasksSection>()));
Assert.AreEqual("http://test.com/", result);
}
[Test]
public void Get_Base_Url_Single_Server_Orig_Request_Url_With_SSL()
{
var appContext = new ApplicationContext(null)
{
OriginalRequestUrl = "test.com"
};
ConfigurationManager.AppSettings.Set("umbracoUseSSL", "true");
var result = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl(
appContext,
ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(appContext,
Mock.Of<IUmbracoSettingsSection>(
section =>
section.DistributedCall == Mock.Of<IDistributedCallSection>(callSection => callSection.Servers == Enumerable.Empty<IServer>())
&& section.WebRouting == Mock.Of<IWebRoutingSection>(wrSection => wrSection.UmbracoApplicationUrl == (string) null)
&& section.ScheduledTasks == Mock.Of<IScheduledTasksSection>()));
Assert.AreEqual("https://test.com/", result);
// still NOT set
Assert.IsNull(appContext._umbracoApplicationUrl);
}
[Test]
public void Get_Base_Url_Single_Server_Via_Config_Url_No_SSL()
public void SetApplicationUrlFromDcSettingsNoSsl()
{
var appContext = new ApplicationContext(null);
ConfigurationManager.AppSettings.Set("umbracoUseSSL", "false");
var result = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl(
appContext,
ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(appContext,
Mock.Of<IUmbracoSettingsSection>(
section =>
section.DistributedCall == Mock.Of<IDistributedCallSection>(callSection => callSection.Servers == Enumerable.Empty<IServer>())
&& section.ScheduledTasks == Mock.Of<IScheduledTasksSection>(tasksSection => tasksSection.BaseUrl == "mycoolhost.com/hello/world")));
&& section.WebRouting == Mock.Of<IWebRoutingSection>(wrSection => wrSection.UmbracoApplicationUrl == (string) null)
&& section.ScheduledTasks == Mock.Of<IScheduledTasksSection>(tasksSection => tasksSection.BaseUrl == "mycoolhost.com/hello/world/")));
Assert.AreEqual("http://mycoolhost.com/hello/world/", result);
Assert.AreEqual("http://mycoolhost.com/hello/world", appContext._umbracoApplicationUrl);
}
[Test]
public void Get_Base_Url_Single_Server_Via_Config_Url_With_SSL()
public void SetApplicationUrlFromDcSettingsSsl()
{
var appContext = new ApplicationContext(null);
ConfigurationManager.AppSettings.Set("umbracoUseSSL", "true");
var result = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl(
appContext,
ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(appContext,
Mock.Of<IUmbracoSettingsSection>(
section =>
section.DistributedCall == Mock.Of<IDistributedCallSection>(callSection => callSection.Servers == Enumerable.Empty<IServer>())
&& section.WebRouting == Mock.Of<IWebRoutingSection>(wrSection => wrSection.UmbracoApplicationUrl == (string) null)
&& section.ScheduledTasks == Mock.Of<IScheduledTasksSection>(tasksSection => tasksSection.BaseUrl == "mycoolhost.com/hello/world")));
Assert.AreEqual("https://mycoolhost.com/hello/world/", result);
Assert.AreEqual("https://mycoolhost.com/hello/world", appContext._umbracoApplicationUrl);
}
[Test]
public void SetApplicationUrlFromWrSettingsSsl()
{
var appContext = new ApplicationContext(null);
ConfigurationManager.AppSettings.Set("umbracoUseSSL", "true"); // does not make a diff here
ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(appContext,
Mock.Of<IUmbracoSettingsSection>(
section =>
section.DistributedCall == Mock.Of<IDistributedCallSection>(callSection => callSection.Servers == Enumerable.Empty<IServer>())
&& section.WebRouting == Mock.Of<IWebRoutingSection>(wrSection => wrSection.UmbracoApplicationUrl == "httpx://whatever.com/hello/world/")
&& section.ScheduledTasks == Mock.Of<IScheduledTasksSection>(tasksSection => tasksSection.BaseUrl == "mycoolhost.com/hello/world")));
Assert.AreEqual("httpx://whatever.com/hello/world", appContext._umbracoApplicationUrl);
}
}
}
+2 -2
View File
@@ -58,9 +58,9 @@
<Reference Include="AutoMapper.Net4">
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.63.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine, Version=0.1.65.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.63.0\lib\Examine.dll</HintPath>
<HintPath>..\packages\Examine.0.1.65.0\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -14,7 +14,7 @@ namespace Umbraco.Tests.UmbracoExamine
public void Events_Ignoring_Node()
{
//change the parent id so that they are all ignored
var existingCriteria = ((IndexCriteria)_indexer.IndexerData);
var existingCriteria = _indexer.IndexerData;
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
999); //change to 999
@@ -53,7 +53,7 @@ namespace Umbraco.Tests.UmbracoExamine
public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID()
{
//change parent id to 1116
var existingCriteria = ((IndexCriteria)_indexer.IndexerData);
var existingCriteria = _indexer.IndexerData;
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
1116);
@@ -110,7 +110,7 @@ namespace Umbraco.Tests.UmbracoExamine
_indexer.ReIndexNode(node, IndexTypes.Media);
//change the parent node id to be the one it used to exist under
var existingCriteria = ((IndexCriteria)_indexer.IndexerData);
var existingCriteria = _indexer.IndexerData;
_indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes,
2222);
+9
View File
@@ -4,12 +4,19 @@ using System.Linq;
using System.Text;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.IO;
namespace Umbraco.Tests
{
[TestFixture]
public class UriExtensionsTests
{
[TearDown]
public void TearDown()
{
SystemDirectories.Root = "";
}
[TestCase("http://www.domain.com/umbraco", "", true)]
[TestCase("http://www.domain.com/Umbraco/", "", true)]
[TestCase("http://www.domain.com/umbraco/default.aspx", "", true)]
@@ -35,6 +42,8 @@ namespace Umbraco.Tests
[TestCase("http://www.domain.com/umbraco/test/legacyAjaxCalls.ashx?some=query&blah=js", "", true)]
public void Is_Back_Office_Request(string input, string virtualPath, bool expected)
{
SystemDirectories.Root = virtualPath;
var source = new Uri(input);
Assert.AreEqual(expected, source.IsBackOfficeRequest(virtualPath));
}
+9 -9
View File
@@ -2,25 +2,25 @@
<packages>
<package id="AspNetWebApi.SelfHost" version="4.0.20710.0" targetFramework="net45" />
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
<package id="Examine" version="0.1.63.0" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net40" />
<package id="Examine" version="0.1.65.0" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net4" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" targetFramework="net40" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net4" />
<package id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" targetFramework="net4" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net4" />
<package id="Microsoft.AspNet.WebApi" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.SelfHost" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net4" />
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="MiniProfiler" version="2.1.0" targetFramework="net45" />
<package id="Moq" version="4.1.1309.0919" targetFramework="net45" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" />
<package id="NUnit" version="2.6.2" targetFramework="net40" />
<package id="Selenium.WebDriver" version="2.32.0" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net4" />
<package id="Selenium.WebDriver" version="2.32.0" targetFramework="net4" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
<package id="SqlServerCE" version="4.0.0.0" targetFramework="net40" />
<package id="SqlServerCE" version="4.0.0.0" targetFramework="net4" />
</packages>
+8 -4
View File
@@ -22,9 +22,13 @@
"jquery": "2.0.3",
"jquery-file-upload": "~9.4.0",
"jquery-ui": "1.10.3",
"angular-dynamic-locale": "~0.1.27"
"angular-dynamic-locale": "~0.1.27",
"bootstrap-tabdrop": "~1.0.0"
},
"exportsOverride": {
"bootstrap-tabdrop": {
"": "build/js/bootstrap-tabdrop.min.js"
},
"rgrove-lazyload": {
"": "lazyload.js"
},
@@ -38,7 +42,7 @@
"": "tmhDynamicLocale.min.{js,js.map}"
},
"jquery": {
"": "jquery.min.{js,map}"
"": "jquery.min.{js,map}"
},
"jquery-file-upload": {
"": "**/jquery.{fileupload,fileupload-process,fileupload-angular,fileupload-image}.js"
@@ -53,9 +57,9 @@
"blueimp-tmpl": {
"ignore": "*.ignore"
},
"blueimp-canvas-to-blob": {
"ignore": "*.ignore"
}
}
}
}
+1 -1
View File
@@ -418,7 +418,7 @@ module.exports = function (grunt) {
//this is the same as 'byComponent', however we will not allow
// folders with '.' in them since the grunt copy task does not like that
var componentWithoutPeriod = component.replace(".", "-");
return path.join(componentWithoutPeriod, type);
return path.join(componentWithoutPeriod, type);
}
}
}
@@ -35,6 +35,14 @@ var app = angular.module("Umbraco.canvasdesigner", ['colorpicker', 'ui.slider',
$scope.previewDevice = device;
};
/*****************************************************************************/
/* Exit Preview */
/*****************************************************************************/
$scope.exitPreview = function () {
window.top.location.href = "/umbraco/endPreview.aspx?redir=%2f" + $scope.pageId;
};
/*****************************************************************************/
/* UI designer managment */
/*****************************************************************************/
@@ -34,6 +34,9 @@
<i></i>
</a>
</li>
<li ng-click="exitPreview()">
<a href="#" title="Exit Preview"><i class="icon icon-wrong"></i><span> </span></a>
</li>
</ul>
<ul class="sections" ng-class="{selected: !showDevicesPreview}" ng-if="enableCanvasdesigner > 0">
@@ -40,6 +40,8 @@ angular.module("umbraco.directives")
}
});
$('.nav-pills, .nav-tabs').tabdrop();
}
scope.showTabs = iAttrs.tabs ? true : false;
@@ -28,7 +28,7 @@ angular.module("umbraco.install").factory('installerService', function($rootScop
'Umbraco is the best of both worlds: 100% free and open source, and backed by a professional and profitable company',
"There's a pretty big chance, you've visited a website powered by Umbraco today",
"'Umbraco-spotting' is the game of spotting big brands running Umbraco",
"At least 2 people have the Umbraco logo tattooed on them",
"At least 3 people have the Umbraco logo tattooed on them",
"'Umbraco' is the danish name for an allen key",
"Umbraco has been around since 2005, that's a looong time in IT",
"More than 400 people from all over the world meet each year in Denmark in June for our annual conference <a target='_blank' href='http://codegarden14.com'>CodeGarden</a>",
@@ -335,4 +335,4 @@ angular.module("umbraco.install").factory('installerService', function($rootScop
};
return service;
});
});
@@ -176,3 +176,70 @@ body {
.emptySection #contentwrapper {left: 80px;}
.emptySection #speechbubble {left: 0;}
.emptySection #navigation {display: none}
@media (max-width: 767px) {
// make leftcolumn smaller on tablets
#leftcolumn {
width: 60px;
}
#applications ul.sections {
width: 60px;
}
ul.sections.sections-tray {
width: 60px;
}
#applications-tray {
left: 60px;
}
#navigation {
left: 60px;
}
#contentwrapper, #contentcolumn {
left: 30px;
}
#umbracoMainPageBody .umb-modal-left.fade.in {
margin-left: 60px;
}
}
@media (max-width: 500px) {
// make leftcolumn smaller on mobiles
#leftcolumn {
width: 40px;
}
#applications ul.sections {
width: 40px;
}
ul.sections li [class^="icon-"]:before {
font-size: 25px!important;
}
#applications ul.sections li.avatar a img {
width: 25px;
}
ul.sections a span {
display:none !important;
}
#applications ul.sections-tray {
width: 40px;
}
ul.sections.sections-tray {
width: 40px;
}
#applications-tray {
left: 40px;
}
#navigation {
left: 40px;
}
#contentwrapper, #contentcolumn {
left: 20px;
}
#umbracoMainPageBody .umb-modal-left.fade.in {
margin-left: 40px;
width: 85%!important;
}
}
+194 -138
View File
@@ -1,19 +1,27 @@
// Panel
// -------------------------
.umb-panel{
background: white;
position: absolute;
top: 0px; bottom: 0px; left: 0px; right: 0px;}
.umb-panel {
background: white;
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
}
.umb-panel-nobody {
padding-top: 100px;
overflow: auto;
}
.umb-panel-nobody{padding-top: 100px; overflow: auto;}
.umb-panel-header {
background: @grayLighter;
border-bottom: 1px solid @grayLight;
position: absolute;
height: 99px;
top: 0px;
right: 0px;
left: 0px;
background: @grayLighter;
border-bottom: 1px solid @grayLight;
position: absolute;
height: 99px;
top: 0px;
right: 0px;
left: 0px;
}
.umb-panel-body {
@@ -25,6 +33,7 @@
clear: both;
overflow: auto;
}
.umb-panel-body.no-header {
top: 20px;
}
@@ -38,71 +47,83 @@
}
.umb-panel-header .umb-headline, .umb-panel-header h1 {
font-size: 16px;
border: none;
background: none;
margin: 15px 0 0 20px;
padding: 3px 5px;
line-height: 1.4;
height: auto;
width: 100%;
border: 1px solid @grayLighter;
font-size: 16px;
border: none;
background: none;
margin: 15px 0 0 20px;
padding: 3px 5px;
line-height: 1.4;
height: auto;
width: 100%;
border: 1px solid @grayLighter;
}
.umb-panel-header .umb-headline:focus,.umb-panel-header .umb-headline:active {
border: 1px solid @grayLight;
background-color: @white;
.umb-panel-header .umb-headline:focus, .umb-panel-header .umb-headline:active {
border: 1px solid @grayLight;
background-color: @white;
}
.umb-headline-editor-wrapper{
position: relative;
.umb-headline-editor-wrapper {
position: relative;
}
.umb-headline-editor-wrapper .help-inline{
right: 0px;
top: 25px;
position: absolute;
font-size: 10px;
color: @red;
}
.umb-headline-editor-wrapper .help-inline {
right: 0px;
top: 25px;
position: absolute;
font-size: 10px;
color: @red;
}
.umb-panel-header .umb-nav-tabs{
bottom: -1px;
position: absolute;
padding: 0px 0px 0px 20px;
.umb-panel-header .umb-nav-tabs {
bottom: -1px;
position: absolute;
padding: 0px 0px 0px 20px;
}
.umb-panel-header p {
margin:0px 20px;
margin: 0px 20px;
}
.umb-btn-toolbar .dimmed, .umb-dimmed{
opacity: 0.6;
.umb-btn-toolbar .dimmed, .umb-dimmed {
opacity: 0.6;
}
.umb-headline-editor-wrapper input {
background: none;
border: none;
margin: -6px 0 0 0;
padding: 0 0 2px 0;
border-radius: 0;
line-height: normal;
border: 1px solid transparent;
color: @black;
letter-spacing: -0.01em
}
.umb-headline-editor-wrapper input.ng-invalid {
color: @red;
.umb-headline-editor-wrapper input {
background: none;
border: none;
margin: -6px 0 0 0;
padding: 0 0 2px 0;
border-radius: 0;
line-height: normal;
border: 1px solid transparent;
color: @black;
letter-spacing: -0.01em;
}
.umb-headline-editor-wrapper input.ng-invalid::-webkit-input-placeholder {color: @red; line-height: 22px;}
.umb-headline-editor-wrapper input.ng-invalid::-moz-placeholder {color: @red; line-height: 22px;}
.umb-headline-editor-wrapper input.ng-invalid:-ms-input-placeholder {color: @red; line-height: 22px;}
.umb-headline-editor-wrapper input.ng-invalid {
color: @red;
}
.umb-headline-editor-wrapper input.ng-invalid::-webkit-input-placeholder {
color: @red;
line-height: 22px;
}
.umb-headline-editor-wrapper input.ng-invalid::-moz-placeholder {
color: @red;
line-height: 22px;
}
.umb-headline-editor-wrapper input.ng-invalid:-ms-input-placeholder {
color: @red;
line-height: 22px;
}
.umb-panel-header i {
font-size: 13px;
vertical-align: middle;
font-size: 13px;
vertical-align: middle;
}
.umb-panel-header-meta {
@@ -115,65 +136,76 @@
}
.umb-panel-footer {
margin: 0;
padding: 20px;
z-index: 999;
position: absolute;
bottom: 0px;
left: 0px;
right: 0px;
margin: 0;
padding: 20px;
z-index: 999;
position: absolute;
bottom: 0px;
left: 0px;
right: 0px;
}
/* Publish */
.umb-btn-toolbar .dropdown-menu {
right: 0;
left: auto;
border-radius: @tabsBorderRadius;
box-shadow: none;
padding: 0
}
right: 0;
left: auto;
border-radius: @tabsBorderRadius;
box-shadow: none;
padding: 0;
}
.umb-btn-toolbar .dropdown-menu small {
background: #fef9db;
display: block;
padding: 10px 20px;
}
.umb-btn-toolbar .dropdown-menu small {
background: #fef9db;
display: block;
padding: 10px 20px;
}
.umb-btn-toolbar .dropdown-menu .btn {
.umb-btn-toolbar .dropdown-menu .btn  {
margin: 20px 29px;
width: 80px
}
width: 80px;
}
/* tab buttons */
.umb-bottom-bar{
.umb-bottom-bar {
background: white;
-webkit-box-shadow: 0px -18px 20px rgba(255, 255, 255, 1);
-moz-box-shadow: 0px -18px 20px rgba(255, 255, 255, 1);
box-shadow: 0px -18px 20px rgba(255, 255, 255, 1);
-moz-box-shadow: 0px -18px 20px rgba(255, 255, 255, 1);
box-shadow: 0px -18px 20px rgba(255, 255, 255, 1);
border-top: 1px solid @grayLighter;
padding: 10px 0 10px 0;
position: fixed;
bottom: 0px;
bottom: 0;
left: 100px;
right: 40px;
z-index: 6010;
}
.umb-tab-buttons{
padding-left: 0px;
}
@media (min-width: 1101px) {
.umb-bottom-bar {
@media (min-width: 1101px) {
left: 460px;
}
}
}
.umb-tab-pane{padding-bottom: 90px}
.umb-tab-buttons {
padding-left: 0;
.tab-content{overflow: visible; }
> .btn-group:not([style*="display:none"]):not([style*="display: none"]) {
margin-left: 0;
}
.umb-panel-footer-nav{
@media (min-width: 768px) {
padding-left: 180px;
}
}
.umb-tab-pane {
padding-bottom: 90px;
}
.tab-content {
overflow: visible;
}
.umb-panel-footer-nav {
position: absolute;
bottom: 0px;
height: 30px;
@@ -187,93 +219,117 @@
}
.umb-panel-footer-nav li a {
border-radius: 0;
display: block;
float: left;
height: 30px;
background: @grayLighter;
text-align: center;
padding: 8px 0px 8px 30px;
position: relative;
margin: 0 1px 0 0;
text-decoration: none;
color: @gray;
font-size: 11px;
border-radius: 0;
display: block;
float: left;
height: 30px;
background: @grayLighter;
text-align: center;
padding: 8px 0px 8px 30px;
position: relative;
margin: 0 1px 0 0;
text-decoration: none;
color: @gray;
font-size: 11px;
}
.umb-panel-footer-nav li a:after {
content: "";
border-top: 16px solid transparent;
border-bottom: 16px solid transparent;
border-left: 16px solid @grayLighter;
position: absolute; right: -16px; top: 0;
z-index: 1;
}
.umb-panel-footer-nav li a:before {
content: "";
border-top: 16px solid transparent;
border-bottom: 16px solid transparent;
border-left: 16px solid @grayLight;
position: absolute; left: 0; top: 0;
content: "";
border-top: 16px solid transparent;
border-bottom: 16px solid transparent;
border-left: 16px solid @grayLighter;
position: absolute;
right: -16px;
top: 0;
z-index: 1;
}
.umb-panel-footer-nav li:first-child a{
padding-left: 20px;
.umb-panel-footer-nav li a:before {
content: "";
border-top: 16px solid transparent;
border-bottom: 16px solid transparent;
border-left: 16px solid @grayLight;
position: absolute;
left: 0;
top: 0;
}
.umb-panel-footer-nav li:first-child a {
padding-left: 20px;
}
.umb-panel-footer-nav li:first-child a:before {
display: none;
display: none;
}
.umb-panel-footer-nav li:last-child a:after {
display: none;
display: none;
}
// Utility classes
// Utility classes
//SD: Had to add these because if we want to use the bootstrap text colors
// in a panel/editor they'll all show up as white on white - so we need to use the
// form styles
//SD: Had to add these because if we want to use the bootstrap text colors
// in a panel/editor they'll all show up as white on white - so we need to use the
// form styles
.umb-dialog .muted,
.umb-panel .muted { color: @grayLight; }
.umb-panel .muted {
color: @grayLight;
}
.umb-dialog a.muted:hover,
.umb-dialog a.muted:focus,
.umb-panel a.muted:hover,
.umb-panel a.muted:focus { color: darken(@grayLight, 10%); }
.umb-panel a.muted:focus {
color: darken(@grayLight, 10%);
}
.umb-dialog .text-warning,
.umb-panel .text-warning { color: @formWarningText; }
.umb-panel .text-warning {
color: @formWarningText;
}
.umb-dialog a.text-warning:hover,
.umb-dialog a.text-warning:focus,
.umb-panel a.text-warning:hover,
.umb-panel a.text-warning:focus { color: darken(@formWarningText, 10%); }
.umb-panel a.text-warning:focus {
color: darken(@formWarningText, 10%);
}
.umb-dialog .text-error,
.umb-panel .text-error { color: @formErrorText; }
.umb-panel .text-error {
color: @formErrorText;
}
.umb-dialog a.text-error:hover,
.umb-dialog a.text-error:focus,
.umb-panel a.text-error:hover,
.umb-panel a.text-error:focus { color: darken(@formErrorText, 10%); }
.umb-panel a.text-error:focus {
color: darken(@formErrorText, 10%);
}
.umb-dialog .text-info,
.umb-panel .text-info { color: @formInfoText; }
.umb-panel .text-info {
color: @formInfoText;
}
.umb-dialog a.text-info:hover,
.umb-dialog a.text-info:focus,
.umb-panel a.text-info:hover,
.umb-panel a.text-info:focus { color: darken(@formInfoText, 10%); }
.umb-panel a.text-info:focus {
color: darken(@formInfoText, 10%);
}
.umb-dialog .text-success,
.umb-panel .text-success { color: @formSuccessText; }
.umb-panel .text-success {
color: @formSuccessText;
}
.umb-dialog a.text-success:hover,
.umb-dialog a.text-success:focus,
.umb-panel a.text-success:hover,
.umb-panel a.text-success:focus { color: darken(@formSuccessText, 10%); }
.umb-panel a.text-success:focus {
color: darken(@formSuccessText, 10%);
}
@@ -522,3 +522,5 @@ ul.color-picker li a {
.bootstrap-datetimepicker-widget .btn{padding: 0;}
.bootstrap-datetimepicker-widget .picker-switch .btn{ background: none; border: none;}
.umb-datepicker .input-append .add-on{cursor: pointer;}
.umb-datepicker p {margin-top:10px;}
.umb-datepicker p a{color: @gray;}
+13 -9
View File
@@ -1,28 +1,32 @@
LazyLoad.js(
[
'lib/jquery/jquery.min.js',
'lib/jquery-ui/jquery-ui.min.js',
/* 1.1.5 */
'lib/angular/1.1.5/angular.min.js',
'lib/underscore/underscore-min.js',
'lib/jquery-ui/jquery-ui.min.js',
'lib/angular/1.1.5/angular-cookies.min.js',
'lib/angular/1.1.5/angular-mobile.min.js',
'lib/angular/1.1.5/angular-mocks.js',
'lib/angular/1.1.5/angular-mobile.js',
'lib/angular/1.1.5/angular-sanitize.min.js',
'lib/angular/angular-ui-sortable.js',
'lib/angular-dynamic-locale/tmhDynamicLocale.min.js',
/* App-wide file-upload helper */
'lib/blueimp-load-image/load-image.all.min.js',
'lib/jquery-file-upload/jquery.fileupload.js',
'lib/jquery-file-upload/jquery.fileupload-process.js',
'lib/jquery-file-upload/jquery.fileupload-image.js',
'lib/jquery-file-upload/jquery.fileupload-angular.js',
'lib/bootstrap/js/bootstrap.2.3.2.min.js',
'lib/underscore/underscore-min.js',
'lib/umbraco/Extensions.js',
'lib/bootstrap-tabdrop/bootstrap-tabdrop.min.js',
'lib/umbraco/Extensions.js',
'lib/umbraco/NamespaceManager.js',
'lib/umbraco/LegacyUmbClientMgr.js',
'lib/umbraco/LegacySpeechBubble.js',
'js/umbraco.servervariables.js',
'js/app.dev.js',
@@ -18,7 +18,8 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
//map the user config
$scope.model.config = angular.extend(config, $scope.model.config);
$scope.datetimePickerValue = $scope.model.value;
$scope.hasDatetimePickerValue = $scope.model.value ? true : false;
$scope.datetimePickerValue = null;
//hide picker if clicking on the document
$scope.hidePicker = function () {
@@ -35,14 +36,20 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
function applyDate(e) {
angularHelper.safeApply($scope, function() {
// when a date is changed, update the model
if (e.date) {
if ($scope.model.config.pickTime) {
$scope.model.value = e.date.format("YYYY-MM-DD HH:mm:ss");
if (e.date && e.date.isValid()) {
$scope.datePickerForm.datepicker.$setValidity("pickerError", true);
$scope.hasDatetimePickerValue = true;
if (!$scope.model.config.format) {
$scope.datetimePickerValue = e.date;
}
else {
$scope.model.value = e.date.format("YYYY-MM-DD");
$scope.datetimePickerValue = e.date.format($scope.model.config.format);
}
}
else {
$scope.hasDatetimePickerValue = false;
$scope.datetimePickerValue = null;
}
if (!$scope.model.config.pickTime) {
$element.find("div:first").datetimepicker("hide", 0);
@@ -50,6 +57,15 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
});
}
var picker = null;
$scope.clearDate = function() {
$scope.hasDatetimePickerValue = false;
$scope.datetimePickerValue = null;
$scope.model.value = null;
$scope.datePickerForm.datepicker.$setValidity("pickerError", true);
}
//get the current user to see if we can localize this picker
userService.getCurrentUser().then(function (user) {
@@ -69,25 +85,39 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
// Get the id of the datepicker button that was clicked
var pickerId = $scope.model.alias;
// Open the datepicker and add a changeDate eventlistener
$element.find("div:first")
.datetimepicker($scope.model.config)
.on("dp.change", applyDate);
var element = $element.find("div:first");
//manually assign the date to the plugin
if (!$scope.model.config.format) {
$element.find("div:first").datetimepicker("setValue", $scope.model.value ? $scope.model.value : null);
}
else {
$element.find("div:first").datetimepicker("setValue", $scope.model.value ? new Date($scope.model.value) : null);
if ($scope.model.value && $scope.model.config.format) {
$scope.datetimePickerValue = moment($scope.model.value).format($scope.model.config.format);
}
}
// Open the datepicker and add a changeDate eventlistener
element
.datetimepicker(angular.extend({ useCurrent: true }, $scope.model.config))
.on("dp.change", applyDate)
.on("dp.error", function(a, b, c) {
$scope.hasDatetimePickerValue = false;
$scope.datePickerForm.datepicker.$setValidity("pickerError", false);
});
if ($scope.hasDatetimePickerValue) {
//assign value to plugin/picker
element.datetimepicker("setValue", $scope.model.value ? new Date($scope.model.value) : moment());
if (!$scope.model.config.format) {
$scope.datetimePickerValue = moment($scope.model.value);
}
else {
$scope.datetimePickerValue = moment($scope.model.value).format($scope.model.config.format);
}
}
element.find("input").bind("blur", function() {
//we need to force an apply here
$scope.$apply();
});
//Ensure to remove the event handler when this instance is destroyted
$scope.$on('$destroy', function () {
$element.find("div:first").datetimepicker("destroy");
$scope.$on('$destroy', function () {
element.find("input").unbind("blur");
element.datetimepicker("destroy");
});
});
});
@@ -95,9 +125,24 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
});
var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
if ($scope.hasDatetimePickerValue) {
if ($scope.model.config.pickTime) {
$scope.model.value = $element.find("div:first").data().DateTimePicker.getDate().format("YYYY-MM-DD HH:mm:ss");
}
else {
$scope.model.value = $element.find("div:first").data().DateTimePicker.getDate().format("YYYY-MM-DD");
}
}
else {
$scope.model.value = null;
}
});
//unbind doc click event!
$scope.$on('$destroy', function () {
$(document).unbind("click", $scope.hidePicker);
unsubscribe();
});
}
@@ -1,14 +1,26 @@
<div class="umb-editor umb-datepicker" ng-controller="Umbraco.PropertyEditors.DatepickerController">
<div class="input-append date datepicker" style="position: relative;" id="datepicker{{model.alias}}">
<input name="datepicker" data-format="{{model.config.format}}" type="text"
ng-model="datetimePickerValue"
ng-required="model.validation.mandatory"
val-server="value" />
<span class="add-on">
<i class="icon-calendar"></i>
</span>
</div>
<ng-form name="datePickerForm">
<div class="input-append date datepicker" style="position: relative;" id="datepicker{{model.alias}}">
<input name="datepicker" data-format="{{model.config.format}}" type="text"
ng-model="datetimePickerValue"
ng-required="model.validation.mandatory"
val-server="value"
class="datepickerinput" />
<span class="help-inline" val-msg-for="datepicker" val-toggle-msg="required">Required</span>
<span class="help-inline" val-msg-for="datepicker" val-toggle-msg="valServer">{{propertyForm.datepicker.errorMsg}}</span>
<span class="add-on">
<i class="icon-calendar"></i>
</span>
</div>
<span class="help-inline" val-msg-for="datepicker" val-toggle-msg="required">Required</span>
<span class="help-inline" val-msg-for="datepicker" val-toggle-msg="valServer">{{datePickerForm.datepicker.errorMsg}}</span>
<span class="help-inline" val-msg-for="datepicker" val-toggle-msg="pickerError">Invalid date</span>
<p ng-show="hasDatetimePickerValue === true || datePickerForm.datepicker.$error.pickerError === true">
<a href ng-click="clearDate()"><i class="icon-delete"></i><small><localize key="content_removeDate">Clear date</localize></small></a>
</p>
</ng-form>
</div>
+9 -8
View File
@@ -47,6 +47,7 @@
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<MvcProjectUpgradeChecked>true</MvcProjectUpgradeChecked>
<UseGlobalApplicationHostFile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\</OutputPath>
@@ -114,9 +115,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
</Reference>
<Reference Include="ClientDependency.Core, Version=1.8.3.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency.1.8.3.1\lib\net45\ClientDependency.Core.dll</HintPath>
<Reference Include="ClientDependency.Core, Version=1.8.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ClientDependency.1.8.4\lib\net45\ClientDependency.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ClientDependency.Core.Mvc, Version=1.8.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -126,9 +127,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\dotless.1.4.1.0\lib\dotless.Core.dll</HintPath>
</Reference>
<Reference Include="Examine">
<HintPath>..\packages\Examine.0.1.63.0\lib\Examine.dll</HintPath>
<Private>True</Private>
<Reference Include="Examine, Version=0.1.65.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.65.0\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -2540,9 +2541,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7260</DevelopmentServerPort>
<DevelopmentServerPort>7270</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7260</IISUrl>
<IISUrl>http://localhost:7270</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -10,7 +10,7 @@ NOTES:
* Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config
* A new version will invalidate both client and server cache and create new persisted files
-->
<clientDependency version="908669955" fileDependencyExtensions=".js,.css" loggerType="Umbraco.Web.UI.CdfLogger, umbraco">
<clientDependency version="2118623877" fileDependencyExtensions=".js,.css" loggerType="Umbraco.Web.UI.CdfLogger, umbraco">
<!--
This section is used for Web Forms only, the enableCompositeFiles="true" is optional and by default is set to true.
@@ -147,10 +147,15 @@
By default you can call any content Id in the url and show the content with that id, for example:
http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting
this setting to true stops that behavior
@umbracoApplicationUrl
The url of the Umbraco application. By default, Umbraco will figure it out from the first request.
Configure it here if you need anything specific. Needs to be a complete url with scheme and umbraco
path, eg http://mysite.com/umbraco. NOT just "mysite.com" or "mysite.com/umbraco" or "http://mysite.com".
-->
<web.routing
trySkipIisCustomErrors="false"
internalRedirectPreservesTemplate="false" disableAlternativeTemplates="false" disableFindContentByIdPath="false">
internalRedirectPreservesTemplate="false" disableAlternativeTemplates="false" disableFindContentByIdPath="false"
umbracoApplicationUrl="">
</web.routing>
</settings>
@@ -276,10 +276,15 @@
By default you can call any content Id in the url and show the content with that id, for example:
http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting
this setting to true stops that behavior
@umbracoApplicationUrl
The url of the Umbraco application. By default, Umbraco will figure it out from the first request.
Configure it here if you need anything specific. Needs to be a complete url with scheme and umbraco
path, eg http://mysite.com/umbraco. NOT just "mysite.com" or "mysite.com/umbraco" or "http://mysite.com".
-->
<web.routing
trySkipIisCustomErrors="false"
internalRedirectPreservesTemplate="false" disableAlternativeTemplates="false" disableFindContentByIdPath="false">
<web.routing
trySkipIisCustomErrors="false"
internalRedirectPreservesTemplate="false" disableAlternativeTemplates="false" disableFindContentByIdPath="false"
umbracoApplicationUrl="">
</web.routing>
</settings>
+25 -25
View File
@@ -1,28 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoMapper" version="3.0.0" targetFramework="net45" userInstalled="true" />
<package id="ClientDependency" version="1.8.3.1" targetFramework="net45" userInstalled="true" />
<package id="ClientDependency-Mvc" version="1.8.0.0" targetFramework="net45" userInstalled="true" />
<package id="dotless" version="1.4.1.0" targetFramework="net45" userInstalled="true" />
<package id="Examine" version="0.1.63.0" targetFramework="net45" />
<package id="ImageProcessor" version="1.9.5.0" targetFramework="net45" userInstalled="true" />
<package id="ImageProcessor.Web" version="3.3.1.0" targetFramework="net45" userInstalled="true" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net4" userInstalled="true" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net4" userInstalled="true" />
<package id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" targetFramework="net4" userInstalled="true" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net4" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi" version="4.0.30506.0" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi.Client" version="4.0.30506.0" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi.Core" version="4.0.30506.0" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.30506.0" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net4" userInstalled="true" />
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" userInstalled="true" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" userInstalled="true" />
<package id="MiniProfiler" version="2.1.0" targetFramework="net45" userInstalled="true" />
<package id="MySql.Data" version="6.9.6" targetFramework="net45" userInstalled="true" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" userInstalled="true" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" userInstalled="true" />
<package id="SqlServerCE" version="4.0.0.0" targetFramework="net4" userInstalled="true" />
<package id="UrlRewritingNet.UrlRewriter" version="2.0.60829.1" targetFramework="net4" userInstalled="true" />
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
<package id="ClientDependency" version="1.8.4" targetFramework="net45" />
<package id="ClientDependency-Mvc" version="1.8.0.0" targetFramework="net45" />
<package id="dotless" version="1.4.1.0" targetFramework="net45" />
<package id="Examine" version="0.1.65.0" targetFramework="net45" />
<package id="ImageProcessor" version="1.9.5.0" targetFramework="net45" />
<package id="ImageProcessor.Web" version="3.3.1.0" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net4" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net4" />
<package id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" targetFramework="net4" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net4" />
<package id="Microsoft.AspNet.WebApi" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net4" />
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="MiniProfiler" version="2.1.0" targetFramework="net45" />
<package id="MySql.Data" version="6.9.6" targetFramework="net45" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
<package id="SqlServerCE" version="4.0.0.0" targetFramework="net4" />
<package id="UrlRewritingNet.UrlRewriter" version="2.0.60829.1" targetFramework="net4" />
</packages>
@@ -14,6 +14,7 @@ using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.member;
using System.Linq;
using umbraco.cms.businesslogic.web;
using Umbraco.Core.Logging;
using Umbraco.Core.Publishing;
using Content = Umbraco.Core.Models.Content;
using ApplicationTree = Umbraco.Core.Models.ApplicationTree;
@@ -30,7 +31,9 @@ namespace Umbraco.Web.Cache
public class CacheRefresherEventHandler : ApplicationEventHandler
{
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
{
LogHelper.Info<CacheRefresherEventHandler>("Initializing Umbraco internal event handlers for cache refreshing");
//bind to application tree events
ApplicationTreeService.Deleted += ApplicationTreeDeleted;
ApplicationTreeService.Updated += ApplicationTreeUpdated;
@@ -98,7 +98,7 @@ namespace Umbraco.Web.Install
// that and we might get lock issues.
try
{
var xmlFile = content.Instance.UmbracoXmlDiskCacheFileName + ".tmp";
var xmlFile = content.GetUmbracoXmlDiskFileName() + ".tmp";
SaveAndDeleteFile(xmlFile);
return true;
}
@@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
@@ -36,22 +37,33 @@ namespace Umbraco.Web.Mvc
return _applicationContext ?? ApplicationContext.Current;
}
public const string AuthorizationType = "AToken";
/// <summary>
/// Used to return the value that needs to go in the Authorization header
/// Used to return the full value that needs to go in the Authorization header
/// </summary>
/// <param name="appContext"></param>
/// <returns></returns>
public static string GetAuthHeaderTokenVal(ApplicationContext appContext)
{
var admin = appContext.Services.UserService.GetUserById(0);
return string.Format("{0} {1}", AuthorizationType, GetAuthHeaderVal(appContext));
}
public static AuthenticationHeaderValue GetAuthenticationHeaderValue(ApplicationContext appContext)
{
return new AuthenticationHeaderValue(AuthorizationType, GetAuthHeaderVal(appContext));
}
private static string GetAuthHeaderVal(ApplicationContext appContext)
{
var admin = appContext.Services.UserService.GetUserById(0);
var token = string.Format("{0}u____u{1}u____u{2}", admin.Email, admin.Username, admin.RawPasswordValue);
var encrypted = token.EncryptWithMachineKey();
var bytes = Encoding.UTF8.GetBytes(encrypted);
var base64 = Convert.ToBase64String(bytes);
return "AToken val=\"" + base64 + "\"";
return string.Format("val=\"{0}\"", base64);
}
/// <summary>
@@ -17,11 +17,10 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
/// if multiple threads are performing publishing tasks that the file will be persisted in accordance with the final resulting
/// xml structure since the file writes are queued.
/// </remarks>
internal class XmlCacheFilePersister : ILatchedBackgroundTask
internal class XmlCacheFilePersister : LatchedBackgroundTaskBase
{
private readonly IBackgroundTaskRunner<XmlCacheFilePersister> _runner;
private readonly content _content;
private readonly ManualResetEventSlim _latch = new ManualResetEventSlim(false);
private readonly object _locko = new object();
private bool _released;
private Timer _timer;
@@ -38,7 +37,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
private const int MaxWaitMilliseconds = 30000; // save the cache after some time (ie no more than 30s of changes)
// save the cache when the app goes down
public bool RunsOnShutdown { get { return true; } }
public override bool RunsOnShutdown { get { return _timer != null; } }
// initialize the first instance, which is inactive (not touched yet)
public XmlCacheFilePersister(IBackgroundTaskRunner<XmlCacheFilePersister> runner, content content)
@@ -141,28 +140,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
lock (_locko)
{
LogHelper.Debug<XmlCacheFilePersister>("Timer: release.");
if (_timer != null)
_timer.Dispose();
_timer = null;
_released = true;
// if running (because of shutdown) this will have no effect
// else it tells the runner it is time to run the task
_latch.Set();
Release();
}
}
public WaitHandle Latch
{
get { return _latch.WaitHandle; }
}
public bool IsLatched
{
get { return true; }
}
public async Task RunAsync(CancellationToken token)
public override async Task RunAsync(CancellationToken token)
{
lock (_locko)
{
@@ -181,15 +165,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
}
public bool IsAsync
public override bool IsAsync
{
get { return true; }
}
public void Dispose()
{ }
public void Run()
public override void Run()
{
lock (_locko)
{
@@ -203,5 +184,15 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
_content.SaveXmlToFile();
}
}
protected override void DisposeResources()
{
base.DisposeResources();
// stop the timer
if (_timer == null) return;
_timer.Change(Timeout.Infinite, Timeout.Infinite);
_timer.Dispose();
}
}
}
@@ -90,7 +90,8 @@ namespace Umbraco.Web.Scheduling
_options = options;
_logPrefix = "[" + name + "] ";
HostingEnvironment.RegisterObject(this);
if (options.Hosted)
HostingEnvironment.RegisterObject(this);
if (options.AutoStart)
StartUp();
@@ -385,6 +386,7 @@ namespace Umbraco.Web.Scheduling
// still latched & not running on shutdown = stop here
if (dbgTask.IsLatched && dbgTask.RunsOnShutdown == false)
{
dbgTask.Dispose(); // will not run
TaskSourceCompleted(taskSource, token);
return;
}
@@ -442,7 +444,7 @@ namespace Umbraco.Web.Scheduling
try
{
using (bgTask) // ensure it's disposed
try
{
if (bgTask.IsAsync)
//configure await = false since we don't care about the context, we're on a background thread.
@@ -450,6 +452,12 @@ namespace Umbraco.Web.Scheduling
else
bgTask.Run();
}
finally // ensure we disposed - unless latched (again)
{
var lbgTask = bgTask as ILatchedBackgroundTask;
if (lbgTask == null || lbgTask.IsLatched == false)
bgTask.Dispose();
}
}
catch (Exception e)
{
@@ -15,6 +15,8 @@ namespace Umbraco.Web.Scheduling
LongRunning = false;
KeepAlive = false;
AutoStart = false;
PreserveRunningTask = false;
Hosted = true;
}
/// <summary>
@@ -36,9 +38,16 @@ namespace Umbraco.Web.Scheduling
public bool AutoStart { get; set; }
/// <summary>
/// Gets or setes a value indicating whether the running task should be preserved
/// Gets or sets a value indicating whether the running task should be preserved
/// once completed, or reset to null. For unit tests.
/// </summary>
public bool PreserveRunningTask { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the runner should register with (and be
/// stopped by) the hosting. Otherwise, something else should take care of stopping
/// the runner. True by default.
/// </summary>
public bool Hosted { get; set; }
}
}
@@ -1,74 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// Provides a base class for recurring background tasks.
/// </summary>
/// <typeparam name="T">The type of the managed tasks.</typeparam>
internal abstract class DelayedRecurringTaskBase<T> : RecurringTaskBase<T>, ILatchedBackgroundTask
where T : class, IBackgroundTask
{
private readonly ManualResetEventSlim _latch;
private Timer _timer;
protected DelayedRecurringTaskBase(IBackgroundTaskRunner<T> runner, int delayMilliseconds, int periodMilliseconds)
: base(runner, periodMilliseconds)
{
if (delayMilliseconds > 0)
{
_latch = new ManualResetEventSlim(false);
_timer = new Timer(_ =>
{
_timer.Dispose();
_timer = null;
_latch.Set();
});
_timer.Change(delayMilliseconds, 0);
}
}
protected DelayedRecurringTaskBase(DelayedRecurringTaskBase<T> source)
: base(source)
{
// no latch on recurring instances
_latch = null;
}
public override void Run()
{
if (_latch != null)
_latch.Dispose();
base.Run();
}
public override async Task RunAsync(CancellationToken token)
{
if (_latch != null)
_latch.Dispose();
await base.RunAsync(token);
}
public WaitHandle Latch
{
get
{
if (_latch == null)
throw new InvalidOperationException("The task is not latched.");
return _latch.WaitHandle;
}
}
public bool IsLatched
{
get { return _latch != null && _latch.IsSet == false; }
}
public virtual bool RunsOnShutdown
{
get { return true; }
}
}
}
+62 -26
View File
@@ -1,47 +1,83 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Sync;
namespace Umbraco.Web.Scheduling
{
internal class KeepAlive
internal class KeepAlive : RecurringTaskBase
{
public static void Start(ApplicationContext appContext, IUmbracoSettingsSection settings)
private readonly ApplicationContext _appContext;
public KeepAlive(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds,
ApplicationContext appContext)
: base(runner, delayMilliseconds, periodMilliseconds)
{
_appContext = appContext;
}
public override bool PerformRun()
{
throw new NotImplementedException();
}
public override async Task<bool> PerformRunAsync(CancellationToken token)
{
if (_appContext == null) return true; // repeat...
// ensure we do not run if not main domain, but do NOT lock it
if (_appContext.MainDom.IsMainDom == false)
{
LogHelper.Debug<ScheduledPublishing>("Does not run if not MainDom.");
return false; // do NOT repeat, going down
}
using (DisposableTimer.DebugDuration<KeepAlive>(() => "Keep alive executing", () => "Keep alive complete"))
{
var umbracoBaseUrl = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl(
appContext,
settings);
{
string umbracoAppUrl = null;
if (string.IsNullOrWhiteSpace(umbracoBaseUrl))
try
{
LogHelper.Warn<KeepAlive>("No url for service (yet), skip.");
}
else
{
var url = string.Format("{0}ping.aspx", umbracoBaseUrl.EnsureEndsWith('/'));
try
umbracoAppUrl = _appContext.UmbracoApplicationUrl;
if (umbracoAppUrl.IsNullOrWhiteSpace())
{
using (var wc = new WebClient())
LogHelper.Warn<KeepAlive>("No url for service (yet), skip.");
return true; // repeat
}
var url = umbracoAppUrl + "/ping.aspx";
using (var wc = new HttpClient())
{
var request = new HttpRequestMessage()
{
wc.DownloadString(url);
}
}
catch (Exception ee)
{
LogHelper.Error<KeepAlive>(
string.Format("Error in ping. The base url used in the request was: {0}, see http://our.umbraco.org/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks documentation for details on setting a baseUrl if this is in error", umbracoBaseUrl)
, ee);
RequestUri = new Uri(url),
Method = HttpMethod.Get
};
var result = await wc.SendAsync(request, token);
}
}
catch (Exception e)
{
LogHelper.Error<KeepAlive>(string.Format("Failed (at \"{0}\").", umbracoAppUrl), e);
}
}
return true; // repeat
}
public override bool IsAsync
{
get { return true; }
}
public override bool RunsOnShutdown
{
get { return false; }
}
}
}
@@ -0,0 +1,63 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Umbraco.Core;
namespace Umbraco.Web.Scheduling
{
internal abstract class LatchedBackgroundTaskBase : DisposableObject, ILatchedBackgroundTask
{
private readonly ManualResetEventSlim _latch;
protected LatchedBackgroundTaskBase()
{
_latch = new ManualResetEventSlim(false);
}
/// <summary>
/// Implements IBackgroundTask.Run().
/// </summary>
public abstract void Run();
/// <summary>
/// Implements IBackgroundTask.RunAsync().
/// </summary>
public abstract Task RunAsync(CancellationToken token);
/// <summary>
/// Indicates whether the background task can run asynchronously.
/// </summary>
public abstract bool IsAsync { get; }
public WaitHandle Latch
{
get { return _latch.WaitHandle; }
}
public bool IsLatched
{
get { return _latch.IsSet == false; }
}
protected void Release()
{
_latch.Set();
}
protected void Reset()
{
_latch.Reset();
}
public abstract bool RunsOnShutdown { get; }
// the task is going to be disposed after execution,
// unless it is latched again, thus indicating it wants to
// remain active
protected override void DisposeResources()
{
_latch.Dispose();
}
}
}
+27 -20
View File
@@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Caching;
@@ -6,15 +7,16 @@ using umbraco.BusinessLogic;
using Umbraco.Core;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Sync;
namespace Umbraco.Web.Scheduling
{
internal class LogScrubber : DelayedRecurringTaskBase<LogScrubber>
internal class LogScrubber : RecurringTaskBase
{
private readonly ApplicationContext _appContext;
private readonly IUmbracoSettingsSection _settings;
public LogScrubber(IBackgroundTaskRunner<LogScrubber> runner, int delayMilliseconds, int periodMilliseconds,
public LogScrubber(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds,
ApplicationContext appContext, IUmbracoSettingsSection settings)
: base(runner, delayMilliseconds, periodMilliseconds)
{
@@ -22,19 +24,7 @@ namespace Umbraco.Web.Scheduling
_settings = settings;
}
public LogScrubber(LogScrubber source)
: base(source)
{
_appContext = source._appContext;
_settings = source._settings;
}
protected override LogScrubber GetRecurring()
{
return new LogScrubber(this);
}
private int GetLogScrubbingMaximumAge(IUmbracoSettingsSection settings)
private static int GetLogScrubbingMaximumAge(IUmbracoSettingsSection settings)
{
int maximumAge = 24 * 60 * 60;
try
@@ -44,7 +34,7 @@ namespace Umbraco.Web.Scheduling
}
catch (Exception e)
{
LogHelper.Error<Scheduler>("Unable to locate a log scrubbing maximum age. Defaulting to 24 horus", e);
LogHelper.Error<LogScrubber>("Unable to locate a log scrubbing maximum age. Defaulting to 24 hours.", e);
}
return maximumAge;
@@ -65,15 +55,32 @@ namespace Umbraco.Web.Scheduling
return interval;
}
public override void PerformRun()
public override bool PerformRun()
{
using (DisposableTimer.DebugDuration<LogScrubber>(() => "Log scrubbing executing", () => "Log scrubbing complete"))
if (_appContext == null) return true; // repeat...
if (ServerEnvironmentHelper.GetStatus(_settings) == CurrentServerEnvironmentStatus.Slave)
{
LogHelper.Debug<LogScrubber>("Does not run on slave servers.");
return false; // do NOT repeat, server status comes from config and will NOT change
}
// ensure we do not run if not main domain, but do NOT lock it
if (_appContext.MainDom.IsMainDom == false)
{
LogHelper.Debug<LogScrubber>("Does not run if not MainDom.");
return false; // do NOT repeat, going down
}
using (DisposableTimer.DebugDuration<LogScrubber>("Log scrubbing executing", "Log scrubbing complete"))
{
Log.CleanLogs(GetLogScrubbingMaximumAge(_settings));
}
}
return true; // repeat
}
public override Task PerformRunAsync()
public override Task<bool> PerformRunAsync(CancellationToken token)
{
throw new NotImplementedException();
}
+40 -59
View File
@@ -6,57 +6,51 @@ namespace Umbraco.Web.Scheduling
/// <summary>
/// Provides a base class for recurring background tasks.
/// </summary>
/// <typeparam name="T">The type of the managed tasks.</typeparam>
internal abstract class RecurringTaskBase<T> : IBackgroundTask
where T : class, IBackgroundTask
internal abstract class RecurringTaskBase : LatchedBackgroundTaskBase
{
private readonly IBackgroundTaskRunner<T> _runner;
private readonly IBackgroundTaskRunner<RecurringTaskBase> _runner;
private readonly int _periodMilliseconds;
private Timer _timer;
private T _recurrent;
private readonly Timer _timer;
private bool _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="RecurringTaskBase{T}"/> class with a tasks runner and a period.
/// Initializes a new instance of the <see cref="RecurringTaskBase"/> class.
/// </summary>
/// <param name="runner">The task runner.</param>
/// <param name="delayMilliseconds">The delay.</param>
/// <param name="periodMilliseconds">The period.</param>
/// <remarks>The task will repeat itself periodically. Use this constructor to create a new task.</remarks>
protected RecurringTaskBase(IBackgroundTaskRunner<T> runner, int periodMilliseconds)
protected RecurringTaskBase(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds)
{
_runner = runner;
_periodMilliseconds = periodMilliseconds;
}
/// <summary>
/// Initializes a new instance of the <see cref="RecurringTaskBase{T}"/> class with a source task.
/// </summary>
/// <param name="source">The source task.</param>
/// <remarks>Use this constructor to create a new task from a source task in <c>GetRecurring</c>.</remarks>
protected RecurringTaskBase(RecurringTaskBase<T> source)
{
_runner = source._runner;
_timer = source._timer;
_periodMilliseconds = source._periodMilliseconds;
// note
// must use the single-parameter constructor on Timer to avoid it from being GC'd
// read http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer
_timer = new Timer(_ => Release());
_timer.Change(delayMilliseconds, 0);
}
/// <summary>
/// Implements IBackgroundTask.Run().
/// </summary>
/// <remarks>Classes inheriting from <c>RecurringTaskBase</c> must implement <c>PerformRun</c>.</remarks>
public virtual void Run()
public override void Run()
{
PerformRun();
Repeat();
var shouldRepeat = PerformRun();
if (shouldRepeat) Repeat();
}
/// <summary>
/// Implements IBackgroundTask.RunAsync().
/// </summary>
/// <remarks>Classes inheriting from <c>RecurringTaskBase</c> must implement <c>PerformRun</c>.</remarks>
public virtual async Task RunAsync(CancellationToken token)
public override async Task RunAsync(CancellationToken token)
{
await PerformRunAsync();
Repeat();
var shouldRepeat = await PerformRunAsync(token);
if (shouldRepeat) Repeat();
}
private void Repeat()
@@ -64,52 +58,39 @@ namespace Umbraco.Web.Scheduling
// again?
if (_runner.IsCompleted) return; // fail fast
if (_periodMilliseconds == 0) return;
if (_periodMilliseconds == 0) return; // safe
_recurrent = GetRecurring();
if (_recurrent == null)
{
_timer.Dispose();
_timer = null;
return; // done
}
Reset(); // re-latch
// note
// must use the single-parameter constructor on Timer to avoid it from being GC'd
// read http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer
_timer = _timer ?? new Timer(_ => _runner.TryAdd(_recurrent));
_timer.Change(_periodMilliseconds, 0);
// try to add again (may fail if runner has completed)
// if added, re-start the timer, else kill it
if (_runner.TryAdd(this))
_timer.Change(_periodMilliseconds, 0);
else
Dispose(true);
}
/// <summary>
/// Indicates whether the background task can run asynchronously.
/// </summary>
public abstract bool IsAsync { get; }
/// <summary>
/// Runs the background task.
/// </summary>
public abstract void PerformRun();
/// <returns>A value indicating whether to repeat the task.</returns>
public abstract bool PerformRun();
/// <summary>
/// Runs the task asynchronously.
/// </summary>
/// <returns>A <see cref="Task"/> instance representing the execution of the background task.</returns>
public abstract Task PerformRunAsync();
/// <param name="token">A cancellation token.</param>
/// <returns>A <see cref="Task{T}"/> instance representing the execution of the background task,
/// and returning a value indicating whether to repeat the task.</returns>
public abstract Task<bool> PerformRunAsync(CancellationToken token);
/// <summary>
/// Gets a new occurence of the recurring task.
/// </summary>
/// <returns>A new task instance to be queued, or <c>null</c> to terminate the recurring task.</returns>
/// <remarks>The new task instance must be created via the <c>RecurringTaskBase(RecurringTaskBase{T} source)</c> constructor,
/// where <c>source</c> is the current task, eg: <c>return new MyTask(this);</c></remarks>
protected abstract T GetRecurring();
protected override void DisposeResources()
{
base.DisposeResources();
/// <summary>
/// Dispose the task.
/// </summary>
public virtual void Dispose()
{ }
// stop the timer
_timer.Change(Timeout.Infinite, Timeout.Infinite);
_timer.Dispose();
}
}
}
@@ -1,25 +1,21 @@
using System;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Publishing;
using Umbraco.Core.Sync;
using Umbraco.Web.Mvc;
namespace Umbraco.Web.Scheduling
{
internal class ScheduledPublishing : DelayedRecurringTaskBase<ScheduledPublishing>
internal class ScheduledPublishing : RecurringTaskBase
{
private readonly ApplicationContext _appContext;
private readonly IUmbracoSettingsSection _settings;
private static bool _isPublishingRunning;
public ScheduledPublishing(IBackgroundTaskRunner<ScheduledPublishing> runner, int delayMilliseconds, int periodMilliseconds,
public ScheduledPublishing(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds,
ApplicationContext appContext, IUmbracoSettingsSection settings)
: base(runner, delayMilliseconds, periodMilliseconds)
{
@@ -27,75 +23,68 @@ namespace Umbraco.Web.Scheduling
_settings = settings;
}
private ScheduledPublishing(ScheduledPublishing source)
: base(source)
{
_appContext = source._appContext;
_settings = source._settings;
}
protected override ScheduledPublishing GetRecurring()
{
return new ScheduledPublishing(this);
}
public override void PerformRun()
{
if (_appContext == null) return;
if (ServerEnvironmentHelper.GetStatus(_settings) == CurrentServerEnvironmentStatus.Slave)
{
LogHelper.Debug<ScheduledPublishing>("Does not run on slave servers.");
return;
}
using (DisposableTimer.DebugDuration<ScheduledPublishing>(() => "Scheduled publishing executing", () => "Scheduled publishing complete"))
{
if (_isPublishingRunning) return;
_isPublishingRunning = true;
var umbracoBaseUrl = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl(_appContext, _settings);
try
{
if (string.IsNullOrWhiteSpace(umbracoBaseUrl))
{
LogHelper.Warn<ScheduledPublishing>("No url for service (yet), skip.");
}
else
{
var url = string.Format("{0}RestServices/ScheduledPublish/Index", umbracoBaseUrl.EnsureEndsWith('/'));
using (var wc = new WebClient())
{
//pass custom the authorization header
wc.Headers.Set("Authorization", AdminTokenAuthorizeAttribute.GetAuthHeaderTokenVal(_appContext));
var result = wc.UploadString(url, "");
}
}
}
catch (Exception ee)
{
LogHelper.Error<ScheduledPublishing>(
string.Format("An error occurred with the scheduled publishing. The base url used in the request was: {0}, see http://our.umbraco.org/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks documentation for details on setting a baseUrl if this is in error", umbracoBaseUrl)
, ee);
}
finally
{
_isPublishingRunning = false;
}
}
}
public override Task PerformRunAsync()
public override bool PerformRun()
{
throw new NotImplementedException();
}
public override async Task<bool> PerformRunAsync(CancellationToken token)
{
if (_appContext == null) return true; // repeat...
if (ServerEnvironmentHelper.GetStatus(_settings) == CurrentServerEnvironmentStatus.Slave)
{
LogHelper.Debug<ScheduledPublishing>("Does not run on slave servers.");
return false; // do NOT repeat, server status comes from config and will NOT change
}
// ensure we do not run if not main domain, but do NOT lock it
if (_appContext.MainDom.IsMainDom == false)
{
LogHelper.Debug<ScheduledPublishing>("Does not run if not MainDom.");
return false; // do NOT repeat, going down
}
using (DisposableTimer.DebugDuration<ScheduledPublishing>(() => "Scheduled publishing executing", () => "Scheduled publishing complete"))
{
string umbracoAppUrl = null;
try
{
umbracoAppUrl = _appContext.UmbracoApplicationUrl;
if (umbracoAppUrl.IsNullOrWhiteSpace())
{
LogHelper.Warn<ScheduledPublishing>("No url for service (yet), skip.");
return true; // repeat
}
var url = umbracoAppUrl + "/RestServices/ScheduledPublish/Index";
using (var wc = new HttpClient())
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri(url),
Method = HttpMethod.Post,
Content = new StringContent(string.Empty)
};
//pass custom the authorization header
request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext);
var result = await wc.SendAsync(request, token);
}
}
catch (Exception e)
{
LogHelper.Error<ScheduledPublishing>(string.Format("Failed (at \"{0}\").", umbracoAppUrl), e);
}
}
return true; // repeat
}
public override bool IsAsync
{
get { return false; }
get { return true; }
}
public override bool RunsOnShutdown
+50 -53
View File
@@ -1,15 +1,13 @@
using System;
using System.Collections;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Umbraco.Core.Configuration;
using Umbraco.Core;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Publishing;
using Umbraco.Core.Sync;
using Umbraco.Core;
namespace Umbraco.Web.Scheduling
{
@@ -17,14 +15,13 @@ namespace Umbraco.Web.Scheduling
// would need to be a publicly available task (URL) which isn't really very good :(
// We should really be using the AdminTokenAuthorizeAttribute for this stuff
internal class ScheduledTasks : DelayedRecurringTaskBase<ScheduledTasks>
internal class ScheduledTasks : RecurringTaskBase
{
private readonly ApplicationContext _appContext;
private readonly IUmbracoSettingsSection _settings;
private static readonly Hashtable ScheduledTaskTimes = new Hashtable();
private static bool _isPublishingRunning = false;
public ScheduledTasks(IBackgroundTaskRunner<ScheduledTasks> runner, int delayMilliseconds, int periodMilliseconds,
public ScheduledTasks(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds, int periodMilliseconds,
ApplicationContext appContext, IUmbracoSettingsSection settings)
: base(runner, delayMilliseconds, periodMilliseconds)
{
@@ -32,30 +29,19 @@ namespace Umbraco.Web.Scheduling
_settings = settings;
}
public ScheduledTasks(ScheduledTasks source)
: base(source)
{
_appContext = source._appContext;
_settings = source._settings;
}
protected override ScheduledTasks GetRecurring()
{
return new ScheduledTasks(this);
}
private void ProcessTasks()
private async Task ProcessTasksAsync(CancellationToken token)
{
var scheduledTasks = _settings.ScheduledTasks.Tasks;
foreach (var t in scheduledTasks)
{
var runTask = false;
if (!ScheduledTaskTimes.ContainsKey(t.Alias))
if (ScheduledTaskTimes.ContainsKey(t.Alias) == false)
{
runTask = true;
ScheduledTaskTimes.Add(t.Alias, DateTime.Now);
}
/// Add 1 second to timespan to compensate for differencies in timer
// Add 1 second to timespan to compensate for differencies in timer
else if (
new TimeSpan(
DateTime.Now.Ticks - ((DateTime)ScheduledTaskTimes[t.Alias]).Ticks).TotalSeconds + 1 >= t.Interval)
@@ -66,69 +52,80 @@ namespace Umbraco.Web.Scheduling
if (runTask)
{
bool taskResult = GetTaskByHttp(t.Url);
var taskResult = await GetTaskByHttpAync(t.Url, token);
if (t.Log)
LogHelper.Info<ScheduledTasks>(string.Format("{0} has been called with response: {1}", t.Alias, taskResult));
}
}
}
private bool GetTaskByHttp(string url)
private async Task<bool> GetTaskByHttpAync(string url, CancellationToken token)
{
var myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
try
using (var wc = new HttpClient())
{
using (var response = (HttpWebResponse)myHttpWebRequest.GetResponse())
var request = new HttpRequestMessage()
{
return response.StatusCode == HttpStatusCode.OK;
}
}
catch (Exception ex)
{
LogHelper.Error<ScheduledTasks>("An error occurred calling web task for url: " + url, ex);
}
RequestUri = new Uri(url),
Method = HttpMethod.Get,
Content = new StringContent(string.Empty)
};
return false;
//TODO: pass custom the authorization header, currently these aren't really secured!
//request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext);
try
{
var result = await wc.SendAsync(request, token);
return result.StatusCode == HttpStatusCode.OK;
}
catch (Exception ex)
{
LogHelper.Error<ScheduledTasks>("An error occurred calling web task for url: " + url, ex);
}
return false;
}
}
public override void PerformRun()
public override bool PerformRun()
{
throw new NotImplementedException();
}
public override async Task<bool> PerformRunAsync(CancellationToken token)
{
if (_appContext == null) return true; // repeat...
if (ServerEnvironmentHelper.GetStatus(_settings) == CurrentServerEnvironmentStatus.Slave)
{
LogHelper.Debug<ScheduledTasks>("Does not run on slave servers.");
return;
return false; // do NOT repeat, server status comes from config and will NOT change
}
// ensure we do not run if not main domain, but do NOT lock it
if (_appContext.MainDom.IsMainDom == false)
{
LogHelper.Debug<ScheduledTasks>("Does not run if not MainDom.");
return false; // do NOT repeat, going down
}
using (DisposableTimer.DebugDuration<ScheduledTasks>(() => "Scheduled tasks executing", () => "Scheduled tasks complete"))
{
if (_isPublishingRunning) return;
_isPublishingRunning = true;
try
{
ProcessTasks();
await ProcessTasksAsync(token);
}
catch (Exception ee)
{
LogHelper.Error<ScheduledTasks>("Error executing scheduled task", ee);
}
finally
{
_isPublishingRunning = false;
}
}
}
public override Task PerformRunAsync()
{
throw new NotImplementedException();
return true; // repeat
}
public override bool IsAsync
{
get { return false; }
get { return true; }
}
public override bool RunsOnShutdown
+7 -17
View File
@@ -1,11 +1,7 @@
using System;
using System.Threading;
using System.Web;
using System.Web;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Sync;
namespace Umbraco.Web.Scheduling
{
@@ -18,7 +14,7 @@ namespace Umbraco.Web.Scheduling
/// </remarks>
internal sealed class Scheduler : ApplicationEventHandler
{
private static Timer _pingTimer;
private static BackgroundTaskRunner<IBackgroundTask> _keepAliveRunner;
private static BackgroundTaskRunner<IBackgroundTask> _publishingRunner;
private static BackgroundTaskRunner<IBackgroundTask> _tasksRunner;
private static BackgroundTaskRunner<IBackgroundTask> _scrubberRunner;
@@ -48,30 +44,24 @@ namespace Umbraco.Web.Scheduling
LogHelper.Debug<Scheduler>(() => "Initializing the scheduler");
// backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly
_keepAliveRunner = new BackgroundTaskRunner<IBackgroundTask>("KeepAlive");
_publishingRunner = new BackgroundTaskRunner<IBackgroundTask>("ScheduledPublishing");
_tasksRunner = new BackgroundTaskRunner<IBackgroundTask>("ScheduledTasks");
_scrubberRunner = new BackgroundTaskRunner<IBackgroundTask>("LogScrubber");
var settings = UmbracoConfig.For.UmbracoSettings();
// note
// must use the single-parameter constructor on Timer to avoid it from being GC'd
// also make the timer static to ensure further GC safety
// read http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer
// ping/keepalive - no need for a background runner - does not need to be web aware, ok if the app domain dies
_pingTimer = new Timer(state => KeepAlive.Start(applicationContext, UmbracoConfig.For.UmbracoSettings()));
_pingTimer.Change(60000, 300000);
// ping/keepalive
// on all servers
_keepAliveRunner.Add(new KeepAlive(_keepAliveRunner, 60000, 300000, applicationContext));
// scheduled publishing/unpublishing
// install on all, will only run on non-slaves servers
// both are delayed recurring tasks
_publishingRunner.Add(new ScheduledPublishing(_publishingRunner, 60000, 60000, applicationContext, settings));
_tasksRunner.Add(new ScheduledTasks(_tasksRunner, 60000, 60000, applicationContext, settings));
// log scrubbing
// install & run on all servers
// LogScrubber is a delayed recurring task
// install on all, will only run on non-slaves servers
_scrubberRunner.Add(new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings), applicationContext, settings));
}
}
+1 -3
View File
@@ -105,9 +105,7 @@ namespace Umbraco.Web.Security
.Where(p => member.ContentType.PropertyTypeExists(p.Alias))
.Where(property => member.Properties.Contains(property.Alias))
//needs to be editable
.Where(p => member.ContentType.MemberCanEditProperty(p.Alias))
//needs to have a value
.Where(p => p.Value != null))
.Where(p => member.ContentType.MemberCanEditProperty(p.Alias)))
{
member.Properties[property.Alias].Value = property.Value;
}
+16 -10
View File
@@ -81,9 +81,9 @@ namespace Umbraco.Web.Templates
//set the doc that was found by id
contentRequest.PublishedContent = doc;
//set the template, either based on the AltTemplate found or the standard template of the doc
contentRequest.TemplateModel = UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates || AltTemplate.HasValue == false
? ApplicationContext.Current.Services.FileService.GetTemplate(doc.TemplateId)
: ApplicationContext.Current.Services.FileService.GetTemplate(AltTemplate.Value);
contentRequest.TemplateModel = UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates || AltTemplate.HasValue == false
? _umbracoContext.Application.Services.FileService.GetTemplate(doc.TemplateId)
: _umbracoContext.Application.Services.FileService.GetTemplate(AltTemplate.Value);
//if there is not template then exit
if (!contentRequest.HasTemplate)
@@ -103,14 +103,20 @@ namespace Umbraco.Web.Templates
//after this page has rendered.
SaveExistingItems();
//set the new items on context objects for this templates execution
SetNewItemsOnContextObjects(contentRequest);
try
{
//set the new items on context objects for this templates execution
SetNewItemsOnContextObjects(contentRequest);
//Render the template
ExecuteTemplateRendering(writer, contentRequest);
//restore items on context objects to continuing rendering the parent template
RestoreItems();
//Render the template
ExecuteTemplateRendering(writer, contentRequest);
}
finally
{
//restore items on context objects to continuing rendering the parent template
RestoreItems();
}
}
private void ExecuteTemplateRendering(TextWriter sw, PublishedContentRequest contentRequest)
@@ -20,6 +20,7 @@
'lib/jquery-file-upload/jquery.fileupload-angular.js',
'lib/bootstrap/js/bootstrap.2.3.2.min.js',
'lib/bootstrap-tabdrop/bootstrap-tabdrop.min.js',
'lib/umbraco/Extensions.js',
'lib/umbraco/NamespaceManager.js',
+6 -6
View File
@@ -104,9 +104,9 @@
<Reference Include="AutoMapper.Net4">
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
</Reference>
<Reference Include="ClientDependency.Core, Version=1.8.2.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency.1.8.3.1\lib\net45\ClientDependency.Core.dll</HintPath>
<Reference Include="ClientDependency.Core, Version=1.8.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ClientDependency.1.8.4\lib\net45\ClientDependency.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="CookComputing.XmlRpcV2, Version=2.5.0.0, Culture=neutral, PublicKeyToken=a7d6e17aa302004d, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -115,9 +115,9 @@
<Reference Include="dotless.Core">
<HintPath>..\packages\dotless.1.4.1.0\lib\dotless.Core.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.63.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine, Version=0.1.65.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.63.0\lib\Examine.dll</HintPath>
<HintPath>..\packages\Examine.0.1.65.0\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="HtmlAgilityPack, Version=1.4.6.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -268,6 +268,7 @@
</Compile>
<Compile Include="ApplicationContextExtensions.cs" />
<Compile Include="AreaRegistrationContextExtensions.cs" />
<Compile Include="Scheduling\LatchedBackgroundTaskBase.cs" />
<Compile Include="Models\ContentExtensions.cs" />
<Compile Include="Models\LegacyConvertedNode.cs" />
<Compile Include="Models\LegacyConvertedNodeProperty.cs" />
@@ -500,7 +501,6 @@
<Compile Include="Routing\CustomRouteUrlProvider.cs" />
<Compile Include="Routing\UrlProviderExtensions.cs" />
<Compile Include="Scheduling\BackgroundTaskRunnerOptions.cs" />
<Compile Include="Scheduling\DelayedRecurringTaskBase.cs" />
<Compile Include="Scheduling\IBackgroundTaskRunner.cs" />
<Compile Include="Scheduling\ILatchedBackgroundTask.cs" />
<Compile Include="Scheduling\RecurringTaskBase.cs" />
+33 -24
View File
@@ -36,37 +36,46 @@ namespace Umbraco.Web
{
#region HttpModule event handlers
private static void EnsureApplicationUrl(HttpRequestBase request)
{
var appctx = ApplicationContext.Current;
// already initialized = ok
// note that getting ApplicationUrl will ALSO try the various settings
if (appctx.UmbracoApplicationUrl.IsNullOrWhiteSpace() == false) return;
// so if we reach that point, nothing was configured
// use the current request as application url
// if (HTTP and SSL not required) or (HTTPS and SSL required),
// use ports from request
// otherwise,
// if non-standard ports used,
// user may need to set umbracoApplicationUrl manually per
// http://our.umbraco.org/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks
var port = (request.IsSecureConnection == false && GlobalSettings.UseSSL == false)
|| (request.IsSecureConnection && GlobalSettings.UseSSL)
? ":" + request.ServerVariables["SERVER_PORT"]
: "";
var ssl = GlobalSettings.UseSSL ? "s" : ""; // force, whatever the first request
var url = "http" + ssl + "://" + request.ServerVariables["SERVER_NAME"] + port + IOHelper.ResolveUrl(SystemDirectories.Umbraco);
appctx.UmbracoApplicationUrl = UriUtility.TrimPathEndSlash(url);
LogHelper.Info<ApplicationContext>("ApplicationUrl: " + appctx.UmbracoApplicationUrl + " (UmbracoModule request)");
}
/// <summary>
/// Begins to process a request.
/// </summary>
/// <param name="httpContext"></param>
static void BeginRequest(HttpContextBase httpContext)
{
// ensure application url is initialized
EnsureApplicationUrl(httpContext.Request);
//we need to set the initial url in our ApplicationContext, this is so our keep alive service works and this must
//exist on a global context because the keep alive service doesn't run in a web context.
//we are NOT going to put a lock on this because locking will slow down the application and we don't really care
//if two threads write to this at the exact same time during first page hit.
//see: http://issues.umbraco.org/issue/U4-2059
if (ApplicationContext.Current.OriginalRequestUrl.IsNullOrWhiteSpace())
{
// If (HTTP and SSL not required) or (HTTPS and SSL required), use ports from request to configure OriginalRequestUrl.
// Otherwise, user may need to set baseUrl manually per http://our.umbraco.org/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks if non-standard ports used.
if ((!httpContext.Request.IsSecureConnection && !GlobalSettings.UseSSL) || (httpContext.Request.IsSecureConnection && GlobalSettings.UseSSL))
{
// Use port from request.
ApplicationContext.Current.OriginalRequestUrl = string.Format("{0}:{1}{2}", httpContext.Request.ServerVariables["SERVER_NAME"], httpContext.Request.ServerVariables["SERVER_PORT"], IOHelper.ResolveUrl(SystemDirectories.Umbraco));
}
else
{
// Omit port entirely.
ApplicationContext.Current.OriginalRequestUrl = string.Format("{0}{1}", httpContext.Request.ServerVariables["SERVER_NAME"], IOHelper.ResolveUrl(SystemDirectories.Umbraco));
}
LogHelper.Info<UmbracoModule>("Setting OriginalRequestUrl: " + ApplicationContext.Current.OriginalRequestUrl);
}
// do not process if client-side request
// do not process if client-side request
if (httpContext.Request.Url.IsClientSideRequest())
return;
+9 -9
View File
@@ -1,24 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
<package id="ClientDependency" version="1.8.3.1" targetFramework="net45" />
<package id="ClientDependency" version="1.8.4" targetFramework="net45" />
<package id="dotless" version="1.4.1.0" targetFramework="net45" />
<package id="Examine" version="0.1.63.0" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.6" targetFramework="net40" />
<package id="Examine" version="0.1.65.0" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.6" targetFramework="net4" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" targetFramework="net40" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net4" />
<package id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" targetFramework="net4" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net4" />
<package id="Microsoft.AspNet.WebApi" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net4" />
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="MiniProfiler" version="2.1.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
<package id="UrlRewritingNet.UrlRewriter" version="2.0.60829.1" targetFramework="net40" />
<package id="xmlrpcnet" version="2.5.0" targetFramework="net40" />
<package id="UrlRewritingNet.UrlRewriter" version="2.0.60829.1" targetFramework="net4" />
<package id="xmlrpcnet" version="2.5.0" targetFramework="net4" />
</packages>
+129 -201
View File
@@ -1,31 +1,30 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Hosting;
using System.Xml;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.web;
using umbraco.DataLayer;
using umbraco.presentation.nodeFactory;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.web;
using Umbraco.Core.Models;
using Umbraco.Core.Profiling;
using umbraco.DataLayer;
using umbraco.presentation.nodeFactory;
using Umbraco.Web;
using Umbraco.Web.PublishedCache.XmlPublishedCache;
using Umbraco.Web.Scheduling;
using Node = umbraco.NodeFactory.Node;
using File = System.IO.File;
using Node = umbraco.NodeFactory.Node;
using Task = System.Threading.Tasks.Task;
namespace umbraco
{
@@ -36,58 +35,44 @@ namespace umbraco
{
private XmlCacheFilePersister _persisterTask;
private volatile bool _released;
#region Constructors
private content()
{
if (SyncToXmlFile)
{
// if we write to file, prepare the lock
// (if we don't use the file, or just read from it, no need to lock)
InitializeFileLock();
// and prepare the persister task
// prepare the persister task
// there's always be one task keeping a ref to the runner
// so it's safe to just create it as a local var here
var runner = new BackgroundTaskRunner<XmlCacheFilePersister>("XmlCacheFilePersister", new BackgroundTaskRunnerOptions
{
LongRunning = true,
KeepAlive = true
KeepAlive = true,
Hosted = false // main domain will take care of stopping the runner (see below)
});
// when the runner is terminating we need to ensure that no modifications
// to content are possible anymore, as they would not be written out to
// the xml file - unfortunately that is not possible in 7.x because we
// cannot lock the content service... and so we do nothing...
//runner.Terminating += (sender, args) =>
//{
//};
// when the runner has terminated we know we will not be writing to the file
// anymore, so we can release the lock now - no need to wait for the AppDomain
// unload - which means any "last minute" saves will be lost - but waiting for
// the AppDomain to unload has issues...
runner.Terminated += (sender, args) =>
{
if (_fileLock == null) return; // not locking (testing?)
if (_fileLocked == null) return; // not locked
// thread-safety
// lock something that's readonly and not null..
lock (_xmlFileName)
{
// double-check
if (_fileLocked == null) return;
LogHelper.Debug<content>("Release file lock.");
_fileLocked.Dispose();
_fileLocked = null;
_fileLock = null; // ensure we don't lock again
}
};
// create (and add to runner)
_persisterTask = new XmlCacheFilePersister(runner, this);
var registered = ApplicationContext.Current.MainDom.Register(
null,
() =>
{
// once released, the cache still works but does not write to file anymore,
// which is OK with database server messenger but will cause data loss with
// another messenger...
runner.Shutdown(false, true); // wait until flushed
_released = true;
});
// failed to become the main domain, we will never use the file
if (registered == false)
runner.Shutdown(false, true);
_released = (registered == false);
}
// initialize content - populate the cache
@@ -127,23 +112,26 @@ namespace umbraco
private static readonly object DbReadSyncLock = new object();
private const string XmlContextContentItemKey = "UmbracoXmlContextContent";
private string _umbracoXmlDiskCacheFileName = string.Empty;
private static string _umbracoXmlDiskCacheFileName = string.Empty;
private volatile XmlDocument _xmlContent;
/// <summary>
/// Gets the path of the umbraco XML disk cache file.
/// </summary>
/// <value>The name of the umbraco XML disk cache file.</value>
public static string GetUmbracoXmlDiskFileName()
{
if (string.IsNullOrEmpty(_umbracoXmlDiskCacheFileName))
{
_umbracoXmlDiskCacheFileName = IOHelper.MapPath(SystemFiles.ContentCacheXml);
}
return _umbracoXmlDiskCacheFileName;
}
[Obsolete("Use the safer static GetUmbracoXmlDiskFileName() method instead to retrieve this value")]
public string UmbracoXmlDiskCacheFileName
{
get
{
if (string.IsNullOrEmpty(_umbracoXmlDiskCacheFileName))
{
_umbracoXmlDiskCacheFileName = IOHelper.MapPath(SystemFiles.ContentCacheXml);
}
return _umbracoXmlDiskCacheFileName;
}
get { return GetUmbracoXmlDiskFileName(); }
set { _umbracoXmlDiskCacheFileName = value; }
}
@@ -204,7 +192,7 @@ namespace umbraco
var node = GetPreviewOrPublishedNode(d, xmlContentCopy, false);
var attr = ((XmlElement)node).GetAttributeNode("sortOrder");
attr.Value = d.sortOrder.ToString();
AddOrUpdateXmlNode(xmlContentCopy, d.Id, d.Level, parentId, node);
xmlContentCopy = GetAddOrUpdateXmlNode(xmlContentCopy, d.Id, d.Level, parentId, node);
// update sitemapprovider
if (updateSitemapProvider && SiteMap.Provider is UmbracoSiteMapProvider)
@@ -368,7 +356,7 @@ namespace umbraco
{
foreach (Document d in Documents)
{
PublishNodeDo(d, safeXml.Xml, true);
safeXml.Xml = PublishNodeDo(d, safeXml.Xml, true);
}
}
@@ -390,7 +378,18 @@ namespace umbraco
public virtual void ClearDocumentCache(int documentId)
{
// Get the document
var d = new Document(documentId);
Document d;
try
{
d = new Document(documentId);
}
catch
{
// if we need the document to remove it... this cannot be LB?!
// shortcut everything here
ClearDocumentXmlCache(documentId);
return;
}
ClearDocumentCache(d);
}
@@ -411,26 +410,8 @@ namespace umbraco
// remove from xml db cache
doc.XmlRemoveFromDB();
// We need to lock content cache here, because we cannot allow other threads
// making changes at the same time, they need to be queued
using (var safeXml = GetSafeXmlReader())
{
// Check if node present, before cloning
x = safeXml.Xml.GetElementById(doc.Id.ToString());
if (x == null)
return;
safeXml.UpgradeToWriter(false);
// Find the document in the xml cache
x = safeXml.Xml.GetElementById(doc.Id.ToString());
if (x != null)
{
// The document already exists in cache, so repopulate it
x.ParentNode.RemoveChild(x);
safeXml.Commit();
}
}
// clear xml cache
ClearDocumentXmlCache(doc.Id);
ClearContextCache();
@@ -446,6 +427,30 @@ namespace umbraco
}
}
internal void ClearDocumentXmlCache(int id)
{
// We need to lock content cache here, because we cannot allow other threads
// making changes at the same time, they need to be queued
using (var safeXml = GetSafeXmlReader())
{
// Check if node present, before cloning
var x = safeXml.Xml.GetElementById(id.ToString());
if (x == null)
return;
safeXml.UpgradeToWriter(false);
// Find the document in the xml cache
x = safeXml.Xml.GetElementById(id.ToString());
if (x != null)
{
// The document already exists in cache, so repopulate it
x.ParentNode.RemoveChild(x);
safeXml.Commit();
}
}
}
/// <summary>
/// Unpublishes the node.
/// </summary>
@@ -669,9 +674,9 @@ order by umbracoNode.level, umbracoNode.sortOrder";
{
//TODO: Should there be a try/catch here in case the file is being written to while this is trying to be executed?
if (File.Exists(UmbracoXmlDiskCacheFileName))
if (File.Exists(GetUmbracoXmlDiskFileName()))
{
return new FileInfo(UmbracoXmlDiskCacheFileName).LastWriteTimeUtc;
return new FileInfo(GetUmbracoXmlDiskFileName()).LastWriteTimeUtc;
}
return DateTime.MinValue;
@@ -801,7 +806,7 @@ order by umbracoNode.level, umbracoNode.sortOrder";
return xmlDoc == null ? null : (XmlDocument) xmlDoc.CloneNode(true);
}
private static void EnsureSchema(string contentTypeAlias, XmlDocument xml)
private static XmlDocument EnsureSchema(string contentTypeAlias, XmlDocument xml)
{
string subset = null;
@@ -814,15 +819,22 @@ order by umbracoNode.level, umbracoNode.sortOrder";
// ensure it contains the content type
if (subset != null && subset.Contains(string.Format("<!ATTLIST {0} id ID #REQUIRED>", contentTypeAlias)))
return;
return xml;
// remove current doctype
xml.RemoveChild(n);
// alas, that does not work, replacing a doctype is ignored and GetElementById fails
//
//// remove current doctype, set new doctype
//xml.RemoveChild(n);
//subset = string.Format("<!ELEMENT {1} ANY>{0}<!ATTLIST {1} id ID #REQUIRED>{0}{2}", Environment.NewLine, contentTypeAlias, subset);
//var doctype = xml.CreateDocumentType("root", null, null, subset);
//xml.InsertAfter(doctype, xml.FirstChild);
// set new doctype
var xml2 = new XmlDocument();
subset = string.Format("<!ELEMENT {1} ANY>{0}<!ATTLIST {1} id ID #REQUIRED>{0}{2}", Environment.NewLine, contentTypeAlias, subset);
var doctype = xml.CreateDocumentType("root", null, null, subset);
xml.InsertAfter(doctype, xml.FirstChild);
var doctype = xml2.CreateDocumentType("root", null, null, subset);
xml2.AppendChild(doctype);
xml2.AppendChild(xml2.ImportNode(xml.DocumentElement, true));
return xml2;
}
private static void InitializeXml(XmlDocument xml, string dtd)
@@ -977,100 +989,6 @@ order by umbracoNode.level, umbracoNode.sortOrder";
private readonly string _xmlFileName = IOHelper.MapPath(SystemFiles.ContentCacheXml);
private DateTime _lastFileRead; // last time the file was read
private DateTime _nextFileCheck; // last time we checked whether the file was changed
private AsyncLock _fileLock; // protects the file
private IDisposable _fileLocked; // protects the file
private const int FileLockTimeoutMilliseconds = 4*60*1000; // 4'
private void InitializeFileLock()
{
// initialize file lock
// ApplicationId will look like "/LM/W3SVC/1/Root/AppName"
// name is system-wide and must be less than 260 chars
//
// From MSDN C++ CreateSemaphore doc:
// "The name can have a "Global\" or "Local\" prefix to explicitly create the object in
// the global or session namespace. The remainder of the name can contain any character
// except the backslash character (\). For more information, see Kernel Object Namespaces."
//
// From MSDN "Kernel object namespaces" doc:
// "The separate client session namespaces enable multiple clients to run the same
// applications without interfering with each other. For processes started under
// a client session, the system uses the session namespace by default. However, these
// processes can use the global namespace by prepending the "Global\" prefix to the object name."
//
// just use "default" (whatever it is) for now - ie, no prefix
//
var name = HostingEnvironment.ApplicationID + "/XmlStore/XmlFile";
_fileLock = new AsyncLock(name);
// the file lock works with a shared, system-wide, semaphore - and we don't want
// to leak a count on that semaphore else the whole process will hang - so we have
// to ensure we dispose of the locker when the domain goes down - in theory the
// async lock should do it via its finalizer, but then there are some weird cases
// where the semaphore has been disposed of before it's been released, and then
// we'd need to GC-pin the semaphore... better dispose the locker explicitely
// when the app domain unloads.
if (AppDomain.CurrentDomain.IsDefaultAppDomain())
{
LogHelper.Debug<content>("Registering Unload handler for default app domain.");
AppDomain.CurrentDomain.ProcessExit += OnDomainUnloadReleaseFileLock;
}
else
{
LogHelper.Debug<content>("Registering Unload handler for non-default app domain.");
AppDomain.CurrentDomain.DomainUnload += OnDomainUnloadReleaseFileLock;
}
}
private void EnsureFileLock()
{
if (_fileLock == null) return; // not locking (testing?)
if (_fileLocked != null) return; // locked already
// thread-safety, acquire lock only once!
// lock something that's readonly and not null..
lock (_xmlFileName)
{
// double-check
if (_fileLock == null) return;
if (_fileLocked != null) return;
// don't hang forever, throws if it cannot lock within the timeout
LogHelper.Debug<content>("Acquiring exclusive access to file for this AppDomain...");
_fileLocked = _fileLock.Lock(FileLockTimeoutMilliseconds);
LogHelper.Debug<content>("Acquired exclusive access to file for this AppDomain.");
}
}
private void OnDomainUnloadReleaseFileLock(object sender, EventArgs args)
{
// the unload event triggers AFTER all hosted objects (eg the file persister
// background task runner) have been stopped, so we should have released the
// lock already - this is for safety - might be possible to get rid of it
// NOTE
// trying to write to the log via LogHelper at that point is a BAD idea
// it can lead to ugly deadlocks with the named semaphore - DONT do it
if (_fileLock == null) return; // not locking (testing?)
if (_fileLocked == null) return; // not locked
// thread-safety
// lock something that's readonly and not null..
lock (_xmlFileName)
{
// double-check
if (_fileLocked == null) return;
// in case you really need to debug... that should be safe...
//System.IO.File.AppendAllText(HostingEnvironment.MapPath("~/App_Data/log.txt"), string.Format("{0} {1} unlock", DateTime.Now, AppDomain.CurrentDomain.Id));
_fileLocked.Dispose();
_fileLock = null; // ensure we don't lock again
}
}
// not used - just try to read the file
//private bool XmlFileExists
@@ -1092,7 +1010,9 @@ order by umbracoNode.level, umbracoNode.sortOrder";
}
}
// assumes file lock
// invoked by XmlCacheFilePersister ONLY and that one manages the MainDom, ie it
// will NOT try to save once the current app domain is not the main domain anymore
// (no need to test _released)
internal void SaveXmlToFile()
{
LogHelper.Info<content>("Save Xml to file...");
@@ -1102,8 +1022,6 @@ order by umbracoNode.level, umbracoNode.sortOrder";
var xml = _xmlContent; // capture (atomic + volatile), immutable anyway
if (xml == null) return;
EnsureFileLock();
// delete existing file, if any
DeleteXmlFile();
@@ -1111,7 +1029,7 @@ order by umbracoNode.level, umbracoNode.sortOrder";
var directoryName = Path.GetDirectoryName(_xmlFileName);
if (directoryName == null)
throw new Exception(string.Format("Invalid XmlFileName \"{0}\".", _xmlFileName));
if (System.IO.File.Exists(_xmlFileName) == false && Directory.Exists(directoryName) == false)
if (File.Exists(_xmlFileName) == false && Directory.Exists(directoryName) == false)
Directory.CreateDirectory(directoryName);
// save
@@ -1132,8 +1050,10 @@ order by umbracoNode.level, umbracoNode.sortOrder";
}
}
// assumes file lock
internal async System.Threading.Tasks.Task SaveXmlToFileAsync()
// invoked by XmlCacheFilePersister ONLY and that one manages the MainDom, ie it
// will NOT try to save once the current app domain is not the main domain anymore
// (no need to test _released)
internal async Task SaveXmlToFileAsync()
{
LogHelper.Info<content>("Save Xml to file...");
@@ -1142,8 +1062,6 @@ order by umbracoNode.level, umbracoNode.sortOrder";
var xml = _xmlContent; // capture (atomic + volatile), immutable anyway
if (xml == null) return;
EnsureFileLock();
// delete existing file, if any
DeleteXmlFile();
@@ -1151,7 +1069,7 @@ order by umbracoNode.level, umbracoNode.sortOrder";
var directoryName = Path.GetDirectoryName(_xmlFileName);
if (directoryName == null)
throw new Exception(string.Format("Invalid XmlFileName \"{0}\".", _xmlFileName));
if (System.IO.File.Exists(_xmlFileName) == false && Directory.Exists(directoryName) == false)
if (File.Exists(_xmlFileName) == false && Directory.Exists(directoryName) == false)
Directory.CreateDirectory(directoryName);
// save
@@ -1200,17 +1118,15 @@ order by umbracoNode.level, umbracoNode.sortOrder";
return sb.ToString();
}
// assumes file lock
private XmlDocument LoadXmlFromFile()
{
// do NOT try to load if we are not the main domain anymore
if (_released) return null;
LogHelper.Info<content>("Load Xml from file...");
try
{
// if we're not writing back to the file, no need to lock
if (SyncToXmlFile)
EnsureFileLock();
var xml = new XmlDocument();
using (var fs = new FileStream(_xmlFileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
@@ -1233,12 +1149,11 @@ order by umbracoNode.level, umbracoNode.sortOrder";
}
}
// (files is always locked)
private void DeleteXmlFile()
{
if (System.IO.File.Exists(_xmlFileName) == false) return;
System.IO.File.SetAttributes(_xmlFileName, FileAttributes.Normal);
System.IO.File.Delete(_xmlFileName);
if (File.Exists(_xmlFileName) == false) return;
File.SetAttributes(_xmlFileName, FileAttributes.Normal);
File.Delete(_xmlFileName);
}
private void ReloadXmlFromFileIfChanged()
@@ -1268,8 +1183,15 @@ order by umbracoNode.level, umbracoNode.sortOrder";
#region Manage change
// adds or updates a node (docNode) into a cache (xml)
//TODO remove as soon as we can break backward compatibility
[Obsolete("Use GetAddOrUpdateXmlNode which returns an updated Xml document.", false)]
public static void AddOrUpdateXmlNode(XmlDocument xml, int id, int level, int parentId, XmlNode docNode)
{
GetAddOrUpdateXmlNode(xml, id, level, parentId, docNode);
}
// adds or updates a node (docNode) into a cache (xml)
public static XmlDocument GetAddOrUpdateXmlNode(XmlDocument xml, int id, int level, int parentId, XmlNode docNode)
{
// sanity checks
if (id != docNode.AttributeValue<int>("id"))
@@ -1283,7 +1205,12 @@ order by umbracoNode.level, umbracoNode.sortOrder";
// if the document is not there already then it's a new document
// we must make sure that its document type exists in the schema
if (currentNode == null && UseLegacySchema == false)
EnsureSchema(docNode.Name, xml);
{
var xml2 = EnsureSchema(docNode.Name, xml);
if (ReferenceEquals(xml, xml2) == false)
docNode = xml2.ImportNode(docNode, true);
xml = xml2;
}
// find the parent
XmlNode parentNode = level == 1
@@ -1292,7 +1219,7 @@ order by umbracoNode.level, umbracoNode.sortOrder";
// no parent = cannot do anything
if (parentNode == null)
return;
return xml;
// insert/move the node under the parent
if (currentNode == null)
@@ -1360,6 +1287,7 @@ order by umbracoNode.level, umbracoNode.sortOrder";
// then we just need to ensure that currentNode is at the right position.
// should be faster that moving all the nodes around.
XmlHelper.SortNode(parentNode, ChildNodesXPath, currentNode, x => x.AttributeValue<int>("sortOrder"));
return xml;
}
private static void TransferValuesFromDocumentXmlToPublishedXml(XmlNode documentNode, XmlNode publishedNode)
@@ -18,9 +18,7 @@ namespace umbraco.presentation
var appContext = (ApplicationContext) sender;
//TODO: This won't always work, in load balanced scenarios ping will not work because
// this original request url will be public and not internal to the server.
var url = string.Format("http://{0}/ping.aspx", appContext.OriginalRequestUrl);
var url = appContext.UmbracoApplicationUrl + "/ping.aspx";
try
{
using (var wc = new WebClient())
@@ -537,8 +537,8 @@ namespace umbraco
/// <returns></returns>
private Control AddMacroResultToCache(Control macroControl)
{
// Add result to cache if successful
if (Model.CacheDuration > 0)
// Add result to cache if successful (and cache is enabled)
if (UmbracoContext.Current.InPreviewMode == false && Model.CacheDuration > 0)
{
// do not add to cache if there's no member and it should cache by personalization
if (!Model.CacheByMember || (Model.CacheByMember && Member.IsLoggedOn()))
@@ -67,7 +67,7 @@ namespace umbraco.cms.presentation.developer.RelationTypes.TreeMenu
/// </summary>
public string JsSource
{
get { return "/umbraco/developer/RelationTypes/TreeMenu/ActionDeleteRelationType.js"; }
get { return "~/umbraco/developer/RelationTypes/TreeMenu/ActionDeleteRelationType.js"; }
}
/// <summary>
@@ -67,7 +67,7 @@ namespace umbraco.cms.presentation.developer.RelationTypes.TreeMenu
/// </summary>
public string JsSource
{
get { return "/umbraco/developer/RelationTypes/TreeMenu/ActionNewRelationType.js"; }
get { return "~/umbraco/developer/RelationTypes/TreeMenu/ActionNewRelationType.js"; }
}
/// <summary>
@@ -102,7 +102,7 @@ namespace umbraco.presentation.preview
if (document.Content.Published == false
&& ApplicationContext.Current.Services.ContentService.HasPublishedVersion(document.Id))
previewXml.Attributes.Append(XmlContent.CreateAttribute("isDraft"));
content.AddOrUpdateXmlNode(XmlContent, document.Id, document.Level, parentId, previewXml);
XmlContent = content.GetAddOrUpdateXmlNode(XmlContent, document.Id, document.Level, parentId, previewXml);
}
if (includeSubs)
@@ -112,7 +112,7 @@ namespace umbraco.presentation.preview
var previewXml = XmlContent.ReadNode(XmlReader.Create(new StringReader(prevNode.Xml)));
if (prevNode.IsDraft)
previewXml.Attributes.Append(XmlContent.CreateAttribute("isDraft"));
content.AddOrUpdateXmlNode(XmlContent, prevNode.NodeId, prevNode.Level, prevNode.ParentId, previewXml);
XmlContent = content.GetAddOrUpdateXmlNode(XmlContent, prevNode.NodeId, prevNode.Level, prevNode.ParentId, previewXml);
}
}
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Examine;
@@ -17,60 +16,7 @@ namespace UmbracoExamine.Config
internal static IIndexCriteria ToIndexCriteria(this IndexSet set, IDataService svc,
IEnumerable<StaticField> indexFieldPolicies)
{
var attributeFields = set.IndexAttributeFields.Cast<IIndexField>().ToArray();
var userFields = set.IndexUserFields.Cast<IIndexField>().ToArray();
var includeNodeTypes = set.IncludeNodeTypes.ToList().Select(x => x.Name).ToArray();
var excludeNodeTypes = set.ExcludeNodeTypes.ToList().Select(x => x.Name).ToArray();
var parentId = set.IndexParentId;
//if there are no user fields defined, we'll populate them from the data source (include them all)
if (set.IndexUserFields.Count == 0)
{
//we need to add all user fields to the collection if it is empty (this is the default if none are specified)
var userProps = svc.ContentService.GetAllUserPropertyNames();
var fields = new List<IIndexField>();
foreach (var u in userProps)
{
var field = new IndexField() { Name = u };
var policy = indexFieldPolicies.FirstOrDefault(x => x.Name == u);
if (policy != null)
{
field.Type = policy.Type;
field.EnableSorting = policy.EnableSorting;
}
fields.Add(field);
}
userFields = fields.ToArray();
}
//if there are no attribute fields defined, we'll populate them from the data source (include them all)
if (set.IndexAttributeFields.Count == 0)
{
//we need to add all system fields to the collection if it is empty (this is the default if none are specified)
var sysProps = svc.ContentService.GetAllSystemPropertyNames();
var fields = new List<IIndexField>();
foreach (var s in sysProps)
{
var field = new IndexField() { Name = s };
var policy = indexFieldPolicies.FirstOrDefault(x => x.Name == s);
if (policy != null)
{
field.Type = policy.Type;
field.EnableSorting = policy.EnableSorting;
}
fields.Add(field);
}
attributeFields = fields.ToArray();
}
return new IndexCriteria(
attributeFields,
userFields,
includeNodeTypes,
excludeNodeTypes,
parentId);
return new LazyIndexCriteria(set, svc, indexFieldPolicies);
}
/// <summary>
@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Examine;
using Examine.LuceneEngine.Config;
using UmbracoExamine.DataServices;
namespace UmbracoExamine.Config
{
internal class LazyIndexCriteria : IIndexCriteria
{
public LazyIndexCriteria(
IndexSet set,
IDataService svc,
IEnumerable<StaticField> indexFieldPolicies)
{
if (set == null) throw new ArgumentNullException("set");
if (indexFieldPolicies == null) throw new ArgumentNullException("indexFieldPolicies");
if (svc == null) throw new ArgumentNullException("svc");
_lazyCriteria = new Lazy<IIndexCriteria>(() =>
{
var attributeFields = set.IndexAttributeFields.Cast<IIndexField>().ToArray();
var userFields = set.IndexUserFields.Cast<IIndexField>().ToArray();
var includeNodeTypes = set.IncludeNodeTypes.Cast<IIndexField>().Select(x => x.Name).ToArray();
var excludeNodeTypes = set.ExcludeNodeTypes.Cast<IIndexField>().Select(x => x.Name).ToArray();
var parentId = set.IndexParentId;
//if there are no user fields defined, we'll populate them from the data source (include them all)
if (set.IndexUserFields.Count == 0)
{
//we need to add all user fields to the collection if it is empty (this is the default if none are specified)
var userProps = svc.ContentService.GetAllUserPropertyNames();
var fields = new List<IIndexField>();
foreach (var u in userProps)
{
var field = new IndexField() { Name = u };
var policy = indexFieldPolicies.FirstOrDefault(x => x.Name == u);
if (policy != null)
{
field.Type = policy.Type;
field.EnableSorting = policy.EnableSorting;
}
fields.Add(field);
}
userFields = fields.ToArray();
}
//if there are no attribute fields defined, we'll populate them from the data source (include them all)
if (set.IndexAttributeFields.Count == 0)
{
//we need to add all system fields to the collection if it is empty (this is the default if none are specified)
var sysProps = svc.ContentService.GetAllSystemPropertyNames();
var fields = new List<IIndexField>();
foreach (var s in sysProps)
{
var field = new IndexField() { Name = s };
var policy = indexFieldPolicies.FirstOrDefault(x => x.Name == s);
if (policy != null)
{
field.Type = policy.Type;
field.EnableSorting = policy.EnableSorting;
}
fields.Add(field);
}
attributeFields = fields.ToArray();
}
return new IndexCriteria(
attributeFields,
userFields,
includeNodeTypes,
excludeNodeTypes,
parentId);
});
}
private readonly Lazy<IIndexCriteria> _lazyCriteria;
public IEnumerable<string> ExcludeNodeTypes
{
get { return _lazyCriteria.Value.ExcludeNodeTypes; }
}
public IEnumerable<string> IncludeNodeTypes
{
get { return _lazyCriteria.Value.IncludeNodeTypes; }
}
public int? ParentNodeId
{
get { return _lazyCriteria.Value.ParentNodeId; }
}
public IEnumerable<IIndexField> StandardFields
{
get { return _lazyCriteria.Value.StandardFields; }
}
public IEnumerable<IIndexField> UserFields
{
get { return _lazyCriteria.Value.UserFields; }
}
}
}
+3 -2
View File
@@ -82,9 +82,9 @@
<AssemblyOriginatorKeyFile>..\Solution Items\TheFARM-Public.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="Examine, Version=0.1.63.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine, Version=0.1.65.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.63.0\lib\Examine.dll</HintPath>
<HintPath>..\packages\Examine.0.1.65.0\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -111,6 +111,7 @@
<ItemGroup>
<Compile Include="BaseUmbracoIndexer.cs" />
<Compile Include="Config\IndexSetExtensions.cs" />
<Compile Include="Config\LazyIndexCriteria.cs" />
<Compile Include="DataServices\IContentService.cs" />
<Compile Include="DataServices\IDataService.cs" />
<Compile Include="DataServices\ILogService.cs" />
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Examine" version="0.1.63.0" targetFramework="net45" />
<package id="Examine" version="0.1.65.0" targetFramework="net45" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
</packages>
+5 -5
View File
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Examine" version="0.1.63.0" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.6" targetFramework="net40" />
<package id="Examine" version="0.1.65.0" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.6" targetFramework="net4" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net4" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net4" />
<package id="Microsoft.AspNet.WebApi" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net4" />
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" />
@@ -45,9 +45,9 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Examine, Version=0.1.63.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine, Version=0.1.65.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.63.0\lib\Examine.dll</HintPath>
<HintPath>..\packages\Examine.0.1.65.0\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="HtmlAgilityPack, Version=1.4.6.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -6,6 +6,7 @@ using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using System.Web.UI;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
@@ -181,7 +182,14 @@ namespace umbraco.BasePages
/// <returns></returns>
public static int GetUserId()
{
var identity = HttpContext.Current.GetCurrentIdentity(true);
var identity = HttpContext.Current.GetCurrentIdentity(
//DO NOT AUTO-AUTH UNLESS THE CURRENT HANDLER IS WEBFORMS!
// Without this check, anything that is using this legacy API, like ui.Text will
// automatically log the back office user in even if it is a front-end request (if there is
// a back office user logged in. This can cause problems becaues the identity is changing mid
// request. For example: http://issues.umbraco.org/issue/U4-4010
HttpContext.Current.CurrentHandler is Page);
if (identity == null)
return -1;
return Convert.ToInt32(identity.Id);
@@ -205,7 +213,14 @@ namespace umbraco.BasePages
/// <returns></returns>
public static bool ValidateCurrentUser()
{
var identity = HttpContext.Current.GetCurrentIdentity(true);
var identity = HttpContext.Current.GetCurrentIdentity(
//DO NOT AUTO-AUTH UNLESS THE CURRENT HANDLER IS WEBFORMS!
// Without this check, anything that is using this legacy API, like ui.Text will
// automatically log the back office user in even if it is a front-end request (if there is
// a back office user logged in. This can cause problems becaues the identity is changing mid
// request. For example: http://issues.umbraco.org/issue/U4-4010
HttpContext.Current.CurrentHandler is Page);
if (identity != null)
{
return true;
@@ -232,7 +247,14 @@ namespace umbraco.BasePages
{
get
{
var identity = HttpContext.Current.GetCurrentIdentity(true);
var identity = HttpContext.Current.GetCurrentIdentity(
//DO NOT AUTO-AUTH UNLESS THE CURRENT HANDLER IS WEBFORMS!
// Without this check, anything that is using this legacy API, like ui.Text will
// automatically log the back office user in even if it is a front-end request (if there is
// a back office user logged in. This can cause problems becaues the identity is changing mid
// request. For example: http://issues.umbraco.org/issue/U4-4010
HttpContext.Current.CurrentHandler is Page);
return identity == null ? "" : identity.SessionId;
}
set
+2 -1
View File
@@ -13,6 +13,7 @@ using umbraco.cms.businesslogic.workflow;
using umbraco.interfaces;
using System.Text.RegularExpressions;
using System.Linq;
using Umbraco.Core.IO;
using TypeFinder = Umbraco.Core.TypeFinder;
namespace umbraco.BusinessLogic.Actions
@@ -88,7 +89,7 @@ namespace umbraco.BusinessLogic.Actions
{
return ActionsResolver.Current.Actions
.Where(x => !string.IsNullOrWhiteSpace(x.JsSource))
.Select(x => x.JsSource).ToList();
.Select(x => IOHelper.ResolveUrl(x.JsSource)).ToList();
//return ActionJsReference;
}
+4 -4
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ClientDependency" version="1.8.3.1" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.6" targetFramework="net40" />
<package id="ClientDependency" version="1.8.4" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.6" targetFramework="net4" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net45" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net40" />
<package id="Tidy.Net" version="1.0.0" targetFramework="net40" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net4" />
<package id="Tidy.Net" version="1.0.0" targetFramework="net4" />
</packages>
+3 -3
View File
@@ -106,9 +106,9 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="ClientDependency.Core, Version=1.8.3.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency.1.8.3.1\lib\net45\ClientDependency.Core.dll</HintPath>
<Reference Include="ClientDependency.Core, Version=1.8.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ClientDependency.1.8.4\lib\net45\ClientDependency.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="HtmlAgilityPack, Version=1.4.6.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
+1 -1
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ClientDependency" version="1.8.3.1" targetFramework="net45" />
<package id="ClientDependency" version="1.8.4" targetFramework="net45" />
</packages>
+3 -3
View File
@@ -68,9 +68,9 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="ClientDependency.Core, Version=1.8.3.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency.1.8.3.1\lib\net45\ClientDependency.Core.dll</HintPath>
<Reference Include="ClientDependency.Core, Version=1.8.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ClientDependency.1.8.4\lib\net45\ClientDependency.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.configuration" />
+1 -1
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ClientDependency" version="1.8.3.1" targetFramework="net45" />
<package id="ClientDependency" version="1.8.4" targetFramework="net45" />
</packages>
@@ -114,9 +114,9 @@
<Project>{651E1350-91B6-44B7-BD60-7207006D7003}</Project>
<Name>Umbraco.Web</Name>
</ProjectReference>
<Reference Include="ClientDependency.Core, Version=1.8.2.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency.1.8.3.1\lib\net45\ClientDependency.Core.dll</HintPath>
<Reference Include="ClientDependency.Core, Version=1.8.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ClientDependency.1.8.4\lib\net45\ClientDependency.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System">
<Name>System</Name>