diff --git a/.github/workflows/docs-checker.yml b/.github/workflows/docs-checker.yml index 90453cd1b4..02a01bf4e4 100644 --- a/.github/workflows/docs-checker.yml +++ b/.github/workflows/docs-checker.yml @@ -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 diff --git a/.github/workflows/publish-assets-develop.yml b/.github/workflows/publish-assets-develop.yml index a23885b508..85f3f7c3b0 100644 --- a/.github/workflows/publish-assets-develop.yml +++ b/.github/workflows/publish-assets-develop.yml @@ -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 diff --git a/.github/workflows/publish-assets-releases.yml b/.github/workflows/publish-assets-releases.yml index a697517c23..a5cc1f8872 100644 --- a/.github/workflows/publish-assets-releases.yml +++ b/.github/workflows/publish-assets-releases.yml @@ -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 diff --git a/cypress/integration/list_view.js b/cypress/integration/list_view.js index 298bb20432..2e865e6a57 100644 --- a/cypress/integration/list_view.js +++ b/cypress/integration/list_view.js @@ -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', () => { }); }); }); - diff --git a/frappe/__init__.py b/frappe/__init__.py index 38904c68d0..64e445973f 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -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 diff --git a/frappe/core/doctype/access_log/access_log.py b/frappe/core/doctype/access_log/access_log.py index d93da02d25..f631353d56 100644 --- a/frappe/core/doctype/access_log/access_log.py +++ b/frappe/core/doctype/access_log/access_log.py @@ -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() diff --git a/frappe/database/database.py b/frappe/database/database.py index c48e86d301..0ee11ea075 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -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")) diff --git a/frappe/desk/doctype/note/note.json b/frappe/desk/doctype/note/note.json index 8d476e83fe..69a9518ac4 100644 --- a/frappe/desk/doctype/note/note.json +++ b/frappe/desk/doctype/note/note.json @@ -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 - } \ No newline at end of file + "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 +} \ No newline at end of file diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index fd74a8cfe4..978f3062c5 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -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 ( diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index dd1d622bab..8afbfa561e 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -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; + } }); } } diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 04ab900859..34eb581f04 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -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(); } } diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 5f2c4a2a2a..1cf4b4c6ac 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -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 = ` - - + ${__(subject_field.label)} @@ -646,7 +647,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
-
@@ -954,9 +955,9 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { let subject_html = ` - - + diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 21841296dc..f534dff1c6 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -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 = $(""); diff --git a/frappe/public/scss/desk/mobile.scss b/frappe/public/scss/desk/mobile.scss index 14fa25e50f..14bee62c74 100644 --- a/frappe/public/scss/desk/mobile.scss +++ b/frappe/public/scss/desk/mobile.scss @@ -202,8 +202,12 @@ body { } // listviews - .list-row { - padding: 13px 15px !important; + .select-like { + margin-right: unset !important; + } + + .list-count { + display: contents; } .doclist-row { diff --git a/frappe/query_builder/__init__.py b/frappe/query_builder/__init__.py index 8798360490..6987ba24ab 100644 --- a/frappe/query_builder/__init__.py +++ b/frappe/query_builder/__init__.py @@ -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 diff --git a/frappe/query_builder/utils.py b/frappe/query_builder/utils.py index 67e2c392f3..c217d0975e 100644 --- a/frappe/query_builder/utils.py +++ b/frappe/query_builder/utils.py @@ -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 diff --git a/frappe/templates/styles/card_style.css b/frappe/templates/styles/card_style.css index cf90ff0bd5..4a2ea38480 100644 --- a/frappe/templates/styles/card_style.css +++ b/frappe/templates/styles/card_style.css @@ -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; } diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py index c18db29259..84d0d31616 100644 --- a/frappe/tests/test_db_query.py +++ b/frappe/tests/test_db_query.py @@ -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() diff --git a/frappe/workflow/doctype/workflow_action/workflow_action.py b/frappe/workflow/doctype/workflow_action/workflow_action.py index a04e9356cd..c1ed7b8957 100644 --- a/frappe/workflow/doctype/workflow_action/workflow_action.py +++ b/frappe/workflow/doctype/workflow_action/workflow_action.py @@ -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 } diff --git a/frappe/www/update-password.html b/frappe/www/update-password.html index d12be86d12..0d66fe5ab5 100644 --- a/frappe/www/update-password.html +++ b/frappe/www/update-password.html @@ -1,32 +1,38 @@ {% extends "templates/web.html" %} {% block title %} {{_("Reset Password")}} {% endblock %} - +{% block head_include %} +{% endblock %} {% block page_content %} - -
-
- {{ _("Reset Password") if frappe.db.get_default('company') else _("Set Password")}} -
-
- -
- - +
+
+
+

{{ _("Reset Password") if frappe.db.get_default('company') else _("Set Password")}}

- - - -
+
+ +
+ + +
+ + +
+ {%- if not disable_signup -%} + + {%- endif -%} +
+ +