Browse Source

Merge branch 'develop' into wspace-new-design

version-14
Shariq Ansari 3 years ago
committed by GitHub
parent
commit
3b188e92e6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 445 additions and 139 deletions
  1. +2
    -1
      frappe/core/doctype/language/language.py
  2. +4
    -3
      frappe/core/doctype/role/role.json
  3. +2
    -2
      frappe/integrations/doctype/connected_app/connected_app.json
  4. +6
    -0
      frappe/public/images/ui-states/empty-app-state.svg
  5. +3
    -1
      frappe/public/js/frappe/form/controls/markdown_editor.js
  6. +12
    -5
      frappe/public/js/frappe/request.js
  7. +0
    -2
      frappe/public/js/frappe/web_form/web_form_list.js
  8. +10
    -0
      frappe/public/scss/common/css_variables.scss
  9. +0
    -9
      frappe/public/scss/desk/css_variables.scss
  10. +3
    -1
      frappe/public/scss/login.bundle.scss
  11. +2
    -0
      frappe/public/scss/website/footer.scss
  12. +16
    -0
      frappe/public/scss/website/index.scss
  13. +116
    -0
      frappe/public/scss/website/my_account.scss
  14. +47
    -0
      frappe/public/scss/website/web_form.scss
  15. +8
    -3
      frappe/sessions.py
  16. +3
    -2
      frappe/templates/includes/list/list.html
  17. +49
    -41
      frappe/templates/styles/card_style.css
  18. +63
    -38
      frappe/tests/test_auth.py
  19. +4
    -4
      frappe/website/doctype/web_form/templates/web_form.html
  20. +1
    -1
      frappe/website/doctype/web_form/test_web_form.py
  21. +4
    -6
      frappe/www/list.html
  22. +80
    -17
      frappe/www/me.html
  23. +2
    -1
      frappe/www/me.py
  24. +8
    -2
      frappe/www/third_party_apps.html

+ 2
- 1
frappe/core/doctype/language/language.py View File

@@ -39,7 +39,8 @@ def sync_languages():
frappe.get_doc({ frappe.get_doc({
'doctype': 'Language', 'doctype': 'Language',
'language_code': l['code'], 'language_code': l['code'],
'language_name': l['name']
'language_name': l['name'],
'enabled': 1,
}).insert() }).insert()


def update_language_names(): def update_language_names():


+ 4
- 3
frappe/core/doctype/role/role.json View File

@@ -12,6 +12,7 @@
"restrict_to_domain", "restrict_to_domain",
"column_break_4", "column_break_4",
"disabled", "disabled",
"is_custom",
"desk_access", "desk_access",
"two_factor_auth", "two_factor_auth",
"navigation_settings_section", "navigation_settings_section",
@@ -24,8 +25,7 @@
"form_settings_section", "form_settings_section",
"form_sidebar", "form_sidebar",
"timeline", "timeline",
"dashboard",
"is_custom"
"dashboard"
], ],
"fields": [ "fields": [
{ {
@@ -148,7 +148,7 @@
"idx": 1, "idx": 1,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2021-10-08 14:06:55.729364",
"modified": "2022-01-12 20:18:18.496230",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Core", "module": "Core",
"name": "Role", "name": "Role",
@@ -170,5 +170,6 @@
"quick_entry": 1, "quick_entry": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "ASC", "sort_order": "ASC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

+ 2
- 2
frappe/integrations/doctype/connected_app/connected_app.json View File

@@ -96,7 +96,7 @@
}, },
{ {
"fieldname": "authorization_uri", "fieldname": "authorization_uri",
"fieldtype": "Data",
"fieldtype": "Small Text",
"label": "Authorization URI", "label": "Authorization URI",
"mandatory_depends_on": "eval:doc.redirect_uri" "mandatory_depends_on": "eval:doc.redirect_uri"
}, },
@@ -139,7 +139,7 @@
"link_fieldname": "connected_app" "link_fieldname": "connected_app"
} }
], ],
"modified": "2021-05-10 05:03:06.296863",
"modified": "2022-01-07 05:28:45.073041",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Integrations", "module": "Integrations",
"name": "Connected App", "name": "Connected App",


+ 6
- 0
frappe/public/images/ui-states/empty-app-state.svg View File

