Merge pull request #1848 from umbraco/temp-U4-9687

Fixes: U4-9687 Update login screen UI
This commit is contained in:
Sebastiaan Janssen
2017-03-30 11:21:42 +02:00
committed by GitHub
10 changed files with 229 additions and 214 deletions

View File

@@ -183,6 +183,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
get { return GetOptionalTextElement("EnablePropertyValueConverters", false); }
}
[ConfigurationProperty("loginBackgroundImage")]
internal InnerTextConfigurationElement<string> LoginBackgroundImage
{
get { return GetOptionalTextElement("loginBackgroundImage", string.Empty); }
}
string IContentSection.NotificationEmailAddress
{
get { return Notifications.NotificationEmailAddress; }
@@ -336,5 +342,10 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
{
get { return EnablePropertyValueConverters; }
}
string IContentSection.LoginBackgroundImage
{
get { return LoginBackgroundImage; }
}
}
}

View File

@@ -72,5 +72,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
bool EnablePropertyValueConverters { get; }
string LoginBackgroundImage { get; }
}
}

View File

@@ -4,9 +4,6 @@
.login-overlay {
width: 100%;
height: 100%;
background: @gray-1 url(../img/application/logo.png) no-repeat 25px 30px fixed !important;
background-size: 30px 30px !important;
color: @white;
position: absolute;
z-index: 10000;
top: 0;
@@ -16,34 +13,24 @@
border: none;
border-radius: 0;
overflow-y: auto;
background-color: @purple-d2;
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
only screen and (min-resolution: 144dpi)
{
.login-overlay {
background-image: url(../img/application/logo@2x.png) !important;
}
.login-overlay__background-image {
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
width: 100%;
height: 100%;
position: absolute;
opacity: 0.05;
}
@media only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and (min-resolution: 192dpi)
{
.login-overlay {
background-image: url(../img/application/logo@2x.png) !important;
}
}
@media only screen and (-webkit-min-device-pixel-ratio: 3),
only screen and (min-resolution: 3dppx),
only screen and (min-resolution: 350dpi)
{
.login-overlay {
background-image: url(../img/application/logo@3x.png) !important;
}
.login-overlay__logo {
position: absolute;
top: 22px;
left: 25px;
z-index: 1;
}
.login-overlay .umb-modalcolumn {
@@ -51,21 +38,56 @@
border: none;
}
.login-overlay .umb-login-container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
position: relative;
z-index: 3;
box-sizing: border-box;
}
@media (max-width: 565px) {
// making sure we don't crash into the logo
.login-overlay .umb-login-container {
padding-top: 80px;
}
}
.login-overlay .form {
position:relative;
display: block;
top: 100px;
left: 165px;
width: 370px;
text-align: right;
background: @white;
padding: 25px;
width: 500px;
margin-left: 25px;
margin-right: 25px;
margin-top: auto;
margin-bottom: auto;
}
.login-overlay .form input[type="text"],
.login-overlay .form input[type="password"] {
height: 36px;
padding-left: 10px;
padding-right: 10px;
}
.login-overlay .btn-success {
padding: 12px 24px;
}
.login-overlay .form label {
font-weight: bold;
}
.login-overlay h1 {
display: block;
text-align: right;
color: @white;
font-size: 18px;
text-align: center;
color: @black;
font-size: 24px;
font-weight: bold;
margin-bottom: 20px;
}
.login-overlay .alert {
@@ -76,62 +98,19 @@
text-align: center;
}
.login-overlay .switch-view {
margin-top: 10px;
.login-overlay .external-logins form {
margin-bottom: 20px;
}
@media (max-width: 767px) and (max-height: 420px) and (orientation: landscape) {
// Move form closer to top on narrow screen sizes
.login-overlay .form {
top: 50px;
}
}
@media (max-width: 565px) {
// Remove padding on login-form on smaller devices
.login-overlay .form {
top: 60px;
right: 25px;
left: inherit;
padding-left: 25px;
padding-right:25px;
width: auto;
}
}
@media (max-width: 339px) {
.login-overlay .form {
input[type="text"], input[type="password"] {
width: 250px;
}
}
}
#hrOr {
height: 30px;
text-align: center;
position: relative;
padding-top: 20px;
}
#hrOr hr {
.login-overlay .btn-social {
padding-top: 8px;
padding-bottom: 8px;
margin: 0;
border: none;
background-color: @gray-3;
height: 1px;
margin-bottom: 5px;
}
#hrOr div {
background-color: @black;
position: relative;
top: -16px;
border: 1px solid @gray-3;
padding: 4px;
border-radius: 50%;
width: 20px;
height: 20px;
margin: auto;
color: @gray-8;
.login-overlay .btn-social>:first-child {
line-height: 36px;
}
.login-overlay .text-error,

View File

@@ -42,6 +42,7 @@
@turquoise-l3: #C0F0ED;
@turquoise-washed: #F3FDFC;
@purple-d2: #1D1333;
@purple-d1: #2E2246;
@purple: #413659;
@purple-l1: #675E7A;

View File

@@ -81,6 +81,7 @@
$scope.externalLoginProviders = externalLoginInfo.providers;
$scope.externalLoginInfo = externalLoginInfo;
$scope.resetPasswordCodeInfo = resetPasswordCodeInfo;
$scope.backgroundImage = Umbraco.Sys.ServerVariables.umbracoSettings.loginBackgroundImage;
$scope.activateKonamiMode = function () {
if ($cookies.konamiLogin == "1") {

View File

@@ -1,133 +1,146 @@
<div ng-controller="Umbraco.Dialogs.LoginController">
<div id="login" class="umb-modalcolumn umb-dialog" ng-class="{'show-validation': loginForm.$invalid}" ng-cloak konami-code="activateKonamiMode()">
<div class="form">
<h1>{{greeting}}</h1>
<div class="login-overlay__background-image" ng-if="backgroundImage" ng-style="{'background-image':'url(' + backgroundImage + ')'}"></div>
<div ng-show="view == 'login'">
<div class="login-overlay__logo">
<img ng-src="assets/img/application/logo.png" ng-srcset="assets/img/application/logo@2x.png 2x, assets/img/application/logo@3x.png 3x">
</div>
<p>
<span ng-show="dialogData.isTimedOut"><localize key="login_timeout">Log in below</localize>.</span>
<localize key="login_instruction">Log in below</localize>
</p>
<div class="umb-login-container">
<div class="external-logins" ng-if="externalLoginProviders.length > 0">
<div class="form">
<h1>{{greeting}}</h1>
<div class="text-error" ng-repeat="error in externalLoginInfo.errors">
<div ng-show="view == 'login'">
<p>
<span ng-show="dialogData.isTimedOut"><localize key="login_timeout">Log in below</localize>.</span>
</p>
<div class="external-logins" ng-if="externalLoginProviders.length > 0">
<div class="text-error" ng-repeat="error in externalLoginInfo.errors">
<span>{{error}}</span>
</div>
<form method="POST" name="externalLoginForm" action="{{externalLoginFormAction}}">
<div ng-repeat="login in externalLoginProviders">
<button type="submit" class="btn btn-block btn-social"
ng-class="login.properties.SocialStyle"
id="{{login.authType}}" name="provider" value="{{login.authType}}"
title="Log in using your {{login.caption}} account">
<i class="fa" ng-class="login.properties.SocialIcon"></i>
<localize key="login_signInWith">Sign in with</localize> {{login.caption}}
</button>
</div>
</form>
</div>
<form method="POST" name="loginForm" ng-submit="loginSubmit(login, password)">
<div class="control-group" ng-show="loginForm.$invalid">
<div class="text-error">{{errorMsg}}</div>
</div>
<div class="control-group" ng-class="{error: loginForm.username.$invalid}">
<label><localize key="general_username">Username</localize></label>
<input type="text" ng-model="login" name="username" class="-full-width-input" localize="placeholder" placeholder="@placeholders_username" />
</div>
<div class="control-group" ng-class="{error: loginForm.password.$invalid}">
<label><localize key="general_password">Password</localize></label>
<input type="password" ng-model="password" name="password" class="-full-width-input" localize="placeholder" placeholder="@placeholders_password" autocomplete="off" />
</div>
<div class="flex justify-between items-center">
<button type="submit" class="btn btn-success" val-trigger-change="#login .form input"><localize key="general_login">Login</localize></button>
<div ng-show="allowPasswordReset">
<a class="muted" style="text-decoration: underline;" href="#" prevent-default ng-click="showRequestPasswordReset()"><localize key="login_forgottenPassword">Forgotten password?</localize></a>
</div>
</div>
</form>
</div>
<div ng-show="view == 'request-password-reset'">
<p>
<localize key="login_forgottenPasswordInstruction">An email will be sent to the address specified with a link to reset your password</localize>
</p>
<form method="POST" name="requestPasswordResetForm" ng-submit="requestPasswordResetSubmit(email)">
<div class="control-group" ng-class="{error: requestPasswordResetForm.email.$invalid}">
<label><localize key="general_email">Email</localize></label>
<input type="text" ng-model="email" name="email" class="-full-width-input" localize="placeholder" placeholder="@placeholders_email" />
</div>
<div class="control-group" ng-show="requestPasswordResetForm.$invalid">
<div class="text-error">{{errorMsg}}</div>
</div>
<div class="control-group" ng-show="showEmailResetConfirmation">
<div class="text-info">
<localize key="login_requestPasswordResetConfirmation">An email with password reset instructions will be sent to the specified address if it matched our records</localize>
</div>
</div>
<div class="flex justify-between items-center">
<button type="submit" class="btn btn-success" val-trigger-change="#login .form input"><localize key="general_submit">Submit</localize></button>
<a class="muted" href="#" prevent-default ng-click="showLogin()" style="text-decoration: underline;"><localize key="login_returnToLogin">Return to login form</localize></a>
</div>
</form>
</div>
<div ng-show="view == 'set-password'">
<p ng-hide="resetComplete">
<localize key="login_setPasswordInstruction">Please provide a new password.</localize>
</p>
<form method="POST" name="setPasswordForm" ng-submit="setPasswordSubmit(password, confirmPassword)">
<div ng-hide="resetComplete" class="control-group" ng-class="{error: setPasswordForm.password.$invalid}">
<label><localize key="user_newPassword">New password</localize></label>
<input type="password" ng-model="password" name="password" class="-full-width-input" localize="placeholder" placeholder="@placeholders_password" />
</div>
<div ng-hide="resetComplete" class="control-group" ng-class="{error: setPasswordForm.confirmPassword.$invalid}">
<label><localize key="user_confirmNewPassword">Confirm new password</localize></label>
<input type="password" ng-model="confirmPassword" name="confirmPassword" class="-full-width-input" localize="placeholder" placeholder="@placeholders_confirmPassword" />
</div>
<div ng-hide="resetComplete" class="control-group" ng-show="setPasswordForm.$invalid">
<div class="text-error">{{errorMsg}}</div>
</div>
<div class="control-group" ng-show="showSetPasswordConfirmation">
<div class="text-info">
<localize key="login_setPasswordConfirmation">Your new password has been set and you may now use it to log in.</localize>
</div>
</div>
<div class="flex justify-between items-center">
<button ng-hide="resetComplete" type="submit" class="btn btn-success" val-trigger-change="#login .form input"><localize key="general_submit">Submit</localize></button>
<a class="muted" href="#" prevent-default ng-click="showLogin()"><localize key="login_returnToLogin">Return to login form</localize></a>
</div>
</form>
</div>
<div ng-show="view == 'password-reset-code-expired'">
<div class="text-error" ng-repeat="error in resetPasswordCodeInfo.errors">
<span>{{error}}</span>
</div>
<form method="POST" name="externalLoginForm" action="{{externalLoginFormAction}}">
<div ng-repeat="login in externalLoginProviders">
<button type="submit" class="btn btn-block btn-social"
ng-class="login.properties.SocialStyle"
id="{{login.authType}}" name="provider" value="{{login.authType}}"
title="Log in using your {{login.caption}} account">
<i class="fa" ng-class="login.properties.SocialIcon"></i>
<localize key="login_signInWith">Sign in with</localize> {{login.caption}}
</button>
</div>
</form>
<div id="hrOr">
<hr />
<div><localize key="general_or">or</localize></div>
</div>
</div>
<form method="POST" name="loginForm" ng-submit="loginSubmit(login, password)">
<div class="control-group" ng-class="{error: loginForm.username.$invalid}">
<input type="text" ng-model="login" name="username" class="input-xlarge input--no-border" localize="placeholder" placeholder="@placeholders_username" />
</div>
<div class="control-group" ng-class="{error: loginForm.password.$invalid}">
<input type="password" ng-model="password" name="password" class="input-xlarge input--no-border" localize="placeholder" placeholder="@placeholders_password" autocomplete="off" />
</div>
<div class="control-group" ng-show="loginForm.$invalid">
<div class="text-error">{{errorMsg}}</div>
</div>
<button type="submit" class="btn" val-trigger-change="#login .form input"><localize key="general_login">Login</localize></button>
<div class="switch-view" ng-show="allowPasswordReset">
<a class="muted" href="#" prevent-default ng-click="showRequestPasswordReset()"><localize key="login_forgottenPassword">Forgotten password?</localize></a>
</div>
</form>
</div>
<div ng-show="view == 'request-password-reset'">
<p>
<localize key="login_forgottenPasswordInstruction">An email will be sent to the address specified with a link to reset your password</localize>
</p>
<form method="POST" name="requestPasswordResetForm" ng-submit="requestPasswordResetSubmit(email)">
<div class="control-group" ng-class="{error: requestPasswordResetForm.email.$invalid}">
<input type="text" ng-model="email" name="email" class="input-xlarge input--no-border" localize="placeholder" placeholder="@placeholders_email" />
</div>
<div class="control-group" ng-show="requestPasswordResetForm.$invalid">
<div class="text-error">{{errorMsg}}</div>
</div>
<div class="control-group" ng-show="showEmailResetConfirmation">
<div class="text-info">
<localize key="login_requestPasswordResetConfirmation">An email with password reset instructions will be sent to the specified address if it matched our records</localize>
</div>
</div>
<button type="submit" class="btn" val-trigger-change="#login .form input"><localize key="general_submit">Submit</localize></button>
<div class="switch-view">
<a class="muted" href="#" prevent-default ng-click="showLogin()"><localize key="login_returnToLogin">Return to login form</localize></a>
</div>
</form>
</div>
<div ng-show="view == 'set-password'">
<p ng-hide="resetComplete">
<localize key="login_setPasswordInstruction">Please provide a new password.</localize>
</p>
<form method="POST" name="setPasswordForm" ng-submit="setPasswordSubmit(password, confirmPassword)">
<div ng-hide="resetComplete" class="control-group" ng-class="{error: setPasswordForm.password.$invalid}">
<input type="password" ng-model="password" name="password" class="input-xlarge" localize="placeholder" placeholder="@placeholders_password" />
</div>
<div ng-hide="resetComplete" class="control-group" ng-class="{error: setPasswordForm.confirmPassword.$invalid}">
<input type="password" ng-model="confirmPassword" name="confirmPassword" class="input-xlarge" localize="placeholder" placeholder="@placeholders_confirmPassword" />
</div>
<div ng-hide="resetComplete" class="control-group" ng-show="setPasswordForm.$invalid">
<div class="text-error">{{errorMsg}}</div>
</div>
<div class="control-group" ng-show="showSetPasswordConfirmation">
<div class="text-info">
<localize key="login_setPasswordConfirmation">Your new password has been set and you may now use it to log in.</localize>
</div>
</div>
<button ng-hide="resetComplete" type="submit" class="btn" val-trigger-change="#login .form input"><localize key="general_submit">Submit</localize></button>
<div class="switch-view">
<a class="muted" href="#" prevent-default ng-click="showLogin()"><localize key="login_returnToLogin">Return to login form</localize></a>
</div>
</form>
</div>
<div ng-show="view == 'password-reset-code-expired'">
<div class="text-error" ng-repeat="error in resetPasswordCodeInfo.errors">
<span>{{error}}</span>
</div>
<div class="switch-view">
<a class="muted" href="#" prevent-default ng-click="showLogin()"><localize key="login_returnToLogin">Return to login form</localize></a>
</div>
</div>
</div>

View File

@@ -58,6 +58,9 @@
<!-- Enables value converters for all built in property editors so that they return strongly typed object, recommended for use with Models Builder -->
<EnablePropertyValueConverters>true</EnablePropertyValueConverters>
<!-- You can specify your own background image for the login screen here. The image will automatically get an overlay to match back office colors - this path is relative to the ~/umbraco path. The default location is: /umbraco/assets/img/installer.jpg -->
<loginBackgroundImage>assets/img/installer.jpg</loginBackgroundImage>
</content>
<security>

View File

@@ -108,7 +108,11 @@
<showDeprecatedPropertyEditors>true</showDeprecatedPropertyEditors>
<!-- Enables value converters for all built in property editors so that they return strongly typed object, recommended for use with Models Builder -->
<EnablePropertyValueConverters>true</EnablePropertyValueConverters>
<EnablePropertyValueConverters>true</EnablePropertyValueConverters>
<!-- You can specify your own background image for the login screen here. The image will automatically get an overlay to match back office colors - this path is relative to the ~/umbraco path. The default location is: /umbraco/assets/img/installer.jpg -->
<loginBackgroundImage>assets/img/normalname.jpg</loginBackgroundImage>
</content>
<security>
@@ -119,8 +123,8 @@
<hideDisabledUsersInBackoffice>false</hideDisabledUsersInBackoffice>
<!-- set to true to enable the UI and API to allow back-office users to reset their passwords -->
<allowPasswordReset>true</allowPasswordReset>
<allowPasswordReset>true</allowPasswordReset>
</security>
<requestHandler>

View File

@@ -395,6 +395,7 @@ namespace Umbraco.Web.Editors
{"keepUserLoggedIn", UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn},
{"cssPath", IOHelper.ResolveUrl(SystemDirectories.Css).TrimEnd('/')},
{"allowPasswordReset", UmbracoConfig.For.UmbracoSettings().Security.AllowPasswordReset},
{"loginBackgroundImage", UmbracoConfig.For.UmbracoSettings().Content.LoginBackgroundImage},
}
},
{
@@ -441,7 +442,7 @@ namespace Umbraco.Web.Editors
return JavaScript(result);
}
[HttpPost]
public ActionResult ExternalLogin(string provider, string redirectUrl = null)
{
@@ -474,7 +475,7 @@ namespace Umbraco.Web.Editors
if (result)
{
//Add a flag and redirect for it to be displayed
TempData[TokenPasswordResetCode] = new ValidatePasswordResetCodeModel {UserId = userId, ResetCode = resetCode};
TempData[TokenPasswordResetCode] = new ValidatePasswordResetCodeModel { UserId = userId, ResetCode = resetCode };
return RedirectToLocal(Url.Action("Default", "BackOffice"));
}
}
@@ -515,7 +516,7 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <returns></returns>
private async Task<ActionResult> RenderDefaultOrProcessExternalLoginAsync(
Func<ActionResult> defaultResponse,
Func<ActionResult> defaultResponse,
Func<ActionResult> externalSignInResponse)
{
if (defaultResponse == null) throw new ArgumentNullException("defaultResponse");
@@ -525,7 +526,7 @@ namespace Umbraco.Web.Editors
//check if there is the TempData with the any token name specified, if so, assign to view bag and render the view
foreach (var tempDataTokenName in TempDataTokenNames)
{
{
if (TempData[tempDataTokenName] != null)
{
ViewData[tempDataTokenName] = TempData[tempDataTokenName];
@@ -708,7 +709,7 @@ namespace Umbraco.Web.Editors
return app;
}
private IEnumerable<Dictionary<string, string>> GetTreePluginsMetaData()
{

View File

@@ -47,7 +47,8 @@ namespace Umbraco.Web
""externalLoginsUrl"": """ + externalLoginsUrl + @"""
},
""umbracoSettings"": {
""allowPasswordReset"": " + (UmbracoConfig.For.UmbracoSettings().Security.AllowPasswordReset ? "true" : "false") + @"
""allowPasswordReset"": " + (UmbracoConfig.For.UmbracoSettings().Security.AllowPasswordReset ? "true" : "false") + @",
""loginBackgroundImage"": """ + UmbracoConfig.For.UmbracoSettings().Content.LoginBackgroundImage + @"""
},
""application"": {
""applicationPath"": """ + html.ViewContext.HttpContext.Request.ApplicationPath + @""",