Compare commits

...

87 Commits

Author SHA1 Message Date
Per Ploug 19e4fe5059 Grid labels header colors for delete and move actions 2015-03-19 14:33:12 +01:00
Sebastiaan Janssen 311d503d8b Updates CDF dependency to latest version 2015-03-19 12:54:00 +01:00
Per Ploug f0fc3313e4 Tweaked borders a tiny bit 2015-03-19 12:46:29 +01:00
Per Ploug dc7cb08824 Styling for grid editors + row labels 2015-03-19 12:29:05 +01:00
Stephan 5ddcf42bc3 U4-3753 - keep some things internal 2015-03-19 09:37:39 +01:00
Shannon af112f6b20 Adds nicer grid editor labels on hover 2015-03-19 17:08:14 +11:00
Shannon cbef51eaf0 Fixes: U4-6294 Feature Request 7.2.1 Grid - UX - labeling the grid editors - but will see if we can do a better job of the labeling 2015-03-19 16:10:46 +11:00
Shannon 85ba41dd06 Fixes: U4-5435 Dropdown list multiple, publish keys - single item 2015-03-19 15:50:58 +11:00
Shannon e5cf7b089c oops, added timeouts where they cannot exist for request cache, fixes build 2015-03-19 14:47:08 +11:00
Shannon 5fae61b196 Merge branch 'mattbrailsford-7.2.1-configurablemacrogrideditor' into dev-v7 2015-03-19 14:34:00 +11:00
Shannon db5c204d8c Merge branch '7.2.1-configurablemacrogrideditor' of https://github.com/mattbrailsford/Umbraco-CMS into mattbrailsford-7.2.1-configurablemacrogrideditor 2015-03-19 14:33:36 +11:00
Shannon b9082cf390 Fixes our usages of GetCacheItem so that there is a timeout, this is needed otherwise memory won't be given back unless memory gets too high where in fact for most of these we want to give it back as soon as possible if it isn't being used. 2015-03-19 14:00:48 +11:00
Shannon 9469b0b844 Fixes: U4-6402 Grid config file should be merged with package.manifests & U4-6427 Grid config file has caching problems because it's downloaded as a static file
This also fixes up the OutputCaching params on the BackOfficeController since OutputCache is bypassed when an action requires authentication, so now we manually do some caching when not in debug mode for authorized actions (of course auth happens before any cached response can occur). This also fixes up the static caching that was happening with the ManifestBuilder so now when that is not in use it gives back it's memory. This also fixes up any client side caching that was happening on BackOfficeController - before we were allowing client cache to happen for a few actions on that controller which is incorrect, we need to disable all client cache for all actions on that controller.
2015-03-19 13:53:15 +11:00
Shannon 5737cdfbe2 Fixes the way HttpResponseMessages are created - they should always be created with Request.CreateReponse! 2015-03-19 12:03:55 +11:00
Shannon 1a082464e9 Fixes: U4-5127 AssetService does not support virtual paths (~/) - and fixes up some formatting 2015-03-19 11:44:37 +11:00
Shannon e004ad31b5 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7
Conflicts:
	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js
2015-03-19 11:36:05 +11:00
Shannon 6cf25f0043 Fixes: U4-6428 Grid configuration does not support virtual paths - and therefore is not compatible with virtual directories 2015-03-19 11:35:04 +11:00
Shannon Deminick c0f50a4c1b Merge pull request #656 from ProfessorFarnsworth/U4-6419
fixed issue U4-6419 by removing that character
2015-03-19 10:05:25 +11:00
Sebastiaan Janssen 00fbc6dfcb Revert "Fixes broken bower jquery reference"
This reverts commit c2cfd26ee6.
2015-03-18 13:05:21 +01:00
Per Ploug 3a9ad91f8f Adds better error handling to missing grid editors 2015-03-18 11:45:22 +01:00
Per Ploug c2cfd26ee6 Fixes broken bower jquery reference 2015-03-18 11:45:04 +01:00
Per Ploug c5f26c7c15 Fixes: U4-6331 The grid tools overlay disable the links/buttons on smaller screens 2015-03-18 11:44:49 +01:00
Helmuth Bederna f742c0a958 fixed issue U4-6419 by removing that character 2015-03-18 10:41:15 +01:00
Shannon Deminick 8cefe22bee Merge pull request #649 from protherj/grid-rendering-quote-fix
Changed the surrounding quotes for Grid custom settings/styles to be double quotes
2015-03-18 19:55:32 +11:00
Shannon fad3599f50 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-03-18 19:52:56 +11:00
Shannon 37b219f6e1 Fixes: U4-4942 Widget disappears in RTE when there is a space in the macro alias 2015-03-18 19:52:45 +11:00
Sebastiaan Janssen 4e9667a067 Don't forcibly remove System.Web.Helpers.dll during upgrades in case people have it already and packages rely on it 2015-03-18 09:49:18 +01:00
Shannon Deminick 300a46f1b2 Merge pull request #613 from gildebrand/patch-4
parseMacroSyntax accepts other characters than a-z
2015-03-18 19:17:33 +11:00
Shannon 9aa5df43bf Fixes: U4-5956 Feature request: Enhance the preview mode with labels clearly explaining the current view 2015-03-18 19:14:47 +11:00
Shannon 02b0e4dd6a Fixes: U4-6380 ClientDependency Bug on server - Can't Access Backoffice 2015-03-18 19:06:41 +11:00
Shannon 9c1d3fabb8 Fixes: U4-6418 Loading back office with ClientDependency does not respect the configured value for the compositeFileHandlerPath 2015-03-18 19:04:49 +11:00
Shannon e9e84a7faa Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-03-18 19:03:57 +11:00
Shannon 137d8553de Fixes: U4-6417 When including a $schema element in package.manifest with a url schema, the regex will incorrectly strip required characters 2015-03-18 19:03:46 +11:00
Sebastiaan Janssen 7210c6cd95 U4-6384 Incorrect Error Message When Uploading Large File
#U4-6384 Fixed Due in version 7.2.3
2015-03-17 17:59:24 +01:00
Stephan 6856f9f341 Merge pull request #654 from umbraco/U4-274-disable-alttemplate
U4-274 - Adds config option to disable alternative templates
2015-03-17 14:32:08 +01:00
Sebastiaan Janssen d24ef3650e U4-274 - Adds config option to disable alternative templates 2015-03-17 13:50:33 +01:00
Shannon 2feef247ed Fixes: U4-3562 Cannot preview when no template is assigned 2015-03-17 19:31:46 +11:00
Stephan 4974e2a2ce U4-6306 - hostname validation issue 2015-03-16 10:52:54 +01:00
Stephan 3eb0dac48a U4-3753 - fix the build 2015-03-16 09:28:24 +01:00
Stephan a44b19da18 U4-6320 - fix default scrubbing interval, set to 4hrs 2015-03-16 09:16:28 +01:00
Shannon Deminick 3ee4234968 Merge pull request #653 from kgiszewski/dev-v7
Fix for U4-6395, U4-6117
2015-03-16 09:50:52 +11:00
kgiszewski f06a468fb4 Fix for U4-6395, U4-6117 2015-03-13 11:40:04 -04:00
Stephan 420c334af4 U4-3753 - refactor
Conflicts:
	src/Umbraco.Web/Umbraco.Web.csproj
