Browse Source

Merge branch 'develop' of https://github.com/frappe/frappe into form-tab-break

version-14
Suraj Shetty 3 years ago
parent
commit
2f427a82c8
22 changed files with 319 additions and 407 deletions
  1. +1
    -1
      .github/workflows/docs-checker.yml
  2. +1
    -1
      .github/workflows/publish-assets-develop.yml
  3. +1
    -1
      .github/workflows/publish-assets-releases.yml
  4. +22
    -1
      cypress/integration/list_view.js
  5. +2
    -1
      frappe/__init__.py
  6. +32
    -16
      frappe/core/doctype/access_log/access_log.py
  7. +1
    -1
      frappe/database/database.py
  8. +105
    -321
      frappe/desk/doctype/note/note.json
  9. +7
    -2
      frappe/model/db_query.py
  10. +14
    -12
      frappe/public/js/frappe/form/grid.js
  11. +2
    -1
      frappe/public/js/frappe/form/layout.js
  12. +6
    -5
      frappe/public/js/frappe/list/list_view.js
  13. +10
    -1
      frappe/public/js/frappe/utils/utils.js
  14. +6
    -2
      frappe/public/scss/desk/mobile.scss
  15. +1
    -1
      frappe/query_builder/__init__.py
  16. +2
    -0
      frappe/query_builder/utils.py
  17. +47
    -5
      frappe/templates/styles/card_style.css
  18. +20
    -0
      frappe/tests/test_db_query.py
  19. +4
    -3
      frappe/workflow/doctype/workflow_action/workflow_action.py
  20. +33
    -29
      frappe/www/update-password.html
  21. +1
    -2
      requirements.txt
  22. +1
    -1
      setup.py

+ 1
- 1
.github/workflows/docs-checker.yml View File

@@ -12,7 +12,7 @@ jobs:
- name: 'Setup Environment'
uses: actions/setup-python@v2
with:
python-version: 3.6
python-version: 3.7

- name: 'Clone repo'
uses: actions/checkout@v2


+ 1
- 1
.github/workflows/publish-assets-develop.yml View File

@@ -18,7 +18,7 @@ jobs:
node-version: 14
- uses: actions/setup-python@v2
with:
python-version: '3.6'
python-version: '3.7'
- name: Set up bench and build assets
run: |
npm install -g yarn


+ 1
- 1
.github/workflows/publish-assets-releases.yml View File

@@ -21,7 +21,7 @@ jobs:
python-version: '12.x'
- uses: actions/setup-python@v2
with:
python-version: '3.6'
python-version: '3.7'
- name: Set up bench and build assets
run: |
npm install -g yarn


+ 22
- 1
cypress/integration/list_view.js View File

@@ -6,6 +6,28 @@ context('List View', () => {
return frappe.xcall("frappe.tests.ui_test_helpers.setup_workflow");
});
});

it('Keep checkbox checked after Bulk Update', () => {
cy.go_to_list('ToDo');
cy.get('.list-row-container .list-row-checkbox').click({ multiple: true, force: true });
cy.get('.actions-btn-group button').contains('Actions').should('be.visible').click();
cy.get('.dropdown-menu li:visible .dropdown-item .menu-item-label[data-label="Edit"]').click();

cy.get('.modal-body .form-control[data-fieldname="field"]').first().select('Due Date').wait(200);
cy.get('.modal-body .frappe-control[data-fieldname="value"] input:visible').first().focus();
cy.get('.datepickers-container .datepicker.active').should('be.visible');

cy.get('.datepickers-container .datepicker.active .datepicker--cell-day.-current-').click({force: true});
cy.get('.modal-body .frappe-control[data-fieldname="value"] input:visible').first().focus();
cy.get('.datepickers-container .datepicker.active .datepicker--cell-day.-current-').click({force: true});

cy.get('.modal-footer .standard-actions .btn-primary').click();
cy.wait(500);

cy.get('.actions-btn-group button').contains('Actions').should('be.visible').click();
cy.get('.list-row-container .list-row-checkbox:checked').should('be.visible');
});

