chore: weekly version-14 releaseversion-14
@@ -17,39 +17,57 @@ def fetch_pr_data(pr_number, repo, endpoint=""): | |||||
req = urllib.request.Request(api_url) | req = urllib.request.Request(api_url) | ||||
res = urllib.request.urlopen(req) | res = urllib.request.urlopen(req) | ||||
return json.loads(res.read().decode('utf8')) | |||||
return json.loads(res.read().decode("utf8")) | |||||
def get_files_list(pr_number, repo="frappe/frappe"): | def get_files_list(pr_number, repo="frappe/frappe"): | ||||
return [change["filename"] for change in fetch_pr_data(pr_number, repo, "files")] | return [change["filename"] for change in fetch_pr_data(pr_number, repo, "files")] | ||||
def get_output(command, shell=True): | def get_output(command, shell=True): | ||||
print(command) | print(command) | ||||
command = shlex.split(command) | command = shlex.split(command) | ||||
return subprocess.check_output(command, shell=shell, encoding="utf8").strip() | return subprocess.check_output(command, shell=shell, encoding="utf8").strip() | ||||
def has_skip_ci_label(pr_number, repo="frappe/frappe"): | def has_skip_ci_label(pr_number, repo="frappe/frappe"): | ||||
return has_label(pr_number, "Skip CI", repo) | return has_label(pr_number, "Skip CI", repo) | ||||
def has_run_server_tests_label(pr_number, repo="frappe/frappe"): | def has_run_server_tests_label(pr_number, repo="frappe/frappe"): | ||||
return has_label(pr_number, "Run Server Tests", repo) | return has_label(pr_number, "Run Server Tests", repo) | ||||
def has_run_ui_tests_label(pr_number, repo="frappe/frappe"): | def has_run_ui_tests_label(pr_number, repo="frappe/frappe"): | ||||
return has_label(pr_number, "Run UI Tests", repo) | return has_label(pr_number, "Run UI Tests", repo) | ||||
def has_label(pr_number, label, repo="frappe/frappe"): | def has_label(pr_number, label, repo="frappe/frappe"): | ||||
return any([fetched_label["name"] for fetched_label in fetch_pr_data(pr_number, repo)["labels"] if fetched_label["name"] == label]) | |||||
return any( | |||||
[ | |||||
fetched_label["name"] | |||||
for fetched_label in fetch_pr_data(pr_number, repo)["labels"] | |||||
if fetched_label["name"] == label | |||||
] | |||||
) | |||||
def is_py(file): | def is_py(file): | ||||
return file.endswith("py") | return file.endswith("py") | ||||
def is_ci(file): | def is_ci(file): | ||||
return ".github" in file | return ".github" in file | ||||
def is_frontend_code(file): | def is_frontend_code(file): | ||||
return file.lower().endswith((".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue", ".html")) | |||||
return file.lower().endswith( | |||||
(".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue", ".html") | |||||
) | |||||
def is_docs(file): | def is_docs(file): | ||||
regex = re.compile(r'\.(md|png|jpg|jpeg|csv|svg)$|^.github|LICENSE') | |||||
regex = re.compile(r"\.(md|png|jpg|jpeg|csv|svg)$|^.github|LICENSE") | |||||
return bool(regex.search(file)) | return bool(regex.search(file)) | ||||
@@ -78,8 +96,13 @@ if __name__ == "__main__": | |||||
only_py_changed = updated_py_file_count == len(files_list) | only_py_changed = updated_py_file_count == len(files_list) | ||||
if has_skip_ci_label(pr_number, repo): | if has_skip_ci_label(pr_number, repo): | ||||
print("Found `Skip CI` label on pr, stopping build process.") | |||||
sys.exit(0) | |||||
if build_type == "ui" and has_run_ui_tests_label(pr_number, repo): | |||||
print("Running UI tests only.") | |||||
elif build_type == "server" and has_run_server_tests_label(pr_number, repo): | |||||
print("Running server tests only.") | |||||
else: | |||||
print("Found `Skip CI` label on pr, stopping build process.") | |||||
sys.exit(0) | |||||
elif ci_files_changed: | elif ci_files_changed: | ||||
print("CI related files were updated, running all build processes.") | print("CI related files were updated, running all build processes.") | ||||
@@ -88,7 +111,11 @@ if __name__ == "__main__": | |||||
print("Only docs were updated, stopping build process.") | print("Only docs were updated, stopping build process.") | ||||
sys.exit(0) | sys.exit(0) | ||||
elif only_frontend_code_changed and build_type == "server" and not has_run_server_tests_label(pr_number, repo): | |||||
elif ( | |||||
only_frontend_code_changed | |||||
and build_type == "server" | |||||
and not has_run_server_tests_label(pr_number, repo) | |||||
): | |||||
print("Only Frontend code was updated; Stopping Python build process.") | print("Only Frontend code was updated; Stopping Python build process.") | ||||
sys.exit(0) | sys.exit(0) | ||||
@@ -22,8 +22,6 @@ jobs: | |||||
strategy: | strategy: | ||||
fail-fast: false | fail-fast: false | ||||
matrix: | |||||
container: [1, 2] | |||||
services: | services: | ||||
mariadb: | mariadb: | ||||
@@ -122,7 +120,4 @@ jobs: | |||||
- name: Run Tests | - name: Run Tests | ||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }} | if: ${{ steps.check-build.outputs.build == 'strawberry' }} | ||||
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator | |||||
env: | |||||
CI_BUILD_ID: ${{ github.run_id }} | |||||
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io | |||||
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests |
@@ -21,8 +21,6 @@ jobs: | |||||
strategy: | strategy: | ||||
fail-fast: false | fail-fast: false | ||||
matrix: | |||||
container: [1, 2] | |||||
services: | services: | ||||
postgres: | postgres: | ||||
@@ -125,7 +123,4 @@ jobs: | |||||
- name: Run Tests | - name: Run Tests | ||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }} | if: ${{ steps.check-build.outputs.build == 'strawberry' }} | ||||
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator | |||||
env: | |||||
CI_BUILD_ID: ${{ github.run_id }} | |||||
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io | |||||
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests |
@@ -21,7 +21,7 @@ jobs: | |||||
strategy: | strategy: | ||||
fail-fast: false | fail-fast: false | ||||
matrix: | matrix: | ||||
containers: [1, 2, 3] | |||||
containers: [1, 2] | |||||
name: UI Tests (Cypress) | name: UI Tests (Cypress) | ||||
@@ -105,19 +105,16 @@ jobs: | |||||
id: yarn-cache | id: yarn-cache | ||||
with: | with: | ||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} | ||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} | |||||
key: ${{ runner.os }}-yarn-ui-${{ hashFiles('**/yarn.lock') }} | |||||
restore-keys: | | restore-keys: | | ||||
${{ runner.os }}-yarn- | |||||
${{ runner.os }}-yarn-ui- | |||||
- name: Cache cypress binary | - name: Cache cypress binary | ||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }} | if: ${{ steps.check-build.outputs.build == 'strawberry' }} | ||||
uses: actions/cache@v3 | uses: actions/cache@v3 | ||||
with: | with: | ||||
path: ~/.cache | |||||
key: ${{ runner.os }}-cypress- | |||||
restore-keys: | | |||||
${{ runner.os }}-cypress- | |||||
${{ runner.os }}- | |||||
path: ~/.cache/Cypress | |||||
key: ${{ runner.os }}-cypress | |||||
- name: Install Dependencies | - name: Install Dependencies | ||||
if: ${{ steps.check-build.outputs.build == 'strawberry' }} | if: ${{ steps.check-build.outputs.build == 'strawberry' }} | ||||
@@ -0,0 +1,23 @@ | |||||
const { defineConfig } = require("cypress"); | |||||
module.exports = defineConfig({ | |||||
projectId: "92odwv", | |||||
adminPassword: "admin", | |||||
defaultCommandTimeout: 20000, | |||||
pageLoadTimeout: 15000, | |||||
video: true, | |||||
videoUploadOnPasses: false, | |||||
retries: { | |||||
runMode: 2, | |||||
openMode: 2, | |||||
}, | |||||
e2e: { | |||||
// We've imported your old cypress plugins here. | |||||
// You may want to clean this up later by importing these. | |||||
setupNodeEvents(on, config) { | |||||
return require("./cypress/plugins/index.js")(on, config); | |||||
}, | |||||
baseUrl: "http://test_site_ui:8000", | |||||
specPattern: ["./cypress/integration/*.js", "**/ui_test_*.js"], | |||||
}, | |||||
}); |
@@ -1,15 +0,0 @@ | |||||
{ | |||||
"baseUrl": "http://test_site_ui:8000", | |||||
"projectId": "92odwv", | |||||
"adminPassword": "admin", | |||||
"defaultCommandTimeout": 20000, | |||||
"pageLoadTimeout": 15000, | |||||
"video": true, | |||||
"videoUploadOnPasses": false, | |||||
"retries": { | |||||
"runMode": 2, | |||||
"openMode": 2 | |||||
}, | |||||
"integrationFolder": ".", | |||||
"testFiles": ["cypress/integration/*.js", "**/ui_test_*.js"] | |||||
} |
@@ -29,6 +29,7 @@ context("Control Float", () => { | |||||
}); | }); | ||||
x.values.forEach((d) => { | x.values.forEach((d) => { | ||||
cy.get_field("float_number", "Float").clear(); | cy.get_field("float_number", "Float").clear(); | ||||
cy.wait(200); | |||||
cy.fill_field("float_number", d.input, "Float").blur(); | cy.fill_field("float_number", d.input, "Float").blur(); | ||||
cy.get_field("float_number", "Float").should("have.value", d.blur_expected); | cy.get_field("float_number", "Float").should("have.value", d.blur_expected); | ||||
@@ -132,9 +132,6 @@ context("Form", () => { | |||||
jump_to_field("Username"); | jump_to_field("Username"); | ||||
type_value("admin42"); | type_value("admin42"); | ||||
jump_to_field("Birth Date"); | |||||
type_value("12-31-01"); | |||||
jump_to_field("Send Welcome Email"); | jump_to_field("Send Welcome Email"); | ||||
cy.focused().uncheck(); | cy.focused().uncheck(); | ||||
@@ -155,16 +152,15 @@ context("Form", () => { | |||||
undo(); | undo(); | ||||
undo(); | undo(); | ||||
undo(); | undo(); | ||||
undo(); | |||||
redo(); | |||||
redo(); | redo(); | ||||
redo(); | redo(); | ||||
redo(); | redo(); | ||||
redo(); | redo(); | ||||
cy.get_field("username").should("have.value", "admin24"); | |||||
cy.get_field("email").should("have.value", "admin@example.com"); | |||||
cy.get_field("birth_date").should("have.value", "12-31-2001"); // parsed value | |||||
cy.get_field("send_welcome_email").should("not.be.checked"); | |||||
cy.compare_document({ | |||||
username: "admin24", | |||||
email: "admin@example.com", | |||||
send_welcome_email: 0, | |||||
}); | |||||
}); | }); | ||||
}); | }); |
@@ -96,17 +96,4 @@ context("Kanban Board", () => { | |||||
.first() | .first() | ||||
.should("not.contain", "ID:"); | .should("not.contain", "ID:"); | ||||
}); | }); | ||||
// it('Drag todo', () => { | |||||
// cy.intercept({ | |||||
// method: 'POST', | |||||
// url: 'api/method/frappe.desk.doctype.kanban_board.kanban_board.update_order_for_single_card' | |||||
// }).as('drag-completed'); | |||||
// cy.get('.kanban-card-body') | |||||
// .contains('Test Kanban ToDo').first() | |||||
// .drag('[data-column-value="Closed"] .kanban-cards', { force: true }); | |||||
// cy.wait('@drag-completed'); | |||||
// }); | |||||
}); | }); |
@@ -10,10 +10,13 @@ context("List View", () => { | |||||
}); | }); | ||||
}); | }); | ||||
it("Keep checkbox checked after Refresh", () => { | |||||
it("Keep checkbox checked after Refresh", { scrollBehavior: false }, () => { | |||||
cy.go_to_list("ToDo"); | cy.go_to_list("ToDo"); | ||||
cy.clear_filters(); | cy.clear_filters(); | ||||
cy.get(".list-row-container .list-row-checkbox").click({ multiple: true, force: true }); | |||||
cy.get(".list-row-container .list-row-checkbox").click({ | |||||
multiple: true, | |||||
force: true, | |||||
}); | |||||
cy.get(".actions-btn-group button").contains("Actions").should("be.visible"); | cy.get(".actions-btn-group button").contains("Actions").should("be.visible"); | ||||
cy.intercept("/api/method/frappe.desk.reportview.get").as("list-refresh"); | cy.intercept("/api/method/frappe.desk.reportview.get").as("list-refresh"); | ||||
cy.wait(3000); // wait before you hit another refresh | cy.wait(3000); // wait before you hit another refresh | ||||
@@ -22,7 +25,7 @@ context("List View", () => { | |||||
cy.get(".list-row-container .list-row-checkbox:checked").should("be.visible"); | cy.get(".list-row-container .list-row-checkbox:checked").should("be.visible"); | ||||
}); | }); | ||||
it('enables "Actions" button', () => { | |||||
it('enables "Actions" button', { scrollBehavior: false }, () => { | |||||
const actions = [ | const actions = [ | ||||
"Approve", | "Approve", | ||||
"Reject", | "Reject", | ||||
@@ -5,12 +5,14 @@ context("List View Settings", () => { | |||||
}); | }); | ||||
it("Default settings", () => { | it("Default settings", () => { | ||||
cy.visit("/app/List/DocType/List"); | cy.visit("/app/List/DocType/List"); | ||||
cy.clear_filters(); | |||||
cy.get(".list-count").should("contain", "20 of"); | cy.get(".list-count").should("contain", "20 of"); | ||||
cy.get(".list-stats").should("contain", "Tags"); | cy.get(".list-stats").should("contain", "Tags"); | ||||
}); | }); | ||||
it("disable count and sidebar stats then verify", () => { | it("disable count and sidebar stats then verify", () => { | ||||
cy.wait(300); | cy.wait(300); | ||||
cy.visit("/app/List/DocType/List"); | cy.visit("/app/List/DocType/List"); | ||||
cy.clear_filters(); | |||||
cy.wait(300); | cy.wait(300); | ||||
cy.get(".list-count").should("contain", "20 of"); | cy.get(".list-count").should("contain", "20 of"); | ||||
cy.get(".menu-btn-group button").click(); | cy.get(".menu-btn-group button").click(); | ||||
@@ -1,93 +0,0 @@ | |||||
context("Timeline Email", () => { | |||||
before(() => { | |||||
cy.visit("/login"); | |||||
cy.login(); | |||||
cy.visit("/app/todo"); | |||||
}); | |||||
it("Adding new ToDo", () => { | |||||
cy.click_listview_primary_button("Add ToDo"); | |||||
cy.get(".custom-actions:visible > .btn").contains("Edit Full Form").click({ delay: 500 }); | |||||
cy.fill_field("description", "Test ToDo", "Text Editor"); | |||||
cy.wait(500); | |||||
cy.get(".primary-action").contains("Save").click({ force: true }); | |||||
cy.wait(700); | |||||
}); | |||||
it("Adding email and verifying timeline content for email attachment", () => { | |||||
cy.visit("/app/todo"); | |||||
cy.click_listview_row_item_with_text("Test ToDo"); | |||||
//Creating a new email | |||||
cy.get(".timeline-actions > .timeline-item > .action-buttons > .action-btn").click(); | |||||
cy.fill_field("recipients", "test@example.com", "MultiSelect"); | |||||
cy.get( | |||||
'.modal.show > .modal-dialog > .modal-content > .modal-body > :nth-child(1) > .form-layout > .form-page > :nth-child(3) > .section-body > .form-column > form > [data-fieldtype="Text Editor"] > .form-group > .control-input-wrapper > .control-input > .ql-container > .ql-editor' | |||||
).type("Test Mail"); | |||||
//Adding attachment to the email | |||||
cy.get(".add-more-attachments > .btn").click(); | |||||
cy.get(".mt-2 > .btn > .mt-1").eq(2).click(); | |||||
cy.get(".input-group > .form-control").type( | |||||
"https://wallpaperplay.com/walls/full/8/2/b/72402.jpg" | |||||
); | |||||
cy.get(".btn-primary").contains("Upload").click(); | |||||
//Sending the email | |||||
cy.click_modal_primary_button("Send", { delay: 500 }); | |||||
//To check if the sent mail content is shown in the timeline content | |||||
cy.get('[data-doctype="Communication"] > .timeline-content').should( | |||||
"contain", | |||||
"Test Mail" | |||||
); | |||||
//To check if the attachment of email is shown in the timeline content | |||||
cy.get(".timeline-content").should("contain", "Added 72402.jpg"); | |||||
//Deleting the sent email | |||||
cy.get('[title="Open Communication"] > .icon').first().click({ force: true }); | |||||
cy.get( | |||||
"#page-Communication > .page-head > .container > .row > .col > .standard-actions > .menu-btn-group > .btn" | |||||
).click(); | |||||
cy.get( | |||||
"#page-Communication > .page-head > .container > .row > .col > .standard-actions > .menu-btn-group > .dropdown-menu > li > .grey-link" | |||||
) | |||||
.eq(9) | |||||
.click(); | |||||
cy.get( | |||||
".modal.show > .modal-dialog > .modal-content > .modal-footer > .standard-actions > .btn-primary" | |||||
).click(); | |||||
}); | |||||
it("Deleting attachment and ToDo", () => { | |||||
cy.visit("/app/todo"); | |||||
cy.click_listview_row_item_with_text("Test ToDo"); | |||||
//Removing the added attachment | |||||
cy.get(".attachment-row > .data-pill > .remove-btn > .icon").click(); | |||||
cy.wait(500); | |||||
cy.get(".modal-footer:visible > .standard-actions > .btn-primary").contains("Yes").click(); | |||||
//To check if the removed attachment is shown in the timeline content | |||||
cy.get(".timeline-content").should("contain", "Removed 72402.jpg"); | |||||
cy.wait(500); | |||||
//To check if the discard button functionality in email is working correctly | |||||
cy.get(".timeline-actions > .timeline-item > .action-buttons > .action-btn").click(); | |||||
cy.fill_field("recipients", "test@example.com", "MultiSelect"); | |||||
cy.get(".modal-footer > .standard-actions > .btn-secondary").contains("Discard").click(); | |||||
cy.wait(500); | |||||
cy.get(".timeline-actions > .timeline-item > .action-buttons > .action-btn").click(); | |||||
cy.wait(500); | |||||
cy.get_field("recipients", "MultiSelect").should("have.text", ""); | |||||
cy.get(".modal-header:visible > .modal-actions > .btn-modal-close > .icon").click(); | |||||
//Deleting the added ToDo | |||||
cy.get(".menu-btn-group:visible > .btn").click(); | |||||
cy.get(".menu-btn-group:visible > .dropdown-menu > li > .dropdown-item") | |||||
.contains("Delete") | |||||
.click(); | |||||
cy.get(".modal-footer:visible > .standard-actions > .btn-primary").click(); | |||||
}); | |||||
}); |
@@ -68,9 +68,10 @@ context("Workspace Blocks", () => { | |||||
cy.intercept({ | cy.intercept({ | ||||
method: "GET", | method: "GET", | ||||
url: "api/method/frappe.desk.form.load.getdoctype", | |||||
url: "api/method/frappe.desk.form.load.getdoctype?**", | |||||
}).as("get_doctype"); | }).as("get_doctype"); | ||||
cy.visit("/app/tools"); | |||||
cy.get(".codex-editor__redactor .ce-block"); | cy.get(".codex-editor__redactor .ce-block"); | ||||
cy.get(".standard-actions .btn-secondary[data-label=Edit]").click(); | cy.get(".standard-actions .btn-secondary[data-label=Edit]").click(); | ||||
@@ -281,12 +281,12 @@ Cypress.Commands.add("get_open_dialog", () => { | |||||
}); | }); | ||||
Cypress.Commands.add("save", () => { | Cypress.Commands.add("save", () => { | ||||
cy.intercept("/api").as("api"); | |||||
cy.intercept("/api/method/frappe.desk.form.save.savedocs").as("save_call"); | |||||
cy.get(`button[data-label="Save"]:visible`).click({ scrollBehavior: false, force: true }); | cy.get(`button[data-label="Save"]:visible`).click({ scrollBehavior: false, force: true }); | ||||
cy.wait("@api"); | |||||
cy.wait("@save_call"); | |||||
}); | }); | ||||
Cypress.Commands.add("hide_dialog", () => { | Cypress.Commands.add("hide_dialog", () => { | ||||
cy.wait(300); | |||||
cy.wait(500); | |||||
cy.get_open_dialog().focus().find(".btn-modal-close").click(); | cy.get_open_dialog().focus().find(".btn-modal-close").click(); | ||||
cy.get(".modal:visible").should("not.exist"); | cy.get(".modal:visible").should("not.exist"); | ||||
}); | }); | ||||
@@ -459,3 +459,26 @@ Cypress.Commands.add("select_listview_row_checkbox", (row_no) => { | |||||
Cypress.Commands.add("click_form_section", (section_name) => { | Cypress.Commands.add("click_form_section", (section_name) => { | ||||
cy.get(".section-head").contains(section_name).click(); | cy.get(".section-head").contains(section_name).click(); | ||||
}); | }); | ||||
const compare_document = (expected, actual) => { | |||||
for (const prop in expected) { | |||||
if (expected[prop] instanceof Array) { | |||||
// recursively compare child documents. | |||||
expected[prop].forEach((item, idx) => { | |||||
compare_document(item, actual[prop][idx]); | |||||
}); | |||||
} else { | |||||
assert.equal(expected[prop], actual[prop], `${prop} should be equal.`); | |||||
} | |||||
} | |||||
}; | |||||
Cypress.Commands.add("compare_document", (expected_document) => { | |||||
cy.window() | |||||
.its("cur_frm") | |||||
.then((frm) => { | |||||
// Don't remove this, cypress can't magically wait for events it has no control over. | |||||
cy.wait(1000); | |||||
compare_document(expected_document, frm.doc); | |||||
}); | |||||
}); |
@@ -111,15 +111,15 @@ function get_redis_subscriber(kind) { | |||||
let retry_strategy; | let retry_strategy; | ||||
let { get_redis_subscriber: get_redis, get_conf } = require("../node_utils"); | let { get_redis_subscriber: get_redis, get_conf } = require("../node_utils"); | ||||
if (process.env.CI == 1 || get_conf().developer_mode == 1) { | |||||
if (process.env.CI == 1 || get_conf().developer_mode == 0) { | |||||
retry_strategy = () => {}; | retry_strategy = () => {}; | ||||
} else { | } else { | ||||
retry_strategy = function (options) { | retry_strategy = function (options) { | ||||
// abort after 10 connection attempts | |||||
if (options.attempt > 10) { | |||||
// abort after 5 x 3 connection attempts ~= 3 seconds | |||||
if (options.attempt > 4) { | |||||
return undefined; | return undefined; | ||||
} | } | ||||
return Math.min(options.attempt * 100, 2000); | |||||
return options.attempt * 100; | |||||
}; | }; | ||||
} | } | ||||
return get_redis(kind, { retry_strategy }); | return get_redis(kind, { retry_strategy }); | ||||
@@ -46,7 +46,7 @@ class RequestContext: | |||||
@local_manager.middleware | @local_manager.middleware | ||||
@Request.application | @Request.application | ||||
def application(request): | |||||
def application(request: Request): | |||||
response = None | response = None | ||||
try: | try: | ||||
@@ -140,6 +140,9 @@ class LoginManager: | |||||
self.set_user_info() | self.set_user_info() | ||||
def login(self): | def login(self): | ||||
if frappe.get_system_settings("disable_user_pass_login"): | |||||
frappe.throw(_("Login with username and password is not allowed."), frappe.AuthenticationError) | |||||
# clear cache | # clear cache | ||||
frappe.clear_cache(user=frappe.form_dict.get("usr")) | frappe.clear_cache(user=frappe.form_dict.get("usr")) | ||||
user, pwd = get_cached_user_pass() | user, pwd = get_cached_user_pass() | ||||
@@ -226,14 +229,16 @@ class LoginManager: | |||||
def clear_active_sessions(self): | def clear_active_sessions(self): | ||||
"""Clear other sessions of the current user if `deny_multiple_sessions` is not set""" | """Clear other sessions of the current user if `deny_multiple_sessions` is not set""" | ||||
if frappe.session.user == "Guest": | |||||
return | |||||
if not ( | if not ( | ||||
cint(frappe.conf.get("deny_multiple_sessions")) | cint(frappe.conf.get("deny_multiple_sessions")) | ||||
or cint(frappe.db.get_system_setting("deny_multiple_sessions")) | or cint(frappe.db.get_system_setting("deny_multiple_sessions")) | ||||
): | ): | ||||
return | return | ||||
if frappe.session.user != "Guest": | |||||
clear_sessions(frappe.session.user, keep_current=True) | |||||
clear_sessions(frappe.session.user, keep_current=True) | |||||
def authenticate(self, user: str = None, pwd: str = None): | def authenticate(self, user: str = None, pwd: str = None): | ||||
from frappe.core.doctype.user.user import User | from frappe.core.doctype.user.user import User | ||||
@@ -879,7 +879,7 @@ def run_ui_tests( | |||||
click.secho("Installing Cypress...", fg="yellow") | click.secho("Installing Cypress...", fg="yellow") | ||||
packages = " ".join( | packages = " ".join( | ||||
[ | [ | ||||
"cypress@^6", | |||||
"cypress@^10", | |||||
"cypress-file-upload@^5", | "cypress-file-upload@^5", | ||||
"@4tw/cypress-drag-drop@^2", | "@4tw/cypress-drag-drop@^2", | ||||
"cypress-real-events", | "cypress-real-events", | ||||
@@ -161,12 +161,13 @@ def update_comments_in_parent(reference_doctype, reference_name, _comments): | |||||
else: | else: | ||||
raise ImplicitCommitError | raise ImplicitCommitError | ||||
else: | else: | ||||
if not frappe.flags.in_patch: | |||||
reference_doc = frappe.get_doc(reference_doctype, reference_name) | |||||
if getattr(reference_doc, "route", None): | |||||
clear_cache(reference_doc.route) | |||||
if frappe.flags.in_patch: | |||||
return | |||||
# Clear route cache | |||||
if route := frappe.get_cached_value(reference_doctype, reference_name, "route"): | |||||
clear_cache(route) | |||||
def update_comments_in_parent_after_request(): | def update_comments_in_parent_after_request(): | ||||
@@ -16,6 +16,7 @@ DEFAULT_LOGTYPES_RETENTION = { | |||||
"Email Queue": 30, | "Email Queue": 30, | ||||
"Error Snapshot": 30, | "Error Snapshot": 30, | ||||
"Scheduled Job Log": 90, | "Scheduled Job Log": 90, | ||||
"Route History": 90, | |||||
} | } | ||||
@@ -39,6 +39,7 @@ | |||||
"deny_multiple_sessions", | "deny_multiple_sessions", | ||||
"allow_login_using_mobile_number", | "allow_login_using_mobile_number", | ||||
"allow_login_using_user_name", | "allow_login_using_user_name", | ||||
"disable_user_pass_login", | |||||
"allow_error_traceback", | "allow_error_traceback", | ||||
"strip_exif_metadata_from_uploaded_images", | "strip_exif_metadata_from_uploaded_images", | ||||
"allow_older_web_view_links", | "allow_older_web_view_links", | ||||
@@ -525,12 +526,19 @@ | |||||
"fieldname": "email_retry_limit", | "fieldname": "email_retry_limit", | ||||
"fieldtype": "Int", | "fieldtype": "Int", | ||||
"label": "Email Retry Limit" | "label": "Email Retry Limit" | ||||
}, | |||||
{ | |||||
"default": "0", | |||||
"description": "Make sure to configure a Social Login Key before disabling to prevent lockout", | |||||
"fieldname": "disable_user_pass_login", | |||||
"fieldtype": "Check", | |||||
"label": "Disable Username/Password Login" | |||||
} | } | ||||
], | ], | ||||
"icon": "fa fa-cog", | "icon": "fa fa-cog", | ||||
"issingle": 1, | "issingle": 1, | ||||
"links": [], | "links": [], | ||||
"modified": "2022-06-21 13:55:04.796152", | |||||
"modified": "2022-09-06 03:16:59.090906", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "System Settings", | "name": "System Settings", | ||||
@@ -43,6 +43,22 @@ class SystemSettings(Document): | |||||
): | ): | ||||
frappe.flags.update_last_reset_password_date = True | frappe.flags.update_last_reset_password_date = True | ||||
self.validate_user_pass_login() | |||||
def validate_user_pass_login(self): | |||||
if not self.disable_user_pass_login: | |||||
return | |||||
social_login_enabled = frappe.db.exists("Social Login Key", {"enable_social_login": 1}) | |||||
ldap_enabled = frappe.db.get_single_value("LDAP Settings", "enabled") | |||||
if not (social_login_enabled or ldap_enabled): | |||||
frappe.throw( | |||||
_( | |||||
"Please enable atleast one Social Login Key or LDAP before disabling username/password based login." | |||||
) | |||||
) | |||||
def on_update(self): | def on_update(self): | ||||
self.set_defaults() | self.set_defaults() | ||||
@@ -1,6 +1,6 @@ | |||||
{ | { | ||||
"charts": [], | "charts": [], | ||||
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"DocType\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Workspace\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Report\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Elements</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Modules\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Models\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Views\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Scripting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Packages\",\"col\":4}}]", | |||||
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"DocType\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Workspace\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Report\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Elements</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Modules\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Models\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Views\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Scripting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Packages\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"System Logs\",\"col\":4}}]", | |||||
"creation": "2021-01-02 10:51:16.579957", | "creation": "2021-01-02 10:51:16.579957", | ||||
"docstatus": 0, | "docstatus": 0, | ||||
"doctype": "Workspace", | "doctype": "Workspace", | ||||
@@ -13,7 +13,7 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Modules", | |||||
"label": "Models", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
@@ -23,9 +23,9 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Module Def", | |||||
"label": "DocType", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Module Def", | |||||
"link_to": "DocType", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | "only_for": "", | ||||
@@ -34,9 +34,9 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Workspace", | |||||
"label": "Workflow", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Workspace", | |||||
"link_to": "Workflow", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | "only_for": "", | ||||
@@ -45,9 +45,19 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Module Onboarding", | |||||
"label": "Scripting", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Module Onboarding", | |||||
"link_type": "DocType", | |||||
"onboard": 0, | |||||
"only_for": "", | |||||
"type": "Card Break" | |||||
}, | |||||
{ | |||||
"hidden": 0, | |||||
"is_query_report": 0, | |||||
"label": "Server Script", | |||||
"link_count": 0, | |||||
"link_to": "Server Script", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | "only_for": "", | ||||
@@ -56,9 +66,9 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Block Module", | |||||
"label": "Client Script", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Block Module", | |||||
"link_to": "Client Script", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | "only_for": "", | ||||
@@ -67,30 +77,56 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Models", | |||||
"label": "Scheduled Job Type", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Scheduled Job Type", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | "only_for": "", | ||||
"type": "Link" | |||||
}, | |||||
{ | |||||
"hidden": 0, | |||||
"is_query_report": 0, | |||||
"label": "Packages", | |||||
"link_count": 2, | |||||
"onboard": 0, | |||||
"type": "Card Break" | "type": "Card Break" | ||||
}, | }, | ||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "DocType", | |||||
"label": "Package", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "DocType", | |||||
"link_to": "Package", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | |||||
"type": "Link" | "type": "Link" | ||||
}, | }, | ||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Workflow", | |||||
"label": "Package Import", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Workflow", | |||||
"link_to": "Package Import", | |||||
"link_type": "DocType", | |||||
"onboard": 0, | |||||
"type": "Link" | |||||
}, | |||||
{ | |||||
"hidden": 0, | |||||
"is_query_report": 0, | |||||
"label": "Modules", | |||||
"link_count": 3, | |||||
"onboard": 0, | |||||
"type": "Card Break" | |||||
}, | |||||
{ | |||||
"hidden": 0, | |||||
"is_query_report": 0, | |||||
"label": "Module Def", | |||||
"link_count": 0, | |||||
"link_to": "Module Def", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | "only_for": "", | ||||
@@ -99,11 +135,30 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Views", | |||||
"label": "Module Onboarding", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Module Onboarding", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | "only_for": "", | ||||
"type": "Link" | |||||
}, | |||||
{ | |||||
"hidden": 0, | |||||
"is_query_report": 0, | |||||
"label": "Module Profile", | |||||
"link_count": 0, | |||||
"link_to": "Module Profile", | |||||
"link_type": "DocType", | |||||
"onboard": 0, | |||||
"type": "Link" | |||||
}, | |||||
{ | |||||
"hidden": 0, | |||||
"is_query_report": 0, | |||||
"label": "Views", | |||||
"link_count": 4, | |||||
"onboard": 0, | |||||
"type": "Card Break" | "type": "Card Break" | ||||
}, | }, | ||||
{ | { | ||||
@@ -131,9 +186,9 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Workspace", | |||||
"label": "Dashboard", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Workspace", | |||||
"link_to": "Dashboard", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | "only_for": "", | ||||
@@ -142,71 +197,67 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Dashboard", | |||||
"label": "Workspace", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Dashboard", | |||||
"link_to": "Workspace", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | |||||
"type": "Link" | "type": "Link" | ||||
}, | }, | ||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Scripting", | |||||
"link_count": 0, | |||||
"link_type": "DocType", | |||||
"label": "System Logs", | |||||
"link_count": 6, | |||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | |||||
"type": "Card Break" | "type": "Card Break" | ||||
}, | }, | ||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Server Script", | |||||
"label": "Background Jobs", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Server Script", | |||||
"link_type": "DocType", | |||||
"link_to": "background_jobs", | |||||
"link_type": "Page", | |||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | |||||
"type": "Link" | "type": "Link" | ||||
}, | }, | ||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Client Script", | |||||
"label": "Scheduled Jobs Logs", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Client Script", | |||||
"link_to": "Scheduled Job Log", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | |||||
"type": "Link" | "type": "Link" | ||||
}, | }, | ||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Scheduled Job Type", | |||||
"label": "Error Logs", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Scheduled Job Type", | |||||
"link_to": "Error Log", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"only_for": "", | |||||
"type": "Link" | "type": "Link" | ||||
}, | }, | ||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Packages", | |||||
"link_count": 2, | |||||
"label": "Error Snapshot", | |||||
"link_count": 0, | |||||
"link_to": "Error Snapshot", | |||||
"link_type": "DocType", | |||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Card Break" | |||||
"type": "Link" | |||||
}, | }, | ||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Package", | |||||
"label": "Communication Logs", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Package", | |||||
"link_to": "Communication", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
@@ -214,21 +265,22 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Package Import", | |||||
"label": "Activity Log", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Package Import", | |||||
"link_to": "Activity Log", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
} | } | ||||
], | ], | ||||
"modified": "2022-01-13 17:26:02.736366", | |||||
"modified": "2022-09-02 01:48:28.029135", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "Build", | "name": "Build", | ||||
"owner": "Administrator", | "owner": "Administrator", | ||||
"parent_page": "", | "parent_page": "", | ||||
"public": 1, | "public": 1, | ||||
"quick_lists": [], | |||||
"restrict_to_domain": "", | "restrict_to_domain": "", | ||||
"roles": [], | "roles": [], | ||||
"sequence_id": 5.0, | "sequence_id": 5.0, | ||||
@@ -1,6 +1,6 @@ | |||||
{ | { | ||||
"charts": [], | "charts": [], | ||||
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Settings</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"System Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Print Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Website Settings\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Data\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Email / Notifications\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Website\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Core\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Printing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Workflow\",\"col\":4}}]", | |||||
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Settings</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"System Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Print Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Website Settings\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Data\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Email / Notifications\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Website\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Core\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Printing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Workflow\",\"col\":4}}]", | |||||
"creation": "2020-03-02 15:09:40.527211", | "creation": "2020-03-02 15:09:40.527211", | ||||
"docstatus": 0, | "docstatus": 0, | ||||
"doctype": "Workspace", | "doctype": "Workspace", | ||||
@@ -224,7 +224,7 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Core", | |||||
"label": "Printing", | |||||
"link_count": 0, | "link_count": 0, | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Card Break" | "type": "Card Break" | ||||
@@ -233,10 +233,10 @@ | |||||
"dependencies": "", | "dependencies": "", | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "System Settings", | |||||
"label": "Print Format Builder", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "System Settings", | |||||
"link_type": "DocType", | |||||
"link_to": "print-format-builder", | |||||
"link_type": "Page", | |||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
}, | }, | ||||
@@ -244,9 +244,9 @@ | |||||
"dependencies": "", | "dependencies": "", | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Error Log", | |||||
"label": "Print Settings", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Error Log", | |||||
"link_to": "Print Settings", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
@@ -255,9 +255,9 @@ | |||||
"dependencies": "", | "dependencies": "", | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Error Snapshot", | |||||
"label": "Print Format", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Error Snapshot", | |||||
"link_to": "Print Format", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
@@ -266,9 +266,9 @@ | |||||
"dependencies": "", | "dependencies": "", | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Domain Settings", | |||||
"label": "Print Style", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Domain Settings", | |||||
"link_to": "Print Style", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
@@ -276,7 +276,7 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Printing", | |||||
"label": "Workflow", | |||||
"link_count": 0, | "link_count": 0, | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Card Break" | "type": "Card Break" | ||||
@@ -285,20 +285,9 @@ | |||||
"dependencies": "", | "dependencies": "", | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Print Format Builder", | |||||
"link_count": 0, | |||||
"link_to": "print-format-builder", | |||||
"link_type": "Page", | |||||
"onboard": 0, | |||||
"type": "Link" | |||||
}, | |||||
{ | |||||
"dependencies": "", | |||||
"hidden": 0, | |||||
"is_query_report": 0, | |||||
"label": "Print Settings", | |||||
"label": "Workflow", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Print Settings", | |||||
"link_to": "Workflow", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
@@ -307,9 +296,9 @@ | |||||
"dependencies": "", | "dependencies": "", | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Print Format", | |||||
"label": "Workflow State", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Print Format", | |||||
"link_to": "Workflow State", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
@@ -318,9 +307,9 @@ | |||||
"dependencies": "", | "dependencies": "", | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Print Style", | |||||
"label": "Workflow Action", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Print Style", | |||||
"link_to": "Workflow Action", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
@@ -328,8 +317,8 @@ | |||||
{ | { | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Workflow", | |||||
"link_count": 0, | |||||
"label": "Core", | |||||
"link_count": 2, | |||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Card Break" | "type": "Card Break" | ||||
}, | }, | ||||
@@ -337,20 +326,9 @@ | |||||
"dependencies": "", | "dependencies": "", | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Workflow", | |||||
"link_count": 0, | |||||
"link_to": "Workflow", | |||||
"link_type": "DocType", | |||||
"onboard": 0, | |||||
"type": "Link" | |||||
}, | |||||
{ | |||||
"dependencies": "", | |||||
"hidden": 0, | |||||
"is_query_report": 0, | |||||
"label": "Workflow State", | |||||
"label": "System Settings", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Workflow State", | |||||
"link_to": "System Settings", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
@@ -359,21 +337,22 @@ | |||||
"dependencies": "", | "dependencies": "", | ||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Workflow Action", | |||||
"label": "Domain Settings", | |||||
"link_count": 0, | "link_count": 0, | ||||
"link_to": "Workflow Action", | |||||
"link_to": "Domain Settings", | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
} | } | ||||
], | ], | ||||
"modified": "2022-01-13 17:49:59.586909", | |||||
"modified": "2022-08-28 21:41:28.065190", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "Settings", | "name": "Settings", | ||||
"owner": "Administrator", | "owner": "Administrator", | ||||
"parent_page": "", | "parent_page": "", | ||||
"public": 1, | "public": 1, | ||||
"quick_lists": [], | |||||
"restrict_to_domain": "", | "restrict_to_domain": "", | ||||
"roles": [], | "roles": [], | ||||
"sequence_id": 29.0, | "sequence_id": 29.0, | ||||
@@ -1,6 +1,6 @@ | |||||
{ | { | ||||
"charts": [], | "charts": [], | ||||
"content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Customization\",\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customize Form\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Custom Role\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Client Script\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Server Script\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Dashboards\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Form Customization\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other\",\"col\":4}}]", | |||||
"content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Customization\",\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customize Form\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Custom Role\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Client Script\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Server Script\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Dashboards\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Form Customization\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other\",\"col\":4}}]", | |||||
"creation": "2020-03-02 15:15:03.839594", | "creation": "2020-03-02 15:15:03.839594", | ||||
"docstatus": 0, | "docstatus": 0, | ||||
"doctype": "Workspace", | "doctype": "Workspace", | ||||
@@ -107,7 +107,7 @@ | |||||
"hidden": 0, | "hidden": 0, | ||||
"is_query_report": 0, | "is_query_report": 0, | ||||
"label": "Other", | "label": "Other", | ||||
"link_count": 0, | |||||
"link_count": 2, | |||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Card Break" | "type": "Card Break" | ||||
}, | }, | ||||
@@ -121,15 +121,26 @@ | |||||
"link_type": "DocType", | "link_type": "DocType", | ||||
"onboard": 0, | "onboard": 0, | ||||
"type": "Link" | "type": "Link" | ||||
}, | |||||
{ | |||||
"hidden": 0, | |||||
"is_query_report": 0, | |||||
"label": "Navbar Settings", | |||||
"link_count": 0, | |||||
"link_to": "Navbar Settings", | |||||
"link_type": "DocType", | |||||
"onboard": 0, | |||||
"type": "Link" | |||||
} | } | ||||
], | ], | ||||
"modified": "2022-01-13 17:28:08.345794", | |||||
"modified": "2022-08-28 20:56:24.980719", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Custom", | "module": "Custom", | ||||
"name": "Customization", | "name": "Customization", | ||||
"owner": "Administrator", | "owner": "Administrator", | ||||
"parent_page": "", | "parent_page": "", | ||||
"public": 1, | "public": 1, | ||||
"quick_lists": [], | |||||
"restrict_to_domain": "", | "restrict_to_domain": "", | ||||
"roles": [], | "roles": [], | ||||
"sequence_id": 8.0, | "sequence_id": 8.0, | ||||
@@ -138,6 +138,7 @@ class Workspace(Document): | |||||
def disable_saving_as_public(): | def disable_saving_as_public(): | ||||
return ( | return ( | ||||
frappe.flags.in_install | frappe.flags.in_install | ||||
or frappe.flags.in_uninstall | |||||
or frappe.flags.in_patch | or frappe.flags.in_patch | ||||
or frappe.flags.in_test | or frappe.flags.in_test | ||||
or frappe.flags.in_fixtures | or frappe.flags.in_fixtures | ||||
@@ -196,6 +196,7 @@ frappe.patches.v14_0.log_settings_migration | |||||
frappe.patches.v14_0.setup_likes_from_feedback | frappe.patches.v14_0.setup_likes_from_feedback | ||||
frappe.patches.v14_0.update_webforms | frappe.patches.v14_0.update_webforms | ||||
frappe.patches.v14_0.delete_payment_gateways | frappe.patches.v14_0.delete_payment_gateways | ||||
frappe.patches.v14_0.event_streaming_deprecation_warning | |||||
[post_model_sync] | [post_model_sync] | ||||
frappe.patches.v14_0.drop_data_import_legacy | frappe.patches.v14_0.drop_data_import_legacy | ||||
@@ -0,0 +1,9 @@ | |||||
import click | |||||
def execute(): | |||||
click.secho( | |||||
"Event Streaming is moved to a separate app in version 15.\n" | |||||
"When upgrading to Frappe version-15, Please install the 'Event Streaming' app to continue using them: https://github.com/frappe/event_streaming", | |||||
fg="yellow", | |||||
) |
@@ -2,11 +2,11 @@ import frappe | |||||
def execute(): | def execute(): | ||||
frappe.reload_doctype("Web Form") | |||||
if not frappe.db.has_column("Web Form", "is_multi_step_form"): | |||||
return | |||||
for web_form in frappe.get_all("Web Form", filters={"is_multi_step_form": 1}): | for web_form in frappe.get_all("Web Form", filters={"is_multi_step_form": 1}): | ||||
web_form_fields = frappe.get_doc("Web Form", web_form.name).web_form_fields | web_form_fields = frappe.get_doc("Web Form", web_form.name).web_form_fields | ||||
for web_form_field in web_form_fields: | for web_form_field in web_form_fields: | ||||
if web_form_field.fieldtype == "Section Break" and web_form_field.idx != 1: | if web_form_field.fieldtype == "Section Break" and web_form_field.idx != 1: | ||||
frappe.db.set_value("Web Form Field", web_form_field.name, "fieldtype", "Page Break") | frappe.db.set_value("Web Form Field", web_form_field.name, "fieldtype", "Page Break") | ||||
frappe.db.commit() |
@@ -94,6 +94,10 @@ frappe.views.TreeView = class TreeView { | |||||
this.page.main.addClass("frappe-card"); | this.page.main.addClass("frappe-card"); | ||||
if (this.opts.show_expand_all) { | if (this.opts.show_expand_all) { | ||||
this.page.add_inner_button(__("Collapse All"), function () { | |||||
me.tree.load_children(me.tree.root_node, false); | |||||
}); | |||||
this.page.add_inner_button(__("Expand All"), function () { | this.page.add_inner_button(__("Expand All"), function () { | ||||
me.tree.load_children(me.tree.root_node, true); | me.tree.load_children(me.tree.root_node, true); | ||||
}); | }); | ||||
@@ -117,6 +117,7 @@ frappe.views.Workspace = class Workspace { | |||||
(page) => page.parent_page == "" || page.parent_page == null | (page) => page.parent_page == "" || page.parent_page == null | ||||
); | ); | ||||
} | } | ||||
root_pages = root_pages.uniqBy((d) => d.title); | |||||
this.build_sidebar_section(category, root_pages); | this.build_sidebar_section(category, root_pages); | ||||
}); | }); | ||||
@@ -196,8 +196,8 @@ | |||||
&.blue { color: var(--blue-500); } | &.blue { color: var(--blue-500); } | ||||
// SIZE & SPACING | // SIZE & SPACING | ||||
margin-top: 12px; | |||||
margin-bottom: 5px; | |||||
padding-top: 12px; | |||||
padding-bottom: 5px; | |||||
// LAYOUT | // LAYOUT | ||||
text-align: center; | text-align: center; | ||||
@@ -98,6 +98,7 @@ class TestAuth(FrappeTestCase): | |||||
def test_deny_multiple_login(self): | def test_deny_multiple_login(self): | ||||
self.set_system_settings("deny_multiple_sessions", 1) | self.set_system_settings("deny_multiple_sessions", 1) | ||||
self.addCleanup(self.set_system_settings, "deny_multiple_sessions", 0) | |||||
first_login = FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password) | first_login = FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password) | ||||
first_login.get_list("ToDo") | first_login.get_list("ToDo") | ||||
@@ -114,6 +115,14 @@ class TestAuth(FrappeTestCase): | |||||
second_login.get_list("ToDo") | second_login.get_list("ToDo") | ||||
third_login.get_list("ToDo") | third_login.get_list("ToDo") | ||||
def test_disable_user_pass_login(self): | |||||
FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password).get_list("ToDo") | |||||
self.set_system_settings("disable_user_pass_login", 1) | |||||
self.addCleanup(self.set_system_settings, "disable_user_pass_login", 0) | |||||
with self.assertRaises(Exception): | |||||
FrappeClient(self.HOST_NAME, self.test_user_email, self.test_user_password).get_list("ToDo") | |||||
class TestLoginAttemptTracker(FrappeTestCase): | class TestLoginAttemptTracker(FrappeTestCase): | ||||
def test_account_lock(self): | def test_account_lock(self): | ||||
@@ -0,0 +1,64 @@ | |||||
""" | |||||
This file contains multiple primitive tests for avoiding performance regressions. | |||||
- Time bound tests: Benchmarks are done on GHA before adding numbers | |||||
- Query count tests: More than expected # of queries for any action is frequent source of | |||||
performance issues. This guards against such problems. | |||||
E.g. We know get_controller is supposed to be cached and hence shouldn't make query post first | |||||
query. This test can be written like this. | |||||
>>> def test_controller_caching(self): | |||||
>>> | |||||
>>> get_controller("User") # <- "warm up code" | |||||
>>> with self.assertQueryCount(0): | |||||
>>> get_controller("User") | |||||
""" | |||||
import unittest | |||||
import frappe | |||||
from frappe.model.base_document import get_controller | |||||
from frappe.tests.utils import FrappeTestCase | |||||
from frappe.website.path_resolver import PathResolver | |||||
class TestPerformance(FrappeTestCase): | |||||
def reset_request_specific_caches(self): | |||||
# To simulate close to request level of handling | |||||
frappe.destroy() # releases everything on frappe.local | |||||
frappe.init(site=self.TEST_SITE) | |||||
frappe.connect() | |||||
frappe.clear_cache() | |||||
def setUp(self) -> None: | |||||
self.reset_request_specific_caches() | |||||
def test_meta_caching(self): | |||||
frappe.get_meta("User") | |||||
with self.assertQueryCount(0): | |||||
frappe.get_meta("User") | |||||
def test_controller_caching(self): | |||||
get_controller("User") | |||||
with self.assertQueryCount(0): | |||||
get_controller("User") | |||||
def test_db_value_cache(self): | |||||
"""Link validation if repeated should just use db.value_cache, hence no extra queries""" | |||||
doc = frappe.get_last_doc("User") | |||||
doc.get_invalid_links() | |||||
with self.assertQueryCount(0): | |||||
doc.get_invalid_links() | |||||
@unittest.skip("Not implemented") | |||||
def test_homepage_resolver(self): | |||||
paths = ["/", "/app"] | |||||
for path in paths: | |||||
PathResolver(path).resolve() | |||||
with self.assertQueryCount(1): | |||||
PathResolver(path).resolve() |
@@ -38,8 +38,7 @@ def create_if_not_exists(doc): | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def create_todo_records(): | def create_todo_records(): | ||||
if frappe.get_all("ToDo", {"description": "this is first todo"}): | |||||
return | |||||
frappe.db.truncate("ToDo") | |||||
frappe.get_doc( | frappe.get_doc( | ||||
{"doctype": "ToDo", "date": add_to_date(now(), days=7), "description": "this is first todo"} | {"doctype": "ToDo", "date": add_to_date(now(), days=7), "description": "this is first todo"} | ||||
@@ -19,10 +19,13 @@ class FrappeTestCase(unittest.TestCase): | |||||
otherwise this class will become ineffective. | otherwise this class will become ineffective. | ||||
""" | """ | ||||
TEST_SITE = "test_site" | |||||
SHOW_TRANSACTION_COMMIT_WARNINGS = False | SHOW_TRANSACTION_COMMIT_WARNINGS = False | ||||
@classmethod | @classmethod | ||||
def setUpClass(cls) -> None: | def setUpClass(cls) -> None: | ||||
cls.TEST_SITE = getattr(frappe.local, "site", None) or cls.TEST_SITE | |||||
# flush changes done so far to avoid flake | # flush changes done so far to avoid flake | ||||
frappe.db.commit() | frappe.db.commit() | ||||
frappe.db.begin() | frappe.db.begin() | ||||
@@ -64,6 +67,21 @@ class FrappeTestCase(unittest.TestCase): | |||||
else: | else: | ||||
self.assertEqual(expected, actual, msg=msg) | self.assertEqual(expected, actual, msg=msg) | ||||
@contextmanager | |||||
def assertQueryCount(self, count): | |||||
def _sql_with_count(*args, **kwargs): | |||||
frappe.db.sql_query_count += 1 | |||||
return orig_sql(*args, **kwargs) | |||||
try: | |||||
orig_sql = frappe.db.sql | |||||
frappe.db.sql_query_count = 0 | |||||
frappe.db.sql = _sql_with_count | |||||
yield | |||||
self.assertLessEqual(frappe.db.sql_query_count, count) | |||||
finally: | |||||
frappe.db.sql = orig_sql | |||||
def _commit_watcher(): | def _commit_watcher(): | ||||
import traceback | import traceback | ||||
@@ -289,12 +289,10 @@ def add_standard_navbar_items(): | |||||
"is_standard": 1, | "is_standard": 1, | ||||
}, | }, | ||||
{ | { | ||||
"item_label": "Background Jobs", | |||||
"item_type": "Route", | |||||
"route": "/app/background_jobs", | |||||
"item_type": "Separator", | |||||
"is_standard": 1, | "is_standard": 1, | ||||
"item_label": "", | |||||
}, | }, | ||||
{"item_type": "Separator", "is_standard": 1}, | |||||
{ | { | ||||
"item_label": "Log out", | "item_label": "Log out", | ||||
"item_type": "Action", | "item_type": "Action", | ||||
@@ -167,6 +167,7 @@ def get_safe_globals(): | |||||
rollback=frappe.db.rollback, | rollback=frappe.db.rollback, | ||||
add_index=frappe.db.add_index, | add_index=frappe.db.add_index, | ||||
), | ), | ||||
lang=getattr(frappe.local, "lang", "en"), | |||||
), | ), | ||||
FrappeClient=FrappeClient, | FrappeClient=FrappeClient, | ||||
style=frappe._dict(border_color="#d1d8dd"), | style=frappe._dict(border_color="#d1d8dd"), | ||||
@@ -1,6 +1,7 @@ | |||||
{% extends "templates/web.html" %} | {% extends "templates/web.html" %} | ||||
{% macro email_login_body() -%} | {% macro email_login_body() -%} | ||||
{% if not disable_user_pass_login or (ldap_settings and ldap_settings.enabled) %} | |||||
<div class="page-card-body"> | <div class="page-card-body"> | ||||
<div class="form-group"> | <div class="form-group"> | ||||
<label class="form-label sr-only" for="login_email">{{ login_label or _("Email")}}</label> | <label class="form-label sr-only" for="login_email">{{ login_label or _("Email")}}</label> | ||||
@@ -38,13 +39,15 @@ | |||||
</div> | </div> | ||||
<p class="forgot-password-message"> | <p class="forgot-password-message"> | ||||
<a href="#forgot">{{ _("Forgot Password?") }}</a></p> | |||||
<a href="#forgot">{{ _("Forgot Password?") }}</a> | |||||
</p> | |||||
</div> | </div> | ||||
{% endif %} | |||||
<div class="page-card-actions"> | <div class="page-card-actions"> | ||||
{% if not disable_user_pass_login %} | |||||
<button class="btn btn-sm btn-primary btn-block btn-login" type="submit"> | <button class="btn btn-sm btn-primary btn-block btn-login" type="submit"> | ||||
{{ _("Login") }}</button> | {{ _("Login") }}</button> | ||||
{% endif %} | |||||
{% if ldap_settings and ldap_settings.enabled %} | {% if ldap_settings and ldap_settings.enabled %} | ||||
<button class="btn btn-sm btn-default btn-block btn-login btn-ldap-login"> | <button class="btn btn-sm btn-default btn-block btn-login btn-ldap-login"> | ||||
{{ _("Login with LDAP") }}</button> | {{ _("Login with LDAP") }}</button> | ||||
@@ -83,7 +86,9 @@ | |||||
{{ email_login_body() }} | {{ email_login_body() }} | ||||
</form> | </form> | ||||
<div class="social-logins text-center"> | <div class="social-logins text-center"> | ||||
{% if not disable_user_pass_login or (ldap_settings and ldap_settings.enabled) %} | |||||
<p class="text-muted login-divider">{{ _("or") }}</p> | <p class="text-muted login-divider">{{ _("or") }}</p> | ||||
{% endif %} | |||||
<div class="social-login-buttons"> | <div class="social-login-buttons"> | ||||
{% for provider in provider_logins %} | {% for provider in provider_logins %} | ||||
<div class="login-button-wrapper"> | <div class="login-button-wrapper"> | ||||
@@ -7,6 +7,7 @@ from frappe import _ | |||||
from frappe.auth import LoginManager | from frappe.auth import LoginManager | ||||
from frappe.integrations.doctype.ldap_settings.ldap_settings import LDAPSettings | from frappe.integrations.doctype.ldap_settings.ldap_settings import LDAPSettings | ||||
from frappe.integrations.oauth2_logins import decoder_compat | from frappe.integrations.oauth2_logins import decoder_compat | ||||
from frappe.utils import cint | |||||
from frappe.utils.html_utils import get_icon_html | from frappe.utils.html_utils import get_icon_html | ||||
from frappe.utils.jinja import guess_is_path | from frappe.utils.jinja import guess_is_path | ||||
from frappe.utils.oauth import ( | from frappe.utils.oauth import ( | ||||
@@ -40,7 +41,8 @@ def get_context(context): | |||||
context.for_test = "login.html" | context.for_test = "login.html" | ||||
context["title"] = "Login" | context["title"] = "Login" | ||||
context["provider_logins"] = [] | context["provider_logins"] = [] | ||||
context["disable_signup"] = frappe.utils.cint(frappe.get_website_settings("disable_signup")) | |||||
context["disable_signup"] = cint(frappe.get_website_settings("disable_signup")) | |||||
context["disable_user_pass_login"] = cint(frappe.get_system_settings("disable_user_pass_login")) | |||||
context["logo"] = frappe.get_website_settings("app_logo") or frappe.get_hooks("app_logo_url")[-1] | context["logo"] = frappe.get_website_settings("app_logo") or frappe.get_hooks("app_logo_url")[-1] | ||||
context["app_name"] = ( | context["app_name"] = ( | ||||
frappe.get_website_settings("app_name") or frappe.get_system_settings("app_name") or _("Frappe") | frappe.get_website_settings("app_name") or frappe.get_system_settings("app_name") or _("Frappe") | ||||