Compare commits

..

31 Commits

Author SHA1 Message Date
Niels Lyngsø 8c264b0815 correcting k to K 2019-05-03 09:54:09 +02:00
Peter Duncanson 12ecc5d650 Index types added in this commit (see: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types) 2019-04-29 00:40:28 +01:00
Peter Duncanson 26dce2c99d Strengthened the types up in the appstate.service with some interfaces and index types as we use a funky accessor method in this one
See: https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types for details on index types.
2019-04-29 00:39:41 +01:00
Peter Duncanson 4649811abd Initial conversion of appstate.service to typescript 2019-04-28 23:38:21 +01:00
Peter Duncanson 6af5795a1d Renamed appstate.service to .ts extension 2019-04-28 22:31:17 +01:00
Peter Duncanson 68f6e27d20 Refactored the namespaces for Overlay.service 2019-04-26 18:28:10 +01:00
Peter Duncanson ef035df8b8 Updated formHelper to use the new Notification namespaces, also binned off iHasScope 2019-04-26 18:27:38 +01:00
Peter Duncanson 35e6a64e73 Changed namespace to be umbrac.services.Notifications and removed the old alias too 2019-04-26 18:22:27 +01:00
Peter Duncanson 5bb6ffc654 Added some missed types to Overlay and changed to umbraco.services.Overlays namespace 2019-04-26 18:19:30 +01:00
Peter Duncanson 30905a9442 Converted search.service to TypeScript (after changing extension in the last commit) 2019-04-26 18:17:44 +01:00
Peter Duncanson ccfec9e092 Converted search.service to TypeScript 2019-04-26 18:17:13 +01:00
Peter Duncanson a2007a96af Converted overlayhelper.,service to TypeScript 2019-04-26 02:23:52 +01:00
Peter Duncanson 0879bdb90e Renamed overlayhelper.service.js to .ts 2019-04-26 02:19:25 +01:00
Peter Duncanson 1f483bc95c Converted overlay.service to TypeScript 2019-04-26 02:18:54 +01:00
Peter Duncanson e1df9d7a00 Renamed overlay.service.js to .ts 2019-04-26 01:50:30 +01:00
Peter Duncanson 41718cb7d5 package-lock.json had changed 2019-04-26 01:27:07 +01:00
Peter Duncanson 75d6e02c63 Changed iHasScope to "any" type instead of "object" as we don't know what shape it might be 2019-04-26 01:25:59 +01:00
Peter Duncanson c1d09c2516 Move the enum's for notification types out of the models namespace on notification service 2019-04-26 01:25:19 +01:00
Peter Duncanson ed429786ca Switched from using object to any for a few types I didn't know, lesson learnt 2019-04-26 01:24:18 +01:00
Peter Duncanson 88f108bf2e Finished converting over formHelper.service.ts 2019-04-26 00:50:14 +01:00
Peter Duncanson 437bf6bea1 Added new iHasScope interface to angularHelper
Used when anyone is passing around an object with a scope field on it
2019-04-26 00:49:52 +01:00
Peter Duncanson 6eb678b821 Converted all the classes to begin with Uppercase as per JS common practise, added a lowercase alias though so we don't break anything 2019-04-25 23:56:16 +01:00
Peter Duncanson 4d3af9284f "Prettified" notifications.service.ts no logic changes just formatting 2019-04-25 23:48:58 +01:00
Peter Duncanson 38d531894f Renamed formhelper to .ts and started converting to TypeScript 2019-04-25 23:42:34 +01:00
Stephan c434558378 IsVisible moves from publishde content to element 2019-04-10 12:44:21 +01:00
Stephan 777276d2e9 Better mapping of enumerable 2019-04-10 12:44:20 +01:00
Stephan b63ff54c19 Fix mapping of enumerable 2019-04-10 12:44:20 +01:00
Niels Lyngsø 96f1189fc6 Fixing model sync, much weird code cleaned up 2019-04-10 12:44:20 +01:00
Niels Lyngsø 2250389cc2 Fixing checkbox list to work in v8 2019-04-10 12:44:20 +01:00
Kenn Jacobsen 6a2b6ba75b Add IsVisible extension on IPublishedElement 2019-04-10 12:44:20 +01:00
Craig Noble 0a43530852 #5215 - Set up the initial build for typescript
I have rewritten the notificationsService and the AngularHelper (used by the NotificationsService) in Typescript. This gives a very good example of how the notificationsService can reference a concrete type even though the angularHelper is being injected in the constructor.

Open the Umbraco.Web.UI.Client in an editor that supports Typescript, such as VS Code or Atom.

In Umbraco.Web.UI.Client, run npm install.  Then run "npm run dev" and it will do the following:
1. Compile the typescript into Umbraco.Web.UI.Client/src/common/services/build/temp/, into their respective files such as notifications.service.js
2. The existing JS build picks up the compiled JS files and merges them into umbraco.services.js, then is saved in Umbraco.Web.UI/Umbraco/js (all stuff it currently does)

I converted the controller into a class, within the umbraco.services namespace.  The benefit of this is having the ability to generate a definitions file that will retain all namespaces and therefore make it extremely easy to find what you are looking for when creating Umbraco extensions such as custom property types, dashboards etc.