@@ -0,0 +1,6 @@
<svg width="70" height="70" viewBox="0 0 70 70" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M26.4844 25.3281V16.0781" stroke="#F56B6B" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M42.6719 25.3281V16.0781" stroke="#F56B6B" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M34.5781 50.7656C30.8982 50.7656 27.3691 49.3038 24.767 46.7017C22.165 44.0997 20.7031 40.5705 20.7031 36.8906V25.3281H48.4531V36.8906C48.4531 40.5705 46.9913 44.0997 44.3892 46.7017C41.7872 49.3038 38.258 50.7656 34.5781 50.7656Z" stroke="#98A1A9" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M57.7032 58.8594C63.3462 53.4851 66.9411 46.3131 67.8703 38.576C68.7994 30.8388 67.0046 23.0197 62.7944 16.4622C58.5842 9.90464 52.2215 5.01829 44.7997 2.64279C37.3778 0.267296 29.3604 0.550997 22.125 3.44515C14.8896 6.33929 8.8882 11.6632 5.15204 18.5018C1.41588 25.3405 0.178293 33.267 1.65196 40.9191C3.12562 48.5713 7.21851 55.4712 13.2273 60.4332C19.236 65.3952 26.7855 68.1094 34.5782 68.1094V56.5469" stroke="#98A1A9" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

+ 3
- 1
frappe/public/js/frappe/form/controls/markdown_editor.js View File

@@ -32,7 +32,9 @@ frappe.ui.form.ControlMarkdownEditor = class ControlMarkdownEditor extends frapp
} }


set_language() { set_language() {
this.df.options = 'Markdown';
if (!this.df.options) {
this.df.options = 'Markdown';
}
super.set_language(); super.set_language();
} }




+ 12
- 5
frappe/public/js/frappe/request.js View File

