Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8baaae4da6 | |||
| ed05a3223f | |||
| 25e3134e35 | |||
| f1b9b98f5d | |||
| d2f7eda074 | |||
| 3062eedb4f | |||
| 1e3dd4c5cd | |||
| a7871abbd9 | |||
| bf3a08cf85 | |||
| 7bc208e490 | |||
| 6aa633c8c5 | |||
| 9be2f99e7b | |||
| 2d5bfc0fe2 | |||
| c01f864e37 | |||
| a8be796d68 | |||
| 4acbdbe896 | |||
| 34a9eef8b8 | |||
| 0f56fa4baa | |||
| 82a8404b2e | |||
| bce8affe16 | |||
| 4bbd8097a2 | |||
| da9c810d78 | |||
| 431d066300 | |||
| 6d50bd891a | |||
| 1d00e3c5d8 | |||
| e69422c8d8 | |||
| a277ac2fe4 | |||
| 1067b1e245 | |||
| 7987f13a11 | |||
| ccda1bdd73 | |||
| 76a6f2d2d0 | |||
| 69428b2281 | |||
| 7cd39918a0 | |||
| 9a626c9da6 | |||
| 0520797c09 | |||
| 9b85ba5b90 | |||
| ca738979ce | |||
| 8cfb56adf4 | |||
| 053346d79b | |||
| 044c9c95f8 | |||
| b23ea69b3e | |||
| 46f9c5ff05 | |||
| 3bc4b50d74 | |||
| 1a2849dfb0 | |||
| bcb5883230 | |||
| 18080c1cfb | |||
| 2600a3887e | |||
| 45bec2a67d | |||
| da7456fd2a | |||
| 275ba00e24 | |||
| d00bf4649d | |||
| 04976f8063 | |||
| 3e7c9fc81e | |||
| a1f4d7b16f | |||
| 811ef4a444 | |||
| 69ac621f5a | |||
| 610582df38 | |||
| 181f7c1819 | |||
| 73fbe7d78c | |||
| 5d1340e864 | |||
| c867f609f1 | |||
| 69879dc8f8 | |||
| f663a13391 |
@@ -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,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
@@ -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")]
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
+8
-3
@@ -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");
|
||||
}
|
||||
|
||||
+50
-5
@@ -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
|
||||
{
|
||||
|
||||
@@ -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('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;}
|
||||
@@ -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',
|
||||
|
||||
+66
-21
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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()))
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
+1
-1
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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>
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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,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>
|
||||
@@ -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,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>
|
||||
|
||||
Reference in New Issue
Block a user