@@ -39,7 +39,8 @@ def sync_languages(): | |||
frappe.get_doc({ | |||
'doctype': 'Language', | |||
'language_code': l['code'], | |||
'language_name': l['name'] | |||
'language_name': l['name'], | |||
'enabled': 1, | |||
}).insert() | |||
def update_language_names(): | |||
@@ -12,6 +12,7 @@ | |||
"restrict_to_domain", | |||
"column_break_4", | |||
"disabled", | |||
"is_custom", | |||
"desk_access", | |||
"two_factor_auth", | |||
"navigation_settings_section", | |||
@@ -24,8 +25,7 @@ | |||
"form_settings_section", | |||
"form_sidebar", | |||
"timeline", | |||
"dashboard", | |||
"is_custom" | |||
"dashboard" | |||
], | |||
"fields": [ | |||
{ | |||
@@ -148,7 +148,7 @@ | |||
"idx": 1, | |||
"index_web_pages_for_search": 1, | |||
"links": [], | |||
"modified": "2021-10-08 14:06:55.729364", | |||
"modified": "2022-01-12 20:18:18.496230", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Role", | |||
@@ -170,5 +170,6 @@ | |||
"quick_entry": 1, | |||
"sort_field": "modified", | |||
"sort_order": "ASC", | |||
"states": [], | |||
"track_changes": 1 | |||
} |
@@ -96,7 +96,7 @@ | |||
}, | |||
{ | |||
"fieldname": "authorization_uri", | |||
"fieldtype": "Data", | |||
"fieldtype": "Small Text", | |||
"label": "Authorization URI", | |||
"mandatory_depends_on": "eval:doc.redirect_uri" | |||
}, | |||
@@ -139,7 +139,7 @@ | |||
"link_fieldname": "connected_app" | |||
} | |||
], | |||
"modified": "2021-05-10 05:03:06.296863", | |||
"modified": "2022-01-07 05:28:45.073041", | |||
"modified_by": "Administrator", | |||
"module": "Integrations", | |||
"name": "Connected App", | |||
@@ -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> |
@@ -32,7 +32,9 @@ frappe.ui.form.ControlMarkdownEditor = class ControlMarkdownEditor extends frapp | |||
} | |||
set_language() { | |||
this.df.options = 'Markdown'; | |||
if (!this.df.options) { | |||
this.df.options = 'Markdown'; | |||
} | |||
super.set_language(); | |||
} | |||
@@ -291,11 +291,18 @@ frappe.request.call = function(opts) { | |||
}) | |||
.fail(function(xhr, textStatus) { | |||
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]; | |||
if (exception_handler) { | |||
exception_handler(data); | |||
@@ -139,8 +139,6 @@ export default class WebFormList { | |||
make_table_head() { | |||
// Create Heading | |||
let thead = this.table.createTHead(); | |||
thead.style.backgroundColor = "#f7fafc"; | |||
thead.style.color = "#8d99a6"; | |||
let row = thead.insertRow(); | |||
let th = document.createElement("th"); | |||
@@ -165,6 +165,16 @@ | |||
--bg-pink: var(--pink-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-light-blue: var(--blue-500); | |||
--text-on-dark-blue: var(--blue-700); | |||
@@ -4,15 +4,6 @@ $input-height: 28px !default; | |||
:root, | |||
[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 | |||
--xxl-width: map-get($grid-breakpoints, '2xl'); | |||
--xl-width: map-get($grid-breakpoints, 'xl'); | |||
@@ -1,7 +1,9 @@ | |||
@import "./desk/variables"; | |||
body { | |||
background-color: var(--bg-light-gray); | |||
@include media-breakpoint-up(sm) { | |||
background-color: var(--bg-light-gray); | |||
} | |||
} | |||
.for-forgot, | |||
@@ -94,6 +94,8 @@ | |||
max-width: 300px; | |||
border: 1px solid var(--dark-border-color); | |||
box-shadow: none; | |||
border-radius: var(--border-radius); | |||
font-size: $font-size-sm; | |||
} | |||
} | |||
} |
@@ -27,6 +27,14 @@ | |||
@import 'navbar'; | |||
@import 'footer'; | |||
@import 'error-state'; | |||
@import 'my_account'; | |||
body { | |||
@include media-breakpoint-up(sm) { | |||
background-color: var(--bg-color); | |||
} | |||
} | |||
.ql-editor.read-mode { | |||
padding: 0; | |||
@@ -166,6 +174,10 @@ a.card { | |||
font-size: inherit; | |||
} | |||
.indicator-pill { | |||
font-size: var(--font-size-xs) | |||
} | |||
h4.modal-title { | |||
font-size: 1em; | |||
} | |||
@@ -298,3 +310,7 @@ h5.modal-title { | |||
margin: 70px auto; | |||
font-size: $font-size-sm; | |||
} | |||
.empty-list-icon { | |||
height: 70px; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -1,5 +1,31 @@ | |||
@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 { | |||
.form-control { | |||
color: var(--text-color); | |||
@@ -16,6 +42,7 @@ | |||
.form-column { | |||
padding: 0 var(--padding-md); | |||
&:first-child { | |||
padding-left: 0; | |||
} | |||
@@ -24,4 +51,24 @@ | |||
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); | |||
} | |||
} | |||
} | |||
} |
@@ -68,9 +68,14 @@ def get_sessions_to_clear(user=None, keep_current=False, device=None): | |||
session = DocType("Sessions") | |||
session_id = frappe.qb.from_(session).where((session.user == user) & (session.device.isin(device))) | |||
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) | |||
@@ -2,8 +2,9 @@ | |||
<h4 class="text-muted">{{ sub_title }}</h4> | |||
{% endif %} | |||
{% 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> | |||
{% else %} | |||
<div class="website-list" data-doctype="{{ doctype }}" | |||
@@ -2,79 +2,87 @@ | |||
background-color: var(--bg-color); | |||
} | |||
body { | |||
background-color: var(--bg-color); | |||
} | |||
.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 { | |||
margin: 80px 0; | |||
} | |||
margin: 80px 0; | |||
} | |||
.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 { | |||
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 { | |||
font-size: 18px; | |||
font-weight: 600; | |||
font-size: 18px; | |||
font-weight: 600; | |||
} | |||
#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 { | |||
color: #36414C; | |||
font-size: 14px; | |||
color: #36414C; | |||
font-size: 14px; | |||
} | |||
.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 { | |||
margin: 0 6px 0.5px 0px; | |||
margin: 0 6px 0.5px 0px; | |||
} | |||
button#update { | |||
font-size: var(--font-size-sm); | |||
font-size: var(--font-size-sm); | |||
} | |||
.page-card .btn { | |||
margin-top: 30px; | |||
margin-top: 30px; | |||
} | |||
.page-card p { | |||
font-size: 14px; | |||
font-size: 14px; | |||
} | |||
.ellipsis { | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
max-width: 100%; | |||
vertical-align: middle; | |||
} | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
max-width: 100%; | |||
vertical-align: middle; | |||
} |
@@ -4,38 +4,44 @@ import time | |||
import unittest | |||
import frappe | |||
from frappe.auth import HTTPRequest, LoginAttemptTracker | |||
import frappe.utils | |||
from frappe.auth import LoginAttemptTracker | |||
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): | |||
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): | |||
frappe.db.set_value("System Settings", "System Settings", k, v) | |||
frappe.clear_cache() | |||
frappe.db.commit() | |||
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) | |||
# 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 | |||
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): | |||
self.set_system_settings('allow_login_using_mobile_number', 0) | |||
@@ -56,14 +62,14 @@ class TestAuth(unittest.TestCase): | |||
# Login by mobile number should fail | |||
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 | |||
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 | |||
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): | |||
self.set_system_settings('allow_login_using_mobile_number', 0) | |||
@@ -71,20 +77,39 @@ class TestAuth(unittest.TestCase): | |||
# Mobile login should fail | |||
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 | |||
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): | |||
self.set_system_settings('allow_login_using_mobile_number', 1) | |||
self.set_system_settings('allow_login_using_user_name', 1) | |||
# 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): | |||
def test_account_lock(self): | |||
@@ -3,7 +3,7 @@ | |||
{% block title %}{{ _(title) }}{% endblock %} | |||
{% block header %} | |||
<h2>{{ _(title) }}</h2> | |||
<h3>{{ _(title) }}</h3> | |||
{% endblock %} | |||
{% block breadcrumbs %} | |||
@@ -29,8 +29,8 @@ data-web-form="{{ name }}" data-web-form-doctype="{{ doc_type }}" data-login-req | |||
{% if is_list %} | |||
{# web form list #} | |||
<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> | |||
{% else %} | |||
{# 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> | |||
<hr> | |||
<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> | |||
{% if show_attachments and not frappe.form_dict.new and attachments %} | |||
@@ -66,7 +66,7 @@ class TestWebForm(unittest.TestCase): | |||
def test_webform_render(self): | |||
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-path="request-data"', content) | |||
self.assertIn('source-type="Generator"', content) |
@@ -5,7 +5,7 @@ | |||
{% endblock %} | |||
{% block header %} | |||
<h1>{{ title or (_("{0} List").format(_(doctype))) }}</h1> | |||
<h3 class="my-account-header">{{ title or (_("{0} List").format(_(doctype))) }}</h3> | |||
{% endblock %} | |||
{% block breadcrumbs %} | |||
@@ -23,11 +23,9 @@ | |||
{% endblock %} | |||
{% 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 %} | |||
{% block script %} | |||
@@ -1,31 +1,94 @@ | |||
{% from "frappe/templates/includes/avatar_macro.html" import avatar %} | |||
{% 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 %} | |||
<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 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 class="row d-block d-sm-none"> | |||
<div class="col-12"> | |||
<div class="col-12 side-list"> | |||
<ul class="list-group"> | |||
{% for item in sidebar_items -%} | |||
<a class="list-group-item" href="{{ item.route }}" | |||
{% if item.target %}target="{{ item.target }}"{% endif %}> | |||
{{ _(item.title or item.label) }} | |||
</a> | |||
{%- endfor %} | |||
{%- endfor %} | |||
</ul> | |||
</div> | |||
</div> | |||
{% endblock %} | |||
{% endblock %} |
@@ -10,5 +10,6 @@ no_cache = 1 | |||
def get_context(context): | |||
if frappe.session.user=='Guest': | |||
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 |
@@ -2,7 +2,7 @@ | |||
{% block title %} {{ _("Third Party Apps") }} {% endblock %} | |||
{% block header %} | |||
<h1>{{ _("Third Party Apps") }}</h1> | |||
<h3 class="my-account-header">{{ _("Third Party Apps") }}</h3> | |||
{% endblock %} | |||
{% block page_sidebar %} | |||
@@ -52,9 +52,15 @@ | |||
</div> | |||
{% endfor %} | |||
{% 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")}} | |||
</div> | |||
<div class="text-muted mt-2"> | |||
{{ _("Looks like you haven’t added any third party apps.")}} | |||
</div> | |||
</div> | |||
{% endif %} | |||
<div class="padding"></div> | |||
<script> | |||