diff --git a/build/NuSpecs/UmbracoCms.StaticAssets.nuspec b/build/NuSpecs/UmbracoCms.StaticAssets.nuspec
index b8109dbfa9..cd8367d8c8 100644
--- a/build/NuSpecs/UmbracoCms.StaticAssets.nuspec
+++ b/build/NuSpecs/UmbracoCms.StaticAssets.nuspec
@@ -28,6 +28,6 @@
-
+
diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml
index 893c62509a..b2824d8237 100644
--- a/build/azure-pipelines.yml
+++ b/build/azure-pipelines.yml
@@ -401,7 +401,7 @@ stages:
publishJUnitResults: true
testResultsFiles: '**\TESTS-*.xml'
- task: PowerShell@1
- displayName: Prepare Packages & Zip
+ displayName: Prepare Packages
inputs:
scriptType: inlineScript
inlineScript: |
@@ -410,19 +410,6 @@ stages:
$ubuild.CompileUmbraco()
$ubuild.PreparePackages()
- $ubuild.PackageZip()
- - task: CopyFiles@2
- displayName: Copy Zip Files to Staging
- inputs:
- SourceFolder: build.out
- Contents: '*.zip'
- TargetFolder: $(build.artifactstagingdirectory)
- CleanTargetFolder: true
- - task: PublishBuildArtifacts@1
- displayName: Publish Zip Files
- inputs:
- PathtoPublish: $(build.artifactstagingdirectory)
- ArtifactName: zips
- task: PowerShell@1
displayName: Verify & Package NuGet
inputs:
diff --git a/build/build.ps1 b/build/build.ps1
index ba99e1e660..fb869738f3 100644
--- a/build/build.ps1
+++ b/build/build.ps1
@@ -351,29 +351,6 @@
$this.RemoveDirectory("$tmp\Templates\UmbracoProject\bin")
})
- $ubuild.DefineMethod("PackageZip",
- {
-
- Write-Host "Create Zip packages"
-
- $src = "$($this.SolutionRoot)\src"
- $tmp = $this.BuildTemp
- $out = $this.BuildOutput
-
- Write-Host "Zip all binaries"
- &$this.BuildEnv.Zip a -r "$out\UmbracoCms.AllBinaries.$($this.Version.Semver).zip" `
- "$tmp\bin\*" `
- "-x!dotless.Core.*" `
- > $null
- if (-not $?) { throw "Failed to zip UmbracoCms.AllBinaries." }
-
- Write-Host "Zip cms"
- &$this.BuildEnv.Zip a -r "$out\UmbracoCms.$($this.Version.Semver).zip" `
- "$tmp\WebApp\*" `
- "-x!dotless.Core.*" "-x!Content_Types.xml" "-x!*.pdb" `
- > $null
- if (-not $?) { throw "Failed to zip UmbracoCms." }
- })
$ubuild.DefineMethod("PrepareBuild",
{
@@ -541,8 +518,6 @@
# not running tests
$this.PreparePackages()
if ($this.OnError()) { return }
- $this.PackageZip()
- if ($this.OnError()) { return }
$this.VerifyNuGet()
if ($this.OnError()) { return }
$this.PackageNuGet()
diff --git a/build/templates/UmbracoProject/.template.config/template.json b/build/templates/UmbracoProject/.template.config/template.json
index 4f2454706d..8a8c396dcb 100644
--- a/build/templates/UmbracoProject/.template.config/template.json
+++ b/build/templates/UmbracoProject/.template.config/template.json
@@ -15,7 +15,7 @@
},
"primaryOutputs": [
{
- "path": "UmbracoProject.csproj"
+ "path": "UmbracoProject.csproj"
}
],
"postActions": [
@@ -29,12 +29,35 @@
"continueOnError": true
}
],
- "sourceName": "Umbraco.Cms.Web.UI.NetCore",
+ "sourceName": "UmbracoProject",
"symbols": {
+ "namespaceReplacer": {
+ "type": "generated",
+ "generator": "regex",
+ "dataType": "string",
+ "replaces": "Umbraco.Cms.Web.UI.NetCore",
+ "parameters": {
+ "source": "name",
+ "steps": [
+ {
+ "regex": "\\s",
+ "replacement": "_"
+ },
+ {
+ "regex": "-",
+ "replacement": "_"
+ },
+ {
+ "regex": "^[^a-zA-Z_]+",
+ "replacement": "_"
+ }
+ ]
+ }
+ },
"version": {
"type": "parameter",
"datatype": "string",
- "defaultValue": "9.0.0-beta003",
+ "defaultValue": "9.0.0-beta004",
"description": "The version of Umbraco to load using NuGet",
"replaces": "UMBRACO_VERSION_FROM_TEMPLATE"
},
@@ -98,30 +121,134 @@
"type": "parameter",
"datatype":"text",
"description": "The friendly name of the user for Umbraco login when using Unattended install (Without installer wizard UI)",
- "replaces": "FRIENDLY_NAME_FROM_TEMPLATE",
"defaultValue": ""
},
+ "FriendlyNameReplaced":{
+ "type": "generated",
+ "generator": "regex",
+ "dataType": "string",
+ "replaces": "FRIENDLY_NAME_FROM_TEMPLATE",
+ "parameters": {
+ "source": "FriendlyName",
+ "steps": [
+ {
+ "regex": "\\\\",
+ "replacement": "\\\\"
+ },
+ {
+ "regex": "\\\"",
+ "replacement": "\\\""
+ },
+ {
+ "regex": "\\\n",
+ "replacement": "\\\n"
+ },
+ {
+ "regex": "\\\t",
+ "replacement": "\\\t"
+ }
+ ]
+ }
+ },
"Email":{
"type": "parameter",
"datatype":"text",
"description": "Email to use for Umbraco login when using Unattended install (Without installer wizard UI)",
- "replaces": "EMAIL_FROM_TEMPLATE",
"defaultValue": ""
},
+ "EmailReplaced":{
+ "type": "generated",
+ "generator": "regex",
+ "dataType": "string",
+ "replaces": "EMAIL_FROM_TEMPLATE",
+ "parameters": {
+ "source": "Email",
+ "steps": [
+ {
+ "regex": "\\\\",
+ "replacement": "\\\\"
+ },
+ {
+ "regex": "\\\"",
+ "replacement": "\\\""
+ },
+ {
+ "regex": "\\\n",
+ "replacement": "\\\n"
+ },
+ {
+ "regex": "\\\t",
+ "replacement": "\\\t"
+ }
+ ]
+ }
+ },
"Password":{
"type": "parameter",
"datatype":"text",
"description": "Password to use for Umbraco login when using Unattended install (Without installer wizard UI)",
- "replaces": "PASSWORD_FROM_TEMPLATE",
"defaultValue": ""
},
+ "PasswordReplaced":{
+ "type": "generated",
+ "generator": "regex",
+ "dataType": "string",
+ "replaces": "PASSWORD_FROM_TEMPLATE",
+ "parameters": {
+ "source": "Password",
+ "steps": [
+ {
+ "regex": "\\\\",
+ "replacement": "\\\\"
+ },
+ {
+ "regex": "\\\"",
+ "replacement": "\\\""
+ },
+ {
+ "regex": "\\\n",
+ "replacement": "\\\n"
+ },
+ {
+ "regex": "\\\t",
+ "replacement": "\\\t"
+ }
+ ]
+ }
+ },
"ConnectionString":{
"type": "parameter",
"datatype":"text",
"description": "Database connection string when using Unattended install (Without installer wizard UI)",
- "replaces": "CONNECTION_FROM_TEMPLATE",
"defaultValue": ""
},
+ "ConnectionStringReplaced":{
+ "type": "generated",
+ "generator": "regex",
+ "dataType": "string",
+ "replaces": "CONNECTION_FROM_TEMPLATE",
+ "parameters": {
+ "source": "ConnectionString",
+ "steps": [
+ {
+ "regex": "\\\\",
+ "replacement": "\\\\"
+ },
+ {
+ "regex": "\\\"",
+ "replacement": "\\\""
+ },
+ {
+ "regex": "\\\n",
+ "replacement": "\\\n"
+ },
+ {
+ "regex": "\\\t",
+ "replacement": "\\\t"
+ }
+ ]
+ }
+ },
"UsingUnattenedInstall":{
"type": "computed",
"value": "(FriendlyName != \"\" && Email != \"\" && Password != \"\" && ConnectionString != \"\")"
diff --git a/build/templates/UmbracoProject/Properties/launchSettings.json b/build/templates/UmbracoProject/Properties/launchSettings.json
index 5f9252d553..f40916f4d1 100644
--- a/build/templates/UmbracoProject/Properties/launchSettings.json
+++ b/build/templates/UmbracoProject/Properties/launchSettings.json
@@ -18,11 +18,12 @@
},
"Umbraco.Web.UI.NetCore": {
"commandName": "Project",
+ "dotnetRunMessages": true,
"launchBrowser": true,
+ "applicationUrl": "https://localhost:HTTPS_PORT_FROM_TEMPLATE;http://localhost:HTTP_PORT_FROM_TEMPLATE",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
- },
- "applicationUrl": "https://localhost:HTTPS_PORT_FROM_TEMPLATE;http://localhost:HTTP_PORT_FROM_TEMPLATE"
+ }
}
}
}
diff --git a/build/templates/UmbracoProject/UmbracoProject.csproj b/build/templates/UmbracoProject/UmbracoProject.csproj
index 9798a40ff2..6850940edd 100644
--- a/build/templates/UmbracoProject/UmbracoProject.csproj
+++ b/build/templates/UmbracoProject/UmbracoProject.csproj
@@ -44,7 +44,7 @@
-
+
false
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 66b7cb4bb5..a1220f9b3f 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -2,7 +2,7 @@
9.0.0
9.0.0
- 9.0.0-beta003
+ 9.0.0-beta004
9.0.0
9.0
en-US
diff --git a/src/Umbraco.Core/Collections/TopoGraph.cs b/src/Umbraco.Core/Collections/TopoGraph.cs
index 12b0d431ea..2e199dd5a4 100644
--- a/src/Umbraco.Core/Collections/TopoGraph.cs
+++ b/src/Umbraco.Core/Collections/TopoGraph.cs
@@ -101,7 +101,7 @@ namespace Umbraco.Cms.Core.Collections
var start = incr > 0 ? 0 : index;
var count = incr > 0 ? index : sorted.Length - index;
if (throwOnCycle && Contains(sorted, item, start, count) == false)
- throw new Exception(CycleDependencyError);
+ throw new Exception(CycleDependencyError +": " + item);
return;
}
diff --git a/src/Umbraco.Core/Composing/ICoreComposer.cs b/src/Umbraco.Core/Composing/ICoreComposer.cs
deleted file mode 100644
index c6d0b3510c..0000000000
--- a/src/Umbraco.Core/Composing/ICoreComposer.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Umbraco.Cms.Core.Composing
-{
- ///
- /// Represents a core .
- ///
- ///
- /// Core composers compose after the initial composer, and before user composers.
- ///
- public interface ICoreComposer : IComposer
- {
- // TODO: This should die, there should be exactly zero core composers.
- }
-}
diff --git a/src/Umbraco.Core/Composing/IUserComposer.cs b/src/Umbraco.Core/Composing/IUserComposer.cs
index 52ed4fdf5c..8e07dae672 100644
--- a/src/Umbraco.Core/Composing/IUserComposer.cs
+++ b/src/Umbraco.Core/Composing/IUserComposer.cs
@@ -6,7 +6,6 @@
///
/// User composers compose after core composers, and before the final composer.
///
- [ComposeAfter(typeof(ICoreComposer))]
public interface IUserComposer : IComposer
{ }
}
diff --git a/src/Umbraco.Core/Events/SendEmailEventArgs.cs b/src/Umbraco.Core/Events/SendEmailEventArgs.cs
index 720f125d9c..c1e626c6c1 100644
--- a/src/Umbraco.Core/Events/SendEmailEventArgs.cs
+++ b/src/Umbraco.Core/Events/SendEmailEventArgs.cs
@@ -1,5 +1,5 @@
using System;
-using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Email;
namespace Umbraco.Cms.Core.Events
{
diff --git a/src/Umbraco.Core/Extensions/StringExtensions.cs b/src/Umbraco.Core/Extensions/StringExtensions.cs
index e815f219ca..0494224614 100644
--- a/src/Umbraco.Core/Extensions/StringExtensions.cs
+++ b/src/Umbraco.Core/Extensions/StringExtensions.cs
@@ -186,7 +186,7 @@ namespace Umbraco.Extensions
/// This methods ensures that the resulting URL is structured correctly, that there's only one '?' and that things are
/// delimited properly with '&'
///
- internal static string AppendQueryStringToUrl(this string url, params string[] queryStrings)
+ public static string AppendQueryStringToUrl(this string url, params string[] queryStrings)
{
//remove any prefixed '&' or '?'
for (var i = 0; i < queryStrings.Length; i++)
diff --git a/src/Umbraco.Core/Extensions/XmlExtensions.cs b/src/Umbraco.Core/Extensions/XmlExtensions.cs
index 8ad37bd93e..aa3fe25eef 100644
--- a/src/Umbraco.Core/Extensions/XmlExtensions.cs
+++ b/src/Umbraco.Core/Extensions/XmlExtensions.cs
@@ -8,6 +8,7 @@ using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
+using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Xml;
namespace Umbraco.Extensions
@@ -238,7 +239,7 @@ namespace Umbraco.Extensions
{
if (xml == null)
{
- throw new ArgumentNullException("xml");
+ throw new ArgumentNullException(nameof(xml));
}
if (xml.HasAttributes == false)
@@ -246,19 +247,19 @@ namespace Umbraco.Extensions
throw new InvalidOperationException($"{attributeName} not found in xml");
}
- if (xml.Attribute(attributeName) == null)
+ XAttribute attribute = xml.Attribute(attributeName);
+ if (attribute is null)
{
throw new InvalidOperationException($"{attributeName} not found in xml");
}
- var val = xml.Attribute(attributeName).Value;
- var result = val.TryConvertTo();
+ Attempt result = attribute.Value.TryConvertTo();
if (result.Success)
{
return result.Result;
}
- throw new InvalidOperationException($"{val} attribute value cannot be converted to {typeof(T)}");
+ throw new InvalidOperationException($"{attribute.Value} attribute value cannot be converted to {typeof(T)}");
}
public static T AttributeValue(this XElement xml, string attributeName)
diff --git a/src/Umbraco.Core/HealthChecks/NotificationMethods/EmailNotificationMethod.cs b/src/Umbraco.Core/HealthChecks/NotificationMethods/EmailNotificationMethod.cs
index 51e31b1811..6e679ddbb1 100644
--- a/src/Umbraco.Core/HealthChecks/NotificationMethods/EmailNotificationMethod.cs
+++ b/src/Umbraco.Core/HealthChecks/NotificationMethods/EmailNotificationMethod.cs
@@ -4,7 +4,7 @@ using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Mail;
-using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Services;
using Umbraco.Extensions;
diff --git a/src/Umbraco.Core/Mail/IEmailSender.cs b/src/Umbraco.Core/Mail/IEmailSender.cs
index 5b5adf71eb..45959a5a9a 100644
--- a/src/Umbraco.Core/Mail/IEmailSender.cs
+++ b/src/Umbraco.Core/Mail/IEmailSender.cs
@@ -1,5 +1,5 @@
using System.Threading.Tasks;
-using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Email;
namespace Umbraco.Cms.Core.Mail
{
diff --git a/src/Umbraco.Core/Mail/NotImplementedEmailSender.cs b/src/Umbraco.Core/Mail/NotImplementedEmailSender.cs
index 3632e79c23..29265b4038 100644
--- a/src/Umbraco.Core/Mail/NotImplementedEmailSender.cs
+++ b/src/Umbraco.Core/Mail/NotImplementedEmailSender.cs
@@ -1,6 +1,6 @@
using System;
using System.Threading.Tasks;
-using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Email;
namespace Umbraco.Cms.Core.Mail
{
diff --git a/src/Umbraco.Core/Models/EmailMessage.cs b/src/Umbraco.Core/Models/Email/EmailMessage.cs
similarity index 96%
rename from src/Umbraco.Core/Models/EmailMessage.cs
rename to src/Umbraco.Core/Models/Email/EmailMessage.cs
index 153e8f33f7..55e30f2150 100644
--- a/src/Umbraco.Core/Models/EmailMessage.cs
+++ b/src/Umbraco.Core/Models/Email/EmailMessage.cs
@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
-namespace Umbraco.Cms.Core.Models
+namespace Umbraco.Cms.Core.Models.Email
{
public class EmailMessage
{
@@ -33,7 +33,6 @@ namespace Umbraco.Cms.Core.Models
public EmailMessage(string from, string[] to, string[] cc, string[] bcc, string[] replyTo, string subject, string body, bool isBodyHtml, IEnumerable attachments)
{
- ArgumentIsNotNullOrEmpty(from, nameof(from));
ArgumentIsNotNullOrEmpty(to, nameof(to));
ArgumentIsNotNullOrEmpty(subject, nameof(subject));
ArgumentIsNotNullOrEmpty(body, nameof(body));
diff --git a/src/Umbraco.Core/Models/EmailMessageAttachment.cs b/src/Umbraco.Core/Models/Email/EmailMessageAttachment.cs
similarity index 88%
rename from src/Umbraco.Core/Models/EmailMessageAttachment.cs
rename to src/Umbraco.Core/Models/Email/EmailMessageAttachment.cs
index ee4f3ef8cb..bbb24b69f7 100644
--- a/src/Umbraco.Core/Models/EmailMessageAttachment.cs
+++ b/src/Umbraco.Core/Models/Email/EmailMessageAttachment.cs
@@ -1,6 +1,6 @@
using System.IO;
-namespace Umbraco.Cms.Core.Models
+namespace Umbraco.Cms.Core.Models.Email
{
public class EmailMessageAttachment
{
diff --git a/src/Umbraco.Core/Models/Email/NotificationEmailAddress.cs b/src/Umbraco.Core/Models/Email/NotificationEmailAddress.cs
new file mode 100644
index 0000000000..755947c6a4
--- /dev/null
+++ b/src/Umbraco.Core/Models/Email/NotificationEmailAddress.cs
@@ -0,0 +1,18 @@
+namespace Umbraco.Cms.Core.Models.Email
+{
+ ///
+ /// Represents an email address used for notifications. Contains both the address and its display name.
+ ///
+ public class NotificationEmailAddress
+ {
+ public string DisplayName { get; }
+
+ public string Address { get; }
+
+ public NotificationEmailAddress(string address, string displayName)
+ {
+ Address = address;
+ DisplayName = displayName;
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Models/Email/NotificationEmailModel.cs b/src/Umbraco.Core/Models/Email/NotificationEmailModel.cs
new file mode 100644
index 0000000000..a606e8e680
--- /dev/null
+++ b/src/Umbraco.Core/Models/Email/NotificationEmailModel.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Umbraco.Cms.Core.Models.Email
+{
+ ///
+ /// Represents an email when sent with notifications.
+ ///
+ public class NotificationEmailModel
+ {
+ public NotificationEmailAddress From { get; }
+
+ public IEnumerable To { get; }
+
+ public IEnumerable Cc { get; }
+
+ public IEnumerable Bcc { get; }
+
+ public IEnumerable ReplyTo { get; }
+
+ public string Subject { get; }
+
+ public string Body { get; }
+
+ public bool IsBodyHtml { get; }
+
+ public IList Attachments { get; }
+
+ public bool HasAttachments => Attachments != null && Attachments.Count > 0;
+
+ public NotificationEmailModel(
+ NotificationEmailAddress from,
+ IEnumerable to,
+ IEnumerable cc,
+ IEnumerable bcc,
+ IEnumerable replyTo,
+ string subject,
+ string body,
+ IEnumerable attachments,
+ bool isBodyHtml)
+ {
+ From = from;
+ To = to;
+ Cc = cc;
+ Bcc = bcc;
+ ReplyTo = replyTo;
+ Subject = subject;
+ Body = body;
+ IsBodyHtml = isBodyHtml;
+ Attachments = attachments?.ToList();
+ }
+
+ }
+}
diff --git a/src/Umbraco.Core/Notifications/SendEmailNotification.cs b/src/Umbraco.Core/Notifications/SendEmailNotification.cs
index 194ee68edc..3c9caabb0e 100644
--- a/src/Umbraco.Core/Notifications/SendEmailNotification.cs
+++ b/src/Umbraco.Core/Notifications/SendEmailNotification.cs
@@ -1,11 +1,11 @@
-using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Email;
namespace Umbraco.Cms.Core.Notifications
{
public class SendEmailNotification : INotification
{
- public SendEmailNotification(EmailMessage message) => Message = message;
+ public SendEmailNotification(NotificationEmailModel message) => Message = message;
- public EmailMessage Message { get; set; }
+ public NotificationEmailModel Message { get; }
}
}
diff --git a/src/Umbraco.Core/Notifications/UmbracoApplicationComponentsInstallingNotification.cs b/src/Umbraco.Core/Notifications/UmbracoApplicationComponentsInstallingNotification.cs
new file mode 100644
index 0000000000..c29df4e85f
--- /dev/null
+++ b/src/Umbraco.Core/Notifications/UmbracoApplicationComponentsInstallingNotification.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Umbraco.
+// See LICENSE for more details.
+
+namespace Umbraco.Cms.Core.Notifications
+{
+ ///
+ /// Notification that occurs during the Umbraco boot process, before instances of initialize.
+ ///
+ public class UmbracoApplicationComponentsInstallingNotification : INotification
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The runtime level
+ public UmbracoApplicationComponentsInstallingNotification(RuntimeLevel runtimeLevel) => RuntimeLevel = runtimeLevel;
+
+ ///
+ /// Gets the runtime level of execution.
+ ///
+ public RuntimeLevel RuntimeLevel { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Notifications/UmbracoApplicationMainDomAcquiredNotification.cs b/src/Umbraco.Core/Notifications/UmbracoApplicationMainDomAcquiredNotification.cs
new file mode 100644
index 0000000000..6a66e2413f
--- /dev/null
+++ b/src/Umbraco.Core/Notifications/UmbracoApplicationMainDomAcquiredNotification.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Umbraco.
+// See LICENSE for more details.
+
+namespace Umbraco.Cms.Core.Notifications
+{
+ ///
+ /// Notification that occurs during Umbraco boot after the MainDom has been acquired.
+ ///
+ public class UmbracoApplicationMainDomAcquiredNotification : INotification
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The runtime level
+ public UmbracoApplicationMainDomAcquiredNotification()
+ {
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs b/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs
index b7de5fa438..7d45cc01a9 100644
--- a/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs
+++ b/src/Umbraco.Core/Packaging/PackageMigrationPlan.cs
@@ -1,5 +1,7 @@
+using System;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Migrations;
+using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Packaging
{
@@ -46,6 +48,12 @@ namespace Umbraco.Cms.Core.Packaging
///
public string PackageName { get; }
+ ///
+ /// Inform the plan executor to ignore all saved package state and
+ /// run the migration from initial state to it's end state.
+ ///
+ public override bool IgnoreCurrentState => true;
+
protected abstract void DefinePlan();
}
diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs
deleted file mode 100644
index 9f49dade80..0000000000
--- a/src/Umbraco.Core/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-[assembly: ComVisible(false)]
-[assembly: Guid("DA322714-FB89-4943-92BD-BB122B82F66B")]
-
-// Umbraco Cms
-[assembly: InternalsVisibleTo("Umbraco.Web")]
-[assembly: InternalsVisibleTo("Umbraco.Web.UI")]
-
-[assembly: InternalsVisibleTo("Umbraco.Tests")]
-[assembly: InternalsVisibleTo("Umbraco.Tests.Benchmarks")]
-
-// Allow this to be mocked in our unit tests
-[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
-
-// Umbraco Deploy
-[assembly: InternalsVisibleTo("Umbraco.Deploy")]
-[assembly: InternalsVisibleTo("Umbraco.Deploy.UI")]
-[assembly: InternalsVisibleTo("Umbraco.Deploy.Cloud")]
-
-// Umbraco Forms
-[assembly: InternalsVisibleTo("Umbraco.Forms.Core")]
-[assembly: InternalsVisibleTo("Umbraco.Forms.Core.Providers")]
-[assembly: InternalsVisibleTo("Umbraco.Forms.Web")]
-
-// Umbraco Headless
-[assembly: InternalsVisibleTo("Umbraco.Cloud.Headless")]
diff --git a/src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs b/src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs
index c777aedd89..d653a831cb 100644
--- a/src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs
+++ b/src/Umbraco.Core/PropertyEditors/FileExtensionConfigItem.cs
@@ -2,6 +2,7 @@
namespace Umbraco.Cms.Core.PropertyEditors
{
+ [DataContract]
public class FileExtensionConfigItem : IFileExtensionConfigItem
{
[DataMember(Name = "id")]
diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs
deleted file mode 100644
index f14e0d6a0b..0000000000
--- a/src/Umbraco.Infrastructure/Compose/NotificationsComposer.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) Umbraco.
-// See LICENSE for more details.
-
-using Umbraco.Cms.Core.Cache;
-using Umbraco.Cms.Core.Composing;
-using Umbraco.Cms.Core.DependencyInjection;
-using Umbraco.Cms.Core.Events;
-using Umbraco.Cms.Core.Handlers;
-using Umbraco.Cms.Core.Notifications;
-using Umbraco.Cms.Core.PropertyEditors;
-using Umbraco.Cms.Core.Routing;
-using Umbraco.Extensions;
-
-namespace Umbraco.Cms.Core.Compose
-{
- public sealed class NotificationsComposer : ICoreComposer
- {
- public void Compose(IUmbracoBuilder builder)
- {
- // add handlers for sending user notifications (i.e. emails)
- builder.Services.AddUnique();
- builder
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler();
-
- // add handlers for building content relations
- builder
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler();
-
- // add notification handlers for property editors
- builder
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler();
-
- // add notification handlers for redirect tracking
- builder
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler();
-
- // Add notification handlers for DistributedCache
- builder
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- ;
- // add notification handlers for auditing
- builder
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler();
- }
- }
-}
diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
index f2cd5cfad7..2b78be069b 100644
--- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
+++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs
@@ -3,14 +3,20 @@ using Examine;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Serilog;
using Umbraco.Cms.Core;
+using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
+using Umbraco.Cms.Core.Events;
+using Umbraco.Cms.Core.Handlers;
using Umbraco.Cms.Core.HealthChecks.NotificationMethods;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Install;
+using Umbraco.Cms.Core.Logging;
using Umbraco.Cms.Core.Logging.Serilog.Enrichers;
+using Umbraco.Cms.Core.Logging.Viewer;
using Umbraco.Cms.Core.Mail;
using Umbraco.Cms.Core.Manifest;
using Umbraco.Cms.Core.Media;
@@ -39,6 +45,7 @@ using Umbraco.Cms.Infrastructure.Migrations;
using Umbraco.Cms.Infrastructure.Migrations.Install;
using Umbraco.Cms.Infrastructure.Migrations.PostMigrations;
using Umbraco.Cms.Infrastructure.Packaging;
+using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Infrastructure.Runtime;
using Umbraco.Cms.Infrastructure.Search;
@@ -112,6 +119,8 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
builder.Services.AddUnique();
builder.Services.AddUnique(factory => new MigrationBuilder(factory));
+ builder.AddPreValueMigrators();
+
builder.Services.AddUnique();
// register the published snapshot accessor - the "current" published snapshot is in the umbraco context
@@ -211,5 +220,133 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
return builder;
}
+
+
+ private static IUmbracoBuilder AddPreValueMigrators(this IUmbracoBuilder builder)
+ {
+ builder.WithCollectionBuilder()
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append();
+
+ return builder;
+ }
+
+ public static IUmbracoBuilder AddLogViewer(this IUmbracoBuilder builder)
+ {
+ builder.Services.AddUnique();
+ builder.SetLogViewer();
+ builder.Services.AddUnique(factory => new SerilogJsonLogViewer(factory.GetRequiredService>(),
+ factory.GetRequiredService(),
+ factory.GetRequiredService(),
+ Log.Logger));
+
+ return builder;
+ }
+
+
+ public static IUmbracoBuilder AddCoreNotifications(this IUmbracoBuilder builder)
+ {
+ // add handlers for sending user notifications (i.e. emails)
+ builder.Services.AddUnique();
+ builder
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler();
+
+ // add handlers for building content relations
+ builder
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler();
+
+ // add notification handlers for property editors
+ builder
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler();
+
+ // add notification handlers for redirect tracking
+ builder
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler();
+
+ // Add notification handlers for DistributedCache
+ builder
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ ;
+ // add notification handlers for auditing
+ builder
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler();
+
+ return builder;
+ }
}
}
diff --git a/src/Umbraco.Infrastructure/EmailSender.cs b/src/Umbraco.Infrastructure/EmailSender.cs
index 02e83405d6..72daad7de1 100644
--- a/src/Umbraco.Infrastructure/EmailSender.cs
+++ b/src/Umbraco.Infrastructure/EmailSender.cs
@@ -7,7 +7,7 @@ using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Mail;
-using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Infrastructure.Extensions;
using SmtpClient = MailKit.Net.Smtp.SmtpClient;
@@ -53,7 +53,8 @@ namespace Umbraco.Cms.Infrastructure
{
if (enableNotification)
{
- await _eventAggregator.PublishAsync(new SendEmailNotification(message));
+ await _eventAggregator.PublishAsync(
+ new SendEmailNotification(message.ToNotificationEmail(_globalSettings.Smtp?.From)));
}
return;
}
diff --git a/src/Umbraco.Infrastructure/Extensions/EmailMessageExtensions.cs b/src/Umbraco.Infrastructure/Extensions/EmailMessageExtensions.cs
index cc1efa271e..f7bed9d74c 100644
--- a/src/Umbraco.Infrastructure/Extensions/EmailMessageExtensions.cs
+++ b/src/Umbraco.Infrastructure/Extensions/EmailMessageExtensions.cs
@@ -1,7 +1,8 @@
using System;
+using System.Collections.Generic;
using MimeKit;
using MimeKit.Text;
-using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Email;
namespace Umbraco.Cms.Infrastructure.Extensions
{
@@ -9,13 +10,9 @@ namespace Umbraco.Cms.Infrastructure.Extensions
{
public static MimeMessage ToMimeMessage(this EmailMessage mailMessage, string configuredFromAddress)
{
- var fromEmail = mailMessage.From;
- if (string.IsNullOrEmpty(fromEmail))
- {
- fromEmail = configuredFromAddress;
- }
+ var fromEmail = string.IsNullOrEmpty(mailMessage.From) ? configuredFromAddress : mailMessage.From;
- if (!InternetAddress.TryParse(mailMessage.From, out InternetAddress fromAddress))
+ if (!InternetAddress.TryParse(fromEmail, out InternetAddress fromAddress))
{
throw new ArgumentException($"Email could not be sent. Could not parse from address {fromEmail} as a valid email address.");
}
@@ -78,5 +75,57 @@ namespace Umbraco.Cms.Infrastructure.Extensions
throw new InvalidOperationException($"Email could not be sent. Could not parse a valid recipient address.");
}
}
+
+ public static NotificationEmailModel ToNotificationEmail(this EmailMessage emailMessage,
+ string configuredFromAddress)
+ {
+ var fromEmail = string.IsNullOrEmpty(emailMessage.From) ? configuredFromAddress : emailMessage.From;
+
+ NotificationEmailAddress from = ToNotificationAddress(fromEmail);
+
+ return new NotificationEmailModel(from,
+ GetNotificationAddresses(emailMessage.To),
+ GetNotificationAddresses(emailMessage.Cc),
+ GetNotificationAddresses(emailMessage.Bcc),
+ GetNotificationAddresses(emailMessage.ReplyTo),
+ emailMessage.Subject,
+ emailMessage.Body,
+ emailMessage.Attachments,
+ emailMessage.IsBodyHtml);
+ }
+
+ private static NotificationEmailAddress ToNotificationAddress(string address)
+ {
+ if (InternetAddress.TryParse(address, out InternetAddress internetAddress))
+ {
+ if (internetAddress is MailboxAddress mailboxAddress)
+ {
+ return new NotificationEmailAddress(mailboxAddress.Address, internetAddress.Name);
+ }
+ }
+
+ return null;
+ }
+
+ private static IEnumerable GetNotificationAddresses(IEnumerable addresses)
+ {
+ if (addresses is null)
+ {
+ return null;
+ }
+
+ var notificationAddresses = new List();
+
+ foreach (var address in addresses)
+ {
+ NotificationEmailAddress notificationAddress = ToNotificationAddress(address);
+ if (notificationAddress is not null)
+ {
+ notificationAddresses.Add(notificationAddress);
+ }
+ }
+
+ return notificationAddresses;
+ }
}
}
diff --git a/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs b/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs
index 131b81322a..70dcb3a04e 100644
--- a/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs
+++ b/src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs
@@ -62,8 +62,9 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
}
finally
{
- // Resume now that the task is complete
- _timer?.Change((int)_delay.TotalMilliseconds, (int)_period.TotalMilliseconds);
+ // Resume now that the task is complete - Note we use period in both because we don't want to execute again after the delay.
+ // So first execution is after _delay, and the we wait _period between each
+ _timer?.Change((int)_period.TotalMilliseconds, (int)_period.TotalMilliseconds);
}
}
diff --git a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs
index b1411270c3..6eab3a60bc 100644
--- a/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs
+++ b/src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs
@@ -52,13 +52,24 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
try
{
- // Send data to LIVE telemetry
- s_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/");
+
+ if (s_httpClient.BaseAddress is null)
+ {
+ // Send data to LIVE telemetry
+ s_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/");
+
+ // Set a low timeout - no need to use a larger default timeout for this POST request
+ s_httpClient.Timeout = new TimeSpan(0, 0, 1);
#if DEBUG
- // Send data to DEBUG telemetry service
- s_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/");
+ // Send data to DEBUG telemetry service
+ s_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/");
+
+
+
#endif
+ }
+
s_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
@@ -67,9 +78,6 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
var postData = new TelemetryReportData { Id = telemetrySiteIdentifier, Version = _umbracoVersion.SemanticVersion.ToSemanticStringWithoutBuild() };
request.Content = new StringContent(JsonConvert.SerializeObject(postData), Encoding.UTF8, "application/json"); //CONTENT-TYPE header
- // Set a low timeout - no need to use a larger default timeout for this POST request
- s_httpClient.Timeout = new TimeSpan(0, 0, 1);
-
// Make a HTTP Post to telemetry service
// https://telemetry.umbraco.com/installs/
// Fire & Forget, do not need to know if its a 200, 500 etc
diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerComposer.cs b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerComposer.cs
deleted file mode 100644
index dcfcb66d5d..0000000000
--- a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerComposer.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Serilog;
-using Umbraco.Cms.Core.Composing;
-using Umbraco.Cms.Core.DependencyInjection;
-using Umbraco.Cms.Core.Logging;
-using Umbraco.Cms.Infrastructure.DependencyInjection;
-using Umbraco.Extensions;
-
-namespace Umbraco.Cms.Core.Logging.Viewer
-{
- // ReSharper disable once UnusedMember.Global
- public class LogViewerComposer : ICoreComposer
- {
- public void Compose(IUmbracoBuilder builder)
- {
- builder.Services.AddUnique();
- builder.SetLogViewer();
- builder.Services.AddUnique(factory =>
- {
-
- return new SerilogJsonLogViewer(factory.GetRequiredService>(),
- factory.GetRequiredService(),
- factory.GetRequiredService(),
- Log.Logger);
- } );
- }
- }
-}
diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorComposer.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorComposer.cs
deleted file mode 100644
index eafec39da3..0000000000
--- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorComposer.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using Umbraco.Cms.Core.Composing;
-using Umbraco.Cms.Core.DependencyInjection;
-
-namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes
-{
- public class PreValueMigratorComposer : ICoreComposer
-{
- public void Compose(IUmbracoBuilder builder)
- {
- // do NOT add DefaultPreValueMigrator to this list!
- // it will be automatically used if nothing matches
-
- builder.WithCollectionBuilder()
- .Append()
- .Append()
- .Append()
- .Append()
- .Append()
- .Append()
- .Append()
- .Append()
- .Append()
- .Append()
- .Append();
- }
-}
-}
diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs
index ecdc17f97d..597a8901da 100644
--- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs
+++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs
@@ -101,6 +101,8 @@ namespace Umbraco.Cms.Infrastructure.Runtime
// acquire the main domain - if this fails then anything that should be registered with MainDom will not operate
AcquireMainDom();
+ await _eventAggregator.PublishAsync(new UmbracoApplicationMainDomAcquiredNotification(), cancellationToken);
+
// notify for unattended install
await _eventAggregator.PublishAsync(new RuntimeUnattendedInstallNotification());
DetermineRuntimeLevel();
@@ -137,6 +139,8 @@ namespace Umbraco.Cms.Infrastructure.Runtime
break;
}
+ await _eventAggregator.PublishAsync(new UmbracoApplicationComponentsInstallingNotification(State.Level), cancellationToken);
+
// create & initialize the components
_components.Initialize();
diff --git a/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs b/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs
index b21ee2254d..6e0b58af09 100644
--- a/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs
+++ b/src/Umbraco.Infrastructure/Runtime/RuntimeState.cs
@@ -155,7 +155,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime
}
else
{
- _logger.LogDebug("Package migrations need to execute but unattended package migrations is disabled. They will need to be run from the back office.");
+ _logger.LogInformation("Package migrations need to execute but unattended package migrations is disabled. They will need to be run from the back office.");
Reason = RuntimeLevelReason.Run;
}
diff --git a/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs b/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs
index 459417b289..724eb77030 100644
--- a/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs
+++ b/src/Umbraco.Infrastructure/Security/MemberIdentityUser.cs
@@ -34,7 +34,7 @@ namespace Umbraco.Cms.Core.Security
///
/// Used to construct a new instance without an identity
///
- public static MemberIdentityUser CreateNew(string username, string email, string memberTypeAlias, string name = null)
+ public static MemberIdentityUser CreateNew(string username, string email, string memberTypeAlias, bool isApproved, string name = null)
{
if (string.IsNullOrWhiteSpace(username))
{
@@ -46,6 +46,7 @@ namespace Umbraco.Cms.Core.Security
user.UserName = username;
user.Email = email;
user.MemberTypeAlias = memberTypeAlias;
+ user.IsApproved = isApproved;
user.Id = null;
user.HasIdentity = false;
user.Name = name;
diff --git a/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs b/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs
index aedad0e56b..c6f440942e 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs
@@ -11,6 +11,7 @@ using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Mail;
using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Persistence.Repositories;
diff --git a/src/Umbraco.PublishedCache.NuCache/Compose/NotificationsComposer.cs b/src/Umbraco.PublishedCache.NuCache/Compose/NotificationsComposer.cs
deleted file mode 100644
index 9cb8936431..0000000000
--- a/src/Umbraco.PublishedCache.NuCache/Compose/NotificationsComposer.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using Umbraco.Cms.Core.Composing;
-using Umbraco.Cms.Core.DependencyInjection;
-using Umbraco.Cms.Core.Events;
-using Umbraco.Cms.Core.Notifications;
-
-namespace Umbraco.Cms.Infrastructure.PublishedCache.Compose
-{
- public sealed class NotificationsComposer : ICoreComposer
- {
- public void Compose(IUmbracoBuilder builder) =>
- builder
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- .AddNotificationHandler()
- ;
- }
-}
diff --git a/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs
index 113a2245d8..b68662644e 100644
--- a/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs
+++ b/src/Umbraco.PublishedCache.NuCache/DependencyInjection/UmbracoBuilderExtensions.cs
@@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
@@ -46,10 +47,32 @@ namespace Umbraco.Extensions
return idkSvc;
});
+ builder.AddNuCacheNotifications();
+
// add the NuCache health check (hidden from type finder)
// TODO: no NuCache health check yet
// composition.HealthChecks().Add();
return builder;
}
+
+
+ private static IUmbracoBuilder AddNuCacheNotifications(this IUmbracoBuilder builder)
+ {
+ builder
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ .AddNotificationHandler()
+ ;
+
+ return builder;
+ }
+
+
}
}
diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs
index f0eac637fd..9629375553 100644
--- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs
+++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs
@@ -90,9 +90,13 @@ namespace Umbraco.Cms.Tests.Integration.Testing
s_firstTestInSession = false;
// Ensure CoreRuntime stopped (now it's a HostedService)
- IHost host = Services.GetRequiredService();
- await host.StopAsync();
- host.Dispose();
+ IHost host = Services?.GetService();
+ if (host is not null)
+ {
+ await host.StopAsync();
+ host.Dispose();
+ }
+
}
[TearDown]
diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs
index 182ea07a7f..b1a3233277 100644
--- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs
+++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs
@@ -17,7 +17,6 @@ using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
using Umbraco.Cms.Infrastructure.PublishedCache;
-using Umbraco.Cms.Infrastructure.PublishedCache.Compose;
using Umbraco.Cms.Tests.Common.Builders;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
@@ -54,8 +53,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services
{
builder.AddNuCache();
builder.Services.AddUnique();
- var composer = new NotificationsComposer();
- composer.Compose(builder);
}
private void AssertJsonStartsWith(int id, string expected)
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs
index 44eabbac6f..14ed2ca6eb 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs
@@ -12,6 +12,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
using NUnit.Framework;
+using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Configuration;
@@ -27,7 +28,6 @@ using Umbraco.Cms.Infrastructure.Migrations.Install;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Infrastructure.Persistence.Mappers;
using Umbraco.Cms.Tests.UnitTests.TestHelpers;
-using Constants = Umbraco.Cms.Core.Constants;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components
{
@@ -458,7 +458,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components
}
[ComposeAfter(typeof(Composer4))]
- public class Composer2 : TestComposerBase, ICoreComposer
+ public class Composer2 : TestComposerBase
{
}
@@ -466,6 +466,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components
{
}
+
public class Composer4 : TestComposerBase
{
}
@@ -526,6 +527,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components
{
}
+ [ComposeAfter(typeof(Composer2))]
+ [ComposeAfter(typeof(Composer4))]
public class Composer9 : TestComposerBase, ITestComposer
{
}
@@ -541,7 +544,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components
}
[ComposeAfter(typeof(Composer4), true)]
- public class Composer12 : TestComposerBase, ICoreComposer
+ public class Composer12 : TestComposerBase
{
}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Extensions/EmailMessageExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Extensions/EmailMessageExtensionsTests.cs
index 7915b6b92c..709873e8a3 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Extensions/EmailMessageExtensionsTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Extensions/EmailMessageExtensionsTests.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using NUnit.Framework;
using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Infrastructure.Extensions;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Extensions
@@ -24,9 +25,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Extensions
const string subject = "Subject";
const string body = "Message
";
const bool isBodyHtml = true;
- var emailMesasge = new EmailMessage(from, to, subject, body, isBodyHtml);
+ var emailMessage = new EmailMessage(from, to, subject, body, isBodyHtml);
- var result = emailMesasge.ToMimeMessage(ConfiguredSender);
+ var result = emailMessage.ToMimeMessage(ConfiguredSender);
Assert.AreEqual(1, result.From.Count());
Assert.AreEqual(from, result.From.First().ToString());
@@ -54,9 +55,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Extensions
{
new EmailMessageAttachment(attachmentStream, "test.txt"),
};
- var emailMesasge = new EmailMessage(from, to, cc, bcc, replyTo, subject, body, isBodyHtml, attachments);
+ var emailMessage = new EmailMessage(from, to, cc, bcc, replyTo, subject, body, isBodyHtml, attachments);
- var result = emailMesasge.ToMimeMessage(ConfiguredSender);
+ var result = emailMessage.ToMimeMessage(ConfiguredSender);
Assert.AreEqual(1, result.From.Count());
Assert.AreEqual(from, result.From.First().ToString());
@@ -77,5 +78,147 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Extensions
Assert.AreEqual(body, result.TextBody.ToString());
Assert.AreEqual(1, result.Attachments.Count());
}
+
+ [Test]
+ public void Can_Construct_MimeMessage_With_ConfiguredSender()
+ {
+ const string to = "to@email.com";
+ const string subject = "Subject";
+ const string body = "Message
";
+ const bool isBodyHtml = true;
+ var emailMessage = new EmailMessage(null, to, subject, body, isBodyHtml);
+
+ var result = emailMessage.ToMimeMessage(ConfiguredSender);
+
+ Assert.AreEqual(1, result.From.Count());
+ Assert.AreEqual(ConfiguredSender, result.From.First().ToString());
+ Assert.AreEqual(1, result.To.Count());
+ Assert.AreEqual(to, result.To.First().ToString());
+ Assert.AreEqual(subject, result.Subject);
+ Assert.IsNull(result.TextBody);
+ Assert.AreEqual(body, result.HtmlBody.ToString());
+ }
+
+ [Test]
+ public void Can_Construct_NotificationEmailModel_From_Simple_MailMessage()
+ {
+ const string from = "from@email.com";
+ const string to = "to@email.com";
+ const string subject = "Subject";
+ const string body = "Message
";
+ const bool isBodyHtml = true;
+ var emailMessage = new EmailMessage(from, to, subject, body, isBodyHtml);
+
+ NotificationEmailModel result = emailMessage.ToNotificationEmail(ConfiguredSender);
+
+ Assert.AreEqual(from, result.From.Address);
+ Assert.AreEqual("", result.From.DisplayName);
+ Assert.AreEqual(1, result.To.Count());
+ Assert.AreEqual(to, result.To.First().Address);
+ Assert.AreEqual("", result.To.First().DisplayName);
+ Assert.AreEqual(subject, result.Subject);
+ Assert.AreEqual(body, result.Body);
+ Assert.IsTrue(result.IsBodyHtml);
+ Assert.IsFalse(result.HasAttachments);
+ }
+
+ [Test]
+ public void Can_Construct_NotificationEmailModel_From_Simple_MailMessage_With_Configured_Sender()
+ {
+ const string to = "to@email.com";
+ const string subject = "Subject";
+ const string body = "Message
";
+ const bool isBodyHtml = true;
+ var emailMessage = new EmailMessage(null, to, subject, body, isBodyHtml);
+
+ NotificationEmailModel result = emailMessage.ToNotificationEmail(ConfiguredSender);
+
+ Assert.AreEqual(ConfiguredSender, result.From.Address);
+ Assert.AreEqual("", result.From.DisplayName);
+ Assert.AreEqual(1, result.To.Count());
+ Assert.AreEqual(to, result.To.First().Address);
+ Assert.AreEqual("", result.To.First().DisplayName);
+ Assert.AreEqual(subject, result.Subject);
+ Assert.AreEqual(body, result.Body);
+ Assert.IsTrue(result.IsBodyHtml);
+ Assert.IsFalse(result.HasAttachments);
+ }
+
+ [Test]
+ public void Can_Construct_NotificationEmailModel_From_Simple_MailMessage_With_DisplayName()
+ {
+ const string from = "\"From Email\" ";
+ const string to = "\"To Email\" ";
+ const string subject = "Subject";
+ const string body = "Message
";
+ const bool isBodyHtml = true;
+ var emailMessage = new EmailMessage(from, to, subject, body, isBodyHtml);
+
+ NotificationEmailModel result = emailMessage.ToNotificationEmail(ConfiguredSender);
+
+ Assert.AreEqual("from@from.com", result.From.Address);
+ Assert.AreEqual("From Email", result.From.DisplayName);
+ Assert.AreEqual(1, result.To.Count());
+ Assert.AreEqual("to@to.com", result.To.First().Address);
+ Assert.AreEqual("To Email", result.To.First().DisplayName);
+ Assert.AreEqual(subject, result.Subject);
+ Assert.AreEqual(body, result.Body);
+ Assert.IsTrue(result.IsBodyHtml);
+ Assert.IsFalse(result.HasAttachments);
+ }
+
+
+ [Test]
+ public void Can_Construct_NotificationEmailModel_From_Full_EmailMessage()
+ {
+ const string from = "\"From Email\" ";
+ string[] to = { "to@email.com", "\"Second Email\" ", "invalid@invalid@invalid" };
+ string[] cc = { "\"First CC\" ", "cc2@email.com", "invalid@invalid@invalid" };
+ string[] bcc = { "bcc@email.com", "bcc2@email.com", "\"Third BCC\" ", "invalid@email@address" };
+ string[] replyTo = { "replyto@email.com", "invalid@invalid@invalid" };
+ const string subject = "Subject";
+ const string body = "Message";
+ const bool isBodyHtml = false;
+
+ using var attachmentStream = new MemoryStream(Encoding.UTF8.GetBytes("test"));
+ var attachments = new List
+ {
+ new EmailMessageAttachment(attachmentStream, "test.txt"),
+ };
+ var emailMessage = new EmailMessage(from, to, cc, bcc, replyTo, subject, body, isBodyHtml, attachments);
+
+ var result = emailMessage.ToNotificationEmail(ConfiguredSender);
+
+ Assert.AreEqual("from@from.com", result.From.Address);
+ Assert.AreEqual("From Email", result.From.DisplayName);
+
+ Assert.AreEqual(2, result.To.Count());
+ Assert.AreEqual("to@email.com", result.To.First().Address);
+ Assert.AreEqual("", result.To.First().DisplayName);
+ Assert.AreEqual("to2@email.com", result.To.Skip(1).First().Address);
+ Assert.AreEqual("Second Email", result.To.Skip(1).First().DisplayName);
+
+ Assert.AreEqual(2, result.Cc.Count());
+ Assert.AreEqual("cc@email.com", result.Cc.First().Address);
+ Assert.AreEqual("First CC", result.Cc.First().DisplayName);
+ Assert.AreEqual("cc2@email.com", result.Cc.Skip(1).First().Address);
+ Assert.AreEqual("", result.Cc.Skip(1).First().DisplayName);
+
+ Assert.AreEqual(3, result.Bcc.Count());
+ Assert.AreEqual("bcc@email.com", result.Bcc.First().Address);
+ Assert.AreEqual("", result.Bcc.First().DisplayName);
+ Assert.AreEqual("bcc2@email.com", result.Bcc.Skip(1).First().Address);
+ Assert.AreEqual("", result.Bcc.Skip(1).First().DisplayName);
+ Assert.AreEqual("bcc3@email.com", result.Bcc.Skip(2).First().Address);
+ Assert.AreEqual("Third BCC", result.Bcc.Skip(2).First().DisplayName);
+
+ Assert.AreEqual(1, result.ReplyTo.Count());
+ Assert.AreEqual("replyto@email.com", result.ReplyTo.First().Address);
+ Assert.AreEqual("", result.ReplyTo.First().DisplayName);
+
+ Assert.AreEqual(subject, result.Subject);
+ Assert.AreEqual(body, result.Body);
+ Assert.AreEqual(1, result.Attachments.Count());
+ }
}
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
index 707c486f3e..c552c0d976 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
@@ -17,6 +17,7 @@ using Umbraco.Cms.Core.Mail;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
+using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Net;
using Umbraco.Cms.Core.Security;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
index 3d435322b8..6045cec8f9 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
@@ -366,6 +366,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
contentItem.Username,
contentItem.Email,
memberType.Alias,
+ contentItem.IsApproved,
contentItem.Name);
IdentityResult created = await _memberManager.CreateAsync(identityMember, contentItem.Password.NewPassword);
diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
index 192399b028..ee09b7d67b 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
@@ -26,6 +26,7 @@ using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Media;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
+using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
@@ -683,12 +684,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
// if it's the current user, the current user cannot reset their own password without providing their old password
if (currentUser.Username == found.Username && string.IsNullOrEmpty(changingPasswordModel.OldPassword))
{
- return new ValidationErrorResult("Password reset is not allowed without providing old password");
+ return ValidationErrorResult.CreateNotificationValidationErrorResult("Password reset is not allowed without providing old password");
}
if (!currentUser.IsAdmin() && found.IsAdmin())
{
- return new ValidationErrorResult("The current user cannot change the password for the specified user");
+ return ValidationErrorResult.CreateNotificationValidationErrorResult("The current user cannot change the password for the specified user");
}
Attempt passwordChangeResult = await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _userManager);
diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs
index 5088f69ef1..b24b0c32fc 100644
--- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs
+++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs
@@ -1,6 +1,7 @@
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.IO;
@@ -49,6 +50,8 @@ namespace Umbraco.Extensions
.AddDistributedCache()
.AddModelsBuilderDashboard()
.AddUnattendedInstallInstallCreateUser()
+ .AddCoreNotifications()
+ .AddLogViewer()
.AddExamine()
.AddExamineIndexes();
@@ -58,7 +61,6 @@ namespace Umbraco.Extensions
return builder;
}
-
///
/// Adds Umbraco preview support
///
diff --git a/src/Umbraco.Web.Common/ApplicationBuilder/IUmbracoApplicationBuilder.cs b/src/Umbraco.Web.Common/ApplicationBuilder/IUmbracoApplicationBuilder.cs
index 68ba148f49..090ef52790 100644
--- a/src/Umbraco.Web.Common/ApplicationBuilder/IUmbracoApplicationBuilder.cs
+++ b/src/Umbraco.Web.Common/ApplicationBuilder/IUmbracoApplicationBuilder.cs
@@ -4,7 +4,7 @@ using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Web.Common.ApplicationBuilder
{
- public interface IUmbracoApplicationBuilder : IUmbracoMiddlewareBuilder
+ public interface IUmbracoApplicationBuilder
{
///
/// Called to include umbraco middleware
diff --git a/src/Umbraco.Web.Common/ApplicationBuilder/UmbracoApplicationBuilder.cs b/src/Umbraco.Web.Common/ApplicationBuilder/UmbracoApplicationBuilder.cs
index 05fc38cc71..b7acc45d22 100644
--- a/src/Umbraco.Web.Common/ApplicationBuilder/UmbracoApplicationBuilder.cs
+++ b/src/Umbraco.Web.Common/ApplicationBuilder/UmbracoApplicationBuilder.cs
@@ -9,7 +9,7 @@ namespace Umbraco.Cms.Web.Common.ApplicationBuilder
///
/// A builder to allow encapsulating the enabled endpoints in Umbraco
///
- internal class UmbracoApplicationBuilder : IUmbracoApplicationBuilder
+ internal class UmbracoApplicationBuilder : IUmbracoApplicationBuilder, IUmbracoMiddlewareBuilder
{
public UmbracoApplicationBuilder(IServiceProvider services, IRuntimeState runtimeState, IApplicationBuilder appBuilder)
{
diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs
index 2a0b77461d..e991a6a6ed 100644
--- a/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs
+++ b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs
@@ -109,6 +109,9 @@ namespace Umbraco.Extensions
public static bool IsAllowedTemplate(this IPublishedContent content, int templateId)
=> content.IsAllowedTemplate(ContentTypeService, WebRoutingSettings.Value, templateId);
+ public static bool IsAllowedTemplate(this IPublishedContent content, string templateAlias)
+ => content.IsAllowedTemplate(WebRoutingSettings.Value.DisableAlternativeTemplates, WebRoutingSettings.Value.ValidateAlternativeTemplates, templateAlias);
+
public static bool IsAllowedTemplate(
this IPublishedContent content,
bool disableAlternativeTemplates,
diff --git a/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs b/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs
index c2608efc41..97ba933838 100644
--- a/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs
+++ b/src/Umbraco.Web.Common/Views/UmbracoViewPage.cs
@@ -228,7 +228,7 @@ namespace Umbraco.Cms.Web.Common.Views
// if not possible or it is not generic then we need to create a new ViewDataDictionary
Type nViewDataType = typeof(ViewDataDictionary<>).MakeGenericType(modelType);
- var tViewData = new ViewDataDictionary(viewData) { Model = null }; // temp view data to copy values
+ var tViewData = new ViewDataDictionary(viewData) { Model = default(TModel) }; // temp view data to copy values
var nViewData = (ViewDataDictionary)Activator.CreateInstance(nViewDataType, tViewData);
return nViewData;
}
diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json
index b127fa3c3d..813ba75095 100644
--- a/src/Umbraco.Web.UI.Client/package.json
+++ b/src/Umbraco.Web.UI.Client/package.json
@@ -52,10 +52,10 @@
},
"devDependencies": {
"@babel/core": "7.6.4",
- "@babel/preset-env": "7.6.3",
"@babel/plugin-proposal-object-rest-spread": "7.13.8",
+ "@babel/preset-env": "7.6.3",
"autoprefixer": "9.6.5",
- "caniuse-lite": "^1.0.30001037",
+ "caniuse-lite": "^1.0.30001237",
"cssnano": "4.1.10",
"fs": "0.0.2",
"gulp": "4.0.2",
@@ -67,6 +67,7 @@
"gulp-eslint": "6.0.0",
"gulp-imagemin": "7.1.0",
"gulp-less": "4.0.1",
+ "gulp-minify": "3.1.0",
"gulp-notify": "^3.0.0",
"gulp-postcss": "8.0.0",
"gulp-rename": "1.4.0",
@@ -75,12 +76,11 @@
"gulp-watch": "5.0.1",
"gulp-wrap": "0.15.0",
"gulp-wrap-js": "0.4.1",
- "gulp-minify": "3.1.0",
"jasmine-core": "3.5.0",
"jsdom": "16.4.0",
"karma": "4.4.1",
- "karma-jsdom-launcher": "^8.0.2",
"karma-jasmine": "2.0.1",
+ "karma-jsdom-launcher": "^8.0.2",
"karma-junit-reporter": "2.0.1",
"karma-spec-reporter": "0.0.32",
"less": "3.10.3",
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dashboard.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dashboard.controller.js
index e788e6fc9b..d7d5153956 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/dashboard.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/common/dashboard.controller.js
@@ -7,38 +7,59 @@
* Controls the dashboards of the application
*
*/
-
-function DashboardController($scope, $routeParams, dashboardResource, localizationService) {
+
+function DashboardController($scope, $q, $routeParams, $location, dashboardResource, localizationService) {
+ const DASHBOARD_QUERY_PARAM = 'dashboard';
$scope.page = {};
$scope.page.nameLocked = true;
$scope.page.loading = true;
$scope.dashboard = {};
- localizationService.localize("sections_" + $routeParams.section).then(function(name){
- $scope.dashboard.name = name;
- });
-
- dashboardResource.getDashboard($routeParams.section).then(function(tabs){
- $scope.dashboard.tabs = tabs;
-
- // set first tab to active
- if($scope.dashboard.tabs && $scope.dashboard.tabs.length > 0) {
- $scope.dashboard.tabs[0].active = true;
- }
+ var promises = [];
+
+ promises.push(localizationService.localize("sections_" + $routeParams.section).then(function (name) {
+ $scope.dashboard.name = name;
+ }));
+
+ promises.push(dashboardResource.getDashboard($routeParams.section).then(function (tabs) {
+ $scope.dashboard.tabs = tabs;
+
+ if ($scope.dashboard.tabs && $scope.dashboard.tabs.length > 0) {
+ initActiveTab();
+ }
+ }));
+
+ $q.all(promises).then(function () {
$scope.page.loading = false;
});
- $scope.changeTab = function(tab) {
- $scope.dashboard.tabs.forEach(function(tab) {
- tab.active = false;
- });
+ $scope.changeTab = function (tab) {
+ if ($scope.dashboard.tabs && $scope.dashboard.tabs.length > 0) {
+ $scope.dashboard.tabs.forEach(function (tab) {
+ tab.active = false;
+ });
+ }
+
tab.active = true;
+ $location.search(DASHBOARD_QUERY_PARAM, tab.alias);
};
+ function initActiveTab() {
+ // Check the query parameter for a dashboard alias
+ const dashboardAlias = $location.search()[DASHBOARD_QUERY_PARAM];
+ const dashboardIndex = $scope.dashboard.tabs.findIndex(tab => tab.alias === dashboardAlias);
+
+ // Set the first dashboard to active if there is no query parameter or we can't find a matching dashboard for the alias
+ const activeIndex = dashboardIndex !== -1 ? dashboardIndex : 0;
+
+ const tab = $scope.dashboard.tabs[activeIndex];
+
+ tab.active = true;
+ $location.search(DASHBOARD_QUERY_PARAM, tab.alias);
+ }
}
-
-//register it
+// Register it
angular.module('umbraco').controller("Umbraco.DashboardController", DashboardController);
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.controller.js
index 4e1b3e868d..e2a6e30405 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.controller.js
@@ -8,141 +8,140 @@
*/
(function () {
- "use strict";
+ "use strict";
- function DataTypeSettingsController($scope, dataTypeResource, dataTypeHelper,
- localizationService, notificationsService, overlayService, formHelper, eventsService) {
+ function DataTypeSettingsController($scope, dataTypeResource, dataTypeHelper,
+ localizationService, notificationsService, overlayService, formHelper, eventsService) {
- var vm = this;
+ var vm = this;
- vm.dataType = {};
- vm.loadingDataType = false;
- vm.saveButtonState = "init";
+ vm.dataType = {};
+ vm.loadingDataType = false;
+ vm.saveButtonState = "init";
- vm.close = close;
- vm.submit = submit;
+ vm.close = close;
+ vm.submit = submit;
- function onInit() {
+ function onInit() {
- setTitle();
+ setTitle();
- if($scope.model.create) {
- createNewDataType();
- } else {
- getDataType();
- }
+ if($scope.model.create) {
+ createNewDataType();
+ } else {
+ getDataType();
+ }
+ }
+
+ function setTitle() {
+ if(!$scope.model.title) {
+ localizationService.localize("contentTypeEditor_editorSettings")
+ .then(function(data){
+ $scope.model.title = data;
+ });
+ }
+ }
+
+ function createNewDataType() {
+
+ vm.loadingDataType = true;
+
+ var parentId = -1;
+ var newDataType = {};
+
+ dataTypeResource.getScaffold(parentId).then(function(dataType) {
+
+ newDataType = dataType;
+
+ // set alias
+ newDataType.selectedEditor = $scope.model.propertyEditor.alias;
+
+ // set name
+ var nameArray = [];
+
+ if ($scope.model.contentTypeName) {
+ nameArray.push($scope.model.contentTypeName);
}
- function setTitle() {
- if(!$scope.model.title) {
- localizationService.localize("contentTypeEditor_editorSettings")
- .then(function(data){
- $scope.model.title = data;
- });
- }
+ if ($scope.model.property.label) {
+ nameArray.push($scope.model.property.label);
}
- function createNewDataType() {
-
- vm.loadingDataType = true;
-
- var parentId = -1;
- var newDataType = {};
-
- dataTypeResource.getScaffold(parentId).then(function(dataType) {
-
- newDataType = dataType;
-
- // set alias
- newDataType.selectedEditor = $scope.model.propertyEditor.alias;
-
- // set name
- var nameArray = [];
-
- if ($scope.model.contentTypeName) {
- nameArray.push($scope.model.contentTypeName);
- }
-
- if ($scope.model.property.label) {
- nameArray.push($scope.model.property.label);
- }
-
- if ($scope.model.propertyEditor.name) {
- nameArray.push($scope.model.propertyEditor.name);
- }
-
- // make name
- newDataType.name = nameArray.join(" - ");
-
- // get pre values
- dataTypeResource.getPreValues(newDataType.selectedEditor).then(function(preValues) {
- newDataType.preValues = dataTypeHelper.createPreValueProps(preValues);
- vm.dataType = newDataType;
- vm.loadingDataType = false;
- });
-
- });
-
+ if ($scope.model.propertyEditor.name) {
+ nameArray.push($scope.model.propertyEditor.name);
}
- function getDataType() {
- vm.loadingDataType = true;
- dataTypeResource.getById($scope.model.id).then(function (dataType) {
- dataType.preValues = dataTypeHelper.createPreValueProps(dataType.preValues);
- vm.dataType = dataType;
- vm.loadingDataType = false;
- });
- }
+ // make name
+ newDataType.name = nameArray.join(" - ");
- function close() {
- if ($scope.model && $scope.model.close) {
- $scope.model.close();
- }
- }
+ // get pre values
+ dataTypeResource.getPreValues(newDataType.selectedEditor).then(function(preValues) {
+ newDataType.preValues = preValues;
+ vm.dataType = newDataType;
+ vm.loadingDataType = false;
+ });
- function submit() {
- if (!formHelper.submitForm({ scope: $scope })) {
- return;
- }
-
- vm.saveButtonState = "busy";
-
- var preValues = dataTypeHelper.createPreValueProps(vm.dataType.preValues);
-
- dataTypeResource.save(vm.dataType, preValues, $scope.model.create).then(
- function(newDataType) {
- $scope.model.dataType = newDataType;
-
- var args = { dataType: newDataType };
- eventsService.emit("editors.dataTypeSettings.saved", args);
-
- vm.saveButtonState = "success";
-
- if ($scope.model && $scope.model.submit) {
- $scope.model.submit($scope.model);
- }
- }, function(err) {
- vm.saveButtonState = "error";
-
- if(err.status === 400) {
- if (err.data && (err.data.ModelState)) {
-
- formHelper.handleServerValidation(err.data.ModelState);
-
- for (var e in err.data.ModelState) {
- notificationsService.error("Validation", err.data.ModelState[e][0]);
- }
- }
- }
- }
- );
-
- }
-
- onInit();
+ });
}
- angular.module("umbraco").controller("Umbraco.Editors.DataTypeSettingsController", DataTypeSettingsController);
+ function getDataType() {
+ vm.loadingDataType = true;
+ dataTypeResource.getById($scope.model.id).then(function (dataType) {
+ vm.dataType = dataType;
+ vm.loadingDataType = false;
+ });
+ }
+
+ function close() {
+ if ($scope.model && $scope.model.close) {
+ $scope.model.close();
+ }
+ }
+
+ function submit() {
+ if (!formHelper.submitForm({ scope: $scope })) {
+ return;
+ }
+
+ vm.saveButtonState = "busy";
+
+ var preValues = dataTypeHelper.createPreValueProps(vm.dataType.preValues);
+
+ dataTypeResource.save(vm.dataType, preValues, $scope.model.create).then(
+ function(newDataType) {
+ $scope.model.dataType = newDataType;
+
+ var args = { dataType: newDataType };
+ eventsService.emit("editors.dataTypeSettings.saved", args);
+
+ vm.saveButtonState = "success";
+
+ if ($scope.model && $scope.model.submit) {
+ $scope.model.submit($scope.model);
+ }
+ }, function(err) {
+ vm.saveButtonState = "error";
+
+ if(err.status === 400) {
+ if (err.data && (err.data.ModelState)) {
+
+ formHelper.handleServerValidation(err.data.ModelState);
+
+ for (var e in err.data.ModelState) {
+ notificationsService.error("Validation", err.data.ModelState[e][0]);
+ }
+ }
+ }
+ }
+ );
+
+ }
+
+ onInit();
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.Editors.DataTypeSettingsController", DataTypeSettingsController);
})();
diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs
index 2f97a15d8f..0419a8c0e4 100644
--- a/src/Umbraco.Web.UI.NetCore/Startup.cs
+++ b/src/Umbraco.Web.UI.NetCore/Startup.cs
@@ -3,9 +3,9 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Extensions;
-using Microsoft.Extensions.Hosting;
namespace Umbraco.Cms.Web.UI.NetCore
{
@@ -41,7 +41,7 @@ namespace Umbraco.Cms.Web.UI.NetCore
{
#pragma warning disable IDE0022 // Use expression body for methods
services.AddUmbraco(_env, _config)
- .AddBackOffice()
+ .AddBackOffice()
.AddWebsite()
.AddComposers()
.Build();
diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json
index aaa5e72f9b..1e2d97a018 100644
--- a/src/Umbraco.Web.UI.NetCore/appsettings.json
+++ b/src/Umbraco.Web.UI.NetCore/appsettings.json
@@ -39,7 +39,7 @@
},
"RuntimeMinification": {
"dataFolder": "umbraco/Data/TEMP/Smidge",
- "version": "637510451273675926"
+ "version": "637550581849220194"
},
"Security": {
"KeepUserLoggedIn": false,
@@ -71,4 +71,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs b/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs
index 2a8dd6a71a..1e6c417ed3 100644
--- a/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs
+++ b/src/Umbraco.Web.Website/Controllers/UmbRegisterController.cs
@@ -127,7 +127,7 @@ namespace Umbraco.Cms.Web.Website.Controllers
model.Username = (model.UsernameIsEmail || model.Username == null) ? model.Email : model.Username;
- var identityUser = MemberIdentityUser.CreateNew(model.Username, model.Email, model.MemberTypeAlias, model.Name);
+ var identityUser = MemberIdentityUser.CreateNew(model.Username, model.Email, model.MemberTypeAlias, true, model.Name);
IdentityResult identityResult = await _memberManager.CreateAsync(
identityUser,
model.Password);
@@ -142,6 +142,7 @@ namespace Umbraco.Cms.Web.Website.Controllers
// should never happen
throw new InvalidOperationException($"Could not find a member with key: {member.Key}.");
}
+
if (model.MemberProperties != null)
{
foreach (MemberPropertyModel property in model.MemberProperties.Where(p => p.Value != null)
@@ -159,7 +160,6 @@ namespace Umbraco.Cms.Web.Website.Controllers
}
return identityResult;
-
}
}
}
diff --git a/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs
index ddca7f37aa..123ed6801a 100644
--- a/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs
+++ b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs
@@ -791,73 +791,73 @@ namespace Umbraco.Extensions
///
/// Strips all HTML tags from a given string, all contents of the tags will remain.
///
- public static IHtmlContent StripHtml(this HtmlHelper helper, IHtmlContent html, params string[] tags)
+ public static IHtmlContent StripHtml(this IHtmlHelper helper, IHtmlContent html, params string[] tags)
=> helper.StripHtml(html.ToHtmlString(), tags);
///
/// Strips all HTML tags from a given string, all contents of the tags will remain.
///
- public static IHtmlContent StripHtml(this HtmlHelper helper, string html, params string[] tags)
+ public static IHtmlContent StripHtml(this IHtmlHelper helper, string html, params string[] tags)
=> StringUtilities.StripHtmlTags(html, tags);
///
/// Will take the first non-null value in the collection and return the value of it.
///
- public static string Coalesce(this HtmlHelper helper, params object[] args)
+ public static string Coalesce(this IHtmlHelper helper, params object[] args)
=> StringUtilities.Coalesce(args);
///
/// Joins any number of int/string/objects into one string
///
- public static string Concatenate(this HtmlHelper helper, params object[] args)
+ public static string Concatenate(this IHtmlHelper helper, params object[] args)
=> StringUtilities.Concatenate(args);
///
/// Joins any number of int/string/objects into one string and separates them with the string separator parameter.
///
- public static string Join(this HtmlHelper helper, string separator, params object[] args)
+ public static string Join(this IHtmlHelper helper, string separator, params object[] args)
=> StringUtilities.Join(separator, args);
///
/// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them
///
- public static IHtmlContent Truncate(this HtmlHelper helper, IHtmlContent html, int length)
+ public static IHtmlContent Truncate(this IHtmlHelper helper, IHtmlContent html, int length)
=> helper.Truncate(html.ToHtmlString(), length, true, false);
///
/// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them
///
- public static IHtmlContent Truncate(this HtmlHelper helper, IHtmlContent html, int length, bool addElipsis)
+ public static IHtmlContent Truncate(this IHtmlHelper helper, IHtmlContent html, int length, bool addElipsis)
=> helper.Truncate(html.ToHtmlString(), length, addElipsis, false);
///
/// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them
///
- public static IHtmlContent Truncate(this HtmlHelper helper, IHtmlContent html, int length, bool addElipsis, bool treatTagsAsContent)
+ public static IHtmlContent Truncate(this IHtmlHelper helper, IHtmlContent html, int length, bool addElipsis, bool treatTagsAsContent)
=> helper.Truncate(html.ToHtmlString(), length, addElipsis, treatTagsAsContent);
///
/// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them
///
- public static IHtmlContent Truncate(this HtmlHelper helper, string html, int length)
+ public static IHtmlContent Truncate(this IHtmlHelper helper, string html, int length)
=> helper.Truncate(html, length, true, false);
///
/// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them
///
- public static IHtmlContent Truncate(this HtmlHelper helper, string html, int length, bool addElipsis)
+ public static IHtmlContent Truncate(this IHtmlHelper helper, string html, int length, bool addElipsis)
=> helper.Truncate(html, length, addElipsis, false);
///
/// Truncates a string to a given length, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them
///
- public static IHtmlContent Truncate(this HtmlHelper helper, string html, int length, bool addElipsis, bool treatTagsAsContent)
+ public static IHtmlContent Truncate(this IHtmlHelper helper, string html, int length, bool addElipsis, bool treatTagsAsContent)
=> StringUtilities.Truncate(html, length, addElipsis, treatTagsAsContent);
///
/// Truncates a string to a given amount of words, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them
///
- public static IHtmlContent TruncateByWords(this HtmlHelper helper, string html, int words)
+ public static IHtmlContent TruncateByWords(this IHtmlHelper helper, string html, int words)
{
int length = StringUtilities.WordsToLength(html, words);
@@ -867,7 +867,7 @@ namespace Umbraco.Extensions
///
/// Truncates a string to a given amount of words, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them
///
- public static IHtmlContent TruncateByWords(this HtmlHelper helper, string html, int words, bool addElipsis)
+ public static IHtmlContent TruncateByWords(this IHtmlHelper helper, string html, int words, bool addElipsis)
{
int length = StringUtilities.WordsToLength(html, words);
@@ -877,7 +877,7 @@ namespace Umbraco.Extensions
///
/// Truncates a string to a given amount of words, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them
///
- public static IHtmlContent TruncateByWords(this HtmlHelper helper, IHtmlContent html, int words)
+ public static IHtmlContent TruncateByWords(this IHtmlHelper helper, IHtmlContent html, int words)
{
int length = StringUtilities.WordsToLength(html.ToHtmlString(), words);
@@ -887,7 +887,7 @@ namespace Umbraco.Extensions
///
/// Truncates a string to a given amount of words, can add a ellipsis at the end (...). Method checks for open HTML tags, and makes sure to close them
///
- public static IHtmlContent TruncateByWords(this HtmlHelper helper, IHtmlContent html, int words, bool addElipsis)
+ public static IHtmlContent TruncateByWords(this IHtmlHelper helper, IHtmlContent html, int words, bool addElipsis)
{
int length = StringUtilities.WordsToLength(html.ToHtmlString(), words);
diff --git a/src/Umbraco.Web/Runtime/WebFinalComposer.cs b/src/Umbraco.Web/Runtime/WebFinalComposer.cs
index 818bff521a..9f2fbb1018 100644
--- a/src/Umbraco.Web/Runtime/WebFinalComposer.cs
+++ b/src/Umbraco.Web/Runtime/WebFinalComposer.cs
@@ -5,7 +5,6 @@ namespace Umbraco.Web.Runtime
// web's final composer composes after all user composers
// and *also* after ICoreComposer (in case IUserComposer is disabled)
[ComposeAfter(typeof(IUserComposer))]
- [ComposeAfter(typeof(ICoreComposer))]
public class WebFinalComposer : ComponentComposer
{
}