2015-03-12 15:03:46 +01:00
Stephan 20b21c86ea U4-3753 - add a way to get the rendering culture of a content 2015-03-12 15:02:18 +01:00
Shannon d2083673be Fixes: U4-3420 Firefox v25.0 - Right click context menu doesn't work - this felt like being a web dev 10 years ago when you had to code totally differently for each browser. Firefox unfortunatley treats 'a' tags differently than anything else and combined with angular saying you can have empty 'href' tags doesn't work. In the end, the fix was easy, just remove the empty (supposed to be supported) href attributes from the 'a' tags in the tree, then the contextmenu starts working in firefox and the events can be canceled as per normal. This commit also moves the angular localization library to use bower. 2015-03-10 13:02:34 +11:00
Shannon fe40b8fe99 ok, fixes the null check for real this time. 2015-03-09 17:53:58 +11:00
Shannon dd477a0cbe whoops, fixes comparison on lazy val 2015-03-09 17:37:25 +11:00
Shannon 2834ccdc2b Fixes: U4-6344 TinyMCE RTE style_formats configuration changes 2015-03-09 17:31:29 +11:00
Shannon b3f6f48834 fixes ugly null check on ContentTypeSort comparison 2015-03-09 16:42:30 +11:00
Shannon 4544520b66 Fixes: U4-6303 SaveAndPublishWithStatus error - updates to latest Examine with the fix. 2015-03-09 15:06:32 +11:00
Shannon 017663833a Missing a fairly critical log entry 2015-03-06 16:54:55 +11:00
Shannon 9a5923771d fixes changing the underscore of a user language to culture. 2015-03-06 16:05:37 +11:00
Shannon 0932c980e9 Fixes: U4-6307 Incorrect culture assigned to user (missing region code) 2015-03-06 16:01:49 +11:00
Shannon 7e9261d4bc Updates member indexer to consume less memory on rebuild, iterates over collection already in memory and just adds nodes as the are iterated, before was re-adding to memory in order to do counts, now we iteratively count. 2015-03-06 13:36:52 +11:00
Shannon 0ce503b369 Fixes: U4-6349 Optimizing indexes says that the index is not optimized 2015-03-06 13:34:09 +11:00
Shannon Deminick 6d5d8fb39c Merge pull request #647 from hAmpzter/U4-6304
U4-6304 ContentTypeBaseRepository.MapContentTypes throws exception with ...
2015-03-05 17:49:48 +11:00
Shannon Deminick 9613772036 Merge pull request #648 from Jeavon/U4-6336
Fixes U4-6336 GetCropUrl - preferFocalPoint is ignored if focal point is...
2015-03-05 17:22:23 +11:00
Shannon Deminick 604779667d Merge pull request #650 from mikkelhm/dev-v7
U4-4080 Missing icons in context menu
2015-03-05 17:18:47 +11:00
Shannon 46212904ef Changes the SetPropertyValueAndDetectChanges to throw an exception if an Enumerable is passed in without using the overload the specifies an IEqualityComparer. Updates all entities that use this method with IEnumerables to specify the correct Equality comparer which now uses the new UnsortedSequenceEqual method. Added unit tests for new EnumerableExtensions and makes the ContainsAll work faster. 2015-03-05 17:08:58 +11:00
Shannon b7bb98d824 fixes localized text request when an invalid cookie is present 2015-03-05 17:04:02 +11:00
Shannon f31d2ca7cf Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-03-05 16:44:34 +11:00
Shannon 88ae95150e Fixes: U4-6342 UmbracoEnsuredPage doesn't work in 7.2.2 2015-03-05 16:36:06 +11:00
Sebastiaan Janssen 97a085d12b Bump version 2015-03-03 20:19:41 +01:00
Sebastiaan Janssen 74d0ec949d Delete bower_components folder before build else missing packages will sometimes not be downloaded 2015-03-03 19:43:19 +01:00
Shannon 9a042fbbdf Removes the assignment of the DefaultPermissions property since this is auto assigned in the ctor (and should be assignable) 2015-03-03 19:40:37 +11:00
Shannon 834b780d8e Removes change tracking for User.DefaultPermissions since it is entirely not necessary (and in fact shouldn't be settable). Also ensures that the cloning of the default permissions is a new list, wasn't doing this before... which might have been the culprit for strange thread issues. 2015-03-03 19:37:13 +11:00
Shannon 8905878a87 Adds some methods to TracksChangesEntityBase to enable/disable change tracking and to reset the change tracking dictionaries, this is then used for DeepCloning so that change tracking is turned off for the cloning process. 2015-03-03 18:33:04 +11:00
Shannon 36bed40db0 typo 2015-03-03 17:37:42 +11:00
Shannon f708a24401 fixes U4-6333, U4-6348 2015-03-03 17:36:53 +11:00
Shannon d32cf12c6f Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-03-02 17:02:35 +11:00
Shannon 100485f748 Fixes: U4-6005 Please add a public constructor to InstallApiController 2015-03-02 17:02:25 +11:00
Shannon 56017a340c makes PropertyEditorResolver faster by not having to re-union every time its accessed 2015-03-02 16:59:40 +11:00
Shannon 21c70462c9 publicizes cache extensions 2015-03-02 16:59:05 +11:00
Sebastiaan Janssen 9b254f8bf9 Previous commit added a IApplicationStartupHandler which broke the count in this unit test 2015-03-01 14:09:35 +01:00
Sebastiaan Janssen 6dd63e8a01 U4-6341 Relate document on copy doesn't create a relation record in umbracoRelations table
#U4-6341 Fixed Due in version 7.2.3
2015-03-01 13:52:02 +01:00
mikkelhm bac940d4ed U4-4080 Missing icons in context menu for relation types, update comments aswell 2015-02-28 22:19:17 +01:00
mikkelhm c1c090faab U4-4080 Missing icons in context menu for relation types 2015-02-28 22:16:07 +01:00
Jason Prothero 32bedfaa6a Changed the surrounding quotes for Grid custom settings/styles to be double quotes 2015-02-27 10:06:45 -08:00
Jeavon Leopold b003c027e5 Removed unused "using" references in cropper extension methods 2015-02-27 14:49:36 +00:00
Jeavon Leopold 0295aaba3b Added test for U4-6336 GetCropUrl - preferFocalPoint is ignored if focal point is in the center 2015-02-27 14:42:00 +00:00
Jeavon Leopold bb04f83a49 Fixes U4-6336 GetCropUrl - preferFocalPoint is ignored if focal point is in the center 2015-02-27 12:15:28 +00:00
hAmpzter 793ba42f9e U4-6304 ContentTypeBaseRepository.MapContentTypes throws exception with SByte / Bool type with Mysql.
Force it with Convert.ToBoolean()
2015-02-27 12:42:55 +01:00
Shannon ded1def8e2 Fixes: U4-6333 - but should fix this better (i.e. centralize the code to clean for xss in JS like we have in c#) 2015-02-27 16:50:33 +11:00
Shannon 5a2a3c3b1e removes duplicate attribute 2015-02-27 01:04:53 +01:00
Sebastiaan Janssen 6f2fa2ed4d Makes sure that the version comment gets cleared when there is no comment. 2015-02-26 12:38:03 +01:00
mattbrailsford 9c8e6ab864 Add "config" support to the macro grid property editor to allow predefining the macro to use 2015-01-26 12:35:01 +00:00
Anton Gildebrand fec70fcf47 parseMacroSyntax accepts other characters than a-z
I had problems with parseMacroSyntax when `syntax` included Swedish characters. Now it works with other characters than a-z as well.
2015-01-05 17:13:50 +01:00
140 changed files with 1741 additions and 1017 deletions
+2 -1
View File
@@ -29,8 +29,9 @@ SET nuGetFolder=%CD%\..\src\packages\
..\src\.nuget\NuGet.exe install ..\src\umbraco.businesslogic\packages.config -OutputDirectory %nuGetFolder%
..\src\.nuget\NuGet.exe install ..\src\Umbraco.Core\packages.config -OutputDirectory %nuGetFolder%
ECHO Removing the belle build folder to make sure everything is clean as a whistle
ECHO Removing the belle build folder and bower_components folder to make sure everything is clean as a whistle
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
RD ..\src\Umbraco.Web.UI.Client\bower_components /Q /S
ECHO Removing existing built files to make sure everything is clean as a whistle
RMDIR /Q /S _BuildOutput
+1 -1
View File
@@ -327,7 +327,7 @@
ReplacementText="$(BUILD_RELEASE)"/>
<FileUpdate Files="..\src\Umbraco.Core\Configuration\UmbracoVersion.cs"
Regex="CurrentComment { get { return &quot;([a-zA-Z]+)?&quot;"
Regex="CurrentComment { get { return &quot;(.+)?&quot;"
ReplacementText="CurrentComment { get { return &quot;$(BUILD_COMMENT)&quot;"/>
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
+1 -1
View File
@@ -28,7 +28,7 @@
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
<dependency id="MySql.Data" version="[6.6.5]" />
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
<dependency id="ClientDependency" version="[1.8.2.1, 2.0.0)" />
<dependency id="ClientDependency" version="[1.8.3.1, 2.0.0)" />
<dependency id="ClientDependency-Mvc" version="[1.8.0, 2.0.0)" />
<dependency id="AutoMapper" version="[3.0.0, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.5, 7.0.0)" />
-1
View File
@@ -63,7 +63,6 @@ if ($project) {
if(Test-Path $umbracoBinFolder\MySql.Data.dll) { Remove-Item $umbracoBinFolder\MySql.Data.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Newtonsoft.Json.dll) { Remove-Item $umbracoBinFolder\Newtonsoft.Json.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Net.Http.Formatting.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Formatting.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\System.Web.Helpers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Http.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Http.WebHost.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.WebHost.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Mvc.dll) { Remove-Item $umbracoBinFolder\System.Web.Mvc.dll -Force -Confirm:$false }
+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.2
7.2.3
@@ -8,7 +8,7 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Extensions for strongly typed access
/// </summary>
internal static class CacheProviderExtensions
public static class CacheProviderExtensions
{
public static T GetCacheItem<T>(this IRuntimeCacheProvider provider,
string cacheKey,
@@ -6,6 +6,8 @@
bool InternalRedirectPreservesTemplate { get; }
bool DisableAlternativeTemplates { get; }
string UrlProviderMode { get; }
}
@@ -16,6 +16,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
get { return (bool) base["internalRedirectPreservesTemplate"]; }
}
[ConfigurationProperty("disableAlternativeTemplates", DefaultValue = "false")]
public bool DisableAlternativeTemplates
{
get { return (bool) base["disableAlternativeTemplates"]; }
}
[ConfigurationProperty("urlProviderMode", DefaultValue = "AutoLegacy")]
public string UrlProviderMode
{
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.2.2");
private static readonly Version Version = new Version("7.2.3");
/// <summary>
/// Gets the current version of Umbraco.
+9 -2
View File
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using AutoMapper;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Mapping;
using Umbraco.Core.Models.PublishedContent;
@@ -21,6 +23,7 @@ using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Core.Publishing;
using Umbraco.Core.Macros;
using Umbraco.Core.Manifest;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
using Umbraco.Core.Strings;
@@ -259,8 +262,12 @@ namespace Umbraco.Core
/// </summary>
protected virtual void InitializeResolvers()
{
PropertyEditorResolver.Current = new PropertyEditorResolver(() => PluginManager.Current.ResolvePropertyEditors());
ParameterEditorResolver.Current = new ParameterEditorResolver(() => PluginManager.Current.ResolveParameterEditors());
var builder = new ManifestBuilder(
ApplicationCache.RuntimeCache,
new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), ApplicationCache.RuntimeCache));
PropertyEditorResolver.Current = new PropertyEditorResolver(() => PluginManager.Current.ResolvePropertyEditors(), builder);
ParameterEditorResolver.Current = new ParameterEditorResolver(() => PluginManager.Current.ResolveParameterEditors(), builder);
//setup the validators resolver with our predefined validators
ValidatorsResolver.Current = new ValidatorsResolver(new[]
+33 -10
View File
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@@ -114,13 +115,7 @@ namespace Umbraco.Core
/// <returns></returns>
public static bool ContainsAll<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> other)
{
var matches = true;
foreach (var i in other)
{
matches = source.Contains(i);
if (!matches) break;
}
return matches;
return other.Except(source).Any() == false;
}
/// <summary>
@@ -132,7 +127,7 @@ namespace Umbraco.Core
/// <returns></returns>
public static bool ContainsAny<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> other)
{
return other.Any(i => source.Contains(i));
return other.Any(source.Contains);
}
/// <summary>
@@ -224,7 +219,7 @@ namespace Umbraco.Core
return sequence.Select(
x =>
{
if (typeof(TActual).IsAssignableFrom(x.GetType()))
if (x is TActual)
{
var casted = x as TActual;
projection.Invoke(casted);
@@ -276,6 +271,34 @@ namespace Umbraco.Core
///<param name="items">The enumerable to search.</param>
///<param name="item">The item to find.</param>
///<returns>The index of the first matching item, or -1 if the item was not found.</returns>
public static int IndexOf<T>(this IEnumerable<T> items, T item) { return items.FindIndex(i => EqualityComparer<T>.Default.Equals(item, i)); }
public static int IndexOf<T>(this IEnumerable<T> items, T item)
{
return items.FindIndex(i => EqualityComparer<T>.Default.Equals(item, i));
}
/// <summary>
/// Determines if 2 lists have equal elements within them regardless of how they are sorted
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="other"></param>
/// <returns></returns>
/// <remarks>
/// The logic for this is taken from:
/// http://stackoverflow.com/questions/4576723/test-whether-two-ienumerablet-have-the-same-values-with-the-same-frequencies
///
/// There's a few answers, this one seems the best for it's simplicity and based on the comment of Eamon
/// </remarks>
public static bool UnsortedSequenceEqual<T>(this IEnumerable<T> source, IEnumerable<T> other)
{
if (source == null && other == null) return true;
if (source == null || other == null) return false;
var list1Groups = source.ToLookup(i => i);
var list2Groups = other.ToLookup(i => i);
return list1Groups.Count == list2Groups.Count
&& list1Groups.All(g => g.Count() == list2Groups[g.Key].Count());
}
}
}
+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=[""']([\w\.]+?)[""'].+?)(?:/>|>.*?</\?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
+59 -38
View File
@@ -1,6 +1,8 @@
using System.Collections.Concurrent;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using Umbraco.Core.Cache;
using Umbraco.Core.IO;
using Umbraco.Core.PropertyEditors;
@@ -11,73 +13,92 @@ namespace Umbraco.Core.Manifest
/// </summary>
internal class ManifestBuilder
{
private readonly IRuntimeCacheProvider _cache;
private readonly ManifestParser _parser;
private static readonly ConcurrentDictionary<string, object> StaticCache = new ConcurrentDictionary<string, object>();
private const string ManifestKey = "manifests";
public ManifestBuilder(IRuntimeCacheProvider cache, ManifestParser parser)
{
_cache = cache;
_parser = parser;
}
private const string GridEditorsKey = "grideditors";
private const string PropertyEditorsKey = "propertyeditors";
private const string ParameterEditorsKey = "parametereditors";
/// <summary>
/// Returns all property editors found in the manfifests
/// Returns all grid editors found in the manfifests
/// </summary>
internal static IEnumerable<PropertyEditor> PropertyEditors
internal IEnumerable<GridEditor> GridEditors
{
get
{
return (IEnumerable<PropertyEditor>) StaticCache.GetOrAdd(
PropertyEditorsKey,
s =>
return _cache.GetCacheItem<IEnumerable<GridEditor>>(
typeof (ManifestBuilder) + GridEditorsKey,
() =>
{
var editors = new List<GridEditor>();
foreach (var manifest in _parser.GetManifests())
{
var editors = new List<PropertyEditor>();
foreach (var manifest in GetManifests())
if (manifest.GridEditors != null)
{
if (manifest.PropertyEditors != null)
{
editors.AddRange(ManifestParser.GetPropertyEditors(manifest.PropertyEditors));
}
editors.AddRange(ManifestParser.GetGridEditors(manifest.GridEditors));
}
return editors;
});
}
return editors;
}, new TimeSpan(0, 10, 0));
}
}
/// <summary>
/// Returns all property editors found in the manfifests
/// </summary>
internal IEnumerable<PropertyEditor> PropertyEditors
{
get
{
return _cache.GetCacheItem<IEnumerable<PropertyEditor>>(
typeof(ManifestBuilder) + PropertyEditorsKey,
() =>
{
var editors = new List<PropertyEditor>();
foreach (var manifest in _parser.GetManifests())
{
if (manifest.PropertyEditors != null)
{
editors.AddRange(ManifestParser.GetPropertyEditors(manifest.PropertyEditors));
}
}
return editors;
}, new TimeSpan(0, 10, 0));
}
}
/// <summary>
/// Returns all parameter editors found in the manfifests and all property editors that are flagged to be parameter editors
/// </summary>
internal static IEnumerable<ParameterEditor> ParameterEditors
internal IEnumerable<ParameterEditor> ParameterEditors
{
get
{
return (IEnumerable<ParameterEditor>)StaticCache.GetOrAdd(
ParameterEditorsKey,
s =>
return _cache.GetCacheItem<IEnumerable<ParameterEditor>>(
typeof (ManifestBuilder) + ParameterEditorsKey,
() =>
{
var editors = new List<ParameterEditor>();
foreach (var manifest in GetManifests())
foreach (var manifest in _parser.GetManifests())
{
if (manifest.ParameterEditors != null)
{
editors.AddRange(ManifestParser.GetParameterEditors(manifest.ParameterEditors));
editors.AddRange(ManifestParser.GetParameterEditors(manifest.ParameterEditors));
}
}
return editors;
});
}, new TimeSpan(0, 10, 0));
}
}
/// <summary>
/// Ensures the manifests are found and loaded into memory
/// </summary>
private static IEnumerable<PackageManifest> GetManifests()
{
return (IEnumerable<PackageManifest>) StaticCache.GetOrAdd(ManifestKey, s =>
{
var parser = new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")));
return parser.GetManifests();
});
}
}
}
+42 -7
View File
@@ -5,6 +5,7 @@ using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Cache;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.PropertyEditors;
@@ -17,15 +18,28 @@ namespace Umbraco.Core.Manifest
internal class ManifestParser
{
private readonly DirectoryInfo _pluginsDir;
private readonly IRuntimeCacheProvider _cache;
//used to strip comments
private static readonly Regex CommentsSurround = new Regex(@"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/", RegexOptions.Compiled);
private static readonly Regex CommentsLine = new Regex(@"//.*?$", RegexOptions.Compiled | RegexOptions.Multiline);
private static readonly Regex CommentsLine = new Regex(@"^\s*//.*?$", RegexOptions.Compiled | RegexOptions.Multiline);
public ManifestParser(DirectoryInfo pluginsDir)
public ManifestParser(DirectoryInfo pluginsDir, IRuntimeCacheProvider cache)
{
if (pluginsDir == null) throw new ArgumentNullException("pluginsDir");
_pluginsDir = pluginsDir;
_cache = cache;
}
/// <summary>
/// Parse the grid editors from the json array
/// </summary>
/// <param name="jsonEditors"></param>
/// <returns></returns>
internal static IEnumerable<GridEditor> GetGridEditors(JArray jsonEditors)
{
return JsonConvert.DeserializeObject<IEnumerable<GridEditor>>(
jsonEditors.ToString());
}
/// <summary>
@@ -57,11 +71,17 @@ namespace Umbraco.Core.Manifest
/// Get all registered manifests
/// </summary>
/// <returns></returns>
/// <remarks>
/// This ensures that we only build and look for all manifests once per Web app (based on the IRuntimeCache)
/// </remarks>
public IEnumerable<PackageManifest> GetManifests()
{
//get all Manifest.js files in the appropriate folders
var manifestFileContents = GetAllManifestFileContents(_pluginsDir);
return CreateManifests(manifestFileContents.ToArray());
return _cache.GetCacheItem<IEnumerable<PackageManifest>>(typeof (ManifestParser) + "GetManifests", () =>
{
//get all Manifest.js files in the appropriate folders
var manifestFileContents = GetAllManifestFileContents(_pluginsDir);
return CreateManifests(manifestFileContents.ToArray());
}, new TimeSpan(0, 10, 0));
}
/// <summary>
@@ -154,6 +174,20 @@ namespace Umbraco.Core.Manifest
throw new FormatException("The manifest is not formatted correctly contains more than one 'propertyEditors' element");
}
//validate the parameterEditors section
var paramEditors = deserialized.Properties().Where(x => x.Name == "parameterEditors").ToArray();
if (paramEditors.Length > 1)
{
throw new FormatException("The manifest is not formatted correctly contains more than one 'parameterEditors' element");
}
//validate the gridEditors section
var gridEditors = deserialized.Properties().Where(x => x.Name == "gridEditors").ToArray();
if (gridEditors.Length > 1)
{
throw new FormatException("The manifest is not formatted correctly contains more than one 'gridEditors' element");
}
var jConfig = init.Any() ? (JArray)deserialized["javascript"] : new JArray();
ReplaceVirtualPaths(jConfig);
@@ -181,7 +215,8 @@ namespace Umbraco.Core.Manifest
JavaScriptInitialize = jConfig,
StylesheetInitialize = cssConfig,
PropertyEditors = propEditors.Any() ? (JArray)deserialized["propertyEditors"] : new JArray(),
ParameterEditors = propEditors.Any() ? (JArray)deserialized["parameterEditors"] : new JArray()
ParameterEditors = propEditors.Any() ? (JArray)deserialized["parameterEditors"] : new JArray(),
GridEditors = propEditors.Any() ? (JArray)deserialized["gridEditors"] : new JArray()
};
result.Add(manifest);
}
@@ -26,5 +26,10 @@ namespace Umbraco.Core.Manifest
/// The json array of parameter editors
/// </summary>
public JArray ParameterEditors { get; set; }
/// <summary>
/// The json array of grid editors
/// </summary>
public JArray GridEditors { get; set; }
}
}
+5 -1
View File
@@ -460,10 +460,14 @@ namespace Umbraco.Core.Models
public override object DeepClone()
{
var clone = (Content)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//need to manually clone this since it's not settable
clone._contentType = (IContentType)ContentType.DeepClone();
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
+5 -1
View File
@@ -90,7 +90,11 @@ namespace Umbraco.Core.Models
{
_allowedTemplates = value;
return _allowedTemplates;
}, _allowedTemplates, AllowedTemplatesSelector);
}, _allowedTemplates, AllowedTemplatesSelector,
//Custom comparer for enumerable
new DelegateEqualityComparer<IEnumerable<ITemplate>>(
(templates, enumerable) => templates.UnsortedSequenceEqual(enumerable),
templates => templates.GetHashCode()));
}
}
+11 -2
View File
@@ -351,7 +351,11 @@ namespace Umbraco.Core.Models
{
_allowedContentTypes = value;
return _allowedContentTypes;
}, _allowedContentTypes, AllowedContentTypesSelector);
}, _allowedContentTypes, AllowedContentTypesSelector,
//Custom comparer for enumerable
new DelegateEqualityComparer<IEnumerable<ContentTypeSort>>(
(sorts, enumerable) => sorts.UnsortedSequenceEqual(enumerable),
sorts => sorts.GetHashCode()));
}
}
@@ -609,13 +613,18 @@ namespace Umbraco.Core.Models
public override object DeepClone()
{
var clone = (ContentTypeBase)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//need to manually wire up the event handlers for the property type collections - we've ensured
// its ignored from the auto-clone process because its return values are unions, not raw and
// we end up with duplicates, see: http://issues.umbraco.org/issue/U4-4842
clone._propertyTypes = (PropertyTypeCollection)_propertyTypes.DeepClone();
clone._propertyTypes.CollectionChanged += clone.PropertyTypesChanged;
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
@@ -255,11 +255,15 @@ namespace Umbraco.Core.Models
public override object DeepClone()
{
var clone = (ContentTypeCompositionBase)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//need to manually assign since this is an internal field and will not be automatically mapped
clone.RemovedContentTypeKeyTracker = new List<int>();
clone._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
+14 -2
View File
@@ -8,10 +8,20 @@ namespace Umbraco.Core.Models
/// </summary>
public class ContentTypeSort : IValueObject, IDeepCloneable
{
[Obsolete("This parameterless constructor should never be used")]
public ContentTypeSort()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
public ContentTypeSort(int id, int sortOrder)
{
Id = new Lazy<int>(() => id);
SortOrder = sortOrder;
}
public ContentTypeSort(Lazy<int> id, int sortOrder, string @alias)
{
Id = id;
@@ -45,7 +55,7 @@ namespace Umbraco.Core.Models
protected bool Equals(ContentTypeSort other)
{
return Id.Value.Equals(other.Id.Value) && string.Equals(Alias, other.Alias);
return Id.Value.Equals(other.Id.Value) && string.Equals(Alias, other.Alias);
}
public override bool Equals(object obj)
@@ -60,7 +70,9 @@ namespace Umbraco.Core.Models
{
unchecked
{
return (Id.GetHashCode()*397) ^ Alias.GetHashCode();
//The hash code will just be the alias if one is assigned, otherwise it will be the hash code of the Id.
//In some cases the alias can be null of the non lazy ctor is used, in that case, the lazy Id will already have a value created.
return Alias != null ? Alias.GetHashCode() : (Id.Value.GetHashCode() * 397);
}
}
+5 -1
View File
@@ -80,7 +80,11 @@ namespace Umbraco.Core.Models
{
_translations = value;
return _translations;
}, _translations, TranslationsSelector);
}, _translations, TranslationsSelector,
//Custom comparer for enumerable
new DelegateEqualityComparer<IEnumerable<IDictionaryTranslation>>(
(enumerable, translations) => enumerable.UnsortedSequenceEqual(translations),
enumerable => enumerable.GetHashCode()));
}
}
@@ -236,9 +236,17 @@ namespace Umbraco.Core.Models.EntityBase
//Memberwise clone on Entity will work since it doesn't have any deep elements
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
var clone = (Entity)MemberwiseClone();
//ensure the clone has it's own dictionaries
clone.ResetChangeTrackingCollections();
//turn off change tracking
clone.DisableChangeTracking();
//Automatically deep clone ref properties that are IDeepCloneable
DeepCloneHelper.DeepCloneRefProperties(this, clone);
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
@@ -18,7 +19,9 @@ namespace Umbraco.Core.Models.EntityBase
public virtual IEnumerable<string> GetDirtyProperties()
{
return _propertyChangedInfo.Where(x => x.Value).Select(x => x.Key);
}
}
private bool _changeTrackingEnabled = true;
/// <summary>
/// Tracks the properties that have changed
@@ -41,6 +44,9 @@ namespace Umbraco.Core.Models.EntityBase
/// <param name="propertyInfo">The property info.</param>
protected virtual void OnPropertyChanged(PropertyInfo propertyInfo)
{
//return if we're not tracking changes
if (_changeTrackingEnabled == false) return;
_propertyChangedInfo[propertyInfo.Name] = true;
if (PropertyChanged != null)
@@ -132,6 +138,22 @@ namespace Umbraco.Core.Models.EntityBase
_propertyChangedInfo = new Dictionary<string, bool>();
}
protected void ResetChangeTrackingCollections()
{
_propertyChangedInfo = new Dictionary<string, bool>();
_lastPropertyChangedInfo = new Dictionary<string, bool>();
}
protected void DisableChangeTracking()
{
_changeTrackingEnabled = false;
}
protected void EnableChangeTracking()
{
_changeTrackingEnabled = true;
}
/// <summary>
/// Used by inheritors to set the value of properties, this will detect if the property value actually changed and if it did
/// it will ensure that the property has a dirty flag set.
@@ -145,17 +167,51 @@ namespace Umbraco.Core.Models.EntityBase
/// save a document type, nearly all properties are flagged as dirty just because we've 'reset' them, but they are all set
/// to the same value, so it's really not dirty.
/// </remarks>
internal virtual bool SetPropertyValueAndDetectChanges<T>(Func<T, T> setValue, T value, PropertyInfo propertySelector)
internal bool SetPropertyValueAndDetectChanges<T>(Func<T, T> setValue, T value, PropertyInfo propertySelector)
{
if ((typeof(T) == typeof(string) == false) && TypeHelper.IsTypeAssignableFrom<IEnumerable>(typeof(T)))
{
throw new InvalidOperationException("This method does not support IEnumerable instances. For IEnumerable instances a manual custom equality check will be required");
}
return SetPropertyValueAndDetectChanges(setValue, value, propertySelector,
new DelegateEqualityComparer<T>(
//Standard Equals comparison
(arg1, arg2) => Equals(arg1, arg2),
arg => arg.GetHashCode()));
}
/// <summary>
/// Used by inheritors to set the value of properties, this will detect if the property value actually changed and if it did
/// it will ensure that the property has a dirty flag set.
/// </summary>
/// <param name="setValue"></param>
/// <param name="value"></param>
/// <param name="propertySelector"></param>
/// <param name="comparer">The equality comparer to use</param>
/// <returns>returns true if the value changed</returns>
/// <remarks>
/// This is required because we don't want a property to show up as "dirty" if the value is the same. For example, when we
/// save a document type, nearly all properties are flagged as dirty just because we've 'reset' them, but they are all set
/// to the same value, so it's really not dirty.
/// </remarks>
internal bool SetPropertyValueAndDetectChanges<T>(Func<T, T> setValue, T value, PropertyInfo propertySelector, IEqualityComparer<T> comparer)
{
var initVal = value;
var newVal = setValue(value);
if (Equals(initVal, newVal) == false)
//don't track changes, just set the value (above)
if (_changeTrackingEnabled == false) return false;
if (comparer.Equals(initVal, newVal) == false)
{
OnPropertyChanged(propertySelector);
return true;
}
return false;
}
}
}
+5 -2
View File
@@ -113,12 +113,15 @@ namespace Umbraco.Core.Models
public override object DeepClone()
{
var clone = (File)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//need to manually assign since they are readonly properties
clone._alias = Alias;
clone._name = Name;
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
+5 -2
View File
@@ -399,14 +399,17 @@ namespace Umbraco.Core.Models
public override object DeepClone()
{
var clone = (Macro)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
clone._addedProperties = new List<string>();
clone._removedProperties = new List<string>();
clone._properties = (MacroPropertyCollection)Properties.DeepClone();
//re-assign the event handler
clone._properties.CollectionChanged += clone.PropertiesChanged;
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
+5 -1
View File
@@ -617,10 +617,14 @@ namespace Umbraco.Core.Models
public override object DeepClone()
{
var clone = (Member)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//need to manually clone this since it's not settable
clone._contentType = (IMemberType)ContentType.DeepClone();
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
+2 -1
View File
@@ -20,7 +20,8 @@ namespace Umbraco.Core.Models.Membership
/// Gets/sets the user type for the user
/// </summary>
IUserType UserType { get; set; }
//TODO: This should be a private set
/// <summary>
/// The default permission set for the user
/// </summary>
+15 -15
View File
@@ -27,7 +27,7 @@ namespace Umbraco.Core.Models.Membership
if (userType == null) throw new ArgumentNullException("userType");
_userType = userType;
_defaultPermissions = _userType.Permissions;
_defaultPermissions = _userType.Permissions == null ? Enumerable.Empty<string>() : new List<string>(_userType.Permissions);
//Groups = new List<object> { userType };
SessionTimeout = 60;
_sectionCollection = new ObservableCollection<string>();
@@ -71,7 +71,9 @@ namespace Umbraco.Core.Models.Membership
private bool _isApproved;
private bool _isLockedOut;
private string _language;
private IEnumerable<string> _defaultPermissions;
private IEnumerable<string> _defaultPermissions;
private bool _defaultToLiveEditing;
private static readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.SessionTimeout);
@@ -86,7 +88,7 @@ namespace Umbraco.Core.Models.Membership
private static readonly PropertyInfo IsLockedOutSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.IsLockedOut);
private static readonly PropertyInfo IsApprovedSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.IsApproved);
private static readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Language);
private static readonly PropertyInfo DefaultPermissionsSelector = ExpressionHelper.GetPropertyInfo<User, IEnumerable<string>>(x => x.DefaultPermissions);
private static readonly PropertyInfo DefaultToLiveEditingSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.DefaultToLiveEditing);
private static readonly PropertyInfo UserTypeSelector = ExpressionHelper.GetPropertyInfo<User, IUserType>(x => x.UserType);
@@ -319,19 +321,13 @@ namespace Umbraco.Core.Models.Membership
}, _language, LanguageSelector);
}
}
//TODO: This should be a private set
[DataMember]
public IEnumerable<string> DefaultPermissions
{
get { return _defaultPermissions; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
_defaultPermissions = value;
return _defaultPermissions;
}, _defaultPermissions, DefaultPermissionsSelector);
}
get { return _defaultPermissions;}
set { _defaultPermissions = value; }
}
[IgnoreDataMember]
@@ -415,15 +411,19 @@ namespace Umbraco.Core.Models.Membership
public override object DeepClone()
{
var clone = (User)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//need to create new collections otherwise they'll get copied by ref
clone._addedSections = new List<string>();
clone._removedSections = new List<string>();
clone._sectionCollection = new ObservableCollection<string>(_sectionCollection.ToList());
clone._defaultPermissions = new List<string>(_defaultPermissions.ToList());
//re-create the event handler
clone._sectionCollection.CollectionChanged += clone.SectionCollectionChanged;
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
@@ -67,7 +68,11 @@ namespace Umbraco.Core.Models.Membership
{
_permissions = value;
return _permissions;
}, _permissions, PermissionsSelector);
}, _permissions, PermissionsSelector,
//Custom comparer for enumerable
new DelegateEqualityComparer<IEnumerable<string>>(
(enum1, enum2) => enum1.UnsortedSequenceEqual(enum2),
enum1 => enum1.GetHashCode()));
}
}
}
+24 -3
View File
@@ -1,4 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
@@ -120,7 +123,7 @@ namespace Umbraco.Core.Models
{
bool typeValidation = _propertyType.IsPropertyTypeValid(value);
if (!typeValidation)
if (typeValidation == false)
throw new Exception(
string.Format(
"Type validation failed. The value type: '{0}' does not match the DataType in PropertyType with alias: '{1}'",
@@ -130,7 +133,21 @@ namespace Umbraco.Core.Models
{
_value = value;
return _value;
}, _value, ValueSelector);
}, _value, ValueSelector,
new DelegateEqualityComparer<object>(
(o, o1) =>
{
//Custom comparer for enumerable if it is enumerable
if (o == null && o1 == null) return true;
if (o == null || o1 == null) return false;
var enum1 = o as IEnumerable;
var enum2 = o1 as IEnumerable;
if (enum1 != null && enum2 != null)
{
return enum1.Cast<object>().UnsortedSequenceEqual(enum2.Cast<object>());
}
return o.Equals(o1);
}, o => o.GetHashCode()));
}
}
@@ -160,10 +177,14 @@ namespace Umbraco.Core.Models
public override object DeepClone()
{
var clone = (Property)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//need to manually assign since this is a readonly property
clone._propertyType = (PropertyType)PropertyType.DeepClone();
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
+5 -2
View File
@@ -456,14 +456,17 @@ namespace Umbraco.Core.Models
public override object DeepClone()
{
var clone = (PropertyType)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//need to manually assign the Lazy value as it will not be automatically mapped
if (PropertyGroupId != null)
{
clone._propertyGroupId = new Lazy<int>(() => PropertyGroupId.Value);
}
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
+5 -2
View File
@@ -193,12 +193,15 @@ namespace Umbraco.Core.Models
public override object DeepClone()
{
var clone = (Template)base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//need to manually assign since they are readonly properties
clone._alias = Alias;
clone._name = Name;
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
+6 -2
View File
@@ -288,7 +288,8 @@ namespace Umbraco.Core.Models
public override object DeepClone()
{
var clone = (UmbracoEntity) base.DeepClone();
//turn off change tracking
clone.DisableChangeTracking();
//This ensures that any value in the dictionary that is deep cloneable is cloned too
foreach (var key in clone.AdditionalData.Keys.ToArray())
{
@@ -298,7 +299,10 @@ namespace Umbraco.Core.Models
clone.AdditionalData[key] = deepCloneable.DeepClone();
}
}
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
//re-enable tracking
clone.EnableChangeTracking();
return clone;
}
+16 -7
View File
@@ -1,6 +1,7 @@
using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
@@ -23,13 +24,21 @@ namespace Umbraco.Core.Models
internal static CultureInfo GetUserCulture(string userLanguage, ILocalizedTextService textService)
{
return textService.GetSupportedCultures()
.FirstOrDefault(culture =>
//match on full name first
culture.Name.InvariantEquals(userLanguage.Replace("_", "-")) ||
//then match on the 2 letter name
culture.TwoLetterISOLanguageName.InvariantEquals(userLanguage));
}
try
{
var culture = CultureInfo.GetCultureInfo(userLanguage.Replace("_", "-"));
//TODO: This is a hack because we store the user language as 2 chars instead of the full culture
// which is actually stored in the language files (which are also named with 2 chars!) so we need to attempt
// to convert to a supported full culture
var result = textService.ConvertToSupportedCultureWithRegionCode(culture);
return result;
}
catch (CultureNotFoundException)
{
//return the default one
return CultureInfo.GetCultureInfo("en");
}
}
/// <summary>
/// Checks if the user has access to the content item based on their start noe
@@ -32,9 +32,7 @@ namespace Umbraco.Core.Persistence.Factories
IsLockedOut = dto.NoConsole,
IsApproved = dto.Disabled == false,
Email = dto.Email,
Language = dto.UserLanguage,
//NOTE: The default permission come from the user type's default permissions
DefaultPermissions = _userType.Permissions
Language = dto.UserLanguage
};
foreach (var app in dto.User2AppDtos)
@@ -27,13 +27,13 @@ namespace Umbraco.Core.Persistence.Repositories
internal abstract class ContentTypeBaseRepository<TEntity> : PetaPocoRepositoryBase<int, TEntity>
where TEntity : class, IContentTypeComposition
{
protected ContentTypeBaseRepository(IDatabaseUnitOfWork work)
: base(work)
protected ContentTypeBaseRepository(IDatabaseUnitOfWork work)
: base(work)
{
}
protected ContentTypeBaseRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
protected ContentTypeBaseRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
{
}
@@ -414,7 +414,7 @@ AND umbracoNode.id <> @id",
.Where<ContentTypeAllowedContentTypeDto>(x => x.Id == id);
var allowedContentTypeDtos = Database.Fetch<ContentTypeAllowedContentTypeDto, ContentTypeDto>(sql);
return allowedContentTypeDtos.Select(x => new ContentTypeSort { Id = new Lazy<int>(() => x.AllowedId), Alias = x.ContentTypeDto.Alias, SortOrder = x.SortOrder }).ToList();
return allowedContentTypeDtos.Select(x => new ContentTypeSort(new Lazy<int>(() => x.AllowedId), x.SortOrder, x.ContentTypeDto.Alias)).ToList();
}
protected PropertyGroupCollection GetPropertyGroupCollection(int id, DateTime createDate, DateTime updateDate)
@@ -694,20 +694,20 @@ AND umbracoNode.id <> @id",
mediaTypeIds = mediaTypeIds.Distinct().ToArray();
var sql = @"SELECT cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.allowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
ParentTypes.parentContentTypeId as chtParentId,
umbracoNode.createDate as nCreateDate, umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
umbracoNode.parentID as nParentId, umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
umbracoNode.uniqueID as nUniqueId
umbracoNode.parentID as nParentId, umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
umbracoNode.uniqueID as nUniqueId
FROM cmsContentType
INNER JOIN umbracoNode
ON cmsContentType.nodeId = umbracoNode.id
LEFT JOIN (
SELECT cmsContentTypeAllowedContentType.Id, cmsContentTypeAllowedContentType.AllowedId, cmsContentType.alias, cmsContentTypeAllowedContentType.SortOrder
FROM cmsContentTypeAllowedContentType
INNER JOIN cmsContentType
ON cmsContentTypeAllowedContentType.AllowedId = cmsContentType.nodeId
SELECT cmsContentTypeAllowedContentType.Id, cmsContentTypeAllowedContentType.AllowedId, cmsContentType.alias, cmsContentTypeAllowedContentType.SortOrder
FROM cmsContentTypeAllowedContentType
INNER JOIN cmsContentType
ON cmsContentTypeAllowedContentType.AllowedId = cmsContentType.nodeId
) AllowedTypes
ON AllowedTypes.Id = cmsContentType.nodeId
LEFT JOIN cmsContentType2ContentType as ParentTypes
@@ -801,30 +801,30 @@ AND umbracoNode.id <> @id",
contentTypeIds = contentTypeIds.Distinct().ToArray();
var sql = @"SELECT cmsDocumentType.IsDefault as dtIsDefault, cmsDocumentType.templateNodeId as dtTemplateId,
cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.allowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
ParentTypes.parentContentTypeId as chtParentId,
umbracoNode.createDate as nCreateDate, umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
umbracoNode.parentID as nParentId, umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
umbracoNode.uniqueID as nUniqueId,
Template.alias as tAlias, Template.nodeId as tId,Template.text as tText
umbracoNode.parentID as nParentId, umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
umbracoNode.uniqueID as nUniqueId,
Template.alias as tAlias, Template.nodeId as tId,Template.text as tText
FROM cmsContentType
INNER JOIN umbracoNode
ON cmsContentType.nodeId = umbracoNode.id
LEFT JOIN cmsDocumentType
ON cmsDocumentType.contentTypeNodeId = cmsContentType.nodeId
LEFT JOIN (
SELECT cmsContentTypeAllowedContentType.Id, cmsContentTypeAllowedContentType.AllowedId, cmsContentType.alias, cmsContentTypeAllowedContentType.SortOrder
FROM cmsContentTypeAllowedContentType
INNER JOIN cmsContentType
ON cmsContentTypeAllowedContentType.AllowedId = cmsContentType.nodeId
SELECT cmsContentTypeAllowedContentType.Id, cmsContentTypeAllowedContentType.AllowedId, cmsContentType.alias, cmsContentTypeAllowedContentType.SortOrder
FROM cmsContentTypeAllowedContentType
INNER JOIN cmsContentType
ON cmsContentTypeAllowedContentType.AllowedId = cmsContentType.nodeId
) AllowedTypes
ON AllowedTypes.Id = cmsContentType.nodeId
LEFT JOIN (
SELECT * FROM cmsTemplate
INNER JOIN umbracoNode
ON cmsTemplate.nodeId = umbracoNode.id
SELECT * FROM cmsTemplate
INNER JOIN umbracoNode
ON cmsTemplate.nodeId = umbracoNode.id
) as Template
ON Template.nodeId = cmsDocumentType.templateNodeId
LEFT JOIN cmsContentType2ContentType as ParentTypes
@@ -872,8 +872,8 @@ AND umbracoNode.id <> @id",
//get the unique list of associated templates
var defaultTemplates = result
.Where(x => x.ctId == currentCtId)
//use a tuple so that distinct checks both values
.Select(x => new Tuple<bool?, int?>(x.dtIsDefault, x.dtTemplateId))
//use a tuple so that distinct checks both values (in some rare cases the dtIsDefault will not compute as bool?, so we force it with Convert.ToBoolean)
.Select(x => new Tuple<bool?, int?>(Convert.ToBoolean(x.dtIsDefault), x.dtTemplateId))
.Where(x => x.Item1.HasValue && x.Item2.HasValue)
.Distinct()
.OrderByDescending(x => x.Item1.Value)
@@ -0,0 +1,62 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Umbraco.Core.PropertyEditors
{
internal class GridEditor
{
public GridEditor()
{
Config = new Dictionary<string, object>();
}
[JsonProperty("name", Required = Required.Always)]
public string Name { get; set; }
[JsonProperty("alias", Required = Required.Always)]
public string Alias { get; set; }
[JsonProperty("view", Required = Required.Always)]
public string View { get; set; }
[JsonProperty("render")]
public string Render { get; set; }
[JsonProperty("icon", Required = Required.Always)]
public string Icon { get; set; }
[JsonProperty("config")]
public IDictionary<string, object> Config { get; set; }
protected bool Equals(GridEditor other)
{
return string.Equals(Alias, other.Alias);
}
/// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// true if the specified object is equal to the current object; otherwise, false.
/// </returns>
/// <param name="obj">The object to compare with the current object. </param>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((GridEditor) obj);
}
/// <summary>
/// Serves as a hash function for a particular type.
/// </summary>
/// <returns>
/// A hash code for the current <see cref="T:System.Object"/>.
/// </returns>
public override int GetHashCode()
{
return Alias.GetHashCode();
}
}
}
@@ -15,11 +15,14 @@ namespace Umbraco.Core.PropertyEditors
/// </remarks>
internal class ParameterEditorResolver : LazyManyObjectsResolverBase<ParameterEditorResolver, IParameterEditor>
{
public ParameterEditorResolver(Func<IEnumerable<Type>> typeListProducerList)
private readonly ManifestBuilder _builder;
public ParameterEditorResolver(Func<IEnumerable<Type>> typeListProducerList, ManifestBuilder builder)
: base(typeListProducerList, ObjectLifetimeScope.Application)
{
_builder = builder;
}
/// <summary>
/// Returns the parameter editors
/// </summary>
@@ -38,9 +41,9 @@ namespace Umbraco.Core.PropertyEditors
//exclude the non parameter editor c# property editors
.Except(filtered)
//include the manifest parameter editors
.Union(ManifestBuilder.ParameterEditors)
.Union(_builder.ParameterEditors)
//include the manifest prop editors that are parameter editors
.Union(ManifestBuilder.PropertyEditors.Where(x => x.IsParameterEditor));
.Union(_builder.PropertyEditors.Where(x => x.IsParameterEditor));
}
}
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Umbraco.Core.IO;
using Umbraco.Core.Manifest;
using Umbraco.Core.ObjectResolution;
@@ -17,14 +19,27 @@ namespace Umbraco.Core.PropertyEditors
public PropertyEditorResolver(Func<IEnumerable<Type>> typeListProducerList)
: base(typeListProducerList, ObjectLifetimeScope.Application)
{
var builder = new ManifestBuilder(
ApplicationContext.Current.ApplicationCache.RuntimeCache,
new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), ApplicationContext.Current.ApplicationCache.RuntimeCache));
_unioned = new Lazy<List<PropertyEditor>>(() => Values.Union(builder.PropertyEditors).ToList());
}
internal PropertyEditorResolver(Func<IEnumerable<Type>> typeListProducerList, ManifestBuilder builder)
: base(typeListProducerList, ObjectLifetimeScope.Application)
{
_unioned = new Lazy<List<PropertyEditor>>(() => Values.Union(builder.PropertyEditors).ToList());
}
private readonly Lazy<List<PropertyEditor>> _unioned;
/// <summary>
/// Returns the property editors
/// </summary>
public IEnumerable<PropertyEditor> PropertyEditors
{
get { return Values.Union(ManifestBuilder.PropertyEditors); }
get { return _unioned.Value; }
}
/// <summary>
@@ -128,7 +128,7 @@ namespace Umbraco.Core.Services
return list;
});
}, new TimeSpan(0, 10, 0));
}
/// <summary>
@@ -33,5 +33,19 @@ namespace Umbraco.Core.Services
/// </summary>
/// <returns></returns>
IEnumerable<CultureInfo> GetSupportedCultures();
/// <summary>
/// Tries to resolve a full 4 letter culture from a 2 letter culture name
/// </summary>
/// <param name="currentCulture">
/// The culture to determine if it is only a 2 letter culture, if so we'll try to convert it, otherwise it will just be returned
/// </param>
/// <returns></returns>
/// <remarks>
/// TODO: This is just a hack due to the way we store the language files, they should be stored with 4 letters since that
/// is what they reference but they are stored with 2, further more our user's languages are stored with 2. So this attempts
/// to resolve the full culture if possible.
/// </remarks>
CultureInfo ConvertToSupportedCultureWithRegionCode(CultureInfo currentCulture);
}
}
@@ -13,6 +13,7 @@ namespace Umbraco.Core.Services
public class LocalizedTextService : ILocalizedTextService
{
private readonly LocalizedTextServiceFileSources _fileSources;
private readonly IDictionary<CultureInfo, IDictionary<string, IDictionary<string, string>>> _dictionarySource;
private readonly IDictionary<CultureInfo, Lazy<XDocument>> _xmlSource;
@@ -22,7 +23,8 @@ namespace Umbraco.Core.Services
/// <param name="fileSources"></param>
public LocalizedTextService(LocalizedTextServiceFileSources fileSources)
{
_xmlSource = fileSources.GetXmlSources();
if (fileSources == null) throw new ArgumentNullException("fileSources");
_fileSources = fileSources;
}
/// <summary>
@@ -49,6 +51,9 @@ namespace Umbraco.Core.Services
{
Mandate.ParameterNotNull(culture, "culture");
//TODO: Hack, see notes on ConvertToSupportedCultureWithRegionCode
culture = ConvertToSupportedCultureWithRegionCode(culture);
//This is what the legacy ui service did
if (string.IsNullOrEmpty(key))
return string.Empty;
@@ -57,10 +62,14 @@ namespace Umbraco.Core.Services
var area = keyParts.Length > 1 ? keyParts[0] : null;
var alias = keyParts.Length > 1 ? keyParts[1] : keyParts[0];
if (_xmlSource != null)
var xmlSource = _xmlSource ?? (_fileSources != null
? _fileSources.GetXmlSources()
: null);
if (xmlSource != null)
{
return GetFromXmlSource(culture, area, alias, tokens);
}
return GetFromXmlSource(xmlSource, culture, area, alias, tokens);
}
else
{
return GetFromDictionarySource(culture, area, alias, tokens);
@@ -75,18 +84,25 @@ namespace Umbraco.Core.Services
{
if (culture == null) throw new ArgumentNullException("culture");
//TODO: Hack, see notes on ConvertToSupportedCultureWithRegionCode
culture = ConvertToSupportedCultureWithRegionCode(culture);
var result = new Dictionary<string, string>();
if (_xmlSource != null)
var xmlSource = _xmlSource ?? (_fileSources != null
? _fileSources.GetXmlSources()
: null);
if (xmlSource != null)
{
if (_xmlSource.ContainsKey(culture) == false)
if (xmlSource.ContainsKey(culture) == false)
{
LogHelper.Warn<LocalizedTextService>("The culture specified {0} was not found in any configured sources for this service", () => culture);
return result;
}
//convert all areas + keys to a single key with a '/'
var areas = _xmlSource[culture].Value.XPathSelectElements("//area");
var areas = xmlSource[culture].Value.XPathSelectElements("//area");
foreach (var area in areas)
{
var keys = area.XPathSelectElements("./key");
@@ -133,7 +149,36 @@ namespace Umbraco.Core.Services
/// <returns></returns>
public IEnumerable<CultureInfo> GetSupportedCultures()
{
return _xmlSource != null ? _xmlSource.Keys : _dictionarySource.Keys;
var xmlSource = _xmlSource ?? (_fileSources != null
? _fileSources.GetXmlSources()
: null);
return xmlSource != null ? xmlSource.Keys : _dictionarySource.Keys;
}
/// <summary>
/// Tries to resolve a full 4 letter culture from a 2 letter culture name
/// </summary>
/// <param name="currentCulture">
/// The culture to determine if it is only a 2 letter culture, if so we'll try to convert it, otherwise it will just be returned
/// </param>
/// <returns></returns>
/// <remarks>
/// TODO: This is just a hack due to the way we store the language files, they should be stored with 4 letters since that
/// is what they reference but they are stored with 2, further more our user's languages are stored with 2. So this attempts
/// to resolve the full culture if possible.
///
/// This only works when this service is constructed with the LocalizedTextServiceFileSources
/// </remarks>
public CultureInfo ConvertToSupportedCultureWithRegionCode(CultureInfo currentCulture)
{
if (currentCulture == null) throw new ArgumentNullException("currentCulture");
if (_fileSources == null) return currentCulture;
if (currentCulture.Name.Length > 2) return currentCulture;
var attempt = _fileSources.TryConvert2LetterCultureTo4Letter(currentCulture.TwoLetterISOLanguageName);
return attempt ? attempt.Result : currentCulture;
}
private string GetFromDictionarySource(CultureInfo culture, string area, string key, IDictionary<string, string> tokens)
@@ -174,15 +219,15 @@ namespace Umbraco.Core.Services
return "[" + key + "]";
}
private string GetFromXmlSource(CultureInfo culture, string area, string key, IDictionary<string, string> tokens)
private static string GetFromXmlSource(IDictionary<CultureInfo, Lazy<XDocument>> xmlSource, CultureInfo culture, string area, string key, IDictionary<string, string> tokens)
{
if (_xmlSource.ContainsKey(culture) == false)
if (xmlSource.ContainsKey(culture) == false)
{
LogHelper.Warn<LocalizedTextService>("The culture specified {0} was not found in any configured sources for this service", () => culture);
return "[" + key + "]";
}
var cultureSource = _xmlSource[culture].Value;
var cultureSource = xmlSource[culture].Value;
var xpath = area.IsNullOrWhiteSpace()
? string.Format("//key [@alias = '{0}']", key)
@@ -213,7 +258,7 @@ namespace Umbraco.Core.Services
/// we support a dictionary which means in the future we can really have any sort of token system.
/// Currently though, the token key's will need to be an integer and sequential - though we aren't going to throw exceptions if that is not the case.
/// </remarks>
internal string ParseTokens(string value, IDictionary<string, string> tokens)
internal static string ParseTokens(string value, IDictionary<string, string> tokens)
{
if (tokens == null || tokens.Any() == false)
{
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
@@ -16,6 +17,9 @@ namespace Umbraco.Core.Services
private readonly IRuntimeCacheProvider _cache;
private readonly DirectoryInfo _fileSourceFolder;
//TODO: See other notes in this class, this is purely a hack because we store 2 letter culture file names that contain 4 letter cultures :(
private readonly Dictionary<string, CultureInfo> _twoLetterCultureConverter = new Dictionary<string, CultureInfo>();
public LocalizedTextServiceFileSources(IRuntimeCacheProvider cache, DirectoryInfo fileSourceFolder)
{
if (cache == null) throw new ArgumentNullException("cache");
@@ -46,10 +50,53 @@ namespace Umbraco.Core.Services
{
var localCopy = fileInfo;
var filename = Path.GetFileNameWithoutExtension(localCopy.FullName).Replace("_", "-");
var culture = CultureInfo.GetCultureInfo(filename);
//TODO: Fix this nonsense... would have to wait until v8 to store the language files with their correct
// names instead of storing them as 2 letters but actually having a 4 letter culture. wtf. So now, we
// need to check if the file is 2 letters, then open it to try to find it's 4 letter culture, then use that
// if it's successful. We're going to assume (though it seems assuming in the legacy logic is never a great idea)
// that any 4 letter file is named with the actual culture that it is!
CultureInfo culture = null;
if (filename.Length == 2)
{
//we need to open the file to see if we can read it's 'real' culture, we'll use XmlReader since we don't
//want to load in the entire doc into mem just to read a single value
using (var fs = fileInfo.OpenRead())
using (var reader = XmlReader.Create(fs))
{
if (reader.IsStartElement())
{
if (reader.Name == "language")
{
if (reader.MoveToAttribute("culture"))
{
var cultureVal = reader.Value;
try
{
culture = CultureInfo.GetCultureInfo(cultureVal);
//add to the tracked dictionary
_twoLetterCultureConverter[filename] = culture;
}
catch (CultureNotFoundException)
{
LogHelper.Warn<LocalizedTextServiceFileSources>(
string.Format("The culture {0} found in the file {1} is not a valid culture", cultureVal, fileInfo.FullName));
//If the culture in the file is invalid, we'll just hope the file name is a valid culture below, otherwise
// an exception will be thrown.
}
}
}
}
}
}
if (culture == null)
{
culture = CultureInfo.GetCultureInfo(filename);
}
//get the lazy value from cache
result.Add(culture, new Lazy<XDocument>(() => _cache.GetCacheItem<XDocument>(
string.Format("{0}-{1}", typeof (LocalizedTextServiceFileSources).Name, culture.TwoLetterISOLanguageName), () =>
string.Format("{0}-{1}", typeof (LocalizedTextServiceFileSources).Name, culture.Name), () =>
{
using (var fs = localCopy.OpenRead())
{
@@ -59,5 +106,15 @@ namespace Umbraco.Core.Services
}
return result;
}
//TODO: See other notes in this class, this is purely a hack because we store 2 letter culture file names that contain 4 letter cultures :(
public Attempt<CultureInfo> TryConvert2LetterCultureTo4Letter(string twoLetterCulture)
{
if (twoLetterCulture.Length != 2) Attempt<CultureInfo>.Fail();
return _twoLetterCultureConverter.ContainsKey(twoLetterCulture)
? Attempt.Succeed(_twoLetterCultureConverter[twoLetterCulture])
: Attempt<CultureInfo>.Fail();
}
}
}
+1 -1
View File
@@ -147,7 +147,7 @@ namespace Umbraco.Core.Services
return list;
});
}, new TimeSpan(0, 10, 0));
}
internal void LoadXml(Func<XDocument, bool> callback, bool saveAfterCallbackIfChanged)
+2
View File
@@ -347,6 +347,7 @@
<Compile Include="IO\ResizedImage.cs" />
<Compile Include="IO\UmbracoMediaFile.cs" />
<Compile Include="Models\IPartialView.cs" />
<Compile Include="PropertyEditors\GridEditor.cs" />
<Compile Include="Services\LocalizedTextService.cs" />
<Compile Include="Services\ILocalizedTextService.cs" />
<Compile Include="Macros\XsltExtension.cs" />
@@ -1124,6 +1125,7 @@
<Compile Include="Standalone\ServiceContextManager.cs" />
<Compile Include="Standalone\StandaloneCoreApplication.cs" />
<Compile Include="Standalone\StandaloneCoreBootManager.cs" />
<Compile Include="Strategies\RelateOnCopyHandler.cs" />
<Compile Include="Strings\ContentBaseExtensions.cs" />
<Compile Include="Strings\Diff.cs" />
<Compile Include="Sync\CurrentServerEnvironmentStatus.cs" />
@@ -347,12 +347,10 @@ namespace Umbraco.Tests.CodeFirst.Definitions
{
if(type == currentType) continue;//If the referenced type is equal to the current type we skip it to avoid a circular dependency
var contentTypeSort = new ContentTypeSort();
var isResolved = _contentTypeCache.ContainsKey(type.FullName);
var lazy = isResolved ? _contentTypeCache[type.FullName].ContentType : GetContentTypeDefinition(type);
contentTypeSort.Id = new Lazy<int>(() => lazy.Value.Id);
contentTypeSort.Alias = lazy.Value.Alias;
contentTypeSort.SortOrder = order;
var contentTypeSort = new ContentTypeSort(new Lazy<int>(() => lazy.Value.Id), order, lazy.Value.Alias);
contentTypeSorts.Add(contentTypeSort);
order++;
}
@@ -11,6 +11,35 @@ namespace Umbraco.Tests
public class EnumerableExtensionsTests
{
[Test]
public void Unsorted_Sequence_Equal()
{
var list1 = new[] { 1, 2, 3, 4, 5, 6 };
var list2 = new[] { 6, 5, 3, 2, 1, 4 };
var list3 = new[] { 6, 5, 4, 3, 2, 2 };
Assert.IsTrue(list1.UnsortedSequenceEqual(list2));
Assert.IsTrue(list2.UnsortedSequenceEqual(list1));
Assert.IsFalse(list1.UnsortedSequenceEqual(list3));
Assert.IsTrue(((IEnumerable<object>)null).UnsortedSequenceEqual(null));
Assert.IsFalse(((IEnumerable<int>)null).UnsortedSequenceEqual(list1));
Assert.IsFalse(list1.UnsortedSequenceEqual(null));
}
[Test]
public void Contains_All()
{
var list1 = new[] {1, 2, 3, 4, 5, 6};
var list2 = new[] {6, 5, 3, 2, 1, 4};
var list3 = new[] {6, 5, 4, 3};
Assert.IsTrue(list1.ContainsAll(list2));
Assert.IsTrue(list2.ContainsAll(list1));
Assert.IsTrue(list1.ContainsAll(list3));
Assert.IsFalse(list3.ContainsAll(list1));
}
[Test]
public void Flatten_List_2()
{
+3 -3
View File
@@ -21,7 +21,7 @@ namespace Umbraco.Tests.Macros
{
var content = @"<p>asdfasdf</p>
<p>asdfsadf</p>
<?UMBRACO_MACRO macroAlias=""My.Map.isCool"" />
<?UMBRACO_MACRO macroAlias=""My.Map.isCool eh[boy!]"" />
<p>asdfasdf</p>";
var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary<string, string>() { { "test1", "value1" }, { "test2", "value2" } });
@@ -34,8 +34,8 @@ namespace Umbraco.Tests.Macros
Assert.AreEqual(@"<p>asdfasdf</p>
<p>asdfsadf</p>
<div class=""umb-macro-holder mceNonEditable"" test1=""value1"" test2=""value2"">
<!-- <?UMBRACO_MACRO macroAlias=""My.Map.isCool"" /> -->
<ins>Macro alias: <strong>My.Map.isCool</strong></ins></div>
<!-- <?UMBRACO_MACRO macroAlias=""My.Map.isCool eh[boy!]"" /> -->
<ins>Macro alias: <strong>My.Map.isCool eh[boy!]</strong></ins></div>
<p>asdfasdf</p>".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty));
}
@@ -63,6 +63,61 @@ namespace Umbraco.Tests.Manifest
Assert.AreEqual(0, parser.ElementAt(0).PreValueEditor.Fields.ElementAt(1).Validators.Count());
}
[Test]
public void Parse_Grid_Editors()
{
var a = JsonConvert.DeserializeObject<JArray>(@"[
{
alias: 'Test.Test1',
name: 'Test 1',
view: 'blah',
icon: 'hello'
},
{
alias: 'Test.Test2',
name: 'Test 2',
config: { key1: 'some default val' },
view: '/hello/world.cshtml',
icon: 'helloworld'
},
{
alias: 'Test.Test3',
name: 'Test 3',
config: { key1: 'some default val' },
view: '/hello/world.html',
render: '/hello/world.cshtml',
icon: 'helloworld'
}
]");
var parser = ManifestParser.GetGridEditors(a).ToArray();
Assert.AreEqual(3, parser.Count());
Assert.AreEqual("Test.Test1", parser.ElementAt(0).Alias);
Assert.AreEqual("Test 1", parser.ElementAt(0).Name);
Assert.AreEqual("blah", parser.ElementAt(0).View);
Assert.AreEqual("hello", parser.ElementAt(0).Icon);
Assert.IsNull(parser.ElementAt(0).Render);
Assert.AreEqual(0, parser.ElementAt(0).Config.Count);
Assert.AreEqual("Test.Test2", parser.ElementAt(1).Alias);
Assert.AreEqual("Test 2", parser.ElementAt(1).Name);
Assert.AreEqual("/hello/world.cshtml", parser.ElementAt(1).View);
Assert.AreEqual("helloworld", parser.ElementAt(1).Icon);
Assert.IsNull(parser.ElementAt(1).Render);
Assert.AreEqual(1, parser.ElementAt(1).Config.Count);
Assert.AreEqual("some default val", parser.ElementAt(1).Config["key1"]);
Assert.AreEqual("Test.Test3", parser.ElementAt(2).Alias);
Assert.AreEqual("Test 3", parser.ElementAt(2).Name);
Assert.AreEqual("/hello/world.html", parser.ElementAt(2).View);
Assert.AreEqual("helloworld", parser.ElementAt(2).Icon);
Assert.AreEqual("/hello/world.cshtml", parser.ElementAt(2).Render);
Assert.AreEqual(1, parser.ElementAt(2).Config.Count);
Assert.AreEqual("some default val", parser.ElementAt(2).Config["key1"]);
}
[Test]
public void Parse_Property_Editors()
{
+3
View File
@@ -51,6 +51,9 @@ namespace Umbraco.Tests.Models
Assert.AreEqual(clone.UserType, item.UserType);
Assert.AreEqual(clone.AllowedSections.Count(), item.AllowedSections.Count());
Assert.AreNotSame(clone.DefaultPermissions, item.DefaultPermissions);
Assert.AreEqual(clone.DefaultPermissions.Count(), item.DefaultPermissions.Count());
//Verify normal properties with reflection
var allProps = clone.GetType().GetProperties();
foreach (var propertyInfo in allProps)
@@ -172,12 +172,7 @@ namespace Umbraco.Tests.Persistence.Repositories
var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage");
contentType.AllowedContentTypes = new List<ContentTypeSort>
{
new ContentTypeSort
{
Alias = contentType.Alias,
Id = new Lazy<int>(() => contentType.Id),
SortOrder = 0
}
new ContentTypeSort(new Lazy<int>(() => contentType.Id), 0, contentType.Alias)
};
var parentPage = MockedContent.CreateSimpleContent(contentType);
contentTypeRepository.AddOrUpdate(contentType);
@@ -386,18 +386,8 @@ namespace Umbraco.Tests.Persistence.Repositories
var contentType = repository.Get(NodeDto.NodeIdSeed);
contentType.AllowedContentTypes = new List<ContentTypeSort>
{
new ContentTypeSort
{
Alias = subpageContentType.Alias,
Id = new Lazy<int>(() => subpageContentType.Id),
SortOrder = 0
},
new ContentTypeSort
{
Alias = simpleSubpageContentType.Alias,
Id = new Lazy<int>(() => simpleSubpageContentType.Id),
SortOrder = 1
}
new ContentTypeSort(new Lazy<int>(() => subpageContentType.Id), 0, subpageContentType.Alias),
new ContentTypeSort(new Lazy<int>(() => simpleSubpageContentType.Id), 1, simpleSubpageContentType.Alias)
};
repository.AddOrUpdate(contentType);
unitOfWork.Commit();
+2 -2
View File
@@ -82,8 +82,8 @@ namespace Umbraco.Tests.Plugins
var originalTypesFound = TypeFinderOriginal.FindClassesOfType<IApplicationStartupHandler>(_assemblies);
Assert.AreEqual(originalTypesFound.Count(), typesFound.Count());
Assert.AreEqual(5, typesFound.Count());
Assert.AreEqual(5, originalTypesFound.Count());
Assert.AreEqual(6, typesFound.Count());
Assert.AreEqual(6, originalTypesFound.Count());
}
[Test]
@@ -112,5 +112,17 @@ namespace Umbraco.Tests.PropertyEditors
var urlString = mediaPath.GetCropUrl(width: 100, height: 270, imageCropMode: ImageCropMode.Crop, imageCropAnchor: ImageCropAnchor.Center);
Assert.AreEqual(mediaPath + "?mode=crop&anchor=center&width=100&height=270", urlString);
}
/// <summary>
/// Test for preferFocalPoint when focal point is centered
/// </summary>
[Test]
public void GetCropUrl_PreferFocalPointCenter()
{
var cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\":\"thumb\",\"width\": 100,\"height\": 100,\"coordinates\": {\"x1\": 0.58729977382575338,\"y1\": 0.055768992440203169,\"x2\": 0,\"y2\": 0.32457553600198386}}]}";
var urlString = mediaPath.GetCropUrl(imageCropperValue: cropperJson, width: 300, height: 150, preferFocalPoint:true);
Assert.AreEqual(mediaPath + "?anchor=center&mode=crop&width=300&height=150", urlString);
}
}
}
@@ -239,12 +239,7 @@ namespace Umbraco.Tests.Services
ServiceContext.ContentTypeService.Save(contentType1);
contentType1.AllowedContentTypes = new List<ContentTypeSort>
{
new ContentTypeSort
{
Alias = contentType1.Alias,
Id = new Lazy<int>(() => contentType1.Id),
SortOrder = 0
}
new ContentTypeSort(new Lazy<int>(() => contentType1.Id), 0, contentType1.Alias)
};
var result = new List<IContent>();
ServiceContext.ContentTypeService.Save(contentType1);
+2 -2
View File
@@ -58,9 +58,9 @@
<Reference Include="AutoMapper.Net4">
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.60.2941, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine, Version=0.1.61.2941, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.60.2941\lib\Examine.dll</HintPath>
<HintPath>..\packages\Examine.0.1.61.2941\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
+1 -1
View File
@@ -2,7 +2,7 @@
<packages>
<package id="AspNetWebApi.SelfHost" version="4.0.20710.0" targetFramework="net45" />
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
<package id="Examine" version="0.1.60.2941" targetFramework="net45" />
<package id="Examine" version="0.1.61.2941" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net40" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
+5 -1
View File
@@ -21,7 +21,8 @@
"rgrove-lazyload": "*",
"jquery": "2.0.3",
"jquery-file-upload": "~9.4.0",
"jquery-ui": "1.10.3"
"jquery-ui": "1.10.3",
"angular-dynamic-locale": "~0.1.27"
},
"exportsOverride": {
"rgrove-lazyload": {
@@ -33,6 +34,9 @@
"underscore": {
"": "underscore-min.{js,map}"
},
"angular-dynamic-locale": {
"": "tmhDynamicLocale.min.{js,js.map}"
},
"jquery": {
"": "jquery.min.{js,map}"
},
@@ -1,186 +0,0 @@
( function(window) {
'use strict';
angular.module('tmh.dynamicLocale', []).provider('tmhDynamicLocale', function() {
var defaultLocale,
localeLocationPattern = 'angular/i18n/angular-locale_{{locale}}.js',
storageFactory = 'tmhDynamicLocaleStorageCache',
storage,
storeKey = 'tmhDynamicLocale.locale',
promiseCache = {},
activeLocale;
/**
* Loads a script asynchronously
*
* @param {string} url The url for the script
@ @param {function) callback A function to be called once the script is loaded
*/
function loadScript(url, callback, errorCallback, $timeout) {
var script = document.createElement('script'),
body = document.getElementsByTagName('body')[0],
removed = false;
script.type = 'text/javascript';
if (script.readyState) { // IE
script.onreadystatechange = function () {
if (script.readyState === 'complete' ||
script.readyState === 'loaded') {
script.onreadystatechange = null;
$timeout(
function () {
if (removed) return;
removed = true;
body.removeChild(script);
callback();
}, 30, false);
}
};
} else { // Others
script.onload = function () {
if (removed) return;
removed = true;
body.removeChild(script);
callback();
};
script.onerror = function () {
if (removed) return;
removed = true;
body.removeChild(script);
errorCallback();
};
}
script.src = url;
script.async = false;
body.appendChild(script);
}
/**
* Loads a locale and replaces the properties from the current locale with the new locale information
*
* @param localeUrl The path to the new locale
* @param $locale The locale at the curent scope
*/
function loadLocale(localeUrl, $locale, localeId, $rootScope, $q, localeCache, $timeout) {
function overrideValues(oldObject, newObject) {
if (activeLocale !== localeId) {
return;
}
angular.forEach(oldObject, function(value, key) {
if (!newObject[key]) {
delete oldObject[key];
} else if (angular.isArray(newObject[key])) {
oldObject[key].length = newObject[key].length;
}
});
angular.forEach(newObject, function(value, key) {
if (angular.isArray(newObject[key]) || angular.isObject(newObject[key])) {
if (!oldObject[key]) {
oldObject[key] = angular.isArray(newObject[key]) ? [] : {};
}
overrideValues(oldObject[key], newObject[key]);
} else {
oldObject[key] = newObject[key];
}
});
}
if (promiseCache[localeId]) return promiseCache[localeId];
var cachedLocale,
deferred = $q.defer();
if (localeId === activeLocale) {
deferred.resolve($locale);
} else if ((cachedLocale = localeCache.get(localeId))) {
activeLocale = localeId;
$rootScope.$evalAsync(function() {
overrideValues($locale, cachedLocale);
$rootScope.$broadcast('$localeChangeSuccess', localeId, $locale);
storage.put(storeKey, localeId);
deferred.resolve($locale);
});
} else {
activeLocale = localeId;
promiseCache[localeId] = deferred.promise;
loadScript(localeUrl, function () {
// Create a new injector with the new locale
var localInjector = angular.injector(['ngLocale']),
externalLocale = localInjector.get('$locale');
overrideValues($locale, externalLocale);
localeCache.put(localeId, externalLocale);
delete promiseCache[localeId];
$rootScope.$apply(function () {
$rootScope.$broadcast('$localeChangeSuccess', localeId, $locale);
storage.put(storeKey, localeId);
deferred.resolve($locale);
});
}, function () {
delete promiseCache[localeId];
$rootScope.$apply(function () {
$rootScope.$broadcast('$localeChangeError', localeId);
deferred.reject(localeId);
});
}, $timeout);
}
return deferred.promise;
}
this.localeLocationPattern = function(value) {
if (value) {
localeLocationPattern = value;
return this;
} else {
return localeLocationPattern;
}
};
this.useStorage = function(storageName) {
storageFactory = storageName;
};
this.useCookieStorage = function() {
this.useStorage('$cookieStore');
};
this.defaultLocale = function (value) {
defaultLocale = value;
};
this.$get = ['$rootScope', '$injector', '$interpolate', '$locale', '$q', 'tmhDynamicLocaleCache', '$timeout', function($rootScope, $injector, interpolate, locale, $q, tmhDynamicLocaleCache, $timeout) {
var localeLocation = interpolate(localeLocationPattern);
storage = $injector.get(storageFactory);
$rootScope.$evalAsync(function () {
var initialLocale;
if ((initialLocale = (storage.get(storeKey) || defaultLocale))) {
loadLocale(localeLocation({locale: initialLocale}), locale, initialLocale, $rootScope, $q, tmhDynamicLocaleCache, $timeout);
}
});
return {
/**
* @ngdoc method
* @description
* @param {string=} value Sets the locale to the new locale. Changing the locale will trigger
* a background task that will retrieve the new locale and configure the current $locale
* instance with the information from the new locale
*/
set: function(value) {
return loadLocale(localeLocation({locale: value}), locale, value, $rootScope, $q, tmhDynamicLocaleCache, $timeout);
}
};
}];
}).provider('tmhDynamicLocaleCache', function() {
this.$get = ['$cacheFactory', function($cacheFactory) {
return $cacheFactory('tmh.dynamicLocales');
}];
}).provider('tmhDynamicLocaleStorageCache', function() {
this.$get = ['$cacheFactory', function($cacheFactory) {
return $cacheFactory('tmh.dynamicLocales.store');
}];
}).run(['tmhDynamicLocale', angular.noop]);
}(window) );
@@ -31,7 +31,7 @@ function inArray(key, arr)
tinymce = parent.tinymce;
editor = tinymce.activeEditor;
var i, userSettings = editor.settings.codemirror ? JSON.parse(editor.settings.codemirror) : {};
var i, userSettings = editor.settings.codemirror ? (typeof editor.settings.codemirror === 'object') ? editor.settings.codemirror : JSON.parse(editor.settings.codemirror) : {};
CMsettings = {
path: userSettings.path || 'CodeMirror',
indentOnInit: userSettings.indentOnInit || false,
@@ -193,6 +193,7 @@ tinymce.PluginManager.add('umbracolink', function(editor) {
});
selection.select(anchorElm);
editor.execCommand('mceEndTyping');
} else {
editor.execCommand('mceInsertLink', false, {
href: href,
@@ -15,12 +15,12 @@ var app = angular.module("Umbraco.canvasdesigner", ['colorpicker', 'ui.slider',
$scope.pageUrl = "../dialogs/Preview.aspx?id=" + $location.search().id;
$scope.valueAreLoaded = false;
$scope.devices = [
{ name: "desktop", css: "desktop", icon: "icon-display" },
{ name: "laptop - 1366px", css: "laptop border", icon: "icon-laptop" },
{ name: "iPad portrait - 768px", css: "iPad-portrait border", icon: "icon-ipad" },
{ name: "iPad landscape - 1024px", css: "iPad-landscape border", icon: "icon-ipad flip" },
{ name: "smartphone portrait - 480px", css: "smartphone-portrait border", icon: "icon-iphone" },
{ name: "smartphone landscape - 320px", css: "smartphone-landscape border", icon: "icon-iphone flip" }
{ name: "desktop", css: "desktop", icon: "icon-display", title: "Desktop" },
{ name: "laptop - 1366px", css: "laptop border", icon: "icon-laptop", title: "Laptop" },
{ name: "iPad portrait - 768px", css: "iPad-portrait border", icon: "icon-ipad", title: "Tablet portrait" },
{ name: "iPad landscape - 1024px", css: "iPad-landscape border", icon: "icon-ipad flip", title: "Tablet landscape" },
{ name: "smartphone portrait - 480px", css: "smartphone-portrait border", icon: "icon-iphone", title: "Smartphone portrait" },
{ name: "smartphone landscape - 320px", css: "smartphone-landscape border", icon: "icon-iphone flip", title: "Smartphone landscape" }
];
$scope.previewDevice = $scope.devices[0];
@@ -25,7 +25,7 @@
<ul class="sections" ng-class="{selected: showDevicesPreview}">
<li ng-repeat="device in devices" ng-class="{ current:previewDevice==device }" ng-click="updatePreviewDevice(device)">
<a href="#"><i class="icon {{device.icon}}"></i><span></span></a>
<a href="#"><i class="icon {{device.icon}}" title="{{device.title}}"></i><span></span></a>
</li>
<li ng-click="closePreviewDevice()" ng-if="enableCanvasdesigner > 0">
<a href="" class="more-options">
@@ -108,7 +108,7 @@ function umbImageFolder($rootScope, assetsService, $timeout, $log, umbRequestHel
//check for the file size error which can only be done with dodgy string checking
scope.$on('fileuploadfail', function (e, data) {
if (data.jqXHR.status === 500 && data.jqXHR.responseText.indexOf("Maximum request length exceeded") >= 0) {
notificationsService.error(data.errorThrown, "The image file size was too big, check with your site administrator to adjust the maximum size allowed");
notificationsService.error(data.errorThrown, "The uploaded file was too large, check with your site administrator to adjust the maximum size allowed");
}
else {
@@ -33,7 +33,7 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
'<h5>' +
'<i ng-if="enablecheckboxes == \'true\'" ng-class="selectEnabledNodeClass(tree.root)"></i>' +
'<a href="#/{{section}}" ng-click="select(tree.root, $event)" class="root-link">{{tree.name}}</a></h5>' +
'<a href class="umb-options" ng-hide="tree.root.isContainer || !tree.root.menuUrl" ng-click="options(tree.root, $event)" ng-swipe-right="options(tree.root, $event)"><i></i><i></i><i></i></a>' +
'<a class="umb-options" ng-hide="tree.root.isContainer || !tree.root.menuUrl" ng-click="options(tree.root, $event)" ng-swipe-right="options(tree.root, $event)"><i></i><i></i><i></i></a>' +
'</div>';
template += '<ul>' +
'<umb-tree-item ng-repeat="child in tree.root.children" eventhandler="eventhandler" node="child" current-node="currentNode" tree="this" section="{{section}}" ng-animate="animation()"></umb-tree-item>' +
@@ -41,9 +41,9 @@ angular.module("umbraco.directives")
'<ins style="width:18px;"></ins>' +
'<ins ng-class="{\'icon-navigation-right\': !node.expanded, \'icon-navigation-down\': node.expanded}" ng-click="load(node)"></ins>' +
'<i class="icon umb-tree-icon sprTree"></i>' +
'<a href ng-click="select(node, $event)" on-right-click="altSelect(node, $event)"></a>' +
'<a ng-click="select(node, $event)"></a>' +
//NOTE: These are the 'option' elipses
'<a href class="umb-options" ng-click="options(node, $event)"><i></i><i></i><i></i></a>' +
'<a class="umb-options" ng-click="options(node, $event)"><i></i><i></i><i></i></a>' +
'<div ng-show="node.loading" class="l"><div></div></div>' +
'</div>' +
'</li>',
@@ -59,6 +59,7 @@ angular.module("umbraco.directives")
// Helper function to emit tree events
function emitEvent(eventName, args) {
if (scope.eventhandler) {
$(scope.eventhandler).trigger(eventName, args);
}
@@ -44,27 +44,36 @@ angular.module('umbraco.services')
.factory('assetsService', function ($q, $log, angularHelper, umbRequestHelper, $rootScope, $http) {
var initAssetsLoaded = false;
var appendRnd = function(url){
var appendRnd = function (url) {
//if we don't have a global umbraco obj yet, the app is bootstrapping
if(!Umbraco.Sys.ServerVariables.application){
if (!Umbraco.Sys.ServerVariables.application) {
return url;
}
var rnd = Umbraco.Sys.ServerVariables.application.version +"."+Umbraco.Sys.ServerVariables.application.cdf;
var _op = (url.indexOf("?")>0) ? "&" : "?";
var rnd = Umbraco.Sys.ServerVariables.application.version + "." + Umbraco.Sys.ServerVariables.application.cdf;
var _op = (url.indexOf("?") > 0) ? "&" : "?";
url = url + _op + "umb__rnd=" + rnd;
return url;
};
function convertVirtualPath(path) {
//make this work for virtual paths
if (path.startsWith("~/")) {
path = umbRequestHelper.convertVirtualToAbsolutePath(path);
}
return path;
}
var service = {
loadedAssets:{},
_getAssetPromise : function(path){
if(this.loadedAssets[path]){
loadedAssets: {},
_getAssetPromise: function (path) {
if (this.loadedAssets[path]) {
return this.loadedAssets[path];
}else{
} else {
var deferred = $q.defer();
this.loadedAssets[path] = {deferred: deferred, state: "new", path: path};
this.loadedAssets[path] = { deferred: deferred, state: "new", path: path };
return this.loadedAssets[path];
}
},
@@ -77,7 +86,7 @@ angular.module('umbraco.services')
//here we need to ensure the required application assets are loaded
if (initAssetsLoaded === false) {
var self = this;
self.loadJs(umbRequestHelper.getApiUrl("serverVarsJs", "", ""), $rootScope).then(function() {
self.loadJs(umbRequestHelper.getApiUrl("serverVarsJs", "", ""), $rootScope).then(function () {
initAssetsLoaded = true;
//now we need to go get the legacyTreeJs - but this can be done async without waiting.
@@ -106,30 +115,33 @@ angular.module('umbraco.services')
* @param {Number} timeout in milliseconds
* @returns {Promise} Promise object which resolves when the file has loaded
*/
loadCss : function(path, scope, attributes, timeout){
var asset = this._getAssetPromise(path); // $q.defer();
var t = timeout || 5000;
var a = attributes || undefined;
if(asset.state === "new"){
asset.state = "loading";
LazyLoad.css(appendRnd(path), function () {
if (!scope) {
asset.state = "loaded";
asset.deferred.resolve(true);
}else{
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
}
});
}else if(asset.state === "loaded"){
asset.deferred.resolve(true);
}
return asset.deferred.promise;
},
loadCss: function (path, scope, attributes, timeout) {
path = convertVirtualPath(path);
var asset = this._getAssetPromise(path); // $q.defer();
var t = timeout || 5000;
var a = attributes || undefined;
if (asset.state === "new") {
asset.state = "loading";
LazyLoad.css(appendRnd(path), function () {
if (!scope) {
asset.state = "loaded";
asset.deferred.resolve(true);
} else {
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
}
});
} else if (asset.state === "loaded") {
asset.deferred.resolve(true);
}
return asset.deferred.promise;
},
/**
* @ngdoc method
* @name umbraco.services.assetsService#loadJs
@@ -144,28 +156,30 @@ angular.module('umbraco.services')
* @param {Number} timeout in milliseconds
* @returns {Promise} Promise object which resolves when the file has loaded
*/
loadJs : function(path, scope, attributes, timeout){
loadJs: function (path, scope, attributes, timeout) {
path = convertVirtualPath(path);
var asset = this._getAssetPromise(path); // $q.defer();
var t = timeout || 5000;
var a = attributes || undefined;
if(asset.state === "new"){
if (asset.state === "new") {
asset.state = "loading";
LazyLoad.js(appendRnd(path), function () {
if (!scope) {
asset.state = "loaded";
asset.deferred.resolve(true);
}else{
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
}
if (!scope) {
asset.state = "loaded";
asset.deferred.resolve(true);
} else {
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
}
});
}else if(asset.state === "loaded"){
} else if (asset.state === "loaded") {
asset.deferred.resolve(true);
}
@@ -192,7 +206,7 @@ angular.module('umbraco.services')
throw "pathArray must be an array";
}
var nonEmpty = _.reject(pathArray, function(item) {
var nonEmpty = _.reject(pathArray, function (item) {
return item === undefined || item === "";
});
@@ -204,12 +218,14 @@ angular.module('umbraco.services')
//compile a list of promises
//blocking
_.each(nonEmpty, function(path){
_.each(nonEmpty, function (path) {
path = convertVirtualPath(path);
var asset = service._getAssetPromise(path);
//if not previously loaded, add to list of promises
if(asset.state !== "loaded")
{
if(asset.state === "new"){
if (asset.state !== "loaded") {
if (asset.state === "new") {
asset.state = "loading";
assets.push(asset);
}
@@ -223,21 +239,21 @@ angular.module('umbraco.services')
//gives a central monitoring of all assets to load
promise = $q.all(promises);
_.each(assets, function(asset){
_.each(assets, function (asset) {
LazyLoad.js(appendRnd(asset.path), function () {
if (!scope) {
asset.state = "loaded";
asset.deferred.resolve(true);
}else{
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
}
});
if (!scope) {
asset.state = "loaded";
asset.deferred.resolve(true);
} else {
asset.state = "loaded";
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
}
});
});
}else{
} else {
//return and resolve
var deferred = $q.defer();
promise = deferred.promise;
@@ -1,7 +1,7 @@
angular.module('umbraco.services')
.factory('gridService', function ($http, $q){
var configPath = "../config/grid.editors.config.js";
var configPath = Umbraco.Sys.ServerVariables.umbracoUrls.gridConfig;
var service = {
getGridEditors: function () {
return $http.get(configPath);
@@ -12,8 +12,10 @@ function macroService() {
/** parses the special macro syntax like <?UMBRACO_MACRO macroAlias="Map" /> and returns an object with the macro alias and it's parameters */
parseMacroSyntax: function (syntax) {
var expression = /(<\?UMBRACO_MACRO macroAlias=["']([\w\.]+?)["'][\s\S]+?)(\/>|>.*?<\/\?UMBRACO_MACRO>)/i;
//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 match = expression.exec(syntax);
if (!match || match.length < 3) {
return null;
@@ -157,4 +159,4 @@ function macroService() {
}
angular.module('umbraco.services').factory('macroService', macroService);
angular.module('umbraco.services').factory('macroService', macroService);
@@ -6,6 +6,30 @@
function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogService, notificationsService) {
return {
/**
* @ngdoc method
* @name umbraco.services.umbRequestHelper#convertVirtualToAbsolutePath
* @methodOf umbraco.services.umbRequestHelper
* @function
*
* @description
* This will convert a virtual path (i.e. ~/App_Plugins/Blah/Test.html ) to an absolute path
*
* @param {string} a virtual path, if this is already an absolute path it will just be returned, if this is a relative path an exception will be thrown
*/
convertVirtualToAbsolutePath: function(virtualPath) {
if (virtualPath.startsWith("/")) {
return virtualPath;
}
if (!virtualPath.startsWith("~/")) {
throw "The path " + virtualPath + " is not a virtual path";
}
if (!Umbraco.Sys.ServerVariables.application.applicationPath) {
throw "No applicationPath defined in Umbraco.ServerVariables.application.applicationPath";
}
return Umbraco.Sys.ServerVariables.application.applicationPath + virtualPath.trimStart("~/");
},
/**
* @ngdoc method
* @name umbraco.services.umbRequestHelper#dictionaryToQueryString
@@ -225,7 +249,7 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
// we have to just check for the existence of a string value but currently that is the best way to
// do this since it's very hacky/difficult to catch this on the server
if (data.indexOf("Maximum request length exceeded") >= 0) {
notificationsService.error("Server error", "The image file size was too big, check with your site administrator to adjust the maximum size allowed");
notificationsService.error("Server error", "The uploaded file was too large, check with your site administrator to adjust the maximum size allowed");
}
else if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) {
//show a ysod dialog
@@ -124,11 +124,12 @@ IFRAME {overflow:hidden;}
border: 1px dashed rgba(0,0,0,0);
}
.usky-grid .tb:hover .usky-row{
border-bottom:1px dashed rgba(182, 182, 182, 0.3);
.umb-grid .tb:hover .usky-row{
border-bottom:1px dashed rgba(182, 182, 182, 0) !important;
}
/*.usky-grid .selectedRow {
/*
.usky-grid .selectedRow {
border: 1px solid rgba(182, 182, 182, 0.3);
}*/
@@ -155,6 +156,8 @@ IFRAME {overflow:hidden;}
opacity: 0.3;
z-index: 50;
}
//special rule to ensure forms doesnt overrride (forms will align in a later release)
.umb-grid .cell-tools{width: 50px !important;}
.usky-grid .cell-tools.with-prompt {
width:200px;
@@ -233,7 +236,7 @@ IFRAME {overflow:hidden;}
.usky-grid .usky-control:hover{
/*border: 1px solid rgba(182, 182, 182, 0.3);*/
//border-bottom: 1px solid rgba(182, 182, 182, 0.3);
/*border-bottom: 1px solid rgba(182, 182, 182, 0.3);*/
}
.usky-grid .usky-control-placeholder:hover{
@@ -249,10 +252,40 @@ IFRAME {overflow:hidden;}
border: 1px dashed @blue !important;
}
.usky-grid .usky-control-inner{
min-height: 60px;
.usky-grid .warnhighlight > ins.item-label{border-color: @red; color: @red;}
.usky-grid .infohighlight > ins.item-label{border-color: @blue; color: @blue;}
.usky-grid ins.item-label {
position: absolute;
top: -22px;
left: -1px;
text-decoration: none;
padding: 0px 7px;
display:none;
font-size:0.8em;
background-color: white;
color: @grayLight;
border: 1px dashed @grayLight;
border-bottom: 1px solid white !important;
height: 20px;
overflow: hidden;
}
.usky-grid .usky-row-inner > ins.item-label{
top: -20px;
left: 0px;
}
.usky-grid .usky-control-inner.selectedControl , .usky-grid .usky-row-inner.selectedRow{
border: 1px dashed @grayLight;
> ins.item-label {
display: block;
z-index:100000;
}
}
// CONTROL PLACEHOLDER
@@ -504,6 +537,7 @@ IFRAME {overflow:hidden;}
/**************************************************************************************************/
.usky-grid .usky-cell{
padding-top: 5px;
padding-bottom: 15px;
}
@@ -522,14 +556,18 @@ IFRAME {overflow:hidden;}
border: 1px dashed transparent;
}
.usky-grid .usky-control-inner{
.usky-grid .usky-control-inner {
padding: 5px;
margin-right: 45px;
margin-bottom: 15px;
border: 1px dashed transparent;
min-height: 60px;
position:relative;
}
/**************************************************************************************************/
/* template */
/**************************************************************************************************/
+1 -1
View File
@@ -12,7 +12,7 @@ LazyLoad.js(
'lib/angular/angular-ui-sortable.js',
'lib/angular/tmhDynamicLocale.js',
'lib/angular-dynamic-locale/tmhDynamicLocale.min.js',
/* App-wide file-upload helper */
'lib/jquery-file-upload/jquery.fileupload.js',
@@ -8,7 +8,14 @@
*
*/
function LegacyController($scope, $routeParams, $element) {
$scope.legacyPath = decodeURIComponent($routeParams.url);
var url = decodeURIComponent($routeParams.url.toLowerCase().trimStart("javascript:"));
var toClean = "*(){}[];:<>\\|'\"";
for (var i = 0; i < toClean.length; i++) {
var reg = new RegExp("\\" + toClean[i], "g");
url = url.replace(reg, "");
}
$scope.legacyPath = url;
}
angular.module("umbraco").controller('Umbraco.LegacyController', LegacyController);
@@ -44,7 +44,7 @@
</a>
</div>
<div class="btn-group" ng-show="content.template && !isNew">
<div class="btn-group" ng-show="!isNew">
<a class="btn" ng-click="preview(content)">
<localize key="buttons_showPage">Preview page</localize>
</a>
@@ -6,7 +6,6 @@
name="name"
localize="placeholder"
class="umb-headline"
localize="placeholder"
select-on-focus
placeholder="{{placeholder}}"
ng-model="model"
@@ -0,0 +1,2 @@
<p class="red">Something went wrong with this editor, below is the data stored:</p>
<pre>{{control | json}}</pre>
@@ -8,7 +8,10 @@ angular.module("umbraco")
dialogService.macroPicker({
dialogData: {
richTextEditor: true,
macroData: $scope.control.value
macroData: $scope.control.value || {
macroAlias: $scope.control.editor.config && $scope.control.editor.config.macroAlias
? $scope.control.editor.config.macroAlias : ""
}
},
callback: function (data) {
$scope.control.value = {
@@ -1,6 +1,6 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.GridController",
function ($scope, $http, assetsService, $rootScope, dialogService, gridService, mediaResource, imageHelper, $timeout) {
function ($scope, $http, assetsService, $rootScope, dialogService, gridService, mediaResource, imageHelper, $timeout, umbRequestHelper) {
// Grid status variables
$scope.currentRow = null;
@@ -63,7 +63,7 @@ angular.module("umbraco")
handle: '.cell-tools-move',
connectWith: ".usky-cell",
forcePlaceholderSize: true,
tolerance:"pointer",
tolerance: "pointer",
zIndex: 999999999999999999,
scrollSensitivity: 100,
cursorAt: {
@@ -152,7 +152,7 @@ angular.module("umbraco")
key: undefined
};
$scope.addItemOverlay = function(event, area, index, key){
$scope.addItemOverlay = function (event, area, index, key) {
$scope.overlayMenu.area = area;
$scope.overlayMenu.index = index;
$scope.overlayMenu.style = {};
@@ -163,10 +163,10 @@ angular.module("umbraco")
var height = $(window).height();
var width = $(window).width();
if((height-offset.top) < 250){
if ((height - offset.top) < 250) {
$scope.overlayMenu.style.bottom = 0;
$scope.overlayMenu.style.top = "initial";
}else if(offset.top < 300){
} else if (offset.top < 300) {
$scope.overlayMenu.style.top = 190;
}
@@ -187,7 +187,7 @@ angular.module("umbraco")
$scope.model.value = angular.copy(template);
//default row data
_.forEach($scope.model.value.sections, function(section){
_.forEach($scope.model.value.sections, function (section) {
$scope.initSection(section);
});
};
@@ -221,14 +221,14 @@ angular.module("umbraco")
$scope.currentInfohighlightRow = null;
};
$scope.getAllowedLayouts = function(column){
$scope.getAllowedLayouts = function (column) {
var layouts = $scope.model.config.items.layouts;
if(column.allowed && column.allowed.length > 0){
return _.filter(layouts, function(layout){
if (column.allowed && column.allowed.length > 0) {
return _.filter(layouts, function (layout) {
return _.indexOf(column.allowed, layout.name) >= 0;
});
}else{
} else {
return layouts;
}
};
@@ -242,8 +242,8 @@ angular.module("umbraco")
row = $scope.initRow(row);
// Push the new row
if(row){
section.rows.push(row);
if (row) {
section.rows.push(row);
}
};
@@ -264,7 +264,7 @@ angular.module("umbraco")
gridItem: gridItem,
config: $scope.model.config,
itemType: itemType,
callback: function(data){
callback: function (data) {
gridItem.styles = data.styles;
gridItem.config = data.config;
@@ -286,11 +286,11 @@ angular.module("umbraco")
$scope.currentCell = null;
};
$scope.cellPreview = function(cell){
if(cell && cell.$allowedEditors){
$scope.cellPreview = function (cell) {
if (cell && cell.$allowedEditors) {
var editor = cell.$allowedEditors[0];
return editor.icon;
}else{
} else {
return "icon-layout";
}
};
@@ -355,7 +355,7 @@ angular.module("umbraco")
};
})();
$scope.addControl = function (editor, cell, index){
$scope.addControl = function (editor, cell, index) {
$scope.closeItemOverlay();
var newControl = {
@@ -369,18 +369,18 @@ angular.module("umbraco")
}
//populate control
$scope.initControl(newControl, index+1);
$scope.initControl(newControl, index + 1);
cell.controls.splice(index + 1, 0, newControl);
};
$scope.addTinyMce = function(cell){
$scope.addTinyMce = function (cell) {
var rte = $scope.getEditor("rte");
$scope.addControl(rte, cell);
};
$scope.getEditor = function(alias){
return _.find($scope.availableEditors, function(editor){return editor.alias === alias;});
$scope.getEditor = function (alias) {
return _.find($scope.availableEditors, function (editor) { return editor.alias === alias; });
};
$scope.removeControl = function (cell, $index) {
@@ -388,12 +388,12 @@ angular.module("umbraco")
cell.controls.splice($index, 1);
};
$scope.percentage = function(spans){
return (( spans/ $scope.model.config.items.columns ) *100).toFixed(1);
$scope.percentage = function (spans) {
return ((spans / $scope.model.config.items.columns) * 100).toFixed(1);
};
$scope.clearPrompt = function(scopedObject, e) {
$scope.clearPrompt = function (scopedObject, e) {
scopedObject.deletePrompt = false;
e.preventDefault();
e.stopPropagation();
@@ -416,68 +416,68 @@ angular.module("umbraco")
// *********************************************
// Init template + sections
// *********************************************
$scope.initContent = function() {
$scope.initContent = function () {
var clear = true;
//settings indicator shortcut
if($scope.model.config.items.config || $scope.model.config.items.styles){
if ($scope.model.config.items.config || $scope.model.config.items.styles) {
$scope.hasSettings = true;
}
//ensure the grid has a column value set, if nothing is found, set it to 12
if($scope.model.config.items.columns && angular.isString($scope.model.config.items.columns)){
if ($scope.model.config.items.columns && angular.isString($scope.model.config.items.columns)) {
$scope.model.config.items.columns = parseInt($scope.model.config.items.columns);
}else{
} else {
$scope.model.config.items.columns = 12;
}
if ($scope.model.value && $scope.model.value.sections && $scope.model.value.sections.length > 0) {
_.forEach($scope.model.value.sections, function(section, index){
_.forEach($scope.model.value.sections, function (section, index) {
if(section.grid > 0){
if (section.grid > 0) {
$scope.initSection(section);
//we do this to ensure that the grid can be reset by deleting the last row
if(section.rows.length > 0){
if (section.rows.length > 0) {
clear = false;
}
}else{
} else {
$scope.model.value.sections.splice(index, 1);
}
});
}else if($scope.model.config.items.templates && $scope.model.config.items.templates.length === 1){
} else if ($scope.model.config.items.templates && $scope.model.config.items.templates.length === 1) {
$scope.addTemplate($scope.model.config.items.templates[0]);
}
if(clear){
if (clear) {
$scope.model.value = undefined;
}
};
$scope.initSection = function(section){
$scope.initSection = function (section) {
section.$percentage = $scope.percentage(section.grid);
var layouts = $scope.model.config.items.layouts;
if(section.allowed && section.allowed.length > 0){
section.$allowedLayouts = _.filter(layouts, function(layout){
if (section.allowed && section.allowed.length > 0) {
section.$allowedLayouts = _.filter(layouts, function (layout) {
return _.indexOf(section.allowed, layout.name) >= 0;
});
}else{
} else {
section.$allowedLayouts = layouts;
}
if(!section.rows){
if (!section.rows) {
section.rows = [];
}else{
_.forEach(section.rows, function(row, index){
if(!row.$initialized){
} else {
_.forEach(section.rows, function (row, index) {
if (!row.$initialized) {
var initd = $scope.initRow(row);
//if init fails, remove
if(!initd){
if (!initd) {
section.rows.splice(index, 1);
}else{
} else {
section.rows[index] = initd;
}
}
@@ -489,25 +489,25 @@ angular.module("umbraco")
// *********************************************
// Init layout / row
// *********************************************
$scope.initRow = function(row){
$scope.initRow = function (row) {
//merge the layout data with the original config data
//if there are no config info on this, splice it out
var original = _.find($scope.model.config.items.layouts, function (o) { return o.name === row.name; });
if(!original){
if (!original) {
return null;
}else{
} else {
//make a copy to not touch the original config
original = angular.copy(original);
original.styles = row.styles;
original.config = row.config;
//sync area configuration
_.each(original.areas, function(area, areaIndex){
_.each(original.areas, function (area, areaIndex) {
if(area.grid > 0){
if (area.grid > 0) {
var currentArea = row.areas[areaIndex];
if (currentArea) {
@@ -516,14 +516,14 @@ angular.module("umbraco")
}
//copy over existing controls into the new areas
if(row.areas.length > areaIndex && row.areas[areaIndex].controls){
if (row.areas.length > areaIndex && row.areas[areaIndex].controls) {
area.controls = currentArea.controls;
_.forEach(area.controls, function(control, controlIndex){
_.forEach(area.controls, function (control, controlIndex) {
$scope.initControl(control, controlIndex);
});
}else{
} else {
area.controls = [];
}
@@ -532,19 +532,19 @@ angular.module("umbraco")
area.$uniqueId = $scope.setUniqueId();
//set editor permissions
if(!area.allowed || area.allowAll === true){
if (!area.allowed || area.allowAll === true) {
area.$allowedEditors = $scope.availableEditors;
area.$allowsRTE = true;
}else{
area.$allowedEditors = _.filter($scope.availableEditors, function(editor){
} else {
area.$allowedEditors = _.filter($scope.availableEditors, function (editor) {
return _.indexOf(area.allowed, editor.alias) >= 0;
});
if(_.indexOf(area.allowed,"rte")>=0){
if (_.indexOf(area.allowed, "rte") >= 0) {
area.$allowsRTE = true;
}
}
}else{
} else {
original.areas.splice(areaIndex, 1);
}
});
@@ -568,26 +568,41 @@ angular.module("umbraco")
// Init control
// *********************************************
$scope.initControl = function(control, index){
$scope.initControl = function (control, index) {
control.$index = index;
control.$uniqueId = $scope.setUniqueId();
if(!control.$editorPath){
var editorConfig = $scope.getEditor(control.editor.alias);
control.editor = editorConfig;
//error handling in case of missing editor..
//should only happen if stripped earlier
if (!control.editor) {
control.$editorPath = "views/propertyeditors/grid/editors/error.html";
}
//if its a path
if(_.indexOf(control.editor.view, "/") >= 0){
control.$editorPath = control.editor.view;
}else{
//use convention
control.$editorPath = "views/propertyeditors/grid/editors/" + control.editor.view + ".html";
if (!control.$editorPath) {
var editorConfig = $scope.getEditor(control.editor.alias);
if (editorConfig) {
control.editor = editorConfig;
//if its an absolute path
if (control.editor.view.startsWith("/") || control.editor.view.startsWith("~/")) {
control.$editorPath = umbRequestHelper.convertVirtualToAbsolutePath(control.editor.view);
}
else {
//use convention
control.$editorPath = "views/propertyeditors/grid/editors/" + control.editor.view + ".html";
}
}
else {
control.$editorPath = "views/propertyeditors/grid/editors/error.html";
}
}
};
gridService.getGridEditors().then(function(response){
gridService.getGridEditors().then(function (response) {
$scope.availableEditors = response.data;
$scope.contentReady = true;
@@ -1,4 +1,4 @@
<div ng-controller="Umbraco.PropertyEditors.GridController" class="usky-grid" id="usky-grid">
<div ng-controller="Umbraco.PropertyEditors.GridController" class="usky-grid umb-editor umb-grid" id="usky-grid">
<div ng-if="contentReady">
@@ -57,7 +57,8 @@
ng-mouseleave="disableCurrentRow()">
<!-- Row tool -->
<div class="row-tools" ng-animate="'fade'" ng-if="currentRow === row && currentControl === null">
<div class="row-tools" ng-animate="'fade'"
ng-if="currentRow === row && currentControl === null">
<!-- delete row -->
<div class="cell-tools-remove">
@@ -72,7 +73,7 @@
<a href ng-click="clearPrompt(row, $event)"><localize key="general_no" /></a>
</span>
<i ng-if="!row.deletePrompt" class="icon icon-trash"></i>
<i ng-if="!row.deletePrompt" class="icon icon-trash" title="@general_delete" localize="title"></i>
</div>
</div>
@@ -81,7 +82,7 @@
ng-mouseover="setInfohighlightRow(row)"
ng-mouseleave="disableInfohighlightRow(row)"
href>
<i class="icon icon-navigation"></i>
<i class="icon icon-navigation" title="@general_move" localize="title"></i>
</a>
</div>
@@ -91,7 +92,7 @@
ng-mouseleave="disableInfohighlightRow(row)"
ng-click="editGridItemSettings(row, 'row')"
href>
<i class="icon icon-settings"></i>
<i class="icon icon-settings" title="@grid_settings" localize="title"></i>
</a>
</div>
@@ -99,8 +100,14 @@
<!-- row container -->
<div class="{{row.cssClass}} mainContainer usky-row-inner"
ng-class="{last:$last,first:$first,warnhighlight:currentWarnhighlightRow == row, infohighlight:currentInfohighlightRow == row}">
ng-class="{last:$last,
first:$first,
warnhighlight:currentWarnhighlightRow == row,
infohighlight:currentInfohighlightRow == row,
selectedRow:currentRow == row && currentControl === null}">
<ins class="item-label">{{row.name}}</ins>
<div class="mainTb">
<div class="tb">
<div class="tr">
@@ -138,7 +145,7 @@
<a href ng-click="clearPrompt(control, $event)"><localize key="general_no" /></a>
</span>
<i ng-if="!control.deletePrompt" class="icon icon-trash"></i>
<i ng-if="!control.deletePrompt" class="icon icon-trash" title="@general_delete" localize="title"></i>
</div>
</div>
@@ -148,7 +155,7 @@
ng-mouseover="setInfohighlightControl(control)"
ng-mouseleave="disableInfohighlightControl(control)"
href>
<i class="icon icon-navigation"></i>
<i class="icon icon-navigation" title="@general_move" localize="title"></i>
</a>
</div>
@@ -159,7 +166,7 @@
ng-mouseleave="disableInfohighlightArea(area)"
ng-click="editGridItemSettings(area, 'cell')"
href>
<i class="icon icon-settings"></i>
<i class="icon icon-settings" title="@grid_settings" localize="title"></i>
</a>
</div>
@@ -169,6 +176,9 @@
infohighlight:currentInfohighlightControl == control,
warnhighlight:currentWarnhighlightControl == control,
selectedControl:currentControl == control}">
<ins class="item-label">{{control.editor.name}}</ins>
<!-- Redering the editor for specific control -->
<div ng-if="control && control.$editorPath"
ng-include="control.$editorPath"
@@ -191,7 +201,7 @@
ng-animate="'fade'">
<a class="iconBox" href ng-click="addItemOverlay($event, area, 0, area.$uniqueId);">
<i class=" icon icon-add"></i>
<i class=" icon icon-add" title="@general_add" localize="title"></i>
</a>
<div ng-if="overlayMenu.show && overlayMenu.key == area.$uniqueId" ng-include="'views/propertyeditors/grid/dialogs/additem.html'"></div>
@@ -1,7 +1,7 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.RTEController",
function ($rootScope, $scope, $q, dialogService, $log, imageHelper, assetsService, $timeout, tinyMceService, angularHelper, stylesheetResource) {
$scope.isLoading = true;
//To id the html textarea we need to use the datetime ticks because we can have multiple rte's per a single property alias
@@ -125,6 +125,31 @@ angular.module("umbraco")
if (tinyMceConfig.customConfig) {
//if there is some custom config, we need to see if the string value of each item might actually be json and if so, we need to
// convert it to json instead of having it as a string since this is what tinymce requires
for (var i in tinyMceConfig.customConfig) {
var val = tinyMceConfig.customConfig[i];
if (val) {
val = val.toString().trim();
if (val.detectIsJson()) {
try {
tinyMceConfig.customConfig[i] = JSON.parse(val);
//now we need to check if this custom config key is defined in our baseline, if it is we don't want to
//overwrite the baseline config item if it is an array, we want to concat the items in the array, otherwise
//if it's an object it will overwrite the baseline
if (angular.isArray(baseLineConfigObj[i]) && angular.isArray(tinyMceConfig.customConfig[i])) {
//concat it and below this concat'd array will overwrite the baseline in angular.extend
tinyMceConfig.customConfig[i] = baseLineConfigObj[i].concat(tinyMceConfig.customConfig[i]);
}
}
catch (e) {
//cannot parse, we'll just leave it
}
}
}
}
angular.extend(baseLineConfigObj, tinyMceConfig.customConfig);
}
@@ -39,6 +39,18 @@ describe('macro service tests', function () {
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' />");
expect(result).not.toBeNull();
expect(result.macroAlias).toBe("Map Test [Hello\\World]");
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 body', function () {
+7 -7
View File
@@ -115,9 +115,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
</Reference>
<Reference Include="ClientDependency.Core, Version=1.8.2.1, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="ClientDependency.Core, Version=1.8.3.1, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency.1.8.2.1\lib\net45\ClientDependency.Core.dll</HintPath>
<HintPath>..\packages\ClientDependency.1.8.3.1\lib\net45\ClientDependency.Core.dll</HintPath>
</Reference>
<Reference Include="ClientDependency.Core.Mvc, Version=1.8.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -127,9 +127,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\dotless.1.4.1.0\lib\dotless.Core.dll</HintPath>
</Reference>
<Reference Include="Examine">
<HintPath>..\packages\Examine.0.1.60.2941\lib\Examine.dll</HintPath>
<Private>True</Private>
<Reference Include="Examine, Version=0.1.61.2941, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.61.2941\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -2547,9 +2547,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7220</DevelopmentServerPort>
<DevelopmentServerPort>7230</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7220</IISUrl>
<IISUrl>http://localhost:7230</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -65,7 +65,7 @@
if(cfg != null)
foreach (JProperty property in cfg.Properties()) {
attrs.Add(property.Name + "='" + property.Value.ToString() + "'");
attrs.Add(property.Name + "=\"" + property.Value.ToString() + "\"");
}
JObject style = contentItem.styles;
@@ -76,7 +76,7 @@
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
if (cssVals.Any())
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
attrs.Add("style=\"" + string.Join(" ", cssVals) + "\"");
}
return new MvcHtmlString(string.Join(" ", attrs));
@@ -65,7 +65,7 @@
if(cfg != null)
foreach (JProperty property in cfg.Properties()) {
attrs.Add(property.Name + "='" + property.Value.ToString() + "'");
attrs.Add(property.Name + "=\"" + property.Value.ToString() + "\"");
}
JObject style = contentItem.styles;
@@ -76,7 +76,7 @@
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
if (cssVals.Any())
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
attrs.Add("style=\"" + string.Join(" ", cssVals) + "\"");
}
return new MvcHtmlString(string.Join(" ", attrs));
@@ -132,10 +132,15 @@
finder or by the alt. template. Set this option to true to preserve the template set by the finder
or by the alt. template, in case of an internal redirect.
(false by default, and in fact should remain false unless you know what you're doing)
@disableAlternativeTemplates
By default you can add a altTemplate querystring or append a template name to the current URL which
will make Umbraco render the content on the current page with the template you requested, for example:
http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the
"About Us" page with a template with the alias Home. Setting this setting to true stops that behavior
-->
<web.routing
trySkipIisCustomErrors="false"
internalRedirectPreservesTemplate="false">
internalRedirectPreservesTemplate="false" disableAlternativeTemplates="false">
</web.routing>
</settings>
@@ -290,10 +290,15 @@
finder or by the alt. template. Set this option to true to preserve the template set by the finder
or by the alt. template, in case of an internal redirect.
(false by default, and in fact should remain false unless you know what you're doing)
@disableAlternativeTemplates
By default you can add a altTemplate querystring or append a template name to the current URL which
will make Umbraco render the content on the current page with the template you requested, for example:
http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the
"About Us" page with a template with the alias Home. Setting this setting to true stops that behavior
-->
<web.routing
trySkipIisCustomErrors="false"
internalRedirectPreservesTemplate="false">
internalRedirectPreservesTemplate="false" disableAlternativeTemplates="false">
</web.routing>
</settings>
+2 -2
View File
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
<package id="ClientDependency" version="1.8.2.1" targetFramework="net45" />
<package id="ClientDependency" version="1.8.3.1" targetFramework="net45" />
<package id="ClientDependency-Mvc" version="1.8.0.0" targetFramework="net45" />
<package id="dotless" version="1.4.1.0" targetFramework="net45" />
<package id="Examine" version="0.1.60.2941" targetFramework="net45" />
<package id="Examine" version="0.1.61.2941" targetFramework="net45" />
<package id="ImageProcessor" version="1.9.5.0" targetFramework="net45" />
<package id="ImageProcessor.Web" version="3.3.1.0" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net40" />
@@ -58,6 +58,9 @@
"umbracoUrls": {
"authenticationApiBaseUrl": "@(Url.GetUmbracoApiServiceBaseUrl<AuthenticationController>(controller => controller.PostLogin(null)))",
"serverVarsJs": '@Url.GetUrlWithCacheBust("ServerVariables", "BackOffice")'
},
"application": {
"applicationPath" : "@Context.Request.ApplicationPath"
}
};
</script>
@@ -51,7 +51,7 @@
</thead>
<tbody data-bind="foreach: domains">
<tr>
<td valign="top"><input class="domain" data-bind="value: Name, uniqueName: true" /><input type="hidden" value="0" /></td>
<td valign="top"><input class="domain" data-bind="value: Name, uniqueName: true" /><input type="hidden" value="0" data-bind="uniqueName: true"/></td>
<td valign="top"><select class="language" data-bind="options: $parent.languages, optionsText: 'Code', optionsValue: 'Id', value: Lang, uniqueName: true"></select></td>
<td valign="top"><a href="#" class="btn btn-danger" data-bind="click: $parent.removeDomain"><i class="icon icon-trash"></i></a></td>
</tr>
+22 -14
View File
@@ -1,16 +1,24 @@
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="endPreview.aspx.cs" Inherits="umbraco.presentation.endPreview" %>
<%@ Page Language="C#" AutoEventWireup="true" Inherits="System.Web.UI.Page" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
Redirecting...
</div>
</form>
</body>
</html>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
global::umbraco.presentation.preview.PreviewContent.ClearPreviewCookie();
if (!Uri.IsWellFormedUriString(Request.QueryString["redir"], UriKind.Relative))
{
Response.Redirect("/", true);
}
Uri url;
if (!Uri.TryCreate(Request.QueryString["redir"], UriKind.Relative, out url))
{
Response.Redirect("/", true);
}
Response.Redirect(url.ToString(), true);
}
</script>
@@ -52,9 +52,15 @@
ko.applyBindings(self);
$.validator.addMethod("domain", function (value, element, param) {
// beware! encode('test') == 'test-'
// read eg https://rt.cpan.org/Public/Bug/Display.html?id=94347
value = punycode.encode(value);
var re = /^(http[s]?:\/\/)?([-\w]+(\.[-\w]+)*)(:\d+)?(\/[-\w]*)?$/gi;
return this.optional(element) || re.test(value);
// that regex is best-effort and certainly not exact
var re = /^(http[s]?:\/\/)?([-\w]+(\.[-\w]+)*)(:\d+)?(\/[-\w]*|-)?$/gi;
var isopt = this.optional(element);
var retest = re.test(value);
var ret = isopt || retest;
return ret;
}, self._opts.invalidDomain);
$.validator.addMethod("duplicate", function (value, element, param) {
@@ -85,7 +91,7 @@
return false;
var mask = $('#komask');
var masked = mask.next();
var masked = mask.parent();
mask.height(masked.height());
mask.width(masked.width());
mask.show();
+264 -178
View File
@@ -14,7 +14,11 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Manifest;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Security;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.Trees;
@@ -30,6 +34,7 @@ namespace Umbraco.Web.Editors
/// A controller to render out the default back office view and JS results
/// </summary>
[UmbracoUseHttps]
[DisableClientCache]
public class BackOfficeController : UmbracoController
{
/// <summary>
@@ -62,7 +67,9 @@ namespace Umbraco.Web.Editors
{
var cultureInfo = culture == null
//if the user is logged in, get their culture, otherwise default to 'en'
? User.Identity.IsAuthenticated ? Security.CurrentUser.GetUserCulture(Services.TextService) : CultureInfo.GetCultureInfo("en")
? User.Identity.IsAuthenticated && User.Identity is UmbracoBackOfficeIdentity
? Security.CurrentUser.GetUserCulture(Services.TextService)
: CultureInfo.GetCultureInfo("en")
: CultureInfo.GetCultureInfo(culture);
var textForCulture = Services.TextService.GetAllStoredValues(cultureInfo)
@@ -78,11 +85,11 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <returns></returns>
[MinifyJavaScriptResult(Order = 0)]
[OutputCache(Order = 1, VaryByParam = "none", Location = OutputCacheLocation.Any, Duration = 5000)]
[OutputCache(Order = 1, VaryByParam = "none", Location = OutputCacheLocation.Server, Duration = 5000)]
public JavaScriptResult Application()
{
var plugins = new DirectoryInfo(Server.MapPath("~/App_Plugins"));
var parser = new ManifestParser(plugins);
var parser = new ManifestParser(plugins, ApplicationContext.ApplicationCache.RuntimeCache);
var initJs = new JsInitialization(parser);
var initCss = new CssInitialization(parser);
@@ -103,16 +110,75 @@ namespace Umbraco.Web.Editors
[HttpGet]
public JsonNetResult GetManifestAssetList()
{
var plugins = new DirectoryInfo(Server.MapPath("~/App_Plugins"));
var parser = new ManifestParser(plugins);
var initJs = new JsInitialization(parser);
var initCss = new CssInitialization(parser);
var jsResult = initJs.GetJavascriptInitializationArray(HttpContext, new JArray());
var cssResult = initCss.GetStylesheetInitializationArray(HttpContext);
Func<JArray> getResult = () =>
{
var plugins = new DirectoryInfo(Server.MapPath("~/App_Plugins"));
var parser = new ManifestParser(plugins, ApplicationContext.ApplicationCache.RuntimeCache);
var initJs = new JsInitialization(parser);
var initCss = new CssInitialization(parser);
var jsResult = initJs.GetJavascriptInitializationArray(HttpContext, new JArray());
var cssResult = initCss.GetStylesheetInitializationArray(HttpContext);
ManifestParser.MergeJArrays(jsResult, cssResult);
return jsResult;
};
ManifestParser.MergeJArrays(jsResult, cssResult);
//cache the result if debugging is disabled
var result = HttpContext.IsDebuggingEnabled
? getResult()
: ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem<JArray>(
typeof (BackOfficeController) + "GetManifestAssetList",
() => getResult(),
new TimeSpan(0, 10, 0));
return new JsonNetResult {Data = jsResult, Formatting = Formatting.Indented};
return new JsonNetResult { Data = result, Formatting = Formatting.Indented };
}
[UmbracoAuthorize(Order = 0)]
[HttpGet]
public JsonNetResult GetGridConfig()
{
Func<List<GridEditor>> getResult = () =>
{
var editors = new List<GridEditor>();
var gridConfig = Server.MapPath("~/Config/grid.editors.config.js");
if (System.IO.File.Exists(gridConfig))
{
try
{
var arr = JArray.Parse(System.IO.File.ReadAllText(gridConfig));
//ensure the contents parse correctly to objects
var parsed = ManifestParser.GetGridEditors(arr);
editors.AddRange(parsed);
}
catch (Exception ex)
{
LogHelper.Error<BackOfficeController>("Could not parse the contents of grid.editors.config.js into a JSON array", ex);
}
}
var plugins = new DirectoryInfo(Server.MapPath("~/App_Plugins"));
var parser = new ManifestParser(plugins, ApplicationContext.ApplicationCache.RuntimeCache);
var builder = new ManifestBuilder(ApplicationContext.ApplicationCache.RuntimeCache, parser);
foreach (var gridEditor in builder.GridEditors)
{
//no duplicates! (based on alias)
if (editors.Contains(gridEditor) == false)
{
editors.Add(gridEditor);
}
}
return editors;
};
//cache the result if debugging is disabled
var result = HttpContext.IsDebuggingEnabled
? getResult()
: ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem<List<GridEditor>>(
typeof(BackOfficeController) + "GetGridConfig",
() => getResult(),
new TimeSpan(0, 10, 0));
return new JsonNetResult { Data = result, Formatting = Formatting.Indented };
}
/// <summary>
@@ -121,172 +187,178 @@ namespace Umbraco.Web.Editors
/// <returns></returns>
[UmbracoAuthorize(Order = 0)]
[MinifyJavaScriptResult(Order = 1)]
[OutputCache(Order = 2, VaryByParam = "none", Location = OutputCacheLocation.Any, Duration = 5000)]
public JavaScriptResult ServerVariables()
{
//authenticationApiBaseUrl
//now we need to build up the variables
var d = new Dictionary<string, object>
Func<Dictionary<string, object>> getResult = () => new Dictionary<string, object>
{
{
"umbracoUrls", new Dictionary<string, object>
{
"umbracoUrls", new Dictionary<string, object>
{
{"legacyTreeJs", Url.Action("LegacyTreeJs", "BackOffice")},
{"manifestAssetList", Url.Action("GetManifestAssetList", "BackOffice")},
{"serverVarsJs", Url.Action("Application", "BackOffice")},
//API URLs
{
"embedApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<RteEmbedController>(
controller => controller.GetEmbed("",0,0))
},
{
"userApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<UserController>(
controller => controller.PostDisableUser(0))
},
{
"contentApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ContentController>(
controller => controller.PostSave(null))
},
{
"mediaApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MediaController>(
controller => controller.GetRootMedia())
},
{
"imagesApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ImagesController>(
controller => controller.GetBigThumbnail(0))
},
{
"sectionApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<SectionController>(
controller => controller.GetSections())
},
{
"treeApplicationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ApplicationTreeController>(
controller => controller.GetApplicationTrees(null, null, null))
},
{
"contentTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ContentTypeController>(
controller => controller.GetAllowedChildren(0))
},
{
"mediaTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MediaTypeController>(
controller => controller.GetAllowedChildren(0))
},
{
"macroApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MacroController>(
controller => controller.GetMacroParameters(0))
},
{
"authenticationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<AuthenticationController>(
controller => controller.PostLogin(null))
},
{
"currentUserApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<CurrentUserController>(
controller => controller.GetMembershipProviderConfig())
},
{
"legacyApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<LegacyController>(
controller => controller.DeleteLegacyItem(null, null, null))
},
{
"entityApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<EntityController>(
controller => controller.GetById(0, UmbracoEntityTypes.Media))
},
{
"dataTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<DataTypeController>(
controller => controller.GetById(0))
},
{
"dashboardApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<DashboardController>(
controller => controller.GetDashboard(null))
},
{
"logApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<LogController>(
controller => controller.GetEntityLog(0))
},
{
"memberApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MemberController>(
controller => controller.GetByKey(Guid.Empty))
},
{
"packageInstallApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<PackageInstallController>(
controller => controller.Fetch(string.Empty))
},
{
"rteApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<RichTextPreValueController>(
controller => controller.GetConfiguration())
},
{
"stylesheetApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<StylesheetController>(
controller => controller.GetAll())
},
{
"memberTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MemberTypeController>(
controller => controller.GetAllTypes())
},
{
"updateCheckApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<UpdateCheckController>(
controller => controller.GetCheck())
},
{
"tagApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<TagsController>(
controller => controller.GetAllTags(null))
},
{
"memberTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MemberTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"mediaTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MediaTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"contentTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ContentTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"tagsDataBaseUrl", Url.GetUmbracoApiServiceBaseUrl<TagsDataController>(
controller => controller.GetTags(""))
},
{
"examineMgmtBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ExamineManagementApiController>(
controller => controller.GetIndexerDetails())
},
{
"xmlDataIntegrityBaseUrl", Url.GetUmbracoApiServiceBaseUrl<XmlDataIntegrityController>(
controller => controller.CheckContentXmlTable())
}
}
},
{
"umbracoSettings", new Dictionary<string, object>
{
{"umbracoPath", GlobalSettings.Path},
{"mediaPath", IOHelper.ResolveUrl(SystemDirectories.Media).TrimEnd('/')},
{"appPluginsPath", IOHelper.ResolveUrl(SystemDirectories.AppPlugins).TrimEnd('/')},
{
"imageFileTypes",
string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes)
},
{"keepUserLoggedIn", UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn},
}
},
{
"umbracoPlugins", new Dictionary<string, object>
{
{"trees", GetTreePluginsMetaData()}
}
},
{"isDebuggingEnabled", HttpContext.IsDebuggingEnabled},
{
"application", GetApplicationState()
{"legacyTreeJs", Url.Action("LegacyTreeJs", "BackOffice")},
{"manifestAssetList", Url.Action("GetManifestAssetList", "BackOffice")},
{"gridConfig", Url.Action("GetGridConfig", "BackOffice")},
{"serverVarsJs", Url.Action("Application", "BackOffice")},
//API URLs
{
"embedApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<RteEmbedController>(
controller => controller.GetEmbed("", 0, 0))
},
{
"userApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<UserController>(
controller => controller.PostDisableUser(0))
},
{
"contentApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ContentController>(
controller => controller.PostSave(null))
},
{
"mediaApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MediaController>(
controller => controller.GetRootMedia())
},
{
"imagesApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ImagesController>(
controller => controller.GetBigThumbnail(0))
},
{
"sectionApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<SectionController>(
controller => controller.GetSections())
},
{
"treeApplicationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ApplicationTreeController>(
controller => controller.GetApplicationTrees(null, null, null))
},
{
"contentTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ContentTypeController>(
controller => controller.GetAllowedChildren(0))
},
{
"mediaTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MediaTypeController>(
controller => controller.GetAllowedChildren(0))
},
{
"macroApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MacroController>(
controller => controller.GetMacroParameters(0))
},
{
"authenticationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<AuthenticationController>(
controller => controller.PostLogin(null))
},
{
"currentUserApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<CurrentUserController>(
controller => controller.GetMembershipProviderConfig())
},
{
"legacyApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<LegacyController>(
controller => controller.DeleteLegacyItem(null, null, null))
},
{
"entityApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<EntityController>(
controller => controller.GetById(0, UmbracoEntityTypes.Media))
},
{
"dataTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<DataTypeController>(
controller => controller.GetById(0))
},
{
"dashboardApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<DashboardController>(
controller => controller.GetDashboard(null))
},
{
"logApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<LogController>(
controller => controller.GetEntityLog(0))
},
{
"memberApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MemberController>(
controller => controller.GetByKey(Guid.Empty))
},
{
"packageInstallApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<PackageInstallController>(
controller => controller.Fetch(string.Empty))
},
{
"rteApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<RichTextPreValueController>(
controller => controller.GetConfiguration())
},
{
"stylesheetApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<StylesheetController>(
controller => controller.GetAll())
},
{
"memberTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MemberTypeController>(
controller => controller.GetAllTypes())
},
{
"updateCheckApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<UpdateCheckController>(
controller => controller.GetCheck())
},
{
"tagApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<TagsController>(
controller => controller.GetAllTags(null))
},
{
"memberTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MemberTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"mediaTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MediaTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"contentTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ContentTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"tagsDataBaseUrl", Url.GetUmbracoApiServiceBaseUrl<TagsDataController>(
controller => controller.GetTags(""))
},
{
"examineMgmtBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ExamineManagementApiController>(
controller => controller.GetIndexerDetails())
},
{
"xmlDataIntegrityBaseUrl", Url.GetUmbracoApiServiceBaseUrl<XmlDataIntegrityController>(
controller => controller.CheckContentXmlTable())
}
}
};
},
{
"umbracoSettings", new Dictionary<string, object>
{
{"umbracoPath", GlobalSettings.Path},
{"mediaPath", IOHelper.ResolveUrl(SystemDirectories.Media).TrimEnd('/')},
{"appPluginsPath", IOHelper.ResolveUrl(SystemDirectories.AppPlugins).TrimEnd('/')},
{
"imageFileTypes",
string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes)
},
{"keepUserLoggedIn", UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn},
}
},
{
"umbracoPlugins", new Dictionary<string, object>
{
{"trees", GetTreePluginsMetaData()}
}
},
{"isDebuggingEnabled", HttpContext.IsDebuggingEnabled},
{"application", GetApplicationState()}
};
//cache the result if debugging is disabled
var result = HttpContext.IsDebuggingEnabled
? getResult()
: ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem<Dictionary<string, object>>(
typeof(BackOfficeController) + "ServerVariables",
() => getResult(),
new TimeSpan(0, 10, 0));
return JavaScript(ServerVariablesParser.Parse(d));
return JavaScript(ServerVariablesParser.Parse(result));
}
/// <summary>
/// Returns the server variables regarding the application state
/// </summary>
/// <returns></returns>
private Dictionary<string, object> GetApplicationState()
{
if (ApplicationContext.IsConfigured == false)
@@ -303,6 +375,8 @@ namespace Umbraco.Web.Editors
app.Add("version", version);
app.Add("cdf", ClientDependency.Core.Config.ClientDependencySettings.Instance.Version);
//useful for dealing with virtual paths on the client side when hosted in virtual directories especially
app.Add("applicationPath", HttpContext.Request.ApplicationPath.EnsureEndsWith('/'));
return app;
}
@@ -339,18 +413,30 @@ namespace Umbraco.Web.Editors
/// <returns></returns>
[UmbracoAuthorize(Order = 0)]
[MinifyJavaScriptResult(Order = 1)]
[OutputCache(Order = 2, VaryByParam = "none", Location = OutputCacheLocation.Any, Duration = 5000)]
public JavaScriptResult LegacyTreeJs()
{
var javascript = new StringBuilder();
javascript.AppendLine(LegacyTreeJavascript.GetLegacyTreeJavascript());
javascript.AppendLine(LegacyTreeJavascript.GetLegacyIActionJavascript());
//add all of the menu blocks
foreach (var file in GetLegacyActionJs(LegacyJsActionType.JsBlock))
{
Func<string> getResult = () =>
{
javascript.AppendLine(file);
}
return JavaScript(javascript.ToString());
var javascript = new StringBuilder();
javascript.AppendLine(LegacyTreeJavascript.GetLegacyTreeJavascript());
javascript.AppendLine(LegacyTreeJavascript.GetLegacyIActionJavascript());
//add all of the menu blocks
foreach (var file in GetLegacyActionJs(LegacyJsActionType.JsBlock))
{
javascript.AppendLine(file);
}
return javascript.ToString();
};
//cache the result if debugging is disabled
var result = HttpContext.IsDebuggingEnabled
? getResult()
: ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem<string>(
typeof(BackOfficeController) + "LegacyTreeJs",
() => getResult(),
new TimeSpan(0, 10, 0));
return JavaScript(result);
}
/// <summary>
@@ -40,10 +40,8 @@ namespace Umbraco.Web.Editors
response = client.DownloadString(new Uri(googleWebFontAPIURL));
}
var resp = new HttpResponseMessage()
{
Content = new StringContent(response)
};
var resp = Request.CreateResponse();
resp.Content = new StringContent(response);
resp.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return resp;
@@ -69,10 +67,8 @@ namespace Umbraco.Web.Editors
}
// Response
var resp = new HttpResponseMessage()
{
Content = new StringContent("{" + String.Join(",", parameters) + "}")
};
var resp = Request.CreateResponse();
resp.Content = new StringContent("{" + String.Join(",", parameters) + "}");
resp.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return resp;
}
@@ -89,10 +85,8 @@ namespace Umbraco.Web.Editors
// Save and compile styles
CanvasDesignerUtility.SaveAndPublishStyle(parameters, pageId, inherited);
var resp = new HttpResponseMessage()
{
Content = new StringContent("ok")
};
var resp = Request.CreateResponse();
resp.Content = new StringContent("ok");
resp.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return resp;
@@ -106,10 +100,8 @@ namespace Umbraco.Web.Editors
CanvasDesignerUtility.DeleteStyle(int.Parse(pageId));
var resp = new HttpResponseMessage()
{
Content = new StringContent("ok")
};
var resp = Request.CreateResponse();
resp.Content = new StringContent("ok");
resp.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
return resp;
+14 -16
View File
@@ -1,19 +1,17 @@
using System.Globalization;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Web.Models;
namespace Umbraco.Web
namespace Umbraco.Web
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Web.Models;
internal static class ImageCropperBaseExtensions
{
@@ -88,7 +86,7 @@ namespace Umbraco.Web
cropUrl.Append("?center=" + cropDataSet.FocalPoint.Top.ToString(CultureInfo.InvariantCulture) + "," + cropDataSet.FocalPoint.Left.ToString(CultureInfo.InvariantCulture));
cropUrl.Append("&mode=crop");
}
else if (crop != null && crop.Coordinates != null)
else if (crop != null && crop.Coordinates != null && preferFocalPoint == false)
{
cropUrl.Append("?crop=");
cropUrl.Append(crop.Coordinates.X1.ToString(CultureInfo.InvariantCulture)).Append(",");
@@ -1,16 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Web.Models;
using Umbraco.Web.PropertyEditors;
namespace Umbraco.Web
namespace Umbraco.Web
{
using System.Globalization;
using System.Text;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Web.Models;
/// <summary>
/// Provides extension methods for getting ImageProcessor Url from the core Image Cropper property editor
@@ -21,13 +21,13 @@ namespace Umbraco.Web.Install.Controllers
[HttpInstallAuthorize]
public class InstallApiController : ApiController
{
protected InstallApiController()
public InstallApiController()
: this(UmbracoContext.Current)
{
}
protected InstallApiController(UmbracoContext umbracoContext)
public InstallApiController(UmbracoContext umbracoContext)
{
if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
UmbracoContext = umbracoContext;
@@ -0,0 +1,60 @@
using System;
using System.Globalization;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Models
{
public static class ContentExtensions
{
/// <summary>
/// Gets the culture that would be selected to render a specified content,
/// within the context of a specified current request.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="current">The request Uri.</param>
/// <returns>The culture that would be selected to render the content.</returns>
public static CultureInfo GetCulture(this IContent content, Uri current = null)
{
return GetCulture(UmbracoContext.Current,
ApplicationContext.Current.Services.LocalizationService,
content.Id, content.Path,
current);
}
/// <summary>
/// Gets the culture that would be selected to render a specified content,
/// within the context of a specified current request.
/// </summary>
/// <param name="umbracoContext">An <see cref="UmbracoContext"/> instance.</param>
/// <param name="localizationService">An <see cref="ILocalizationService"/> implementation.</param>
/// <param name="contentId">The content identifier.</param>
/// <param name="contentPath">The content path.</param>
/// <param name="current">The request Uri.</param>
/// <returns>The culture that would be selected to render the content.</returns>
internal static CultureInfo GetCulture(UmbracoContext umbracoContext, ILocalizationService localizationService,
int contentId, string contentPath, Uri current)
{
var route = umbracoContext.ContentCache.GetRouteById(contentId); // cached
var pos = route.IndexOf('/');
var domain = pos == 0
? null
: DomainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current).Domain;
if (domain == null)
{
var defaultLanguage = localizationService.GetAllLanguages().FirstOrDefault();
return defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.IsoCode);
}
var wcDomain = DomainHelper.FindWildcardDomainInPath(DomainHelper.GetAllDomains(true), contentPath, domain.RootNodeId);
return wcDomain == null
? new CultureInfo(domain.Language.CultureAlias)
: new CultureInfo(wcDomain.Language.CultureAlias);
}
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ namespace Umbraco.Web.Models
[DataMember(Name = "group")]
public string Group { get; set; }
[DataMember(Name = "nodeCount`")]
[DataMember(Name = "nodeCount")]
public int NodeCount { get; set; }
}
}
@@ -0,0 +1,25 @@
using System;
using System.Web;
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Will ensure that client-side cache does not occur by sending the correct response headers
/// </summary>
public class DisableClientCacheAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (filterContext.IsChildAction) base.OnResultExecuting(filterContext);
filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetNoStore();
base.OnResultExecuting(filterContext);
}
}
}
@@ -1,5 +1,4 @@
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc;
using System.Web.UI;
using ClientDependency.Core;
using ClientDependency.Core.CompositeFiles;
@@ -7,7 +6,7 @@ using ClientDependency.Core.CompositeFiles;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Minifies and caches the result for the JavaScriptResult
/// Minifies the result for the JavaScriptResult
/// </summary>
/// <remarks>
/// Only minifies in release mode

Some files were not shown because too many files have changed in this diff Show More