Compare commits

...

59 Commits

Author SHA1 Message Date
Sebastiaan Janssen f100d82e2c Bump version 2015-06-03 14:03:04 +02:00
Shannon 60ce993c09 Fixes change password dashboard to have autocomplete = off and to not show plain text passwords. 2015-06-03 12:20:20 +02:00
Shannon c07021d2a0 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-06-02 22:41:06 +02:00
Shannon 744ee39904 Changes the IIS header removal to BeginRequest instead of PreSendRequestHeaders since that is not a recommended practice 2015-06-02 22:40:57 +02:00
Shannon Deminick d669bf64c0 Merge pull request #698 from Nicholas-Westby/dev-v7
Fixes U4-6665 (Localization Bug)
2015-06-02 21:29:16 +02:00
Shannon ff277cd99c Fixes: U4-6654 NullReferenceException downloading translation xml 2015-06-02 21:13:29 +02:00
Shannon Deminick 30cd303a13 Merge pull request #697 from wp-manaosoftware/dev-7-angularthrowexception
U4-6621 - fix issue AngularJS throws exception that locationEvent was…
2015-06-02 21:04:08 +02:00
Shannon 60ce987507 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-06-02 20:53:55 +02:00
Shannon a231899052 Fixes: U4-6635 with label overlaying the folder icon when the folder icons are small. 2015-06-02 20:53:46 +02:00
Shannon Deminick 7ddcccb0c5 Merge pull request #685 from gavinfaux/U4-6618
Back office insecure content warning loading Google Web Fonts when running over HTTPS
2015-06-02 20:16:12 +02:00
Nicholas-Westby db824d8daf Fixes U4-6665 (Localization Bug)
Fix for localization not working some of the time: http://issues.umbraco.org/issue/U4-6665
2015-05-29 21:02:07 -07:00
Warm a1a30240f6 U4-6621 - fix issue AngularJS throws exception that locationEvent was not defined when leaved a page without saving 2015-05-29 10:40:48 +07:00
Shannon Deminick 7c4a189aa3 Merge pull request #696 from tomfulton/fix/u4-6655
Fix U4-6655:  RTE Macro not rendering in backoffice when "macroAlias" is not first attribute
2015-05-26 08:01:40 +10:00
Tom Fulton 4ec66bc868 Fix U4-6655: RTE Macro not rendering in backoffice when "macroAlias" is not first attribute
Loosens UMBRACO_MACRO regex parsing to fix, add tests
2015-05-22 20:40:30 -06:00
Stephan fa8fe8ec66 BackgroundTaskRunner - few fixes 2015-05-21 21:54:30 +02:00
Stephan a76ba9a0a7 BackgroundTaskRunner - more fixes 2015-05-21 19:37:22 +02:00
Stephan b804ff6107 Explicit exception on cache reentry 2015-05-21 16:09:20 +02:00
Shannon 72046a6ab9 removes test - expected behavior 2015-05-21 16:36:09 +10:00
Shannon ea2303c327 commits cache tests with recursive key lookup 2015-05-21 16:13:58 +10:00
Shannon 4c4117a052 Fixes BackgroundTaskRunner - moves the OnStopped call to where it should be. Un-ignores tests - these need to execute and cannot be ignored. They are not long running but if there are issues then they will timeout - DO NOT IGNORE. if the build server has issues with these then it's our logic that is at fault. 2015-05-21 13:05:58 +10:00
Stephan 521b8076d2 U4-6638 - bugfix BackgroundTaskRunner 2015-05-20 20:56:05 +02:00
Stephan 1def88d764 U4-6626 2015-05-20 18:26:08 +02:00
Stephan 910c349f7c Bugfix StyleSheet concatenation causing LOH GC issues 2015-05-20 17:00:32 +02:00
Shannon 6cfb5bc678 Have made the buttons always left aligned with the editing section, seems better since it's consistent on all screens and less right hand motion to click a button 2015-05-20 16:13:36 +10:00
Shannon a3b5996372 Fixes save/publish button placement on small screens 2015-05-20 16:01:44 +10:00
Shannon 31b4560daa Merge branch 'sebastian-claesson-dev-v7' into dev-v7 2015-05-20 15:08:45 +10:00
Shannon 2f2dd9c9b6 Merge branch 'dev-v7' of https://github.com/sebastian-claesson/Umbraco-CMS into sebastian-claesson-dev-v7 2015-05-20 15:07:48 +10:00
Shannon Deminick 02599e1254 Merge pull request #694 from Alain-es/datepicker-custom-format
Datepicker custom format
2015-05-20 14:48:36 +10:00
Stephan ac7ff31a67 U4-6626 - XmlCache, bugfix locks 2015-05-19 20:08:52 +02:00
Stephan 009f071f48 XmlCache - comments 2015-05-19 14:04:32 +02:00
Stephan e0ecb3291d XmlCache - bugfix running with file cache disabled 2015-05-19 10:20:36 +02:00
Stephan dbaf7c0190 U4-6597 - fix issue with caches that would cache exceptions 2015-05-19 09:24:38 +02:00
Stephan 16e06b6d4d Revert 444a028, 0e5a469 and 9d8f33e 2015-05-19 09:24:25 +02:00
Shannon 444a028e30 adds tests for caching exceptions and only caching one time and fixes HttpRequestCacheProvider to work with them. 2015-05-19 15:57:11 +10:00
Shannon 0e5a469577 Fixes: U4-6597 Umbraco fails to reconnect to a SQL Server if it started up when SQL server was offline - however this requires some review. 2015-05-19 14:29:25 +10:00
Shannon 9d8f33e98e Fixes: U4-6597 Umbraco fails to reconnect to a SQL Server if it started up when SQL server was offline - however this requires some review. 2015-05-19 14:25:31 +10:00
Alain-es 2abb3177af Datepicker custom format 2015-05-18 15:57:27 +01:00
Alain-es 43f99e11a5 Datepicker custom format 2015-05-18 15:54:50 +01:00
Sebastian Claesson Hoffback d1d9878ab6 Fix content-edit-view on mobile devices
The content editing view did not work at all on small screens. The label
took to much space, leaving the controller with almost none. Now the
label and the controller get its own row on smaller screens. Much
cleaner look and you can acctually edit a page on a small screen. Note:
Grids still don't work well.
2015-05-18 13:59:42 +02:00
Sebastian Claesson Hoffback e9753c72a7 Merge pull request #1 from umbraco/dev-v7
Merge Shannons changes
2015-05-18 12:13:33 +02:00
Shannon 3b791c75f6 Fixes: U4-5020 Login view on mobile screen is too big 2015-05-18 20:09:03 +10:00
Shannon Deminick 35cb4d5690 Merge pull request #693 from sebastian-claesson/dev-v7
Fixed login-view on mobile devices
2015-05-18 19:37:33 +10:00
Sebastian Claesson Hoffback adde7c2f50 Fixed login-view on mobile devices
On smaller screens the login-view was too far to the right.

