[design] Redesign, cleanup Login, Message pages (#2564)

* [cleanup] login, message pages

* [style] cleanups

* [cleanup]

* [cleanup] update password

* [fix] name for web form (fixes edit-profile)
This commit is contained in:
Rushabh Mehta 2017-01-10 14:44:42 +05:30 committed by GitHub
parent 1d2a91c393
commit 042e2b09cb
25 changed files with 384 additions and 253 deletions

View file

@ -301,6 +301,9 @@ def msgprint(msg, title=None, raise_exception=0, as_table=False, indicator=None,
message_log.append(json.dumps(out))
_raise_exception()
def clear_messages():
local.message_log = []
def throw(msg, exc=ValidationError, title=None):
"""Throw execption and show message (`msgprint`).
@ -994,23 +997,42 @@ def compare(val1, condition, val2):
import frappe.utils
return frappe.utils.compare(val1, condition, val2)
def respond_as_web_page(title, html, success=None, http_status_code=None, context=None):
def respond_as_web_page(title, html, success=None, http_status_code=None,
context=None, indicator_color=None, primary_action='/', primary_label = None):
"""Send response as a web page with a message rather than JSON. Used to show permission errors etc.
:param title: Page title and heading.
:param message: Message to be shown.
:param success: Alert message.
:param http_status_code: HTTP status code."""
:param http_status_code: HTTP status code
:param context: web template context
:param indicator_color: color of indicator in title
:param primary_action: route on primary button (default is `/`)
:param primary_label: label on primary button (defaut is "Home")"""
local.message_title = title
local.message = html
local.message_success = success
local.response['type'] = 'page'
local.response['route'] = 'message'
if http_status_code:
local.response['http_status_code'] = http_status_code
if context:
local.response['context'] = context
if not context:
context = {}
if not indicator_color:
if success:
indicator_color = 'green'
elif http_status_code and http_status_code > 300:
indicator_color = 'red'
else:
indicator_color = 'blue'
context['indicator_color'] = indicator_color
context['primary_label'] = primary_label
context['primary_action'] = primary_action
context['error_code'] = http_status_code
local.response['context'] = context
def redirect_to_message(title, html, http_status_code=None, context=None):
"""Redirects to /message?id=random

View file

@ -24,6 +24,7 @@ from frappe.utils import get_site_name, get_site_path
from frappe.middlewares import StaticDataMiddleware
from frappe.utils.error import make_error_snapshot
from frappe.core.doctype.communication.comment import update_comments_in_parent_after_request
from frappe import _
local_manager = LocalManager([frappe.local])
@ -123,6 +124,7 @@ def make_form_dict(request):
def handle_exception(e):
http_status_code = getattr(e, "http_status_code", 500)
return_as_message = False
if (http_status_code==500
and isinstance(e, MySQLdb.OperationalError)
@ -132,17 +134,31 @@ def handle_exception(e):
# code 409 represents conflict
http_status_code = 508
if frappe.local.is_ajax or 'application/json' in frappe.local.request.headers.get('Accept', ''):
if http_status_code==403:
frappe.respond_as_web_page(_("Not Permitted"),
_("You do not have enough permissions to complete the action"),
http_status_code=http_status_code, indicator_color='red')
return_as_message = True
elif http_status_code==404:
frappe.respond_as_web_page(_("Not Found"),
_("The resource you are looking for is not available"),
http_status_code=http_status_code, indicator_color='red')
return_as_message = True
elif frappe.local.is_ajax or 'application/json' in frappe.local.request.headers.get('Accept', ''):
response = frappe.utils.response.report_error(http_status_code)
else:
traceback = "<pre>"+frappe.get_traceback()+"</pre>"
if frappe.local.flags.disable_traceback:
traceback = ""
frappe.respond_as_web_page("Server Error",
traceback,
http_status_code=http_status_code)
response = frappe.website.render.render("message", http_status_code=http_status_code)
traceback, http_status_code=http_status_code,
indicator_color='red')
return_as_message = True
if e.__class__ == frappe.AuthenticationError:
if hasattr(frappe.local, "login_manager"):
@ -152,6 +168,9 @@ def handle_exception(e):
frappe.logger().error('Request Error', exc_info=True)
make_error_snapshot(e)
if return_as_message:
response = frappe.website.render.render("message", http_status_code=http_status_code)
return response
def after_request(rollback):

View file

@ -534,15 +534,7 @@ def update_password(new_password, key=None, old_password=None):
def test_password_strength(new_password, key=None, old_password=None):
from frappe.utils.password_strength import test_password_strength as _test_password_strength
res = _get_user_for_update_password(key, old_password)
if not res:
return
elif res.get('message'):
return res['message']
else:
user = res['user']
user_data = frappe.db.get_value('User', user, ['first_name', 'middle_name', 'last_name', 'email', 'birth_date'])
user_data = frappe.db.get_value('User', frappe.session.user, ['first_name', 'middle_name', 'last_name', 'email', 'birth_date'])
if new_password:
return _test_password_strength(new_password, user_inputs=user_data)
@ -586,9 +578,9 @@ def sign_up(email, full_name, redirect_to):
user = frappe.db.get("User", {"email": email})
if user:
if user.disabled:
return _("Registered but disabled.")
return 0, _("Registered but disabled")
else:
return _("Already Registered")
return 0, _("Already Registered")
else:
if frappe.db.sql("""select count(*) from tabUser where
HOUR(TIMEDIFF(CURRENT_TIMESTAMP, TIMESTAMP(modified)))=1""")[0][0] > 300:
@ -613,24 +605,25 @@ def sign_up(email, full_name, redirect_to):
frappe.cache().hset('redirect_after_login', user.name, redirect_to)
if user.flags.email_sent:
return _("Please check your email for verification")
return 1, _("Please check your email for verification")
else:
return _("Please ask your administrator to verify your sign-up")
return 2, _("Please ask your administrator to verify your sign-up")
@frappe.whitelist(allow_guest=True)
def reset_password(user):
if user=="Administrator":
return _("Not allowed to reset the password of {0}").format(user)
return 'not allowed'
try:
user = frappe.get_doc("User", user)
user.validate_reset_password()
user.reset_password(send_email=True)
return _("Password reset instructions have been sent to your email")
return frappe.msgprint(_("Password reset instructions have been sent to your email"))
except frappe.DoesNotExistError:
return _("User {0} does not exist").format(user)
frappe.clear_messages()
return 'not found'
def user_query(doctype, txt, searchfield, start, page_len, filters):
from frappe.desk.reportview import get_match_cond

View file

@ -98,7 +98,8 @@ def unsubscribe(email, name):
return_unsubscribed_page(email)
def return_unsubscribed_page(email):
frappe.respond_as_web_page(_("Unsubscribed"), _("{0} has been successfully unsubscribed from this list.").format(email))
frappe.respond_as_web_page(_("Unsubscribed"),
_("{0} has been successfully unsubscribed from this list.").format(email), indicator_color='green')
def create_lead(email_id):
"""create a lead if it does not exist"""
@ -157,7 +158,9 @@ def confirm_subscription(email):
add_subscribers(_("Website"), email)
frappe.db.commit()
frappe.respond_as_web_page(_("Confirmed"), _("{0} has been successfully added to our Email Group.").format(email))
frappe.respond_as_web_page(_("Confirmed"),
_("{0} has been successfully added to the Email Group.").format(email),
indicator_color='green')
def send_newsletter(newsletter):

View file

@ -218,7 +218,9 @@ def unsubscribe(doctype, name, email):
return_unsubscribed_page(email, doctype, name)
def return_unsubscribed_page(email, doctype, name):
frappe.respond_as_web_page(_("Unsubscribed"), _("{0} has left the conversation in {1} {2}").format(email, _(doctype), name))
frappe.respond_as_web_page(_("Unsubscribed"),
_("{0} has left the conversation in {1} {2}").format(email, _(doctype), name),
indicator_color='green')
def flush(from_test=False):
"""flush email queue, every time: called from scheduler"""

View file

@ -30,7 +30,9 @@ def execute_cmd(cmd, from_async=False):
try:
method = get_attr(cmd)
except:
frappe.throw('Invalid method', frappe.NotFound)
frappe.respond_as_web_page(title='Invalid Method', html='Method not found',
indicator_color='red', http_status_code=404)
return
if from_async:
method = method.queue
@ -79,7 +81,8 @@ def logout():
def web_logout():
frappe.local.login_manager.logout()
frappe.db.commit()
frappe.respond_as_web_page("Logged Out", """<p><a href="/index" class="text-muted">Back to Home</a></p>""")
frappe.respond_as_web_page(_("Logged Out"), _("You have been successfully logged out"),
indicator_color='green')
@frappe.whitelist(allow_guest=True)
def run_custom_method(doctype, name, custom_method):

View file

@ -148,15 +148,16 @@ def dropbox_callback(oauth_token=None, not_approved=False):
close = '<p class="text-muted">' + _('Please close this window') + '</p>'
frappe.respond_as_web_page(_("Dropbox Setup"),
_("Illegal Access Token. Please try again") + close,
success=False, http_status_code=frappe.AuthenticationError.http_status_code)
indicator_color='red',
http_status_code=frappe.AuthenticationError.http_status_code)
else:
frappe.respond_as_web_page(_("Dropbox Setup"),
_("You did not apporve Dropbox Access.") + close,
success=False, http_status_code=frappe.AuthenticationError.http_status_code)
indicator_color='red')
frappe.respond_as_web_page(_("Dropbox Setup"),
_("Dropbox access is approved!") + close,
success=False, http_status_code=frappe.AuthenticationError.http_status_code)
indicator_color='red')
# backup process
@frappe.whitelist()

View file

@ -217,7 +217,7 @@ def get_express_checkout_details(token):
if response.get("ACK")[0] != "Success":
frappe.respond_as_web_page(_("Something went wrong"),
_("Looks like something went wrong during the transaction. Since we haven't confirmed the payment, Paypal will automatically refund you this amount. If it doesn't, please send us an email and mention the Correlation ID: {0}.").format(response.get("CORRELATIONID", [None])[0]),
success=False,
indicator_color='red',
http_status_code=frappe.ValidationError.http_status_code)
return
@ -300,5 +300,5 @@ def get_checkout_url(**kwargs):
except Exception:
frappe.respond_as_web_page(_("Something went wrong"),
_("Looks like something is wrong with this site's Paypal configuration. Don't worry! No payment has been made from your Paypal account."),
success=False,
indicator_color='red',
http_status_code=frappe.ValidationError.http_status_code)

View file

@ -219,8 +219,8 @@ def get_checkout_url(**kwargs):
return frappe.get_doc("Razorpay Settings").get_payment_url(**kwargs)
except Exception:
frappe.respond_as_web_page(_("Something went wrong"),
_("Looks like something is wrong with this site's Razorpay configuration. Don't worry! No payment has been made."),
success=False,
_("Looks like something is wrong with this site's Razorpay configuration. No payment has been made."),
indicator_color='red',
http_status_code=frappe.ValidationError.http_status_code)
@frappe.whitelist()

View file

@ -920,10 +920,26 @@ li .footer-child-item {
}
.page-card {
max-width: 360px;
padding: 30px;
margin: auto;
padding: 15px;
margin: 70px auto;
border: 1px solid #d1d8dd;
border-radius: 4px;
margin: 30px auto;
background-color: #fff;
box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1);
}
.page-card .page-card-head {
padding: 10px 15px;
margin: -15px;
margin-bottom: 15px;
border-bottom: 1px solid #d1d8dd;
}
.page-card .page-card-head .indicator {
color: #36414C;
font-size: 14px;
}
.page-card .page-card-head .indicator::before {
margin: 0 6px 0.5px 0px;
}
.page-card .btn {
margin-top: 30px;
}

View file

@ -661,10 +661,30 @@ li .footer-child-item {
.page-card {
max-width: 360px;
padding: 30px;
margin: auto;
padding: 15px;
margin: 70px auto;
border: 1px solid @border-color;
border-radius: 4px;
margin: 30px auto;
background-color: #fff;
background-color: #fff;
box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1);
.page-card-head {
padding: 10px 15px;
margin: -15px;
margin-bottom: 15px;
border-bottom: 1px solid @border-color;
.indicator {
color: @text-color;
font-size: 14px;
}
.indicator::before {
margin: 0 6px 0.5px 0px;
}
}
.btn {
margin-top: 30px;
}
}

View file

@ -69,7 +69,7 @@
<div data-html-block="hero">
{%- block hero -%}{%- endblock -%}
</div>
<div class="container" data-html-block="content">
<div class="container">
{% block content %}{% endblock %}
</div>
</div>

View file

@ -1,8 +1,8 @@
/* login-css */
footer {
background-color: #ffffff;
}
.hero-and-content {
/*background-color: #f5f7fa;*/
background-color: #fafbfc;
.page-sidebar, #wrap-footer, .page-header {
display: none;
@ -13,20 +13,6 @@ footer {
width: 100%;
}
.blue {
color: #7575ff;
}
.icon-facebook, .icon-facebook-sign{
color: #3b5998;
}
.icon-google-plus, .icon-google-plus-sign{
color: #C63D2D;
}
.icon-github, .icon-github-sign{
color: black;
}
.icon-twitter, .icon-twitter-sign{
color: #00a0d1;
}
@ -39,8 +25,11 @@ footer {
background-color: #7575ff;
}
.form-signin {
section {
display: none;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
@ -55,7 +44,7 @@ footer {
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 6px;
font-size: 16px;
font-size: 14px;
margin-bottom: 10px;
}
.form-signin .form-control:focus {
@ -66,6 +55,26 @@ footer {
margin: 10px;
}
.social-logins .fa {
margin-right: 5px;
color: #8D99A6;
}
.form-footer {
margin-top: -45px;
text-align: center;
}
.form-footer, .form-footer a, .form-footer h6 {
font-size: 12px;
color: #8D99A6;
font-weight: bold;
}
.form-footer .btn-default {
color: #36414C;
}
h5 {
position: relative;
text-align: center;
@ -73,44 +82,11 @@ h5 {
margin-bottom:20px;
}
h5 span {
background: #fff;
padding: 0 15px;
position: relative;
z-index: 1;
}
h5:before {
background: #ddd;
content: "";
display: block;
height: 1px;
position: absolute;
top: 50%;
width: 100%;
}
h5:before {
left: 0;
}
.login_header{
font-size: 30px;
position: relative;
text-align: center;
margin: 10px 0px 30px 0px;
letter-spacing: 0.5px;
}
p{
p {
margin-bottom:20px;
}
.btn-login, .btn-signup, .btn-forgot {
background: #7575ff;
color: white;
}
.btn-login {
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 400;
.login-content .btn {
font-size: 14px;
margin-top: 45px;
}

View file

@ -33,7 +33,7 @@ login.bind_events = function() {
args.redirect_to = get_url_arg("redirect-to") || '';
args.full_name = ($("#signup_fullname").val() || "").trim();
if(!args.email || !valid_email(args.email) || !args.full_name) {
frappe.msgprint(__("Valid email and name required"));
login.set_indicator(__("Valid email and name required"), 'red');
return false;
}
login.call(args);
@ -46,21 +46,21 @@ login.bind_events = function() {
args.cmd = "frappe.core.doctype.user.user.reset_password";
args.user = ($("#forgot_email").val() || "").trim();
if(!args.user) {
frappe.msgprint(__("Valid Login id required."));
login.set_indicator(__("Valid Login id required."), 'red');
return false;
}
login.call(args);
return false;
});
$(".btn-ldpa-login").on("click", function(){
$(".btn-ldap-login").on("click", function(){
var args = {};
args.cmd = "{{ ldap_settings.method }}";
args.usr = ($("#login_email").val() || "").trim();
args.pwd = $("#login_password").val();
args.device = "desktop";
if(!args.usr || !args.pwd) {
frappe.msgprint(__("Both login and password required"));
login.set_indicator(__("Both login and password required"), 'red');
return false;
}
login.call(args);
@ -75,32 +75,49 @@ login.route = function() {
login[route]();
}
login.reset_sections = function(hide) {
if(hide || hide===undefined) {
$("section").toggle(false);
}
$('section .indicator').each(function() {
$(this).removeClass().addClass('indicator').addClass('blue')
.text($(this).attr('data-text'));
});
}
login.login = function() {
$("form").toggle(false);
$(".form-login").toggle(true);
login.reset_sections();
$(".for-login").toggle(true);
}
login.forgot = function() {
$("form").toggle(false);
$(".form-forgot").toggle(true);
login.reset_sections();
$(".for-forgot").toggle(true);
}
login.signup = function() {
$("form").toggle(false);
$(".form-signup").toggle(true);
login.reset_sections();
$(".for-signup").toggle(true);
}
// Login
login.call = function(args) {
login.call = function(args, callback) {
login.set_indicator(__('Verifying...'), 'blue');
return frappe.call({
type: "POST",
args: args,
callback: callback,
freeze: true,
statusCode: login.login_handlers
});
}
login.set_indicator = function(message, color) {
$('section:visible .indicator')
.removeClass().addClass('indicator').addClass(color).text(message)
}
login.login_handlers = (function() {
var get_error_handler = function(default_message) {
return function(xhr, data) {
@ -110,7 +127,7 @@ login.login_handlers = (function() {
var message = default_message;
if (data._server_messages) {
message = ($.map(JSON.parse(data._server_messages || '[]'), function() {
message = ($.map(JSON.parse(data._server_messages || '[]'), function(v) {
// temp fix for messages sent as dict
try {
return JSON.parse(v).message;
@ -120,15 +137,22 @@ login.login_handlers = (function() {
}) || []).join('<br>') || default_message;
}
frappe.msgprint(message);
if(message===default_message) {
login.set_indicator(message, 'red');
} else {
login.reset_sections(false);
}
};
}
var login_handlers = {
200: function(data) {
if(data.message=="Logged In") {
login.set_indicator(__("Success"), 'green');
window.location.href = get_url_arg("redirect-to") || data.home_page;
} else if(data.message=="No App") {
login.set_indicator(__("Success"), 'green');
if(localStorage) {
var last_visited =
localStorage.getItem("last_visited")
@ -145,11 +169,26 @@ login.login_handlers = (function() {
} else {
window.location.href = data.home_page;
}
} else if(["#signup", "#forgot"].indexOf(window.location.hash)!==-1) {
frappe.msgprint(data.message);
} else if(window.location.hash === '#forgot') {
console.log(data.message);
if(data.message==='not found') {
login.set_indicator(__("Not a valid user"), 'red');
} else if (data.message=='not allowed') {
login.set_indicator(__("Not Allowed"), 'red');
}
} else if(window.location.hash === '#signup') {
if(cint(data.message[0])==0) {
login.set_indicator(data.message[1], 'red');
} else {
login.set_indicator(__('Success'), 'green');
frappe.msgprint(data.message[1])
}
//login.set_indicator(__(data.message), 'green');
}
},
401: get_error_handler(__("Invalid Login")),
401: get_error_handler(__("Invalid Login. Try again.")),
417: get_error_handler(__("Oops! Something went wrong"))
};

View file

@ -144,13 +144,10 @@ def get_context(context):
if not frappe.form_dict.name and not frappe.form_dict.new:
self.build_as_list(context)
else:
name = None
if frappe.session.user != 'Guest':
name = frappe.db.get_value(self.doc_type, {"owner": frappe.session.user}, "name")
if frappe.session.user != 'Guest' and not frappe.form_dict.name:
frappe.form_dict.name = frappe.db.get_value(self.doc_type, {"owner": frappe.session.user}, "name")
if name:
frappe.form_dict.name = name
else:
if not frappe.form_dict.name:
# only a single doc allowed and no existing doc, hence new
frappe.form_dict.new = 1

View file

@ -120,10 +120,10 @@ $.extend(frappe, {
}
if(data.exc) {
if(opts.btn) {
$(opts.btn).addClass($(opts.btn).is('button') || $(opts.btn).hasClass('btn') ? "btn-danger" : "text-danger");
setTimeout(function() { $(opts.btn).removeClass("btn-danger text-danger"); }, 1000);
}
// if(opts.btn) {
// $(opts.btn).addClass($(opts.btn).is('button') || $(opts.btn).hasClass('btn') ? "btn-danger" : "text-danger");
// setTimeout(function() { $(opts.btn).removeClass("btn-danger text-danger"); }, 1000);
// }
try {
var err = JSON.parse(data.exc);
if($.isArray(err)) {
@ -135,10 +135,10 @@ $.extend(frappe, {
}
} else{
if(opts.btn) {
$(opts.btn).addClass($(opts.btn).is('button') || $(opts.btn).hasClass('btn') ? "btn-success" : "text-success");
setTimeout(function() { $(opts.btn).removeClass("btn-success text-success"); }, 1000);
}
// if(opts.btn) {
// $(opts.btn).addClass($(opts.btn).is('button') || $(opts.btn).hasClass('btn') ? "btn-success" : "text-success");
// setTimeout(function() { $(opts.btn).removeClass("btn-success text-success"); }, 1000);
// }
}
if(opts.msg && data.message) {
$(opts.msg).html(data.message).toggle(true);

View file

@ -218,11 +218,13 @@ def clear_cache(path=None):
def render_403(e, pathname):
path = "message"
frappe.local.message = """<p><strong>{error}</strong></p>
<p>
<a href="/login?redirect-to=/{pathname}" class="btn btn-primary">{login}</a>
</p>""".format(error=cstr(e.message), login=_("Login"), pathname=frappe.local.path)
frappe.local.message = cstr(e.message)
frappe.local.message_title = _("Not Permitted")
frappe.local.response['context'] = dict(
indicator_color = 'red',
primary_action = '/login',
primary_label = _('Login')
)
return render_page(path), e.http_status_code
def get_doctype_from_path(path):

View file

@ -2,13 +2,18 @@
{%- block title -%}{{_("Not Found")}}{%- endblock -%}
{%- block header -%}
<h1 class="text-danger" style="margin-top: 100px;">{{_("Page missing or moved")}}</h1>
{%- endblock -%}
{% block page_content %}
<div class="404-content" style="margin-bottom: 100px;">
<p>{{_("We are very sorry for this, but the page you are looking for is missing (this could be because of a typo in the address) or moved.")}}</p>
<div class='page-card'>
<div class='page-card-head'>
<span class='indicator darkgrey'>{{_("Page Missing or Moved")}}</span>
</div>
<p>{{_("The page you are looking for is missing. This could be because it is moved or there is a typo in the link.")}}</p>
<div><a href='/' class='btn btn-primary btn-xs'>{{ _("Home") }}</a></div>
</div>
<p class='text-muted text-center small' style='margin-top: -20px;'>{{ _("Error Code: {0}").format('404') }}</p>
<style>
.hero-and-content {
background-color: #f5f7fa;
}
</style>
{% endblock %}

View file

@ -19,7 +19,7 @@
{%- endfor -%}
</head>
<body>
<div class="centered splash" style="width: 200px; height: 200px;">
<div class="centered splash" style="width: 100px; height: 100px;">
<img src="{{ splash_image or "/assets/frappe/images/frappe-bird-thin.svg" }}">
</div>
<div class="main-section">

View file

@ -4,7 +4,7 @@
{% block header %}
<h2 class="text-danger">
<i class="fa fa-exclamation-sign"></i> Oops, a server error has occured
<i class="fa fa-exclamation-sign"></i> Uncaught Server Exception
</h2>
{% endblock %}

View file

@ -8,92 +8,101 @@
{% block page_content %}
<!-- {{ for_test }} -->
<section class='for-login'>
<div class="login-content page-card" style="margin-top: 20px;">
<form class="form-signin form-login" role="form">
<div class="page-card-head">
<span class="indicator blue" data-text="{{ _("Sign In") }}"></span>
</div>
<div class="login-content page-card">
<form class="form-signin form-login" role="form">
<input type="text" id="login_email"
class="form-control" placeholder="{{ _('Email address') }}" required autofocus>
<div class="login_header">Sign In</div>
<input type="password" id="login_password"
class="form-control" placeholder="{{ _('Password') }}" required>
<input type="text" id="login_email"
class="form-control" placeholder="{{ _('Email address') }}" required autofocus>
<button class="btn btn-sm btn-primary btn-block btn-login" type="submit">
{{ _("Sign in") }}</button>
<input type="password" id="login_password"
class="form-control" placeholder="{{ _('Password') }}" required>
{% if ldap_settings.enabled %}
<button class="btn btn-sm btn-default btn-block btn-login btn-ldap-login">
{{ _("Login with LDAP") }}</button>
{% endif %}
</form>
</div>
<div class='form-footer'>
<div>
<!-- <p style="float:left" class="text-center small"><input type="checkbox" value="remember-me"> Remember me </p> -->
<p style="float:right" class="text-center small"><a href="#forgot">{{ _("Forgot Password?") }}</a></p>
</div>
<div class='social-logins'>
{%- if social_login -%}
<h6>{{ _("Or login with") }}</h6>
<button class="btn btn-lg btn-default btn-block btn-login" type="submit">{{ _("Sign in") }}</button>
<div class='social-logins'>
{%- if social_login -%}
<h5><span>{{ _("Or login with") }}</span></h5>
<p class="text-center" style="margin-top: 15px">
<p class="text-center" style="margin-top: 15px">
{%- if facebook_login is defined %}
<a href="{{ facebook_login }}" class="no-decoration btn-social btn-facebook">
<i class="fa fa-facebook fa-2x"></i></a>
<a href="{{ facebook_login }}"
class="btn btn-default btn-xs btn-social btn-facebook">
<i class="fa fa-facebook-official"></i> {{ _("Facebook") }}</a>
{%- endif -%}
{%- if google_login is defined %}
<a href="{{ google_login }}" class="no-decoration btn-social btn-google">
<i class="fa fa-google-plus fa-2x"></i></a>
<a href="{{ google_login }}" class="btn btn-default btn-xs btn-social btn-google">
<i class="fa fa-google-plus"></i> {{ _("Google") }}</a>
{%- endif -%}
{%- if github_login is defined %}
<a href="{{ github_login }}" class="no-decoration btn-social btn-github">
<i class="fa fa-github fa-2x"></i></a>
<a href="{{ github_login }}" class="btn btn-default btn-xs btn-social btn-github">
<i class="fa fa-github"></i> {{ _("GitHub") }}</a>
{%- endif -%}
{%- if frappe_login is defined %}
<a href="{{ frappe_login }}" class="no-decoration">
<img src="/assets/frappe/images/favicon.png"></a>
<a href="{{ frappe_login }}" class="btn btn-default btn-xs btn-social">
<img style='width: 16px; height: 16px;'
src="/assets/frappe/images/favicon.png"> {{ _("Frappe") }}</a>
{%- endif -%}
</p>
{%- endif -%}
{% if ldap_settings.enabled %}
<p>
<button class="btn btn-lg btn-default btn-block btn-login btn-ldpa-login">
{{ _("Login via LDAP") }}</button>
</p>
{% endif %}
</p>
{%- endif -%}
</div>
{%- if not disable_signup -%}
<p class="text-center">
<a href="#signup" style="margin-top: -2px;">{{ _("Dont have an account? Sign up") }}</a>
</p>
{%- endif -%}
<p class="text-center">
<a href="#forgot">{{ _("Forgot Password?") }}</a></p>
</div>
<div style="margin-top: 25px">
{%- if not disable_signup -%}
<p class="text-center">
{{ _("Dont have an account? ") }} <a class="btn btn-default" href="#signup" style="margin-top: -2px;">{{ _("Sign up.") }}</a>
</p>
{%- endif -%}
</div>
<div class='padding'></div>
</form>
</section>
<section class='for-signup'>
<div class="login-content page-card" style="margin-top: 20px;">
<form class="form-signin form-signup hide" role="form">
<div class="page-card-head">
<span class="indicator blue" data-text="{{ _("Sign Up") }}"></span>
</div>
<input type="text" id="signup_fullname"
class="form-control" placeholder="{{ _('Full Name') }}" required autofocus>
<input type="email" id="signup_email"
class="form-control" placeholder="{{ _('Email address') }}" required>
<br>
<button class="btn btn-lg btn-default btn-block btn-signup" type="submit">{{ _("Sign up") }}</button>
<p class="text-center small">
{{ _("Have an account? ") }} <a href="#login" class="blue">{{ _("Login") }}</a>
</p>
<button class="btn btn-sm btn-primary btn-block btn-signup" type="submit">{{ _("Sign up") }}</button>
</form>
</div>
<div class='form-footer'>
<a href="#login" class="blue">{{ _("Have an account? Login") }}</a>
</div>
</section>
<section class='for-forgot'>
<div class="login-content page-card" style="margin-top: 20px;">
<form class="form-signin form-forgot hide" role="form">
<div class="page-card-head">
<span class="indicator blue" data-text="{{ _("Forgot Password") }}"></span></div>
<input type="email" id="forgot_email"
class="form-control" placeholder="{{ _('Email address') }}" required autofocus>
<br>
<button class="btn btn-lg btn-default btn-block btn-forgot" type="submit">{{ _("Send Password") }}</button>
<p class="text-center small">
<br><a href="#login" class="blue">{{ _("Back to Login") }}</a>
</p>
<button class="btn btn-sm btn-primary btn-block btn-forgot" type="submit">{{ _("Send Password") }}</button>
</form>
</div>
</div>
<div class='form-footer'>
<a href="#login">{{ _("Back to Login") }}</a>
</div>
</section>
{% endblock %}
{% block script %}

View file

@ -27,10 +27,10 @@ def get_context(context):
if get_oauth_keys(provider):
context["{provider}_login".format(provider=provider)] = get_oauth2_authorize_url(provider)
context["social_login"] = True
ldap_settings = get_ldap_settings()
context["ldap_settings"] = ldap_settings
return context
@frappe.whitelist(allow_guest=True)

View file

@ -4,7 +4,6 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils.user import get_fullname_and_avatar
import frappe.www.list
no_cache = 1

View file

@ -2,17 +2,22 @@
{% block title %}{{ title or _("Message") }}{% endblock %}
{% block header %}
{% if header is defined -%}
<h1>{{ header }}</h1>
{%- endif %}
{% endblock %}
{% block page_content %}
<div class="message-content" style="min-height: 200px;">
<div>
{{ message or _("No Message") }}
<div class='page-card'>
<div class='page-card-head'>
<span class='indicator {{ indicator_color }}'>{{ title or _("Message") }}</span>
</div>
<p>{{ message or "" }}</p>
<div><a href='{{ primary_action or "/" }}' class='btn btn-primary btn-sm'>
{{ primary_label or _("Home") }}</a></div>
</div>
{% if error_code %}
<p class='text-muted text-center small' style='margin-top: -20px;'>{{ _("Error Code: {0}").format(error_code) }}</p>
{% endif %}
<style>
.hero-and-content {
background-color: #f5f7fa;
}
</style>
{% endblock %}

View file

@ -2,29 +2,32 @@
{% block title %} {{_("Reset Password")}} {% endblock %}
{% block header %}<h1>{{_("Reset Password")}}</h1>{% endblock %}
{% block page_content %}
<div class="row" style="margin-top: 40px; margin-bottom: 20px">
<div class="col-sm-6">
<form id="reset-password">
<div class="form-group">
<input id="old_password" type="password"
class="form-control" placeholder="{{ _("Old Password") }}">
</div>
<div class="form-group">
<input id="new_password" type="password"
class="form-control" placeholder="{{ _("New Password") }}">
<span class="password-strength-indicator indicator"></span>
</div>
<p class='password-strength-message text-muted small hidden'></p>
<div class="form-group">
<button type="submit" id="update"
class="btn btn-primary">{{_("Update")}}</button>
</div>
</form>
<div class="page-card">
<div class='page-card-head'>
<span class='indicator blue'>{{ _("Reset Password") }}</span>
</div>
<form id="reset-password">
<div class="form-group">
<input id="old_password" type="password"
class="form-control" placeholder="{{ _("Old Password") }}">
</div>
<div class="form-group">
<input id="new_password" type="password"
class="form-control" placeholder="{{ _("New Password") }}">
<span class="password-strength-indicator indicator"></span>
</div>
<p class='password-strength-message text-muted small hidden'></p>
<button type="submit" id="update"
class="btn btn-primary">{{_("Update")}}</button>
</form>
</div>
<style>
.hero-and-content {
background-color: #f5f7fa;
}
</style>
<script>
@ -62,17 +65,25 @@ frappe.ready(function() {
method: "frappe.core.doctype.user.user.update_password",
btn: $("#update"),
args: args,
callback: function(r) {
$("input").val("");
if(r.message) {
frappe.msgprint(__("Password Updated"));
setTimeout(function() {
window.location.href = r.message;
}, 2000);
statusCode: {
401: function() {
$('.page-card-head .indicator').removeClass().addClass('indicator red')
.text(__('Invalid Password'));
},
200: function(r) {
$("input").val("");
strength_indicator.addClass('hidden');
strength_message.addClass('hidden');
$('.page-card-head .indicator')
.removeClass().addClass('indicator green')
.html(_('Password Updated'));
if(r.message) {
frappe.msgprint(__("Password Updated"));
setTimeout(function() {
window.location.href = r.message;
}, 2000);
}
}
if(r.exc) {
frappe.msgprint(r.exc);
}
}
});
@ -106,23 +117,32 @@ frappe.ready(function() {
method: 'frappe.core.doctype.user.user.test_password_strength',
args: args,
callback: function(r) {
if (r.message && r.message.entropy) {
var score = r.message.score,
feedback = r.message.feedback;
// console.log(r.message);
},
statusCode: {
401: function() {
$('.page-card-head .indicator').removeClass().addClass('indicator red')
.text(__('Invalid Password'));
},
200: function(r) {
if (r.message && r.message.entropy) {
var score = r.message.score,
feedback = r.message.feedback;
feedback.crack_time_display = r.message.crack_time_display;
feedback.score = score;
feedback.crack_time_display = r.message.crack_time_display;
feedback.score = score;
if (score < 2) {
set_strength_indicator('red', feedback);
} else if (score < 4) {
set_strength_indicator('yellow', feedback);
} else {
set_strength_indicator('green', feedback);
if (score < 2) {
set_strength_indicator('red', feedback);
} else if (score < 4) {
set_strength_indicator('yellow', feedback);
} else {
set_strength_indicator('green', feedback);
}
}
}
// console.log(r.message);
}
});
};