it('enables "Actions" button', () => {
const actions = ['Approve', 'Reject', 'Edit', 'Export', 'Assign To', 'Apply Assignment Rule', 'Add Tags', 'Print', 'Delete'];
cy.go_to_list('ToDo');
@@ -30,4 +52,3 @@ context('List View', () => {
});
});
});


+ 2
- 1
frappe/__init__.py View File

@@ -235,12 +235,13 @@ def connect_replica():
from frappe.database import get_db
user = local.conf.db_name
password = local.conf.db_password
port = local.conf.replica_db_port

if local.conf.different_credentials_for_replica:
user = local.conf.replica_db_name
password = local.conf.replica_db_password

local.replica_db = get_db(host=local.conf.replica_host, user=user, password=password)
local.replica_db = get_db(host=local.conf.replica_host, user=user, password=password, port=port)

# swap db connections
local.primary_db = local.db


+ 32
- 16
frappe/core/doctype/access_log/access_log.py View File

@@ -1,6 +1,7 @@
# Copyright (c) 2019, Frappe Technologies and contributors
# Copyright (c) 2021, Frappe Technologies and contributors
# License: MIT. See LICENSE
import frappe
from tenacity import retry, retry_if_exception_type, stop_after_attempt
from frappe.model.document import Document


@@ -10,25 +11,40 @@ class AccessLog(Document):

@frappe.whitelist()
@frappe.write_only()
def make_access_log(doctype=None, document=None, method=None, file_type=None,
report_name=None, filters=None, page=None, columns=None):
@retry(
stop=stop_after_attempt(3), retry=retry_if_exception_type(frappe.DuplicateEntryError)
)
def make_access_log(
doctype=None,
document=None,
method=None,
file_type=None,
report_name=None,
filters=None,
page=None,
columns=None,
):

user = frappe.session.user
in_request = frappe.request and frappe.request.method == "GET"

doc = frappe.get_doc({
'doctype': 'Access Log',
'user': user,
'export_from': doctype,
'reference_document': document,
'file_type': file_type,
'report_name': report_name,
'page': page,
'method': method,
'filters': frappe.utils.cstr(filters) if filters else None,
'columns': columns
})
doc = frappe.get_doc(
{
"doctype": "Access Log",
"user": user,
"export_from": doctype,
"reference_document": document,
"file_type": file_type,
"report_name": report_name,
"page": page,
"method": method,
"filters": frappe.utils.cstr(filters) if filters else None,
"columns": columns,
}
)
doc.insert(ignore_permissions=True)

# `frappe.db.commit` added because insert doesnt `commit` when called in GET requests like `printview`
if frappe.request and frappe.request.method == 'GET':
# dont commit in test mode
if not frappe.flags.in_test or in_request:
frappe.db.commit()

+ 1
- 1
frappe/database/database.py View File

@@ -332,7 +332,7 @@ class Database(object):
values[key] = value
if isinstance(value, (list, tuple)):
# value is a tuple like ("!=", 0)
_operator = value[0]
_operator = value[0].lower()
values[key] = value[1]
if isinstance(value[1], (tuple, list)):
# value is a list in tuple ("in", ("A", "B"))


+ 105
- 321
frappe/desk/doctype/note/note.json View File