Related issue: http://issues.umbraco.org/issue/U4-5020
2015-05-18 11:34:13 +02:00
Shannon Deminick 62943d391f Merge pull request #692 from bjarnef/dev-v7-U4-6629
Fix changes in user section labels
2015-05-18 13:47:13 +10:00
Shannon Deminick 0942eb839d Merge pull request #691 from Vrijdagonline/dev-v7
Updating Dutch Translations
2015-05-18 11:38:29 +10:00
bjarnef 21a4dfe33e Fix changes in user section labels
Fixed translation in da.xml according to change in en.xml  from 7.2.4 to
7.2.5
2015-05-17 22:16:39 +02:00
Stephan 8121fc42e5 U4-6626 - fix log level 2015-05-17 21:47:31 +02:00
Stephan c356ed1df8 Logger - add AppDomain.Id to log 2015-05-17 18:48:26 +02:00
Stephan 92f56ccedd U4-6626 - XmlCache, bugfix locks 2015-05-17 18:48:25 +02:00
Stephan 7cbe2410e4 Do not flood the log when installing a new version 2015-05-17 18:48:21 +02:00
Arjan Woldring 1dda3ce45d Updating Dutch Translations
Missing keys added, deleted keys deleted, reorderded some keys based on
the english one and corrected couple of wrong translations.
2015-05-16 19:06:04 +02:00
Shannon Deminick 1e29a4a8ca Merge pull request #688 from sebastian-claesson/dev-v7
Swedish was using the wrong format for variables
2015-05-15 15:46:17 +10:00
Sebastian Claesson Hoffback 01de0bd6a1 Swedish was using the wrong format for variables
The swedish translation was using {0} instead of %0%. Leading to alot of
texts being weirdly formated.
2015-05-15 07:22:11 +02:00
Shannon Deminick 4d0d29c004 Merge pull request #686 from Aaen/dev-v7
Change translation for add row for fix issue U4-6564
2015-05-14 07:57:12 +10:00
Shannon Deminick 741c2bb085 Merge pull request #687 from sebastian-claesson/dev-v7
Swedish translation for the grid-editor
2015-05-14 07:54:16 +10:00
Sebastian Claesson Hoffback 2c809857af Swedish translation for the grid-editor
Swedish translation was completly missing for the grid-editor
2015-05-13 14:59:27 +02:00
Dennis Aaen 236d6ee302 Change translation for add row for fix issue U4-6564 2015-05-13 13:27:37 +02:00
Gavin Faux 3094dea5d4 Back office insecure content warning loading Google Web Fonts when running over HTTPS
Amend to use https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js
2015-05-13 09:45:20 +01:00
Shannon f829b0b16c Fixes text not overlayed on top of icon for folders in folder browser 2015-05-13 10:56:25 +10:00
53 changed files with 1141 additions and 583 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.2.5
7.2.6
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.2.5")]
[assembly: AssemblyInformationalVersion("7.2.5")]
[assembly: AssemblyFileVersion("7.2.6")]
[assembly: AssemblyInformationalVersion("7.2.6")]
+11 -7
View File
@@ -165,6 +165,8 @@ namespace Umbraco.Core
/// </remarks>
internal string OriginalRequestUrl { get; set; }
private bool _versionsDifferenceReported;
/// <summary>
/// Checks if the version configured matches the assembly version
/// </summary>
@@ -174,17 +176,19 @@ namespace Umbraco.Core
{
try
{
string configStatus = ConfigurationStatus;
string currentVersion = UmbracoVersion.Current.ToString(3);
var configStatus = ConfigurationStatus;
var currentVersion = UmbracoVersion.Current.ToString(3);
var ok = configStatus == currentVersion;
if (currentVersion != configStatus)
if (ok == false && _versionsDifferenceReported == false)
{
LogHelper.Info<ApplicationContext>("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'");
// 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 (configStatus == currentVersion);
return ok;
}
catch
{
+9
View File
@@ -11,6 +11,15 @@ namespace Umbraco.Core
// - this is NOT a reader/writer lock
// - this is NOT a recursive lock
//
// using a named Semaphore here and not a Mutex because mutexes have thread
// affinity which does not work with async situations
//
// it is important that managed code properly release the Semaphore before
// going down else it will maintain the lock - however note that when the
// whole process (w3wp.exe) goes down and all handles to the Semaphore have
// been closed, the Semaphore system object is destroyed - so in any case
// an iisreset should clean up everything
//
internal class AsyncLock
{
private readonly SemaphoreSlim _semaphore;
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Cache
protected const string CacheItemPrefix = "umbrtmche";
// an object that represent a value that has not been created yet
protected readonly object ValueNotCreated = new object();
protected internal static readonly object ValueNotCreated = new object();
// manupulate the underlying cache entries
// these *must* be called from within the appropriate locks
@@ -30,21 +30,58 @@ namespace Umbraco.Core.Cache
return string.Format("{0}-{1}", CacheItemPrefix, key);
}
protected object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
protected internal static Lazy<object> GetSafeLazy(Func<object> getCacheItem)
{
// try to generate the value and if it fails,
// wrap in an ExceptionHolder - would be much simpler
// to just use lazy.IsValueFaulted alas that field is
// internal
return new Lazy<object>(() =>
{
try
{
return getCacheItem();
}
catch (Exception e)
{
return new ExceptionHolder(e);
}
});
}
protected internal static object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
{
// if onlyIfValueIsCreated, do not trigger value creation
// must return something, though, to differenciate from null values
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
// if execution has thrown then lazy.IsValueCreated is false
// and lazy.IsValueFaulted is true (but internal) so we use our
// own exception holder (see Lazy<T> source code) to return null
if (lazy.Value is ExceptionHolder) return null;
// we have a value and execution has not thrown so returning
// here does not throw - unless we're re-entering, take care of it
try
{
// if onlyIfValueIsCreated, do not trigger value creation
// must return something, though, to differenciate from null values
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
return lazy.Value;
}
catch
catch (InvalidOperationException e)
{
return null;
throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e);
}
}
internal class ExceptionHolder
{
public ExceptionHolder(Exception e)
{
Exception = e;
}
public Exception Exception { get; private set; }
}
#region Clear
public virtual void ClearAllCache()
@@ -105,15 +105,22 @@ namespace Umbraco.Core.Cache
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = new Lazy<object>(getCacheItem);
result = GetSafeLazy(getCacheItem);
ContextItems[cacheKey] = result;
}
}
// this may throw if getCacheItem throws, but this is the only place where
// it would throw as everywhere else we use GetLazySaveValue() to hide exceptions
// and pretend exceptions were never inserted into cache to begin with.
return result.Value;
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
// exceptions (but try again and again) and silently eat them - however at
// some point we have to report them - so need to re-throw here
// this does not throw anymore
//return result.Value;
var value = result.Value; // will not throw (safe lazy)
var eh = value as ExceptionHolder;
if (eh != null) throw eh.Exception; // throw once!
return value;
}
#endregion
@@ -129,7 +129,7 @@ namespace Umbraco.Core.Cache
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = new Lazy<object>(getCacheItem);
result = GetSafeLazy(getCacheItem);
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);
@@ -138,10 +138,17 @@ namespace Umbraco.Core.Cache
}
}
// this may throw if getCacheItem throws, but this is the only place where
// it would throw as everywhere else we use GetLazySaveValue() to hide exceptions
// and pretend exceptions were never inserted into cache to begin with.
return result.Value;
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
// exceptions (but try again and again) and silently eat them - however at
// some point we have to report them - so need to re-throw here
// this does not throw anymore
//return result.Value;
value = result.Value; // will not throw (safe lazy)
var eh = value as ExceptionHolder;
if (eh != null) throw eh.Exception; // throw once!
return value;
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
@@ -173,7 +180,7 @@ namespace Umbraco.Core.Cache
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
// and make sure we don't store a null value.
var result = new Lazy<object>(getCacheItem);
var result = GetSafeLazy(getCacheItem);
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)
@@ -19,29 +19,11 @@ namespace Umbraco.Core.Cache
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
internal ObjectCache MemoryCache;
// an object that represent a value that has not been created yet
protected readonly object ValueNotCreated = new object();
public ObjectCacheRuntimeCacheProvider()
{
MemoryCache = new MemoryCache("in-memory");
}
protected object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
{
try
{
// if onlyIfValueIsCreated, do not trigger value creation
// must return something, though, to differenciate from null values
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
return lazy.Value;
}
catch
{
return null;
}
}
#region Clear
public virtual void ClearAllCache()
@@ -72,7 +54,7 @@ namespace Umbraco.Core.Cache
// x.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
return value == null || value.GetType().ToString().InvariantEquals(typeName);
})
.Select(x => x.Key)
@@ -92,7 +74,7 @@ namespace Umbraco.Core.Cache
// x.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
return value == null || value.GetType() == typeOfT;
})
.Select(x => x.Key)
@@ -112,7 +94,7 @@ namespace Umbraco.Core.Cache
// x.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = GetSafeLazyValue((Lazy<object>)x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
if (value == null) return true;
return value.GetType() == typeOfT
&& predicate(x.Key, (T) value);
@@ -161,7 +143,7 @@ namespace Umbraco.Core.Cache
.ToArray(); // evaluate while locked
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
@@ -176,7 +158,7 @@ namespace Umbraco.Core.Cache
.ToArray(); // evaluate while locked
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
@@ -188,7 +170,7 @@ namespace Umbraco.Core.Cache
{
result = MemoryCache.Get(cacheKey) as Lazy<object>; // null if key not found
}
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
return result == null ? null : DictionaryCacheProviderBase.GetSafeLazyValue(result); // return exceptions as null
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
@@ -205,9 +187,9 @@ namespace Umbraco.Core.Cache
using (var lck = new UpgradeableReadLock(_locker))
{
result = MemoryCache.Get(cacheKey) as Lazy<object>;
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
if (result == null || DictionaryCacheProviderBase.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = new Lazy<object>(getCacheItem);
result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
lck.UpgradeToWriteLock();
@@ -215,7 +197,12 @@ namespace Umbraco.Core.Cache
}
}
return result.Value;
//return result.Value;
var value = result.Value; // will not throw (safe lazy)
var eh = value as DictionaryCacheProviderBase.ExceptionHolder;
if (eh != null) throw eh.Exception; // throw once!
return value;
}
#endregion
@@ -227,7 +214,7 @@ namespace Umbraco.Core.Cache
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
// and make sure we don't store a null value.
var result = new Lazy<object>(getCacheItem);
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
var value = result.Value; // force evaluation now
if (value == null) return; // do not store null values (backward compat)
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.2.5");
private static readonly Version Version = new Version("7.2.6");
/// <summary>
/// Gets the current version of Umbraco.
+1 -1
View File
@@ -66,7 +66,7 @@ namespace Umbraco.Core
InitializeProfilerResolver();
_timer = DisposableTimer.DebugDuration<CoreBootManager>("Umbraco application starting", "Umbraco application startup complete");
_timer = DisposableTimer.TraceDuration<CoreBootManager>("Umbraco application starting", "Umbraco application startup complete");
CreateApplicationCache();
+2 -2
View File
@@ -40,8 +40,8 @@ namespace Umbraco.Core.Logging
/// <returns></returns>
private static string PrefixThreadId(string generateMessageFormat)
{
return "[Thread " + Thread.CurrentThread.ManagedThreadId + "] " + generateMessageFormat;
}
return "[T" + Thread.CurrentThread.ManagedThreadId + "/D" + AppDomain.CurrentDomain.Id + "] " + generateMessageFormat;
}
#region Error
/// <summary>
+1 -1
View File
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Macros
internal class MacroTagParser
{
private static readonly Regex MacroRteContent = new Regex(@"(<!--\s*?)(<\?UMBRACO_MACRO.*?/>)(\s*?-->)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?</\?UMBRACO_MACRO>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO (?:.+)?macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?</\?UMBRACO_MACRO>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
/// <summary>
/// This formats the persisted string to something useful for the rte so that the macro renders properly since we
+79 -1
View File
@@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using System.Web.UI;
using NUnit.Framework;
using Umbraco.Core.Cache;
@@ -21,6 +22,83 @@ namespace Umbraco.Tests.Cache
public virtual void TearDown()
{
Provider.ClearAllCache();
}
[Test]
public void Throws_On_Reentry()
{
// don't run for StaticCacheProvider - not making sense
if (GetType() == typeof (StaticCacheProviderTests))
Assert.Ignore("Do not run for StaticCacheProvider.");
Exception exception = null;
var result = Provider.GetCacheItem("blah", () =>
{
try
{
var result2 = Provider.GetCacheItem("blah");
}
catch (Exception e)
{
exception = e;
}
return "value";
});
Assert.IsNotNull(exception);
Assert.IsAssignableFrom<InvalidOperationException>(exception);
}
[Test]
public void Does_Not_Cache_Exceptions()
{
var counter = 0;
object result;
try
{
result = Provider.GetCacheItem("Blah", () =>
{
counter++;
throw new Exception("Do not cache this");
});
}
catch (Exception){}
try
{
result = Provider.GetCacheItem("Blah", () =>
{
counter++;
throw new Exception("Do not cache this");
});
}
catch (Exception){}
Assert.Greater(counter, 1);
}
[Test]
public void Ensures_Delegate_Result_Is_Cached_Once()
{
var counter = 0;
object result;
result = Provider.GetCacheItem("Blah", () =>
{
counter++;
return "";
});
result = Provider.GetCacheItem("Blah", () =>
{
counter++;
return "";
});
Assert.AreEqual(counter, 1);
}
[Test]
@@ -1,4 +1,5 @@
using System.Web;
using System;
using System.Web;
using NUnit.Framework;
using Umbraco.Core.Cache;
@@ -29,5 +30,29 @@ namespace Umbraco.Tests.Cache
{
get { return _provider; }
}
[Test]
public void DoesNotCacheExceptions()
{
string value;
Assert.Throws<Exception>(() => { value = (string)_provider.GetCacheItem("key", () => GetValue(1)); });
Assert.Throws<Exception>(() => { value = (string)_provider.GetCacheItem("key", () => GetValue(2)); });
// does not throw
value = (string)_provider.GetCacheItem("key", () => GetValue(3));
Assert.AreEqual("succ3", value);
// cache
value = (string)_provider.GetCacheItem("key", () => GetValue(4));
Assert.AreEqual("succ3", value);
}
private static string GetValue(int i)
{
Console.WriteLine("get" + i);
if (i < 3)
throw new Exception("fail");
return "succ" + i;
}
}
}
@@ -109,6 +109,29 @@ namespace Umbraco.Tests.Macros
<p>asdfasdf</p>".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty));
}
[Test]
public void Format_RTE_Data_For_Editor_With_Params_When_MacroAlias_Not_First()
{
var content = @"<p>asdfasdf</p>
<p>asdfsadf</p>
<?UMBRACO_MACRO test1=""value1"" test2=""value2"" macroAlias=""Map"" />
<p>asdfasdf</p>";
var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary<string, string>() { { "test1", "value1" }, { "test2", "value2" } });
// Assert.AreEqual(@"<p>asdfasdf</p>
//<p>asdfsadf</p>
//<div class=""umb-macro-holder Map mceNonEditable"" test1=""value1"" test2=""value2"">
//<!-- <?UMBRACO_MACRO macroAlias=""Map"" test1=""value1"" test2=""value2"" /> -->
//<ins>Macro alias: <strong>Map</strong></ins></div>
//<p>asdfasdf</p>".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty));
Assert.AreEqual(@"<p>asdfasdf</p>
<p>asdfsadf</p>
<div class=""umb-macro-holder mceNonEditable"" test1=""value1"" test2=""value2"">
<!-- <?UMBRACO_MACRO test1=""value1"" test2=""value2"" macroAlias=""Map"" /> -->
<ins>Macro alias: <strong>Map</strong></ins></div>
<p>asdfasdf</p>".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty));
}
[Test]
public void Format_RTE_Data_For_Editor_With_Params_Closing_Tag()
{
@@ -21,17 +21,29 @@ namespace Umbraco.Tests.Scheduling
TestHelper.SetupLog4NetForTests();
}
/*
[Test]
public async void ShutdownWaitWhenRunning()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions { AutoStart = true, KeepAlive = true }))
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions
{
Assert.IsTrue(runner.IsRunning);
Thread.Sleep(800); // for long
Assert.IsTrue(runner.IsRunning);
AutoStart = true,
KeepAlive = true
}))
{
var stopped = false;
runner.Stopped += (sender, args) => { stopped = true; };
Assert.IsTrue(runner.IsRunning); // because AutoStart is true
Thread.Sleep(500); // and because KeepAlive is true...
Assert.IsTrue(runner.IsRunning); // ...it keeps running
runner.Shutdown(false, true); // -force +wait
await runner; // wait for the entire runner operation to complete
Assert.IsTrue(runner.IsCompleted);
await runner.StoppedAwaitable; // runner stops, within test's timeout
Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner
Assert.IsFalse(runner.IsRunning); // no more running tasks
Assert.IsTrue(stopped);
}
}
@@ -44,38 +56,121 @@ namespace Umbraco.Tests.Scheduling
runner.TaskStarting += (sender, args) => Console.WriteLine("starting {0}", DateTime.Now);
runner.TaskCompleted += (sender, args) => Console.WriteLine("completed {0}", DateTime.Now);
runner.Stopped += (sender, args) => Console.WriteLine("stopped {0}", DateTime.Now);
Assert.IsFalse(runner.IsRunning);
Assert.IsFalse(runner.IsRunning); // because AutoStart is false
Console.WriteLine("Adding task {0}", DateTime.Now);
runner.Add(new MyTask(5000));
Thread.Sleep(500);
Assert.IsTrue(runner.IsRunning); // is running the task
Console.WriteLine("Shutting down {0}", DateTime.Now);
runner.Shutdown(false, false); // -force -wait
Assert.IsTrue(runner.IsCompleted);
Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner
Assert.IsTrue(runner.IsRunning); // still running that task
Thread.Sleep(3000); // wait slightly less than the task takes to complete
Assert.IsTrue(runner.IsRunning); // still running that task
await runner; // wait for the entire runner operation to complete
await runner.StoppedAwaitable; // runner stops, within test's timeout
Console.WriteLine("End {0}", DateTime.Now);
}
}
*/
[Test]
public async void ShutdownWhenRunningWithWait()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
var stopped = false;
runner.Stopped += (sender, args) => { stopped = true; };
Assert.IsFalse(runner.IsRunning); // because AutoStart is false
runner.Add(new MyTask(5000));
Assert.IsTrue(runner.IsRunning); // is running the task
runner.Shutdown(false, true); // -force +wait
// all this before we await because +wait
Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner
Assert.IsFalse(runner.IsRunning); // no more running tasks
Assert.IsTrue(stopped);
await runner.StoppedAwaitable; // runner stops, within test's timeout
}
}
[Test]
public async void ShutdownWhenRunningWithoutWait()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
var stopped = false;
runner.Stopped += (sender, args) => { stopped = true; };
Assert.IsFalse(runner.IsRunning); // because AutoStart is false
runner.Add(new MyTask(5000));
Assert.IsTrue(runner.IsRunning); // is running the task
runner.Shutdown(false, false); // -force +wait
// all this before we await because -wait
Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner
Assert.IsTrue(runner.IsRunning); // still running the task
Assert.IsFalse(stopped);
await runner.StoppedAwaitable; // runner stops, within test's timeout
// and then...
Assert.IsFalse(runner.IsRunning); // no more running tasks
Assert.IsTrue(stopped);
}
}
[Test]
public async void ShutdownCompletesTheRunner()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
Assert.IsFalse(runner.IsRunning); // because AutoStart is false
// shutdown -force => run all queued tasks
runner.Shutdown(false, false); // -force -wait
await runner.StoppedAwaitable; // runner stops, within test's timeout
Assert.IsFalse(runner.IsRunning); // still not running anything
Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner
// cannot add tasks to it anymore
Assert.IsFalse(runner.TryAdd(new MyTask()));
Assert.Throws<InvalidOperationException>(() =>
{
runner.Add(new MyTask());
});
}
}
[Test]
public async void ShutdownFlushesTheQueue()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
Assert.IsFalse(runner.IsRunning);
MyTask t;
Assert.IsFalse(runner.IsRunning); // because AutoStart is false
runner.Add(new MyTask(5000));
runner.Add(new MyTask());
var t = new MyTask();
runner.Add(t);
Assert.IsTrue(runner.IsRunning); // is running the first task
runner.Add(t = new MyTask());
Assert.IsTrue(runner.IsRunning); // is running tasks
// shutdown -force => run all queued tasks
runner.Shutdown(false, false); // -force -wait
await runner; // wait for the entire runner operation to complete
Assert.IsTrue(runner.IsRunning); // is running tasks
await runner.StoppedAwaitable; // runner stops, within test's timeout
Assert.AreNotEqual(DateTime.MinValue, t.Ended); // t has run
}
}
@@ -85,15 +180,20 @@ namespace Umbraco.Tests.Scheduling
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
Assert.IsFalse(runner.IsRunning);
MyTask t;
Assert.IsFalse(runner.IsRunning); // because AutoStart is false
runner.Add(new MyTask(5000));
runner.Add(new MyTask());
var t = new MyTask();
runner.Add(t);
Assert.IsTrue(runner.IsRunning); // is running the first task
runner.Add(t = new MyTask());
Assert.IsTrue(runner.IsRunning); // is running tasks
// shutdown +force => tries to cancel the current task, ignores queued tasks
runner.Shutdown(true, false); // +force -wait
await runner; // wait for the entire runner operation to complete
Assert.AreEqual(DateTime.MinValue, t.Ended); // t has not run
Assert.IsTrue(runner.IsRunning); // is running that long task it cannot cancel
await runner.StoppedAwaitable; // runner stops, within test's timeout
Assert.AreEqual(DateTime.MinValue, t.Ended); // t has *not* run
}
}
@@ -102,23 +202,106 @@ namespace Umbraco.Tests.Scheduling
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
Assert.IsFalse(runner.IsRunning);
Assert.IsFalse(runner.IsRunning); // because AutoStart is false
runner.Add(new MyTask(5000));
runner.Add(new MyTask());
runner.Add(new MyTask());
Assert.IsTrue(runner.IsRunning); // is running the task
Assert.IsTrue(runner.IsRunning); // is running tasks
// shutdown -force => run all queued tasks
runner.Shutdown(false, false); // -force -wait
Assert.IsTrue(runner.IsCompleted);
Assert.IsTrue(runner.IsRunning); // still running that task
Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner
Assert.IsTrue(runner.IsRunning); // still running a task
Thread.Sleep(3000);
Assert.IsTrue(runner.IsRunning); // still running that task
Assert.IsTrue(runner.IsRunning); // still running a task
// shutdown +force => tries to cancel the current task, ignores queued tasks
runner.Shutdown(true, false); // +force -wait
await runner; // wait for the entire runner operation to complete
await runner.StoppedAwaitable; // runner stops, within test's timeout
}
}
[Test]
public async void HostingStopNonImmediate()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
MyTask t;
var stopped = false;
runner.Stopped += (sender, args) => { stopped = true; };
var terminating = false;
runner.Terminating += (sender, args) => { terminating = true; };
var terminated = false;
runner.Terminated += (sender, args) => { terminated = true; };
Assert.IsFalse(runner.IsRunning); // because AutoStart is false
runner.Add(new MyTask(5000));
runner.Add(new MyTask());
runner.Add(t = new MyTask());
Assert.IsTrue(runner.IsRunning); // is running the task
runner.Stop(false); // -immediate = -force, -wait
Assert.IsTrue(terminating); // has raised that event
Assert.IsFalse(terminated); // but not terminated yet
// all this before we await because -wait
Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner
Assert.IsTrue(runner.IsRunning); // still running the task
await runner.StoppedAwaitable; // runner stops, within test's timeout
Assert.IsFalse(runner.IsRunning);
Assert.IsTrue(stopped);
await runner.TerminatedAwaitable; // runner terminates, within test's timeout
Assert.IsTrue(terminated); // has raised that event
Assert.AreNotEqual(DateTime.MinValue, t.Ended); // t has run
}
}
[Test]
public void Create_IsRunning()
public async void HostingStopImmediate()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
MyTask t;
var stopped = false;
runner.Stopped += (sender, args) => { stopped = true; };
var terminating = false;
runner.Terminating += (sender, args) => { terminating = true; };
var terminated = false;
runner.Terminated += (sender, args) => { terminated = true; };
Assert.IsFalse(runner.IsRunning); // because AutoStart is false
runner.Add(new MyTask(5000));
runner.Add(new MyTask());
runner.Add(t = new MyTask());
Assert.IsTrue(runner.IsRunning); // is running the task
runner.Stop(true); // +immediate = +force, +wait
Assert.IsTrue(terminating); // has raised that event
Assert.IsTrue(terminated); // and that event
Assert.IsTrue(stopped); // and that one
// and all this before we await because +wait
Assert.IsTrue(runner.IsCompleted); // shutdown completes the runner
Assert.IsFalse(runner.IsRunning); // done running
await runner.StoppedAwaitable; // runner stops, within test's timeout
await runner.TerminatedAwaitable; // runner terminates, within test's timeout
Assert.AreEqual(DateTime.MinValue, t.Ended); // t has *not* run
}
}
[Test]
public void Create_IsNotRunning()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
@@ -126,14 +309,17 @@ namespace Umbraco.Tests.Scheduling
}
}
[Ignore]
[Test]
public async void Create_AutoStart_IsRunning()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions { AutoStart = true }))
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions
{
Assert.IsTrue(runner.IsRunning);
await runner; // wait for the entire runner operation to complete
AutoStart = true
}))
{
Assert.IsTrue(runner.IsRunning); // because AutoStart is true
await runner.StoppedAwaitable; // runner stops, within test's timeout
}
}
@@ -142,7 +328,7 @@ namespace Umbraco.Tests.Scheduling
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions { AutoStart = true, KeepAlive = true }))
{
Assert.IsTrue(runner.IsRunning);
Assert.IsTrue(runner.IsRunning); // because AutoStart is true
Thread.Sleep(800); // for long
Assert.IsTrue(runner.IsRunning);
// dispose will stop it
@@ -159,8 +345,13 @@ namespace Umbraco.Tests.Scheduling
// dispose will stop it
}
await runner; // wait for the entire runner operation to complete
await runner.StoppedAwaitable; // runner stops, within test's timeout
//await runner.TerminatedAwaitable; // NO! see note below
Assert.Throws<InvalidOperationException>(() => runner.Add(new MyTask()));
// but do NOT await on TerminatedAwaitable - disposing just shuts the runner down
// so that we don't have a runaway task in tests, etc - but it does NOT terminate
// the runner - it really is NOT a nice way to end a runner - it's there for tests
}
[Test]
@@ -188,7 +379,7 @@ namespace Umbraco.Tests.Scheduling
runner.Add(new MyTask());
Assert.IsTrue(runner.IsRunning);
waitHandle.WaitOne();
await runner; //since we are not being kept alive, it will quit
await runner.StoppedAwaitable; //since we are not being kept alive, it will quit
Assert.IsFalse(runner.IsRunning);
}
}
@@ -221,11 +412,11 @@ namespace Umbraco.Tests.Scheduling
runner.Add(task);
await runner.CurrentThreadingTask; // wait for the Task operation to complete
Assert.IsTrue(task.Ended != default(DateTime)); // task is done
await runner; // wait for the entire runner operation to complete
await runner.StoppedAwaitable; // wait for the entire runner operation to complete
}
}
[Ignore]
[Test]
public async void WaitOnRunner_Tasks()
{
@@ -237,7 +428,7 @@ namespace Umbraco.Tests.Scheduling
{
tasks.ForEach(runner.Add);
await runner; // wait for the entire runner operation to complete
await runner.StoppedAwaitable; // wait for the entire runner operation to complete
// check that tasks are done
Assert.IsTrue(tasks.All(x => x.Ended != default(DateTime)));
@@ -260,7 +451,7 @@ namespace Umbraco.Tests.Scheduling
runner.Add(task);
waitHandle.WaitOne(); // wait 'til task is done
Assert.IsTrue(task.Ended != default(DateTime)); // task is done
await runner; // wait for the entire runner operation to complete
await runner.StoppedAwaitable; // wait for the entire runner operation to complete
}
}
@@ -280,7 +471,7 @@ namespace Umbraco.Tests.Scheduling
WaitHandle.WaitAll(tasks.Values.Select(x => (WaitHandle)x).ToArray());
Assert.IsTrue(tasks.All(x => x.Key.Ended != default(DateTime)));
await runner; // wait for the entire runner operation to complete
await runner.StoppedAwaitable; // wait for the entire runner operation to complete
}
}
@@ -382,7 +573,7 @@ namespace Umbraco.Tests.Scheduling
}
}
[Ignore]
[Test]
public void RecurringTaskTest()
{
@@ -422,7 +613,7 @@ namespace Umbraco.Tests.Scheduling
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
var task = new MyDelayedTask(200);
var task = new MyDelayedTask(200, false);
runner.Add(task);
Assert.IsTrue(runner.IsRunning);
Thread.Sleep(5000);
@@ -431,7 +622,7 @@ namespace Umbraco.Tests.Scheduling
task.Release();
await runner.CurrentThreadingTask; //wait for current task to complete
Assert.IsTrue(task.HasRun);
await runner; // wait for the entire runner operation to complete
await runner.StoppedAwaitable; // wait for the entire runner operation to complete
}
}
@@ -440,19 +631,19 @@ namespace Umbraco.Tests.Scheduling
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
var task = new MyDelayedTask(200);
var task = new MyDelayedTask(200, true);
runner.Add(task);
Assert.IsTrue(runner.IsRunning);
Thread.Sleep(5000);
Assert.IsTrue(runner.IsRunning); // still waiting for the task to release
Assert.IsFalse(task.HasRun);
runner.Shutdown(false, false);
await runner; // wait for the entire runner operation to complete
await runner.StoppedAwaitable; // wait for the entire runner operation to complete
Assert.IsTrue(task.HasRun);
}
}
[Ignore]
[Test]
public void DelayedRecurring()
{
@@ -478,6 +669,7 @@ namespace Umbraco.Tests.Scheduling
waitHandle.WaitOne();
Assert.GreaterOrEqual(runCount, 4);
Assert.IsTrue(task.HasRun);
// stops recurring
runner.Shutdown(false, false);
@@ -492,10 +684,27 @@ namespace Umbraco.Tests.Scheduling
var exceptions = new ConcurrentQueue<Exception>();
runner.TaskError += (sender, args) => exceptions.Enqueue(args.Exception);
var task = new MyFailingTask(false); // -async
var task = new MyFailingTask(false, true, false); // -async, +running, -disposing
runner.Add(task);
Assert.IsTrue(runner.IsRunning);
await runner; // wait for the entire runner operation to complete
await runner.StoppedAwaitable; // wait for the entire runner operation to complete
Assert.AreEqual(1, exceptions.Count); // traced and reported
}
}
[Test]
public async void FailingTaskDisposing()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
var exceptions = new ConcurrentQueue<Exception>();
runner.TaskError += (sender, args) => exceptions.Enqueue(args.Exception);
var task = new MyFailingTask(false, false, true); // -async, -running, +disposing
runner.Add(task);
Assert.IsTrue(runner.IsRunning);
await runner.StoppedAwaitable; // wait for the entire runner operation to complete
Assert.AreEqual(1, exceptions.Count); // traced and reported
}
@@ -509,10 +718,27 @@ namespace Umbraco.Tests.Scheduling
var exceptions = new ConcurrentQueue<Exception>();
runner.TaskError += (sender, args) => exceptions.Enqueue(args.Exception);
var task = new MyFailingTask(true); // +async
var task = new MyFailingTask(true, true, false); // +async, +running, -disposing
runner.Add(task);
Assert.IsTrue(runner.IsRunning);
await runner; // wait for the entire runner operation to complete
await runner.StoppedAwaitable; // wait for the entire runner operation to complete
Assert.AreEqual(1, exceptions.Count); // traced and reported
}
}
[Test]
public async void FailingTaskDisposingAsync()
{
using (var runner = new BackgroundTaskRunner<IBackgroundTask>(new BackgroundTaskRunnerOptions()))
{
var exceptions = new ConcurrentQueue<Exception>();
runner.TaskError += (sender, args) => exceptions.Enqueue(args.Exception);
var task = new MyFailingTask(false, false, true); // -async, -running, +disposing
runner.Add(task);
Assert.IsTrue(runner.IsRunning);
await runner.StoppedAwaitable; // wait for the entire runner operation to complete
Assert.AreEqual(1, exceptions.Count); // traced and reported
}
@@ -521,22 +747,28 @@ namespace Umbraco.Tests.Scheduling
private class MyFailingTask : IBackgroundTask
{
private readonly bool _isAsync;
private readonly bool _running;
private readonly bool _disposing;
public MyFailingTask(bool isAsync)
public MyFailingTask(bool isAsync, bool running, bool disposing)
{
_isAsync = isAsync;
_running = running;
_disposing = disposing;
}
public void Run()
{
Thread.Sleep(1000);
throw new Exception("Task has thrown.");
if (_running)
throw new Exception("Task has thrown.");
}
public async Task RunAsync(CancellationToken token)
{
await Task.Delay(1000);
throw new Exception("Task has thrown.");
if (_running)
throw new Exception("Task has thrown.");
}
public bool IsAsync
@@ -544,13 +776,17 @@ namespace Umbraco.Tests.Scheduling
get { return _isAsync; }
}
// fixme - must also test what happens if we throw on dispose!
public void Dispose()
{ }
{
if (_disposing)
throw new Exception("Task has thrown.");
}
}
private class MyDelayedRecurringTask : DelayedRecurringTaskBase<MyDelayedRecurringTask>
{
public bool HasRun { get; private set; }
public MyDelayedRecurringTask(IBackgroundTaskRunner<MyDelayedRecurringTask> runner, int delayMilliseconds, int periodMilliseconds)
: base(runner, delayMilliseconds, periodMilliseconds)
{ }
@@ -566,7 +802,7 @@ namespace Umbraco.Tests.Scheduling
public override void PerformRun()
{
// nothing to do at the moment
HasRun = true;
}
public override Task PerformRunAsync()
@@ -583,30 +819,28 @@ namespace Umbraco.Tests.Scheduling
private class MyDelayedTask : ILatchedBackgroundTask
{
private readonly int _runMilliseconds;
private readonly ManualResetEvent _gate;
private readonly ManualResetEventSlim _gate;
public bool HasRun { get; private set; }
public MyDelayedTask(int runMilliseconds)
public MyDelayedTask(int runMilliseconds, bool runsOnShutdown)
{
_runMilliseconds = runMilliseconds;
_gate = new ManualResetEvent(false);
_gate = new ManualResetEventSlim(false);
RunsOnShutdown = runsOnShutdown;
}
public WaitHandle Latch
{
get { return _gate; }
get { return _gate.WaitHandle; }
}
public bool IsLatched
{
get { return true; }
get { return _gate.IsSet == false; }
}
public bool RunsOnShutdown
{
get { return true; }
}
public bool RunsOnShutdown { get; private set; }
public void Run()
{
@@ -9,7 +9,7 @@ LazyLoad.js([
'/Umbraco/js/umbraco.security.js',
'/Umbraco/ServerVariables',
'/Umbraco/lib/spectrum/spectrum.js',
'http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js',
'https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js',
'/umbraco/js/canvasdesigner.panel.js',
], function () {
jQuery(document).ready(function () {
@@ -180,7 +180,7 @@ var app = angular.module("Umbraco.canvasdesigner", ['colorpicker', 'ui.slider',
// TODO: special init for font family picker
if (item.type == "googlefontpicker" && item.values.fontFamily) {
var variant = item.values.fontWeight != "" || item.values.fontStyle != "" ? ":" + item.values.fontWeight + item.values.fontStyle : "";
var gimport = "@import url('http://fonts.googleapis.com/css?family=" + item.values.fontFamily + variant + "');";
var gimport = "@import url('https://fonts.googleapis.com/css?family=" + item.values.fontFamily + variant + "');";
if ($.inArray(gimport, parameters) < 0) {
parameters.splice(0, 0, gimport);
}
@@ -412,7 +412,7 @@ var app = angular.module("Umbraco.canvasdesigner", ['colorpicker', 'ui.slider',
var webFontScriptLoaded = false;
var loadGoogleFont = function (font) {
if (!webFontScriptLoaded) {
$.getScript('http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js')
$.getScript('https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js')
.done(function () {
webFontScriptLoaded = true;
// Recursively call once webfont script is available.
@@ -19,7 +19,7 @@ var refreshLayout = function (parameters) {
var webFontScriptLoaded = false;
var getFont = function (font) {
if (!webFontScriptLoaded) {
$.getScript('http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js')
$.getScript('https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js')
.done(function () {
webFontScriptLoaded = true;
// Recursively call once webfont script is available.
@@ -70,7 +70,7 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not
//This handles the 'unsaved changes' dialog which is triggered when a route is attempting to be changed but
// the form has pending changes
unsubscribe.push($rootScope.$on('$locationChangeStart', function(event, nextLocation, currentLocation) {
var locationEvent = $rootScope.$on('$locationChangeStart', function(event, nextLocation, currentLocation) {
if (!formCtrl.$dirty || isSavingNewItem) {
return;
}
@@ -93,7 +93,9 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not
eventsService.emit("valFormManager.pendingChanges", true);
}
}));
});
unsubscribe.push(locationEvent);
//Ensure to remove the event handler when this instance is destroyted
scope.$on('$destroy', function() {
for (var u in unsubscribe) {
@@ -15,7 +15,7 @@ function macroService() {
//This regex will match an alias of anything except characters that are quotes or new lines (for legacy reasons, when new macros are created
// their aliases are cleaned an invalid chars are stripped)
var expression = /(<\?UMBRACO_MACRO macroAlias=["']([^\"\'\n\r]+?)["'][\s\S]+?)(\/>|>.*?<\/\?UMBRACO_MACRO>)/i;
var expression = /(<\?UMBRACO_MACRO (?:.+)?macroAlias=["']([^\"\'\n\r]+?)["'][\s\S]+?)(\/>|>.*?<\/\?UMBRACO_MACRO>)/i;
var match = expression.exec(syntax);
if (!match || match.length < 3) {
return null;
@@ -794,3 +794,17 @@ legend + .control-group {
position: relative;
z-index: 1000;
}
@media (max-width: 767px) {
// Labels on own row
.form-horizontal .control-label {
width: 100%;
}
.form-horizontal .controls {
margin-left: 0;
}
}
+11 -2
View File
@@ -22,9 +22,10 @@
}
.login-overlay .form {
position:fixed;
display: block;
padding-top: 100px;
padding-left: 165px;
top: 100px;
left: 165px;
width: 370px;
text-align: right
}
@@ -44,4 +45,12 @@
padding-left: 6px;
margin-top: 10px;
text-align: center;
}
@media (max-width: 565px) {
// Remove padding on login-form on smaller devices
.login-overlay .form {
left: inherit;
right:25px;
}
}
+1 -1
View File
@@ -146,7 +146,7 @@ h5{
.controls-row {
padding-top: 5px;
margin-left: 240px !important;
margin-left: 240px;
}
.controls-row label {
display: inline-block
+10 -15
View File
@@ -37,11 +37,6 @@
bottom: 31px !important;
}
/*
.umb-tab-buttons.umb-bottom-bar {
bottom: 50px !important;
}*/
.umb-panel-header .umb-headline, .umb-panel-header h1 {
font-size: 16px;
border: none;
@@ -153,27 +148,27 @@
/* tab buttons */
.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);
border-top: 1px solid @grayLighter;
padding: 10px 0 10px 0;
position: fixed;
bottom: 0px;
left: 100px;
right: 20px;
z-index: 6010;
};
@media (min-width: 1101px) {
.umb-bottom-bar {left: 460px;}
}
.umb-tab-buttons{padding-left: 240px;}
.umb-tab-buttons{
padding-left: 0px;
}
@media (min-width: 1101px) {
.umb-bottom-bar {
left: 460px;
}
}
.umb-tab-pane{padding-bottom: 90px}
.tab-content{overflow: visible; }
@@ -308,10 +308,6 @@ ul.color-picker li a {
line-height: 120px
}
.umb-folderbrowser .selector-overlay{
display: none;
}
.umb-upload-drop-zone{
margin-bottom:5px;
}
@@ -369,7 +365,7 @@ ul.color-picker li a {
.umb-photo-folder .picrow div a:first-child {
width:100%;
width:100%;
height:100%;
}
@@ -389,6 +385,10 @@ ul.color-picker li a {
text-decoration: none;
}
.umb-photo-folder .selector-overlay{
display: none;
}
//this is a temp hack, to provide selectors in the dialog:
.umb-photo-folder .pic:hover .selector-overlay {
position: absolute;
@@ -421,6 +421,14 @@ ul.color-picker li a {
transform: translateY(-50%);
}
.umb-photo-folder .umb-non-thumbnail span{
position: absolute;
display: block;
margin: auto;
width: 100%;
top: 20px;
}
.umb-photo-folder .selected{
position: relative;
}
@@ -11,8 +11,7 @@
<div ng-style="img.style" class="umb-non-thumbnail" ng-if="!img.thumbnail">
<i class="icon large {{img.icon}}"></i>
{{img.name}}
<span>{{img.name}}</span>
</div>
<div ng-if="img.thumbnail" class="umb-photo" ng-style="img.thumbStyle" alt="{{img.name}}">
@@ -20,29 +20,32 @@
<!-- we need to show the old pass field when the provider cannot retrieve the password -->
<umb-control-group alias="oldPassword" label="Old password" ng-show="$parent.showOldPass()">
<input type="text" name="oldPassword" ng-model="$parent.model.value.oldPassword"
<input type="password" name="oldPassword" ng-model="$parent.model.value.oldPassword"
class="input-large umb-textstring textstring"
ng-required="$parent.showOldPass()"
val-server="oldPassword" no-dirty-check/>
val-server="oldPassword" no-dirty-check
autocomplete="off"/>
<span class="help-inline" val-msg-for="oldPassword" val-toggle-msg="required">Required</span>
<span class="help-inline" val-msg-for="oldPassword" val-toggle-msg="valServer"></span>
</umb-control-group>
<umb-control-group alias="password" label="New password" ng-show="$parent.showNewPass()">
<input type="text" name="password" ng-model="$parent.model.value.newPassword"
class="input-large umb-textstring textstring"
<input type="password" name="password" ng-model="$parent.model.value.newPassword"
class="input-large umb-textstring textstring"
ng-required="!$parent.model.value.reset"
val-server="value"
ng-minlength="{{$parent.model.config.minPasswordLength}}" no-dirty-check/>
val-server="value"
ng-minlength="{{$parent.model.config.minPasswordLength}}" no-dirty-check
autocomplete="off" />
<span class="help-inline" val-msg-for="password" val-toggle-msg="required">Required</span>
<span class="help-inline" val-msg-for="password" val-toggle-msg="minlength">Minimum {{$parent.model.config.minPasswordLength}} characters</span>
<span class="help-inline" val-msg-for="password" val-toggle-msg="valServer"></span>
</umb-control-group>
<umb-control-group alias="confirmpassword" label="Confirm password" ng-show="$parent.showConfirmPass()">
<input type="text" name="confirmpassword" ng-model="$parent.model.confirm"
<input type="password" name="confirmpassword" ng-model="$parent.model.confirm"
class="input-large umb-textstring textstring"
val-compare="password" no-dirty-check/>
val-compare="password" no-dirty-check
autocomplete="off" />
<span class="help-inline" val-msg-for="confirmpassword" val-toggle-msg="valCompare">Passwords must match</span>
</umb-control-group>
@@ -18,9 +18,16 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
//map the user config
$scope.model.config = angular.extend(config, $scope.model.config);
$scope.datetimePickerValue = $scope.model.value;
//hide picker if clicking on the document
$scope.hidePicker = function () {
$element.find("div:first").datetimepicker("hide");
//$element.find("div:first").datetimepicker("hide");
// Sometimes the statement above fails and generates errors in the browser console. The following statements fix that.
var dtp = $element.find("div:first");
if (dtp && dtp.datetimepicker) {
dtp.datetimepicker("hide");
}
};
$(document).bind("click", $scope.hidePicker);
@@ -67,8 +74,16 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
.datetimepicker($scope.model.config)
.on("dp.change", applyDate);
//manually assign the date to the plugin
$element.find("div:first").datetimepicker("setValue", $scope.model.value ? $scope.model.value : null);
//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);
}
}
//Ensure to remove the event handler when this instance is destroyted
$scope.$on('$destroy', function () {
@@ -1,7 +1,7 @@
<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="model.value"
ng-model="datetimePickerValue"
ng-required="model.validation.mandatory"
val-server="value" />
<span class="add-on">
@@ -40,6 +40,18 @@ describe('macro service tests', function () {
expect(result.macroParamsDictionary.test2).toBe("hello");
});
it('can parse syntax for macros when macroAlias is not the first parameter', function () {
var result = macroService.parseMacroSyntax("<?UMBRACO_MACRO test=\"asdf\" test2='hello' macroAlias='Map.Test' />");
expect(result).not.toBeNull();
expect(result.macroAlias).toBe("Map.Test");
expect(result.macroParamsDictionary.test).not.toBeUndefined();
expect(result.macroParamsDictionary.test).toBe("asdf");
expect(result.macroParamsDictionary.test2).not.toBeUndefined();
expect(result.macroParamsDictionary.test2).toBe("hello");
});
it('can parse syntax for macros with aliases containing whitespace and other chars', function () {
var result = macroService.parseMacroSyntax("<?UMBRACO_MACRO macroAlias='Map Test [Hello\\World]' test=\"asdf\" test2='hello' />");
+2 -2
View File
@@ -2540,9 +2540,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7250</DevelopmentServerPort>
<DevelopmentServerPort>7260</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7250</IISUrl>
<IISUrl>http://localhost:7260</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -118,33 +118,10 @@
<useDomainPrefixes>false</useDomainPrefixes>
<!-- this will add a trailing slash (/) to urls when in directory url mode -->
<addTrailingSlash>true</addTrailingSlash>
<urlReplacing removeDoubleDashes="true">
<urlReplacing removeDoubleDashes="true" toAscii="true">
<char org=" ">-</char>
<char org="&quot;"></char>
<char org="'"></char>
<char org="%"></char>
<char org="."></char>
<char org=";"></char>
<char org="/"></char>
<char org="\"></char>
<char org=":"></char>
<char org="#"></char>
<char org="+">plus</char>
<char org="*">star</char>
<char org="&amp;"></char>
<char org="?"></char>
<char org="æ">ae</char>
<char org="ø">oe</char>
<char org="å">aa</char>
<char org="ä">ae</char>
<char org="ö">oe</char>
<char org="ü">ue</char>
<char org="ß">ss</char>
<char org="Ä">ae</char>
<char org="Ö">oe</char>
<char org="|">-</char>
<char org="&lt;"></char>
<char org="&gt;"></char>
<char org="ö">oxxx</char>
<!--<char org="ü">ue</char>-->
</urlReplacing>
</requestHandler>
@@ -863,7 +863,7 @@ Mange hilsner fra Umbraco robotten
<key alias="editors">Redaktør</key>
<key alias="excerptField">Uddragsfelt</key>
<key alias="language">Sprog</key>
<key alias="loginname">Login</key>
<key alias="loginname">Brugernavn</key>
<key alias="mediastartnode">Startnode i mediearkivet</key>
<key alias="modules">Moduler</key>
<key alias="noConsole">Deaktivér adgang til Umbraco</key>
@@ -881,12 +881,11 @@ Mange hilsner fra Umbraco robotten
<key alias="permissionSelectPages">Vælg sider for at ændre deres rettigheder</key>
<key alias="searchAllChildren">Søg alle 'børn'</key>
<key alias="startnode">Start node</key>
<key alias="username">Brugernavn</key>
<key alias="username">Navn</key>
<key alias="userPermissions">Bruger tilladelser</key>
<key alias="usertype">Brugertype</key>
<key alias="userTypes">Bruger typer</key>
<key alias="writer">Forfatter</key>
<key alias="yourProfile">Din profil</key>
<key alias="yourHistory">Din historik</key>
<key alias="sessionExpires">Session udløber</key>
@@ -849,7 +849,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<area alias="grid">
<key alias="insertControl">Insert control</key>
<key alias="addRows">Add rows to your layout</key>
<key alias="addRows">Choose a layout for the page</key>
<key alias="addElement"><![CDATA[To start, click the <i class="icon icon-add blue"></i> below and add your first element]]></key>
<key alias="clickToEmbed">Click to embed</key>
@@ -849,7 +849,7 @@ To manage your website, simply open the Umbraco back office and start adding con
</area>
<area alias="grid">
<key alias="insertControl">Insert control</key>
<key alias="addRows">Add rows to your layout</key>
<key alias="addRows">Choose a layout for the page</key>
<key alias="addElement"><![CDATA[To start, click the <i class="icon icon-add blue"></i> below and add your first element]]></key>
<key alias="clickToEmbed">Click to embed</key>
+55 -34
View File
@@ -16,8 +16,6 @@
<key alias="disable">Uitschakelen</key>
<key alias="emptyTrashcan">Prullenbak leegmaken</key>
<key alias="exportDocumentType">Documenttype exporteren</key>
<key alias="exportDocumentTypeAsCode">Exporteer naar .NET</key>
<key alias="exportDocumentTypeAsCode-Full">Exporteer naar .NET</key>
<key alias="importDocumentType">Documenttype importeren</key>
<key alias="importPackage">Package importeren</key>
<key alias="liveEdit">Aanpassen in Canvas</key>
@@ -51,11 +49,11 @@
<key alias="domainCreated">Nieuw domein '%0%' is aangemaakt</key>
<key alias="domainDeleted">Domein '%0%' is verwijderd</key>
<key alias="domainExists">Domein '%0' is al aanwezig</key>
<key alias="domainUpdated">Domein '%0%' is bijgewerkt</key>
<key alias="orEdit">Bewerk huidige domeinen</key>
<key alias="domainHelp"><![CDATA[Geldige domeinnamen zijn: "example.com", "www.example.com", "example.com:8080" of
"https://www.example.com/".<br /><br />Zgn. 'one-level' paden in domeinen worden ondersteund, bijv. "example.com/en". Echter, ze
zouden moeten worden vermeden. Gebruik bij voorkeur de cultuurinstelling hierboven.]]></key>
<key alias="domainUpdated">Domein '%0%' is bijgewerkt</key>
<key alias="orEdit">Bewerk huidige domeinen</key>
<key alias="inherit">Overerven</key>
<key alias="setLanguage">Cultuur</key>
<key alias="setLanguageHelp"><![CDATA[Zet de cultuur voor de nodes onder de huidige node,<br /> of erf de cultuur over van de ouder nodes. Zal ook van toepassing <br />
@@ -87,6 +85,7 @@
<key alias="macroInsert">Macro invoegen</key>
<key alias="pictureInsert">Afbeelding invoegen</key>
<key alias="relations">Relaties wijzigen</key>
<key alias="returnToList">Terug naar overzicht</key>
<key alias="save">Opslaan</key>
<key alias="saveAndPublish">Opslaan en publiceren</key>
<key alias="saveToPublish">Opslaan en verzenden voor goedkeuring</key>
@@ -117,6 +116,7 @@
<key alias="validDocTypesNote">Alleen alternatieve types geldig voor de huidige locatie worden weergegeven.</key>
</area>
<area alias="content">
<key alias="isPublished" version="7.2">Is gepubliceerd</key>
<key alias="about">Over deze pagina</key>
<key alias="alias">Alternatieve link</key>
<key alias="alternativeTextHelp">(hoe zou jij de foto beschrijven via de telefoon)</key>
@@ -133,6 +133,7 @@
<key alias="itemChanged">Dit item is gewijzigd na publicatie</key>
<key alias="itemNotPublished">Dit item is niet gepubliceerd</key>
<key alias="lastPublished">Laatst gepubliceerd op</key>
<key alias="listViewNoItems" version="7.1.5">Nog geen items om weer te geven.</key>
<key alias="mediatype">Mediatype</key>
<key alias="mediaLinks">Link naar media item(s)</key>
<key alias="membergroup">Ledengroep</key>
@@ -152,11 +153,12 @@
<key alias="sortHelp">Om nodes te sorteren, sleep de nodes of klik op één van de kolomtitels. Je kan meerdere nodes tegelijk selecteren door de "shift"- of "control"knop in te drukken tijdens het selecteren.</key>
<key alias="statistics">Statistieken</key>
<key alias="titleOptional">Titel (optioneel)</key>
<key alias="altTextOptional">Alternatieve tekst (optioneel)</key>
<key alias="type">Type</key>
<key alias="unPublish">Depubliceren</key>
<key alias="updateDate">Laatst gewijzigd</key>
<key alias="updateDateDesc" version="7.0">Date/time this document was edited</key>
<key alias="uploadClear">Bestand verwijderen</key>
<key alias="uploadClear">Bestand(en) verwijderen</key>
<key alias="urls">Link naar het document</key>
<key alias="memberof">Lid van groep(en)</key>
<key alias="notmemberof">Geen lid van groep(en)</key>
@@ -170,7 +172,7 @@
</area>
<area alias="create">
<key alias="chooseNode">Waar wil je de nieuwe %0% aanmaken?</key>
<key alias="createUnder">Aanmaken op</key>
<key alias="createUnder">Aanmaken onder</key>
<key alias="updateData">Kies een type en een titel</key>
<key alias="noDocumentTypes" version="7.0"><![CDATA[Er zijn geen toegestane documenttypes beschikbaar. Je moet deze inschakelen in de Instellingen sectie onder <strong>"Documenttypes"</ strong>.]]></key>
@@ -241,16 +243,20 @@
<key alias="displayName">Cultuurnaam</key>
</area>
<area alias="placeholders">
<key alias="username">Type je gebruikersnaam</key>
<key alias="password">Type je wachtwoord</key>
<key alias="username">Typ je gebruikersnaam</key>
<key alias="password">Typ je wachtwoord</key>
<key alias="nameentity">Benoem de %0%...</key>
<key alias="entername">Type een naam...</key>
<key alias="search">Type om te zoeken...</key>
<key alias="filter">Type om te filteren...</key>
<key alias="entername">Typ een naam...</key>
<key alias="search">Typ om te zoeken...</key>
<key alias="filter">Typ om te filteren...</key>
<key alias="enterTags">Typ om tags toe te voegen (druk op enter na elke tag)...</key>
</area>
<area alias="editcontenttype">
<key alias="allowAtRoot" version="7.2">Toestaan op root-niveau</key>
<key alias="allowAtRootDesc" version="7.2">Wanneer aangevinkt dan mag dit document type aangemaakt worden op het root-niveau van content of media trees.</key>
<key alias="allowedchildnodetypes">Toegelaten subnodetypes</key>
<key alias="contenttypecompositions">Document Type Composities</key>
<key alias="create">Nieuw</key>
<key alias="deletetab">Tab verwijderen</key>
<key alias="description">Omschrijving</key>
@@ -258,6 +264,11 @@
<key alias="tab">Tab</key>
<key alias="thumbnail">Miniatuur</key>
<key alias="hasListView">Lijstweergave inschakelen</key>
<key alias="hasListViewDesc" version="7.2">Stelt het content item in zodat een sorteer- en zoekbare lijstweergave van onderliggende nodes wordt getoond. De onderliggende nodes worden niet in de tree getoond</key>
<key alias="currentListView" version="7.2">Huidige lijstweergave</key>
<key alias="currentListViewDesc" version="7.2">De actieve data type in lijstweergave</key>
<key alias="createListView" version="7.2">Maak een aangepaste lijstweergave</key>
<key alias="removeListView" version="7.2">Verwijder aangepaste lijstweergave</key>
</area>
<area alias="editdatatype">
<key alias="addPrevalue">Prevalue toevoegen</key>
@@ -501,7 +512,6 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="timeout">Sessie is verlopen</key>
<key alias="bottomText"><![CDATA[<p style="text-align:right;">&copy; 2001 - %0% <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">umbraco.com</a></p>]]></key>
<key alias="topText">Welkom bij Umbraco, geef je gebruikersnaam en wachtwoord op in de onderstaande velden:</key>
</area>
<area alias="main">
<key alias="dashboard">Dashboard</key>
@@ -662,6 +672,9 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="newWindow">Open in nieuw venster</key>
<key alias="removeLink">Verwijder link</key>
</area>
<area alias="imagecropper">
<key alias="reset">Reset</key>
</area>
<area alias="rollback">
<key alias="currentVersion">Huidige versie</key>
<key alias="diffHelp"><![CDATA[Hier worden de verschillen getoond tussen de huidige en de geselecteerde versie<br /><del>Rode</del> tekst wordt niet getoond in de geselecteerde versie , <ins>groen betekent toegevoegd</ins>]]></key>
@@ -690,6 +703,8 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="contour" version="4.0">Umbraco Contour</key>
<key alias="help" version="7.0">Help</key>
<key alias="forms">Formulieren</key>
<key alias="analytics">Analytics</key>
</area>
<area alias="settings">
<key alias="defaulttemplate">Standaard template</key>
@@ -708,6 +723,8 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="contentTypeUses">Dit inhoudstype gebruikt</key>
<key alias="asAContentMasterType">als basis inhoudstype. Tabs van basis inhoudstypes worden niet getoond en kunnen alleen worden aangepast op het basis inhoudstype zelf</key>
<key alias="noPropertiesDefinedOnTab">Geen eigenschappen gedefinieerd op dit tabblad. Klik op de link "voeg een nieuwe eigenschap" aan de bovenkant om een nieuwe eigenschap te creëren.</key>
<key alias="masterDocumentType">Master Document Type</key>
<key alias="createMatchingTemplate">Maak een bijbehorend template</key>
</area>
<area alias="sort">
<key alias="sortDone">Sorteren gereed.</key>
@@ -788,36 +805,40 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="quickGuide">Quick Guide voor Umbraco template tags</key>
<key alias="template">Sjabloon</key>
</area>
<area alias="grid">
<key alias="insertControl">Item toevoegen</key>
<key alias="addRows">Een rij aan de lay-out toevoegen</key>
<key alias="addElement"><![CDATA[Klik om te starten op het <i class="icon icon-add blue"></i> teken onderaan en voeg je eerste item toe]]></key>
<area alias="grid">
<key alias="insertControl">Item toevoegen</key>
<key alias="addRows">Een rij aan de lay-out toevoegen</key>
<key alias="addElement"><![CDATA[Klik om te starten op het <i class="icon icon-add blue"></i> teken onderaan en voeg je eerste item toe]]></key>
<key alias="gridLayouts">Grid lay-outs</key>
<key alias="gridLayoutsDetail">Lay-outs zijn het globale werkgebied voor de grid editor. Je hebt meestal maar één of twee verschillende lay-outs nodig</key>
<key alias="addGridLayout">Een grid layout toevoegen</key>
<key alias="addGridLayoutDetail">De lay-out aanpassen door de kolombreedte aan te passen en extra kolommen toe te voegen</key>
<key alias="clickToEmbed">Klik om een item te embedden</key>
<key alias="clickToInsertImage">Klik om een afbeelding in te voegen</key>
<key alias="placeholderImageCaption">Afbeelding ondertitel...</key>
<key alias="placeholderWriteHere">Typ hier......</key>
<key alias="gridLayouts">Grid lay-outs</key>
<key alias="gridLayoutsDetail">Lay-outs zijn het globale werkgebied voor de grid editor. Je hebt meestal maar één of twee verschillende lay-outs nodig</key>
<key alias="addGridLayout">Een grid layout toevoegen</key>
<key alias="addGridLayoutDetail">De lay-out aanpassen door de kolombreedte aan te passen en extra kolommen toe te voegen</key>
<key alias="rowConfigurations">Rijconfiguratie</key>
<key alias="rowConfigurationsDetail">Rijen zijn voorgedefinieerde cellen die horizontaal zijn gerangschikt</key>
<key alias="addRowConfiguration">Een rijconfiguratie toevoegen</key>
<key alias="addRowConfigurationDetail">De rijconfiguratie aanpassen door de breedte van de cel in te stellen en extra cellen toe te voegen</key>
<key alias="rowConfigurations">Rijconfiguratie</key>
<key alias="rowConfigurationsDetail">Rijen zijn voorgedefinieerde cellen die horizontaal zijn gerangschikt</key>
<key alias="addRowConfiguration">Een rijconfiguratie toevoegen</key>
<key alias="addRowConfigurationDetail">De rijconfiguratie aanpassen door de breedte van de cel in te stellen en extra cellen toe te voegen</key>
<key alias="columns">Kolommen</key>
<key alias="columnsDetails">Het totaal aantal gecombineerde kolommen in de grid layout</key>
<key alias="columns">Kolommen</key>
<key alias="columnsDetails">Het totaal aantal gecombineerde kolommen in de grid layout</key>
<key alias="settings">Instellingen</key>
<key alias="settingsDetails">Configureren welke instellingen de editors kunnen aanpassen</key>
<key alias="settings">Instellingen</key>
<key alias="settingsDetails">Configureren welke instellingen de editors kunnen aanpassen</key>
<key alias="styles">Styles</key>
<key alias="stylesDetails">Configureren welke stijlen de editors kunnen aanpassen</key>
<key alias="styles">Styles</key>
<key alias="stylesDetails">Configureren welke stijlen de editors kunnen aanpassen</key>
<key alias="settingDialogDetails">De instellingen worden enkel bewaard indien de ingevoerde Json geldig is</key>
<key alias="settingDialogDetails">De instellingen worden enkel bewaard indien de ingevoerde Json geldig is</key>
<key alias="allowAllEditors">Alle editors toelaten</key>
<key alias="allowAllRowConfigurations">Alle rijconfiguraties toelaten</key>
</area>
<key alias="allowAllEditors">Alle editors toelaten</key>
<key alias="allowAllRowConfigurations">Alle rijconfiguraties toelaten</key>
</area>
<area alias="templateEditor">
<key alias="alternativeField">Alternatief veld</key>
<key alias="alternativeText">Alternatieve tekst</key>
+72 -46
View File
@@ -40,11 +40,11 @@
<area alias="assignDomain">
<key alias="addNew">Lägg till nytt domännamn</key>
<key alias="domain">Domännamn</key>
<key alias="domainCreated">Har skapat domännamnet {0}</key>
<key alias="domainDeleted">Har tagit bort domännamnet {0}</key>
<key alias="domainExists">Domänen {0} är redan tillagd</key>
<key alias="domainCreated">Har skapat domännamnet %0%</key>
<key alias="domainDeleted">Har tagit bort domännamnet %0%</key>
<key alias="domainExists">Domänen %0% är redan tillagd</key>
<key alias="domainHelp">t.ex.: dittdomannamn.se, www.dittdomannamn.se</key>
<key alias="domainUpdated">Domännamnet {0} har uppdaterats</key>
<key alias="domainUpdated">Domännamnet %0% har uppdaterats</key>
<key alias="duplicateDomain">Domänen är redan tilldelad</key>
<key alias="inherit">Ärv</key>
<key alias="invalidDomain">Ogiltigt domännamn</key>
@@ -145,7 +145,7 @@
<key alias="nodeName">Sidnamn</key>
<key alias="notmemberof">Ej medlem av grupp(er)</key>
<key alias="otherElements">Egenskaper</key>
<key alias="parentNotPublished">Detta dokument är publicerat men syns inte eftersom den överordnade sidan {0} inte är publicerad</key>
<key alias="parentNotPublished">Detta dokument är publicerat men syns inte eftersom den överordnade sidan %0% inte är publicerad</key>
<key alias="parentNotPublishedAnomaly">Oops: detta dokument är publicerat men finns inte i cacheminnet (internt fel)</key>
<key alias="publish">Publicera</key>
<key alias="publishStatus">Publiceringsstatus</key>
@@ -166,7 +166,7 @@
<key alias="urls">Länk till dokument</key>
</area>
<area alias="create">
<key alias="chooseNode">Var vill du skapa den nya {0}</key>
<key alias="chooseNode">Var vill du skapa den nya %0%</key>
<key alias="createUnder">Skapa innehåll under</key>
<key alias="noDocumentTypes"><![CDATA[Det finns inga giltiga dokumenttyper tillgängliga. Du måste aktivera dessa under sektionen inställningar och under <strong>"dokumenttyper"</strong>.]]></key>
<key alias="noMediaTypes"><![CDATA[Det finns inga giltiga mediatyper tillgängliga. Du måste aktivera dessa under sektionen inställningar och under <strong>"mediatyper"</strong>.]]></key>
@@ -187,7 +187,7 @@
<key alias="closeThisWindow">Stäng fönstret</key>
<key alias="confirmdelete">Är du säker på att du vill ta bort</key>
<key alias="confirmdisable">Är du säker på att du vill avaktivera</key>
<key alias="confirmEmptyTrashcan">Kryssa i denna ruta för att bekräfta att {0} objekt tas bort</key>
<key alias="confirmEmptyTrashcan">Kryssa i denna ruta för att bekräfta att %0% objekt tas bort</key>
<key alias="confirmlogout">Är du säker?</key>
<key alias="confirmSure">Är du säker?</key>
<key alias="cut">Klipp ut</key>
@@ -228,7 +228,7 @@
<key alias="viewCacheItem">Se cachat objekt</key>
</area>
<area alias="dictionaryItem">
<key alias="description">Redigera de olika översättningarna för ordboksinlägget {0} nedan. Du kan lägga till ytterligare språk under 'språk' i menyn till vänster.</key>
<key alias="description">Redigera de olika översättningarna för ordboksinlägget %0% nedan. Du kan lägga till ytterligare språk under 'språk' i menyn till vänster.</key>
<key alias="displayName">Språknamn</key>
</area>
<area alias="editcontenttype">
@@ -265,15 +265,15 @@
<area alias="errorHandling">
<key alias="errorButDataWasSaved">Informationen har sparats, men innan du kan publicera denna sida måste du åtgärda följande fel:</key>
<key alias="errorChangingProviderPassword">Det går inte att byta lösenord i den medlemshanterare du har valt (EnablePasswordRetrieval måste vara satt till 'true').</key>
<key alias="errorExistsWithoutTab">{0} redan finns</key>
<key alias="errorExistsWithoutTab">%0% redan finns</key>
<key alias="errorHeader">Följande fel inträffade:</key>
<key alias="errorHeaderWithoutTab">Följande fel inträffade:</key>
<key alias="errorInPasswordFormat">Lösenordet måste bestå av minst {0} tecken varav minst {1} är icke-alfanumeriska tecken (t.ex. %, #, !, @).</key>
<key alias="errorIntegerWithoutTab">{0} måste vara ett heltal</key>
<key alias="errorMandatory">{0} under {1} är ett obligatoriskt fält</key>
<key alias="errorMandatoryWithoutTab">{0} är ett obligatoriskt fält</key>
<key alias="errorRegExp">{0} under {1} har ett felaktigt format</key>
<key alias="errorRegExpWithoutTab">{0} har ett felaktigt format</key>
<key alias="errorInPasswordFormat">Lösenordet måste bestå av minst %0% tecken varav minst %1% är icke-alfanumeriska tecken (t.ex. %, #, !, @).</key>
<key alias="errorIntegerWithoutTab">%0% måste vara ett heltal</key>
<key alias="errorMandatory">%0% under %1% är ett obligatoriskt fält</key>
<key alias="errorMandatoryWithoutTab">%0% är ett obligatoriskt fält</key>
<key alias="errorRegExp">%0% under %1% har ett felaktigt format</key>
<key alias="errorRegExpWithoutTab">%0% har ett felaktigt format</key>
</area>
<area alias="errors">
<key alias="codemirroriewarning">Även om CodeMirror är aktiverad i konfigurationen, så är den avaktiverad i Internet Explorer på grund av att den inte är tillräckligt stabil</key>
@@ -408,12 +408,12 @@
<key alias="databaseErrorWebConfig">Kunde inte spara filen web.config. Vänligen ändra databasanslutnings-inställningarna manuellt.</key>
<key alias="databaseFound">Din databas har lokaliserats och är identifierad som</key>
<key alias="databaseHeader">Databaskonfiguration</key>
<key alias="databaseInstall"><![CDATA[För att installera Umbraco {0} databasen, tryck på knappen <strong>installera</strong>]]></key>
<key alias="databaseInstallDone"><![CDATA[Nu har Umbraco {0} kopierats till din databas. Tryck <strong>Nästa</strong> för att fortsätta.]]></key>
<key alias="databaseInstall"><![CDATA[För att installera Umbraco %0% databasen, tryck på knappen <strong>installera</strong>]]></key>
<key alias="databaseInstallDone"><![CDATA[Nu har Umbraco %0% kopierats till din databas. Tryck <strong>Nästa</strong> för att fortsätta.]]></key>
<key alias="databaseNotFound"><![CDATA[<p>Databasen kunde inte hittas! Kontrollera att informationen i databasanslutnings-inställningarna i filen "web.config" är rätt.</p> <p>För att fortsätta måste du redigera filen "web.config" (du kan använda Visual Studio eller din favorit text-redigerare), bläddra till slutet, lägg till databasanslutnings-inställningarna för din databas i fältet som heter "umbracoDbDSN" och spara filen. </p> <p> Klicka på <strong>Försök igen</strong> knappen när du är klar.<br />><a href="http://our.umbraco.org/documentation/Using-Umbraco/Config-files/webconfig7" target="_blank"> Mer information om att redigera web.config hittar du här.</a></p>]]></key>
<key alias="databaseText"><![CDATA[För att avsluta det här steget måste du veta lite information om din databasserver ("connection string").<br /> Eventuellt kan du behöva kontakta ditt webb-hotell. Om du installerar på en lokal maskin eller server kan du få informationen från din systemadministratör.]]></key>
<key alias="databaseUpgrade"><![CDATA[<p> Tryck <strong>Uppgradera</strong> knappen för att uppgradera din databas till Umbraco {0}</p> <p> Du behöver inte vara orolig. Inget innehåll kommer att raderas och efteråt kommer allt att fungera som vanligt! </p>]]></key>
<key alias="databaseUpgradeDone"><![CDATA[Din databas har nu uppgraderats till den senaste versionen {0}.<br />Tryck <strong>Nästa</strong> för att fortsätta.]]></key>
<key alias="databaseUpgrade"><![CDATA[<p> Tryck <strong>Uppgradera</strong> knappen för att uppgradera din databas till Umbraco %0%</p> <p> Du behöver inte vara orolig. Inget innehåll kommer att raderas och efteråt kommer allt att fungera som vanligt! </p>]]></key>
<key alias="databaseUpgradeDone"><![CDATA[Din databas har nu uppgraderats till den senaste versionen %0%.<br />Tryck <strong>Nästa</strong> för att fortsätta.]]></key>
<key alias="databaseUpToDate"><![CDATA[Din nuvarande databas behöver inte uppgraderas! Klicka <strong>Nästa</strong> för att fortsätta med konfigurationsguiden]]></key>
<key alias="defaultUserChangePass"><![CDATA[<strong>Lösenordet på standardanvändaren måste bytas!</strong>]]></key>
<key alias="defaultUserDisabled"><![CDATA[<strong>Standardanvändaren har avaktiverats eller har inte åtkomst till Umbraco!</strong></p><p>Du behöver inte göra något ytterligare här. Klicka <b>Next</b> för att fortsätta.]]></key>
@@ -455,13 +455,13 @@
<key alias="thankYou">Tack för att du valde Umbraco</key>
<key alias="theEndBrowseSite"><![CDATA[<h3>Besök din nya webbplats</h3> Du installerade Runway, så varför inte se hur din nya webbplats ser ut.]]></key>
<key alias="theEndFurtherHelp"><![CDATA[<h3>Ytterligare hjälp och information</h3> Få hjälp från våra prisbelönta community, bläddra i dokumentationen eller titta på några gratis videor om hur man bygger en enkel webbplats, hur du använder paket eller en snabbguide till Umbracos terminologi]]></key>
<key alias="theEndHeader">Umbraco {0} är installerat och klart för användning</key>
<key alias="theEndInstallFailed"><![CDATA[För att avsluta installationen måste du manuellt redigera <strong>/web.config</strong> filen och ändra AppSettingsnyckeln <strong>UmbracoConfigurationStatus</strong> på slutet till <strong>{0}</strong>]]></key>
<key alias="theEndHeader">Umbraco %0% är installerat och klart för användning</key>
<key alias="theEndInstallFailed"><![CDATA[För att avsluta installationen måste du manuellt redigera <strong>/web.config</strong> filen och ändra AppSettingsnyckeln <strong>UmbracoConfigurationStatus</strong> på slutet till <strong>%0%</strong>]]></key>
<key alias="theEndInstallSuccess"><![CDATA[Du kan <strong>börja omedelbart</strong> genom att klicka på "Starta Umbraco"-knappen nedan. <br />Om du är en <strong>ny Umbraco användare</strong>kan du hitta massor av resurser på våra kom igång sidor.]]></key>
<key alias="theEndOpenUmbraco"><![CDATA[<h3>Starta Umbraco</h3> För att administrera din webbplats öppnar du bara Umbraco back office och börjar lägga till innehåll, uppdatera mallar och stilmallar eller lägga till nya funktioner.]]></key>
<key alias="Unavailable">Anslutningen till databasen misslyckades.</key>
<key alias="watch">Se</key>
<key alias="welcomeIntro"><![CDATA[Den här guiden kommer att guida dig genom processen med att konfigurera <strong>Umbraco {0}</strong> antingen för en ny installation eller en uppgradering från version 3.0. <br /><br /> Tryck på <strong>"next"</strong> för att börja.]]></key>
<key alias="welcomeIntro"><![CDATA[Den här guiden kommer att guida dig genom processen med att konfigurera <strong>Umbraco %0%</strong> antingen för en ny installation eller en uppgradering från version 3.0. <br /><br /> Tryck på <strong>"next"</strong> för att börja.]]></key>
<key alias="Version3">Umbraco Version 3</key>
<key alias="Version4">Umbraco Version 4</key>
</area>
@@ -474,7 +474,7 @@
<key alias="renewSession">Förnya nu för att spara ditt arbete</key>
</area>
<area alias="login">
<key alias="bottomText"><![CDATA[<p style="text-align:right;">&copy; 2001 - {0} <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">umbraco.com</a></p> ]]></key>
<key alias="bottomText"><![CDATA[<p style="text-align:right;">&copy; 2001 - %0% <br /><a href="http://umbraco.com" style="text-decoration: none" target="_blank">umbraco.com</a></p> ]]></key>
<key alias="greeting0">Happy super Sunday</key>
<key alias="greeting1">Happy manic Monday</key>
<key alias="greeting2">Happy tremendous Tuesday</key>
@@ -496,10 +496,10 @@
</area>
<area alias="moveOrCopy">
<key alias="choose">Välj sida ovan...</key>
<key alias="copyDone">{0} har kopierats till {1}</key>
<key alias="copyTo">Ange mål att kopiera sidan {0} till nedan</key>
<key alias="moveDone">{0} har flyttats till {1}</key>
<key alias="moveTo">Ange vart sidan {0} skall flyttas till nedan</key>
<key alias="copyDone">%0% har kopierats till %1%</key>
<key alias="copyTo">Ange mål att kopiera sidan %0% till nedan</key>
<key alias="moveDone">%0% har flyttats till %1%</key>
<key alias="moveTo">Ange vart sidan %0% skall flyttas till nedan</key>
<key alias="nodeSelected">är nu roten för ditt nya innehåll. Klicka 'ok' nedan.</key>
<key alias="noNodeSelected">Du har inte valt någon sida än. Välj en sida i listan ovan och klicka sedan 'fortsätt'.</key>
<key alias="notAllowedAtRoot">Aktuell nod får inte existera i roten</key>
@@ -550,7 +550,7 @@
<area alias="placeholders">
<key alias="entername">Fyll i ett namn...</key>
<key alias="filter">Skriv för att filtrera...</key>
<key alias="nameentity">Namnge {0}...</key>
<key alias="nameentity">Namnge %0%...</key>
<key alias="password">Fyll i ditt lösenord</key>
<key alias="search">Skriv för att söka...</key>
<key alias="username">Fyll i ditt lösenord</key>
@@ -562,8 +562,8 @@
<key alias="paErrorPage">Sida med felmeddelande</key>
<key alias="paErrorPageHelp">Används när en användare är inloggad, men saknar rättigheter att se sidan</key>
<key alias="paHowWould">Välj hur du vill lösenordsskydda sidan</key>
<key alias="paIsProtected">{0} är nu lösenordsskyddad</key>
<key alias="paIsRemoved">Lösenordsskyddet är nu borttaget på {0}</key>
<key alias="paIsProtected">%0% är nu lösenordsskyddad</key>
<key alias="paIsRemoved">Lösenordsskyddet är nu borttaget på %0%</key>
<key alias="paLoginPage">Inloggningssida</key>
<key alias="paLoginPageHelp">Välj sidan med inloggningsformuläret</key>
<key alias="paRemoveProtection">Ta bort lösenordsskydd</key>
@@ -574,17 +574,17 @@
<key alias="paSimpleHelp">Välj detta alternativ om du vill skydda sidan med ett enkelt användarnamn och lösenord. Alla loggar då in med samma inloggningsuppgifter.</key>
</area>
<area alias="publish">
<key alias="contentPublishedFailedAwaitingRelease"> {0} kunde inte publiceras på grund av dess tidsinställda publicering.</key>
<key alias="contentPublishedFailedByEvent">{0} kunde inte publiceras på grund av att ett tredjepartstillägg avbröt publiceringen.</key>
<key alias="contentPublishedFailedByParent">{0} kan inte publiceras, på grund av att överordnad nod inte är publicerad.</key>
<key alias="contentPublishedFailedInvalid">{0} kunde inte publiceras på grund av följande orsaker: {1} passerade inte valideringen.</key>
<key alias="contentPublishedFailedAwaitingRelease"> %0% kunde inte publiceras på grund av dess tidsinställda publicering.</key>
<key alias="contentPublishedFailedByEvent">%0% kunde inte publiceras på grund av att ett tredjepartstillägg avbröt publiceringen.</key>
<key alias="contentPublishedFailedByParent">%0% kan inte publiceras, på grund av att överordnad nod inte är publicerad.</key>
<key alias="contentPublishedFailedInvalid">%0% kunde inte publiceras på grund av följande orsaker: %1% passerade inte valideringen.</key>
<key alias="includeUnpublished">Inkludera opublicerade undersidor</key>
<key alias="inProgress">Publicering pågår - vänligen vänta...</key>
<key alias="inProgressCounter">{0} av {1} sidor har publicerats...</key>
<key alias="nodePublish">{0} har publicerats</key>
<key alias="nodePublishAll">{0} och underliggande sidor har publicerats</key>
<key alias="publishAll">Publicera {0} och alla dess underordnade sidor</key>
<key alias="publishHelp"><![CDATA[Klicka på <em>ok</em> för att publicera <strong>{0}</strong>. Därmed blir innehållet publikt.<br/><br /> Du kan publicera denna sida och alla dess undersidor genom att kryssa i <em>publicera alla undersidor</em>. ]]></key>
<key alias="inProgressCounter">%0% av %1% sidor har publicerats...</key>
<key alias="nodePublish">%0% har publicerats</key>
<key alias="nodePublishAll">%0% och underliggande sidor har publicerats</key>
<key alias="publishAll">Publicera %0% och alla dess underordnade sidor</key>
<key alias="publishHelp"><![CDATA[Klicka på <em>ok</em> för att publicera <strong>%0%</strong>. Därmed blir innehållet publikt.<br/><br /> Du kan publicera denna sida och alla dess undersidor genom att kryssa i <em>publicera alla undersidor</em>. ]]></key>
</area>
<area alias="relatedlinks">
<key alias="addExternal">Lägg till extern länk</key>
@@ -654,12 +654,12 @@
<key alias="contentPublishedFailedByEvent">Publiceringen avbröts av ett tredjepartstillägg</key>
<key alias="contentTypeDublicatePropertyType">Egenskapstyp finns redan</key>
<key alias="contentTypePropertyTypeCreated">Egenskapstyp skapad</key>
<key alias="contentTypePropertyTypeCreatedText"><![CDATA[Namn: {0} <br /> Datatyp: {1}]]></key>
<key alias="contentTypePropertyTypeCreatedText"><![CDATA[Namn: %0% <br /> Datatyp: %1%]]></key>
<key alias="contentTypePropertyTypeDeleted">Egenskapstypen har tagits bort</key>
<key alias="contentTypeSavedHeader">Innehållstypen har sparats</key>
<key alias="contentTypeTabCreated">Ny flik skapad</key>
<key alias="contentTypeTabDeleted">Fliken har tagits bort</key>
<key alias="contentTypeTabDeletedText">Fliken med id: {0} har tagits bort</key>
<key alias="contentTypeTabDeletedText">Fliken med id: %0% har tagits bort</key>
<key alias="contentUnpublished">Innehållet är avpublicerat</key>
<key alias="cssErrorHeader">Stilmallen kunde inte sparas</key>
<key alias="cssSavedHeader">Stilmallen sparades</key>
@@ -724,6 +724,32 @@
<key alias="quickGuide">Snabbguide för taggar i Umbracos sidmallar</key>
<key alias="template">Sidmall</key>
</area>
<area alias="grid">
<key alias="insertControl">Lägg till</key>
<key alias="addRows">Lägg till rad</key>
<key alias="addElement"><![CDATA[Klicka på <i class="icon icon-add blue"></i> nedan för att lägga till ditt första element]]></key>
<key alias="clickToEmbed">Klicka för att lägga in</key>
<key alias="clickToInsertImage">Klicka för att lägga till bild</key>
<key alias="placeholderImageCaption">Bildtext...</key>
<key alias="placeholderWriteHere">Skriv här...</key>
<key alias="gridLayouts">Rutnätslayouter</key>
<key alias="gridLayoutsDetail">Layouter är arbetsytan för rutnätet, oftast så behöver du bara en eller två layouter</key>
<key alias="addGridLayout">Lägg till layout</key>
<key alias="addGridLayoutDetail">Redigera layouten genom att sätta kolumnbredd och lägg till fler sektioner</key>
<key alias="rowConfigurations">Radkonfigureringar</key>
<key alias="rowConfigurationsDetail">Rader är fördefinierade celler, arrangerade horisontellt</key>
<key alias="addRowConfiguration">Lägg till radkonfiguration</key>
<key alias="addRowConfigurationDetail">Justera rad genom att ställa in cellbredder och lägga till ytterligare celler</key>
<key alias="columns">Kolumner</key>
<key alias="columnsDetails">Sammanlagda antalet kolumner i rutnätslayout</key>
<key alias="settings">Inställningar</key>
<key alias="settingsDetails">Konfigurera vilka inställningar redaktörer kan ändra</key>
<key alias="styles">Stilar</key>
<key alias="stylesDetails">Konfigurera vilken styling redaktörer kan ändra</key>
<key alias="settingDialogDetails">Inställningarna kommer bara att sparas om det inmatade json-konfigurationen är giltig</key>
<key alias="allowAllEditors">Tillåt alla editors</key>
<key alias="allowAllRowConfigurations">Tillåt alla rad- konfigurationer</key>
</area>
<area alias="templateEditor">
<key alias="alternativeField">Alternativt fält</key>
<key alias="alternativeText">Alternativ text</key>
@@ -764,13 +790,13 @@
<key alias="DownloadXmlDTD">Ladda hem DTD för XML</key>
<key alias="fields">Fält</key>
<key alias="includeSubpages">Inkludera undersidor</key>
<key alias="mailBody">Hej {0}. Detta är ett automatisk mail skickat for att informera dig om att det finns en översättningsförfrågan på dokument '%1' till '{5}' skickad av {2}. För att redigere, besök http://{3}/translation/details.aspx?id={4}. För att få en översikt över dina översättningsuppgigter loggar du in i Umbraco på: http://{3}</key>
<key alias="mailSubject">[{0}] Översättningsuppgit för {1}</key>
<key alias="mailBody">Hej %0%. Detta är ett automatisk mail skickat for att informera dig om att det finns en översättningsförfrågan på dokument '%1%' till '%5%' skickad av %2%. För att redigera, besök http://%3%/translation/details.aspx?id=%4%. För att få en översikt över dina översättningsuppgigter loggar du in i Umbraco på: http://%3%</key>
<key alias="mailSubject">[%0%] Översättningsuppgift för %1%</key>
<key alias="noTranslators">Hittade inga användare som är översättare. Vänligen skapa en användare som är översättare innan du börjar skicka innehåll för översättning</key>
<key alias="ownedTasks">Arbetsuppgifter som du har skapat</key>
<key alias="ownedTasksHelp"><![CDATA[Listan nedan visar sidor <strong>som du har skapat</strong>. För att se en detaljvy med kommentarer, klicka på "Detaljer" eller på sidans namn. Du kan också ladda ned sidan i XML-format genom att klicka på länken "Ladda ned XML".<br/> För att markera ett översättningsjobb som avslutat, gå till detaljvyn och klicka på knappen "Stäng".]]></key>
<key alias="pageHasBeenSendToTranslation">Sidan {0} har skickats för översättning</key>
<key alias="sendToTranslate">Skicka sidan {0} för översättning</key>
<key alias="pageHasBeenSendToTranslation">Sidan %0% har skickats för översättning</key>
<key alias="sendToTranslate">Skicka sidan %0% för översättning</key>
<key alias="taskAssignedBy">Tilldelat av</key>
<key alias="taskOpened">Arbetsuppgift öppnad</key>
<key alias="totalWords">Totalt antal ord</key>
@@ -815,7 +841,7 @@
</area>
<area alias="update">
<key alias="updateAvailable">Ny uppdatering tillgänglig</key>
<key alias="updateDownloadText">{0} är klart, klicka här för att ladda ner</key>
<key alias="updateDownloadText">%0% är klart, klicka här för att ladda ner</key>
<key alias="updateNoServer">Ingen kontakt med server</key>
<key alias="updateNoServerError">Fel vid kontroll av uppdatering. Se trace-stack för mer information.</key>
</area>
@@ -65,7 +65,7 @@ namespace Umbraco.Web.Editors
[HttpGet]
public JsonNetResult LocalizedText(string culture = null)
{
var cultureInfo = culture == null
var cultureInfo = string.IsNullOrWhiteSpace(culture)
//if the user is logged in, get their culture, otherwise default to 'en'
? User.Identity.IsAuthenticated && User.Identity is UmbracoBackOfficeIdentity
? Security.CurrentUser.GetUserCulture(Services.TextService)
@@ -23,9 +23,6 @@ namespace Umbraco.Web.PropertyEditors
private IDictionary<string, object> _defaultPreVals;
/// <summary>
/// Overridden because we ONLY support Date (no time) format and we don't have pre-values in the db.
/// </summary>
public override IDictionary<string, object> DefaultPreValues
{
get { return _defaultPreVals; }
@@ -60,5 +57,16 @@ namespace Umbraco.Web.PropertyEditors
}
}
protected override PreValueEditor CreatePreValueEditor()
{
return new DatePreValueEditor();
}
internal class DatePreValueEditor : PreValueEditor
{
[PreValueField("format", "Date format", "textstring", Description = "If left empty then the format is YYYY-MM-DD. (see momentjs.com for supported formats)")]
public string DefaultValue { get; set; }
}
}
}
@@ -106,7 +106,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
else if (_timer == null) // we don't have a timer yet
{
LogHelper.Debug<XmlCacheFilePersister>("Touched, was idle, start and save in {0}ms.");
LogHelper.Debug<XmlCacheFilePersister>("Touched, was idle, start and save in {0}ms.", () => WaitMilliseconds);
_initialTouch = DateTime.Now;
_timer = new Timer(_ => TimerRelease());
_timer.Change(WaitMilliseconds, 0);
@@ -130,7 +130,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
if (runNow)
Run();
//Run();
LogHelper.Warn<XmlCacheFilePersister>("Cannot write now because we are going down, changes may be lost.");
return ret; // this, by default, unless we created a new one
}
+180 -112
View File
@@ -1,47 +1,74 @@
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
namespace Umbraco.Web.Scheduling
{
// exists for logging purposes
internal class BackgroundTaskRunner
{ }
/// <summary>
/// Manages a queue of tasks of type <typeparamref name="T"/> and runs them in the background.
/// </summary>
/// <typeparam name="T">The type of the managed tasks.</typeparam>
/// <remarks>The task runner is web-aware and will ensure that it shuts down correctly when the AppDomain
/// shuts down (ie is unloaded).</remarks>
internal class BackgroundTaskRunner<T> : IBackgroundTaskRunner<T>
internal class BackgroundTaskRunner<T> : BackgroundTaskRunner, IBackgroundTaskRunner<T>
where T : class, IBackgroundTask
{
private readonly string _logPrefix;
private readonly BackgroundTaskRunnerOptions _options;
private readonly BlockingCollection<T> _tasks = new BlockingCollection<T>();
private readonly object _locker = new object();
private readonly ManualResetEventSlim _completedEvent = new ManualResetEventSlim(false);
private BackgroundTaskRunnerAwaiter<T> _awaiter;
// that event is used to stop the pump when it is alive and waiting
// on a latched task - so it waits on the latch, the cancellation token,
// and the completed event
private readonly ManualResetEventSlim _completedEvent = new ManualResetEventSlim(false);
// in various places we are testing these vars outside a lock, so make them volatile
private volatile bool _isRunning; // is running
private volatile bool _isCompleted; // does not accept tasks anymore, may still be running
private Task _runningTask;
private Task _runningTask;
private CancellationTokenSource _tokenSource;
private bool _terminating; // ensures we raise that event only once
private bool _terminated; // remember we've terminated
private TaskCompletionSource<int> _terminatedSource; // awaitable source
internal event TypedEventHandler<BackgroundTaskRunner<T>, TaskEventArgs<T>> TaskError;
internal event TypedEventHandler<BackgroundTaskRunner<T>, TaskEventArgs<T>> TaskStarting;
internal event TypedEventHandler<BackgroundTaskRunner<T>, TaskEventArgs<T>> TaskCompleted;
internal event TypedEventHandler<BackgroundTaskRunner<T>, TaskEventArgs<T>> TaskCancelled;
internal event TypedEventHandler<BackgroundTaskRunner<T>, EventArgs> Completed;
// triggers when the runner stops (but could start again if a task is added to it)
internal event TypedEventHandler<BackgroundTaskRunner<T>, EventArgs> Stopped;
// triggers when the hosting environment requests that the runner terminates
internal event TypedEventHandler<BackgroundTaskRunner<T>, EventArgs> Terminating;
// triggers when the runner terminates (no task can be added, no task is running)
internal event TypedEventHandler<BackgroundTaskRunner<T>, EventArgs> Terminated;
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundTaskRunner{T}"/> class.
/// </summary>
public BackgroundTaskRunner()
: this(new BackgroundTaskRunnerOptions())
: this(typeof (T).FullName, new BackgroundTaskRunnerOptions())
{ }
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundTaskRunner{T}"/> class.
/// </summary>
/// <param name="name">The name of the runner.</param>
public BackgroundTaskRunner(string name)
: this(name, new BackgroundTaskRunnerOptions())
{ }
/// <summary>
@@ -49,9 +76,19 @@ namespace Umbraco.Web.Scheduling
/// </summary>
/// <param name="options">The set of options.</param>
public BackgroundTaskRunner(BackgroundTaskRunnerOptions options)
: this(typeof (T).FullName, options)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="BackgroundTaskRunner{T}"/> class with a set of options.
/// </summary>
/// <param name="name">The name of the runner.</param>
/// <param name="options">The set of options.</param>
public BackgroundTaskRunner(string name, BackgroundTaskRunnerOptions options)
{
if (options == null) throw new ArgumentNullException("options");
_options = options;
_logPrefix = "[" + name + "] ";
HostingEnvironment.RegisterObject(this);
@@ -84,7 +121,7 @@ namespace Umbraco.Web.Scheduling
}
/// <summary>
/// Gets an awaiter used to await the running Threading.Task.
/// Gets the running task as an immutable object.
/// </summary>
/// <exception cref="InvalidOperationException">There is no running task.</exception>
/// <remarks>
@@ -92,32 +129,54 @@ namespace Umbraco.Web.Scheduling
/// a background task is added to the queue. Unless the KeepAlive option is true, there
/// will be no running task when the queue is empty.
/// </remarks>
public ThreadingTaskAwaiter CurrentThreadingTask
public ThreadingTaskImmutable CurrentThreadingTask
{
get
{
if (_runningTask == null)
throw new InvalidOperationException("There is no current Threading.Task.");
return new ThreadingTaskAwaiter(_runningTask);
lock (_locker)
{
if (_runningTask == null)
throw new InvalidOperationException("There is no current Threading.Task.");
return new ThreadingTaskImmutable(_runningTask);
}
}
}
/// <summary>
/// Gets an awaiter used to await the BackgroundTaskRunner running operation
/// Gets an awaitable used to await the runner running operation.
/// </summary>
/// <returns>An awaiter for the BackgroundTaskRunner running operation</returns>
/// <remarks>
/// <para>This is used to wait until the background task runner is no longer running (IsRunning == false)
/// </para>
/// <para> So long as we have a method called GetAwaiter() that returns an instance of INotifyCompletion
/// we can await anything. In this case we are awaiting with a custom BackgroundTaskRunnerAwaiter
/// which waits for the Completed event to be raised.
/// ref: http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115642.aspx
/// </para>
/// </remarks>
public BackgroundTaskRunnerAwaiter<T> GetAwaiter()
/// <returns>An awaitable instance.</returns>
/// <remarks>Used to wait until the runner is no longer running (IsRunning == false),
/// though the runner could be started again afterwards by adding tasks to it.</remarks>
public ThreadingTaskImmutable StoppedAwaitable
{
return _awaiter ?? (_awaiter = new BackgroundTaskRunnerAwaiter<T>(this));
get
{
lock (_locker)
{
var task = _runningTask ?? Task.FromResult(0);
return new ThreadingTaskImmutable(task);
}
}
}
/// <summary>
/// Gets an awaitable used to await the runner.
/// </summary>
/// <returns>An awaitable instance.</returns>
/// <remarks>Used to wait until the runner is terminated.</remarks>
public ThreadingTaskImmutable TerminatedAwaitable
{
get
{
lock (_locker)
{
if (_terminatedSource == null && _terminated == false)
_terminatedSource = new TaskCompletionSource<int>();
var task = _terminatedSource == null ? Task.FromResult(0) : _terminatedSource.Task;
return new ThreadingTaskImmutable(task);
}
}
}
/// <summary>
@@ -133,7 +192,7 @@ namespace Umbraco.Web.Scheduling
throw new InvalidOperationException("The task runner has completed.");
// add task
LogHelper.Debug<BackgroundTaskRunner<T>>("Task added {0}", task.GetType);
LogHelper.Debug<BackgroundTaskRunner>(_logPrefix + "Task added {0}", task.GetType);
_tasks.Add(task);
// start
@@ -154,7 +213,7 @@ namespace Umbraco.Web.Scheduling
if (_isCompleted) return false;
// add task
LogHelper.Debug<BackgroundTaskRunner<T>>("Task added {0}", task.GetType);
LogHelper.Debug<BackgroundTaskRunner>(_logPrefix + "Task added {0}", task.GetType);
_tasks.Add(task);
// start
@@ -195,7 +254,7 @@ namespace Umbraco.Web.Scheduling
// create a new token source since this is a new process
_tokenSource = new CancellationTokenSource();
_runningTask = PumpIBackgroundTasks(Task.Factory, _tokenSource.Token);
LogHelper.Debug<BackgroundTaskRunner<T>>("Starting");
LogHelper.Debug<BackgroundTaskRunner>(_logPrefix + "Starting");
}
/// <summary>
@@ -221,7 +280,7 @@ namespace Umbraco.Web.Scheduling
if (force)
{
// we must bring everything down, now
Thread.Sleep(100); // give time to CompleAdding()
Thread.Sleep(100); // give time to CompleteAdding()
lock (_locker)
{
// was CompleteAdding() enough?
@@ -236,7 +295,6 @@ namespace Umbraco.Web.Scheduling
// tasks in the queue will be executed...
if (wait == false) return;
_runningTask.Wait(); // wait for whatever is running to end...
}
/// <summary>
@@ -256,27 +314,35 @@ namespace Umbraco.Web.Scheduling
// because the pump does not lock, there's a race condition,
// the pump may stop and then we still have tasks to process,
// and then we must restart the pump - lock to avoid race cond
var onStopped = false;
lock (_locker)
{
if (token.IsCancellationRequested || _tasks.Count == 0)
{
LogHelper.Debug<BackgroundTaskRunner<T>>("_isRunning = false");
LogHelper.Debug<BackgroundTaskRunner>(_logPrefix + "Stopping");
_isRunning = false; // done
if (_options.PreserveRunningTask == false)
_runningTask = null;
//raise event
OnCompleted();
return;
// stopped
_isRunning = false;
onStopped = true;
}
}
if (onStopped)
{
OnEvent(Stopped, "Stopped");
return;
}
// if _runningTask is taskSource.Task then we must keep continuing it,
// not starting a new taskSource, else _runningTask would complete and
// something may be waiting on it
//PumpIBackgroundTasks(factory, token); // restart
// ReSharper disable once MethodSupportsCancellation // always run
// ReSharper disable MethodSupportsCancellation // always run
t.ContinueWithTask(_ => PumpIBackgroundTasks(factory, token)); // restart
// ReSharper restore MethodSupportsCancellation
});
Action<Task> pump = null;
@@ -288,7 +354,7 @@ namespace Umbraco.Web.Scheduling
if (task != null && task.IsFaulted)
{
var exception = task.Exception;
LogHelper.Error<BackgroundTaskRunner<T>>("Task runner exception.", exception);
LogHelper.Error<BackgroundTaskRunner>(_logPrefix + "Task runner exception.", exception);
}
// is it ok to run?
@@ -298,6 +364,7 @@ namespace Umbraco.Web.Scheduling
// the blocking MoveNext will end if token is cancelled or collection is completed
T bgTask;
var hasBgTask = _options.KeepAlive
// ReSharper disable once PossibleNullReferenceException
? (bgTask = enumerator.MoveNext() ? enumerator.Current : null) != null // blocking
: _tasks.TryTake(out bgTask); // non-blocking
@@ -343,7 +410,7 @@ namespace Umbraco.Web.Scheduling
return taskSourceContinuing;
}
private bool TaskSourceCanceled(TaskCompletionSource<object> taskSource, CancellationToken token)
private static bool TaskSourceCanceled(TaskCompletionSource<object> taskSource, CancellationToken token)
{
if (token.IsCancellationRequested)
{
@@ -353,7 +420,7 @@ namespace Umbraco.Web.Scheduling
return false;
}
private void TaskSourceCompleted(TaskCompletionSource<object> taskSource, CancellationToken token)
private static void TaskSourceCompleted(TaskCompletionSource<object> taskSource, CancellationToken token)
{
if (token.IsCancellationRequested)
taskSource.SetCanceled();
@@ -394,85 +461,55 @@ namespace Umbraco.Web.Scheduling
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner<T>>("Task has failed.", ex);
LogHelper.Error<BackgroundTaskRunner>(_logPrefix + "Task has failed", ex);
}
}
#region Events
private void OnEvent(TypedEventHandler<BackgroundTaskRunner<T>, EventArgs> handler, string name)
{
if (handler == null) return;
OnEvent(handler, name, EventArgs.Empty);
}
private void OnEvent<TArgs>(TypedEventHandler<BackgroundTaskRunner<T>, TArgs> handler, string name, TArgs e)
{
if (handler == null) return;
try
{
handler(this, e);
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner>(_logPrefix + name + " exception occurred", ex);
}
}
protected virtual void OnTaskError(TaskEventArgs<T> e)
{
var handler = TaskError;
if (handler != null) handler(this, e);
OnEvent(TaskError, "TaskError", e);
}
protected virtual void OnTaskStarting(TaskEventArgs<T> e)
{
var handler = TaskStarting;
if (handler != null)
{
try
{
handler(this, e);
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner<T>>("TaskStarting exception occurred", ex);
}
}
OnEvent(TaskStarting, "TaskStarting", e);
}
protected virtual void OnTaskCompleted(TaskEventArgs<T> e)
{
var handler = TaskCompleted;
if (handler != null)
{
try
{
handler(this, e);
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner<T>>("TaskCompleted exception occurred", ex);
}
}
OnEvent(TaskCompleted, "TaskCompleted", e);
}
protected virtual void OnTaskCancelled(TaskEventArgs<T> e)
{
var handler = TaskCancelled;
if (handler != null)
{
try
{
handler(this, e);
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner<T>>("TaskCancelled exception occurred", ex);
}
}
OnEvent(TaskCancelled, "TaskCancelled", e);
//dispose it
e.Task.Dispose();
}
protected virtual void OnCompleted()
{
var handler = Completed;
if (handler != null)
{
try
{
handler(this, EventArgs.Empty);
}
catch (Exception ex)
{
LogHelper.Error<BackgroundTaskRunner<T>>("OnCompleted exception occurred", ex);
}
}
}
#endregion
#region IDisposable
@@ -482,7 +519,7 @@ namespace Umbraco.Web.Scheduling
~BackgroundTaskRunner()
{
this.Dispose(false);
Dispose(false);
}
public void Dispose()
@@ -493,7 +530,7 @@ namespace Umbraco.Web.Scheduling
protected virtual void Dispose(bool disposing)
{
if (this.IsDisposed || disposing == false)
if (IsDisposed || disposing == false)
return;
lock (_disposalLocker)
@@ -525,28 +562,41 @@ namespace Umbraco.Web.Scheduling
/// </remarks>
public void Stop(bool immediate)
{
// the first time the hosting environment requests that the runner terminates,
// raise the Terminating event - that could be used to prevent any process that
// would expect the runner to be available from starting.
var onTerminating = false;
lock (_locker)
{
if (_terminating == false)
{
_terminating = true;
LogHelper.Info<BackgroundTaskRunner>(_logPrefix + "Terminating" + (immediate ? " (immediate)" : ""));
onTerminating = true;
}
}
if (onTerminating)
OnEvent(Terminating, "Terminating");
if (immediate == false)
{
// The Stop method is first called with the immediate parameter set to false. The object can either complete
// processing, call the UnregisterObject method, and then return or it can return immediately and complete
// processing asynchronously before calling the UnregisterObject method.
LogHelper.Debug<BackgroundTaskRunner<T>>("Shutting down, waiting for tasks to complete.");
LogHelper.Info<BackgroundTaskRunner>(_logPrefix + "Waiting for tasks to complete");
Shutdown(false, false); // do not accept any more tasks, flush the queue, do not wait
// raise the completed event only after the running task has completed
// and there's no more task running
lock (_locker)
{
if (_runningTask != null)
_runningTask.ContinueWith(_ =>
{
HostingEnvironment.UnregisterObject(this);
LogHelper.Info<BackgroundTaskRunner<T>>("Down, tasks completed.");
});
_runningTask.ContinueWith(_ => Terminate(false));
else
{
HostingEnvironment.UnregisterObject(this);
LogHelper.Info<BackgroundTaskRunner<T>>("Down, tasks completed.");
}
Terminate(false);
}
}
else
@@ -556,13 +606,31 @@ namespace Umbraco.Web.Scheduling
// immediate parameter is true, the registered object must call the UnregisterObject method before returning;
// otherwise, its registration will be removed by the application manager.
LogHelper.Info<BackgroundTaskRunner<T>>("Shutting down immediately.");
LogHelper.Info<BackgroundTaskRunner>(_logPrefix + "Cancelling tasks");
Shutdown(true, true); // cancel all tasks, wait for the current one to end
HostingEnvironment.UnregisterObject(this);
LogHelper.Info<BackgroundTaskRunner<T>>("Down.");
Terminate(true);
}
}
private void Terminate(bool immediate)
{
// signal the environment we have terminated
// log
// raise the Terminated event
// complete the awaitable completion source, if any
HostingEnvironment.UnregisterObject(this);
LogHelper.Info<BackgroundTaskRunner>(_logPrefix + "Tasks " + (immediate ? "cancelled" : "completed") + ", terminated");
OnEvent(Terminated, "Terminated");
TaskCompletionSource<int> terminatedSource;
lock (_locker)
{
_terminated = true;
terminatedSource = _terminatedSource;
}
if (terminatedSource != null)
terminatedSource.SetResult(0);
}
}
}
@@ -1,78 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Umbraco.Core.Logging;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// Custom awaiter used to await when the BackgroundTaskRunner is completed (IsRunning == false)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <remarks>
/// This custom awaiter simply uses a TaskCompletionSource to set the result when the Completed event of the
/// BackgroundTaskRunner executes.
/// A custom awaiter requires implementing INotifyCompletion as well as IsCompleted, OnCompleted and GetResult
/// see: http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115642.aspx
/// </remarks>
internal class BackgroundTaskRunnerAwaiter<T> : INotifyCompletion where T : class, IBackgroundTask
{
private readonly BackgroundTaskRunner<T> _runner;
private readonly TaskCompletionSource<int> _tcs;
private readonly TaskAwaiter<int> _awaiter;
public BackgroundTaskRunnerAwaiter(BackgroundTaskRunner<T> runner)
{
if (runner == null) throw new ArgumentNullException("runner");
_runner = runner;
_tcs = new TaskCompletionSource<int>();
_awaiter = _tcs.Task.GetAwaiter();
if (_runner.IsRunning)
{
_runner.Completed += (s, e) =>
{
LogHelper.Debug<BackgroundTaskRunnerAwaiter<T>>("Setting result");
_tcs.SetResult(0);
};
}
else
{
//not running, just set the result
_tcs.SetResult(0);
}
}
public BackgroundTaskRunnerAwaiter<T> GetAwaiter()
{
return this;
}
/// <summary>
/// This is completed when the runner is finished running
/// </summary>
public bool IsCompleted
{
get
{
LogHelper.Debug<BackgroundTaskRunnerAwaiter<T>>("IsCompleted :: " + _tcs.Task.IsCompleted + ", " + (_runner.IsRunning == false));
//Need to check if the task is completed because it might already be done on the ctor and the runner never runs
return _tcs.Task.IsCompleted || _runner.IsRunning == false;
}
}
public void OnCompleted(Action continuation)
{
_awaiter.OnCompleted(continuation);
}
public void GetResult()
{
_awaiter.GetResult();
}
}
}
@@ -63,7 +63,7 @@ namespace Umbraco.Web.Scheduling
public bool IsLatched
{
get { return _latch != null; }
get { return _latch != null && _latch.IsSet == false; }
}
public virtual bool RunsOnShutdown
@@ -21,6 +21,7 @@ namespace Umbraco.Web.Scheduling
/// <summary>
/// Gets a value indicating whether the task is latched.
/// </summary>
/// <remarks>Should return false as soon as the condition is met.</remarks>
bool IsLatched { get; }
/// <summary>
+3 -3
View File
@@ -48,9 +48,9 @@ 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
_publishingRunner = new BackgroundTaskRunner<IBackgroundTask>();
_tasksRunner = new BackgroundTaskRunner<IBackgroundTask>();
_scrubberRunner = new BackgroundTaskRunner<IBackgroundTask>();
_publishingRunner = new BackgroundTaskRunner<IBackgroundTask>("ScheduledPublishing");
_tasksRunner = new BackgroundTaskRunner<IBackgroundTask>("ScheduledTasks");
_scrubberRunner = new BackgroundTaskRunner<IBackgroundTask>("LogScrubber");
var settings = UmbracoConfig.For.UmbracoSettings();
@@ -1,42 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// This is used to return an awaitable instance from a Task without actually returning the
/// underlying Task instance since it shouldn't be mutable.
/// </summary>
internal class ThreadingTaskAwaiter
{
private readonly Task _task;
public ThreadingTaskAwaiter(Task task)
{
if (task == null) throw new ArgumentNullException("task");
_task = task;
}
/// <summary>
/// With a GetAwaiter declared it means that this instance can be awaited on with the await keyword
/// </summary>
/// <returns></returns>
public TaskAwaiter GetAwaiter()
{
return _task.GetAwaiter();
}
/// <summary>
/// Gets the status of the running task.
/// </summary>
/// <exception cref="InvalidOperationException">There is no running task.</exception>
/// <remarks>Unless the AutoStart option is true, there will be no running task until
/// a background task is added to the queue. Unless the KeepAlive option is true, there
/// will be no running task when the queue is empty.</remarks>
public TaskStatus Status
{
get { return _task.Status; }
}
}
}
@@ -0,0 +1,44 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Umbraco.Web.Scheduling
{
/// <summary>
/// Wraps a Task within an object that gives access to its GetAwaiter method and Status
/// property while ensuring that it cannot be modified in any way.
/// </summary>
internal class ThreadingTaskImmutable
{
private readonly Task _task;
/// <summary>
/// Initializes a new instance of the <see cref="ThreadingTaskImmutable"/> class with a Task.
/// </summary>
/// <param name="task">The task.</param>
public ThreadingTaskImmutable(Task task)
{
if (task == null)
throw new ArgumentNullException("task");
_task = task;
}
/// <summary>
/// Gets an awaiter used to await the task.
/// </summary>
/// <returns>An awaiter instance.</returns>
public TaskAwaiter GetAwaiter()
{
return _task.GetAwaiter();
}
/// <summary>
/// Gets the TaskStatus of the task.
/// </summary>
/// <returns>The current TaskStatus of the task.</returns>
public TaskStatus Status
{
get { return _task.Status; }
}
}
}
+1 -2
View File
@@ -274,7 +274,7 @@
<Compile Include="Mvc\DisableClientCacheAttribute.cs" />
<Compile Include="Mvc\MvcVersionCheck.cs" />
<Compile Include="Mvc\ReflectedFixedRazorViewEngine.cs" />
<Compile Include="Scheduling\ThreadingTaskAwaiter.cs" />
<Compile Include="Scheduling\ThreadingTaskImmutable.cs" />
<Compile Include="Scheduling\BackgroundTaskRunner.cs" />
<Compile Include="BatchedServerMessenger.cs" />
<Compile Include="CacheHelperExtensions.cs" />
@@ -499,7 +499,6 @@
<Compile Include="Mvc\UmbracoVirtualNodeRouteHandler.cs" />
<Compile Include="Routing\CustomRouteUrlProvider.cs" />
<Compile Include="Routing\UrlProviderExtensions.cs" />
<Compile Include="Scheduling\BackgroundTaskRunnerAwaiter.cs" />
<Compile Include="Scheduling\BackgroundTaskRunnerOptions.cs" />
<Compile Include="Scheduling\DelayedRecurringTaskBase.cs" />
<Compile Include="Scheduling\IBackgroundTaskRunner.cs" />
+23 -17
View File
@@ -409,15 +409,23 @@ namespace Umbraco.Web
return false;
}
private bool _notConfiguredReported;
// ensures Umbraco is configured
// if not, redirect to install and return false
// if yes, return true
private static bool EnsureIsConfigured(HttpContextBase httpContext, Uri uri)
private bool EnsureIsConfigured(HttpContextBase httpContext, Uri uri)
{
if (ApplicationContext.Current.IsConfigured)
return true;
LogHelper.Warn<UmbracoModule>("Umbraco is not configured");
if (_notConfiguredReported)
{
// 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
_notConfiguredReported = true;
LogHelper.Warn<UmbracoModule>("Umbraco is not configured");
}
var installPath = UriUtility.ToAbsolute(SystemDirectories.Install);
var installUrl = string.Format("{0}/?redir=true&url={1}", installPath, HttpUtility.UrlEncode(uri.ToString()));
@@ -588,6 +596,19 @@ namespace Umbraco.Web
var httpContext = ((HttpApplication)sender).Context;
LogHelper.Debug<UmbracoModule>("Begin request: {0}.", () => httpContext.Request.Url);
BeginRequest(new HttpContextWrapper(httpContext));
//disable asp.net headers (security)
try
{
httpContext.Response.Headers.Remove("Server");
//this doesn't normally work since IIS sets it but we'll keep it here anyways.
httpContext.Response.Headers.Remove("X-Powered-By");
}
catch (PlatformNotSupportedException ex)
{
// can't remove headers this way on IIS6 or cassini.
}
};
app.AuthenticateRequest += AuthenticateRequest;
@@ -612,21 +633,6 @@ namespace Umbraco.Web
DisposeHttpContextItems(httpContext);
};
//disable asp.net headers (security)
app.PreSendRequestHeaders += (sender, args) =>
{
var httpContext = ((HttpApplication)sender).Context;
try
{
httpContext.Response.Headers.Remove("Server");
//this doesn't normally work since IIS sets it but we'll keep it here anyways.
httpContext.Response.Headers.Remove("X-Powered-By");
}
catch (PlatformNotSupportedException ex)
{
// can't remove headers this way on IIS6 or cassini.
}
};
}
public void Dispose()
+75 -23
View File
@@ -40,26 +40,66 @@ namespace umbraco
private content()
{
if (SyncToXmlFile == false) return;
// 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>(new BackgroundTaskRunnerOptions
if (SyncToXmlFile)
{
LongRunning = true,
KeepAlive = true
});
// 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();
// create (and add to runner)
_persisterTask = new XmlCacheFilePersister(runner, this);
// and 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
});
InitializeFileLock();
// 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);
}
// initialize content - populate the cache
using (var safeXml = GetSafeXmlWriter(false))
{
bool registerXmlChange;
// if we don't use the file then LoadXmlLocked will not even
// read from the file and will go straight to database
LoadXmlLocked(safeXml, out registerXmlChange);
// if we use the file and registerXmlChange is true this will
// write to file, else it will not
safeXml.Commit(registerXmlChange);
}
}
@@ -1007,8 +1047,8 @@ order by umbracoNode.level, umbracoNode.sortOrder";
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 NOT be accessing
// the file from now one - release the lock
// 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
@@ -1056,12 +1096,14 @@ order by umbracoNode.level, umbracoNode.sortOrder";
internal void SaveXmlToFile()
{
LogHelper.Info<content>("Save Xml to file...");
EnsureFileLock();
var xml = _xmlContent; // capture (atomic + volatile), immutable anyway
try
{
var xml = _xmlContent; // capture (atomic + volatile), immutable anyway
if (xml == null) return;
EnsureFileLock();
// delete existing file, if any
DeleteXmlFile();
@@ -1079,7 +1121,7 @@ order by umbracoNode.level, umbracoNode.sortOrder";
fs.Write(bytes, 0, bytes.Length);
}
LogHelper.Debug<content>("Saved Xml to file.");
LogHelper.Info<content>("Saved Xml to file.");
}
catch (Exception e)
{
@@ -1094,12 +1136,14 @@ order by umbracoNode.level, umbracoNode.sortOrder";
internal async System.Threading.Tasks.Task SaveXmlToFileAsync()
{
LogHelper.Info<content>("Save Xml to file...");
EnsureFileLock();
var xml = _xmlContent; // capture (atomic + volatile), immutable anyway
try
{
var xml = _xmlContent; // capture (atomic + volatile), immutable anyway
if (xml == null) return;
EnsureFileLock();
// delete existing file, if any
DeleteXmlFile();
@@ -1117,7 +1161,7 @@ order by umbracoNode.level, umbracoNode.sortOrder";
await fs.WriteAsync(bytes, 0, bytes.Length);
}
LogHelper.Debug<content>("Saved Xml to file.");
LogHelper.Info<content>("Saved Xml to file.");
}
catch (Exception e)
{
@@ -1160,19 +1204,27 @@ order by umbracoNode.level, umbracoNode.sortOrder";
private XmlDocument LoadXmlFromFile()
{
LogHelper.Info<content>("Load Xml from file...");
EnsureFileLock();
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))
{
xml.Load(fs);
}
_lastFileRead = DateTime.UtcNow;
LogHelper.Info<content>("Successfully loaded Xml from file.");
LogHelper.Info<content>("Loaded Xml from file.");
return xml;
}
catch (FileNotFoundException)
{
LogHelper.Warn<content>("Failed to load Xml, file does not exist.");
return null;
}
catch (Exception e)
{
LogHelper.Error<content>("Failed to load Xml from file.", e);
@@ -8,6 +8,7 @@ using umbraco.cms.businesslogic.language;
using umbraco.cms.businesslogic.property;
using umbraco.cms.businesslogic.task;
using umbraco.cms.businesslogic.web;
using Umbraco.Core;
using Umbraco.Core.IO;
namespace umbraco.cms.businesslogic.translation
@@ -95,10 +96,14 @@ namespace umbraco.cms.businesslogic.translation
var props = d.GenericProperties;
foreach (Property p in props)
{
if (p.Value.GetType() == "".GetType())
var asString = p.Value as string;
if (asString != null)
{
if (p.Value.ToString().Trim() != "")
words += CountWordsInString(p.Value.ToString());
var trimmed = asString.Trim();
if (trimmed.IsNullOrWhiteSpace() == false)
{
words += CountWordsInString(trimmed);
}
}
}
+14 -11
View File
@@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using Umbraco.Core.Cache;
@@ -121,27 +122,29 @@ namespace umbraco.cms.businesslogic.web
using (var re = File.OpenText(IOHelper.MapPath(String.Format("{0}/{1}.css", SystemDirectories.Css, this.Text))))
{
string input = null;
_content = string.Empty;
// NH: Updates the reader to support properties
var readingProperties = false;
// stop concatenating strings, use string builders!
var contentBuilder = new StringBuilder();
var propertiesBuilder = new StringBuilder();
while ((input = re.ReadLine()) != null && true)
string input;
while ((input = re.ReadLine()) != null)
{
if (input.Contains("EDITOR PROPERTIES"))
{
readingProperties = true;
}
else
if (readingProperties)
{
propertiesContent += input.Replace("\n", "") + "\n";
}
else
{
_content += input.Replace("\n", "") + "\n";
}
{
var builder = readingProperties ? propertiesBuilder : contentBuilder;
builder.Append(input.Replace("\n", ""));
builder.Append("\n");
}
}
_content = contentBuilder.ToString();
propertiesContent = propertiesBuilder.ToString();
}
// update properties