diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index 8113777d0d..6c8c2aab50 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -88,6 +88,20 @@ stages: gulpFile: src/Umbraco.Web.UI.Client/gulpfile.js targets: coreBuild workingDirectory: src/Umbraco.Web.UI.Client + - task: npm@1 + displayName: Run npm ci + inputs: + command: custom + workingDir: src/Umbraco.Web.UI.Login + verbose: false + customCommand: ci + - task: npm@1 + displayName: Run npm build + inputs: + command: custom + workingDir: src/Umbraco.Web.UI.Login + verbose: false + customCommand: run build - task: UseDotNet@2 displayName: Use .NET $(dotnetVersion) inputs: diff --git a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj index 1cfb2dbd85..f2b95c3a8d 100644 --- a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj +++ b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj @@ -25,6 +25,8 @@ + + diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/AuthorizeUpgrade.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/AuthorizeUpgrade.cshtml index ab45a61ed0..cce2140d06 100644 --- a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/AuthorizeUpgrade.cshtml +++ b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/AuthorizeUpgrade.cshtml @@ -56,15 +56,6 @@ @await Html.BareMinimumServerVariablesScriptAsync(backOfficeServerVariables) - - @*And finally we can load in our angular app*@ diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Default.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Default.cshtml index 51a9d3d9fa..7e66a42d39 100644 --- a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Default.cshtml +++ b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoBackOffice/Default.cshtml @@ -1,12 +1,10 @@ @using Microsoft.Extensions.Options; @using System.Globalization -@using Umbraco.Cms.Core @using Umbraco.Cms.Core.Configuration @using Umbraco.Cms.Core.Configuration.Models @using Umbraco.Cms.Core.Hosting @using Umbraco.Cms.Core.Logging @using Umbraco.Cms.Core.Routing -@using Umbraco.Cms.Core.Services @using Umbraco.Cms.Core.WebAssets @using Umbraco.Cms.Infrastructure.WebAssets @using Umbraco.Cms.Web.BackOffice.Controllers @@ -18,7 +16,6 @@ @inject IOptions globalSettings @inject IRuntimeMinifier runtimeMinifier @inject IProfilerHtml profilerHtml -@inject IIconService IconService @inject IBackOfficeExternalLoginProviders externalLogins @{ bool.TryParse(Context.Request.Query["umbDebug"], out bool isDebug); @@ -27,7 +24,7 @@ - + @@ -41,9 +38,11 @@ @Html.Raw(await runtimeMinifier.RenderCssHereAsync(BackOfficeWebAssets.UmbracoInitCssBundleName)) + + + - -
+ +
- + - - - - - - - - - - - - + + + @await Html.BareMinimumServerVariablesScriptAsync(backOfficeServerVariables) diff --git a/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml new file mode 100644 index 0000000000..08915506fa --- /dev/null +++ b/src/Umbraco.Cms.StaticAssets/umbraco/UmbracoLogin/Index.cshtml @@ -0,0 +1,76 @@ +@using System.Globalization +@using Microsoft.AspNetCore.Routing +@using Microsoft.Extensions.Options; +@using Umbraco.Cms.Core +@using Umbraco.Cms.Core.Configuration.Models +@using Umbraco.Cms.Core.Hosting +@using Umbraco.Cms.Core.Mail +@using Umbraco.Cms.Web.BackOffice.Controllers +@using Umbraco.Cms.Web.BackOffice.Security +@using Umbraco.Extensions +@inject IOptions ContentSettings +@inject IOptions SecuritySettings +@inject IEmailSender EmailSender +@inject IHostingEnvironment HostingEnvironment +@inject IOptions GlobalSettings +@inject IBackOfficeExternalLoginProviders ExternalLogins +@inject LinkGenerator LinkGenerator +@{ + var backOfficePath = GlobalSettings.Value.GetBackOfficePath(HostingEnvironment); + var loginLogoImage = ContentSettings.Value.LoginLogoImage; + var loginBackgroundImage = ContentSettings.Value.LoginBackgroundImage; + var usernameIsEmail = SecuritySettings.Value.UsernameIsEmail; + var allowUserInvite = EmailSender.CanSendRequiredEmail(); + var allowPasswordReset = SecuritySettings.Value.AllowPasswordReset && EmailSender.CanSendRequiredEmail(); + var disableLocalLogin = ExternalLogins.HasDenyLocalLogin(); + var externalLoginsUrl = LinkGenerator.GetPathByAction(nameof(BackOfficeController.ExternalLogin), ControllerExtensions.GetControllerName(), new { area = Constants.Web.Mvc.BackOfficeArea }); + var externalLoginProviders = await ExternalLogins.GetBackOfficeProvidersAsync(); +} +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers + + + + + + + + + Umbraco + + + + + + + + + + @foreach (var provider in externalLoginProviders) + { + + + } + + + + diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs index 6eb184fb40..0c39c1b1b0 100644 --- a/src/Umbraco.Core/Constants-Web.cs +++ b/src/Umbraco.Core/Constants-Web.cs @@ -54,6 +54,7 @@ public static partial class Constants public const string BackOfficeArea = "UmbracoBackOffice"; // Used for area routes of non-api controllers public const string BackOfficeApiArea = "UmbracoApi"; // Same name as v8 so all routing remains the same public const string BackOfficeTreeArea = "UmbracoTrees"; // Same name as v8 so all routing remains the same + public const string BackOfficeLoginArea = "UmbracoLogin"; // Used for area routes of non-api controllers for login } public static class Routing diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml index 008faa39fe..c24bbdcdd0 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml @@ -834,6 +834,7 @@ Sortér Status Indsend + Succes Type Skriv for at søge... under @@ -1022,6 +1023,7 @@ Log ind nedenfor Log ind med Din session er udløbet + Ups! Vi kunne ikke logge dig ind. Tjek at dit brugernavn og adgangskode er korrekt og prøv igen. © 2001 - %0%
umbraco.com

]]>
Glemt adgangskode? diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index 4213dcc16a..8c35554043 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -1075,6 +1075,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Log in below Sign in with Session timed out + Oops! We couldn't log you in. Please check your credentials and try again. © 2001 - %0%
Umbraco.com

]]>
Forgotten password? diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml index 8a1b63ed11..3369f61af6 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml @@ -1104,6 +1104,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Log in below Sign in with Session timed out + Oops! We couldn't log you in. Please check your credentials and try again. © 2001 - %0%
Umbraco.com

]]>
Forgotten password? diff --git a/src/Umbraco.Core/Models/UuiButtonColor.cs b/src/Umbraco.Core/Models/UuiButtonColor.cs new file mode 100644 index 0000000000..65f4016fad --- /dev/null +++ b/src/Umbraco.Core/Models/UuiButtonColor.cs @@ -0,0 +1,15 @@ +namespace Umbraco.Cms.Core.Models; + +/// +/// Option to set the color of a uui-button element. +/// +/// +/// See the UUI documentation for more details: https://uui.umbraco.com/?path=/story/uui-button--looks-and-colors. +/// +public enum UuiButtonColor +{ + Default, + Positive, + Warning, + Danger +} diff --git a/src/Umbraco.Core/Models/UuiButtonLook.cs b/src/Umbraco.Core/Models/UuiButtonLook.cs new file mode 100644 index 0000000000..33742681b1 --- /dev/null +++ b/src/Umbraco.Core/Models/UuiButtonLook.cs @@ -0,0 +1,16 @@ +namespace Umbraco.Cms.Core.Models; + +/// +/// Option to set the look of a uui-button element. +/// +/// +/// See the UUI documentation for more details: https://uui.umbraco.com/?path=/story/uui-button--looks-and-colors. +/// +public enum UuiButtonLook +{ + Default, + Primary, + Secondary, + Outline, + Placeholder +} diff --git a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs index ab25dc691c..56f740804e 100644 --- a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs +++ b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs @@ -59,8 +59,6 @@ public class BackOfficeWebAssets BundlingOptions.NotOptimizedAndComposite, FormatPaths( "assets/css/umbraco.min.css", - "lib/umbraco-ui/uui-css/dist/custom-properties.css", - "lib/umbraco-ui/uui-css/dist/uui-text.css", "lib/bootstrap-social/bootstrap-social.css", "lib/font-awesome/css/font-awesome.min.css")); diff --git a/src/Umbraco.Infrastructure/WebAssets/JsInitialize.js b/src/Umbraco.Infrastructure/WebAssets/JsInitialize.js index 1d7068c425..14dab707c1 100644 --- a/src/Umbraco.Infrastructure/WebAssets/JsInitialize.js +++ b/src/Umbraco.Infrastructure/WebAssets/JsInitialize.js @@ -1,5 +1,5 @@ [ - + 'lib/jquery/jquery.min.js', 'lib/jquery-ui/jquery-ui.min.js', 'lib/jquery-ui-touch-punch/jquery.ui.touch-punch.min.js', @@ -35,8 +35,6 @@ 'lib/umbraco/NamespaceManager.js', 'lib/umbraco/LegacySpeechBubble.js', - 'lib/umbraco-ui/uui/dist/uui.min.js', - 'js/utilities.min.js', 'js/app.min.js', @@ -48,5 +46,5 @@ 'js/umbraco.interceptors.min.js', 'js/umbraco.controllers.min.js', 'js/routes.min.js', - 'js/init.min.js' + 'js/init.min.js' ] diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index fe105bee67..71eb240fd5 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -307,14 +307,15 @@ public class AuthenticationController : UmbracoApiControllerBase /// When a user is invited they are not approved but we need to resolve the partially logged on (non approved) /// user. /// - /// + /// It returns a 403 error if the logged-in user has already been created. /// /// We cannot user GetCurrentUser since that requires they are approved, this is the same as GetCurrentUser but doesn't /// require them to be approved /// [Authorize(Policy = AuthorizationPolicies.BackOfficeAccessWithoutApproval)] - [SetAngularAntiForgeryTokens] [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] + [SetAngularAntiForgeryTokens] + [AllowAnonymous] // Needed for users that are invited when they use the link from the mail they may have logged in on a different session, so we don't want to redirect them. public ActionResult GetCurrentInvitedUser() { IUser? user = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; @@ -336,6 +337,76 @@ public class AuthenticationController : UmbracoApiControllerBase return result; } + /// + /// When a user is invited and they click on the invitation link, they will be partially logged in + /// where they can set their username/password. + /// + /// The model for the new password. + /// The user model for the invited user. + /// + /// This only works when the user is logged in (partially). + /// + [Authorize(Policy = AuthorizationPolicies.BackOfficeAccessWithoutApproval)] + [Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)] + [SetAngularAntiForgeryTokens] + [AllowAnonymous] // Needed for users that are invited when they use the link from the mail they may have logged in on a different session, so we don't want to redirect them. + public async Task> PostSetInvitedUserPassword(InvitePasswordModel invitePasswordModel) + { + IUser? currentUser = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; + + if (currentUser is null) + { + return BadRequest("Could not find user"); + } + + if (currentUser.IsApproved) + { + // if they are approved, than they are no longer invited and we can return an error + return Forbid(); + } + + BackOfficeIdentityUser? user = await _userManager.FindByIdAsync(currentUser!.Id.ToString()); + + if (user is null) + { + return BadRequest("Could not find identity user"); + } + + IdentityResult result = await _userManager.AddPasswordAsync(user, invitePasswordModel.NewPassword); + + if (result.Succeeded is false) + { + // it wasn't successful, so add the change error to the model state, we've name the property alias _umb_password on the form + // so that is why it is being used here. + ModelState.AddModelError("value", result.Errors.ToErrorMessage()); + + return ValidationProblem(ModelState); + } + + if (_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser is not null) + { + // They've successfully set their password, we can now update their user account to be approved + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.IsApproved = true; + + // They've successfully set their password, and will now get fully logged into the back office, so the lastlogindate is set so the backoffice shows they have logged in + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.LastLoginDate = DateTime.UtcNow; + + _userService.Save(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser); + } + + + // now we can return their full object since they are now really logged into the back office + UserDetail? userDisplay = + _umbracoMapper.Map(_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser); + + if (userDisplay is not null) + { + userDisplay.SecondsUntilTimeout = HttpContext.User.GetRemainingAuthSeconds(); + } + + return userDisplay; + } + /// /// Logs a user in /// @@ -358,11 +429,6 @@ public class AuthenticationController : UmbracoApiControllerBase if (result.RequiresTwoFactor) { var twofactorView = _backOfficeTwoFactorOptions.GetTwoFactorView(loginModel.Username); - if (twofactorView.IsNullOrWhiteSpace()) - { - return new ValidationErrorResult( - $"The registered {typeof(IBackOfficeTwoFactorOptions)} of type {_backOfficeTwoFactorOptions.GetType()} did not return a view for two factor auth "); - } IUser? attemptedUser = _userService.GetByUsername(loginModel.Username); diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index 94ad31d2d0..134a89372f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -128,14 +128,47 @@ public class BackOfficeController : UmbracoController ["redir"] = _globalSettings.GetBackOfficePath(_hostingEnvironment), }); } - return Redirect("/"); + + return RedirectToLocal(null); } // force authentication to occur since this is not an authorized endpoint AuthenticateResult result = await this.AuthenticateBackOfficeAsync(); + // if we are not authenticated then we need to redirect to the login page + if (!result.Succeeded) + { + return RedirectToLogin(null); + } + + ViewResult defaultView = DefaultView(); + + return await RenderDefaultOrProcessExternalLoginAsync( + result, + () => defaultView, + () => defaultView); + } + + /// + /// Returns the default view for the BackOffice + /// + /// The default view currently /umbraco/UmbracoBackOffice/Default.cshtml + public ViewResult DefaultView() + { var viewPath = Path.Combine(Constants.SystemDirectories.Umbraco, Constants.Web.Mvc.BackOfficeArea, nameof(Default) + ".cshtml") .Replace("\\", "/"); // convert to forward slashes since it's a virtual path + return View(viewPath); + } + + [HttpGet] + [AllowAnonymous] + public async Task Login() + { + // force authentication to occur since this is not an authorized endpoint + AuthenticateResult result = await this.AuthenticateBackOfficeAsync(); + + var viewPath = Path.Combine(Constants.SystemDirectories.Umbraco, Constants.Web.Mvc.BackOfficeLoginArea, "Index.cshtml") + .Replace("\\", "/"); // convert to forward slashes since it's a virtual path return await RenderDefaultOrProcessExternalLoginAsync( result, @@ -160,7 +193,7 @@ public class BackOfficeController : UmbracoController if (invite == null) { _logger.LogWarning("VerifyUser endpoint reached with invalid token: NULL"); - return RedirectToAction(nameof(Default)); + return RedirectToLogin(new { flow = "invite-user", status = "invalidToken" }); } var parts = WebUtility.UrlDecode(invite).Split('|'); @@ -168,7 +201,7 @@ public class BackOfficeController : UmbracoController if (parts.Length != 2) { _logger.LogWarning("VerifyUser endpoint reached with invalid token: {Invite}", invite); - return RedirectToAction(nameof(Default)); + return RedirectToLogin(new { flow = "invite-user", status = "invalidToken" }); } var token = parts[1]; @@ -177,7 +210,7 @@ public class BackOfficeController : UmbracoController if (decoded.IsNullOrWhiteSpace()) { _logger.LogWarning("VerifyUser endpoint reached with invalid token: {Invite}", invite); - return RedirectToAction(nameof(Default)); + return RedirectToLogin(new { flow = "invite-user", status = "invalidToken" }); } var id = parts[0]; @@ -186,7 +219,7 @@ public class BackOfficeController : UmbracoController if (identityUser == null) { _logger.LogWarning("VerifyUser endpoint reached with non existing user: {UserId}", id); - return RedirectToAction(nameof(Default)); + return RedirectToLogin(new { flow = "invite-user", status = "nonExistingUser" }); } IdentityResult result = await _userManager.ConfirmEmailAsync(identityUser, decoded!); @@ -194,17 +227,18 @@ public class BackOfficeController : UmbracoController if (result.Succeeded == false) { _logger.LogWarning("Could not verify email, Error: {Errors}, Token: {Invite}", result.Errors.ToErrorMessage(), invite); - return new RedirectResult(Url.Action(nameof(Default)) + "#/login/false?invite=3"); + return RedirectToLogin(new { flow = "invite-user", status = "false", invite = "3" }); } - //sign the user in + // sign the user in DateTime? previousLastLoginDate = identityUser.LastLoginDateUtc; await _signInManager.SignInAsync(identityUser, false); - //reset the lastlogindate back to previous as the user hasn't actually logged in, to add a flag or similar to BackOfficeSignInManager would be a breaking change + + // reset the lastlogindate back to previous as the user hasn't actually logged in, to add a flag or similar to BackOfficeSignInManager would be a breaking change identityUser.LastLoginDateUtc = previousLastLoginDate; await _userManager.UpdateAsync(identityUser); - return new RedirectResult(Url.Action(nameof(Default)) + "#/login/false?invite=1"); + return RedirectToLogin(new { flow = "invite-user", invite = "1" }); } /// @@ -227,14 +261,8 @@ public class BackOfficeController : UmbracoController return new LocalRedirectResult(installerUrl); } - var viewPath = Path.Combine(Constants.SystemDirectories.Umbraco, Constants.Web.Mvc.BackOfficeArea, nameof(AuthorizeUpgrade) + ".cshtml"); - - return await RenderDefaultOrProcessExternalLoginAsync( - result, - //The default view to render when there is no external login info or errors - () => View(viewPath), - //The IActionResult to perform if external login is successful - () => Redirect("/")); + // Redirect to login if we're not authorized + return RedirectToLogin(new { returnPath = Url.Action(nameof(AuthorizeUpgrade), this.GetControllerName()) }); } /// @@ -367,23 +395,20 @@ public class BackOfficeController : UmbracoController public async Task ValidatePasswordResetCode([Bind(Prefix = "u")] int userId, [Bind(Prefix = "r")] string resetCode) { BackOfficeIdentityUser? user = await _userManager.FindByIdAsync(userId.ToString(CultureInfo.InvariantCulture)); - if (user != null) + if (user is null) { - var result = await _userManager.VerifyUserTokenAsync(user, "Default", "ResetPassword", resetCode); - if (result) - { - //Add a flag and redirect for it to be displayed - TempData[ViewDataExtensions.TokenPasswordResetCode] = - _jsonSerializer.Serialize( - new ValidatePasswordResetCodeModel { UserId = userId, ResetCode = resetCode }); - return RedirectToLocal(Url.Action(nameof(Default), this.GetControllerName())); - } + return RedirectToLogin(new { flow = "reset-password", status = "userNotFound" }); } - //Add error and redirect for it to be displayed - TempData[ViewDataExtensions.TokenPasswordResetCode] = - new[] { _textService.Localize("login", "resetCodeExpired") }; - return RedirectToLocal(Url.Action(nameof(Default), this.GetControllerName())); + var result = await _userManager.VerifyUserTokenAsync(user, "Default", "ResetPassword", resetCode); + + return result ? + + // Redirect to login with userId and resetCode + RedirectToLogin(new { flow = "reset-password", userId, resetCode }) : + + // Redirect to login with error code + RedirectToLogin(new { flow = "reset-password", status = "resetCodeExpired" }); } /// @@ -403,8 +428,7 @@ public class BackOfficeController : UmbracoController if (user == null) { // ... this should really not happen - TempData[ViewDataExtensions.TokenExternalSignInError] = new[] { "Local user does not exist" }; - return RedirectToLocal(Url.Action(nameof(Default), this.GetControllerName())); + return RedirectToLogin(new { flow = "external-login", status = "localUserNotFound" }); } ExternalLoginInfo? info = @@ -412,10 +436,8 @@ public class BackOfficeController : UmbracoController if (info == null) { - //Add error and redirect for it to be displayed - TempData[ViewDataExtensions.TokenExternalSignInError] = - new[] { "An error occurred, could not get external login info" }; - return RedirectToLocal(Url.Action(nameof(Default), this.GetControllerName())); + // Add error and redirect for it to be displayed + return RedirectToLogin(new { flow = "external-login", status = "externalLoginInfoNotFound" }); } IdentityResult addLoginResult = await _userManager.AddLoginAsync(user, info); @@ -427,9 +449,8 @@ public class BackOfficeController : UmbracoController return RedirectToLocal(Url.Action(nameof(Default), this.GetControllerName())); } - //Add errors and redirect for it to be displayed - TempData[ViewDataExtensions.TokenExternalSignInError] = addLoginResult.Errors; - return RedirectToLocal(Url.Action(nameof(Default), this.GetControllerName())); + // Add errors and redirect for it to be displayed + return RedirectToLogin(new { flow = "external-login", status = "failed" }); } /// @@ -442,19 +463,12 @@ public class BackOfficeController : UmbracoController Func defaultResponse, Func externalSignInResponse) { - if (defaultResponse is null) - { - throw new ArgumentNullException(nameof(defaultResponse)); - } - - if (externalSignInResponse is null) - { - throw new ArgumentNullException(nameof(externalSignInResponse)); - } + ArgumentNullException.ThrowIfNull(defaultResponse); + ArgumentNullException.ThrowIfNull(externalSignInResponse); ViewData.SetUmbracoPath(_globalSettings.GetUmbracoMvcArea(_hostingEnvironment)); - //check if there is the TempData or cookies with the any token name specified, if so, assign to view bag and render the view + // check if there is the TempData or cookies with the any token name specified, if so, assign to view bag and render the view if (ViewData.FromBase64CookieData( _httpContextAccessor.HttpContext, ViewDataExtensions.TokenExternalSignInError, @@ -464,7 +478,7 @@ public class BackOfficeController : UmbracoController return defaultResponse(); } - //First check if there's external login info, if there's not proceed as normal + // First check if there's external login info, if there's not proceed as normal ExternalLoginInfo? loginInfo = await _signInManager.GetExternalLoginInfoAsync(); if (loginInfo == null || loginInfo.Principal == null) @@ -482,7 +496,7 @@ public class BackOfficeController : UmbracoController return defaultResponse(); } - //we're just logging in with an external source, not linking accounts + // we're just logging in with an external source, not linking accounts return await ExternalSignInAsync(loginInfo, externalSignInResponse); } @@ -512,7 +526,7 @@ public class BackOfficeController : UmbracoController if (_runtimeState.Level == RuntimeLevel.Upgrade) { // redirect to the the installer - return Redirect("/"); + return RedirectToLocal(null); } } else if (result == SignInResult.TwoFactorRequired) @@ -600,7 +614,7 @@ public class BackOfficeController : UmbracoController return response(); } - private IActionResult RedirectToLocal(string? returnUrl) + private RedirectResult RedirectToLocal(string? returnUrl) { if (Url.IsLocalUrl(returnUrl)) { @@ -609,4 +623,16 @@ public class BackOfficeController : UmbracoController return Redirect("/"); } + + /// + /// Redirect the user to the login action with the specified path as string and parameter as object + /// + /// Object containing route values + /// Redirects the user session to the login page + private LocalRedirectResult RedirectToLogin(object? values) + { + var url = Url.Action(nameof(Login).ToLower(), this.GetControllerName(), values); + + return new LocalRedirectResult(url ?? "/"); + } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs index 1b74953a4f..edfed10d91 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs @@ -226,10 +226,12 @@ public class CurrentUserController : UmbracoAuthorizedJsonController /// /// /// + /// /// /// This only works when the user is logged in (partially) /// [AllowAnonymous] + [Obsolete("This is no longer used and will be removed in future versions. Use the AuthenticationController.PostSetInvitedUserPassword instead.")] public async Task> PostSetInvitedUserPassword([FromBody] string newPassword) { var userId = _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().ResultOr(0).ToString(); diff --git a/src/Umbraco.Web.BackOffice/Extensions/HtmlHelperBackOfficeExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/HtmlHelperBackOfficeExtensions.cs index 41ecde4760..12f325f0c9 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/HtmlHelperBackOfficeExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/HtmlHelperBackOfficeExtensions.cs @@ -44,6 +44,7 @@ public static class HtmlHelperBackOfficeExtensions /// /// /// + [Obsolete("This is deprecated and will be removed in V15")] public static async Task AngularValueExternalLoginInfoScriptAsync(this IHtmlHelper html, IBackOfficeExternalLoginProviders externalLogins, BackOfficeExternalLoginProviderErrors externalLoginErrors) @@ -56,7 +57,17 @@ public static class HtmlHelperBackOfficeExtensions { authType = p.ExternalLoginProvider.AuthenticationType, caption = p.AuthenticationScheme.DisplayName, - properties = p.ExternalLoginProvider.Options + options = new + { + allowManualLinking = p.ExternalLoginProvider.Options.AutoLinkOptions.AllowManualLinking, + buttonStyle = p.ExternalLoginProvider.Options.ButtonStyle, + buttonLook = p.ExternalLoginProvider.Options.ButtonLook.ToString().ToLowerInvariant(), + buttonColor = p.ExternalLoginProvider.Options.ButtonColor.ToString().ToLowerInvariant(), + customBackOfficeView = p.ExternalLoginProvider.Options.CustomBackOfficeView, + denyLocalLogin = p.ExternalLoginProvider.Options.DenyLocalLogin, + icon = p.ExternalLoginProvider.Options.Icon, + }, + properties = p.ExternalLoginProvider.Options, }) .ToArray(); @@ -95,6 +106,7 @@ public static class HtmlHelperBackOfficeExtensions /// /// /// + [Obsolete("This is deprecated and will be removed in V15")] public static IHtmlContent AngularValueResetPasswordCodeInfoScript(this IHtmlHelper html, object? val) { var sb = new StringBuilder(); diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs index 6fd58e7f95..8117007986 100644 --- a/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeExternalLoginProviderOptions.cs @@ -1,3 +1,5 @@ +using Umbraco.Cms.Core.Models; + namespace Umbraco.Cms.Web.BackOffice.Security; /// @@ -5,6 +7,8 @@ namespace Umbraco.Cms.Web.BackOffice.Security; /// public class BackOfficeExternalLoginProviderOptions { + private string _buttonStyle = "btn-openid"; + public BackOfficeExternalLoginProviderOptions( string buttonStyle, string icon, @@ -25,22 +29,79 @@ public class BackOfficeExternalLoginProviderOptions { } - public string ButtonStyle { get; set; } = "btn-openid"; + /// + /// Gets or sets the icon to use for the login button. + /// + [Obsolete("This is no longer used and will be removed in V15. Please set the ButtonLook and ButtonColor properties instead.")] + public string ButtonStyle + { + get => _buttonStyle; + set + { + _buttonStyle = value; - public string Icon { get; set; } = "fa fa-user"; + // Map cases from buttons.less + switch (value.ToLowerInvariant()) + { + case "btn-primary": + ButtonColor = UuiButtonColor.Default; + ButtonLook = UuiButtonLook.Primary; + break; + case "btn-warning": + ButtonColor = UuiButtonColor.Warning; + ButtonLook = UuiButtonLook.Primary; + break; + case "btn-danger": + ButtonColor = UuiButtonColor.Danger; + ButtonLook = UuiButtonLook.Primary; + break; + case "btn-success": + ButtonColor = UuiButtonColor.Positive; + ButtonLook = UuiButtonLook.Primary; + break; + default: + ButtonColor = UuiButtonColor.Default; + ButtonLook = UuiButtonLook.Outline; + break; + } + } + } /// - /// Options used to control how users can be auto-linked/created/updated based on the external login provider + /// Gets or sets the look to use for the login button. + /// See the UUI documentation for more details: https://uui.umbraco.com/?path=/story/uui-button--looks-and-colors. + /// + public UuiButtonLook ButtonLook { get; set; } = UuiButtonLook.Outline; + + /// + /// Gets or sets the color to use for the login button. + /// See the UUI documentation for more details: https://uui.umbraco.com/?path=/story/uui-button--looks-and-colors. + /// + public UuiButtonColor ButtonColor { get; set; } = UuiButtonColor.Default; + + /// + /// Gets or sets the icon to use for the login button. + /// The standard icons of the Backoffice is available. + /// + /// + /// It is possible to add custom icons to your provider by adding the icons to the + /// ~/App_Plugins/{providerAlias}/icons folder as SVG files. The icon name should be the same as the file name. + /// + public string Icon { get; set; } = "icon-user"; + + /// + /// Gets or sets options used to control how users can be auto-linked/created/updated based on the external login provider /// public ExternalSignInAutoLinkOptions AutoLinkOptions { get; set; } = new(); /// - /// When set to true will disable all local user login functionality + /// Gets or sets a value indicating whether when set to true will disable all local user login functionality. + /// This is useful if you want to force users to login with an external provider. /// public bool DenyLocalLogin { get; set; } /// - /// When specified this will automatically redirect to the OAuth login provider instead of prompting the user to click + /// Gets or sets a value indicating whether when specified this will automatically redirect to the OAuth login provider instead of prompting the user to click /// on the OAuth button first. /// /// @@ -51,13 +112,13 @@ public class BackOfficeExternalLoginProviderOptions public bool AutoRedirectLoginToExternalProvider { get; set; } /// - /// A virtual path to a custom angular view that is used to replace the entire UI that renders the external login - /// button that the user interacts with + /// Gets or sets a virtual path to a custom JavaScript module that will be rendered in place of the default OAuth login buttons. + /// The view can optionally replace the entire login screen if the option is set to true. /// /// /// If this view is specified it is 100% up to the user to render the html responsible for rendering the link/un-link - /// buttons along with showing any errors - /// that occur. This overrides what Umbraco normally does by default. + /// buttons along with showing any errors that occur. + /// This overrides what Umbraco normally does by default. /// public string? CustomBackOfficeView { get; set; } } diff --git a/src/Umbraco.Web.BackOffice/Security/DefaultBackOfficeTwoFactorOptions.cs b/src/Umbraco.Web.BackOffice/Security/DefaultBackOfficeTwoFactorOptions.cs index 27312642b4..7eeb51077b 100644 --- a/src/Umbraco.Web.BackOffice/Security/DefaultBackOfficeTwoFactorOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/DefaultBackOfficeTwoFactorOptions.cs @@ -1,6 +1,15 @@ namespace Umbraco.Cms.Web.BackOffice.Security; +/// public class DefaultBackOfficeTwoFactorOptions : IBackOfficeTwoFactorOptions { - public string GetTwoFactorView(string username) => "views\\common\\login-2fa.html"; + /// + /// Gets the view to display for the two factor challenge. + /// + /// + /// Defaults to null to let the login screen show its default. + /// + /// The username of the logged-in user. + /// A null value to let the login screen show its default. + public string? GetTwoFactorView(string username) => null; } diff --git a/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs b/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs index 2199a3c3c0..792fbf237a 100644 --- a/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/IBackOfficeTwoFactorOptions.cs @@ -1,14 +1,14 @@ namespace Umbraco.Cms.Web.BackOffice.Security; /// -/// Options used to control 2FA for the Umbraco back office +/// Options used to control 2FA for the Umbraco back office. /// public interface IBackOfficeTwoFactorOptions { /// - /// Returns the angular view for handling 2FA interaction + /// Returns the path to a JavaScript module to handle 2FA interaction. /// - /// - /// + /// The username of the logged-in user. + /// Returns the path to a JavaScript module string? GetTwoFactorView(string username); } diff --git a/src/Umbraco.Web.Common/Extensions/ViewDataExtensions.cs b/src/Umbraco.Web.Common/Extensions/ViewDataExtensions.cs index 0ede5a3911..7710152792 100644 --- a/src/Umbraco.Web.Common/Extensions/ViewDataExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ViewDataExtensions.cs @@ -21,7 +21,7 @@ public static class ViewDataExtensions public static bool FromTempData(this ViewDataDictionary viewData, ITempDataDictionary tempData, string token) { - if (tempData[token] == null) + if (tempData?[token] is null) { return false; } diff --git a/src/Umbraco.Web.Common/Models/InvitePasswordModel.cs b/src/Umbraco.Web.Common/Models/InvitePasswordModel.cs new file mode 100644 index 0000000000..da21cec2ed --- /dev/null +++ b/src/Umbraco.Web.Common/Models/InvitePasswordModel.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace Umbraco.Cms.Web.Common.Models; + +/// +/// A model used for setting a new password for a user that has been invited to Umbraco. +/// +public class InvitePasswordModel +{ + /// + /// Gets or sets the desired password for the user. + /// + [Required] + [DataType(DataType.Password)] + [Display(Name = "New password")] + [StringLength(256)] + public string NewPassword { get; set; } = null!; +} diff --git a/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js b/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js index 2f711245e3..65fd7199d1 100644 --- a/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js +++ b/src/Umbraco.Web.UI.Client/gulp/tasks/dependencies.js @@ -282,16 +282,6 @@ function dependencies() { ], "base": "./node_modules/wicg-inert" }, - { - "name": "umbraco-ui", - "src": [ - "./node_modules/@umbraco-ui/uui/dist/uui.min.js", - "./node_modules/@umbraco-ui/uui/dist/uui.min.js.map", - "./node_modules/@umbraco-ui/uui-css/dist/custom-properties.css", - "./node_modules/@umbraco-ui/uui-css/dist/uui-text.css" - ], - "base": "./node_modules/@umbraco-ui" - } ]; // add streams for node modules diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 171573e389..90b31f6e17 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -7,8 +7,6 @@ "name": "ui", "dependencies": { "@microsoft/signalr": "7.0.12", - "@umbraco-ui/uui": "1.4.0", - "@umbraco-ui/uui-css": "1.4.0", "ace-builds": "1.30.0", "angular": "1.8.3", "angular-animate": "1.8.3", @@ -2079,19 +2077,6 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, - "node_modules/@lit-labs/ssr-dom-shim": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.2.tgz", - "integrity": "sha512-jnOD+/+dSrfTWYfSXBXlo5l5f0q1UuJo3tkbMDCYA2lKUYq79jaxqtGEvnRoh049nt1vdo1+45RinipU6FGY2g==" - }, - "node_modules/@lit/reactive-element": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.3.tgz", - "integrity": "sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ==", - "dependencies": { - "@lit-labs/ssr-dom-shim": "^1.0.0" - } - }, "node_modules/@microsoft/signalr": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-7.0.12.tgz", @@ -2241,788 +2226,6 @@ "dev": true, "optional": true }, - "node_modules/@types/trusted-types": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.4.tgz", - "integrity": "sha512-IDaobHimLQhjwsQ/NMwRVfa/yL7L/wriQPMhw1ZJall0KX6E1oxk29XMDeilW5qTIg5aoiqf5Udy8U/51aNoQQ==" - }, - "node_modules/@umbraco-ui/uui": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui/-/uui-1.4.0.tgz", - "integrity": "sha512-VG+C37WIS5Uv7ERDs/jQHT9mIncD9UrEsEQlgFnf2XZWc/TcBlV1Tvvt3xSYzZz9kIjwoymEG6lc5t6wJMqSfw==", - "dependencies": { - "@umbraco-ui/uui-action-bar": "1.4.0", - "@umbraco-ui/uui-avatar": "1.4.0", - "@umbraco-ui/uui-avatar-group": "1.4.0", - "@umbraco-ui/uui-badge": "1.4.0", - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-boolean-input": "1.4.0", - "@umbraco-ui/uui-box": "1.4.0", - "@umbraco-ui/uui-breadcrumbs": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", - "@umbraco-ui/uui-button-group": "1.4.0", - "@umbraco-ui/uui-button-inline-create": "1.4.0", - "@umbraco-ui/uui-card": "1.4.0", - "@umbraco-ui/uui-card-content-node": "1.4.0", - "@umbraco-ui/uui-card-media": "1.4.0", - "@umbraco-ui/uui-card-user": "1.4.0", - "@umbraco-ui/uui-caret": "1.4.0", - "@umbraco-ui/uui-checkbox": "1.4.0", - "@umbraco-ui/uui-color-area": "1.4.0", - "@umbraco-ui/uui-color-picker": "1.4.0", - "@umbraco-ui/uui-color-slider": "1.4.0", - "@umbraco-ui/uui-color-swatch": "1.4.0", - "@umbraco-ui/uui-color-swatches": "1.4.0", - "@umbraco-ui/uui-combobox": "1.4.0", - "@umbraco-ui/uui-combobox-list": "1.4.0", - "@umbraco-ui/uui-css": "1.4.0", - "@umbraco-ui/uui-dialog": "1.4.0", - "@umbraco-ui/uui-dialog-layout": "1.4.0", - "@umbraco-ui/uui-file-dropzone": "1.4.0", - "@umbraco-ui/uui-file-preview": "1.4.0", - "@umbraco-ui/uui-form": "1.4.0", - "@umbraco-ui/uui-form-layout-item": "1.4.0", - "@umbraco-ui/uui-form-validation-message": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-icon-registry": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0", - "@umbraco-ui/uui-input": "1.4.0", - "@umbraco-ui/uui-input-file": "1.4.0", - "@umbraco-ui/uui-input-lock": "1.4.0", - "@umbraco-ui/uui-input-password": "1.4.0", - "@umbraco-ui/uui-keyboard-shortcut": "1.4.0", - "@umbraco-ui/uui-label": "1.4.0", - "@umbraco-ui/uui-loader": "1.4.0", - "@umbraco-ui/uui-loader-bar": "1.4.0", - "@umbraco-ui/uui-loader-circle": "1.4.0", - "@umbraco-ui/uui-menu-item": "1.4.0", - "@umbraco-ui/uui-modal": "1.4.0", - "@umbraco-ui/uui-pagination": "1.4.0", - "@umbraco-ui/uui-popover": "1.4.0", - "@umbraco-ui/uui-progress-bar": "1.4.0", - "@umbraco-ui/uui-radio": "1.4.0", - "@umbraco-ui/uui-range-slider": "1.4.0", - "@umbraco-ui/uui-ref": "1.4.0", - "@umbraco-ui/uui-ref-list": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0", - "@umbraco-ui/uui-ref-node-data-type": "1.4.0", - "@umbraco-ui/uui-ref-node-document-type": "1.4.0", - "@umbraco-ui/uui-ref-node-form": "1.4.0", - "@umbraco-ui/uui-ref-node-member": "1.4.0", - "@umbraco-ui/uui-ref-node-package": "1.4.0", - "@umbraco-ui/uui-ref-node-user": "1.4.0", - "@umbraco-ui/uui-scroll-container": "1.4.0", - "@umbraco-ui/uui-select": "1.4.0", - "@umbraco-ui/uui-slider": "1.4.0", - "@umbraco-ui/uui-symbol-expand": "1.4.0", - "@umbraco-ui/uui-symbol-file": "1.4.0", - "@umbraco-ui/uui-symbol-file-dropzone": "1.4.0", - "@umbraco-ui/uui-symbol-file-thumbnail": "1.4.0", - "@umbraco-ui/uui-symbol-folder": "1.4.0", - "@umbraco-ui/uui-symbol-lock": "1.4.0", - "@umbraco-ui/uui-symbol-more": "1.4.0", - "@umbraco-ui/uui-symbol-sort": "1.4.0", - "@umbraco-ui/uui-table": "1.4.0", - "@umbraco-ui/uui-tabs": "1.4.0", - "@umbraco-ui/uui-tag": "1.4.0", - "@umbraco-ui/uui-textarea": "1.4.0", - "@umbraco-ui/uui-toast-notification": "1.4.0", - "@umbraco-ui/uui-toast-notification-container": "1.4.0", - "@umbraco-ui/uui-toast-notification-layout": "1.4.0", - "@umbraco-ui/uui-toggle": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-action-bar": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-action-bar/-/uui-action-bar-1.4.0.tgz", - "integrity": "sha512-FMTSWXZOhWEziGL3OFvRGczAdRu2Ic82XLh4kCpCbRlKJHouqymOfo9FT3NbHEION37JUl9bv1nKiNA0m4s2bg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button-group": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-avatar": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar/-/uui-avatar-1.4.0.tgz", - "integrity": "sha512-sUvQKsaWXP+5xQO5p2YAqQyUITiyzIzK6cVRlGRUoEla3QlhCd7YHrRnrIJTNxwmfPygDtxGa9Zx8GNkW8N91w==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-avatar-group": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar-group/-/uui-avatar-group-1.4.0.tgz", - "integrity": "sha512-xpWMumABRNqVH3sdLBH43gBk8RSNjknTvqfuvfMgdrVUqAYE3cIjeadUDf9OfmzMWVoQn7PXyLSX7l/JRUhZJQ==", - "dependencies": { - "@umbraco-ui/uui-avatar": "1.4.0", - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-badge": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-badge/-/uui-badge-1.4.0.tgz", - "integrity": "sha512-6qUhcoGL43FWFS/Q6yozieaigQfKp2zqIrUGkdDpC3LqvUBshzuCFuDQEE+nobW/0oUkGV9MaMfa7hBI88eQTQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-base": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-base/-/uui-base-1.4.0.tgz", - "integrity": "sha512-RcNY2WfE2vTyAiDVyItBdo/o5owgMF16V+IFqa4xHeFlu1i08fp9/Qmyk+5Mb4LRJatt/V21zaOM0QlloyuNUg==", - "dependencies": { - "lit": "^2.3.1" - } - }, - "node_modules/@umbraco-ui/uui-boolean-input": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-boolean-input/-/uui-boolean-input-1.4.0.tgz", - "integrity": "sha512-yIhvUpT5KBE+nmROtYdrkyTg7k5OQd2f5YpSKK2RrAA1Ex7J7ZZpGIO4B7w6wNuZLLPA657YxRADwrPKU91nNw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-box": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-box/-/uui-box-1.4.0.tgz", - "integrity": "sha512-dQ8IeX86rAEmaz/ulJGDTGvmP0bMgm6LkRhGumignIRaVDLJdK5AIcPauVoq2n39IuczmoFjAEm6MFTAeQqZaQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-css": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-breadcrumbs": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-breadcrumbs/-/uui-breadcrumbs-1.4.0.tgz", - "integrity": "sha512-NfV8uVq093JceBC/Dog30iLi9z6ZwzwyS90At3qnCdIRn/ydxPghUA0xhS0Hf83GDQRgs9Ni7XbZv1P/SFdgrw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-button": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button/-/uui-button-1.4.0.tgz", - "integrity": "sha512-8a6lZ/PLWg8iDuOv4YDhKvczWv844C3OfhPngLlmaK6UdkaiPlkxEoK41zZaVUV70B0ZhKk/odQYBp5nEUeeDA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-button-group": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-group/-/uui-button-group-1.4.0.tgz", - "integrity": "sha512-Cwb1tFQbmo8XBpcTRwM5yolrselxBiDue0z+WyGWjKVuhNK/Cxlt1X2iT+MBlsgI1xW+I611+7d4n9V57wPXlQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-button-inline-create": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-inline-create/-/uui-button-inline-create-1.4.0.tgz", - "integrity": "sha512-pngszZKSk4uIaW0L06aBjBImKykxarNp7JTx6YJqi+rF+GXTS31/gRuckWN4pN0/BgUTJMd0Q11zVWfB0uwjvA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-card": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card/-/uui-card-1.4.0.tgz", - "integrity": "sha512-eS5QdKzNqQQ+en3ZpPq88YGSWD1mSr4Nk9okpZ06fQmEZlYMMliR0A3WKFBQHhnleZafaEgHq3VwpVL1SQrluw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-card-content-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-content-node/-/uui-card-content-node-1.4.0.tgz", - "integrity": "sha512-8xbaSytLMsA7pXMKI4gttgiXjRgoQFh/pc3HzaQf3hKaWfeCPUxUaponXfZXmXjqMAi+eoyyxS1qeUt+Zlt0Rw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-card": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-card-media": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-media/-/uui-card-media-1.4.0.tgz", - "integrity": "sha512-rQT4m0KFYMelEszFExFMYYNIBHHcYlDd0alqiKitEUBlpu2UXCHK7mXyQlU+sFWLJ262zSONMmwSaXsqhMLVug==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-card": "1.4.0", - "@umbraco-ui/uui-symbol-file": "1.4.0", - "@umbraco-ui/uui-symbol-folder": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-card-user": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-user/-/uui-card-user-1.4.0.tgz", - "integrity": "sha512-t7C7F1sFrxAizNZJG7JDu+Wk0vizm7lN8UZCNggPiua6AkVVDpH8YN013Tk/reKxfTp9PkYh9aVUeAyyhWYa4g==", - "dependencies": { - "@umbraco-ui/uui-avatar": "1.4.0", - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-card": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-caret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-caret/-/uui-caret-1.4.0.tgz", - "integrity": "sha512-RtWgCSvFelya+E0INy95XDiLNYDH3Tv7AdMvUTUKf/5PKYp/yR5MYo70P9EvUkCVMvIFVf/VVGd9mDwvLr2k+A==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-checkbox": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-checkbox/-/uui-checkbox-1.4.0.tgz", - "integrity": "sha512-VCcYycChEPmaOo5q2QF1xsxxYQ5XToGh/z+46GmFyc5TDFP2OyOWqVm6+4gVpljcvf4aS9IRqcoONa/Bv2LQqQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-boolean-input": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-color-area": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-area/-/uui-color-area-1.4.0.tgz", - "integrity": "sha512-csIswxLN9YDhmL6veZ9iR8SjQrDi8wscPPJB0i7w4TQDI8TwlvB0mAdb86FM0eoobXLPFeMDFkYGQijWpv69Gw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "colord": "^2.9.3" - } - }, - "node_modules/@umbraco-ui/uui-color-picker": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-picker/-/uui-color-picker-1.4.0.tgz", - "integrity": "sha512-zxOpmhEGEfQtLp/RYSPNBi8S2K+KjiuVyWhvmoqgO1gb/uNU5Om2xW1Q7pz/jiKe1qwWHO3whGl8LHM6el/C2w==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "colord": "^2.9.3" - } - }, - "node_modules/@umbraco-ui/uui-color-slider": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-slider/-/uui-color-slider-1.4.0.tgz", - "integrity": "sha512-XEgi6shSGCnB4LhQgalcWfsHXyC2oLGw0ZCANr9l/4LpjaoZ0Uq4H/CL8UFfwiLXbJWdzZwqQqJcP928QmUFYQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-color-swatch": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatch/-/uui-color-swatch-1.4.0.tgz", - "integrity": "sha512-/k1SgzfdA1sCueqDaGYXJyb+bZjMdffHgM4Qk5LMSjX3JDL+c6yKvoc/w2Bvky+9N1NUp+tEMbJKD7bzQalQlg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0", - "colord": "^2.9.3" - } - }, - "node_modules/@umbraco-ui/uui-color-swatches": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatches/-/uui-color-swatches-1.4.0.tgz", - "integrity": "sha512-U6+0fu9OULPqRW0TuwVpj1PLectXM7ha2dc1Cw+rEzOtqBEbDmJTs4bh7EosMmxksmZQdXFhVkxu1yBHhXUJtQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-color-swatch": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-combobox": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox/-/uui-combobox-1.4.0.tgz", - "integrity": "sha512-epBlmRtVlUKeToA+DbYJYEWzTvKQahm2RnUMzFk9BvISP1xE9X5q7MtZLPRoiTjA9wf4SYrxIgHlYBGUOmy9lQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", - "@umbraco-ui/uui-combobox-list": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-scroll-container": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-combobox-list": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox-list/-/uui-combobox-list-1.4.0.tgz", - "integrity": "sha512-T6fOqHcOSB/NxfUmjZHlNWUU1ct9eVghXdQpA4tcPE83HSfHhWS5F1nbE9Cr/LO/al2Fe8iFfub9ed9OOsNqdA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-css": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-css/-/uui-css-1.4.0.tgz", - "integrity": "sha512-HBCFPuXJijeZbjnjdqmg3oqOGB3RmpQKT/s/Uy0TSJfaQGfz0e73o2eRghYHWF2rdqHw6brKFrZTZHBVvCE/xA==", - "dependencies": { - "lit": "^2.2.2" - } - }, - "node_modules/@umbraco-ui/uui-dialog": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog/-/uui-dialog-1.4.0.tgz", - "integrity": "sha512-FCrz17nKh2zybsDeN0AIxBQJjSFhK1q8OdZGSzaegPKx6R/xmZBPx6KPZeQnmjdGzQJHwh4xILKHXGazZbIZXA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-css": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-dialog-layout": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog-layout/-/uui-dialog-layout-1.4.0.tgz", - "integrity": "sha512-67/yVhysc+wMsyVEQXSP2E21YlzoQfir/CQjxCRlfKGe8FdCck/m3HSnzyb1rvPfbXrxGUMCUmcTqDBoazBfAw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-file-dropzone": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-dropzone/-/uui-file-dropzone-1.4.0.tgz", - "integrity": "sha512-pbNcTS7x7fvSyCrvR+yA7HzjWLtJXLHcLZvkJ4yNoAxS1d4/5ppyi/Fyz0QakBgLWzPuBv1mKj2o6RvBy29QWA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-symbol-file-dropzone": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-file-preview": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-preview/-/uui-file-preview-1.4.0.tgz", - "integrity": "sha512-UYi4Omww0/COjheTuAUdvZHqEAITT65Vsi5NSDHaUH3AM9BSVlj0FR3wOpwF7OwbOXjIeIonMEC8xMf1JtjusQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-symbol-file": "1.4.0", - "@umbraco-ui/uui-symbol-file-thumbnail": "1.4.0", - "@umbraco-ui/uui-symbol-folder": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-form": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form/-/uui-form-1.4.0.tgz", - "integrity": "sha512-jjukKI+eoKmvw9Jc8n0ryle6gAA1ogQM3GLgId509qS9qiFGxMetMJ0KQjcRkrisRM/oQjz7huf9tF1es/prOQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-form-layout-item": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-layout-item/-/uui-form-layout-item-1.4.0.tgz", - "integrity": "sha512-aHBfwq7Y0YAWVHpiXZ1lnwSXyLbsGdk7lPkJ6hqVaBJ77VA/N2oDGMUjsRcCd1vKtD8AA3Nc2kT2e++NlUIPDg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-form-validation-message": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-form-validation-message": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-validation-message/-/uui-form-validation-message-1.4.0.tgz", - "integrity": "sha512-AZXcvusVb48H5YrPIj71iMMUOXn2pZtensi3fUj55sVY1RNFa+QuJW/vC/79qDBLw/vQJu3NcZGbi4q4NBKh9A==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-icon": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon/-/uui-icon-1.4.0.tgz", - "integrity": "sha512-aLzVYbubk+VSI4iKHJSKFxlHMe9CGq5JbaUfuy9a9U/D7VfUUrroM+tDMPFP4qEvSkjthyCzdPBxodJ+QQOZew==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-icon-registry": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry/-/uui-icon-registry-1.4.0.tgz", - "integrity": "sha512-76XXyxq96XIp4qIT58UgY4vp4+agD2YvfpCd+Dhs/rdu5iQq56PmYoxJ7qr7JYTSf8xxZ//0/PiuamwWkPmSEw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-icon-registry-essential": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry-essential/-/uui-icon-registry-essential-1.4.0.tgz", - "integrity": "sha512-o1woHz7YFjyOBIQHsdoCxE3vpXrJ/Sj0QNcGexdlFqUsvv/LhHAJ9a88cmTve1Y8nYDWW2pyyKZbyX1nDokByg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon-registry": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-input": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input/-/uui-input-1.4.0.tgz", - "integrity": "sha512-mtlONZWmLV5OOYt2APhjl9cukTktrWNl1w4yF889F/wO2ZiGasBWwL9amtW4RIby/5nxns9yGgzXXG1/6GaqYw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-input-file": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-file/-/uui-input-file-1.4.0.tgz", - "integrity": "sha512-qdRce6NA6VDgFR71hUhuasX28N4qmCtWscWwoU+2E/rxfYWd2MIFOSsBqnIW6R4wagw+LnC7YXV6oy4vZiCKuQ==", - "dependencies": { - "@umbraco-ui/uui-action-bar": "1.4.0", - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", - "@umbraco-ui/uui-file-dropzone": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-input-lock": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-lock/-/uui-input-lock-1.4.0.tgz", - "integrity": "sha512-LK9jgCmSJFENRA+Hj7qnwhuhuYmMgWYPc44LMYdowqTKlkffr67mY2VqaK+92WbjmH8PKStJr0wf0L8tuEczWQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-input": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-input-password": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-password/-/uui-input-password-1.4.0.tgz", - "integrity": "sha512-slxRycyh8okgl6vH89O/y9lWPkfrga6s3Myijz4RXnprWfVtntIkB5pZoM17yT9bjSfo15UKd4E4GdOS9YpcaQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0", - "@umbraco-ui/uui-input": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-keyboard-shortcut": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-keyboard-shortcut/-/uui-keyboard-shortcut-1.4.0.tgz", - "integrity": "sha512-3hTFxrilMW7hGwfFtsNUmJdF0e4wk5pM8oGMuwwkKxsuxMdGzdpmht0PnB3G0EPQAsA60Xypiuvm0EgFnX91zg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-label": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-label/-/uui-label-1.4.0.tgz", - "integrity": "sha512-XTKH92Z0Apu15qI3MvJew1z3oAyOVBgByIipxVmWPb52Nlvj/Haa8QUlfksJWp4E4c2IhhYTPVXeft8CpS2q1w==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-loader": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader/-/uui-loader-1.4.0.tgz", - "integrity": "sha512-5KgUdzusuJeMwgIwtScuqgMnJ9NW+/G0/Osj3B20UBPwcwVm1z4s3cWlt3kKJmPA/W4fzbdTxRt2MRdSEp3+cw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-loader-bar": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-bar/-/uui-loader-bar-1.4.0.tgz", - "integrity": "sha512-n+sxqJxp1aKy7lF8rbB9a72OzcdhTuHif5bR2XD2NwMmEZ7jl6xd+Em+sHo35ePqqmualpwetM2DlO50/uTDgg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-loader-circle": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-circle/-/uui-loader-circle-1.4.0.tgz", - "integrity": "sha512-mFQB6psm9W4U/g9KEPPoUNFeEju2k/oJ+J5I1g0fz20HpfvDKIoebqErcCd0wngZfk4FZm1ditpN3t2eFGBR4A==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-menu-item": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-menu-item/-/uui-menu-item-1.4.0.tgz", - "integrity": "sha512-GqrjrUlzQzbctDzzg1X0fVRO4Yxll/H5oqnXZBuDZBpqu++AlXknqMuAjur0cFkeiV0Kn7N9w+uZl1NYWW9OJA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-loader-bar": "1.4.0", - "@umbraco-ui/uui-symbol-expand": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-modal": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-modal/-/uui-modal-1.4.0.tgz", - "integrity": "sha512-v+jiYIGCLTL4NY+Td5UIgoK52pxGVWxWEe+xxNJLYSUtiRsp+7dw9UwmNqLdflR3ngfyBVY+rfEXSfbcfjiQdA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-pagination": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-pagination/-/uui-pagination-1.4.0.tgz", - "integrity": "sha512-f38AuUTyZ5/JNWZFU02EzAaQ81R3sa38jClSjyDScQ9Vh+8Uwj16sRPnbnveFWU/c5URVMFpG5OGXA/RXI3WEg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", - "@umbraco-ui/uui-button-group": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-popover": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-popover/-/uui-popover-1.4.0.tgz", - "integrity": "sha512-jbYHUGoN1S81VU4TbUh0HKipGcCnqiwINtQNDGf2W61Rgy++wBR3MfWqCaXd1K10GL8+wgkly6RsJKKUzqrDNw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-progress-bar": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-progress-bar/-/uui-progress-bar-1.4.0.tgz", - "integrity": "sha512-lJdoxiJMaDl7Qsaa6TkeuiudWV7Zer1LjWS9yO0aAZ4xWkkVxLf89qIlaTukdOat+Sr8ZtI2mjmRih5IjMdalg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-radio": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-radio/-/uui-radio-1.4.0.tgz", - "integrity": "sha512-pIJjmzWRIKPDxzwmB4CbBJNmMhlB97NOcgMoiIruiacVGEfZTWqXYXAkNtMragYGVQ0oz+ySYxEgl4iVvg2tdw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-range-slider": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-range-slider/-/uui-range-slider-1.4.0.tgz", - "integrity": "sha512-3rGrXEAOfztQHvD8aJlGuBfe0tXkpZgWtzq888D+8X68RMvPHs89X32FVqT8e34kK1/vfm8I7BwbDSXL6FTzbg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-ref": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref/-/uui-ref-1.4.0.tgz", - "integrity": "sha512-zESd9N+72zON+kLCv95zzQtfmFY10zJU9DzzLR0GdZouujtyysU5qIwJG+dTy5ewm1jzGq5DHAyJtwO6IQSx7Q==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-list": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-list/-/uui-ref-list-1.4.0.tgz", - "integrity": "sha512-r8X0dSUsbvbyvK+2Yy7jsaCE4Q+PV7CDGQAO1eArYywCuJWjdVO18zt26Abvl1Z+v5qAWnbPiJHvF0h6mYTGMg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node/-/uui-ref-node-1.4.0.tgz", - "integrity": "sha512-Jc8ews6mC9au4gUvzjRYfTeQWgFkrSICcsxd1oPz1qxVsyXWk66b3tWjAwkyjWwI13EOp4YoGK9QsPXbQKeTvg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-ref": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-data-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-data-type/-/uui-ref-node-data-type-1.4.0.tgz", - "integrity": "sha512-tcuRnbYJxV8X3/ezP1gQ/DY2Vy9f+TDB/HFKtsNp+n891zShRbcEQ1As/fOoXGtM2JVAJ7VUYboyMhJ195hBVw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-document-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-document-type/-/uui-ref-node-document-type-1.4.0.tgz", - "integrity": "sha512-pWESJsEm+Dect5kUws+sse0Xj8Z9+ZZkR1ZaeTHDL3kPMLxD6wMfMwWJtMeAIh7OvqJY0B/ldLonTof/ysebdA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-form": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-form/-/uui-ref-node-form-1.4.0.tgz", - "integrity": "sha512-Xd17jQycvjq5TGfxkTZr+Kb/OU/lsUPkh4ft8/V4W/p0xv4sTio6txPw0bjDDcjJ/75zuHOLyTYicmcchcjXbA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-member": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-member/-/uui-ref-node-member-1.4.0.tgz", - "integrity": "sha512-Lhpsh1CAwQRKOaR4tPkXBBZN3fjuEJMENlVHDB2UmmSJvFozl2byEWX4dEHwvPQpe0cbU8lE0By8iNDaEbl7Qw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-package": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-package/-/uui-ref-node-package-1.4.0.tgz", - "integrity": "sha512-FQgAZ8NOjBVUWLyDg93pg6bqgONcM275qbqM3Htd+JMmmYcoYii/oTXlBqhGq7+9eDhcb8tGko2RN/tH9p8KSQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-ref-node-user": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-user/-/uui-ref-node-user-1.4.0.tgz", - "integrity": "sha512-dmp44LDXJbnupP8dnUpAMSPCU2+udhMSE9uQDx1hfmX08Q49Phw6R4Az9h1ESh5uSxSm6UEb/Y7JEblods7C3w==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-scroll-container": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-scroll-container/-/uui-scroll-container-1.4.0.tgz", - "integrity": "sha512-/Rfqjtw+9LCCjvxl/MEmAjVfn4+aE8elfZ77EoItbF79R8WVmoJsIJUezjFp/Hvtp51PsgVgu/Da94dxTR4QBA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-select": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-select/-/uui-select-1.4.0.tgz", - "integrity": "sha512-bvdVIGot2vWiuoQmQL9dCriY8KnmpqLyn0q6FCvx7xGAl9nFBn1MfZFbs4INxriIGWjq17YFvUXklTWuhMLGTA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-slider": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-slider/-/uui-slider-1.4.0.tgz", - "integrity": "sha512-eDTIcXhYAiMSpPwI5e5gnMMpr0zOpx8te8pxF6K2YrGo8mCO2CI1zXZTzuv7e4ImL4HLmLoph8kbk+/wlrEtLw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-expand": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-expand/-/uui-symbol-expand-1.4.0.tgz", - "integrity": "sha512-vSWRYiUwTjERuWtbiAW7IB49s57bqjN2XrSmCrOtyS9i4t5jIjsZ11If97WD+gQI/tt+khQZ85oPWNcj6C3eVQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-file": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file/-/uui-symbol-file-1.4.0.tgz", - "integrity": "sha512-wGmdw47jXjIcjpThf/TZ+6EZh+aQwqBA/1SMlgTtNBbUZDSy77NZ0pOWw8SaXzKqRrDqgFqIZukb7MILio3fwQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-file-dropzone": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-dropzone/-/uui-symbol-file-dropzone-1.4.0.tgz", - "integrity": "sha512-GftR6cK+9kbY43fV9a3+ICJX0rn8iT6SEe9vt85Uu4JMi2GCOT3TnKnIxgXRP3u9SyHhMNMiWmSgRfLpgJ2v/w==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-file-thumbnail": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-thumbnail/-/uui-symbol-file-thumbnail-1.4.0.tgz", - "integrity": "sha512-Xlu7NQ88AiQI9kfKOQKi1kH0zMkop7GqtGyuIXbnt7rM3EZfioTdltW1NvqgKzc2QpZPqMY1s449hravObHUUg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-folder": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-folder/-/uui-symbol-folder-1.4.0.tgz", - "integrity": "sha512-/cpV6Br3bOZkOh6YNr5PbIA/+NKKjyj1PkJwITSGm5/TnW2a4J5nzJTVn5ez7IjId176loRDZM2w05bemRavmA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-lock": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-lock/-/uui-symbol-lock-1.4.0.tgz", - "integrity": "sha512-BUPxOwhjjl4GVixbbGkKOPi9FI+C1fr1cy5NT2uLNY64z5r3jFzbnHMySKGzvpfig8wD+1hsuSPGP3lypzknOQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-more": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-more/-/uui-symbol-more-1.4.0.tgz", - "integrity": "sha512-fx125CCeBY+sspQpWITYt79AKYZ11NFaa72Zquz8cxH+hQA1z32jOUDL+m6oF3jTYwQkKQlCoff3VnOaJ91VyA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-symbol-sort": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-sort/-/uui-symbol-sort-1.4.0.tgz", - "integrity": "sha512-pVeT7qrKhRK8NUX3IDodSK0GNAKOKyWyzRhrxKrDT7wRuMManKmAK6WAVYpLaRqO+PRF8+NljfoCOEtJAHlGUg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-table": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-table/-/uui-table-1.4.0.tgz", - "integrity": "sha512-wpEqTmUQrAWjloeHZQqzAt5HR+j5ihMJusHpqZmY4076LcvnmpZHPhtmwpIzosZNqRq2N1rbrPIyEotlzSg9Fw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-tabs": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tabs/-/uui-tabs-1.4.0.tgz", - "integrity": "sha512-RWoLJHwMb9MbKqMyuyz3DaSc9ZGCa/NBtgBDpKpn/8oolbmNYBnr9e4sabHARtqfsEWFWKWP3kUw9iTQZNa0oA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-tag": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tag/-/uui-tag-1.4.0.tgz", - "integrity": "sha512-9R+WJrJav780ZoA+dbZb7bHYazxrHxADnLdNOHoLvNyggLyxIT/SRsSxrP3x9zFRwbcRLZ8MRxQ3I32YiWacKw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-textarea": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-textarea/-/uui-textarea-1.4.0.tgz", - "integrity": "sha512-nd6kWBmAvWaNLmXbEhfLRnWMfAp8rkll7XtHec9W32EQJwcHlYrS3wga6Xu32d3rKb3zUg+VXHh3EKKQH8M4uQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-toast-notification": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification/-/uui-toast-notification-1.4.0.tgz", - "integrity": "sha512-ioiTTxqaOV/2ggnK9/IrnJPf1KRaKEIXd6qrXkMaYH1orCmv3BIdQMnl3TxFOM1YMlnbVZrfxBe2++iqV6TxHA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", - "@umbraco-ui/uui-css": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-toast-notification-container": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-container/-/uui-toast-notification-container-1.4.0.tgz", - "integrity": "sha512-VIftKhOoQ0EdtM9pvDUM2IcvR8S9Fveh/QwMHgGLVlsgUogBNkCPGJKLfh9hzE5RS2v9FdPIkk72qP2A4fpspQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-toast-notification": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-toast-notification-layout": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-layout/-/uui-toast-notification-layout-1.4.0.tgz", - "integrity": "sha512-Secrk5+GlZYzOrg1MQ28+rLGW5krXYxYSAhSe5uDKOqTFLjuag7/qiraQDG3xBtf9ZfAAJ3qUy9n50adshoDbA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-css": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-toggle": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toggle/-/uui-toggle-1.4.0.tgz", - "integrity": "sha512-APIOxs96fcn6HvD/SksN7rhEk6IAta7XU6s0T2Fa+RPIeOBS0NbbvFUX6hW3qjpiD5DdsjOpO2jn/R1fH3nqnQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-boolean-input": "1.4.0" - } - }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -5010,7 +4213,8 @@ "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true }, "node_modules/colornames": { "version": "1.1.1", @@ -11684,34 +10888,6 @@ "node": ">=10" } }, - "node_modules/lit": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/lit/-/lit-2.8.0.tgz", - "integrity": "sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==", - "dependencies": { - "@lit/reactive-element": "^1.6.0", - "lit-element": "^3.3.0", - "lit-html": "^2.8.0" - } - }, - "node_modules/lit-element": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.3.3.tgz", - "integrity": "sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA==", - "dependencies": { - "@lit-labs/ssr-dom-shim": "^1.1.0", - "@lit/reactive-element": "^1.3.0", - "lit-html": "^2.8.0" - } - }, - "node_modules/lit-html": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.8.0.tgz", - "integrity": "sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==", - "dependencies": { - "@types/trusted-types": "^2.0.2" - } - }, "node_modules/load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index bf2a4c9b0c..b728f388d6 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -19,8 +19,6 @@ }, "dependencies": { "@microsoft/signalr": "7.0.12", - "@umbraco-ui/uui": "1.4.0", - "@umbraco-ui/uui-css": "1.4.0", "ace-builds": "1.30.0", "angular": "1.8.3", "angular-animate": "1.8.3", diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-circle-fill.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-circle-fill.svg new file mode 100644 index 0000000000..ce854391c9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-circle-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-circle-line.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-circle-line.svg new file mode 100644 index 0000000000..6e2287fecc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-circle-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-fill.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-fill.svg new file mode 100644 index 0000000000..88a85ad087 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-line.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-line.svg new file mode 100644 index 0000000000..8ae0d14163 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-facebook-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-github-fill.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-github-fill.svg new file mode 100644 index 0000000000..9ad1d73e18 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-github-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-github-line.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-github-line.svg new file mode 100644 index 0000000000..61809ed342 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-github-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-google-fill.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-google-fill.svg new file mode 100644 index 0000000000..aafa688500 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-google-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-google-line.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-google-line.svg new file mode 100644 index 0000000000..94ed1a3561 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-google-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-box-fill.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-box-fill.svg new file mode 100644 index 0000000000..5268ef9761 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-box-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-box-line.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-box-line.svg new file mode 100644 index 0000000000..68ec684f94 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-box-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-fill.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-fill.svg new file mode 100644 index 0000000000..8914b81b71 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-line.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-line.svg new file mode 100644 index 0000000000..3e6f2976f8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-linkedin-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-mastodon-fill.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-mastodon-fill.svg new file mode 100644 index 0000000000..ee9488b7a3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-mastodon-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-mastodon-line.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-mastodon-line.svg new file mode 100644 index 0000000000..f955e62781 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-mastodon-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-microsoft-fill.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-microsoft-fill.svg new file mode 100644 index 0000000000..1d58b3a594 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-microsoft-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-microsoft-line.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-microsoft-line.svg new file mode 100644 index 0000000000..a8751d63ba --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-microsoft-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-fill.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-fill.svg new file mode 100644 index 0000000000..4cd7ab30cd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-line.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-line.svg new file mode 100644 index 0000000000..f6d7efc900 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-x-fill.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-x-fill.svg new file mode 100644 index 0000000000..f23e8f6d5f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-x-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-x-line.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-x-line.svg new file mode 100644 index 0000000000..4555a842d0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-twitter-x-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js index 1d195e8a1c..e2c6d87e19 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umblogin.directive.js @@ -13,29 +13,16 @@ } }); - function UmbLoginController($scope, $location, currentUserResource, formHelper, - mediaHelper, umbRequestHelper, localizationService, - userService, externalLoginInfo, externalLoginInfoService, - resetPasswordCodeInfo, authResource, $q) { + function UmbLoginController($location, externalLoginInfoService, userService) { const vm = this; + vm.onLoginSuccess = loginSuccess; - vm.invitedUser = null; - - vm.invitedUserPasswordModel = { - password: "", - confirmPassword: "", - buttonState: "", - passwordPolicies: null, - passwordPolicyText: "" - }; - - vm.loginStates = { - submitButton: "init" - }; - + vm.backgroundImage = Umbraco.Sys.ServerVariables.umbracoSettings.loginBackgroundImage; + vm.logoImage = Umbraco.Sys.ServerVariables.umbracoSettings.loginLogoImage; vm.allowPasswordReset = Umbraco.Sys.ServerVariables.umbracoSettings.canSendRequiredEmail && Umbraco.Sys.ServerVariables.umbracoSettings.allowPasswordReset; - vm.errorMsg = ""; + vm.usernameIsEmail = Umbraco.Sys.ServerVariables.umbracoSettings.usernameIsEmail; + const tempUrl = new URL(Umbraco.Sys.ServerVariables.umbracoUrls.externalLoginsUrl, window.location.origin); tempUrl.searchParams.append("redirectUrl", decodeURIComponent($location.search().returnPath ?? "")) @@ -43,383 +30,27 @@ vm.externalLoginProviders = externalLoginInfoService.getLoginProviders(); vm.externalLoginProviders.forEach(x => { x.customView = externalLoginInfoService.getLoginProviderView(x); - // if there are errors set for this specific provider than assign them directly to the model - if (externalLoginInfo.errorProvider === x.authType) { - x.errors = externalLoginInfo.errors; - } }); - vm.denyLocalLogin = externalLoginInfoService.hasDenyLocalLogin(); - vm.externalLoginInfo = externalLoginInfo; - vm.resetPasswordCodeInfo = resetPasswordCodeInfo; - vm.logoImage = Umbraco.Sys.ServerVariables.umbracoSettings.loginLogoImage; - vm.backgroundImage = Umbraco.Sys.ServerVariables.umbracoSettings.loginBackgroundImage; - vm.usernameIsEmail = Umbraco.Sys.ServerVariables.umbracoSettings.usernameIsEmail; + vm.disableLocalLogin = externalLoginInfoService.hasDenyLocalLogin(); - vm.$onInit = onInit; - vm.togglePassword = togglePassword; - vm.getStarted = getStarted; - vm.inviteSavePassword = inviteSavePassword; - vm.showLogin = showLogin; - vm.showRequestPasswordReset = showRequestPasswordReset; - vm.showSetPassword = showSetPassword; - vm.loginSubmit = loginSubmit; - vm.requestPasswordResetSubmit = requestPasswordResetSubmit; - vm.setPasswordSubmit = setPasswordSubmit; - vm.newPasswordKeyUp = newPasswordKeyUp; - vm.labels = {}; - localizationService.localizeMany([ - vm.usernameIsEmail ? "general_email" : "general_username", - vm.usernameIsEmail ? "placeholders_email" : "placeholders_usernameHint", - vm.usernameIsEmail ? "placeholders_emptyEmail" : "placeholders_emptyUsername", - "placeholders_emptyPassword"] - ).then(function (data) { - vm.labels.usernameLabel = data[0]; - vm.labels.usernamePlaceholder = data[1]; - vm.labels.usernameError = data[2]; - vm.labels.passwordError = data[3]; - }); + /** + * This is called when the user has successfully logged in + * by the login screen sending out the event "umb-login-success" + * @access private + */ + function loginSuccess(evt) { + const user = evt?.originalEvent?.detail; - vm.twoFactor = {}; - - vm.loginSuccess = loginSuccess; - - function onInit() { - - // Check if it is a new user - const inviteVal = $location.search().invite; - - //1 = enter password, 2 = password set, 3 = invalid token - if (inviteVal && (inviteVal === "1" || inviteVal === "2")) { - - $q.all([ - //get the current invite user - authResource.getCurrentInvitedUser().then(function (data) { - vm.invitedUser = data; - }, - function () { - //it failed so we should remove the search - $location.search('invite', null); - }), - //get the membership provider config for password policies - authResource.getPasswordConfig(0).then(function (data) { - vm.invitedUserPasswordModel.passwordPolicies = data; - - //localize the text - localizationService.localize("errorHandling_errorInPasswordFormat", [ - vm.invitedUserPasswordModel.passwordPolicies.minPasswordLength, - vm.invitedUserPasswordModel.passwordPolicies.minNonAlphaNumericChars - ]).then(function (data) { - vm.invitedUserPasswordModel.passwordPolicyText = data; - }); - }) - ]).then(function () { - vm.inviteStep = Number(inviteVal); - }); - - } else if (inviteVal && inviteVal === "3") { - vm.inviteStep = Number(inviteVal); + if (user) { + userService.setAuthenticationSuccessful(user); + } else { + console.error("No user was returned from the login event"); } - // set the welcome greeting - setGreeting(); - - // show the correct panel - if (vm.resetPasswordCodeInfo.resetCodeModel) { - vm.showSetPassword(); - } - else if (vm.resetPasswordCodeInfo.errors.length > 0) { - vm.view = "password-reset-code-expired"; - } - else { - vm.showLogin(); - } - - SetTitle(); - } - - 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(); - } - - function getStarted() { - $location.search('invite', null); if (vm.onLogin) { vm.onLogin(); } } - - function inviteSavePassword() { - - if (formHelper.submitForm({ scope: $scope, formCtrl: vm.inviteUserPasswordForm })) { - - vm.invitedUserPasswordModel.buttonState = "busy"; - - currentUserResource.performSetInvitedUserPassword(vm.invitedUserPasswordModel.password) - .then(function (data) { - - //success - formHelper.resetForm({ scope: $scope, formCtrl: vm.inviteUserPasswordForm }); - vm.invitedUserPasswordModel.buttonState = "success"; - - //set the user - vm.invitedUser = data; - - // hide the password form - vm.inviteStep = 2; - - // set the user as logged in, which will initialise the app flow in init.js - // and eventually redirect the user to the content section when it's ready - userService.setAuthenticationSuccessful(data); - - }, function (err) { - formHelper.resetForm({ scope: $scope, hasErrors: true, formCtrl: vm.inviteUserPasswordForm }); - formHelper.handleError(err); - vm.invitedUserPasswordModel.buttonState = "error"; - }); - } - } - - function showLogin() { - vm.errorMsg = ""; - resetInputValidation(); - vm.view = "login"; - SetTitle(); - } - - function showRequestPasswordReset() { - vm.errorMsg = ""; - resetInputValidation(); - vm.view = "request-password-reset"; - vm.showEmailResetConfirmation = false; - SetTitle(); - } - - function showSetPassword() { - vm.errorMsg = ""; - resetInputValidation(); - vm.view = "set-password"; - SetTitle(); - } - - function loginSuccess() { - vm.loginStates.submitButton = "success"; - userService._retryRequestQueue(true); - if (vm.onLogin) { - vm.onLogin(); - } - } - - function loginSubmit() { - - if (formHelper.submitForm({ scope: $scope, formCtrl: vm.loginForm })) { - //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) { - vm.loginForm.username.$setValidity('auth', true); - vm.loginForm.password.$setValidity('auth', true); - } - - if (vm.loginForm.$invalid) { - SetTitle(); - return; - } - - // make sure that we are returning to the login view. - vm.view = "login"; - - vm.loginStates.submitButton = "busy"; - - userService.authenticate(vm.login, vm.password) - .then(function (data) { - loginSuccess(); - }, - function (reason) { - - //is Two Factor required? - if (reason.status === 402) { - vm.errorMsg = "Additional authentication required"; - show2FALoginDialog(reason.data.twoFactorView); - } else { - vm.loginStates.submitButton = "error"; - vm.errorMsg = reason.errorMsg; - - //set the form inputs to invalid - vm.loginForm.username.$setValidity("auth", false); - vm.loginForm.password.$setValidity("auth", false); - } - - userService._retryRequestQueue(); - - }); - - //setup a watch for both of the model values changing, if they change - // while the form is invalid, then revalidate them so that the form can - // be submitted again. - vm.loginForm.username.$viewChangeListeners.push(function () { - if (vm.loginForm.$invalid) { - vm.loginForm.username.$setValidity('auth', true); - vm.loginForm.password.$setValidity('auth', true); - } - }); - vm.loginForm.password.$viewChangeListeners.push(function () { - if (vm.loginForm.$invalid) { - vm.loginForm.username.$setValidity('auth', true); - vm.loginForm.password.$setValidity('auth', true); - } - }); - } - } - - function requestPasswordResetSubmit(email) { - - // TODO: Do validation properly like in the invite password update - - if (email && email.length > 0) { - vm.requestPasswordResetForm.email.$setValidity('auth', true); - } - - vm.showEmailResetConfirmation = false; - - if (vm.requestPasswordResetForm.$invalid) { - vm.errorMsg = 'Email address cannot be empty'; - return; - } - - vm.errorMsg = ""; - - authResource.performRequestPasswordReset(email) - .then(function () { - //remove the email entered - vm.email = ""; - vm.showEmailResetConfirmation = true; - }, function (reason) { - vm.errorMsg = reason.errorMsg; - vm.requestPasswordResetForm.email.$setValidity("auth", false); - }); - - vm.requestPasswordResetForm.email.$viewChangeListeners.push(function () { - if (vm.requestPasswordResetForm.email.$invalid) { - vm.requestPasswordResetForm.email.$setValidity('auth', true); - } - }); - } - - function setPasswordSubmit(password, confirmPassword) { - - vm.showSetPasswordConfirmation = false; - - if (password && confirmPassword && password.length > 0 && confirmPassword.length > 0) { - vm.setPasswordForm.password.$setValidity('auth', true); - vm.setPasswordForm.confirmPassword.$setValidity('auth', true); - } - - if (vm.setPasswordForm.$invalid) { - return; - } - - // TODO: All of this logic can/should be shared! We should do validation the nice way instead of all of this manual stuff, see: inviteSavePassword - authResource.performSetPassword(vm.resetPasswordCodeInfo.resetCodeModel.userId, password, confirmPassword, vm.resetPasswordCodeInfo.resetCodeModel.resetCode) - .then(function () { - vm.showSetPasswordConfirmation = true; - vm.resetComplete = true; - - //reset the values in the resetPasswordCodeInfo angular so if someone logs out the change password isn't shown again - resetPasswordCodeInfo.resetCodeModel = null; - - }, function (reason) { - if (reason.data && reason.data.Message) { - vm.errorMsg = reason.data.Message; - } - else { - vm.errorMsg = reason.errorMsg; - } - vm.setPasswordForm.password.$setValidity("auth", false); - vm.setPasswordForm.confirmPassword.$setValidity("auth", false); - }); - - vm.setPasswordForm.password.$viewChangeListeners.push(function () { - if (vm.setPasswordForm.password.$invalid) { - vm.setPasswordForm.password.$setValidity('auth', true); - } - }); - - vm.setPasswordForm.confirmPassword.$viewChangeListeners.push(function () { - if (vm.setPasswordForm.confirmPassword.$invalid) { - vm.setPasswordForm.confirmPassword.$setValidity('auth', true); - } - }); - } - - function newPasswordKeyUp(event) { - vm.passwordVal = event.target.value; - } - - //// - - function setGreeting() { - const date = new Date(); - localizationService.localize("login_greeting" + date.getDay()).then(function (label) { - $scope.greeting = label; - }); - } - - function show2FALoginDialog(viewPath) { - vm.twoFactor.submitCallback = function submitCallback() { - vm.onLogin(); - } - vm.twoFactor.cancelCallback = function cancelCallback() { - vm.showLogin(); - } - vm.twoFactor.view = viewPath; - vm.view = "2fa-login"; - SetTitle(); - } - - function resetInputValidation() { - vm.loginStates.submitButton = "init"; - vm.confirmPassword = ""; - vm.password = ""; - vm.login = ""; - if (vm.loginForm) { - vm.loginForm.username.$setValidity('auth', true); - vm.loginForm.password.$setValidity('auth', true); - } - if (vm.requestPasswordResetForm) { - vm.requestPasswordResetForm.email.$setValidity("auth", true); - } - if (vm.setPasswordForm) { - vm.setPasswordForm.password.$setValidity('auth', true); - vm.setPasswordForm.confirmPassword.$setValidity('auth', true); - } - } - - - function SetTitle() { - var title = null; - switch (vm.view.toLowerCase()) { - case "login": - title = "Login"; - break; - case "password-reset-code-expired": - case "request-password-reset": - title = "Password Reset"; - break; - case "set-password": - title = "Change Password"; - break; - case "2fa-login": - title = "Two Factor Authentication"; - break; - } - - $scope.$emit("$changeTitle", title); - } - } })(); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js b/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js index 10092aaf38..0cb314c08d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/externallogininfo.service.js @@ -14,27 +14,27 @@ function externalLoginInfoService(externalLoginInfo, umbRequestHelper) { } function getLoginProviderView(provider) { - if (provider && provider.properties && provider.properties.CustomBackOfficeView) { - return umbRequestHelper.convertVirtualToAbsolutePath(provider.properties.CustomBackOfficeView); + if (provider && provider.options && provider.options.customBackOfficeView) { + return umbRequestHelper.convertVirtualToAbsolutePath(provider.options.customBackOfficeView); } return null; } /** - * Returns true if any provider denies local login if `provider` is null, else whether the passed + * Returns true if any provider denies local login if `provider` is null, else whether the passed * @param {any} provider */ function hasDenyLocalLogin(provider) { if (!provider) { - return _.some(externalLoginInfo.providers, x => x.properties && (x.properties.DenyLocalLogin === true)); + return _.some(externalLoginInfo.providers, x => x.options.denyLocalLogin === true); } else { - return provider && provider.properties && (provider.properties.DenyLocalLogin === true); + return provider && provider.options.denyLocalLogin === true; } } /** - * Returns all login providers + * Returns all login providers */ function getLoginProviders() { return externalLoginInfo.providers; @@ -49,12 +49,8 @@ function externalLoginInfoService(externalLoginInfo, umbRequestHelper) { if (x.customView) { return true; } - else if (x.properties.AutoLinkOptions) { - return x.properties.AutoLinkOptions.AllowManualLinking; - } - else { - return false; - } + + return x.options.allowManualLinking; }); return providers; } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js index ee9aa0864f..83f49121cb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js @@ -216,6 +216,8 @@ angular.module('umbraco.services') //when it's successful, return the user data setCurrentUser(data); + this._retryRequestQueue(true); + var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" }; //broadcast a global event @@ -248,8 +250,6 @@ angular.module('umbraco.services') authResource.getCurrentUser() .then(function (data) { - var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" }; - setCurrentUser(data); deferred.resolve(currentUser); diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/_flexbox.less b/src/Umbraco.Web.UI.Client/src/less/utilities/_flexbox.less index a3427074cd..f9b697ad01 100644 --- a/src/Umbraco.Web.UI.Client/src/less/utilities/_flexbox.less +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/_flexbox.less @@ -37,6 +37,9 @@ flex: 1; } +.flx-gap-sm { + gap: 20px; +} .flx-g0 { flex-grow: 0; diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/layout/_display.less b/src/Umbraco.Web.UI.Client/src/less/utilities/layout/_display.less index b156b4135b..05959977de 100644 --- a/src/Umbraco.Web.UI.Client/src/less/utilities/layout/_display.less +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/layout/_display.less @@ -38,4 +38,4 @@ .dt--fixed { table-layout: fixed; width: 100%; -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.controller.js index 53c8b13bff..b51caff236 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.controller.js @@ -79,7 +79,7 @@ angular.module("umbraco") vm.changePasswordModel.config = data; //ensure the hasPassword config option is set to true (the user of course has a password already assigned) //this will ensure the oldPassword is shown so they can change it - // disable reset password functionality beacuse it does not make sense inside the backoffice + // disable reset password functionality because it does not make sense inside the backoffice vm.changePasswordModel.config.hasPassword = true; vm.changePasswordModel.config.disableToggle = true; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.html index ed8cbd13e0..a68179e133 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/user/user.html @@ -33,41 +33,46 @@ - +
-
+ -
-
+ - - + + + + Link your + {{ ::login.caption}} + account +
- + name="provider" + label="Un-Link your {{ ::login.caption }} account" + look="{{ ::login.options.buttonLook }}" + color="{{ ::login.options.buttonColor }}"> + + Un-link your + {{ ::login.caption }} + account +
- +
@@ -94,7 +99,7 @@
{{tab.label}}
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html index 4a9dc85865..242b4eab9c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html @@ -1,240 +1,28 @@