@@ -1,322 +1,106 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 1,
"beta": 0,
"creation": "2013-05-24 13:41:00",
"custom": 0,
"description": "",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "title",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Title",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "public",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Public",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"depends_on": "public",
"fieldname": "notify_on_login",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Notify users with a popup when they log in",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"default": "0",
"depends_on": "notify_on_login",
"description": "If enabled, users will be notified every time they login. If not enabled, users will only be notified once.",
"fieldname": "notify_on_every_login",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Notify Users On Every Login",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.notify_on_login && doc.public",
"fieldname": "expire_notification_on",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Expire Notification On",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"description": "Help: To link to another record in the system, use \"#Form/Note/[Note Name]\" as the Link URL. (don't use \"http://\")",
"fieldname": "content",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Content",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "seen_by_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Seen By",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "seen_by",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Seen By Table",
"length": 0,
"no_copy": 0,
"options": "Note Seen By",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-file-text",
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-09-21 15:15:44.909636",
"modified_by": "Administrator",
"module": "Desk",
"name": "Note",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 1,
"show_name_in_global_search": 0,
"sort_order": "ASC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}
"actions": [],
"allow_rename": 1,
"creation": "2013-05-24 13:41:00",
"doctype": "DocType",
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
"title",
"public",
"notify_on_login",
"notify_on_every_login",
"expire_notification_on",
"content",
"seen_by_section",
"seen_by"
],
"fields": [
{
"fieldname": "title",
"fieldtype": "Data",
"in_global_search": 1,
"in_list_view": 1,
"label": "Title",
"no_copy": 1,
"print_hide": 1,
"reqd": 1
},
{
"bold": 1,
"default": "0",
"fieldname": "public",
"fieldtype": "Check",
"label": "Public",
"print_hide": 1
},
{
"bold": 1,
"default": "0",
"depends_on": "public",
"fieldname": "notify_on_login",
"fieldtype": "Check",
"label": "Notify users with a popup when they log in"
},
{
"bold": 1,
"default": "0",
"depends_on": "notify_on_login",
"description": "If enabled, users will be notified every time they login. If not enabled, users will only be notified once.",
"fieldname": "notify_on_every_login",
"fieldtype": "Check",
"label": "Notify Users On Every Login"
},
{
"depends_on": "eval:doc.notify_on_login && doc.public",
"fieldname": "expire_notification_on",
"fieldtype": "Date",
"label": "Expire Notification On",
"search_index": 1
},
{
"bold": 1,
"description": "Help: To link to another record in the system, use \"/app/note/[Note Name]\" as the Link URL. (don't use \"http://\")",
"fieldname": "content",
"fieldtype": "Text Editor",
"in_global_search": 1,
"label": "Content"
},
{
"collapsible": 1,
"fieldname": "seen_by_section",
"fieldtype": "Section Break",
"label": "Seen By"
},
{
"fieldname": "seen_by",
"fieldtype": "Table",
"label": "Seen By Table",
"options": "Note Seen By"
}
],
"icon": "fa fa-file-text",
"idx": 1,
"links": [],
"modified": "2021-09-18 10:57:51.352643",
"modified_by": "Administrator",
"module": "Desk",
"name": "Note",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"role": "All",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1
}

+ 7
- 2
frappe/model/db_query.py View File

@@ -4,6 +4,7 @@

from typing import List
import frappe.defaults
from frappe.query_builder.utils import Column
import frappe.share
from frappe import _
import frappe.permissions
@@ -491,7 +492,7 @@ class DatabaseQuery(object):
f.value = date_range
fallback = "'0001-01-01 00:00:00'"

if f.operator in ('>', '<') and (f.fieldname in ('creation', 'modified')):
if (f.fieldname in ('creation', 'modified')):
value = cstr(f.value)
fallback = "NULL"

@@ -547,8 +548,12 @@ class DatabaseQuery(object):
value = flt(f.value)
fallback = 0

if isinstance(f.value, Column):
quote = '"' if frappe.conf.db_type == 'postgres' else "`"
value = f"{tname}.{quote}{f.value.name}{quote}"

# escape value
if isinstance(value, str) and not f.operator.lower() == 'between':
elif isinstance(value, str) and not f.operator.lower() == 'between':
value = f"{frappe.db.escape(value, percent=False)}"