Note: I have set up a definitions folder for global variables that are declared outside of Typescript.  We can add typings in for Angular etc at a later date if needed.
2019-04-10 11:58:19 +01:00
366 changed files with 8304 additions and 10137 deletions
+1 -1
View File
@@ -157,8 +157,8 @@ build.tmp/
build/hooks/
build/temp/
/src/Umbraco.Web.UI.Client/src/common/**/build/temp/
# eof
/src/Umbraco.Web.UI.Client/TESTS-*.xml
/src/ApiDocs/api/*
+1
View File
@@ -54,5 +54,6 @@
<!-- symbols -->
<file src="$BuildTmp$\bin\Umbraco.Core.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Core\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Core" />
</files>
</package>
+2
View File
@@ -59,6 +59,8 @@
<!-- symbols -->
<file src="$BuildTmp$\bin\Umbraco.Web.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Web\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web" />
<file src="$BuildTmp$\bin\Umbraco.Examine.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Examine\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Examine" />
</files>
</package>
-1
View File
@@ -58,7 +58,6 @@
<!-- config transforms -->
<!-- beware! config transforms not supported by PackageReference -->
<file src="tools\Web.config.install.xdt" target="Content\Web.config.install.xdt" />
<file src="tools\serilog.config.install.xdt" target="Content\config\serilog.config.install.xdt" />
<file src="tools\ClientDependency.config.install.xdt" target="Content\config\ClientDependency.config.install.xdt" />
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" /> <!-- FIXME: Content\ !! and then... transform?! -->
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">>
<appSettings>
<add key="serilog:using:File" value="Umbraco.Core" xdt:Transform="SetAttributes(value)" xdt:Locator="Match(key)"/>
<add key="serilog:write-to:File.shared" xdt:Transform="Remove" xdt:Locator="Match(key)"/>
</appSettings>
</configuration>
-44
View File
@@ -1,44 +0,0 @@
$uenv=build/build.ps1 -get
$src = "$($uenv.SolutionRoot)\src"
$tmp = $uenv.BuildTemp
$out = $uenv.BuildOutput
$DocFxJson = "$src\ApiDocs\docfx.json"
$DocFxSiteOutput = "$tmp\_site\*.*"
################ Do the UI docs
$uenv.CompileBelle()
"Moving to Umbraco.Web.UI.Client folder"
cd .\src\Umbraco.Web.UI.Client
"Generating the docs and waiting before executing the next commands"
& gulp docs | Out-Null
# change baseUrl
$BaseUrl = "https://our.umbraco.com/apidocs/v8/ui/"
$IndexPath = "./docs/api/index.html"
(Get-Content $IndexPath).replace('location.href.replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath
# zip it
& $uenv.BuildEnv.Zip a -tzip -r "$out\ui-docs.zip" "$src\Umbraco.Web.UI.Client\docs\api\*.*"
################ Do the c# docs
# Build the solution in debug mode
$SolutionPath = Join-Path -Path $src -ChildPath "umbraco.sln"
#$uenv.CompileUmbraco()
#restore nuget packages
$uenv.RestoreNuGet()
# run DocFx
$DocFx = $uenv.BuildEnv.DocFx
Write-Host "$DocFxJson"
& $DocFx metadata $DocFxJson
& $DocFx build $DocFxJson
# zip it
& $uenv.BuildEnv.Zip a -tzip -r "$out\csharp-docs.zip" $DocFxSiteOutput
+3 -56
View File
@@ -11,11 +11,6 @@
[Alias("loc")]
[switch] $local = $false,
# enable docfx
[Parameter(Mandatory=$false)]
[Alias("doc")]
[switch] $docfx = $false,
# keep the build directories, don't clear them
[Parameter(Mandatory=$false)]
[Alias("c")]
@@ -36,7 +31,7 @@
$ubuild = &"$PSScriptRoot\build-bootstrap.ps1"
if (-not $?) { return }
$ubuild.Boot($PSScriptRoot,
@{ Local = $local; WithDocFx = $docfx },
@{ Local = $local; },
@{ Continue = $continue })
if ($ubuild.OnError()) { return }
@@ -392,13 +387,13 @@
&$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.Core.nuspec" `
-Properties BuildTmp="$($this.BuildTemp)" `
-Version "$($this.Version.Semver.ToString())" `
-Symbols -SymbolPackageFormat snupkg -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmscore.log"
-Symbols -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmscore.log"
if (-not $?) { throw "Failed to pack NuGet UmbracoCms.Core." }
&$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.Web.nuspec" `
-Properties BuildTmp="$($this.BuildTemp)" `
-Version "$($this.Version.Semver.ToString())" `
-Symbols -SymbolPackageFormat snupkg -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmsweb.log"
-Symbols -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmsweb.log"
if (-not $?) { throw "Failed to pack NuGet UmbracoCms.Web." }
&$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.nuspec" `
@@ -429,53 +424,6 @@
Write-Host "Prepare Azure Gallery"
$this.CopyFile("$($this.SolutionRoot)\build\Azure\azuregalleryrelease.ps1", $this.BuildOutput)
})
$ubuild.DefineMethod("PrepareCSharpDocs",
{
Write-Host "Prepare C# Documentation"
$src = "$($this.SolutionRoot)\src"
$tmp = $this.BuildTemp
$out = $this.BuildOutput
$DocFxJson = Join-Path -Path $src "\ApiDocs\docfx.json"
$DocFxSiteOutput = Join-Path -Path $tmp "\_site\*.*"
#restore nuget packages
$this.RestoreNuGet()
# run DocFx
$DocFx = $this.BuildEnv.DocFx
& $DocFx metadata $DocFxJson
& $DocFx build $DocFxJson
# zip it
& $this.BuildEnv.Zip a -tzip -r "$out\csharp-docs.zip" $DocFxSiteOutput
})
$ubuild.DefineMethod("PrepareAngularDocs",
{
Write-Host "Prepare Angular Documentation"
$src = "$($this.SolutionRoot)\src"
$out = $this.BuildOutput
$this.CompileBelle()
"Moving to Umbraco.Web.UI.Client folder"
cd .\src\Umbraco.Web.UI.Client
"Generating the docs and waiting before executing the next commands"
& gulp docs | Out-Null
# change baseUrl
$BaseUrl = "https://our.umbraco.com/apidocs/v8/ui/"
$IndexPath = "./docs/api/index.html"
(Get-Content $IndexPath).replace('location.href.replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath
# zip it
& $this.BuildEnv.Zip a -tzip -r "$out\ui-docs.zip" "$src\Umbraco.Web.UI.Client\docs\api\*.*"
})
$ubuild.DefineMethod("Build",
{
@@ -508,7 +456,6 @@
if ($this.OnError()) { return }
$this.PostPackageHook()
if ($this.OnError()) { return }
Write-Host "Done"
})
+24 -10
View File
@@ -2,20 +2,21 @@
"metadata": [
{
"src": [
{
"src": "../",
{
"files": [
"Umbraco.Core/Umbraco.Core.csproj",
"Umbraco.Web/Umbraco.Web.csproj"
],
"exclude": [
"**/obj/**",
"**/bin/**"
]
"**/bin/**",
"_site/**"
],
"cwd": "../src"
}
],
"dest": "api",
"filter": "docfx.filter.yml"
"dest": "../apidocs/api",
"filter": "../apidocs/docfx.filter.yml"
}
],
"build": {
@@ -34,7 +35,19 @@
"*.md"
],
"exclude": [
"obj/**"
"obj/**",
"_site/**"
]
}
],
"resource": [
{
"files": [
"images/**"
],
"exclude": [
"obj/**",
"_site/**"
]
}
],
@@ -44,7 +57,8 @@
"**.md"
],
"exclude": [
"obj/**"
"obj/**",
"_site/**"
]
}
],
@@ -53,9 +67,9 @@
"_enableSearch": true,
"_disableContribution": false
},
"dest": "../../build.tmp/_site",
"dest": "_site",
"template": [
"default", "umbracotemplate"
]
}
}
}
+34 -91
View File
@@ -70,23 +70,7 @@ namespace Umbraco.Core.Composing
}
}
internal IEnumerable<Type> PrepareComposerTypes()
{
var requirements = GetRequirements();
// only for debugging, this is verbose
//_logger.Debug<Composers>(GetComposersReport(requirements));
var sortedComposerTypes = SortComposers(requirements);
// bit verbose but should help for troubleshooting
//var text = "Ordered Composers: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComposerTypes) + Environment.NewLine;
_logger.Debug<Composers>("Ordered Composers: {SortedComposerTypes}", sortedComposerTypes);
return sortedComposerTypes;
}
internal Dictionary<Type, List<Type>> GetRequirements(bool throwOnMissing = true)
private IEnumerable<Type> PrepareComposerTypes()
{
// create a list, remove those that cannot be enabled due to runtime level
var composerTypeList = _composerTypes
@@ -105,69 +89,25 @@ namespace Umbraco.Core.Composing
// enable or disable composers
EnableDisableComposers(composerTypeList);
void GatherInterfaces<TAttribute>(Type type, Func<TAttribute, Type> getTypeInAttribute, HashSet<Type> iset, List<Type> set2)
where TAttribute : Attribute
{
foreach (var attribute in type.GetCustomAttributes<TAttribute>())
{
var typeInAttribute = getTypeInAttribute(attribute);
if (typeInAttribute != null && // if the attribute references a type ...
typeInAttribute.IsInterface && // ... which is an interface ...
typeof(IComposer).IsAssignableFrom(typeInAttribute) && // ... which implements IComposer ...
!iset.Contains(typeInAttribute)) // ... which is not already in the list
{
// add it to the new list
iset.Add(typeInAttribute);
set2.Add(typeInAttribute);
// add all its interfaces implementing IComposer
foreach (var i in typeInAttribute.GetInterfaces().Where(x => typeof(IComposer).IsAssignableFrom(x)))
{
iset.Add(i);
set2.Add(i);
}
}
}
}
// gather interfaces too
var interfaces = new HashSet<Type>(composerTypeList.SelectMany(x => x.GetInterfaces().Where(y => typeof(IComposer).IsAssignableFrom(y))));
composerTypeList.AddRange(interfaces);
var list1 = composerTypeList;
while (list1.Count > 0)
{
var list2 = new List<Type>();
foreach (var t in list1)
{
GatherInterfaces<ComposeAfterAttribute>(t, a => a.RequiredType, interfaces, list2);
GatherInterfaces<ComposeBeforeAttribute>(t, a => a.RequiringType, interfaces, list2);
}
composerTypeList.AddRange(list2);
list1 = list2;
}
// sort the composers according to their dependencies
var requirements = new Dictionary<Type, List<Type>>();
foreach (var type in composerTypeList)
requirements[type] = null;
foreach (var type in composerTypeList) requirements[type] = null;
foreach (var type in composerTypeList)
{
GatherRequirementsFromAfterAttribute(type, composerTypeList, requirements, throwOnMissing);
GatherRequirementsFromBeforeAttribute(type, composerTypeList, requirements);
GatherRequirementsFromRequireAttribute(type, composerTypeList, requirements);
GatherRequirementsFromRequiredByAttribute(type, composerTypeList, requirements);
}
return requirements;
}
// only for debugging, this is verbose
//_logger.Debug<Composers>(GetComposersReport(requirements));
internal IEnumerable<Type> SortComposers(Dictionary<Type, List<Type>> requirements)
{
// sort composers
var graph = new TopoGraph<Type, KeyValuePair<Type, List<Type>>>(kvp => kvp.Key, kvp => kvp.Value);
graph.AddItems(requirements);
List<Type> sortedComposerTypes;
try
{
sortedComposerTypes = graph.GetSortedItems().Select(x => x.Key).Where(x => !x.IsInterface).ToList();
sortedComposerTypes = graph.GetSortedItems().Select(x => x.Key).ToList();
}
catch (Exception e)
{
@@ -177,37 +117,40 @@ namespace Umbraco.Core.Composing
throw;
}
// bit verbose but should help for troubleshooting
//var text = "Ordered Composers: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComposerTypes) + Environment.NewLine;
_logger.Debug<Composers>("Ordered Composers: {SortedComposerTypes}", sortedComposerTypes);
return sortedComposerTypes;
}
internal static string GetComposersReport(Dictionary<Type, List<Type>> requirements)
private static string GetComposersReport(Dictionary<Type, List<Type>> requirements)
{
var text = new StringBuilder();
text.AppendLine("Composers & Dependencies:");
text.AppendLine(" < compose before");
text.AppendLine(" > compose after");
text.AppendLine(" : implements");
text.AppendLine(" = depends");
text.AppendLine();
bool HasReq(IEnumerable<Type> types, Type type)
=> types.Any(x => type.IsAssignableFrom(x) && !x.IsInterface);
foreach (var kvp in requirements)
{
var type = kvp.Key;
text.AppendLine(type.FullName);
foreach (var attribute in type.GetCustomAttributes<ComposeAfterAttribute>())
{
var weak = !(attribute.RequiredType.IsInterface ? attribute.Weak == false : attribute.Weak != true);
text.AppendLine(" > " + attribute.RequiredType +
(weak ? " (weak" : " (strong") + (HasReq(requirements.Keys, attribute.RequiredType) ? ", found" : ", missing") + ")");
}
text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue
? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")"))
: ""));
foreach (var attribute in type.GetCustomAttributes<ComposeBeforeAttribute>())
text.AppendLine(" < " + attribute.RequiringType);
text.AppendLine(" -< " + attribute.RequiringType);
foreach (var i in type.GetInterfaces())
{
text.AppendLine(" : " + i.FullName);
foreach (var attribute in i.GetCustomAttributes<ComposeAfterAttribute>())
text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue
? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")"))
: ""));
foreach (var attribute in i.GetCustomAttributes<ComposeBeforeAttribute>())
text.AppendLine(" -< " + attribute.RequiringType);
}
if (kvp.Value != null)
foreach (var t in kvp.Value)
text.AppendLine(" = " + t);
@@ -278,16 +221,16 @@ namespace Umbraco.Core.Composing
types.Remove(kvp.Key);
}
private static void GatherRequirementsFromAfterAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements, bool throwOnMissing = true)
private static void GatherRequirementsFromRequireAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements)
{
// get 'require' attributes
// these attributes are *not* inherited because we want to "custom-inherit" for interfaces only
var afterAttributes = type
var requireAttributes = type
.GetInterfaces().SelectMany(x => x.GetCustomAttributes<ComposeAfterAttribute>()) // those marking interfaces
.Concat(type.GetCustomAttributes<ComposeAfterAttribute>()); // those marking the composer
// what happens in case of conflicting attributes (different strong/weak for same type) is not specified.
foreach (var attr in afterAttributes)
foreach (var attr in requireAttributes)
{
if (attr.RequiredType == type) continue; // ignore self-requirements (+ exclude in implems, below)
@@ -295,13 +238,13 @@ namespace Umbraco.Core.Composing
// unless strong, and then require at least one enabled composer implementing that interface
if (attr.RequiredType.IsInterface)
{
var implems = types.Where(x => x != type && attr.RequiredType.IsAssignableFrom(x) && !x.IsInterface).ToList();
var implems = types.Where(x => x != type && attr.RequiredType.IsAssignableFrom(x)).ToList();
if (implems.Count > 0)
{
if (requirements[type] == null) requirements[type] = new List<Type>();
requirements[type].AddRange(implems);
}
else if (attr.Weak == false && throwOnMissing) // if explicitly set to !weak, is strong, else is weak
else if (attr.Weak == false) // if explicitly set to !weak, is strong, else is weak
throw new Exception($"Broken composer dependency: {type.FullName} -> {attr.RequiredType.FullName}.");
}
// requiring a class = require that the composer is enabled
@@ -313,28 +256,28 @@ namespace Umbraco.Core.Composing
if (requirements[type] == null) requirements[type] = new List<Type>();
requirements[type].Add(attr.RequiredType);
}
else if (attr.Weak != true && throwOnMissing) // if not explicitly set to weak, is strong
else if (attr.Weak != true) // if not explicitly set to weak, is strong
throw new Exception($"Broken composer dependency: {type.FullName} -> {attr.RequiredType.FullName}.");
}
}
}
private static void GatherRequirementsFromBeforeAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements)
private static void GatherRequirementsFromRequiredByAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements)
{
// get 'required' attributes
// these attributes are *not* inherited because we want to "custom-inherit" for interfaces only
var beforeAttributes = type
var requiredAttributes = type
.GetInterfaces().SelectMany(x => x.GetCustomAttributes<ComposeBeforeAttribute>()) // those marking interfaces
.Concat(type.GetCustomAttributes<ComposeBeforeAttribute>()); // those marking the composer
foreach (var attr in beforeAttributes)
foreach (var attr in requiredAttributes)
{
if (attr.RequiringType == type) continue; // ignore self-requirements (+ exclude in implems, below)
// required by an interface = by any enabled composer implementing this that interface
if (attr.RequiringType.IsInterface)
{
var implems = types.Where(x => x != type && attr.RequiringType.IsAssignableFrom(x) && !x.IsInterface).ToList();
var implems = types.Where(x => x != type && attr.RequiringType.IsAssignableFrom(x)).ToList();
foreach (var implem in implems)
{
if (requirements[implem] == null) requirements[implem] = new List<Type>();
@@ -6,50 +6,30 @@ namespace Umbraco.Core.Composing
/// <summary>
/// Provides a base class for collections of types.
/// </summary>
public abstract class TypeCollectionBuilderBase<TBuilder, TCollection, TConstraint> : ICollectionBuilder<TCollection, Type>
where TBuilder : TypeCollectionBuilderBase<TBuilder, TCollection, TConstraint>
public abstract class TypeCollectionBuilderBase<TCollection, TConstraint> : ICollectionBuilder<TCollection, Type>
where TCollection : class, IBuilderCollection<Type>
{
private readonly HashSet<Type> _types = new HashSet<Type>();
protected abstract TBuilder This { get; }
private static Type Validate(Type type, string action)
private Type Validate(Type type, string action)
{
if (!typeof(TConstraint).IsAssignableFrom(type))
throw new InvalidOperationException($"Cannot {action} type {type.FullName} as it does not inherit from/implement {typeof(TConstraint).FullName}.");
return type;
}
public TBuilder Add(Type type)
{
_types.Add(Validate(type, "add"));
return This;
}
public void Add(Type type) => _types.Add(Validate(type, "add"));
public TBuilder Add<T>()
{
Add(typeof(T));
return This;
}
public void Add<T>() => Add(typeof(T));
public TBuilder Add(IEnumerable<Type> types)
public void Add(IEnumerable<Type> types)
{
foreach (var type in types) Add(type);
return This;
}
public TBuilder Remove(Type type)
{
_types.Remove(Validate(type, "remove"));
return This;
}
public void Remove(Type type) => _types.Remove(Validate(type, "remove"));
public TBuilder Remove<T>()
{
Remove(typeof(T));
return This;
}
public void Remove<T>() => Remove(typeof(T));
public TCollection CreateCollection(IFactory factory)
{
@@ -315,7 +315,7 @@ namespace Umbraco.Core.Configuration
var hash = hashString.GenerateHash();
var siteTemp = System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", hash);
return _localTempPath = siteTemp;
return _localTempPath = System.IO.Path.Combine(siteTemp, "umbraco.config");
//case LocalTempStorage.Default:
//case LocalTempStorage.Unknown:
+1 -13
View File
@@ -10,18 +10,6 @@ namespace Umbraco.Core
///</summary>
public static class EnumerableExtensions
{
internal static bool HasDuplicates<T>(this IEnumerable<T> items, bool includeNull)
{
var hs = new HashSet<T>();
foreach (var item in items)
{
if ((item != null || includeNull) && !hs.Add(item))
return true;
}
return false;
}
/// <summary>
/// Wraps this object instance into an IEnumerable{T} consisting of a single item.
/// </summary>
@@ -112,7 +100,7 @@ namespace Umbraco.Core
}
}
/// <summary>
/// Returns true if all items in the other collection exist in this collection
/// </summary>
@@ -1,11 +1,7 @@
using System;
using System.Text;
using System.Web;
using Serilog;
using Serilog.Configuration;
using Serilog.Core;
using Serilog.Events;
using Serilog.Formatting;
using Serilog.Formatting.Compact;
using Umbraco.Core.Logging.Serilog.Enrichers;
@@ -13,7 +9,6 @@ namespace Umbraco.Core.Logging.Serilog
{
public static class LoggerConfigExtensions
{
private const string AppDomainId = "AppDomainId";
/// <summary>
/// This configures Serilog with some defaults
/// Such as adding ProcessID, Thread, AppDomain etc
@@ -33,14 +28,14 @@ namespace Umbraco.Core.Logging.Serilog
.Enrich.WithProcessId()
.Enrich.WithProcessName()
.Enrich.WithThreadId()
.Enrich.WithProperty(AppDomainId, AppDomain.CurrentDomain.Id)
.Enrich.WithProperty("AppDomainId", AppDomain.CurrentDomain.Id)
.Enrich.WithProperty("AppDomainAppId", HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty))
.Enrich.WithProperty("MachineName", Environment.MachineName)
.Enrich.With<Log4NetLevelMapperEnricher>()
.Enrich.With<HttpSessionIdEnricher>()
.Enrich.With<HttpRequestNumberEnricher>()
.Enrich.With<HttpRequestIdEnricher>();
return logConfig;
}
@@ -55,51 +50,15 @@ namespace Umbraco.Core.Logging.Serilog
//Main .txt logfile - in similar format to older Log4Net output
//Ends with ..txt as Date is inserted before file extension substring
logConfig.WriteTo.File($@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\UmbracoTraceLog.{Environment.MachineName}..txt",
shared: true,
rollingInterval: RollingInterval.Day,
restrictedToMinimumLevel: minimumLevel,
retainedFileCountLimit: null, //Setting to null means we keep all files - default is 31 days
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss,fff} [P{ProcessId}/D{AppDomainId}/T{ThreadId}] {Log4NetLevel} {SourceContext} - {Message:lj}{NewLine}{Exception}");
shared: true,
rollingInterval: RollingInterval.Day,
restrictedToMinimumLevel: minimumLevel,
retainedFileCountLimit: null, //Setting to null means we keep all files - default is 31 days
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss,fff} [P{ProcessId}/D{AppDomainId}/T{ThreadId}] {Log4NetLevel} {SourceContext} - {Message:lj}{NewLine}{Exception}");
return logConfig;
}
/// <remarks>
/// Used in config - If renamed or moved to other assembly the config file also has be updated.
/// </remarks>
public static LoggerConfiguration File(this LoggerSinkConfiguration configuration, ITextFormatter formatter,
string path,
LogEventLevel restrictedToMinimumLevel = LogEventLevel.Verbose,
LoggingLevelSwitch levelSwitch = null,
long? fileSizeLimitBytes = 1073741824,
TimeSpan? flushToDiskInterval = null,
RollingInterval rollingInterval = RollingInterval.Infinite,
bool rollOnFileSizeLimit = false,
int? retainedFileCountLimit = 31,
Encoding encoding = null
)
{
return configuration.Async(
asyncConfiguration => asyncConfiguration.Map(AppDomainId, (_,mapConfiguration) =>
mapConfiguration.File(
formatter,
path,
restrictedToMinimumLevel,
fileSizeLimitBytes,
levelSwitch,
buffered:true,
shared:false,
flushToDiskInterval,
rollingInterval,
rollOnFileSizeLimit,
retainedFileCountLimit,
encoding),
sinkMapCountLimit:0)
);
}
/// <summary>
/// Outputs a CLEF format JSON log at /App_Data/Logs/
/// </summary>
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Logging.Serilog
///<summary>
/// Implements <see cref="ILogger"/> on top of Serilog.
///</summary>
public class SerilogLogger : ILogger, IDisposable
public class SerilogLogger : ILogger
{
/// <summary>
/// Initialize a new instance of the <see cref="SerilogLogger"/> class with a configuration file.
@@ -271,10 +271,5 @@ namespace Umbraco.Core.Logging.Serilog
{
LoggerFor(reporting).Verbose(messageTemplate, propertyValues);
}
public void Dispose()
{
Log.CloseAndFlush();
}
}
}
@@ -39,7 +39,7 @@ namespace Umbraco.Core.Logging.Viewer
for (var day = startDate.Date; day.Date <= endDate.Date; day = day.AddDays(1))
{
//Filename ending to search for (As could be multiple)
var filesToFind = GetSearchPattern(day);
var filesToFind = $"*{day:yyyyMMdd}.json";
var filesForCurrentDay = Directory.GetFiles(logDirectory, filesToFind);
@@ -52,11 +52,6 @@ namespace Umbraco.Core.Logging.Viewer
return logSizeAsMegabytes <= FileSizeCap;
}
private string GetSearchPattern(DateTime day)
{
return $"*{day:yyyyMMdd}*.json";
}
protected override IReadOnlyList<LogEvent> GetLogs(DateTimeOffset startDate, DateTimeOffset endDate, ILogFilter filter, int skip, int take)
{
var logs = new List<LogEvent>();
@@ -71,7 +66,7 @@ namespace Umbraco.Core.Logging.Viewer
for (var day = startDate.Date; day.Date <= endDate.Date; day = day.AddDays(1))
{
//Filename ending to search for (As could be multiple)
var filesToFind = GetSearchPattern(day);
var filesToFind = $"*{day:yyyyMMdd}.json";
var filesForCurrentDay = Directory.GetFiles(logDirectory, filesToFind);
+10 -16
View File
@@ -1,6 +1,5 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
@@ -30,16 +29,11 @@ namespace Umbraco.Core.Mapping
/// </remarks>
public class UmbracoMapper
{
// note
//
// the outer dictionary *can* be modified, see GetCtor and GetMap, hence have to be ConcurrentDictionary
// the inner dictionaries are never modified and therefore can be simple Dictionary
private readonly Dictionary<Type, Dictionary<Type, Func<object, MapperContext, object>>> _ctors
= new Dictionary<Type, Dictionary<Type, Func<object, MapperContext, object>>>();
private readonly ConcurrentDictionary<Type, Dictionary<Type, Func<object, MapperContext, object>>> _ctors
= new ConcurrentDictionary<Type, Dictionary<Type, Func<object, MapperContext, object>>>();
private readonly ConcurrentDictionary<Type, Dictionary<Type, Action<object, object, MapperContext>>> _maps
= new ConcurrentDictionary<Type, Dictionary<Type, Action<object, object, MapperContext>>>();
private readonly Dictionary<Type, Dictionary<Type, Action<object, object, MapperContext>>> _maps
= new Dictionary<Type, Dictionary<Type, Action<object, object, MapperContext>>>();
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoMapper"/> class.
@@ -107,12 +101,16 @@ namespace Umbraco.Core.Mapping
private Dictionary<Type, Func<object, MapperContext, object>> DefineCtors(Type sourceType)
{
return _ctors.GetOrAdd(sourceType, _ => new Dictionary<Type, Func<object, MapperContext, object>>());
if (!_ctors.TryGetValue(sourceType, out var sourceCtor))
sourceCtor = _ctors[sourceType] = new Dictionary<Type, Func<object, MapperContext, object>>();
return sourceCtor;
}
private Dictionary<Type, Action<object, object, MapperContext>> DefineMaps(Type sourceType)
{
return _maps.GetOrAdd(sourceType, _ => new Dictionary<Type, Action<object, object, MapperContext>>());
if (!_maps.TryGetValue(sourceType, out var sourceMap))
sourceMap = _maps[sourceType] = new Dictionary<Type, Action<object, object, MapperContext>>();
return sourceMap;
}
#endregion
@@ -328,8 +326,6 @@ namespace Umbraco.Core.Mapping
if (_ctors.TryGetValue(sourceType, out var sourceCtor) && sourceCtor.TryGetValue(targetType, out var ctor))
return ctor;
// we *may* run this more than once but it does not matter
ctor = null;
foreach (var (stype, sctors) in _ctors)
{
@@ -351,8 +347,6 @@ namespace Umbraco.Core.Mapping
if (_maps.TryGetValue(sourceType, out var sourceMap) && sourceMap.TryGetValue(targetType, out var map))
return map;
// we *may* run this more than once but it does not matter
map = null;
foreach (var (stype, smap) in _maps)
{
@@ -83,25 +83,17 @@ namespace Umbraco.Core.Migrations.Expressions.Create
}
/// <inheritdoc />
public ICreateConstraintOnTableBuilder PrimaryKey() => PrimaryKey(true);
/// <inheritdoc />
public ICreateConstraintOnTableBuilder PrimaryKey(bool clustered)
public ICreateConstraintOnTableBuilder PrimaryKey()
{
var expression = new CreateConstraintExpression(_context, ConstraintType.PrimaryKey);
expression.Constraint.IsPrimaryKeyClustered = clustered;
return new CreateConstraintBuilder(expression);
}
/// <inheritdoc />
public ICreateConstraintOnTableBuilder PrimaryKey(string primaryKeyName) => PrimaryKey(primaryKeyName, true);
/// <inheritdoc />
public ICreateConstraintOnTableBuilder PrimaryKey(string primaryKeyName, bool clustered)
public ICreateConstraintOnTableBuilder PrimaryKey(string primaryKeyName)
{
var expression = new CreateConstraintExpression(_context, ConstraintType.PrimaryKey);
expression.Constraint.ConstraintName = primaryKeyName;
expression.Constraint.IsPrimaryKeyClustered = clustered;
return new CreateConstraintBuilder(expression);
}
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Migrations.Expressions.Create.Expressions
var constraintType = (Constraint.IsPrimaryKeyConstraint) ? "PRIMARY KEY" : "UNIQUE";
if (Constraint.IsPrimaryKeyConstraint && SqlSyntax.SupportsClustered())
constraintType += Constraint.IsPrimaryKeyClustered ? " CLUSTERED" : " NONCLUSTERED";
constraintType += " CLUSTERED";
if (Constraint.IsNonUniqueConstraint)
constraintType = string.Empty;
@@ -68,16 +68,6 @@ namespace Umbraco.Core.Migrations.Expressions.Create
/// </summary>
ICreateConstraintOnTableBuilder PrimaryKey(string primaryKeyName);
/// <summary>
/// Builds a Create Primary Key expression.
/// </summary>
ICreateConstraintOnTableBuilder PrimaryKey(bool clustered);
/// <summary>
/// Builds a Create Primary Key expression.
/// </summary>
ICreateConstraintOnTableBuilder PrimaryKey(string primaryKeyName, bool clustered);
/// <summary>
/// Builds a Create Unique Constraint expression.
/// </summary>
@@ -226,8 +226,6 @@ namespace Umbraco.Core.Migrations.Install
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 32, UniqueId = 32.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLockoutDate, Name = Constants.Conventions.Member.LastLockoutDateLabel, SortOrder = 4, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 33, UniqueId = 33.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLoginDate, Name = Constants.Conventions.Member.LastLoginDateLabel, SortOrder = 5, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 34, UniqueId = 34.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastPasswordChangeDate, Name = Constants.Conventions.Member.LastPasswordChangeDateLabel, SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 35, UniqueId = 35.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = null, Alias = Constants.Conventions.Member.PasswordQuestion, Name = Constants.Conventions.Member.PasswordQuestionLabel, SortOrder = 7, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 36, UniqueId = 36.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = null, Alias = Constants.Conventions.Member.PasswordAnswer, Name = Constants.Conventions.Member.PasswordAnswerLabel, SortOrder = 8, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
}
@@ -6,7 +6,6 @@ using Umbraco.Core.Migrations.Upgrade.V_7_12_0;
using Umbraco.Core.Migrations.Upgrade.V_7_14_0;
using Umbraco.Core.Migrations.Upgrade.V_8_0_0;
using Umbraco.Core.Migrations.Upgrade.V_8_0_1;
using Umbraco.Core.Migrations.Upgrade.V_8_1_0;
namespace Umbraco.Core.Migrations.Upgrade
{
@@ -140,7 +139,6 @@ namespace Umbraco.Core.Migrations.Upgrade
To<RenameLabelAndRichTextPropertyEditorAliases>("{E0CBE54D-A84F-4A8F-9B13-900945FD7ED9}");
To<MergeDateAndDateTimePropertyEditor>("{78BAF571-90D0-4D28-8175-EF96316DA789}");
To<ChangeNuCacheJsonFormat>("{80C0A0CB-0DD5-4573-B000-C4B7C313C70D}");
To<ConvertTinyMceAndGridMediaUrlsToLocalLink>("{B69B6E8C-A769-4044-A27E-4A4E18D1645A}");
//FINAL
@@ -79,8 +79,7 @@ HAVING COUNT(v2.id) <> 1").Any())
// transform column versionId from guid to integer (contentVersion.id)
if (ColumnType(PreTables.PropertyData, "versionId") == "uniqueidentifier")
{
Alter.Table(PreTables.PropertyData).AddColumn("versionId2").AsInt32().Nullable().Do();
Database.Execute($"ALTER TABLE {PreTables.PropertyData} ADD COLUMN versionId2 INT NULL;");
// SQLCE does not support UPDATE...FROM
var temp = Database.Fetch<dynamic>($"SELECT id, versionId FROM {PreTables.ContentVersion}");
foreach (var t in temp)
@@ -1,89 +0,0 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Migrations.PostMigrations;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Services;
namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
{
public class ConvertTinyMceAndGridMediaUrlsToLocalLink : MigrationBase
{
private readonly IMediaService _mediaService;
public ConvertTinyMceAndGridMediaUrlsToLocalLink(IMigrationContext context, IMediaService mediaService) : base(context)
{
_mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService));
}
public override void Migrate()
{
var mediaLinkPattern = new Regex(
@"(<a[^>]*href="")(\/ media[^""\?]*)([^>]*>)",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
var sqlPropertyData = Sql()
.Select<PropertyDataDto>(x => x.Id, x => x.TextValue)
.AndSelect<DataTypeDto>(x => x.EditorAlias)
.From<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>().On<PropertyDataDto, PropertyTypeDto>((left, right) => left.PropertyTypeId == right.Id)
.InnerJoin<DataTypeDto>().On<PropertyTypeDto, DataTypeDto>((left, right) => left.DataTypeId == right.NodeId)
.Where<DataTypeDto>(x =>
x.EditorAlias == Constants.PropertyEditors.Aliases.TinyMce ||
x.EditorAlias == Constants.PropertyEditors.Aliases.Grid);
var properties = Database.Fetch<PropertyDataDto>(sqlPropertyData);
foreach (var property in properties)
{
var value = property.TextValue;
if (string.IsNullOrWhiteSpace(value)) continue;
if (property.PropertyTypeDto.DataTypeDto.EditorAlias == Constants.PropertyEditors.Aliases.Grid)
{
var obj = JsonConvert.DeserializeObject<JObject>(value);
var allControls = obj.SelectTokens("$.sections..rows..areas..controls");
foreach (var control in allControls.SelectMany(c => c))
{
var controlValue = control["value"];
if (controlValue.Type == JTokenType.String)
{
control["value"] = UpdateMediaUrls(mediaLinkPattern, controlValue.Value<string>());
}
}
property.TextValue = JsonConvert.SerializeObject(obj);
}
else
{
property.TextValue = UpdateMediaUrls(mediaLinkPattern, value);
}
Database.Update(property, x => x.TextValue);
}
Context.AddPostMigration<RebuildPublishedSnapshot>();
}
private string UpdateMediaUrls(Regex mediaLinkPattern, string value)
{
return mediaLinkPattern.Replace(value, match =>
{
// match groups:
// - 1 = from the beginning of the a tag until href attribute value begins
// - 2 = the href attribute value excluding the querystring (if present)
// - 3 = anything after group 2 until the a tag is closed
var href = match.Groups[2].Value;
var media = _mediaService.GetMediaByPath(href);
return media == null
? match.Value
: $"{match.Groups[1].Value}/{{localLink:{media.GetUdi()}}}{match.Groups[3].Value}";
});
}
}
}
+3 -19
View File
@@ -95,21 +95,6 @@ namespace Umbraco.Core.Models
protected void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//enable this to detect duplicate property aliases. We do want this, however making this change in a
//patch release might be a little dangerous
////detect if there are any duplicate aliases - this cannot be allowed
//if (e.Action == NotifyCollectionChangedAction.Add
// || e.Action == NotifyCollectionChangedAction.Replace)
//{
// var allAliases = _noGroupPropertyTypes.Concat(PropertyGroups.SelectMany(x => x.PropertyTypes)).Select(x => x.Alias);
// if (allAliases.HasDuplicates(false))
// {
// var newAliases = string.Join(", ", e.NewItems.Cast<PropertyType>().Select(x => x.Alias));
// throw new InvalidOperationException($"Other property types already exist with the aliases: {newAliases}");
// }
//}
OnPropertyChanged(nameof(PropertyTypes));
}
@@ -403,16 +388,15 @@ namespace Umbraco.Core.Models
var group = PropertyGroups[propertyGroupName];
if (group == null) return;
// first remove the group
PropertyGroups.RemoveItem(propertyGroupName);
// Then re-assign the group's properties to no group
// re-assign the group's properties to no group
foreach (var property in group.PropertyTypes)
{
property.PropertyGroupId = null;
_noGroupPropertyTypes.Add(property);
}
// actually remove the group
PropertyGroups.RemoveItem(propertyGroupName);
OnPropertyChanged(nameof(PropertyGroups));
}
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Models
public class PropertyCollection : KeyedCollection<string, Property>, INotifyCollectionChanged, IDeepCloneable
{
private readonly object _addLocker = new object();
internal Action OnAdd;
internal Func<Property, bool> AdditionValidator { get; set; }
/// <summary>
@@ -49,12 +49,10 @@ namespace Umbraco.Core.Models
/// </summary>
internal void Reset(IEnumerable<Property> properties)
{
//collection events will be raised in each of these calls
Clear();
//collection events will be raised in each of these calls
foreach (var property in properties)
Add(property);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
@@ -62,9 +60,8 @@ namespace Umbraco.Core.Models
/// </summary>
protected override void SetItem(int index, Property property)
{
var oldItem = index >= 0 ? this[index] : property;
base.SetItem(index, property);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, property, oldItem));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property, index));
}
/// <summary>
@@ -123,8 +120,10 @@ namespace Umbraco.Core.Models
}
}
//collection events will be raised in InsertItem with Add
base.Add(property);
OnAdd?.Invoke();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property));
}
}
@@ -19,6 +19,9 @@ namespace Umbraco.Core.Models
{
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
// TODO: this doesn't seem to be used anywhere
internal Action OnAdd;
internal PropertyGroupCollection()
{ }
@@ -34,19 +37,16 @@ namespace Umbraco.Core.Models
/// <remarks></remarks>
internal void Reset(IEnumerable<PropertyGroup> groups)
{
//collection events will be raised in each of these calls
Clear();
//collection events will be raised in each of these calls
foreach (var group in groups)
Add(group);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void SetItem(int index, PropertyGroup item)
{
var oldItem = index >= 0 ? this[index] : item;
base.SetItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
protected override void RemoveItem(int index)
@@ -84,7 +84,6 @@ namespace Umbraco.Core.Models
if (keyExists)
throw new Exception($"Naming conflict: Changing the name of PropertyGroup '{item.Name}' would result in duplicates");
//collection events will be raised in SetItem
SetItem(IndexOfKey(item.Id), item);
return;
}
@@ -97,14 +96,16 @@ namespace Umbraco.Core.Models
var exists = Contains(key);
if (exists)
{
//collection events will be raised in SetItem
SetItem(IndexOfKey(key), item);
return;
}
}
}
//collection events will be raised in InsertItem
base.Add(item);
OnAdd?.Invoke();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
finally
{
@@ -19,6 +19,9 @@ namespace Umbraco.Core.Models
[IgnoreDataMember]
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
// TODO: This doesn't seem to be used
[IgnoreDataMember]
internal Action OnAdd;
internal PropertyTypeCollection(bool supportsPublishing)
{
@@ -40,44 +43,36 @@ namespace Umbraco.Core.Models
/// <remarks></remarks>
internal void Reset(IEnumerable<PropertyType> properties)
{
//collection events will be raised in each of these calls
Clear();
//collection events will be raised in each of these calls
foreach (var property in properties)
Add(property);
Add(property);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void SetItem(int index, PropertyType item)
{
item.SupportsPublishing = SupportsPublishing;
var oldItem = index >= 0 ? this[index] : item;
base.SetItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem));
item.PropertyChanged += Item_PropertyChanged;
base.SetItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
protected override void RemoveItem(int index)
{
var removed = this[index];
base.RemoveItem(index);
removed.PropertyChanged -= Item_PropertyChanged;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
}
protected override void InsertItem(int index, PropertyType item)
{
item.SupportsPublishing = SupportsPublishing;
base.InsertItem(index, item);
base.InsertItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
item.PropertyChanged += Item_PropertyChanged;
}
protected override void ClearItems()
{
base.ClearItems();
foreach (var item in this)
item.PropertyChanged -= Item_PropertyChanged;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
@@ -96,7 +91,6 @@ namespace Umbraco.Core.Models
var exists = Contains(key);
if (exists)
{
//collection events will be raised in SetItem
SetItem(IndexOfKey(key), item);
return;
}
@@ -109,8 +103,10 @@ namespace Umbraco.Core.Models
item.SortOrder = this.Max(x => x.SortOrder) + 1;
}
//collection events will be raised in InsertItem
base.Add(item);
OnAdd?.Invoke();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
finally
{
@@ -119,17 +115,6 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// Occurs when a property changes on a PropertyType that exists in this collection
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
var propType = (PropertyType)sender;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, propType, propType));
}
/// <summary>
/// Determines whether this collection contains a <see cref="Property"/> whose alias matches the specified PropertyType.
/// </summary>
@@ -18,6 +18,5 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
public string ConstraintName { get; set; }
public string TableName { get; set; }
public ICollection<string> Columns = new HashSet<string>();
public bool IsPrimaryKeyClustered { get; set; }
}
}
@@ -379,20 +379,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return "variantName";
}
// content type alias is invariant
if(ordering.OrderBy.InvariantEquals("contentTypeAlias"))
{
var joins = Sql()
.InnerJoin<ContentTypeDto>("ctype").On<ContentDto, ContentTypeDto>((content, contentType) => content.ContentTypeId == contentType.NodeId, aliasRight: "ctype");
// see notes in ApplyOrdering: the field MUST be selected + aliased
sql = Sql(InsertBefore(sql, "FROM", ", " + SqlSyntax.GetFieldName<ContentTypeDto>(x => x.Alias, "ctype") + " AS ordering "), sql.Arguments);
sql = InsertJoins(sql, joins);
return "ordering";
}
// previously, we'd accept anything and just sanitize it - not anymore
throw new NotSupportedException($"Ordering by {ordering.OrderBy} not supported.");
}
@@ -240,25 +240,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
propertyIx++;
}
contentType.NoGroupPropertyTypes = noGroupPropertyTypes;
// ensure builtin properties
if (contentType is MemberType memberType)
{
// ensure that the group exists (ok if it already exists)
memberType.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupName);
// ensure that property types exist (ok if they already exist)
foreach (var (alias, propertyType) in builtinProperties)
{
var added = memberType.AddPropertyType(propertyType, Constants.Conventions.Member.StandardPropertiesGroupName);
if (added)
{
var access = new MemberTypePropertyProfileAccess(false, false, false);
memberType.MemberTypePropertyTypes[alias] = access;
}
}
}
}
}
@@ -283,7 +264,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (contentType is MemberType memberType)
{
var access = new MemberTypePropertyProfileAccess(dto.ViewOnProfile, dto.CanEdit, dto.IsSensitive);
memberType.MemberTypePropertyTypes[dto.Alias] = access;
memberType.MemberTypePropertyTypes.Add(dto.Alias, access);
}
return new PropertyType(dto.DataTypeDto.EditorAlias, storageType, readonlyStorageType, dto.Alias)
@@ -13,15 +13,12 @@ namespace Umbraco.Core.PropertyEditors
/// </summary>
public class ConfigurationEditor : IConfigurationEditor
{
private IDictionary<string, object> _defaultConfiguration;
/// <summary>
/// Initializes a new instance of the <see cref="ConfigurationEditor"/> class.
/// </summary>
public ConfigurationEditor()
{
Fields = new List<ConfigurationField>();
_defaultConfiguration = new Dictionary<string, object>();
}
/// <summary>
@@ -64,10 +61,7 @@ namespace Umbraco.Core.PropertyEditors
/// <inheritdoc />
[JsonProperty("defaultConfig")]
public virtual IDictionary<string, object> DefaultConfiguration {
get => _defaultConfiguration;
internal set => _defaultConfiguration = value;
}
public virtual IDictionary<string, object> DefaultConfiguration => new Dictionary<string, object>();
/// <inheritdoc />
public virtual object DefaultConfigurationObject => DefaultConfiguration;
@@ -173,13 +173,7 @@ namespace Umbraco.Core.PropertyEditors
/// </summary>
protected virtual IConfigurationEditor CreateConfigurationEditor()
{
var editor = new ConfigurationEditor();
// pass the default configuration if this is not a property value editor
if((Type & EditorType.PropertyValue) == 0)
{
editor.DefaultConfiguration = _defaultConfiguration;
}
return editor;
return new ConfigurationEditor();
}
/// <summary>
@@ -1,8 +1,8 @@
namespace Umbraco.Core.PropertyEditors
{
public class DropDownFlexibleConfiguration : ValueListConfiguration
internal class DropDownFlexibleConfiguration : ValueListConfiguration
{
[ConfigurationField("multiple", "Enable multiple choice", "boolean", Description = "When checked, the dropdown will be a select multiple / combo box style dropdown.")]
public bool Multiple { get; set; }
}
}
}
@@ -9,18 +9,18 @@
public bool EnableRange { get; set; }
[ConfigurationField("initVal1", "Initial value", "number")]
public decimal InitialValue { get; set; }
public int InitialValue { get; set; }
[ConfigurationField("initVal2", "Initial value 2", "number", Description = "Used when range is enabled")]
public decimal InitialValue2 { get; set; }
public int InitialValue2 { get; set; }
[ConfigurationField("minVal", "Minimum value", "number")]
public decimal MinimumValue { get; set; }
public int MinimumValue { get; set; }
[ConfigurationField("maxVal", "Maximum value", "number")]
public decimal MaximumValue { get; set; }
public int MaximumValue { get; set; }
[ConfigurationField("step", "Step increments", "number")]
public decimal StepIncrements { get; set; }
public int StepIncrements { get; set; }
}
}
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
@@ -316,16 +315,8 @@ namespace Umbraco.Core.Services
/// <summary>
/// Empties the recycle bin.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")]
OperationResult EmptyRecycleBin();
/// <summary>
/// Empties the Recycle Bin by deleting all <see cref="IContent"/> that resides in the bin
/// </summary>
/// <param name="userId">Optional Id of the User emptying the Recycle Bin</param>
OperationResult EmptyRecycleBin(int userId = Constants.Security.SuperUserId);
/// <summary>
/// Sorts documents.
/// </summary>
@@ -162,16 +162,8 @@ namespace Umbraco.Core.Services
/// <summary>
/// Empties the Recycle Bin by deleting all <see cref="IMedia"/> that resides in the bin
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")]
OperationResult EmptyRecycleBin();
/// <summary>
/// Empties the Recycle Bin by deleting all <see cref="IMedia"/> that resides in the bin
/// </summary>
/// <param name="userId">Optional Id of the User emptying the Recycle Bin</param>
OperationResult EmptyRecycleBin(int userId = Constants.Security.SuperUserId);
/// <summary>
/// Deletes all media of specified type. All children of deleted media is moved to Recycle Bin.
/// </summary>
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using Umbraco.Core.Events;
@@ -1889,8 +1888,9 @@ namespace Umbraco.Core.Services.Implement
content.ParentId = parentId;
// get the level delta (old pos to new pos)
// note that recycle bin (id:-20) level is 0!
var levelDelta = 1 - content.Level + (parent?.Level ?? 0);
var levelDelta = parent == null
? 1 - content.Level + (parentId == Constants.System.RecycleBinContent ? 1 : 0)
: parent.Level + 1 - content.Level;
var paths = new Dictionary<int, string>();
@@ -1939,14 +1939,7 @@ namespace Umbraco.Core.Services.Implement
/// <summary>
/// Empties the Recycle Bin by deleting all <see cref="IContent"/> that resides in the bin
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")]
public OperationResult EmptyRecycleBin() => EmptyRecycleBin(Constants.Security.SuperUserId);
/// <summary>
/// Empties the Recycle Bin by deleting all <see cref="IContent"/> that resides in the bin
/// </summary>
public OperationResult EmptyRecycleBin(int userId = Constants.Security.SuperUserId)
public OperationResult EmptyRecycleBin()
{
var nodeObjectType = Constants.ObjectTypes.Document;
var deleted = new List<IContent>();
@@ -1981,7 +1974,7 @@ namespace Umbraco.Core.Services.Implement
recycleBinEventArgs.RecycleBinEmptiedSuccessfully = true; // oh my?!
scope.Events.Dispatch(EmptiedRecycleBin, this, recycleBinEventArgs);
scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange<IContent>(x, TreeChangeTypes.Remove)).ToEventArgs());
Audit(AuditType.Delete, userId, Constants.System.RecycleBinContent, "Recycle bin emptied");
Audit(AuditType.Delete, 0, Constants.System.RecycleBinContent, "Recycle bin emptied");
scope.Complete();
}
@@ -2885,16 +2878,25 @@ namespace Umbraco.Core.Services.Implement
{
foreach (var property in blueprint.Properties)
{
var propertyCulture = property.PropertyType.VariesByCulture() ? culture : null;
content.SetValue(property.Alias, property.GetValue(propertyCulture), propertyCulture);
if (property.PropertyType.VariesByCulture())
{
content.SetValue(property.Alias, property.GetValue(culture), culture);
}
else
{
content.SetValue(property.Alias, property.GetValue());
}
}
content.Name = blueprint.Name;
if (!string.IsNullOrEmpty(culture))
{
content.SetCultureInfo(culture, blueprint.GetCultureName(culture), now);
}
}
return content;
}
@@ -1034,12 +1034,10 @@ namespace Umbraco.Core.Services.Implement
//strip the @inherits if it's there
snippetContent = StripPartialViewHeader(snippetContent);
//Update Model.Content to be Model when used as PartialView
//Update Model.Content. to be Model. when used as PartialView
if (partialViewType == PartialViewType.PartialView)
{
snippetContent = snippetContent
.Replace("Model.Content.", "Model.")
.Replace("(Model.Content)", "(Model)");
snippetContent = snippetContent.Replace("Model.Content.", "Model.");
}
var content = $"{partialViewHeader}{Environment.NewLine}{snippetContent}";
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -290,7 +289,7 @@ namespace Umbraco.Core.Services.Implement
scope.Events.Dispatch(Saved, this, saveEventArgs);
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IMedia>(media, TreeChangeTypes.RefreshNode).ToEventArgs());
}
if (withIdentity == false)
return;
@@ -717,7 +716,7 @@ namespace Umbraco.Core.Services.Implement
#endregion
#region Delete
/// <summary>
/// Permanently deletes an <see cref="IMedia"/> object
/// </summary>
@@ -976,8 +975,9 @@ namespace Umbraco.Core.Services.Implement
media.ParentId = parentId;
// get the level delta (old pos to new pos)
// note that recycle bin (id:-20) level is 0!
var levelDelta = 1 - media.Level + (parent?.Level ?? 0);
var levelDelta = parent == null
? 1 - media.Level + (parentId == Constants.System.RecycleBinMedia ? 1 : 0)
: parent.Level + 1 - media.Level;
var paths = new Dictionary<int, string>();
@@ -1024,15 +1024,7 @@ namespace Umbraco.Core.Services.Implement
/// <summary>
/// Empties the Recycle Bin by deleting all <see cref="IMedia"/> that resides in the bin
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")]
public OperationResult EmptyRecycleBin() => EmptyRecycleBin(Constants.Security.SuperUserId);
/// <summary>
/// Empties the Recycle Bin by deleting all <see cref="IMedia"/> that resides in the bin
/// </summary>
/// <param name="userId">Optional Id of the User emptying the Recycle Bin</param>
public OperationResult EmptyRecycleBin(int userId = Constants.Security.SuperUserId)
public OperationResult EmptyRecycleBin()
{
var nodeObjectType = Constants.ObjectTypes.Media;
var deleted = new List<IMedia>();
@@ -1071,7 +1063,7 @@ namespace Umbraco.Core.Services.Implement
args.CanCancel = false;
scope.Events.Dispatch(EmptiedRecycleBin, this, args);
scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange<IMedia>(x, TreeChangeTypes.Remove)).ToEventArgs());
Audit(AuditType.Delete, userId, Constants.System.RecycleBinMedia, "Empty Media recycle bin");
Audit(AuditType.Delete, 0, Constants.System.RecycleBinMedia, "Empty Media recycle bin");
scope.Complete();
}
-7
View File
@@ -55,12 +55,6 @@
</ItemGroup>
<ItemGroup>
<!-- note: NuGet deals with transitive references now -->
<PackageReference Include="Serilog.Sinks.Async">
<Version>1.3.0</Version>
</PackageReference>
<PackageReference Include="Serilog.Sinks.Map">
<Version>1.0.0</Version>
</PackageReference>
<PackageReference Include="Umbraco.Code">
<Version>1.0.5</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
@@ -225,7 +219,6 @@
<Compile Include="Mapping\MapDefinitionCollectionBuilder.cs" />
<Compile Include="Mapping\IMapDefinition.cs" />
<Compile Include="Mapping\UmbracoMapper.cs" />
<Compile Include="Migrations\Upgrade\V_8_1_0\ConvertTinyMceAndGridMediaUrlsToLocalLink.cs" />
<Compile Include="Models\CultureImpact.cs" />
<Compile Include="Models\PublishedContent\ILivePublishedModelFactory.cs" />
<Compile Include="Persistence\Dtos\PropertyTypeCommonDto.cs" />
@@ -159,7 +159,6 @@ namespace Umbraco.Tests.Cache
TestObjects.GetUmbracoSettings(),
TestObjects.GetGlobalSettings(),
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>()),
Mock.Of<IUserService>());
// just assert it does not throw
@@ -81,7 +81,6 @@ namespace Umbraco.Tests.Cache.PublishedCache
new WebSecurity(_httpContextFactory.HttpContext, Current.Services.UserService, globalSettings),
umbracoSettings,
Enumerable.Empty<IUrlProvider>(),
Enumerable.Empty<IMediaUrlProvider>(),
globalSettings,
new TestVariationContextAccessor());
@@ -4,7 +4,6 @@ using System.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Compose;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
@@ -300,19 +299,11 @@ namespace Umbraco.Tests.Components
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
Composed.Clear();
Assert.Throws<Exception>(() => composers.Compose());
Console.WriteLine("throws:");
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
var requirements = composers.GetRequirements(false);
Console.WriteLine(Composers.GetComposersReport(requirements));
types = new[] { typeof(Composer2) };
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
Composed.Clear();
Assert.Throws<Exception>(() => composers.Compose());
Console.WriteLine("throws:");
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
requirements = composers.GetRequirements(false);
Console.WriteLine(Composers.GetComposersReport(requirements));
types = new[] { typeof(Composer12) };
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
@@ -358,25 +349,6 @@ namespace Umbraco.Tests.Components
Assert.AreEqual(typeof(Composer27), Composed[1]);
}
[Test]
public void AllComposers()
{
var typeLoader = new TypeLoader(AppCaches.Disabled.RuntimeCache, IOHelper.MapPath("~/App_Data/TEMP"), Mock.Of<IProfilingLogger>());
var register = MockRegister();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Run));
var types = typeLoader.GetTypes<IComposer>().Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web"));
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
var requirements = composers.GetRequirements();
var report = Composers.GetComposersReport(requirements);
Console.WriteLine(report);
var composerTypes = composers.SortComposers(requirements);
foreach (var type in composerTypes)
Console.WriteLine(type);
}
#region Compothings
public class TestComposerBase : IComposer
@@ -27,7 +27,10 @@ namespace Umbraco.Tests.Composing
public void Initialize()
{
// this ensures it's reset
_typeLoader = new TypeLoader(NoAppCache.Instance, IOHelper.MapPath("~/App_Data/TEMP"), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()), false);
_typeLoader = new TypeLoader(NoAppCache.Instance, IOHelper.MapPath("~/App_Data/TEMP"), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
foreach (var file in Directory.GetFiles(IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "TypesCache")))
File.Delete(file);
// for testing, we'll specify which assemblies are scanned for the PluginTypeResolver
// TODO: Should probably update this so it only searches this assembly and add custom types to be found
@@ -381,9 +381,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
}
}
public override IPublishedContent GetById(bool preview, Udi nodeId)
=> throw new NotSupportedException();
public override bool HasById(bool preview, int contentId)
{
return GetXml(preview).CreateNavigator().MoveToId(contentId.ToString(CultureInfo.InvariantCulture));
@@ -97,9 +97,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
throw new NotImplementedException();
}
public override IPublishedContent GetById(bool preview, Udi nodeId)
=> throw new NotSupportedException();
public override bool HasById(bool preview, int contentId)
{
return GetUmbracoMedia(contentId) != null;
+1 -81
View File
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using NUnit.Framework;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models;
@@ -110,68 +108,6 @@ namespace Umbraco.Tests.Mapping
var target = mapper.Map<IEnumerable<ContentPropertyDto>>(source);
}
[Test]
[Explicit]
public void ConcurrentMap()
{
var definitions = new MapDefinitionCollection(new IMapDefinition[]
{
new MapperDefinition1(),
new MapperDefinition3(),
});
var mapper = new UmbracoMapper(definitions);
// the mapper currently has a map from Thing1 to Thing2
// because Thing3 inherits from Thing1, it will map a Thing3 instance,
// and register a new map from Thing3 to Thing2,
// thus modifying its internal dictionaries
// if timing is good, and mapper does have non-concurrent dictionaries, it fails
// practically, to reproduce, one needs to add a 1s sleep in the mapper's loop
// hence, this test is explicit
var thing3 = new Thing3 { Value = "value" };
var thing4 = new Thing4();
Exception caught = null;
void ThreadLoop()
{
// keep failing at mapping - and looping through the maps
for (var i = 0; i < 10; i++)
{
try
{
mapper.Map<Thing2>(thing4);
}
catch (Exception e)
{
caught = e;
Console.WriteLine($"{e.GetType().Name} {e.Message}");
}
}
Console.WriteLine("done");
}
var thread = new Thread(ThreadLoop);
thread.Start();
Thread.Sleep(1000);
try
{
Console.WriteLine($"{DateTime.Now:O} mapping");
var thing2 = mapper.Map<Thing2>(thing3);
Console.WriteLine($"{DateTime.Now:O} mapped");
Assert.IsNotNull(thing2);
Assert.AreEqual("value", thing2.Value);
}
finally
{
thread.Join();
}
}
private class Thing1
{
public string Value { get; set; }
@@ -185,9 +121,6 @@ namespace Umbraco.Tests.Mapping
public string Value { get; set; }
}
private class Thing4
{ }
private class MapperDefinition1 : IMapDefinition
{
public void DefineMaps(UmbracoMapper mapper)
@@ -211,18 +144,5 @@ namespace Umbraco.Tests.Mapping
private static void Map(Property source, ContentPropertyDto target, MapperContext context)
{ }
}
private class MapperDefinition3 : IMapDefinition
{
public void DefineMaps(UmbracoMapper mapper)
{
// just some random things so that the mapper contains things
mapper.Define<int, object>();
mapper.Define<string, object>();
mapper.Define<double, object>();
mapper.Define<UmbracoMapper, object>();
mapper.Define<Property, object>();
}
}
}
}
+9 -43
View File
@@ -15,47 +15,13 @@ namespace Umbraco.Tests.Models
[TestFixture]
public class ContentTypeTests : UmbracoTestBase
{
[Test]
[Ignore("Ignoring this test until we actually enforce this, see comments in ContentTypeBase.PropertyTypesChanged")]
public void Cannot_Add_Duplicate_Property_Aliases()
{
var contentType = MockedContentTypes.CreateBasicContentType();
contentType.PropertyGroups.Add(new PropertyGroup(new PropertyTypeCollection(false, new[]
{
new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar){ Alias = "myPropertyType" }
})));
Assert.Throws<InvalidOperationException>(() =>
contentType.PropertyTypeCollection.Add(
new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar) { Alias = "myPropertyType" }));
}
[Test]
[Ignore("Ignoring this test until we actually enforce this, see comments in ContentTypeBase.PropertyTypesChanged")]
public void Cannot_Update_Duplicate_Property_Aliases()
{
var contentType = MockedContentTypes.CreateBasicContentType();
contentType.PropertyGroups.Add(new PropertyGroup(new PropertyTypeCollection(false, new[]
{
new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar){ Alias = "myPropertyType" }
})));
contentType.PropertyTypeCollection.Add(new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar) { Alias = "myPropertyType2" });
var toUpdate = contentType.PropertyTypeCollection["myPropertyType2"];
Assert.Throws<InvalidOperationException>(() => toUpdate.Alias = "myPropertyType");
}
[Test]
public void Can_Deep_Clone_Content_Type_Sort()
{
var contentType = new ContentTypeSort(new Lazy<int>(() => 3), 4, "test");
var clone = (ContentTypeSort)contentType.DeepClone();
var clone = (ContentTypeSort) contentType.DeepClone();
Assert.AreNotSame(clone, contentType);
Assert.AreEqual(clone, contentType);
Assert.AreEqual(clone.Id.Value, contentType.Id.Value);
@@ -88,7 +54,7 @@ namespace Umbraco.Tests.Models
contentType.Id = 10;
contentType.CreateDate = DateTime.Now;
contentType.CreatorId = 22;
contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate")
contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate")
{
Id = 88
});
@@ -151,12 +117,12 @@ namespace Umbraco.Tests.Models
{
group.Id = ++i;
}
contentType.AllowedTemplates = new[] { new Template((string)"Name", (string)"name") { Id = 200 }, new Template((string)"Name2", (string)"name2") { Id = 201 } };
contentType.AllowedTemplates = new[] { new Template((string) "Name", (string) "name") { Id = 200 }, new Template((string) "Name2", (string) "name2") { Id = 201 } };
contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy<int>(() => 888), 8, "sub"), new ContentTypeSort(new Lazy<int>(() => 889), 9, "sub2") };
contentType.Id = 10;
contentType.CreateDate = DateTime.Now;
contentType.CreatorId = 22;
contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate")
contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate")
{
Id = 88
});
@@ -201,12 +167,12 @@ namespace Umbraco.Tests.Models
{
group.Id = ++i;
}
contentType.AllowedTemplates = new[] { new Template((string)"Name", (string)"name") { Id = 200 }, new Template((string)"Name2", (string)"name2") { Id = 201 } };
contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy<int>(() => 888), 8, "sub"), new ContentTypeSort(new Lazy<int>(() => 889), 9, "sub2") };
contentType.AllowedTemplates = new[] { new Template((string) "Name", (string) "name") { Id = 200 }, new Template((string) "Name2", (string) "name2") { Id = 201 } };
contentType.AllowedContentTypes = new[] {new ContentTypeSort(new Lazy<int>(() => 888), 8, "sub"), new ContentTypeSort(new Lazy<int>(() => 889), 9, "sub2")};
contentType.Id = 10;
contentType.CreateDate = DateTime.Now;
contentType.CreatorId = 22;
contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate")
contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate")
{
Id = 88
});
@@ -298,12 +264,12 @@ namespace Umbraco.Tests.Models
{
propertyType.Id = ++i;
}
contentType.AllowedTemplates = new[] { new Template((string)"Name", (string)"name") { Id = 200 }, new Template((string)"Name2", (string)"name2") { Id = 201 } };
contentType.AllowedTemplates = new[] { new Template((string) "Name", (string) "name") { Id = 200 }, new Template((string) "Name2", (string) "name2") { Id = 201 } };
contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy<int>(() => 888), 8, "sub"), new ContentTypeSort(new Lazy<int>(() => 889), 9, "sub2") };
contentType.Id = 10;
contentType.CreateDate = DateTime.Now;
contentType.CreatorId = 22;
contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate")
contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate")
{
Id = 88
});
@@ -171,6 +171,7 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
//NOTE: This tests for left join logic (rev 7b14e8eacc65f82d4f184ef46c23340c09569052)
[Test]
public void Can_Get_All_Members_When_No_Properties_Assigned()
@@ -199,6 +200,7 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
[Test]
public void Can_Get_Member_Type_By_Id()
{
@@ -231,114 +233,22 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
// See: https://github.com/umbraco/Umbraco-CMS/issues/4963#issuecomment-483516698
[Test]
public void Bug_Changing_Built_In_Member_Type_Property_Type_Aliases_Results_In_Exception()
{
var stubs = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
var provider = TestObjects.GetScopeProvider(Logger);
using (provider.CreateScope())
{
var repository = CreateRepository(provider);
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType("mtype");
// created without the stub properties
Assert.AreEqual(1, memberType.PropertyGroups.Count);
Assert.AreEqual(3, memberType.PropertyTypes.Count());
// saving *new* member type adds the stub properties
repository.Save(memberType);
// saving has added (and saved) the stub properties
Assert.AreEqual(2, memberType.PropertyGroups.Count);
Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count());
foreach (var stub in stubs)
{
var prop = memberType.PropertyTypes.First(x => x.Alias == stub.Key);
prop.Alias = prop.Alias + "__0000";
}
// saving *existing* member type does *not* ensure stub properties
repository.Save(memberType);
// therefore, nothing has changed
Assert.AreEqual(2, memberType.PropertyGroups.Count);
Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count());
// fetching ensures that the stub properties are there
memberType = repository.Get("mtype");
Assert.IsNotNull(memberType);
Assert.AreEqual(2, memberType.PropertyGroups.Count);
Assert.AreEqual(3 + stubs.Count * 2, memberType.PropertyTypes.Count());
}
}
[Test]
public void Built_In_Member_Type_Properties_Are_Automatically_Added_When_Creating()
{
var stubs = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
var provider = TestObjects.GetScopeProvider(Logger);
using (provider.CreateScope())
using (var scope = provider.CreateScope())
{
var repository = CreateRepository(provider);
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
// created without the stub properties
Assert.AreEqual(1, memberType.PropertyGroups.Count);
Assert.AreEqual(3, memberType.PropertyTypes.Count());
// saving *new* member type adds the stub properties
repository.Save(memberType);
// saving has added (and saved) the stub properties
Assert.AreEqual(2, memberType.PropertyGroups.Count);
Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count());
// getting with stub properties
memberType = repository.Get(memberType.Id);
Assert.AreEqual(2, memberType.PropertyGroups.Count);
Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count());
}
}
[Test]
public void Built_In_Member_Type_Properties_Missing_Are_Automatically_Added_When_Creating()
{
var stubs = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
var provider = TestObjects.GetScopeProvider(Logger);
using (provider.CreateScope())
{
var repository = CreateRepository(provider);
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
// created without the stub properties
Assert.AreEqual(1, memberType.PropertyGroups.Count);
Assert.AreEqual(3, memberType.PropertyTypes.Count());
// add one stub property, others are still missing
memberType.AddPropertyType(stubs.First().Value, Constants.Conventions.Member.StandardPropertiesGroupName);
// saving *new* member type adds the (missing) stub properties
repository.Save(memberType);
// saving has added (and saved) the (missing) stub properties
Assert.AreEqual(2, memberType.PropertyGroups.Count);
Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count());
// getting with stub properties
memberType = repository.Get(memberType.Id);
Assert.AreEqual(2, memberType.PropertyGroups.Count);
Assert.AreEqual(3 + stubs.Count, memberType.PropertyTypes.Count());
Assert.That(memberType.PropertyTypes.Count(), Is.EqualTo(3 + Constants.Conventions.Member.GetStandardPropertyTypeStubs().Count));
Assert.That(memberType.PropertyGroups.Count(), Is.EqualTo(2));
}
}
@@ -71,7 +71,6 @@ namespace Umbraco.Tests.PublishedContent
new WebSecurity(httpContext, Current.Services.UserService, globalSettings),
TestObjects.GetUmbracoSettings(),
Enumerable.Empty<IUrlProvider>(),
Enumerable.Empty<IMediaUrlProvider>(),
globalSettings,
new TestVariationContextAccessor());
@@ -92,9 +92,6 @@ namespace Umbraco.Tests.PublishedContent
throw new NotImplementedException();
}
public override IPublishedContent GetById(bool preview, Udi nodeId)
=> throw new NotSupportedException();
public override bool HasById(bool preview, int contentId)
{
return _content.ContainsKey(contentId);
@@ -1,152 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
using Newtonsoft.Json;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Tests.PublishedContent;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Stubs;
using Umbraco.Tests.Testing;
using Umbraco.Web.Routing;
namespace Umbraco.Tests.Routing
{
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)]
public class MediaUrlProviderTests : BaseWebTest
{
private DefaultMediaUrlProvider _mediaUrlProvider;
public override void SetUp()
{
base.SetUp();
_mediaUrlProvider = new DefaultMediaUrlProvider();
}
public override void TearDown()
{
base.TearDown();
_mediaUrlProvider = null;
}
[Test]
public void Get_Media_Url_Resolves_Url_From_Upload_Property_Editor()
{
const string expected = "/media/rfeiw584/test.jpg";
var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider });
var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, expected, null);
var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Auto, null, null);
Assert.AreEqual(expected, resolvedUrl);
}
[Test]
public void Get_Media_Url_Resolves_Url_From_Image_Cropper_Property_Editor()
{
const string expected = "/media/rfeiw584/test.jpg";
var configuration = new ImageCropperConfiguration();
var imageCropperValue = JsonConvert.SerializeObject(new ImageCropperValue
{
Src = expected
});
var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider });
var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.ImageCropper, imageCropperValue, configuration);
var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Auto, null, null);
Assert.AreEqual(expected, resolvedUrl);
}
[Test]
public void Get_Media_Url_Can_Resolve_Absolute_Url()
{
const string mediaUrl = "/media/rfeiw584/test.jpg";
var expected = $"http://localhost{mediaUrl}";
var umbracoContext = GetUmbracoContext("http://localhost", mediaUrlProviders: new[] { _mediaUrlProvider });
var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, mediaUrl, null);
var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Absolute, null, null);
Assert.AreEqual(expected, resolvedUrl);
}
[Test]
public void Get_Media_Url_Returns_Empty_String_When_PropertyType_Is_Not_Supported()
{
var umbracoContext = GetUmbracoContext("/", mediaUrlProviders: new[] { _mediaUrlProvider });
var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.Boolean, "0", null);
var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "test", UrlProviderMode.Absolute, null, null);
Assert.AreEqual(string.Empty, resolvedUrl);
}
[Test]
public void Get_Media_Url_Can_Resolve_Variant_Property_Url()
{
var umbracoContext = GetUmbracoContext("http://localhost", mediaUrlProviders: new[] { _mediaUrlProvider });
var umbracoFilePropertyType = CreatePropertyType(Constants.PropertyEditors.Aliases.UploadField, null, ContentVariation.Culture);
const string enMediaUrl = "/media/rfeiw584/en.jpg";
const string daMediaUrl = "/media/uf8ewud2/da.jpg";
var property = new SolidPublishedPropertyWithLanguageVariants
{
Alias = "umbracoFile",
PropertyType = umbracoFilePropertyType,
};
property.SetValue("en", enMediaUrl, true);
property.SetValue("da", daMediaUrl);
var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty<string>(), new [] { umbracoFilePropertyType }, ContentVariation.Culture);
var publishedContent = new SolidPublishedContent(contentType) {Properties = new[] {property}};
var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, "umbracoFile", UrlProviderMode.Auto, "da", null);
Assert.AreEqual(daMediaUrl, resolvedUrl);
}
private static TestPublishedContent CreatePublishedContent(string propertyEditorAlias, object propertyValue, object dataTypeConfiguration)
{
var umbracoFilePropertyType = CreatePropertyType(propertyEditorAlias, dataTypeConfiguration, ContentVariation.Nothing);
var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty<string>(),
new[] {umbracoFilePropertyType}, ContentVariation.Nothing);
return new TestPublishedContent(contentType, 1234, Guid.NewGuid(),
new Dictionary<string, object> {{"umbracoFile", propertyValue } }, false);
}
private static PublishedPropertyType CreatePropertyType(string propertyEditorAlias, object dataTypeConfiguration, ContentVariation variation)
{
var uploadDataType = new PublishedDataType(1234, propertyEditorAlias, new Lazy<object>(() => dataTypeConfiguration));
var propertyValueConverters = new PropertyValueConverterCollection(new IPropertyValueConverter[]
{
new UploadPropertyConverter(),
new ImageCropperValueConverter(),
});
var publishedModelFactory = Mock.Of<IPublishedModelFactory>();
var publishedContentTypeFactory = new Mock<IPublishedContentTypeFactory>();
publishedContentTypeFactory.Setup(x => x.GetDataType(It.IsAny<int>()))
.Returns(uploadDataType);
return new PublishedPropertyType("umbracoFile", 42, true, variation, propertyValueConverters, publishedModelFactory, publishedContentTypeFactory.Object);
}
}
}
@@ -118,7 +118,6 @@ namespace Umbraco.Tests.Scoping
new WebSecurity(httpContext, Current.Services.UserService, globalSettings),
umbracoSettings ?? SettingsForTests.GetDefaultUmbracoSettings(),
urlProviders ?? Enumerable.Empty<IUrlProvider>(),
Enumerable.Empty<IMediaUrlProvider>(),
globalSettings,
new TestVariationContextAccessor());
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using Microsoft.Owin;
using Moq;
@@ -34,7 +33,7 @@ namespace Umbraco.Tests.Security
Mock.Of<HttpContextBase>(),
Mock.Of<IPublishedSnapshotService>(),
new WebSecurity(Mock.Of<HttpContextBase>(), Current.Services.UserService, globalSettings),
TestObjects.GetUmbracoSettings(), new List<IUrlProvider>(), Enumerable.Empty<IMediaUrlProvider>(), globalSettings,
TestObjects.GetUmbracoSettings(), new List<IUrlProvider>(),globalSettings,
new TestVariationContextAccessor());
var runtime = Mock.Of<IRuntimeState>(x => x.Level == RuntimeLevel.Install);
@@ -54,7 +53,7 @@ namespace Umbraco.Tests.Security
Mock.Of<HttpContextBase>(),
Mock.Of<IPublishedSnapshotService>(),
new WebSecurity(Mock.Of<HttpContextBase>(), Current.Services.UserService, globalSettings),
TestObjects.GetUmbracoSettings(), new List<IUrlProvider>(), Enumerable.Empty<IMediaUrlProvider>(), globalSettings,
TestObjects.GetUmbracoSettings(), new List<IUrlProvider>(), globalSettings,
new TestVariationContextAccessor());
var runtime = Mock.Of<IRuntimeState>(x => x.Level == RuntimeLevel.Run);
@@ -139,7 +139,6 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
webSecurity.Object,
Mock.Of<IUmbracoSettingsSection>(section => section.WebRouting == Mock.Of<IWebRoutingSection>(routingSection => routingSection.UrlProviderMode == "Auto")),
Enumerable.Empty<IUrlProvider>(),
Enumerable.Empty<IMediaUrlProvider>(),
globalSettings,
new TestVariationContextAccessor());
@@ -122,7 +122,6 @@ namespace Umbraco.Tests.TestHelpers
var umbracoSettings = GetUmbracoSettings();
var globalSettings = GetGlobalSettings();
var urlProviders = new UrlProviderCollection(Enumerable.Empty<IUrlProvider>());
var mediaUrlProviders = new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>());
if (accessor == null) accessor = new TestUmbracoContextAccessor();
@@ -134,7 +133,6 @@ namespace Umbraco.Tests.TestHelpers
umbracoSettings,
globalSettings,
urlProviders,
mediaUrlProviders,
Mock.Of<IUserService>());
return umbracoContextFactory.EnsureUmbracoContext(httpContext).UmbracoContext;
@@ -353,7 +353,7 @@ namespace Umbraco.Tests.TestHelpers
}
}
protected UmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable<IUrlProvider> urlProviders = null, IEnumerable<IMediaUrlProvider> mediaUrlProviders = null, IGlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null)
protected UmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable<IUrlProvider> urlProviders = null, IGlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null)
{
// ensure we have a PublishedCachesService
var service = snapshotService ?? PublishedSnapshotService as PublishedSnapshotService;
@@ -380,7 +380,6 @@ namespace Umbraco.Tests.TestHelpers
Factory.GetInstance<IGlobalSettings>()),
umbracoSettings ?? Factory.GetInstance<IUmbracoSettingsSection>(),
urlProviders ?? Enumerable.Empty<IUrlProvider>(),
mediaUrlProviders ?? Enumerable.Empty<IMediaUrlProvider>(),
globalSettings ?? Factory.GetInstance<IGlobalSettings>(),
new TestVariationContextAccessor());
@@ -80,7 +80,7 @@ namespace Umbraco.Tests.Testing.TestingTests
.Returns(UrlInfo.Url("/hello/world/1234"));
var urlProvider = urlProviderMock.Object;
var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }, Enumerable.Empty<IMediaUrlProvider>(), umbracoContext.VariationContextAccessor);
var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }, umbracoContext.VariationContextAccessor);
var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty<string>(), Enumerable.Empty<PublishedPropertyType>(), ContentVariation.Nothing);
var publishedContent = Mock.Of<IPublishedContent>();
-1
View File
@@ -143,7 +143,6 @@
<Compile Include="PublishedContent\PublishedContentSnapshotTestBase.cs" />
<Compile Include="PublishedContent\SolidPublishedSnapshot.cs" />
<Compile Include="PublishedContent\NuCacheTests.cs" />
<Compile Include="Routing\MediaUrlProviderTests.cs" />
<Compile Include="Runtimes\StandaloneTests.cs" />
<Compile Include="Routing\GetContentUrlsTests.cs" />
<Compile Include="Services\AmbiguousEventTests.cs" />
@@ -72,7 +72,6 @@ namespace Umbraco.Tests.Web.Mvc
TestObjects.GetUmbracoSettings(),
globalSettings,
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -102,7 +101,6 @@ namespace Umbraco.Tests.Web.Mvc
TestObjects.GetUmbracoSettings(),
globalSettings,
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -132,7 +130,6 @@ namespace Umbraco.Tests.Web.Mvc
TestObjects.GetUmbracoSettings(),
globalSettings,
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -162,7 +159,6 @@ namespace Umbraco.Tests.Web.Mvc
TestObjects.GetUmbracoSettings(),
globalSettings,
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -47,7 +47,6 @@ namespace Umbraco.Tests.Web.Mvc
TestObjects.GetUmbracoSettings(),
globalSettings,
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -75,7 +74,6 @@ namespace Umbraco.Tests.Web.Mvc
TestObjects.GetUmbracoSettings(),
globalSettings,
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -106,7 +104,6 @@ namespace Umbraco.Tests.Web.Mvc
Mock.Of<IUmbracoSettingsSection>(section => section.WebRouting == Mock.Of<IWebRoutingSection>(routingSection => routingSection.UrlProviderMode == "Auto")),
globalSettings,
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -144,7 +141,6 @@ namespace Umbraco.Tests.Web.Mvc
Mock.Of<IUmbracoSettingsSection>(section => section.WebRouting == webRoutingSettings),
globalSettings,
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -440,7 +440,6 @@ namespace Umbraco.Tests.Web.Mvc
new WebSecurity(http, Current.Services.UserService, globalSettings),
TestObjects.GetUmbracoSettings(),
Enumerable.Empty<IUrlProvider>(),
Enumerable.Empty<IMediaUrlProvider>(),
globalSettings,
new TestVariationContextAccessor());
@@ -61,12 +61,10 @@ namespace Umbraco.Tests.Web
[TestCase("", "")]
[TestCase("hello href=\"{localLink:1234}\" world ", "hello href=\"/my-test-url\" world ")]
[TestCase("hello href=\"{localLink:umb://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
[TestCase("hello href=\"{localLink:umb://document/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
[TestCase("hello href=\"{localLink:umb://media/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/media/1001/my-image.jpg\" world ")]
[TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
[TestCase("hello href=\"{localLink:umb://document-type/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
//this one has an invalid char so won't match
[TestCase("hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ")]
[TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"#\" world ")]
[TestCase("hello href=\"{localLink:umb^://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"{localLink:umb^://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ")]
public void ParseLocalLinks(string input, string result)
{
var serviceCtxMock = new TestObjects(null).GetServiceContextMock();
@@ -79,7 +77,7 @@ namespace Umbraco.Tests.Web
// return Attempt.Succeed(1234);
// });
//setup a mock url provider which we'll use for testing
//setup a mock url provider which we'll use fo rtesting
var testUrlProvider = new Mock<IUrlProvider>();
testUrlProvider
.Setup(x => x.GetUrl(It.IsAny<UmbracoContext>(), It.IsAny<IPublishedContent>(), It.IsAny<UrlProviderMode>(), It.IsAny<string>(), It.IsAny<Uri>()))
@@ -98,10 +96,6 @@ namespace Umbraco.Tests.Web
Mock.Get(snapshot).Setup(x => x.Content).Returns(contentCache);
var snapshotService = Mock.Of<IPublishedSnapshotService>();
Mock.Get(snapshotService).Setup(x => x.CreatePublishedSnapshot(It.IsAny<string>())).Returns(snapshot);
var media = Mock.Of<IPublishedContent>();
Mock.Get(media).Setup(x => x.Url).Returns("/media/1001/my-image.jpg");
var mediaCache = Mock.Of<IPublishedMediaCache>();
Mock.Get(mediaCache).Setup(x => x.GetById(It.IsAny<Guid>())).Returns(media);
var umbracoContextFactory = new UmbracoContextFactory(
Umbraco.Web.Composing.Current.UmbracoContextAccessor,
@@ -111,12 +105,11 @@ namespace Umbraco.Tests.Web
Mock.Of<IUmbracoSettingsSection>(section => section.WebRouting == Mock.Of<IWebRoutingSection>(routingSection => routingSection.UrlProviderMode == "Auto")),
globalSettings,
new UrlProviderCollection(new[] { testUrlProvider.Object }),
new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>()),
Mock.Of<IUserService>());
using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>()))
{
var output = TemplateUtilities.ParseInternalLinks(input, reference.UmbracoContext.UrlProvider, mediaCache);
var output = TemplateUtilities.ParseInternalLinks(input, reference.UmbracoContext.UrlProvider);
Assert.AreEqual(result, output);
}
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
@@ -32,7 +31,6 @@ namespace Umbraco.Tests.Web
new WebSecurity(Mock.Of<HttpContextBase>(), Current.Services.UserService, TestObjects.GetGlobalSettings()),
TestObjects.GetUmbracoSettings(),
new List<IUrlProvider>(),
Enumerable.Empty<IMediaUrlProvider>(),
TestObjects.GetGlobalSettings(),
new TestVariationContextAccessor());
var r1 = new RouteData();
@@ -51,7 +49,6 @@ namespace Umbraco.Tests.Web
new WebSecurity(Mock.Of<HttpContextBase>(), Current.Services.UserService, TestObjects.GetGlobalSettings()),
TestObjects.GetUmbracoSettings(),
new List<IUrlProvider>(),
Enumerable.Empty<IMediaUrlProvider>(),
TestObjects.GetGlobalSettings(),
new TestVariationContextAccessor());
@@ -80,7 +77,6 @@ namespace Umbraco.Tests.Web
new WebSecurity(Mock.Of<HttpContextBase>(), Current.Services.UserService, TestObjects.GetGlobalSettings()),
TestObjects.GetUmbracoSettings(),
new List<IUrlProvider>(),
Enumerable.Empty<IMediaUrlProvider>(),
TestObjects.GetGlobalSettings(),
new TestVariationContextAccessor());
+2 -9
View File
@@ -1,10 +1,3 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 version, not dead, > 0.5%, not ie 11"
}
]
]
}
"presets": ["@babel/preset-env"]
}
@@ -6,6 +6,46 @@
This documentation relates to the angular, js, css and less APIs for developing back office components
##Running the site with mocked data
This won't require any database or setup, as everything is running through node. All you have to do is install
node and grunt on either windows or OSX and the entire setup is ready for you.
###Install node.js
We need node to run tests and automated less compiling and other automated tasks. go to http://nodejs.org. Node.js is a powerfull javascript engine, which allows us to run all our tests and tasks written in javascript locally.
*note:* On windows you might need to restart explorer.exe to register node.
###Install dependencies
Next we need to install all the required packages. This is done with the package tool, included with node.js, open /Umbraco.Belle.Client in cmd.exe or osx terminal and run the command:
npm install
this will fetch all needed packages to your local machine.
###Install grunt globally
Grunt is a task runner for node.js, and we use it for all automated tasks in the build process. For convenience we need to install it globally on your machine, so it can be used directly in cmd.exe or the terminal.
So run the command:
npm install grunt-cli -g
*note:* On windows you might need to restart explorer.exe to register the grunt cmd.
*note:* On OSX you might need to run:
sudo npm install grunt-cli -g
Now that you have node and grunt installed, you can open `/Umbraco.Web.UI.Client` in either `cmd.exe` or terminal and run:
grunt dev
This will build the site, merge less files, run tests and create the /Build folder, and finally open the site in your
browser.
##Getting started
The current app is built, following conventions from angularJs and bootstrap. To get started with the applicaton you will need to atleast know the basics of these frameworks
+4
View File
@@ -25,6 +25,10 @@ module.exports = {
security: { files: ["./src/common/interceptors/**/*.js"], out: "umbraco.interceptors.js" }
},
ts: {
services: { files: ["./src/common/services/**/*.ts"], out: "./src/common/services/build/temp/" }
},
//selectors for copying all views into the build
//processed in the views task
views:{
@@ -6,5 +6,5 @@ var runSequence = require('run-sequence');
// Build - build the files ready for production
gulp.task('build', function(cb) {
runSequence(["js", "dependencies", "less", "views"], "test:unit", cb);
runSequence(["typescript", "js", "dependencies", "less", "views"], "test:unit", cb);
});
+1 -1
View File
@@ -6,5 +6,5 @@ var runSequence = require('run-sequence');
// Dev - build the files ready for development and start watchers
gulp.task('dev', function(cb) {
runSequence(["dependencies", "js", "less", "views"], "watch", cb);
runSequence(["dependencies", "typescript", "js", "less", "views"], "watch", cb);
});
@@ -9,5 +9,5 @@ gulp.task('fastdev', function(cb) {
global.isProd = false;
runSequence(["dependencies", "js", "less", "views"], "watch", cb);
runSequence(["dependencies", "typescript", "js", "less", "views"], "watch", cb);
});
@@ -0,0 +1,31 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var _ = require('lodash');
var MergeStream = require('merge-stream');
var processTs = require('../util/processTypescript');
/**************************
* Copies all angular JS files into their seperate umbraco.*.js file
**************************/
gulp.task('typescript', function () {
//we run multiple streams, so merge them all together
var stream = new MergeStream();
stream.add(
gulp.src(config.sources.globs.js)
.pipe(gulp.dest(config.root + config.targets.js))
);
// compile TS
_.forEach(config.sources.ts, function (group) {
if(group.files.length > 0)
stream.add (processTs(group.files, group.out));
});
return stream;
});
@@ -7,7 +7,6 @@ var babel = require("gulp-babel");
var sort = require('gulp-sort');
var concat = require('gulp-concat');
var wrap = require("gulp-wrap-js");
var embedTemplates = require('gulp-angular-embed-templates');
module.exports = function(files, out) {
@@ -23,7 +22,6 @@ module.exports = function(files, out) {
// sort files in stream by path or any custom sort comparator
task = task.pipe(babel())
.pipe(sort())
.pipe(embedTemplates({ basePath: "./src/" }))
.pipe(concat(out))
.pipe(wrap('(function(){\n%= body %\n})();'))
.pipe(gulp.dest(config.root + config.targets.js));
@@ -31,4 +29,4 @@ module.exports = function(files, out) {
return task;
};
};
@@ -0,0 +1,21 @@
var config = require('../config');
var gulp = require('gulp');
var ts = require('gulp-typescript');
module.exports = function(files, out) {
var task = gulp.src(files);
var tsProject = ts.createProject('tsconfig.json');
// sort files in stream by path or any custom sort comparator
task = task.pipe(tsProject())
.pipe(gulp.dest(out));
return task;
};
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -29,7 +29,7 @@
"diff": "3.5.0",
"flatpickr": "4.5.2",
"font-awesome": "4.7.0",
"jquery": "^3.4.0",
"jquery": "3.3.1",
"jquery-ui-dist": "1.12.1",
"jquery-ui-touch-punch": "0.2.3",
"lazyload-js": "1.0.0",
@@ -41,6 +41,7 @@
"spectrum-colorpicker": "1.8.0",
"tinymce": "4.9.2",
"typeahead.js": "0.11.1",
"typescript": "^3.2.1",
"underscore": "1.9.1"
},
"devDependencies": {
@@ -50,7 +51,6 @@
"cssnano": "4.1.7",
"fs": "0.0.2",
"gulp": "^3.9.1",
"gulp-angular-embed-templates": "^2.3.0",
"gulp-babel": "8.0.0",
"gulp-clean-css": "4.0.0",
"gulp-cli": "^2.0.1",
@@ -65,6 +65,7 @@
"gulp-postcss": "8.0.0",
"gulp-rename": "1.4.0",
"gulp-sort": "2.0.0",
"gulp-typescript": "^5.0.1",
"gulp-watch": "5.0.1",
"gulp-wrap": "0.14.0",
"gulp-wrap-js": "0.4.1",
@@ -0,0 +1,3 @@
declare var angular: any;
declare var _: any;
declare var $q: any;
@@ -0,0 +1,4 @@
interface StringConstructor {
CreateGuid(): string;
}
@@ -1,7 +1,7 @@
(function () {
"use strict";
function AppHeaderDirective(eventsService, appState, userService, focusService) {
function AppHeaderDirective(eventsService, appState, userService) {
function link(scope, el, attr, ctrl) {
@@ -54,9 +54,7 @@
}
});
}));
scope.rememberFocus = focusService.rememberFocus;
scope.searchClick = function() {
var showSearch = appState.getSearchState("show");
appState.setSearchState("show", !showSearch);
@@ -103,4 +101,4 @@
angular.module("umbraco.directives").directive("umbAppHeader", AppHeaderDirective);
})();
})();
@@ -16,6 +16,7 @@
function UmbLoginController($scope, $location, currentUserResource, formHelper, mediaHelper, umbRequestHelper, Upload, localizationService, userService, externalLoginInfo, resetPasswordCodeInfo, $timeout, authResource, $q) {
const vm = this;
let twoFactorloginDialog = null;
vm.invitedUser = null;
@@ -68,9 +69,7 @@
).then(function (data) {
vm.labels.usernameLabel = data[0];
vm.labels.usernamePlaceholder = data[1];
});
vm.twoFactor = {};
})
function onInit() {
@@ -127,7 +126,6 @@
function togglePassword() {
var elem = $("form[name='vm.loginForm'] input[name='password']");
elem.attr("type", (elem.attr("type") === "text" ? "password" : "text"));
elem.focus();
$(".password-text.show, .password-text.hide").toggle();
}
@@ -188,18 +186,15 @@
vm.view = "set-password";
}
function loginSubmit() {
// make sure that we are returning to the login view.
vm.view = "login";
function loginSubmit(login, password) {
// TODO: Do validation properly like in the invite password update
//if the login and password are not empty we need to automatically
// validate them - this is because if there are validation errors on the server
// then the user has to change both username & password to resubmit which isn't ideal,
// so if they're not empty, we'll just make sure to set them to valid.
if (vm.login && vm.password && vm.login.length > 0 && vm.password.length > 0) {
if (login && password && login.length > 0 && password.length > 0) {
vm.loginForm.username.$setValidity('auth', true);
vm.loginForm.password.$setValidity('auth', true);
}
@@ -210,7 +205,7 @@
vm.loginStates.submitButton = "busy";
userService.authenticate(vm.login, vm.password)
userService.authenticate(login, password)
.then(function (data) {
vm.loginStates.submitButton = "success";
userService._retryRequestQueue(true);
@@ -223,7 +218,7 @@
//is Two Factor required?
if (reason.status === 402) {
vm.errorMsg = "Additional authentication required";
show2FALoginDialog(reason.data.twoFactorView);
show2FALoginDialog(reason.data.twoFactorView, submit);
}
else {
vm.loginStates.submitButton = "error";
@@ -407,12 +402,8 @@
});
}
function show2FALoginDialog(viewPath) {
vm.twoFactor.submitCallback = function submitCallback() {
vm.onLogin();
}
vm.twoFactor.view = viewPath;
vm.view = "2fa-login";
function show2FALoginDialog(view, callback) {
// TODO: show 2FA window
}
function resetInputValidation() {
@@ -13,7 +13,7 @@
}
};
function umbSearchController($timeout, backdropService, searchService, focusService) {
function umbSearchController($timeout, backdropService, searchService) {
var vm = this;
@@ -25,9 +25,6 @@
vm.handleKeyUp = handleKeyUp;
vm.closeSearch = closeSearch;
vm.focusSearch = focusSearch;
//we need to capture the focus before this element is initialized.
vm.focusBeforeOpening = focusService.getLastKnownFocus();
function onInit() {
vm.searchQuery = "";
@@ -73,10 +70,6 @@
* @param {object} event
*/
function handleKeyUp(event) {
event.stopPropagation();
event.preventDefault();
// esc
if(event.keyCode === 27) {
closeSearch();
@@ -87,9 +80,6 @@
* Used to proxy a callback
*/
function closeSearch() {
if(vm.focusBeforeOpening) {
vm.focusBeforeOpening.focus();
}
if(vm.onClose) {
vm.onClose();
}
@@ -125,4 +115,4 @@
angular.module('umbraco.directives').component('umbSearch', umbSearch);
})();
})();
@@ -117,8 +117,6 @@ Use this directive to render an umbraco button. The directive can be used to gen
vm.innerState = "init";
vm.buttonLabel = vm.label;
// is this a primary button style (i.e. anything but an 'info' button)?
vm.isPrimaryButtonStyle = vm.buttonStyle && vm.buttonStyle !== 'info';
if (vm.buttonStyle) {
@@ -1,105 +0,0 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbToggleGroup
@restrict E
@scope
@description
Use this directive to render a group of toggle buttons.
<h3>Markup example</h3>
<pre>
<div ng-controller="My.Controller as vm">
<umb-toggle-group
items="vm.items"
on-click="vm.toggle(item)">
</umb-toggle-group>
</div>
</pre>
<h3>Controller example</h3>
<pre>
(function () {
"use strict";
function Controller() {
var vm = this;
vm.toggle = toggle;
function toggle(item) {
if(item.checked) {
// do something if item is checked
}
else {
// do something else if item is unchecked
}
}
function init() {
vm.items = [{
name: "Item 1",
description: "Item 1 description",
checked: false,
disabled: false
}, {
name: "Item 2",
description: "Item 2 description",
checked: true,
disabled: true
}];
}
init();
}
angular.module("umbraco").controller("My.Controller", Controller);
})();
</pre>
@param {Array} items The items to list in the toggle group
@param {callback} onClick The function which should be called when the toggle is clicked for one of the items.
**/
(function () {
'use strict';
function ToggleGroupDirective() {
function link(scope, el, attr, ctrl) {
scope.change = function(item) {
if (item.disabled) {
return;
}
item.checked = !item.checked;
if(scope.onClick) {
scope.onClick({'item': item});
}
};
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/buttons/umb-toggle-group.html',
scope: {
items: "=",
onClick: "&"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbToggleGroup', ToggleGroupDirective);
})();
@@ -55,9 +55,9 @@
}
}
// if we still dont have a app, lets show the first one:
if (isAppPresent === false && content.apps.length) {
if (isAppPresent === false) {
content.apps[0].active = true;
$scope.appChanged(content.apps[0]);
}
@@ -198,11 +198,9 @@
return a.alias === "umbContent";
});
if (contentApp) {
//The view model for the content app is simply the index of the variant being edited
var variantIndex = vm.content.variants.indexOf(variant);
contentApp.viewModel = variantIndex;
}
//The view model for the content app is simply the index of the variant being edited
var variantIndex = vm.content.variants.indexOf(variant);
contentApp.viewModel = variantIndex;
// make sure the same app it set to active in the new variant
if(activeAppAlias) {
@@ -47,9 +47,6 @@ The sub header is sticky and will follow along down the page when scrolling.
transclude: true,
restrict: 'E',
replace: true,
scope: {
"appearance": "@?"
},
templateUrl: 'views/components/editor/subheader/umb-editor-sub-header.html'
};
@@ -4,7 +4,7 @@
**/
angular.module("umbraco.directives")
.directive('hotkey', function($window, keyboardService, $log, focusService) {
.directive('hotkey', function($window, keyboardService, $log) {
return function(scope, el, attrs) {
@@ -28,9 +28,7 @@ angular.module("umbraco.directives")
}
keyboardService.bind(keyCombo, function() {
focusService.rememberFocus();
var element = $(el);
var activeElementType = document.activeElement.tagName;
var clickableElements = ["A", "BUTTON"];
@@ -6,7 +6,7 @@
**/
angular.module("umbraco.directives")
.directive('umbImageCrop',
function ($timeout, cropperHelper) {
function ($timeout, localizationService, cropperHelper, $log) {
return {
restrict: 'E',
replace: true,
@@ -21,9 +21,6 @@ angular.module("umbraco.directives")
},
link: function(scope, element, attrs) {
let sliderRef = null;
scope.width = 400;
scope.height = 320;
@@ -33,56 +30,12 @@ angular.module("umbraco.directives")
viewport:{},
margin: 20,
scale: {
min: 0,
min: 0.3,
max: 3,
current: 1
}
};
};
scope.sliderOptions = {
"start": scope.dimensions.scale.current,
"step": 0.001,
"tooltips": [false],
"format": {
to: function (value) {
return parseFloat(parseFloat(value).toFixed(3)); //Math.round(value);
},
from: function (value) {
return parseFloat(parseFloat(value).toFixed(3)); //Math.round(value);
}
},
"range": {
"min": scope.dimensions.scale.min,
"max": scope.dimensions.scale.max
}
};
scope.setup = function (slider) {
sliderRef = slider;
// Set slider handle position
sliderRef.noUiSlider.set(scope.dimensions.scale.current);
// Update slider range min/max
sliderRef.noUiSlider.updateOptions({
"range": {
"min": scope.dimensions.scale.min,
"max": scope.dimensions.scale.max
}
});
};
scope.slide = function (values) {
if (values) {
scope.dimensions.scale.current = parseFloat(values);
}
};
scope.change = function (values) {
if (values) {
scope.dimensions.scale.current = parseFloat(values);
}
};
//live rendering of viewport and image styles
scope.style = function () {
@@ -110,6 +63,7 @@ angular.module("umbraco.directives")
constraints.top.min = scope.dimensions.margin + scope.dimensions.cropper.height - scope.dimensions.image.height;
};
var setDimensions = function(originalImage){
originalImage.width("auto");
originalImage.height("auto");
@@ -177,11 +131,13 @@ angular.module("umbraco.directives")
scope.dimensions.scale.current = scope.dimensions.image.ratio;
// Update min and max based on original width/height
//min max based on original width/height
scope.dimensions.scale.min = ratioCalculation.ratio;
scope.dimensions.scale.max = 2;
scope.dimensions.scale.max = 2;
};
var validatePosition = function(left, top){
if(left > constraints.left.max)
{
@@ -235,6 +191,8 @@ angular.module("umbraco.directives")
}
});
var init = function(image){
scope.loaded = false;
@@ -261,10 +219,10 @@ angular.module("umbraco.directives")
};
// Watchers
/// WATCHERS ////
scope.$watchCollection('[width, height]', function(newValues, oldValues){
// We have to reinit the whole thing if
// one of the external params changes
//we have to reinit the whole thing if
//one of the external params changes
if(newValues !== oldValues){
setDimensions($image);
setConstraints();
@@ -272,18 +230,29 @@ angular.module("umbraco.directives")
});
var throttledResizing = _.throttle(function(){
resizeImageToScale(scope.dimensions.scale.current);
resizeImageToScale(scope.dimensions.scale.current);
calculateCropBox();
}, 15);
// Happens when we change the scale
scope.$watch("dimensions.scale.current", function (newValue, oldValue) {
if (scope.loaded) {
//happens when we change the scale
scope.$watch("dimensions.scale.current", function(){
if(scope.loaded){
throttledResizing();
}
});
// Init
//ie hack
if(window.navigator.userAgent.indexOf("MSIE ") >= 0){
var ranger = element.find("input");
ranger.on("change",function(){
scope.$apply(function(){
scope.dimensions.scale.current = ranger.val();
});
});
}
//// INIT /////
$image.on("load", function(){
$timeout(function(){
init($image);
@@ -295,8 +295,7 @@ Opens an overlay to show a custom YSOD. </br>
scope.closeOverLay();
});
}
event.stopPropagation();
event.preventDefault();
}
@@ -512,7 +511,6 @@ Opens an overlay to show a custom YSOD. </br>
model: "=",
view: "=",
position: "@",
size: "=?",
parentScope: "=?"
},
link: link
@@ -41,7 +41,6 @@
vm.removeTag = removeTag;
vm.showPrompt = showPrompt;
vm.hidePrompt = hidePrompt;
vm.onKeyUpOnTag = onKeyUpOnTag;
vm.htmlId = "t" + String.CreateGuid();
vm.isLoading = true;
@@ -273,12 +272,6 @@
vm.promptIsVisible = "-1";
}
function onKeyUpOnTag(tag, $event) {
if ($event.keyCode === 8 || $event.keyCode === 46) {
removeTag(tag);
}
}
// helper method to remove current tags
function removeCurrentTagsFromSuggestions(suggestions) {
return $.grep(suggestions, function (suggestion) {
@@ -12,7 +12,6 @@ function treeSearchBox(localizationService, searchService, $q) {
searchFromName: "@",
showSearch: "@",
section: "@",
ignoreUserStartNodes: "@",
hideSearchCallback: "=",
searchCallback: "="
},
@@ -35,7 +34,6 @@ function treeSearchBox(localizationService, searchService, $q) {
scope.showSearch = "false";
}
//used to cancel any request in progress if another one needs to take it's place
var canceler = null;
@@ -62,11 +60,6 @@ function treeSearchBox(localizationService, searchService, $q) {
searchArgs["searchFrom"] = scope.searchFromId;
}
//append ignoreUserStartNodes value if there is one
if (scope.ignoreUserStartNodes) {
searchArgs["ignoreUserStartNodes"] = scope.ignoreUserStartNodes;
}
searcher(searchArgs).then(function (data) {
scope.searchCallback(data);
//set back to null so it can be re-created
@@ -60,7 +60,6 @@ function confirmDirective() {
link: function (scope, element, attr, ctrl) {
scope.showCancel = false;
scope.showConfirm = false;
scope.confirmButtonState = "init";
if (scope.onConfirm) {
scope.showConfirm = true;
@@ -69,15 +68,6 @@ function confirmDirective() {
if (scope.onCancel) {
scope.showCancel = true;
}
scope.confirm = function () {
if (!scope.onConfirm) {
return;
}
scope.confirmButtonState = "busy";
scope.onConfirm();
}
}
};
}
@@ -3,7 +3,7 @@
function () {
var link = function ($scope) {
// Clone the model because some property editors
// do weird things like updating and config values
// so we want to ensure we start from a fresh every
@@ -12,10 +12,10 @@
$scope.nodeContext = $scope.model;
// Find the selected tab
var selectedTab = $scope.model.variants[0].tabs[0];
var selectedTab = $scope.model.tabs[0];
if ($scope.tabAlias) {
angular.forEach($scope.model.variants[0].tabs, function (tab) {
angular.forEach($scope.model.tabs, function (tab) {
if (tab.alias.toLowerCase() === $scope.tabAlias.toLowerCase()) {
selectedTab = tab;
return;
@@ -31,9 +31,9 @@
// Tell inner controls we are submitting
$scope.$broadcast("formSubmitting", { scope: $scope });
// Sync the values back
angular.forEach($scope.ngModel.variants[0].tabs, function (tab) {
angular.forEach($scope.ngModel.tabs, function (tab) {
if (tab.alias.toLowerCase() === selectedTab.alias.toLowerCase()) {
var localPropsMap = selectedTab.properties.reduce(function (map, obj) {
@@ -94,4 +94,4 @@
// },
// link: link
// }
//});
//});
@@ -91,10 +91,6 @@ Use this directive to generate a pagination.
function link(scope, el, attr, ctrl) {
function activate() {
// page number is sometimes a string - let's make sure it's an int before we do anything with it
if (scope.pageNumber) {
scope.pageNumber = parseInt(scope.pageNumber);
}
scope.pagination = [];
@@ -0,0 +1,36 @@
(function () {
'use strict';
function PermissionDirective() {
function link(scope, el, attr, ctrl) {
scope.change = function() {
scope.selected = !scope.selected;
if(scope.onChange) {
scope.onChange({'selected': scope.selected});
}
};
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/users/umb-permission.html',
scope: {
name: "=",
description: "=?",
selected: "=",
onChange: "&"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbPermission', PermissionDirective);
})();
@@ -1,31 +0,0 @@
/**
* @ngdoc filter
* @name umbraco.filters.filter:truncate
* @namespace truncateFilter
*
* param {any} wordwise if true, the string will be cut after last fully displayed word.
* param {any} max max length of the outputtet string
* param {any} tail option tail, defaults to: ' ...'
*
* @description
* Limits the length of a string, if a cut happens only the string will be appended with three dots to indicate that more is available.
*/
angular.module("umbraco.filters").filter('truncate',
function () {
return function (value, wordwise, max, tail) {
if (!value) return '';
max = parseInt(max, 10);
if (!max) return value;
if (value.length <= max) return value;
value = value.substr(0, max);
if (wordwise) {
var lastspace = value.lastIndexOf(' ');
if (lastspace != -1) {
value = value.substr(0, lastspace);
}
}
return value + (tail || (wordwise ? ' …' : '…'));
};
}
);
@@ -59,27 +59,20 @@
}
//A 401 means that the user is not logged in
if (rejection.status === 401) {
//avoid an infinite loop
var umbRequestHelper = $injector.get('umbRequestHelper');
var getCurrentUserPath = umbRequestHelper.getApiUrl("authenticationApiBaseUrl", "GetCurrentUser");
if (!rejection.config.url.endsWith(getCurrentUserPath)) {
if (rejection.status === 401 && !rejection.config.url.endsWith("umbraco/backoffice/UmbracoApi/Authentication/GetCurrentUser")) {
var userService = $injector.get('userService'); // see above
var userService = $injector.get('userService'); // see above
//Associate the user name with the retry to ensure we retry for the right user
return userService.getCurrentUser()
.then(function(user) {
var userName = user ? user.name : null;
//The request bounced because it was not authorized - add a new request to the retry queue
return requestRetryQueue.pushRetryFn('unauthorized-server',
userName,
function retryRequest() {
// We must use $injector to get the $http service to prevent circular dependency
return $injector.get('$http')(rejection.config);
});
//Associate the user name with the retry to ensure we retry for the right user
return userService.getCurrentUser()
.then(function (user) {
var userName = user ? user.name : null;
//The request bounced because it was not authorized - add a new request to the retry queue
return requestRetryQueue.pushRetryFn('unauthorized-server', userName, function retryRequest() {
// We must use $injector to get the $http service to prevent circular dependency
return $injector.get('$http')(rejection.config);
});
}
});
}
else if (rejection.status === 404) {
@@ -215,6 +215,14 @@ angular.module('umbraco.mocks').
"placeholders_nameentity": "Name the %0%...",
"placeholders_search": "Type to search...",
"placeholders_filter": "Type to filter...",
"editcontenttype_allowedchildnodetypes": "Allowed child nodetypes",
"editcontenttype_create": "Create",
"editcontenttype_deletetab": "Delete tab",
"editcontenttype_description": "Description",
"editcontenttype_newtab": "New tab",
"editcontenttype_tab": "Tab",
"editcontenttype_thumbnail": "Thumbnail",
"editcontenttype_iscontainercontenttype": "Use as container content type",
"editdatatype_addPrevalue": "Add prevalue",
"editdatatype_dataBaseDatatype": "Database datatype",
"editdatatype_guid": "Property editor GUID",
@@ -365,28 +365,17 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
* </pre>
*
* @param {Int} id id of content item to return
* @param {Bool} options.ignoreUserStartNodes set to true to allow a user to choose nodes that they normally don't have access to
* @param {Int} culture optional culture to retrieve the item in
* @returns {Promise} resourcePromise object containing the content item.
*
*/
getById: function (id, options) {
var defaults = {
ignoreUserStartNodes: false
};
if (options === undefined) {
options = {};
}
//overwrite the defaults if there are any specified
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
getById: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetById",
[{ id: id }, { ignoreUserStartNodes: options.ignoreUserStartNodes }])),
{ id: id })),
'Failed to retrieve data for content id ' + id)
.then(function (result) {
return $q.when(umbDataFormatter.formatContentGetData(result));
@@ -344,12 +344,6 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter, loca
$http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "Import", { file: file })),
"Failed to import document type " + file
);
},
createDefaultTemplate: function (id) {
return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostCreateDefaultTemplate", { id: id })),
'Failed to create default template for content type with id ' + id);
}
};
}
@@ -284,31 +284,15 @@ function entityResource($q, $http, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the entity.
*
*/
getAncestors: function (id, type, culture, options) {
var defaults = {
ignoreUserStartNodes: false
};
if (options === undefined) {
options = {};
}
//overwrite the defaults if there are any specified
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
getAncestors: function (id, type, culture) {
if (culture === undefined) culture = "";
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetAncestors",
[
{ id: id },
{ type: type },
{ culture: culture },
{ ignoreUserStartNodes: options.ignoreUserStartNodes }
])),
'Failed to retrieve ancestor data for id ' + id);
[{ id: id }, { type: type }, { culture: culture }])),
'Failed to retrieve ancestor data for id ' + id);
},
/**
@@ -440,8 +424,7 @@ function entityResource($q, $http, umbRequestHelper) {
pageNumber: 1,
filter: '',
orderDirection: "Ascending",
orderBy: "SortOrder",
ignoreUserStartNodes: false
orderBy: "SortOrder"
};
if (options === undefined) {
options = {};
@@ -470,8 +453,7 @@ function entityResource($q, $http, umbRequestHelper) {
pageSize: options.pageSize,
orderBy: options.orderBy,
orderDirection: options.orderDirection,
filter: encodeURIComponent(options.filter),
ignoreUserStartNodes: options.ignoreUserStartNodes
filter: encodeURIComponent(options.filter)
}
)),
'Failed to retrieve child data for id ' + parentId);
@@ -499,19 +481,12 @@ function entityResource($q, $http, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the entity array.
*
*/
search: function (query, type, options, canceler) {
search: function (query, type, searchFrom, canceler) {
var args = [{ query: query }, { type: type }];
if(options !== undefined) {
if (options.searchFrom) {
args.push({ searchFrom: options.searchFrom });
}
if (options.ignoreUserStartNodes) {
args.push({ ignoreUserStartNodes: options.ignoreUserStartNodes });
}
if (searchFrom) {
args.push({ searchFrom: searchFrom });
}
var httpConfig = {};
if (canceler) {

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