@@ -291,11 +291,18 @@ frappe.request.call = function(opts) {
}) })
.fail(function(xhr, textStatus) { .fail(function(xhr, textStatus) {
try { try {
if (xhr.responseText) {
var data = JSON.parse(xhr.responseText);
if (data.exception) {
// frappe.exceptions.CustomError -> CustomError
var exception = data.exception.split('.').at(-1);
if (xhr.getResponseHeader('content-type') == 'application/json' && xhr.responseText) {
var data;
try {
data = JSON.parse(xhr.responseText);
} catch (e) {
console.log("Unable to parse reponse text");
console.log(xhr.responseText);
console.log(e);
}
if (data && data.exception) {
// frappe.exceptions.CustomError: (1024, ...) -> CustomError
var exception = data.exception.split('.').at(-1).split(':').at(0);
var exception_handler = exception_handlers[exception]; var exception_handler = exception_handlers[exception];
if (exception_handler) { if (exception_handler) {
exception_handler(data); exception_handler(data);


+ 0
- 2
frappe/public/js/frappe/web_form/web_form_list.js View File

@@ -139,8 +139,6 @@ export default class WebFormList {
make_table_head() { make_table_head() {
// Create Heading // Create Heading
let thead = this.table.createTHead(); let thead = this.table.createTHead();
thead.style.backgroundColor = "#f7fafc";
thead.style.color = "#8d99a6";
let row = thead.insertRow(); let row = thead.insertRow();


let th = document.createElement("th"); let th = document.createElement("th");


+ 10
- 0
frappe/public/scss/common/css_variables.scss View File

@@ -165,6 +165,16 @@
--bg-pink: var(--pink-50); --bg-pink: var(--pink-50);
--bg-cyan: var(--cyan-50); --bg-cyan: var(--cyan-50);


//font sizes
--text-xs: 11px;
--text-sm: 12px;
--text-md: 13px;
--text-base: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 20px;
--text-3xl: 22px;

--text-on-blue: var(--blue-600); --text-on-blue: var(--blue-600);
--text-on-light-blue: var(--blue-500); --text-on-light-blue: var(--blue-500);
--text-on-dark-blue: var(--blue-700); --text-on-dark-blue: var(--blue-700);


+ 0
- 9
frappe/public/scss/desk/css_variables.scss View File

@@ -4,15 +4,6 @@ $input-height: 28px !default;


:root, :root,
[data-theme="light"] { [data-theme="light"] {
--text-xs: 11px;
--text-sm: 12px;
--text-md: 13px;
--text-base: 14px;
--text-lg: 16px;
--text-xl: 18px;
--text-2xl: 20px;
--text-3xl: 22px;

// breakpoints // breakpoints
--xxl-width: map-get($grid-breakpoints, '2xl'); --xxl-width: map-get($grid-breakpoints, '2xl');
--xl-width: map-get($grid-breakpoints, 'xl'); --xl-width: map-get($grid-breakpoints, 'xl');


+ 3
- 1
frappe/public/scss/login.bundle.scss View File

@@ -1,7 +1,9 @@
@import "./desk/variables"; @import "./desk/variables";


body { body {
background-color: var(--bg-light-gray);
@include media-breakpoint-up(sm) {
background-color: var(--bg-light-gray);
}
} }


.for-forgot, .for-forgot,


+ 2
- 0
frappe/public/scss/website/footer.scss View File

@@ -94,6 +94,8 @@
max-width: 300px; max-width: 300px;
border: 1px solid var(--dark-border-color); border: 1px solid var(--dark-border-color);
box-shadow: none; box-shadow: none;
border-radius: var(--border-radius);
font-size: $font-size-sm;
} }
} }
} }

+ 16
- 0
frappe/public/scss/website/index.scss View File

@@ -27,6 +27,14 @@
@import 'navbar'; @import 'navbar';
@import 'footer'; @import 'footer';
@import 'error-state'; @import 'error-state';
@import 'my_account';


body {
@include media-breakpoint-up(sm) {
background-color: var(--bg-color);
}
}


.ql-editor.read-mode { .ql-editor.read-mode {
padding: 0; padding: 0;
@@ -166,6 +174,10 @@ a.card {
font-size: inherit; font-size: inherit;
} }


.indicator-pill {
font-size: var(--font-size-xs)
}

h4.modal-title { h4.modal-title {
font-size: 1em; font-size: 1em;
} }
@@ -298,3 +310,7 @@ h5.modal-title {
margin: 70px auto; margin: 70px auto;
font-size: $font-size-sm; font-size: $font-size-sm;
} }

.empty-list-icon {
height: 70px;
}

+ 116
- 0
frappe/public/scss/website/my_account.scss View File

@@ -0,0 +1,116 @@
//styles for my account and edit-profile page
@include media-breakpoint-up(sm) {
body[data-path="me"],
body[data-path="list"] {
background-color: var(--bg-color);
}
}

@include media-breakpoint-down(sm) {
#page-me {
.side-list {
.list-group {
display: none;
}
}
}
}

.my-account-header {
color: var(--gray-900);
margin-bottom: var(--margin-lg);
font-weight: bold;

@include media-breakpoint-down(sm) {
margin-left: -1rem;
}
}

.account-info {
background-color: var(--fg-color);
border-radius: var(--border-radius-md);
padding: var(--padding-sm) 25px;
max-width: 850px;

@include media-breakpoint-up(sm) {
margin-left: 0;
}

@include media-breakpoint-down(sm) {
padding: 0;
}

.my-account-name,
.my-account-item {
color: var(--gray-900);
font-weight: var(--text-bold);
}

.my-account-avatar {

.avatar {
height: 60px;
width: 60px;
}
}

.my-account-item-desc {
color: var(--gray-700);
font-size: var(--text-md);
}

.my-account-item-link {
font-size: var(--text-md);

a {
text-decoration: none;

.edit-profile-icon {
stroke: var(--blue-500);
}
}

.right-icon {
@include media-breakpoint-up(sm) {
display: none;
}
}

.item-link-text {
@include media-breakpoint-down(sm) {
display: none;
}
}
}

.col {
padding: var(--padding-md) 0;
border-bottom: 1px solid var(--border-color);

.form-group {
margin-right: var(--margin-lg);
}
}

:last-child {
border: 0;
}
}

//styles for third party apps page
//center wrt to outer most container and not immediate parent
.empty-apps-state {
position: relative;
padding-top: 10rem;
margin-left: -250px;
text-align: center;

@include media-breakpoint-down(sm) {
margin: auto;
padding-top: 5rem;
}

@include media-breakpoint-down(md) {
margin-left: 0;
}
}

+ 47
- 0
frappe/public/scss/website/web_form.scss View File

@@ -1,5 +1,31 @@
@import "../common/form"; @import "../common/form";



[data-doctype="Web Form"] {
.page-content-wrapper {
.breadcrumb-container.container {
@include media-breakpoint-up(sm) {
padding-left: var(--padding-sm);
}
}

.container {
max-width: 800px;

&.my-4 {
background-color: var(--fg-color);

@include media-breakpoint-up(sm) {
padding: 1.8rem;
border-radius: var(--border-radius-md);
box-shadow: var(--card-shadow);
}
}
}
}
}

.web-form-wrapper { .web-form-wrapper {
.form-control { .form-control {
color: var(--text-color); color: var(--text-color);
@@ -16,6 +42,7 @@


.form-column { .form-column {
padding: 0 var(--padding-md); padding: 0 var(--padding-md);

&:first-child { &:first-child {
padding-left: 0; padding-left: 0;
} }
@@ -24,4 +51,24 @@
padding-right: 0; padding-right: 0;
} }
} }
}

.web-form-wrapper~#datatable {
.table {
thead {
th {
border: 0;
font-weight: normal;
color: var(--text-muted)
}
}

tr {
color: var(--text-color);

td {
border-top: 1px solid var(--border-color);
}
}
}
} }

+ 8
- 3
frappe/sessions.py View File

@@ -68,9 +68,14 @@ def get_sessions_to_clear(user=None, keep_current=False, device=None):
session = DocType("Sessions") session = DocType("Sessions")
session_id = frappe.qb.from_(session).where((session.user == user) & (session.device.isin(device))) session_id = frappe.qb.from_(session).where((session.user == user) & (session.device.isin(device)))
if keep_current: if keep_current:
session_id = session_id.where(session.sid != frappe.db.escape(frappe.session.sid))

query = session_id.select(session.sid).offset(offset).limit(100).orderby(session.lastupdate, order=Order.desc)
session_id = session_id.where(session.sid != frappe.session.sid)

query = (
session_id.select(session.sid)
.offset(offset)
.limit(100)
.orderby(session.lastupdate, order=Order.desc)
)


return query.run(pluck=True) return query.run(pluck=True)




+ 3
- 2
frappe/templates/includes/list/list.html View File

@@ -2,8 +2,9 @@
<h4 class="text-muted">{{ sub_title }}</h4> <h4 class="text-muted">{{ sub_title }}</h4>
{% endif %} {% endif %}
{% if not result -%} {% if not result -%}
<div class="text-muted" style="min-height: 300px;">
{{ no_result_message or _("Nothing to show") }}
<div class="empty-apps-state">
<img class="empty-list-icon" src="/assets/frappe/images/ui-states/list-empty-state.svg"/>
<div class="mt-4">{{ no_result_message or _("Nothing to show") }}</div>
</div> </div>
{% else %} {% else %}
<div class="website-list" data-doctype="{{ doctype }}" <div class="website-list" data-doctype="{{ doctype }}"


+ 49
- 41
frappe/templates/styles/card_style.css View File

@@ -2,79 +2,87 @@
background-color: var(--bg-color); background-color: var(--bg-color);
} }


body {
background-color: var(--bg-color);
}


.page-card { .page-card {
max-width: 360px;
padding: 15px;
margin: 70px auto;
border-radius: 4px;
background-color: var(--fg-color);
box-shadow: var(--shadow-base);
max-width: 360px;
padding: 15px;
margin: 70px auto;
border-radius: 4px;
background-color: var(--fg-color);
/* box-shadow: var(--shadow-base); */
} }


.for-reset-password { .for-reset-password {
margin: 80px 0;
}
margin: 80px 0;
}


.for-reset-password .page-card { .for-reset-password .page-card {
border: 0;
max-width: 450px;
margin: auto;
padding: 40px 60px;
border-radius: 10px;
box-shadow: var(--shadow-base);
border: 0;
max-width: 450px;
margin: auto;
border-radius: 10px;
}

@media (min-width: 567px) {
.for-reset-password .page-card {
box-shadow: var(--shadow-base);
padding: 40px 60px;

}
} }


.page-card .page-card-head { .page-card .page-card-head {
padding: 10px 15px;
margin: -15px;
margin-bottom: 15px;
border-bottom: 1px solid var(--border-color);
padding: 10px 15px;
margin: -15px;
margin-bottom: 15px;
border-bottom: 1px solid var(--border-color);
} }


.for-reset-password .page-card .page-card-head {
border-bottom: 0;
.for-reset-password .page-card .page-card-head {
border-bottom: 0;
} }


.page-card-head h4 { .page-card-head h4 {
font-size: 18px;
font-weight: 600;
font-size: 18px;
font-weight: 600;
} }


#reset-password .form-group { #reset-password .form-group {
margin-bottom: 10px;
font-size: var(--font-size-sm);
margin-bottom: 10px;
font-size: var(--font-size-sm);
} }

.page-card .page-card-head .indicator { .page-card .page-card-head .indicator {
color: #36414C;
font-size: 14px;
color: #36414C;
font-size: 14px;
} }


.sign-up-message { .sign-up-message {
margin-top: 20px;
font-size: 13px;
color: var(--text-color);
margin-top: 20px;
font-size: 13px;
color: var(--text-color);
} }

.page-card .page-card-head .indicator::before { .page-card .page-card-head .indicator::before {
margin: 0 6px 0.5px 0px;
margin: 0 6px 0.5px 0px;
} }


button#update { button#update {
font-size: var(--font-size-sm);
font-size: var(--font-size-sm);
} }

.page-card .btn { .page-card .btn {
margin-top: 30px;
margin-top: 30px;
} }


.page-card p { .page-card p {
font-size: 14px;
font-size: 14px;
} }


.ellipsis { .ellipsis {
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
vertical-align: middle;
}
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
vertical-align: middle;
}

+ 63
- 38
frappe/tests/test_auth.py View File

@@ -4,38 +4,44 @@ import time
import unittest import unittest


import frappe import frappe
from frappe.auth import HTTPRequest, LoginAttemptTracker
import frappe.utils
from frappe.auth import LoginAttemptTracker
from frappe.frappeclient import FrappeClient, AuthError from frappe.frappeclient import FrappeClient, AuthError
from frappe.utils import set_request


def add_user(email, password, username=None, mobile_no=None):
first_name = email.split('@', 1)[0]
user = frappe.get_doc(
dict(doctype='User', email=email, first_name=first_name, username=username, mobile_no=mobile_no)
).insert()
user.new_password = password
user.add_roles("System Manager")
frappe.db.commit()



class TestAuth(unittest.TestCase): class TestAuth(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(TestAuth, self).__init__(*args, **kwargs)
self.test_user_email = 'test_auth@test.com'
self.test_user_name = 'test_auth_user'
self.test_user_mobile = '+911234567890'
self.test_user_password = 'pwd_012'

def setUp(self):
self.tearDown()

self.add_user(self.test_user_email, self.test_user_password,
username=self.test_user_name, mobile_no=self.test_user_mobile)

def tearDown(self):
frappe.delete_doc('User', self.test_user_email, force=True)

def add_user(self, email, password, username=None, mobile_no=None):
first_name = email.split('@', 1)[0]
user = frappe.get_doc(
dict(doctype='User', email=email, first_name=first_name, username=username, mobile_no=mobile_no)
).insert()
user.new_password = password
user.save()
frappe.db.commit()
@classmethod
def setUpClass(cls):
cls.HOST_NAME = (
frappe.get_site_config().host_name
or frappe.utils.get_site_url(frappe.local.site)
)
cls.test_user_email = 'test_auth@test.com'
cls.test_user_name = 'test_auth_user'
cls.test_user_mobile = '+911234567890'
cls.test_user_password = 'pwd_012'

cls.tearDownClass()
add_user(email=cls.test_user_email, password=cls.test_user_password,
username=cls.test_user_name, mobile_no=cls.test_user_mobile)

@classmethod
def tearDownClass(cls):
frappe.delete_doc('User', cls.test_user_email, force=True)


def set_system_settings(self, k, v): def set_system_settings(self, k, v):
frappe.db.set_value("System Settings", "System Settings", k, v) frappe.db.set_value("System Settings", "System Settings", k, v)
frappe.clear_cache()
frappe.db.commit() frappe.db.commit()


def test_allow_login_using_mobile(self): def test_allow_login_using_mobile(self):
@@ -43,12 +49,12 @@ class TestAuth(unittest.TestCase):
self.set_system_settings('allow_login_using_user_name', 0) self.set_system_settings('allow_login_using_user_name', 0)


# Login by both email and mobile should work # Login by both email and mobile should work
FrappeClient(frappe.get_site_config().host_name, self.test_user_mobile, self.test_user_password)
FrappeClient(frappe.get_site_config().host_name, self.test_user_email, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_mobile, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password)


# login by username should fail # login by username should fail
with self.assertRaises(AuthError): with self.assertRaises(AuthError):
FrappeClient(frappe.get_site_config().host_name, self.test_user_name, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_name, self.test_user_password)


def test_allow_login_using_only_email(self): def test_allow_login_using_only_email(self):
self.set_system_settings('allow_login_using_mobile_number', 0) self.set_system_settings('allow_login_using_mobile_number', 0)
@@ -56,14 +62,14 @@ class TestAuth(unittest.TestCase):


# Login by mobile number should fail # Login by mobile number should fail
with self.assertRaises(AuthError): with self.assertRaises(AuthError):
FrappeClient(frappe.get_site_config().host_name, self.test_user_mobile, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_mobile, self.test_user_password)


# login by username should fail # login by username should fail
with self.assertRaises(AuthError): with self.assertRaises(AuthError):
FrappeClient(frappe.get_site_config().host_name, self.test_user_name, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_name, self.test_user_password)


# Login by email should work # Login by email should work
FrappeClient(frappe.get_site_config().host_name, self.test_user_email, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password)


def test_allow_login_using_username(self): def test_allow_login_using_username(self):
self.set_system_settings('allow_login_using_mobile_number', 0) self.set_system_settings('allow_login_using_mobile_number', 0)
@@ -71,20 +77,39 @@ class TestAuth(unittest.TestCase):


# Mobile login should fail # Mobile login should fail
with self.assertRaises(AuthError): with self.assertRaises(AuthError):
FrappeClient(frappe.get_site_config().host_name, self.test_user_mobile, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_mobile, self.test_user_password)


# Both email and username logins should work # Both email and username logins should work
FrappeClient(frappe.get_site_config().host_name, self.test_user_email, self.test_user_password)
FrappeClient(frappe.get_site_config().host_name, self.test_user_name, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_name, self.test_user_password)


def test_allow_login_using_username_and_mobile(self): def test_allow_login_using_username_and_mobile(self):
self.set_system_settings('allow_login_using_mobile_number', 1) self.set_system_settings('allow_login_using_mobile_number', 1)
self.set_system_settings('allow_login_using_user_name', 1) self.set_system_settings('allow_login_using_user_name', 1)


# Both email and username and mobile logins should work # Both email and username and mobile logins should work
FrappeClient(frappe.get_site_config().host_name, self.test_user_mobile, self.test_user_password)
FrappeClient(frappe.get_site_config().host_name, self.test_user_email, self.test_user_password)
FrappeClient(frappe.get_site_config().host_name, self.test_user_name, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_mobile, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password)
FrappeClient(self.HOST_NAME, self.test_user_name, self.test_user_password)

def test_deny_multiple_login(self):
self.set_system_settings('deny_multiple_sessions', 1)

first_login = FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password)
first_login.get_list("ToDo")

second_login = FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password)
second_login.get_list("ToDo")
with self.assertRaises(Exception):
first_login.get_list("ToDo")

third_login = FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password)
with self.assertRaises(Exception):
first_login.get_list("ToDo")
with self.assertRaises(Exception):
second_login.get_list("ToDo")
third_login.get_list("ToDo")



class TestLoginAttemptTracker(unittest.TestCase): class TestLoginAttemptTracker(unittest.TestCase):
def test_account_lock(self): def test_account_lock(self):


+ 4
- 4
frappe/website/doctype/web_form/templates/web_form.html View File

@@ -3,7 +3,7 @@
{% block title %}{{ _(title) }}{% endblock %} {% block title %}{{ _(title) }}{% endblock %}


{% block header %} {% block header %}
<h2>{{ _(title) }}</h2>
<h3>{{ _(title) }}</h3>
{% endblock %} {% endblock %}


{% block breadcrumbs %} {% block breadcrumbs %}
@@ -29,8 +29,8 @@ data-web-form="{{ name }}" data-web-form-doctype="{{ doc_type }}" data-login-req
{% if is_list %} {% if is_list %}
{# web form list #} {# web form list #}
<div class="web-form-wrapper" {{ container_attributes() }}></div> <div class="web-form-wrapper" {{ container_attributes() }}></div>
<div id="list-filters" class="row"></div>
<div id="datatable" class="pt-4"></div>
<div id="list-filters" class="row mt-4"></div>
<div id="datatable" class="pt-4 overflow-auto"></div>
<div class="list-view-footer text-right"></div> <div class="list-view-footer text-right"></div>
{% else %} {% else %}
{# web form #} {# web form #}
@@ -38,7 +38,7 @@ data-web-form="{{ name }}" data-web-form-doctype="{{ doc_type }}" data-login-req
<div id="introduction" class="text-muted"></div> <div id="introduction" class="text-muted"></div>
<hr> <hr>
<div class="web-form-wrapper" {{ container_attributes() }}></div> <div class="web-form-wrapper" {{ container_attributes() }}></div>
<div class="web-form-footer pull-right"></div>
<div class="web-form-footer text-right"></div>
</div> </div>


{% if show_attachments and not frappe.form_dict.new and attachments %} {% if show_attachments and not frappe.form_dict.new and attachments %}


+ 1
- 1
frappe/website/doctype/web_form/test_web_form.py View File

@@ -66,7 +66,7 @@ class TestWebForm(unittest.TestCase):


def test_webform_render(self): def test_webform_render(self):
content = get_response_content('request-data') content = get_response_content('request-data')
self.assertIn('<h2>Request Data</h2>', content)
self.assertIn('<h3>Request Data</h3>', content)
self.assertIn('data-doctype="Web Form"', content) self.assertIn('data-doctype="Web Form"', content)
self.assertIn('data-path="request-data"', content) self.assertIn('data-path="request-data"', content)
self.assertIn('source-type="Generator"', content) self.assertIn('source-type="Generator"', content)

+ 4
- 6
frappe/www/list.html View File

@@ -5,7 +5,7 @@
{% endblock %} {% endblock %}


{% block header %} {% block header %}
<h1>{{ title or (_("{0} List").format(_(doctype))) }}</h1>
<h3 class="my-account-header">{{ title or (_("{0} List").format(_(doctype))) }}</h3>
{% endblock %} {% endblock %}


{% block breadcrumbs %} {% block breadcrumbs %}
@@ -23,11 +23,9 @@
{% endblock %} {% endblock %}


{% block page_content %} {% block page_content %}

{% if introduction %}<p>{{ introduction }}</p>{% endif %}
{% include list_template or "templates/includes/list/list.html" %}
{% if list_footer %}{{ list_footer }}{% endif %}

{% if introduction %}<p>{{ introduction }}</p>{% endif %}
{% include list_template or "templates/includes/list/list.html" %}
{% if list_footer %}{{ list_footer }}{% endif %}
{% endblock %} {% endblock %}


{% block script %} {% block script %}


+ 80
- 17
frappe/www/me.html View File

@@ -1,31 +1,94 @@
{% from "frappe/templates/includes/avatar_macro.html" import avatar %}
{% extends "templates/web.html" %} {% extends "templates/web.html" %}

{% block title %}{{ _("My Account") }}{% endblock %}
{% block header %}<h1>{{ _("My Account") }}</h1>{% endblock %}

{% block title %}
{{ _("My Account") }}
{% endblock %}
{% block header %}
<h3 class="my-account-header">{{_("My Account") }}</h3>
{% endblock %}
{% block page_content %} {% block page_content %}
<div class="row your-account-info d-none d-sm-block">
<div class="col-sm-4">
<ul class="list-unstyled">
<li><a href="/update-password">{{ _("Reset Password") }}</a></li>
<li><a href="/update-profile?name={{ user }}">{{ _("Edit Profile") }}</a></li>
<li><a href="/third_party_apps">{{ _("Manage Third Party Apps") }}</a></li>
{% if frappe.db.get_single_value("Website Settings", "show_account_deletion_link") %}
<li><a href="/request-for-account-deletion?new=1">{{ _("Request for Account Deletion") }}</a></li>
{% endif %}
</ul>
<div class="row account-info d-flex flex-column">
<div class="col d-flex justify-content-between align-items-center">
<div>
<span class="my-account-avatar">
{{avatar(current_user.name)}}
</span>
<span class="my-account-name ml-4">
{{current_user.full_name }}
</span>
</div>
<div>
<span class="my-account-item-link">
<a href="/update-profile?name={{ user }}">
<svg class="edit-profile-icon icon icon-md">
<use xlink:href="#icon-edit">
</use>
</svg>
<span class="item-link-text pl-2">
{{_("Edit Profile") }}
</span>
</a>
</span>
</div>
</div> </div>
<div class="col d-flex justify-content-between align-items-center">
<span>
<div class="my-account-item">{{_("Reset Password") }}</div>
<div class="my-account-item-desc">{{_("Reset the password for your account") }}</div>
</span>
<span class="my-account-item-link">
<a href="/update-password">
<svg class="right-icon icon icon-md">
<use xlink:href="#icon-right">
</use>
</svg>
<span class="item-link-text">{{_("Reset Password") }}</span>
</a>
</span>
</div>
<div class="col d-flex justify-content-between align-items-center">
<span>
<div class="my-account-item">{{_("Manage third party apps") }}</div>
<div class="my-account-item-desc">{{_("To manage your authorized third party apps") }}</div>
</span>
<span class="my-account-item-link">
<a href="/third_party_apps">
<svg class="right-icon icon icon-md">
<use xlink:href="#icon-right">
</use>
</svg>
<span class="item-link-text">{{_("Manage your apps") }}</span>
</a>
</span>
</div>
{% if frappe.db.get_single_value("Website Settings", "show_account_deletion_link") %}
<div class="col d-flex justify-content-between align-items-center">
<span>
<div class="my-account-item">{{_("Request Account Deletion") }}</div>
<div class="my-account-item-desc">{{_("Send a request to delete your account") }}</div>
</span>
<span class="my-account-item-link">
<a href="/request-for-account-deletion?new=1">
<svg class="right-icon icon icon-md">
<use xlink:href="#icon-right">
</use>
</svg>
<span class="item-link-text">{{_("Delete Account") }}</span>
</a>
</span>
</div>
{% endif %}
</div> </div>
<div class="row d-block d-sm-none"> <div class="row d-block d-sm-none">
<div class="col-12">
<div class="col-12 side-list">
<ul class="list-group"> <ul class="list-group">
{% for item in sidebar_items -%} {% for item in sidebar_items -%}
<a class="list-group-item" href="{{ item.route }}" <a class="list-group-item" href="{{ item.route }}"
{% if item.target %}target="{{ item.target }}"{% endif %}> {% if item.target %}target="{{ item.target }}"{% endif %}>
{{ _(item.title or item.label) }} {{ _(item.title or item.label) }}
</a> </a>
{%- endfor %}
{%- endfor %}
</ul> </ul>
</div> </div>
</div> </div>
{% endblock %}
{% endblock %}

+ 2
- 1
frappe/www/me.py View File

@@ -10,5 +10,6 @@ no_cache = 1
def get_context(context): def get_context(context):
if frappe.session.user=='Guest': if frappe.session.user=='Guest':
frappe.throw(_("You need to be logged in to access this page"), frappe.PermissionError) frappe.throw(_("You need to be logged in to access this page"), frappe.PermissionError)

context.current_user = frappe.get_doc("User", frappe.session.user)
context.show_sidebar=True context.show_sidebar=True

+ 8
- 2
frappe/www/third_party_apps.html View File

@@ -2,7 +2,7 @@


{% block title %} {{ _("Third Party Apps") }} {% endblock %} {% block title %} {{ _("Third Party Apps") }} {% endblock %}
{% block header %} {% block header %}
<h1>{{ _("Third Party Apps") }}</h1>
<h3 class="my-account-header">{{ _("Third Party Apps") }}</h3>
{% endblock %} {% endblock %}


{% block page_sidebar %} {% block page_sidebar %}
@@ -52,9 +52,15 @@
</div> </div>
{% endfor %} {% endfor %}
{% else %} {% else %}
<div class="text-muted">
<div class="empty-apps-state">
<img src="/assets/frappe/images/ui-states/empty-app-state.svg"/>
<div class="font-weight-bold mt-4">
{{ _("No Active Sessions")}} {{ _("No Active Sessions")}}
</div> </div>
<div class="text-muted mt-2">
{{ _("Looks like you haven’t added any third party apps.")}}
</div>
</div>
{% endif %} {% endif %}
<div class="padding"></div> <div class="padding"></div>
<script> <script>


Loading…
Cancel
Save