if (


+ 14
- 12
frappe/public/js/frappe/form/grid.js View File

@@ -212,13 +212,12 @@ export default class Grid {

delete_all_rows() {
frappe.confirm(__("Are you sure you want to delete all rows?"), () => {
this.grid_rows.forEach(row => {
row.remove();
});
this.frm.script_manager.trigger(this.df.fieldname + "_delete", this.doctype);

this.wrapper.find('.grid-heading-row .grid-row-check:checked:first').prop('checked', 0);
this.frm.doc[this.df.fieldname] = [];
$(this.parent).find('.rows').empty();
this.grid_rows = [];
this.refresh();
this.frm && this.frm.script_manager.trigger(this.df.fieldname + "_delete", this.doctype);
this.frm && this.frm.dirty();
this.scroll_to_top();
});
}
@@ -244,8 +243,10 @@ export default class Grid {

this.remove_rows_button.toggleClass('hidden',
this.wrapper.find('.grid-body .grid-row-check:checked:first').length ? false : true);
this.remove_all_rows_button.toggleClass('hidden',
this.wrapper.find('.grid-heading-row .grid-row-check:checked:first').length ? false : true);

let select_all_checkbox_checked = this.wrapper.find('.grid-heading-row .grid-row-check:checked:first').length;
let show_delete_all_btn = select_all_checkbox_checked && this.data.length > this.get_selected_children().length;
this.remove_all_rows_button.toggleClass('hidden', !show_delete_all_btn);
}

get_selected() {
@@ -835,10 +836,11 @@ export default class Grid {
$.each(row, (ci, value) => {
var fieldname = fieldnames[ci];
var df = frappe.meta.get_docfield(me.df.options, fieldname);

d[fieldnames[ci]] = value_formatter_map[df.fieldtype]
? value_formatter_map[df.fieldtype](value)
: value;
if (df) {
d[fieldnames[ci]] = value_formatter_map[df.fieldtype]
? value_formatter_map[df.fieldtype](value)
: value;
}
});
}
}


+ 2
- 1
frappe/public/js/frappe/form/layout.js View File

@@ -483,7 +483,8 @@ frappe.ui.form.Layout = class Layout {
// next row
grid_row.grid.grid_rows[grid_row.doc.idx].toggle_view(true);
}
} else {
} else if (!shift) {
// End of tab navigation
$(this.primary_button).focus();
}
}


+ 6
- 5
frappe/public/js/frappe/list/list_view.js View File

@@ -572,6 +572,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {

render() {
this.render_list();
this.set_rows_as_checked();
this.on_row_checked();
this.render_count();
}
@@ -607,9 +608,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {

const subject_field = this.columns[0].df;
let subject_html = `
<input class="level-item list-check-all hidden-xs" type="checkbox"
<input class="level-item list-check-all" type="checkbox"
title="${__("Select All")}">
<span class="level-item list-liked-by-me">
<span class="level-item list-liked-by-me hidden-xs">
<span title="${__("Likes")}">${frappe.utils.icon('heart', 'sm', 'like-icon')}</span>
</span>
<span class="level-item">${__(subject_field.label)}</span>
@@ -646,7 +647,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
</div>
<div class="level-left checkbox-actions">
<div class="level list-subject">
<input class="level-item list-check-all hidden-xs" type="checkbox"
<input class="level-item list-check-all" type="checkbox"
title="${__("Select All")}">
<span class="level-item list-header-meta"></span>
</div>
@@ -954,9 +955,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {

let subject_html = `
<span class="level-item select-like">
<input class="list-row-checkbox hidden-xs" type="checkbox"
<input class="list-row-checkbox" type="checkbox"
data-name="${escape(doc.name)}">
<span class="list-row-like style="margin-bottom: 1px;">
<span class="list-row-like hidden-xs style="margin-bottom: 1px;">
${this.get_like_html(doc)}
</span>
</span>


+ 10
- 1
frappe/public/js/frappe/utils/utils.js View File

@@ -927,7 +927,16 @@ Object.assign(frappe.utils, {
// decodes base64 to string
let parts = dataURI.split(',');
const encoded_data = parts[1];
return decodeURIComponent(escape(atob(encoded_data)));
let decoded = atob(encoded_data);
try {
const escaped = escape(decoded);
decoded = decodeURIComponent(escaped);

} catch (e) {
// pass decodeURIComponent failure
// just return atob response
}
return decoded;
},
copy_to_clipboard(string) {
let input = $("<input>");


+ 6
- 2
frappe/public/scss/desk/mobile.scss View File

@@ -202,8 +202,12 @@ body {
}

// listviews
.list-row {
padding: 13px 15px !important;
.select-like {
margin-right: unset !important;
}

.list-count {
display: contents;
}

.doclist-row {


+ 1
- 1
frappe/query_builder/__init__.py View File

@@ -1,2 +1,2 @@
from pypika import *
from frappe.query_builder.utils import get_query_builder, patch_query_execute
from frappe.query_builder.utils import Column, get_query_builder, patch_query_execute

+ 2
- 0
frappe/query_builder/utils.py View File

@@ -3,8 +3,10 @@ from typing import Any, Callable, Dict, get_type_hints
from importlib import import_module

from pypika import Query
from pypika.queries import Column

import frappe

from .builder import MariaDB, Postgres




+ 47
- 5
frappe/templates/styles/card_style.css View File

@@ -1,31 +1,73 @@
.hero-and-content {
background-color: #f5f7fa;
background-color: var(--bg-color);
}

body {
background-color: var(--bg-color);
}
.page-card {
max-width: 360px;
padding: 15px;
margin: 70px auto;
border: 1px solid #d1d8dd;
border-radius: 4px;
background-color: #fff;
box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.1);
background-color: var(--fg-color);
box-shadow: var(--shadow-base);
}

.for-reset-password {
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);
}

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

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

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

#reset-password .form-group {
margin-bottom: 10px;
font-size: var(--font-size-sm);
}
.page-card .page-card-head .indicator {
color: #36414C;
font-size: 14px;
}

.sign-up-message {
margin-top: 20px;
font-size: 13px;
color: var(--text-color);
}
.page-card .page-card-head .indicator::before {
margin: 0 6px 0.5px 0px;
}

button#update {
font-size: var(--font-size-sm);
}
.page-card .btn {
margin-top: 30px;
}

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


+ 20
- 0
frappe/tests/test_db_query.py View File

@@ -4,6 +4,7 @@ import frappe, unittest

from frappe.model.db_query import DatabaseQuery
from frappe.desk.reportview import get_filters_cond
from frappe.query_builder import Column

from frappe.core.page.permission_manager.permission_manager import update, reset, add
from frappe.permissions import add_user_permission, clear_user_permissions_for_doctype
@@ -373,6 +374,25 @@ class TestReportview(unittest.TestCase):
owners = DatabaseQuery("DocType").execute(filters={"name": "DocType"}, pluck="owner")
self.assertEqual(owners, ["Administrator"])

def test_column_comparison(self):
"""Test DatabaseQuery.execute to test column comparison
"""
users_unedited = frappe.get_all(
"User",
filters={"creation": Column("modified")},
fields=["name", "creation", "modified"],
limit=1,
)
users_edited = frappe.get_all(
"User",
filters={"creation": ("!=", Column("modified"))},
fields=["name", "creation", "modified"],
limit=1,
)

self.assertEqual(users_unedited[0].modified, users_unedited[0].creation)
self.assertNotEqual(users_edited[0].modified, users_edited[0].creation)

def test_reportview_get(self):
user = frappe.get_doc("User", "test@example.com")
add_child_table_to_blog_post()


+ 4
- 3
frappe/workflow/doctype/workflow_action/workflow_action.py View File

@@ -9,9 +9,10 @@ from frappe.desk.form.utils import get_pdf_link
from frappe.utils.verified_command import get_signed_params, verify_request
from frappe import _
from frappe.model.workflow import apply_workflow, get_workflow_name, has_approval_access, \
get_workflow_state_field, send_email_alert, get_workflow_field_value, is_transition_condition_satisfied
get_workflow_state_field, send_email_alert, is_transition_condition_satisfied
from frappe.desk.notifications import clear_doctype_notifications
from frappe.utils.user import get_users_with_role
from frappe.utils.data import get_link_to_form

class WorkflowAction(Document):
pass
@@ -287,12 +288,12 @@ def get_common_email_args(doc):
response = frappe.render_template(email_template.response, vars(doc))
else:
subject = _('Workflow Action')
response = _('{0}: {1}').format(doctype, docname)
response = get_link_to_form(doctype, docname, f"{doctype}: {docname}")

common_args = {
'template': 'workflow_action',
'header': 'Workflow Action',
'attachments': [frappe.attach_print(doctype, docname , file_name=docname)],
'attachments': [frappe.attach_print(doctype, docname, file_name=docname, doc=doc)],
'subject': subject,
'message': response
}


+ 33
- 29
frappe/www/update-password.html View File

@@ -1,32 +1,38 @@
{% extends "templates/web.html" %}

{% block title %} {{_("Reset Password")}} {% endblock %}

{% block head_include %}
{% endblock %}
{% block page_content %}

<div class="page-card">
<div class='page-card-head'>
<span class='indicator blue password-box'>{{ _("Reset Password") if frappe.db.get_default('company') else _("Set Password")}}</span>
</div>
<form id="reset-password">
<div class="form-group" style="display: none;">
<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>
<section class="for-reset-password d-block">
<div class="page-card">
<div class='page-card-head text-center'>
<h4 class="reset-password-heading">{{ _("Reset Password") if frappe.db.get_default('company') else _("Set Password")}}</h4>
</div>
<p class='password-strength-message text-muted small hidden'></p>
<button type="submit" id="update"
class="btn btn-primary">{{_("Update")}}</button>
</form>
</div>
<form id="reset-password">
<div class="form-group" style="display: none;">
<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 btn-block btn-update">{{_("Confirm")}}</button>
</form>
{%- if not disable_signup -%}
<div class="text-center sign-up-message">
{{ _("Don't have an account?") }}
<a href="/login#signup">{{ _("Sign up") }}</a>
</div>
{%- endif -%}
</div>
</section>
<style>
.hero-and-content {
background-color: #f5f7fa;
}
</style>

<script>
@@ -69,20 +75,19 @@ frappe.ready(function() {
args: args,
statusCode: {
401: function() {
$(".page-card-head .indicator").removeClass().addClass("indicator red").text(__("Invalid Password"));
$(".page-card-head .reset-password-heading").text(__("Invalid Password"));
},
410: function({ responseJSON }) {
const title = __("Invalid Link");
const message = responseJSON.message;
$(".page-card-head .indicator").removeClass().addClass("indicator grey").text(title);
$(".page-card-head .reset-password-heading").text(title);
frappe.msgprint({ title: title, message: message, clear: true });
},
200: function(r) {
$("input").val("");
strength_indicator.addClass("hidden");
strength_message.addClass("hidden");
$(".page-card-head .indicator")
.removeClass().addClass("indicator blue")
$(".page-card-head .reset-password-heading")
.html(__("Status Updated"));
if(r.message) {
frappe.msgprint({
@@ -132,7 +137,7 @@ frappe.ready(function() {
},
statusCode: {
401: function() {
$('.page-card-head .indicator').removeClass().addClass('indicator red')
$('.page-card-head .reset-password-heading')
.text("{{ _('Invalid Password') }}");
},
200: function(r) {
@@ -175,7 +180,6 @@ frappe.ready(function() {
}
}

strength_indicator.removeClass().addClass('password-strength-indicator indicator ' + color);
strength_message.html(message.join(' ') || '').removeClass('hidden');
// strength_indicator.attr('title', message.join(' ') || '');
}


+ 1
- 2
requirements.txt View File

@@ -24,8 +24,7 @@ googlemaps~=4.4.5
gunicorn~=20.1.0
html2text==2020.1.16
html5lib~=1.1
ipython~=7.16.1
jedi==0.17.2 # not directly required. Pinned to fix upstream IPython issue (https://github.com/ipython/ipython/issues/12740)
ipython~=7.27.0
Jinja2~=3.0.1
ldap3~=2.9
markdown2~=2.4.0


+ 1
- 1
setup.py View File

@@ -57,5 +57,5 @@ setup(
{
'clean': CleanCommand
},
python_requires='>=3.6'
python_requires='>=3.7'
)

Loading…
Cancel
Save