Merge branch 'develop' into whats-new

This commit is contained in:
Suhail 2023-07-17 12:15:28 +05:30 committed by GitHub
commit dc0d76a6db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
155 changed files with 1129 additions and 1264 deletions

View file

@ -2,65 +2,32 @@
"env": {
"browser": true,
"node": true,
"es6": true
"es2022": true
},
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
"tab",
{ "SwitchCase": 1 }
],
"brace-style": [
"error",
"1tbs"
],
"space-unary-ops": [
"error",
{ "words": true }
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"off"
],
"semi": [
"warn",
"always"
],
"camelcase": [
"off"
],
"no-unused-vars": [
"warn"
],
"no-redeclare": [
"warn"
],
"no-console": [
"warn"
],
"no-extra-boolean-cast": [
"off"
],
"no-control-regex": [
"off"
],
"space-before-blocks": "warn",
"keyword-spacing": "warn",
"comma-spacing": "warn",
"key-spacing": "warn",
"indent": "off",
"brace-style": "off",
"no-mixed-spaces-and-tabs": "off",
"no-useless-escape": "off",
"space-unary-ops": ["error", { "words": true }],
"linebreak-style": "off",
"quotes": ["off"],
"semi": "off",
"camelcase": "off",
"no-unused-vars": "off",
"no-console": ["warn"],
"no-extra-boolean-cast": ["off"],
"no-control-regex": ["off"],
},
"root": true,
"globals": {
"frappe": true,
"Vue": true,
"SetVueGlobals": true,
"__": true,
"repl": true,
"Class": true,
@ -76,8 +43,10 @@
"is_null": true,
"in_list": true,
"has_common": true,
"posthog": true,
"has_words": true,
"validate_email": true,
"open_web_template_values_editor": true,
"validate_name": true,
"validate_phone": true,
"validate_url": true,

View file

@ -62,6 +62,7 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: pip
- uses: pre-commit/action@v3.0.0
- name: Download Semgrep rules
@ -69,7 +70,7 @@ jobs:
- name: Run Semgrep rules
run: |
pip install semgrep==0.97.0
pip install semgrep
semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness
deps-vulnerable-check:

View file

@ -29,10 +29,14 @@ jobs:
steps:
- name: Update notes
run: |
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' )
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/generate-notes -f tag_name=$RELEASE_TAG \
| jq -r '.body' \
| sed -E '/^\* (chore|ci|test|docs|style)/d' \
| sed -E 's/by @mergify //'
)
RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/tags/$RELEASE_TAG | jq -r '.id')
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/$RELEASE_ID -f body=$NEW_NOTES
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/$RELEASE_ID -f body="$NEW_NOTES"
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }}

View file

@ -21,7 +21,7 @@ repos:
- id: debug-statements
- repo: https://github.com/asottile/pyupgrade
rev: v2.34.0
rev: v3.9.0
hooks:
- id: pyupgrade
args: ['--py310-plus']
@ -48,13 +48,31 @@ repos:
)$
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.44.0
hooks:
- id: eslint
types_or: [javascript]
args: ['--quiet']
# Ignore any files that might contain jinja / bundles
exclude: |
(?x)^(
frappe/public/dist/.*|
cypress/.*|
.*node_modules.*|
.*boilerplate.*|
frappe/www/website_script.js|
frappe/templates/includes/.*|
frappe/public/js/lib/.*
)$
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
rev: 6.0.0
hooks:
- id: flake8
additional_dependencies: ['flake8-bugbear',]

View file

@ -9,38 +9,23 @@ context("Form Builder", () => {
it("Open Form Builder for Web Form Doctype/Customize Form", () => {
// doctype
cy.visit("/app/form-builder/Web Form");
cy.visit("/app/doctype/Web Form");
cy.findByRole("tab", { name: "Form" }).click();
cy.get(".form-builder-container").should("exist");
// customize form
cy.visit("/app/form-builder/Web Form/customize");
cy.visit("/app/customize-form?doc_type=Web%20Form");
cy.findByRole("tab", { name: "Form" }).click();
cy.get(".form-builder-container").should("exist");
});
it("Change Doctype using page title dialog", () => {
cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link");
cy.visit(`/app/form-builder/Web Form`);
cy.get(".form-builder-container").should("exist");
cy.get(".page-title").click();
cy.get(".frappe-control[data-fieldname='doctype'] input").click().as("input");
cy.get("@input").type("{rightArrow}Web Form Field", { delay: 200 });
cy.wait("@search_link");
cy.get("@input").type("{enter}").blur();
cy.click_modal_primary_button("Edit");
cy.get(".page-title .title-text").should("have.text", "Web Form Field");
});
it("Save without change, check form dirty and reset changes", () => {
cy.visit(`/app/form-builder/${doctype_name}`);
it("Save without change, check form dirty", () => {
cy.visit(`/app/doctype/${doctype_name}`);
cy.findByRole("tab", { name: "Form" }).click();
// Save without change
cy.click_doc_primary_button("Save");
cy.get(".desk-alert.orange .alert-message").should("have.text", "No changes to save");
cy.get(".desk-alert.orange .alert-message").should("have.text", "No changes in document");
// Check form dirty
cy.get(".tab-content.active .section-columns-container:first .column:first .field:first")
@ -48,14 +33,11 @@ context("Form Builder", () => {
.dblclick()
.type("Dirty");
cy.get(".title-area .indicator-pill.orange").should("have.text", "Not Saved");
// Reset changes
cy.get(".page-actions .custom-actions .btn").contains("Reset Changes").click();
cy.get(".title-area .indicator-pill.orange").should("not.exist");
});
it("Add empty section and save", () => {
cy.visit(`/app/form-builder/${doctype_name}`);
cy.visit(`/app/doctype/${doctype_name}`);
cy.findByRole("tab", { name: "Form" }).click();
let first_section = ".tab-content.active .form-section-container:first";
@ -71,7 +53,8 @@ context("Form Builder", () => {
it("Add Table field and check if columns are rendered", () => {
cy.intercept("POST", "/api/method/frappe.desk.search.search_link").as("search_link");
cy.visit(`/app/form-builder/${doctype_name}`);
cy.visit(`/app/doctype/${doctype_name}`);
cy.findByRole("tab", { name: "Form" }).click();
let first_field =
".tab-content.active .section-columns-container:first .column:first .field:first";
@ -126,20 +109,23 @@ context("Form Builder", () => {
});
it("Drag Field/Column/Section & Tab", () => {
cy.visit(`/app/form-builder/${doctype_name}`);
cy.visit(`/app/doctype/${doctype_name}`);
cy.findByRole("tab", { name: "Form" }).click();
let first_column = ".tab-content.active .section-columns-container:first .column:first";
let first_field = first_column + " .field:first";
let label = "div[title='Double click to edit label'] span:first";
cy.get(".tab-header .tabs .tab:first").click();
// drag first tab to second position
cy.get(".tabs .tab:first").drag(".tabs .tab:nth-child(2)", {
cy.get(".tab-header .tabs .tab:first").drag(".tab-header .tabs .tab:nth-child(2)", {
target: { x: 10, y: 10 },
force: true,
});
cy.get(".tabs .tab:first").find(label).should("have.text", "Tab 2");
cy.get(".tab-header .tabs .tab:first").find(label).should("have.text", "Tab 2");
cy.get(".tabs .tab:first").click();
cy.get(".tab-header .tabs .tab:first").click();
cy.get(".sidebar-container .tab:first").click();
// drag check field to first column
@ -151,7 +137,7 @@ context("Form Builder", () => {
cy.get(first_field)
.find("div[title='Double click to edit label']")
.dblclick()
.type("Test Check{enter}");
.type("Test Check");
cy.get(first_field).find(label).should("have.text", "Test Check");
// drag the first field to second position
@ -184,13 +170,14 @@ context("Form Builder", () => {
});
it("Add New Tab/Section/Column to Form", () => {
cy.visit(`/app/form-builder/${doctype_name}`);
cy.visit(`/app/doctype/${doctype_name}`);
cy.findByRole("tab", { name: "Form" }).click();
let first_section = ".tab-content.active .form-section-container:first";
// add new tab
cy.get(".tab-header").realHover().find(".tab-actions .new-tab-btn").click();
cy.get(".tabs .tab").should("have.length", 3);
cy.get(".tab-header .tabs .tab").should("have.length", 3);
// add new section
cy.get(first_section).click(15, 10);
@ -218,11 +205,12 @@ context("Form Builder", () => {
// remove tab
cy.get(".tab-header").realHover().find(".tab-actions .remove-tab-btn").click();
cy.get(".tabs .tab").should("have.length", 2);
cy.get(".tab-header .tabs .tab").should("have.length", 2);
});
it("Update Title field Label to New Title through Customize Form", () => {
cy.visit(`/app/form-builder/${doctype_name}`);
cy.visit(`/app/doctype/${doctype_name}`);
cy.findByRole("tab", { name: "Form" }).click();
let first_field =
".tab-content.active .section-columns-container:first .column:first .field:first";
@ -239,7 +227,8 @@ context("Form Builder", () => {
});
it("Validate Duplicate Name & reqd + hidden without default logic", () => {
cy.visit(`/app/form-builder/${doctype_name}`);
cy.visit(`/app/doctype/${doctype_name}`);
cy.findByRole("tab", { name: "Form" }).click();
let first_field =
".tab-content.active .section-columns-container:first .column:first .field:first";
@ -275,10 +264,11 @@ context("Form Builder", () => {
});
it("Undo/Redo", () => {
cy.visit(`/app/form-builder/${doctype_name}`);
cy.visit(`/app/doctype/${doctype_name}`);
cy.findByRole("tab", { name: "Form" }).click();
// click on second tab
cy.get(".tabs .tab:last").click();
cy.get(".tab-header .tabs .tab:last").click();
let first_column = ".tab-content.active .section-columns-container:first .column:first";
let first_field = first_column + " .field:first";

View file

@ -4,6 +4,8 @@ context("Grid Configuration", () => {
cy.visit("/app/doctype/User");
});
it("Set user wise grid settings", () => {
cy.findByRole("tab", { name: "Form" }).click();
cy.get('.form-section[data-fieldname="fields_section"]').click();
cy.wait(100);
cy.get('.frappe-control[data-fieldname="fields"]').as("table");
cy.get("@table").find(".icon-sm").click();

View file

@ -27,60 +27,70 @@ context("Sidebar", () => {
});
it("Verify attachment visibility config", () => {
verify_attachment_visibility("doctype/Blog Post", true);
cy.call("frappe.tests.ui_test_helpers.create_todo", {
description: "Sidebar Attachment ToDo",
}).then((todo) => {
verify_attachment_visibility(`todo/${todo.message.name}`, true);
});
verify_attachment_visibility("blog-post/test-blog-attachment-post", false);
});
it('Test for checking "Assigned To" counter value, adding filter and adding & removing an assignment', () => {
cy.visit("/app/doctype");
cy.click_sidebar_button("Assigned To");
cy.call("frappe.tests.ui_test_helpers.create_todo", {
description: "Sidebar Attachment ToDo",
}).then((todo) => {
let todo_name = todo.message.name;
cy.visit("/app/todo");
cy.click_sidebar_button("Assigned To");
//To check if no filter is available in "Assigned To" dropdown
cy.get(".empty-state").should("contain", "No filters found");
//To check if no filter is available in "Assigned To" dropdown
cy.get(".empty-state").should("contain", "No filters found");
//Assigning a doctype to a user
cy.visit("/app/doctype/ToDo");
cy.get(".form-assignments > .flex > .text-muted").click();
cy.get_field("assign_to_me", "Check").click();
cy.get(".modal-footer > .standard-actions > .btn-primary").click();
cy.visit("/app/doctype");
cy.click_sidebar_button("Assigned To");
//Assigning a doctype to a user
cy.visit(`/app/todo/${todo_name}`);
cy.get(".form-assignments > .flex > .text-muted").click();
cy.get_field("assign_to_me", "Check").click();
cy.get(".modal-footer > .standard-actions > .btn-primary").click();
cy.visit("/app/todo");
cy.click_sidebar_button("Assigned To");
//To check if filter is added in "Assigned To" dropdown after assignment
cy.get(".group-by-field.show > .dropdown-menu > .group-by-item > .dropdown-item").should(
"contain",
"1"
);
//To check if filter is added in "Assigned To" dropdown after assignment
cy.get(
".group-by-field.show > .dropdown-menu > .group-by-item > .dropdown-item"
).should("contain", "1");
//To check if there is no filter added to the listview
cy.get(".filter-button").should("contain", "Filter");
//To check if there is no filter added to the listview
cy.get(".filter-button").should("contain", "Filter");
//To add a filter to display data into the listview
cy.get(".group-by-field.show > .dropdown-menu > .group-by-item > .dropdown-item").click();
//To add a filter to display data into the listview
cy.get(
".group-by-field.show > .dropdown-menu > .group-by-item > .dropdown-item"
).click();
//To check if filter is applied
cy.click_filter_button().should("contain", "1 filter");
cy.get(".fieldname-select-area > .awesomplete > .form-control").should(
"have.value",
"Assigned To"
);
cy.get(".condition").should("have.value", "like");
cy.get(".filter-field > .form-group > .input-with-feedback").should(
"have.value",
`%${cy.config("testUser")}%`
);
cy.click_filter_button();
//To check if filter is applied
cy.click_filter_button().should("contain", "1 filter");
cy.get(".fieldname-select-area > .awesomplete > .form-control").should(
"have.value",
"Assigned To"
);
cy.get(".condition").should("have.value", "like");
cy.get(".filter-field > .form-group > .input-with-feedback").should(
"have.value",
`%${cy.config("testUser")}%`
);
cy.click_filter_button();
//To remove the applied filter
cy.clear_filters();
//To remove the applied filter
cy.clear_filters();
//To remove the assignment
cy.visit("/app/doctype/ToDo");
cy.get(".assignments > .avatar-group > .avatar > .avatar-frame").click();
cy.get(".remove-btn").click({ force: true });
cy.hide_dialog();
cy.visit("/app/doctype");
cy.click_sidebar_button("Assigned To");
cy.get(".empty-state").should("contain", "No filters found");
//To remove the assignment
cy.visit(`/app/todo/${todo_name}`);
cy.get(".assignments > .avatar-group > .avatar > .avatar-frame").click();
cy.get(".remove-btn").click({ force: true });
cy.hide_dialog();
cy.visit("/app/todo");
cy.click_sidebar_button("Assigned To");
cy.get(".empty-state").should("contain", "No filters found");
});
});
});

View file

@ -1,4 +1,3 @@
/* eslint-disable no-console */
const path = require("path");
const fs = require("fs");
const glob = require("fast-glob");

View file

@ -1,4 +1,3 @@
/* eslint-disable no-console */
const path = require("path");
const fs = require("fs");
const glob = require("fast-glob");

View file

@ -94,16 +94,16 @@ function get_cli_arg(name) {
function log_error(message, badge = "ERROR") {
badge = chalk.white.bgRed(` ${badge} `);
console.error(`${badge} ${message}`); // eslint-disable-line no-console
console.error(`${badge} ${message}`);
}
function log_warn(message, badge = "WARN") {
badge = chalk.black.bgYellowBright(` ${badge} `);
console.warn(`${badge} ${message}`); // eslint-disable-line no-console
console.warn(`${badge} ${message}`);
}
function log(...args) {
console.log(...args); // eslint-disable-line no-console
console.log(...args);
}
function get_redis_subscriber(kind) {

View file

@ -19,7 +19,8 @@ import os
import re
import unicodedata
import warnings
from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, TypeAlias, overload
from collections.abc import Callable
from typing import TYPE_CHECKING, Any, Literal, Optional, TypeAlias, overload
import click
from werkzeug.local import Local, release_local
@ -243,7 +244,7 @@ def init(site: str, sites_path: str = ".", new_site: bool = False, force=False)
local.preload_assets = {"style": [], "script": []}
local.session = _dict()
local.dev_server = _dev_server
local.qb = get_query_builder(local.conf.db_type or "mariadb")
local.qb = get_query_builder(local.conf.db_type)
local.qb.get_query = get_query
setup_redis_cache_connection()
setup_module_map()
@ -274,9 +275,12 @@ def connect(
set_user("Administrator")
def connect_replica():
def connect_replica() -> bool:
from frappe.database import get_db
if local and hasattr(local, "replica_db") and hasattr(local, "primary_db"):
return False
user = local.conf.db_name
password = local.conf.db_password
port = local.conf.replica_db_port
@ -291,6 +295,8 @@ def connect_replica():
local.primary_db = local.db
local.db = local.replica_db
return True
def get_site_config(sites_path: str | None = None, site_path: str | None = None) -> dict[str, Any]:
"""Returns `site_config.json` combined with `sites/common_site_config.json`.
@ -320,6 +326,27 @@ def get_site_config(sites_path: str | None = None, site_path: str | None = None)
elif local.site and not local.flags.new_site:
raise IncorrectSitePath(f"{local.site} does not exist")
# Generalized env variable overrides and defaults
def db_default_ports(db_type):
from frappe.database.mariadb.database import MariaDBDatabase
return {
"mariadb": MariaDBDatabase.default_port, # 3306
"postgres": 5432,
}[db_type]
config["redis_queue"] = (
os.environ.get("FRAPPE_REDIS_QUEUE") or config.get("redis_queue") or "redis://127.0.0.1:11311"
)
config["redis_cache"] = (
os.environ.get("FRAPPE_REDIS_CACHE") or config.get("redis_cache") or "redis://127.0.0.1:13311"
)
config["db_type"] = os.environ.get("FRAPPE_DB_TYPE") or config.get("db_type") or "mariadb"
config["db_host"] = os.environ.get("FRAPPE_DB_HOST") or config.get("db_host") or "127.0.0.1"
config["db_port"] = (
os.environ.get("FRAPPE_DB_PORT") or config.get("db_port") or db_default_ports(config["db_type"])
)
return _dict(config)
@ -360,7 +387,7 @@ def setup_redis_cache_connection():
if not cache:
from frappe.utils.redis_wrapper import RedisWrapper
cache = RedisWrapper.from_url(conf.get("redis_cache") or "redis://localhost:11311")
cache = RedisWrapper.from_url(conf.get("redis_cache"))
def get_traceback(with_context: bool = False) -> str:
@ -788,13 +815,17 @@ def is_whitelisted(method):
def read_only():
def innfn(fn):
def wrapper_fn(*args, **kwargs):
# frappe.read_only could be called from nested functions, in such cases don't swap the
# connection again.
switched_connection = False
if conf.read_from_replica:
connect_replica()
switched_connection = connect_replica()
try:
retval = fn(*args, **get_newargs(fn, kwargs))
finally:
if local and hasattr(local, "primary_db"):
if switched_connection and local and hasattr(local, "primary_db"):
local.db.close()
local.db = local.primary_db

View file

@ -284,6 +284,8 @@ def handle_exception(e):
or (frappe.local.request.path.startswith("/api/") and not accept_header.startswith("text"))
)
allow_traceback = frappe.get_system_settings("allow_error_traceback") if frappe.db else False
if not frappe.session.user:
# If session creation fails then user won't be unset. This causes a lot of code that
# assumes presence of this to fail. Session creation fails => guest or expired login
@ -338,7 +340,7 @@ def handle_exception(e):
else:
traceback = "<pre>" + sanitize_html(frappe.get_traceback()) + "</pre>"
# disable traceback in production if flag is set
if frappe.local.flags.disable_traceback and not frappe.local.dev_server:
if frappe.local.flags.disable_traceback or not allow_traceback and not frappe.local.dev_server:
traceback = ""
frappe.respond_as_web_page(

View file

@ -12,6 +12,8 @@ from frappe.contacts.doctype.contact.contact import (
get_contacts_linked_from,
get_contacts_linking_to,
)
from frappe.core.doctype.communication.email import make
from frappe.desk.form import assign_to
from frappe.model.document import Document
from frappe.utils import (
add_days,
@ -372,14 +374,14 @@ class AutoRepeat(Document):
elif "{" in self.message:
message = frappe.render_template(self.message, {"doc": new_doc})
frappe.sendmail(
reference_doctype=new_doc.doctype,
reference_name=new_doc.name,
make(
doctype=new_doc.doctype,
name=new_doc.name,
recipients=self.recipients,
subject=subject,
content=message,
attachments=attachments,
expose_recipients="header",
send_email=1,
)
@frappe.whitelist()

View file

@ -469,7 +469,7 @@ def database(context, extra_args):
if not site:
raise SiteNotSpecifiedError
frappe.init(site=site)
if not frappe.conf.db_type or frappe.conf.db_type == "mariadb":
if frappe.conf.db_type == "mariadb":
_mariadb(extra_args=extra_args)
elif frappe.conf.db_type == "postgres":
_psql(extra_args=extra_args)
@ -505,19 +505,17 @@ def postgres(context, extra_args):
def _mariadb(extra_args=None):
from frappe.database.mariadb.database import MariaDBDatabase
mysql = which("mysql")
command = [
mysql,
"--port",
str(frappe.conf.db_port or MariaDBDatabase.default_port),
str(frappe.conf.db_port),
"-u",
frappe.conf.db_name,
f"-p{frappe.conf.db_password}",
frappe.conf.db_name,
"-h",
frappe.conf.db_host or "localhost",
frappe.conf.db_host,
"--pager=less -SFX",
"--safe-updates",
"-A",
@ -530,8 +528,8 @@ def _mariadb(extra_args=None):
def _psql(extra_args=None):
psql = which("psql")
host = frappe.conf.db_host or "127.0.0.1"
port = frappe.conf.db_port or "5432"
host = frappe.conf.db_host
port = frappe.conf.db_port
env = os.environ.copy()
env["PGPASSWORD"] = frappe.conf.db_password
conn_string = f"postgresql://{frappe.conf.db_name}@{host}:{port}/{frappe.conf.db_name}"
@ -666,7 +664,7 @@ def transform_database(context, table, engine, row_format, failfast):
skipped = 0
frappe.init(site=site)
if frappe.conf.db_type and frappe.conf.db_type != "mariadb":
if frappe.conf.db_type != "mariadb":
click.secho("This command only has support for MariaDB databases at this point", fg="yellow")
sys.exit(1)

View file

@ -2,6 +2,7 @@
# License: MIT. See LICENSE
import frappe
from frappe.contacts.doctype.contact.contact import get_full_name
from frappe.email import get_contact_list
from frappe.tests.utils import FrappeTestCase
test_dependencies = ["Contact", "Salutation"]
@ -44,6 +45,17 @@ class TestContact(FrappeTestCase):
"John Jane Doe",
)
def test_get_contact_list(self):
# First time from database
results = get_contact_list("_Test Supplier")
self.assertEqual(results[0].label, "test_contact@example.com")
self.assertEqual(results[0].description, "_Test Contact For _Test Supplier")
# Second time from cache
results = get_contact_list("_Test Supplier")
self.assertEqual(results[0].label, "test_contact@example.com")
self.assertEqual(results[0].description, "_Test Contact For _Test Supplier")
def create_contact(name, salutation, emails=None, phones=None, save=True):
doc = frappe.get_doc(

View file

@ -7,11 +7,14 @@ import frappe
from frappe import _
from frappe.core.doctype.data_import.exporter import Exporter
from frappe.core.doctype.data_import.importer import Importer
from frappe.model import core_doctypes_list
from frappe.model.document import Document
from frappe.modules.import_file import import_file_by_path
from frappe.utils.background_jobs import enqueue, is_job_enqueued
from frappe.utils.csvutils import validate_google_sheets_url
BLOCKED_DOCTYPES = set(core_doctypes_list) - {"User", "Role"}
class DataImport(Document):
def validate(self):
@ -24,10 +27,15 @@ class DataImport(Document):
self.template_options = ""
self.template_warnings = ""
self.validate_doctype()
self.validate_import_file()
self.validate_google_sheets_url()
self.set_payload_count()
def validate_doctype(self):
if self.reference_doctype in BLOCKED_DOCTYPES:
frappe.throw(_("Importing {0} is not allowed.").format(self.reference_doctype))
def validate_import_file(self):
if self.import_file:
# validate template

View file

@ -2,6 +2,26 @@
// MIT License. See license.txt
frappe.ui.form.on("DocType", {
before_save: function (frm) {
let form_builder = frappe.form_builder;
if (form_builder?.store) {
let fields = form_builder.store.update_fields();
// if fields is a string, it means there is an error
if (typeof fields === "string") {
frappe.throw(fields);
}
}
},
after_save: function (frm) {
if (
frappe.form_builder &&
frappe.form_builder.doctype === frm.doc.name &&
frappe.form_builder.store
) {
frappe.form_builder.store.fetch();
}
},
refresh: function (frm) {
frm.set_query("role", "permissions", function (doc) {
if (doc.custom && frappe.session.user != "Administrator") {
@ -21,8 +41,6 @@ frappe.ui.form.on("DocType", {
frm.toggle_enable("beta", 0);
}
render_form_builder_message(frm);
if (!frm.is_new() && !frm.doc.istable) {
if (frm.doc.issingle) {
frm.add_custom_button(__("Go to {0}", [__(frm.doc.name)]), () => {
@ -72,6 +90,8 @@ frappe.ui.form.on("DocType", {
frm.cscript.autoname(frm);
frm.cscript.set_naming_rule_description(frm);
frm.trigger("setup_default_views");
render_form_builder(frm);
},
istable: (frm) => {
@ -142,4 +162,30 @@ function render_form_builder_message(frm) {
}
}
function render_form_builder(frm) {
if (frappe.form_builder && frappe.form_builder.doctype === frm.doc.name) {
frappe.form_builder.setup_page_actions();
frappe.form_builder.store.fetch();
return;
}
if (frappe.form_builder) {
frappe.form_builder.wrapper = $(frm.fields_dict["form_builder"].wrapper);
frappe.form_builder.frm = frm;
frappe.form_builder.doctype = frm.doc.name;
frappe.form_builder.customize = false;
frappe.form_builder.init(true);
frappe.form_builder.store.fetch();
} else {
frappe.require("form_builder.bundle.js").then(() => {
frappe.form_builder = new frappe.ui.FormBuilder({
wrapper: $(frm.fields_dict["form_builder"].wrapper),
frm: frm,
doctype: frm.doc.name,
customize: false,
});
});
}
}
extend_cscript(cur_frm.cscript, new frappe.model.DocTypeController({ frm: cur_frm }));

View file

@ -25,9 +25,6 @@
"beta",
"is_virtual",
"queue_in_background",
"fields_section_break",
"try_form_builder_html",
"fields",
"sb1",
"naming_rule",
"autoname",
@ -35,6 +32,32 @@
"column_break_15",
"description",
"documentation",
"sb2",
"permissions",
"restrict_to_domain",
"read_only",
"in_create",
"actions_section",
"actions",
"links_section",
"links",
"document_states_section",
"states",
"web_view",
"has_web_view",
"allow_guest_to_view",
"index_web_pages_for_search",
"route",
"is_published_field",
"website_search_field",
"advanced",
"engine",
"migration_hash",
"form_builder_tab",
"form_builder",
"fields_section",
"fields",
"settings_tab",
"form_settings_section",
"image_field",
"timeline_field",
@ -68,28 +91,7 @@
"column_break_51",
"email_append_to",
"sender_field",
"subject_field",
"sb2",
"permissions",
"restrict_to_domain",
"read_only",
"in_create",
"actions_section",
"actions",
"links_section",
"links",
"document_states_section",
"states",
"web_view",
"has_web_view",
"allow_guest_to_view",
"index_web_pages_for_search",
"route",
"is_published_field",
"website_search_field",
"advanced",
"engine",
"migration_hash"
"subject_field"
],
"fields": [
{
@ -195,12 +197,6 @@
"fieldtype": "Check",
"label": "Beta"
},
{
"fieldname": "fields_section_break",
"fieldtype": "Section Break",
"label": "Fields",
"oldfieldtype": "Section Break"
},
{
"fieldname": "fields",
"fieldtype": "Table",
@ -633,9 +629,25 @@
"label": "Is Calendar and Gantt"
},
{
"fieldname": "try_form_builder_html",
"fieldname": "settings_tab",
"fieldtype": "Tab Break",
"label": "Settings"
},
{
"fieldname": "form_builder_tab",
"fieldtype": "Tab Break",
"label": "Form"
},
{
"fieldname": "form_builder",
"fieldtype": "HTML",
"label": "Try Form Builder HTML"
"label": "Form Builder"
},
{
"collapsible": 1,
"fieldname": "fields_section",
"fieldtype": "Section Break",
"label": "Fields"
}
],
"icon": "fa fa-bolt",
@ -718,7 +730,7 @@
"link_fieldname": "reference_doctype"
}
],
"modified": "2023-05-15 14:07:51.526257",
"modified": "2023-07-12 13:56:26.185637",
"modified_by": "Administrator",
"module": "Core",
"name": "DocType",
@ -755,4 +767,4 @@
"states": [],
"track_changes": 1,
"translated_doctype": 1
}
}

View file

@ -1496,6 +1496,7 @@ def get_fields_not_allowed_in_list_view(meta) -> list[str]:
not_allowed_in_list_view.append("Attach Image")
if meta.istable:
not_allowed_in_list_view.remove("Button")
not_allowed_in_list_view.remove("HTML")
return not_allowed_in_list_view

View file

@ -1,28 +0,0 @@
frappe.listview_settings["DocType"] = {
onload: function (me) {
me.page.btn_primary.addClass("hidden");
this.setup_select_primary_button(me);
},
setup_select_primary_button: function (me) {
let actions = [
{
label: __("Add DocType (Form Builder)"),
description: __("Use the form builder to create a new DocType"),
action: () => frappe.set_route("form-builder", "new-doctype"),
},
{
label: __("Add DocType"),
description: __("Create a new DocType"),
action: () => frappe.new_doc("DocType"),
},
];
frappe.utils.add_select_group_button(
me.page.btn_primary.parent(),
actions,
"btn-primary",
"add"
);
},
};

View file

@ -308,7 +308,7 @@ def attach_files_to_document(doc: "Document", event) -> None:
# we dont want the update to fail if file cannot be attached for some reason
value = doc.get(df.fieldname)
if not (value or "").startswith(("/files", "/private/files")):
return
continue
if frappe.db.exists(
"File",
@ -319,7 +319,7 @@ def attach_files_to_document(doc: "Document", event) -> None:
"attached_to_field": df.fieldname,
},
):
return
continue
unattached_file = frappe.db.exists(
"File",
@ -341,7 +341,7 @@ def attach_files_to_document(doc: "Document", event) -> None:
"attached_to_field": df.fieldname,
},
)
return
continue
file: "File" = frappe.get_doc(
doctype="File",

View file

@ -1,6 +1,5 @@
// Copyright (c) {year}, {app_publisher} and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["{name}"] = {{
"filters": [

View file

@ -271,7 +271,7 @@ frappe.ui.form.on("User", {
if (frappe.route_flags.unsaved === 1) {
delete frappe.route_flags.unsaved;
for (var i = 0; i < frm.doc.user_emails.length; i++) {
for (let i = 0; i < frm.doc.user_emails.length; i++) {
frm.doc.user_emails[i].idx = frm.doc.user_emails[i].idx + 1;
}
frm.dirty();
@ -308,7 +308,7 @@ frappe.ui.form.on("User", {
enable_incoming: 1,
};
frappe.model.with_doctype("Email Account", function (doc) {
var doc = frappe.model.get_new_doc("Email Account");
doc = frappe.model.get_new_doc("Email Account");
frappe.route_flags.linked_user = frm.doc.name;
frappe.route_flags.delete_user_from_locals = true;
frappe.set_route("Form", "Email Account", doc.name);

View file

@ -1,7 +1,8 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
from collections.abc import Sequence
from datetime import timedelta
from typing import Optional, Sequence
from typing import Optional
import frappe
import frappe.defaults

View file

@ -60,6 +60,10 @@ class UserPermission(Document):
frappe.throw(_("{0} has already assigned default value for {1}.").format(ref_link, self.allow))
def send_user_permissions(bootinfo):
bootinfo.user["user_permissions"] = get_user_permissions()
@frappe.whitelist()
def get_user_permissions(user=None):
"""Get all users permissions for the user as a dict of doctype"""

View file

@ -1,6 +1,5 @@
// Copyright (c) 2022, Frappe Technologies and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Database Storage Usage By Tables"] = {
filters: [],

View file

@ -1,6 +1,5 @@
// Copyright (c) 2019, Frappe Technologies and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Transaction Log Report"] = {
onload: function (query_report) {

View file

@ -100,8 +100,6 @@ frappe.ui.form.on("Customize Form", {
frm.page.set_title(__("Customize Form - {0}", [frm.doc.doc_type]));
frappe.customize_form.set_primary_action(frm);
render_form_builder_message(frm);
frm.add_custom_button(
__("Go to {0} List", [__(frm.doc.doc_type)]),
function () {
@ -149,6 +147,8 @@ frappe.ui.form.on("Customize Form", {
["queue_in_background"],
frappe.get_meta(frm.doc.doc_type).is_submittable || 0
);
render_form_builder(frm);
});
}
@ -334,37 +334,6 @@ frappe.ui.form.on("DocType State", {
},
});
frappe.customize_form.validate_fieldnames = async function (frm) {
for (let i = 0; i < frm.doc.fields.length; i++) {
let field = frm.doc.fields[i];
let fieldname = field.label && frappe.model.scrub(field.label).toLowerCase();
if (
field.label &&
!field.fieldname &&
in_list(frappe.model.restricted_fields, fieldname)
) {
let message = __(
"For field <b>{0}</b> in row <b>{1}</b>, fieldname <b>{2}</b> is restricted it will be renamed as <b>{2}1</b>. Do you want to continue?",
[field.label, field.idx, fieldname]
);
await pause_to_confirm(message);
}
}
function pause_to_confirm(message) {
return new Promise((resolve) => {
frappe.confirm(
message,
() => resolve(),
() => {
frm.page.btn_primary.prop("disabled", false);
}
);
});
}
};
frappe.customize_form.save_customization = function (frm) {
if (frm.doc.doc_type) {
return frm.call({
@ -383,9 +352,22 @@ frappe.customize_form.save_customization = function (frm) {
}
};
frappe.customize_form.update_fields_from_form_builder = function (frm) {
let form_builder = frappe.form_builder;
if (form_builder?.store) {
let fields = form_builder.store.update_fields();
// if fields is a string, it means there is an error
if (typeof fields === "string") {
frappe.throw(fields);
}
frm.refresh_fields();
}
};
frappe.customize_form.set_primary_action = function (frm) {
frm.page.set_primary_action(__("Update"), async () => {
await this.validate_fieldnames(frm);
frm.page.set_primary_action(__("Update"), () => {
this.update_fields_from_form_builder(frm);
this.save_customization(frm);
});
};
@ -433,30 +415,29 @@ frappe.customize_form.clear_locals_and_refresh = function (frm) {
frm.refresh();
};
function render_form_builder_message(frm) {
$(frm.fields_dict["try_form_builder_html"].wrapper).empty();
if (!frm.is_new() && frm.fields_dict["try_form_builder_html"]) {
let title = __("Use Form Builder to visually customize your form layout");
let msg = __(
"You can drag and drop fields to create your form layout, add tabs, sections and columns to organize your form and update field properties all from one screen."
);
function render_form_builder(frm) {
if (frappe.form_builder && frappe.form_builder.doctype === frm.doc.doc_type) {
frappe.form_builder.setup_page_actions();
frappe.form_builder.store.fetch();
return;
}
let message = `
<div class="flex form-message blue p-3">
<div class="mr-3"><img style="border-radius: var(--border-radius-md)" width="275" src="/assets/frappe/images/form-builder.gif"></div>
<div>
<p style="font-size: var(--text-lg)">${title}</p>
<p>${msg}</p>
<div>
<a class="btn btn-primary btn-sm" href="/app/form-builder/${frm.doc.doc_type}/customize">
${__("Form Builder")} ${frappe.utils.icon("right", "xs")}
</a>
</div>
</div>
</div>
`;
$(frm.fields_dict["try_form_builder_html"].wrapper).html(message);
if (frappe.form_builder) {
frappe.form_builder.wrapper = $(frm.fields_dict["form_builder"].wrapper);
frappe.form_builder.frm = frm;
frappe.form_builder.doctype = frm.doc.doc_type;
frappe.form_builder.customize = true;
frappe.form_builder.init(true);
frappe.form_builder.store.fetch();
} else {
frappe.require("form_builder.bundle.js").then(() => {
frappe.form_builder = new frappe.ui.FormBuilder({
wrapper: $(frm.fields_dict["form_builder"].wrapper),
frm: frm,
doctype: frm.doc.doc_type,
customize: true,
});
});
}
}

View file

@ -21,12 +21,20 @@
"allow_auto_repeat",
"allow_import",
"queue_in_background",
"fields_section_break",
"try_form_builder_html",
"fields",
"naming_section",
"naming_rule",
"autoname",
"document_actions_section",
"actions",
"document_links_section",
"links",
"document_states_section",
"states",
"form_tab",
"form_builder",
"fields_section_break",
"fields",
"settings_tab",
"form_settings_section",
"image_field",
"max_attachments",
@ -48,12 +56,6 @@
"email_append_to",
"sender_field",
"subject_field",
"document_actions_section",
"actions",
"document_links_section",
"links",
"document_states_section",
"states",
"section_break_8",
"sort_field",
"column_break_10",
@ -174,8 +176,8 @@
"options": "ASC\nDESC"
},
{
"collapsible": 1,
"depends_on": "doc_type",
"description": "Customize Label, Print Hide, Default etc.",
"fieldname": "fields_section_break",
"fieldtype": "Section Break",
"label": "Fields"
@ -369,9 +371,19 @@
"label": "Is Calendar and Gantt"
},
{
"fieldname": "try_form_builder_html",
"fieldname": "settings_tab",
"fieldtype": "Tab Break",
"label": "Settings"
},
{
"fieldname": "form_builder",
"fieldtype": "HTML",
"label": "Try Form Builder HTML"
"label": "Form Builder"
},
{
"fieldname": "form_tab",
"fieldtype": "Tab Break",
"label": "Form"
}
],
"hide_toolbar": 1,
@ -380,7 +392,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-05-15 16:03:19.872532",
"modified": "2023-07-16 13:25:46.201184",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form",

View file

@ -31,7 +31,7 @@ frappe.ui.form.on("DocType Layout", {
await frm.events.sync_fields(frm, false);
if (frm.is_new()) {
frm.doc.__newname = document_name;
frm.doc.__newname = document_name; // eslint-disable-line
frm.refresh_field("__newname");
}
}

View file

@ -1,6 +1,5 @@
// Copyright (c) 2023, Frappe Technologies and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Audit System Hooks"] = {
filters: [],

View file

@ -8,9 +8,10 @@ import random
import re
import string
import traceback
from collections.abc import Iterable, Sequence
from contextlib import contextmanager, suppress
from time import time
from typing import Any, Iterable, Sequence
from typing import Any
from pypika.dialects import MySQLQueryBuilder, PostgreSQLQueryBuilder
from pypika.terms import Criterion, NullValue
@ -87,8 +88,8 @@ class Database:
port=None,
):
self.setup_type_map()
self.host = host or frappe.conf.db_host or "127.0.0.1"
self.port = port or frappe.conf.db_port or ""
self.host = host or frappe.conf.db_host
self.port = port or frappe.conf.db_port
self.user = user or frappe.conf.db_name
self.db_name = frappe.conf.db_name
self._conn = None

View file

@ -68,11 +68,7 @@ class DbManager:
if pipe:
print("Restoring Database file...")
command = (
"{pipe} mysql -u {user} -p{password} -h{host} "
+ ("-P{port}" if frappe.db.port else "")
+ " {target} {source}"
)
command = "{pipe} mysql -u {user} -p{password} -h{host} -P{port} {target} {source}"
command = command.format(
pipe=pipe,
user=esc(user),

View file

@ -2,7 +2,7 @@
# MIT License. See license.txt
import operator
from typing import Callable
from collections.abc import Callable
import frappe
from frappe.database.utils import NestedSetHierarchy

View file

@ -54,7 +54,7 @@ def import_db_from_sql(source_sql=None, verbose=False):
_command = (
f"psql {frappe.conf.db_name} "
f"-h {frappe.conf.db_host or 'localhost'} -p {str(frappe.conf.db_port or '5432')} "
f"-h {frappe.conf.db_host} -p {str(frappe.conf.db_port)} "
f"-U {frappe.conf.db_name}"
)

View file

@ -6,8 +6,7 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"script",
"output"
"script"
],
"fields": [
{
@ -16,20 +15,15 @@
"in_list_view": 1,
"label": "Script",
"read_only": 1
},
{
"fieldname": "output",
"fieldtype": "Code",
"label": "Output",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2020-08-18 20:07:57.587344",
"modified": "2023-07-05 22:16:02.823955",
"modified_by": "Administrator",
"module": "Desk",
"name": "Console Log",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{
@ -48,5 +42,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View file

@ -75,7 +75,7 @@
"label": "Event Type",
"oldfieldname": "event_type",
"oldfieldtype": "Select",
"options": "Private\nPublic\nCancelled",
"options": "Private\nPublic",
"reqd": 1,
"search_index": 1
},
@ -223,7 +223,7 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
"options": "Open\nCompleted\nClosed"
"options": "Open\nCompleted\nClosed\nCancelled"
},
{
"collapsible": 1,
@ -295,7 +295,7 @@
"icon": "fa fa-calendar",
"idx": 1,
"links": [],
"modified": "2022-08-12 19:24:34.794098",
"modified": "2023-06-23 10:33:15.685368",
"modified_by": "Administrator",
"module": "Desk",
"name": "Event",

View file

@ -9,6 +9,7 @@ frappe.ui.form.on("Form Tour", {
frm.set_query("reference_doctype", () => {
return { filters: { istable: 0 } };
});
frm.trigger("reference_doctype");
frm.set_query("report_name", () => {
if (frm.doc.reference_doctype) {
return {

View file

@ -26,7 +26,7 @@ class SystemConsole(Document):
else:
frappe.db.rollback()
frappe.get_doc(dict(doctype="Console Log", script=self.console, output=self.output)).insert()
frappe.get_doc(dict(doctype="Console Log", script=self.console)).insert()
frappe.db.commit()

View file

@ -1,211 +0,0 @@
frappe.pages["form-builder"].on_page_load = function (wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
title: __("Form Builder"),
single_column: true,
});
// hot reload in development
if (frappe.boot.developer_mode) {
frappe.hot_update = frappe.hot_update || [];
frappe.hot_update.push(() => load_form_builder(wrapper));
}
};
frappe.pages["form-builder"].on_page_show = function (wrapper) {
load_form_builder(wrapper);
};
function load_form_builder(wrapper) {
let route = frappe.get_route();
route = route.filter((a) => a);
if (route.length > 1 && route[1] === "new-doctype") {
frappe.pages["form-builder"].new_doctype(route[2]);
} else if (route.length > 1) {
let doctype = route[1];
let is_customize_form = route[2] === "customize";
if (frappe.form_builder?.doctype) {
frappe.form_builder.doctype = frappe.form_builder.store.doctype = doctype;
frappe.form_builder.customize = frappe.form_builder.store.is_customize_form =
is_customize_form;
frappe.form_builder.init(true);
frappe.form_builder.store.fetch();
return;
}
let $parent = $(wrapper).find(".layout-main-section");
$parent.empty();
frappe.require("form_builder.bundle.js").then(() => {
frappe.form_builder = new frappe.ui.FormBuilder({
wrapper: $parent,
page: wrapper.page,
doctype: doctype,
customize: is_customize_form,
});
});
} else {
frappe.pages["form-builder"].select_doctype();
}
}
frappe.pages["form-builder"].select_doctype = function () {
let d = new frappe.ui.Dialog({
title: __("Select DocType"),
fields: [
{
label: __("Select DocType"),
fieldname: "doctype",
fieldtype: "Link",
options: "DocType",
only_select: 1,
},
{
label: __("Customize"),
fieldname: "customize",
fieldtype: "Check",
},
],
primary_action_label: __("Edit"),
primary_action({ doctype, customize }) {
if (customize) {
frappe.model.with_doctype(doctype).then(() => {
let meta = frappe.get_meta(doctype);
if (in_list(frappe.model.core_doctypes_list, this.doctype))
frappe.throw(__("Core DocTypes cannot be customized."));
if (meta.issingle) frappe.throw(__("Single DocTypes cannot be customized."));
if (meta.custom)
frappe.throw(
__(
"Only standard DocTypes are allowed to be customized from Customize Form."
)
);
frappe.set_route("form-builder", doctype, "customize");
});
} else {
frappe.set_route("form-builder", doctype);
}
},
secondary_action_label: __("Create New DocType"),
secondary_action() {
let doctype = d.get_value("doctype") || "";
d.hide();
frappe.set_route("form-builder", "new-doctype", doctype);
},
});
d.show();
};
frappe.pages["form-builder"].new_doctype = function (doctype) {
let non_developer = frappe.session.user !== "Administrator" || !frappe.boot.developer_mode;
let new_d = new frappe.ui.Dialog({
title: __("Create New DocType"),
fields: [
{
label: __("DocType Name"),
fieldname: "doctype_name",
fieldtype: "Data",
default: doctype,
reqd: 1,
},
{ fieldtype: "Column Break" },
{
label: __("Module"),
fieldname: "module",
fieldtype: "Link",
options: "Module Def",
reqd: 1,
},
{ fieldtype: "Section Break" },
{
label: __("Is Submittable"),
fieldname: "is_submittable",
fieldtype: "Check",
description: __(
"Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended."
),
depends_on: "eval:!doc.istable && !doc.issingle",
},
{
label: __("Is Child Table"),
fieldname: "istable",
fieldtype: "Check",
description: __("Child Tables are shown as a Grid in other DocTypes"),
depends_on: "eval:!doc.is_submittable && !doc.issingle",
},
{
label: __("Editable Grid"),
fieldname: "editable_grid",
fieldtype: "Check",
depends_on: "istable",
default: 1,
},
{
label: __("Is Single"),
fieldname: "issingle",
fieldtype: "Check",
description: __(
"Single Types have only one record no tables associated. Values are stored in tabSingles"
),
depends_on: "eval:!doc.istable && !doc.is_submittable",
},
{
label: __("Custom?"),
fieldname: "custom",
fieldtype: "Check",
default: non_developer,
read_only: non_developer,
},
],
primary_action_label: __("Create & Continue"),
primary_action(values) {
if (!values.istable) values.editable_grid = 0;
frappe.db
.insert({
doctype: "DocType",
name: values.doctype_name,
module: values.module,
istable: values.istable,
editable_grid: values.editable_grid,
issingle: values.issingle,
custom: values.custom,
is_submittable: values.is_submittable,
permissions: [
{
create: 1,
delete: 1,
email: 1,
export: 1,
print: 1,
read: 1,
report: 1,
role: "System Manager",
share: 1,
write: 1,
},
],
fields: [
{
label: "Title",
fieldname: "title",
fieldtype: "Data",
},
],
})
.then((doc) => {
frappe.set_route("form-builder", doc.name);
});
},
secondary_action_label: __("Back"),
secondary_action() {
new_d.hide();
window.history.back();
},
});
new_d.show();
};

View file

@ -1,19 +0,0 @@
{
"content": null,
"creation": "2022-10-10 22:42:53.597423",
"docstatus": 0,
"doctype": "Page",
"idx": 0,
"modified": "2022-10-10 22:42:53.597423",
"modified_by": "Administrator",
"module": "Desk",
"name": "form-builder",
"owner": "Administrator",
"page_name": "form-builder",
"roles": [],
"script": null,
"standard": "Yes",
"style": null,
"system_page": 0,
"title": "Form Builder"
}

View file

@ -642,7 +642,7 @@ function guess_country(country_info) {
try {
const system_timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
for ([country, info] of Object.entries(country_info)) {
for (let [country, info] of Object.entries(country_info)) {
let possible_timezones = (info.timezones || []).filter((t) => t == system_timezone);
if (possible_timezones.length) return country;
}

View file

@ -377,7 +377,7 @@ def email_setup_wizard_exception(traceback, args):
traceback=traceback,
args="\n".join(pretty_args),
user=frappe.session.user,
headers=frappe.request.headers,
headers=frappe.request.headers if frappe.request else "[no request]",
)
frappe.sendmail(

View file

@ -145,7 +145,6 @@ class UserProfile {
});
}
// eslint-disable-next-line no-unused-vars
render_percentage_chart(field, title) {
frappe
.xcall(

View file

@ -1,6 +1,5 @@
// Copyright (c) 2016, Frappe Technologies and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["ToDo"] = {
filters: [],

View file

@ -525,47 +525,47 @@ def get_stats(stats, doctype, filters=None):
if filters is None:
filters = []
tags = json.loads(stats)
columns = json.loads(stats)
if filters:
filters = json.loads(filters)
stats = {}
results = {}
try:
columns = frappe.db.get_table_columns(doctype)
db_columns = frappe.db.get_table_columns(doctype)
except (frappe.db.InternalError, frappe.db.ProgrammingError):
# raised when _user_tags column is added on the fly
# raised if its a virtual doctype
columns = []
db_columns = []
for tag in tags:
if tag not in columns:
for column in columns:
if column not in db_columns:
continue
try:
tag_count = frappe.get_list(
doctype,
fields=[tag, "count(*)"],
filters=filters + [[tag, "!=", ""]],
group_by=tag,
fields=[column, "count(*)"],
filters=filters + [[column, "!=", ""]],
group_by=column,
as_list=True,
distinct=1,
)
if tag == "_user_tags":
stats[tag] = scrub_user_tags(tag_count)
if column == "_user_tags":
results[column] = scrub_user_tags(tag_count)
no_tag_count = frappe.get_list(
doctype,
fields=[tag, "count(*)"],
filters=filters + [[tag, "in", ("", ",")]],
fields=[column, "count(*)"],
filters=filters + [[column, "in", ("", ",")]],
as_list=True,
group_by=tag,
order_by=tag,
group_by=column,
order_by=column,
)
no_tag_count = no_tag_count[0][1] if no_tag_count else 0
stats[tag].append([_("No Tags"), no_tag_count])
results[column].append([_("No Tags"), no_tag_count])
else:
stats[tag] = tag_count
results[column] = tag_count
except frappe.db.SQLError:
pass
@ -573,7 +573,7 @@ def get_stats(stats, doctype, filters=None):
# raised when _user_tags column is added on the fly
pass
return stats
return results
@frappe.whitelist()

View file

@ -53,6 +53,8 @@ def get_csv_bytes(data: list[list], csv_params: dict) -> bytes:
def provide_binary_file(filename: str, extension: str, content: bytes) -> None:
"""Provide a binary file to the client."""
from frappe import _
frappe.response["type"] = "binary"
frappe.response["filecontent"] = content
frappe.response["filename"] = f"{filename}.{extension}"
frappe.response["filename"] = f"{_(filename)}.{extension}"

View file

@ -2,7 +2,6 @@
# License: MIT. See LICENSE
import frappe
from frappe.desk.reportview import build_match_conditions
def sendmail_to_system_managers(subject, content):
@ -12,31 +11,38 @@ def sendmail_to_system_managers(subject, content):
@frappe.whitelist()
def get_contact_list(txt, page_length=20) -> list[dict]:
"""Return email ids for a multiselect field."""
from frappe.contacts.doctype.contact.contact import get_full_name
if cached_contacts := get_cached_contacts(txt):
return cached_contacts[:page_length]
reportview_conditions = build_match_conditions("Contact")
match_conditions = f"and {reportview_conditions}" if reportview_conditions else ""
fields = ["name", "first_name", "middle_name", "last_name", "company_name"]
contacts = frappe.get_list(
"Contact",
fields=fields + ["`tabContact Email`.email_id"],
filters=[
["Contact Email", "email_id", "is", "set"],
],
or_filters=[[field, "like", f"%{txt}%"] for field in fields]
+ [["Contact Email", "email_id", "like", f"%{txt}%"]],
limit_page_length=page_length,
)
# The multiselect field will store the `label` as the selected value.
# The `value` is just used as a unique key to distinguish between the options.
# https://github.com/frappe/frappe/blob/6c6a89bcdd9454060a1333e23b855d0505c9ebc2/frappe/public/js/frappe/form/controls/autocomplete.js#L29-L35
out = frappe.db.sql(
f"""select name as value, email_id as label,
concat(first_name, ifnull(concat(' ',last_name), '' )) as description
from tabContact
where (name like %(txt)s or email_id like %(txt)s) and email_id != ''
{match_conditions}
limit %(page_length)s""",
{"txt": f"%{txt}%", "page_length": page_length},
as_dict=True,
)
out = list(filter(None, out))
result = [
frappe._dict(
value=d.name,
label=d.email_id,
description=get_full_name(d.first_name, d.middle_name, d.last_name, d.company_name),
)
for d in contacts
]
update_contact_cache(out)
update_contact_cache(result)
return out
return result
def get_system_managers():

View file

@ -857,7 +857,7 @@ class InboundMail(Email):
"""Remove Prefixes like 'fw', FWD', 're' etc from subject."""
# Match strings like "fw:", "re :" etc.
regex = r"(^\s*(fw|fwd|wg)[^:]*:|\s*(re|aw)[^:]*:\s*)*"
return frappe.as_unicode(strip(re.sub(regex, "", subject, 0, flags=re.IGNORECASE)))
return frappe.as_unicode(strip(re.sub(regex, "", subject, count=0, flags=re.IGNORECASE)))
@staticmethod
def get_email_fields(doctype):

View file

@ -11,6 +11,10 @@ class SiteNotSpecifiedError(Exception):
super(Exception, self).__init__(self.message)
class UrlSchemeNotSupported(Exception):
pass
class ValidationError(Exception):
http_status_code = 417

View file

@ -427,6 +427,11 @@ after_job = [
extend_bootinfo = [
"frappe.utils.telemetry.add_bootinfo",
"frappe.core.doctype.user_permission.user_permission.send_user_permissions",
]
naming_series_variables = {
"PM": "frappe.tests.test_naming.parse_naming_series_variable",
}
get_changelog_feed = "frappe.desk.doctype.changelog_feed.changelog_feed.get_feed"

View file

@ -133,7 +133,7 @@ def install_db(
from frappe.database import setup_database
if not db_type:
db_type = frappe.conf.db_type or "mariadb"
db_type = frappe.conf.db_type
if not root_login and db_type == "mariadb":
root_login = "root"
@ -772,7 +772,7 @@ def is_downgrade(sql_file_path, verbose=False):
# This function is only tested with mariadb
# TODO: Add postgres support
if frappe.conf.db_type not in (None, "mariadb"):
if frappe.conf.db_type != "mariadb":
return False
from semantic_version import Version
@ -824,7 +824,7 @@ def is_partial(sql_file_path):
def partial_restore(sql_file_path, verbose=False):
sql_file = extract_sql_from_archive(sql_file_path)
if frappe.conf.db_type in (None, "mariadb"):
if frappe.conf.db_type == "mariadb":
from frappe.database.mariadb.setup_db import import_db_from_sql
elif frappe.conf.db_type == "postgres":
import warnings

View file

@ -481,7 +481,7 @@ def update_event_in_google_calendar(doc, method=None):
event["description"] = doc.description
event["recurrence"] = repeat_on_to_google_calendar_recurrence_rule(doc)
event["status"] = (
"cancelled" if doc.event_type == "Cancelled" or doc.status == "Closed" else event.get("status")
"cancelled" if doc.status == "Cancelled" or doc.status == "Closed" else event.get("status")
)
event.update(
format_date_according_to_google_calendar(

View file

@ -3,7 +3,8 @@
import hashlib
import json
import time
from typing import Any, Generator, Iterable
from collections.abc import Generator, Iterable
from typing import Any
from werkzeug.exceptions import NotFound

View file

@ -3,7 +3,8 @@
import datetime
import re
from typing import TYPE_CHECKING, Callable, Optional
from collections.abc import Callable
from typing import TYPE_CHECKING, Optional
import frappe
from frappe import _
@ -338,11 +339,11 @@ def parse_naming_series(
part = determine_consecutive_week_number(today)
elif e == "timestamp":
part = str(today)
elif e == "FY":
part = frappe.defaults.get_user_default("fiscal_year")
elif doc and (e.startswith("{") or doc.get(e, _sentinel) is not _sentinel):
e = e.replace("{", "").replace("}", "")
part = doc.get(e)
elif method := has_custom_parser(e):
part = frappe.get_attr(method[0])(doc, e)
else:
part = e
@ -354,6 +355,11 @@ def parse_naming_series(
return name
def has_custom_parser(e):
"""Returns true if the naming series part has a custom parser"""
return frappe.get_hooks("naming_series_variables", {}).get(e)
def determine_consecutive_week_number(datetime):
"""Determines the consecutive calendar week"""
m = datetime.month

View file

@ -226,3 +226,4 @@ frappe.desk.doctype.form_tour.patches.introduce_ui_tours
execute:frappe.delete_doc_if_exists("Workspace", "Customization")
execute:frappe.db.set_single_value("Document Naming Settings", "default_amend_naming", "Amend Counter")
execute:frappe.delete_doc_if_exists("DocType", "Error Snapshot")
frappe.patches.v15_0.move_event_cancelled_to_status

View file

@ -0,0 +1,12 @@
import frappe
def execute():
Event = frappe.qb.DocType("Event")
query = (
frappe.qb.update(Event)
.set(Event.event_type, "Private")
.set(Event.status, "Cancelled")
.where(Event.event_type == "Cancelled")
)
query.run()

View file

@ -93,10 +93,10 @@ def has_permission(
doc = frappe.get_doc(meta.name, doc)
perm = get_doc_permissions(doc, user=user, ptype=ptype).get(ptype)
if not perm:
push_perm_check_log(
_("User {0} does not have access to this document").format(frappe.bold(user))
+ f": {_(doc.doctype)} - {doc.name}"
)
msg = _("User {0} does not have access to this document").format(frappe.bold(user))
if frappe.has_permission(doc.doctype):
msg += f": {_(doc.doctype)} - {doc.name}"
push_perm_check_log(msg)
else:
if ptype == "submit" and not cint(meta.is_submittable):
push_perm_check_log(_("Document Type is not submittable"))

View file

@ -372,10 +372,11 @@ frappe.PrintFormatBuilder = class PrintFormatBuilder {
if (!$item.hasClass("print-format-builder-field")) {
var fieldname = $item.attr("data-fieldname");
let field;
if (fieldname === "_custom_html") {
var field = me.get_custom_html_field();
field = me.get_custom_html_field();
} else {
var field = frappe.meta.get_docfield(me.print_format.doc_type, fieldname);
field = frappe.meta.get_docfield(me.print_format.doc_type, fieldname);
}
var html = frappe.render_template("print_format_builder_field", {
@ -561,7 +562,7 @@ frappe.PrintFormatBuilder = class PrintFormatBuilder {
resize();
} else if (new_no_of_columns > no_of_columns) {
// add empty column and resize old columns
for (var i = no_of_columns; i < new_no_of_columns; i++) {
for (let i = no_of_columns; i < new_no_of_columns; i++) {
var col = $(
'<div class="section-column">\
<div class="print-format-builder-column"></div></div>'

View file

@ -3,7 +3,7 @@ import Sidebar from "./components/Sidebar.vue"
import Tabs from "./components/Tabs.vue";
import { computed, onMounted, watch, ref } from "vue";
import { useStore } from "./store";
import { onClickOutside, useMagicKeys, whenever } from "@vueuse/core";
import { onClickOutside } from "@vueuse/core";
let store = useStore();
@ -14,19 +14,6 @@ let should_render = computed(() => {
let container = ref(null);
onClickOutside(container, () => store.form.selected_field = null);
// cmd/ctrl + s to save the form
const { meta_s, ctrl_s } = useMagicKeys();
whenever(() => meta_s.value || ctrl_s.value, () => {
if (store.dirty) {
store.save_changes();
}
});
function setup_change_doctype_dialog() {
store.page.$title_area.on("click", () => {
frappe.pages["form-builder"].select_doctype();
});
}
watch(
() => store.form.layout,
@ -34,10 +21,7 @@ watch(
{ deep: true }
);
onMounted(() => {
store.fetch();
setup_change_doctype_dialog();
});
onMounted(() => store.fetch());
</script>
<template>
@ -62,9 +46,8 @@ onMounted(() => {
<style lang="scss" scoped>
.form-builder-container {
margin-bottom: -60px;
margin: -12px -20px -5px;
display: flex;
gap: 20px;
&.resizing {
user-select: none;
@ -79,12 +62,20 @@ onMounted(() => {
flex: 1;
}
.form-sidebar,
.form-sidebar {
border-right: 1px solid var(--border-color);
border-bottom-left-radius: var(--border-radius);
}
.form-main {
border-radius: var(--border-radius);
box-shadow: var(--card-shadow);
background-color: var(--card-bg);
margin: 10px;
}
.form-sidebar,
.form-main {
:deep(.section-columns.has-one-column .field) {
input.form-control, .signature-field {
width: calc(50% - 19px);

View file

@ -67,7 +67,7 @@ function on_drag_end(evt) {
<style lang="scss" scoped>
.fields-container {
height: calc(100vh - 250px);
height: calc(100vh - 233px);
overflow-y: auto;
display: grid;
gap: 8px;

View file

@ -77,7 +77,7 @@ watch(
opacity: 0;
background-color: var(--bg-gray);
transition: opacity 0.2s ease;
z-index: 10;
z-index: 4;
cursor: col-resize;
&:hover, &.resizing {

View file

@ -20,7 +20,8 @@ function activate_tab(tab) {
nextTick(() => {
$(".tabs .tab.active")[0].scrollIntoView({
behavior: "smooth",
inline: "center"
inline: "center",
block: "nearest",
});
});
}
@ -280,6 +281,7 @@ function delete_tab(with_children) {
.tab-contents {
max-height: calc(100vh - 210px);
overflow-y: auto;
overflow-x: hidden;
border-radius: var(--border-radius);
min-height: 70px;

View file

@ -33,12 +33,12 @@ let doctype_df = computed(() => {
}));
let options = [{ label: __("Select DocType"), value: "" }, ...doctypes.value];
return { fieldtype: "Select", label: __("Fetch Form"), options };
return { fieldtype: "Select", label: __("Fetch From"), options };
});
let field_df = computedAsync(async () => {
let options = [{ label: __("Select Field"), value: "" }];
let df = { fieldtype: "Select", label: __("Fetch Form"), options };
let df = { fieldtype: "Select", label: __("Fetch From"), options };
if (!doctype.value) return df;
let doctype_name = doctypes.value?.find(df => df.value == doctype.value).doctype_name;
if (!doctype_name) return df;

View file

@ -5,9 +5,10 @@ import FormBuilderComponent from "./FormBuilder.vue";
import { registerGlobalComponents } from "./globals.js";
class FormBuilder {
constructor({ wrapper, page, doctype, customize }) {
constructor({ wrapper, frm, doctype, customize }) {
this.$wrapper = $(wrapper);
this.page = page;
this.frm = frm;
this.page = frm.page;
this.doctype = doctype;
this.customize = customize;
this.read_only = false;
@ -21,21 +22,14 @@ class FormBuilder {
this.setup_page_actions();
!refresh && this.setup_app();
refresh && this.update_store();
this.watch_changes();
}
async setup_page_actions() {
// clear actions
this.page.clear_actions();
this.page.clear_menu();
this.page.clear_custom_actions();
// setup page actions
this.primary_btn = this.page.set_primary_action(__("Save"), () =>
this.store.save_changes()
);
setup_page_actions() {
this.preview_btn?.remove();
this.preview_btn = this.page.add_button(__("Show Preview"), () => {
this.store.frm.layout.tabs.find((tab) => tab.label === "Form").set_active();
this.store.preview = !this.store.preview;
if (this.store.read_only && !this.read_only) {
@ -44,34 +38,10 @@ class FormBuilder {
this.store.read_only = this.store.preview;
this.read_only = true;
});
this.reset_changes_btn = this.page.add_button(__("Reset Changes"), () => {
this.store.reset_changes();
// toggle preview btn text
this.preview_btn.text(this.store.preview ? __("Hide Preview") : __("Show Preview"));
});
this.go_to_doctype_list_btn = this.page.add_button(
__("Go to {0} List", [__(this.doctype)]),
() => {
window.open(`/app/${frappe.router.slug(this.doctype)}`);
}
);
this.customize_form_btn = this.page.add_menu_item(__("Switch to Customize"), () => {
frappe.set_route("form-builder", this.doctype, "customize");
});
this.doctype_form_btn = this.page.add_menu_item(__("Switch to DocType"), () => {
frappe.set_route("form-builder", this.doctype);
});
this.go_to_doctype_btn = this.page.add_menu_item(__("Go to DocType"), () =>
frappe.set_route("Form", "DocType", this.doctype)
);
this.go_to_customize_form_btn = this.page.add_menu_item(__("Go to Customize Form"), () =>
frappe.set_route("Form", "Customize Form", {
doc_type: this.doctype,
})
);
}
setup_app() {
@ -85,9 +55,7 @@ class FormBuilder {
// create a store
this.store = useStore();
this.store.doctype = this.doctype;
this.store.is_customize_form = this.customize;
this.store.page = this.page;
this.update_store();
// register global components
registerGlobalComponents(app);
@ -96,69 +64,21 @@ class FormBuilder {
this.$form_builder = app.mount(this.$wrapper.get(0));
}
update_store() {
this.store.doctype = this.doctype;
this.store.is_customize_form = this.customize;
this.store.page = this.page;
this.store.frm = this.frm;
}
watch_changes() {
watchEffect(() => {
if (this.store.dirty) {
this.page.set_indicator(__("Not Saved"), "orange");
this.reset_changes_btn.show();
if (this.store.dirty || this.frm.is_dirty()) {
this.frm.dirty();
} else {
this.page.clear_indicator();
this.reset_changes_btn.hide();
}
// hide all buttons
this.go_to_doctype_list_btn.hide();
this.customize_form_btn.hide();
this.doctype_form_btn.hide();
this.go_to_doctype_btn.hide();
this.go_to_customize_form_btn.hide();
this.page.menu_btn_group.show();
let hide_menu = true;
// show customize form & Go to customize form btn
if (
this.store.doc &&
!this.store.doc.custom &&
!this.store.doc.issingle &&
!this.store.is_customize_form &&
!in_list(frappe.model.core_doctypes_list, this.doctype)
) {
this.customize_form_btn.show();
this.go_to_customize_form_btn.show();
hide_menu = false;
}
// show doctype form & Go to doctype form btn
if (
this.store.doc &&
!this.store.doc.custom &&
!this.store.doc.issingle &&
this.store.is_customize_form
) {
this.doctype_form_btn.show();
this.go_to_doctype_btn.show();
hide_menu = false;
}
// show Go to {0} List or Go to {0} button
if (this.store.doc && !this.store.doc.istable) {
let label = this.store.doc.issingle
? __("Go to {0}", [__(this.doctype)])
: __("Go to {0} List", [__(this.doctype)]);
this.go_to_doctype_list_btn.text(label).show();
}
if (hide_menu && window.matchMedia("(min-device-width: 992px)").matches) {
this.page.menu_btn_group.hide();
}
// toggle preview btn text
this.preview_btn.text(this.store.preview ? __("Hide Preview") : __("Show Preview"));
// toggle primary btn and show indicator based on read_only state
this.primary_btn.toggle(!this.store.read_only);
if (this.store.read_only) {
let message = this.store.preview ? __("Preview Mode") : __("Read Only");
this.page.set_indicator(message, "orange");

View file

@ -5,6 +5,7 @@ import { useDebouncedRefHistory, onKeyDown } from "@vueuse/core";
export const useStore = defineStore("form-builder-store", () => {
let doctype = ref("");
let frm = ref(null);
let doc = ref(null);
let docfields = ref([]);
let custom_docfields = ref([]);
@ -69,17 +70,9 @@ export const useStore = defineStore("form-builder-store", () => {
}
async function fetch() {
await frappe.model.clear_doc("DocType", doctype.value);
await frappe.model.with_doctype(doctype.value);
if (is_customize_form.value) {
await frappe.model.with_doc("Customize Form");
let _doc = frappe.get_doc("Customize Form");
_doc.doc_type = doctype.value;
let r = await frappe.call({ method: "fetch_to_customize", doc: _doc });
doc.value = r.docs[0];
} else {
doc.value = await frappe.db.get_doc("DocType", doctype.value);
doc.value = frm.value.doc;
if (doctype.value.startsWith("new-doctype-")) {
doc.value.fields = [get_df("Data", "", __("Title"))];
}
if (!get_docfields.value.length) {
@ -99,18 +92,19 @@ export const useStore = defineStore("form-builder-store", () => {
nextTick(() => {
dirty.value = false;
frm.value.doc.__unsaved = 0;
frm.value.page.clear_indicator();
read_only.value =
!is_customize_form.value && !frappe.boot.developer_mode && !doc.value.custom;
preview.value = false;
});
setup_undo_redo();
setup_breadcrumbs();
}
let undo_redo_keyboard_event = onKeyDown(true, (e) => {
if (!ref_history.value) return;
if (e.ctrlKey || e.metaKey) {
if (frm.value.get_active_tab().label == "Form" && (e.ctrlKey || e.metaKey)) {
if (e.key === "z" && !e.shiftKey && ref_history.value.canUndo) {
ref_history.value.undo();
} else if (e.key === "z" && e.shiftKey && ref_history.value.canRedo) {
@ -125,30 +119,17 @@ export const useStore = defineStore("form-builder-store", () => {
undo_redo_keyboard_event;
}
function setup_breadcrumbs() {
!is_customize_form.value && frappe.model.init_doctype("DocType");
let breadcrumbs = `
<li><a href="/app/doctype">${__("DocType")}</a></li>
<li><a href="/app/doctype/${doctype.value}">${__(doctype.value)}</a></li>
`;
if (is_customize_form.value) {
breadcrumbs = `
<li><a href="/app/customize-form?doc_type=${doctype.value}">
${__("Customize Form")}
</a></li>
`;
}
breadcrumbs += `<li class="disabled"><a href="#">${__("Form Builder")}</a></li>`;
frappe.breadcrumbs.clear();
frappe.breadcrumbs.$breadcrumbs.append(breadcrumbs);
}
function reset_changes() {
fetch();
}
function validate_fields(fields, is_table) {
fields = scrub_field_names(fields);
let error_message = "";
let has_fields = fields.some((df) => {
return !["Section Break", "Tab Break", "Column Break"].includes(df.fieldtype);
});
if (!has_fields) {
error_message = __("DocType must have atleast one field");
}
let not_allowed_in_list_view = ["Attach Image", ...frappe.model.no_value_type];
if (is_table) {
@ -168,70 +149,56 @@ export const useStore = defineStore("form-builder-store", () => {
// check if fieldname already exist
let duplicate = fields.filter((f) => f.fieldname == df.fieldname);
if (duplicate.length > 1) {
frappe.throw(__("Fieldname {0} appears multiple times", get_field_data(df)));
error_message = __("Fieldname {0} appears multiple times", get_field_data(df));
}
// Link & Table fields should always have options set
if (in_list(["Link", ...frappe.model.table_fields], df.fieldtype) && !df.options) {
frappe.throw(
__("Options is required for field {0} of type {1}", get_field_data(df))
error_message = __(
"Options is required for field {0} of type {1}",
get_field_data(df)
);
}
// Do not allow if field is hidden & required but doesn't have default value
if (df.hidden && df.reqd && !df.default) {
frappe.throw(
__(
"{0} cannot be hidden and mandatory without any default value",
get_field_data(df)
)
error_message = __(
"{0} cannot be hidden and mandatory without any default value",
get_field_data(df)
);
}
// In List View is not allowed for some fieldtypes
if (df.in_list_view && in_list(not_allowed_in_list_view, df.fieldtype)) {
frappe.throw(
__(
"'In List View' is not allowed for field {0} of type {1}",
get_field_data(df)
)
error_message = __(
"'In List View' is not allowed for field {0} of type {1}",
get_field_data(df)
);
}
// In Global Search is not allowed for no_value_type fields
if (df.in_global_search && in_list(frappe.model.no_value_type, df.fieldtype)) {
frappe.throw(
__(
"'In Global Search' is not allowed for field {0} of type {1}",
get_field_data(df)
)
error_message = __(
"'In Global Search' is not allowed for field {0} of type {1}",
get_field_data(df)
);
}
});
return error_message;
}
async function save_changes() {
if (!dirty.value) {
frappe.show_alert({ message: __("No changes to save"), indicator: "orange" });
return;
}
function update_fields() {
if (!dirty.value && !frm.value.is_new()) return;
frappe.dom.freeze(__("Saving..."));
try {
if (is_customize_form.value) {
let _doc = frappe.get_doc("Customize Form");
_doc.doc_type = doctype.value;
_doc.fields = get_updated_fields();
validate_fields(_doc.fields, _doc.istable);
await frappe.call({ method: "save_customization", doc: _doc });
} else {
doc.value.fields = get_updated_fields();
validate_fields(doc.value.fields, doc.value.istable);
await frappe.call("frappe.client.save", { doc: doc.value });
frappe.toast("Fields Table Updated");
}
fetch();
let fields = get_updated_fields();
let has_error = validate_fields(fields, doc.value.istable);
if (has_error) return has_error;
doc.value.fields = fields;
return fields;
} catch (e) {
console.error(e);
} finally {
@ -242,6 +209,9 @@ export const useStore = defineStore("form-builder-store", () => {
function get_updated_fields() {
let fields = [];
let idx = 0;
let new_field_name = is_customize_form.value
? "new-customize-form-field-"
: "new-docfield-";
let layout_fields = JSON.parse(JSON.stringify(form.value.layout.tabs));
@ -252,6 +222,9 @@ export const useStore = defineStore("form-builder-store", () => {
) {
idx++;
tab.df.idx = idx;
if (tab.df.__unsaved && tab.df.__islocal) {
tab.df.name = new_field_name + idx;
}
fields.push(tab.df);
}
@ -265,6 +238,9 @@ export const useStore = defineStore("form-builder-store", () => {
if ((j == 0 && is_df_updated(section.df, get_df("Section Break"))) || j > 0) {
idx++;
section.df.idx = idx;
if (section.df.__unsaved && section.df.__islocal) {
section.df.name = new_field_name + idx;
}
fields.push(section.df);
}
@ -277,12 +253,18 @@ export const useStore = defineStore("form-builder-store", () => {
) {
idx++;
column.df.idx = idx;
if (column.df.__unsaved && column.df.__islocal) {
column.df.name = new_field_name + idx;
}
fields.push(column.df);
}
column.fields.forEach((field) => {
idx++;
field.df.idx = idx;
if (field.df.__unsaved && field.df.__islocal) {
field.df.name = new_field_name + idx;
}
fields.push(field.df);
section.has_fields = true;
});
@ -300,9 +282,11 @@ export const useStore = defineStore("form-builder-store", () => {
}
function is_df_updated(df, new_df) {
delete df.name;
delete new_df.name;
return JSON.stringify(df) != JSON.stringify(new_df);
let df_copy = JSON.parse(JSON.stringify(df));
let new_df_copy = JSON.parse(JSON.stringify(new_df));
delete df_copy.name;
delete new_df_copy.name;
return JSON.stringify(df_copy) != JSON.stringify(new_df_copy);
}
function get_layout() {
@ -311,6 +295,7 @@ export const useStore = defineStore("form-builder-store", () => {
return {
doctype,
frm,
doc,
form,
dirty,
@ -326,9 +311,8 @@ export const useStore = defineStore("form-builder-store", () => {
has_standard_field,
is_user_generated_field,
fetch,
reset_changes,
validate_fields,
save_changes,
update_fields,
get_updated_fields,
is_df_updated,
get_layout,

View file

@ -61,8 +61,6 @@ export function create_layout(fields) {
if (df.fieldname) {
// make a copy to avoid mutation bugs
df = JSON.parse(JSON.stringify(df));
} else {
continue;
}
if (df.fieldtype === "Tab Break") {
@ -71,7 +69,7 @@ export function create_layout(fields) {
set_section(df);
} else if (df.fieldtype === "Column Break") {
set_column(df);
} else if (df.name) {
} else {
if (!column) set_column();
let field = { df: df };

View file

@ -28,8 +28,11 @@ frappe.assets = {
}
if (localStorage._last_load) {
var not_updated_since = new Date() - new Date(localStorage._last_load);
if (not_updated_since < 10000 || not_updated_since > 86400000) {
let not_updated_since = new Date() - new Date(localStorage._last_load);
// Evict cache every 2 days
// Evict cache if page is reloaded within 10 seconds. Which could be user trying to
// refresh if things feel broken.
if ((not_updated_since < 5000 && is_reload()) || not_updated_since > 2 * 86400000) {
frappe.assets.clear_local_storage();
}
} else {
@ -184,3 +187,15 @@ frappe.assets = {
return path;
},
};
function is_reload() {
try {
return window.performance
?.getEntriesByType("navigation")
.map((nav) => nav.type)
.includes("reload");
} catch (e) {
// Safari probably
return true;
}
}

View file

@ -122,7 +122,6 @@ class Picker {
}
setup_hue_event() {
// eslint-disable-next-line no-unused-vars
let on_drag = (x, y) => {
this.hue_selector_position.x = x;
this.hue = Math.round((x * 360) / this.hue_map.offsetWidth);
@ -152,9 +151,7 @@ class Picker {
}
get_pointer_coords() {
// eslint-disable-next-line no-unused-vars
let h, s, v;
// eslint-disable-next-line no-unused-vars
[h, s, v] = utils.get_hsv(this.get_color());
let width = this.color_map.offsetWidth;
let height = this.color_map.offsetHeight;

View file

@ -8,7 +8,7 @@ frappe.defaults = {
if (!d && frappe.defaults.is_a_user_permission_key(key)) {
d = defaults[frappe.model.scrub(key)];
// Check for default user permission values
user_default = this.get_user_permission_default(key, defaults);
let user_default = this.get_user_permission_default(key, defaults);
if (user_default) d = user_default;
}
if ($.isArray(d)) d = d[0];
@ -130,4 +130,12 @@ frappe.defaults = {
}
});
},
load_user_permission_from_boot: function () {
if (frappe.boot.user.user_permissions) {
this._user_permissions = Object.assign({}, frappe.boot.user.user_permissions);
} else {
frappe.defaults.update_user_permissions();
}
},
};

View file

@ -151,27 +151,6 @@ frappe.Application = class Application {
// REDESIGN-TODO: Fix preview popovers
this.link_preview = new frappe.ui.LinkPreview();
if (!frappe.boot.developer_mode) {
if (frappe.user.has_role("System Manager")) {
setInterval(function () {
frappe.call({
method: "frappe.core.doctype.log_settings.log_settings.has_unseen_error_log",
args: {
user: frappe.session.user,
},
callback: function (r) {
if (r.message && r.message.show_alert) {
frappe.show_alert({
indicator: "red",
message: r.message.message,
});
}
},
});
}, 600000); // check every 10 minutes
}
}
}
set_route() {
@ -298,7 +277,7 @@ frappe.Application = class Application {
}
load_user_permissions() {
frappe.defaults.update_user_permissions();
frappe.defaults.load_user_permission_from_boot();
frappe.realtime.on(
"update_user_permissions",

View file

@ -134,8 +134,6 @@ frappe.model.DocTypeController = class DocTypeController extends frappe.ui.form.
setTimeout(() => (this.frm.__from_autoname = false), 500);
}
this.frm.set_df_property("fields", "reqd", this.frm.doc.autoname !== "Prompt");
}
setup_fetch_from_fields(doc, doctype, docname) {

View file

@ -177,7 +177,7 @@ frappe.ui.form.ControlAutocomplete = class ControlAutoComplete extends frappe.ui
options = options.map((o) => {
if (typeof o !== "string") {
o.label = cstr(o.label);
o.label = __(cstr(o.label));
o.value = cstr(o.value);
}
return o;

View file

@ -62,25 +62,22 @@ frappe.ui.form.Control = class BaseControl {
// like in case of a dialog box
if (cint(this.df.hidden)) {
// eslint-disable-next-line
if (explain) console.log("By Hidden: None"); // eslint-disable-line no-console
if (explain) console.log("By Hidden: None");
return "None";
} else if (cint(this.df.hidden_due_to_dependency)) {
// eslint-disable-next-line
if (explain) console.log("By Hidden Dependency: None"); // eslint-disable-line no-console
if (explain) console.log("By Hidden Dependency: None");
return "None";
} else if (
cint(this.df.read_only || this.df.is_virtual || this.df.fieldtype === "Read Only")
) {
// eslint-disable-next-line
if (explain) console.log("By Read Only: Read"); // eslint-disable-line no-console
if (explain) console.log("By Read Only: Read");
status = "Read";
} else if (
(this.grid && this.grid.display_status == "Read") ||
(this.layout && this.layout.grid && this.layout.grid.display_status == "Read")
) {
// parent grid is read
if (explain) console.log("By Parent Grid Read-only: Read"); // eslint-disable-line no-console
if (explain) console.log("By Parent Grid Read-only: Read");
status = "Read";
}
@ -112,7 +109,7 @@ frappe.ui.form.Control = class BaseControl {
var grid = this.grid || this.layout.grid;
if (grid.display_status == "Read") {
status = "Read";
if (explain) console.log("By Parent Grid Read-only: Read"); // eslint-disable-line no-console
if (explain) console.log("By Parent Grid Read-only: Read");
}
}
@ -132,8 +129,7 @@ frappe.ui.form.Control = class BaseControl {
is_null(value) &&
!in_list(["HTML", "Image", "Button", "Geolocation"], this.df.fieldtype)
) {
// eslint-disable-next-line
if (explain) console.log("By Hide Read-only, null fields: None"); // eslint-disable-line no-console
if (explain) console.log("By Hide Read-only, null fields: None");
status = "None";
}

View file

@ -165,7 +165,6 @@ frappe.ui.form.ControlCode = class ControlCode extends frappe.ui.form.ControlTex
const valid_languages = Object.keys(language_map);
if (language && !valid_languages.includes(language)) {
// eslint-disable-next-line
console.warn(
`Invalid language option provided for field "${
this.df.label

View file

@ -47,7 +47,6 @@ frappe.ui.form.make_control = function (opts) {
if (frappe.ui.form[control_class_name]) {
return new frappe.ui.form[control_class_name](opts);
} else {
// eslint-disable-next-line
console.log("Invalid Control Name: " + opts.df.fieldtype);
}
};

View file

@ -591,13 +591,19 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
let field_value = "";
for (const [target_field, source_field] of Object.entries(fetch_map)) {
if (value) field_value = response[source_field];
frappe.model.set_value(
df.parent,
docname,
target_field,
field_value,
df.fieldtype
);
let target_df = frappe.meta.get_docfield(df.parent, target_field);
let target_value = frappe.model.get_value(df.parent, docname, target_field);
if (target_df?.fetch_if_empty && target_value) {
continue;
} else {
frappe.model.set_value(
df.parent,
docname,
target_field,
field_value,
df.fieldtype
);
}
}
}

View file

@ -1,4 +1,3 @@
/* eslint-disable */
import Quill from "quill";
const Embed = Quill.import("blots/embed");

View file

@ -1,4 +1,3 @@
/* eslint-disable */
const Keys = {
TAB: "Tab",
ENTER: "Enter",

View file

@ -1,4 +1,3 @@
/* eslint-disable */
import Quill from "quill";
import Keys from "./constants/keys";
import "./blots/mention";

View file

@ -77,9 +77,12 @@ frappe.ui.form.Form = class FrappeForm {
// wrapper
this.wrapper = this.parent;
this.$wrapper = $(this.wrapper);
let is_single_column = this.doctype === "DocType" ? true : this.meta.hide_toolbar;
frappe.ui.make_app_page({
parent: this.wrapper,
single_column: this.meta.hide_toolbar,
single_column: is_single_column,
});
this.page = this.wrapper.page;
this.layout_main = this.page.main.get(0);
@ -157,12 +160,14 @@ frappe.ui.form.Form = class FrappeForm {
action: () => this.undo_manager.undo(),
page: this.page,
description: __("Undo last action"),
condition: () => !this.is_form_builder(),
});
frappe.ui.keys.add_shortcut({
shortcut: "shift+ctrl+z",
action: () => this.undo_manager.redo(),
page: this.page,
description: __("Redo last action"),
condition: () => !this.is_form_builder(),
});
frappe.ui.keys.add_shortcut({
shortcut: "ctrl+y",
@ -747,7 +752,7 @@ frappe.ui.form.Form = class FrappeForm {
me.show_success_action();
})
.catch((e) => {
console.error(e); // eslint-disable-line
console.error(e);
});
}
@ -1361,6 +1366,13 @@ frappe.ui.form.Form = class FrappeForm {
return this.doc.__islocal;
}
is_form_builder() {
return (
in_list(["DocType", "Customize Form"], this.doctype) &&
this.get_active_tab().label == "Form"
);
}
get_perm(permlevel, access_type) {
return this.perm[permlevel] ? this.perm[permlevel][access_type] : null;
}

View file

@ -441,12 +441,13 @@ export default class Grid {
if (d.name === undefined) {
d.name = "row " + d.idx;
}
let grid_row;
if (this.grid_rows[ri] && !append_row) {
var grid_row = this.grid_rows[ri];
grid_row = this.grid_rows[ri];
grid_row.doc = d;
grid_row.refresh();
} else {
var grid_row = new GridRow({
grid_row = new GridRow({
parent: $rows,
parent_df: this.df,
docfields: this.docfields,

View file

@ -123,7 +123,7 @@ export default class GridRow {
])
.catch((e) => {
// aborted
console.trace(e); // eslint-disable-line
console.trace(e);
});
} else {
let data = null;

View file

@ -240,7 +240,6 @@ frappe.ui.form.Layout = class Layout {
}
make_page(df) {
// eslint-disable-line no-unused-vars
let me = this;
let head = $(`
<div class="form-clickable-section text-center">
@ -365,7 +364,10 @@ frappe.ui.form.Layout = class Layout {
const section = $(this).removeClass("empty-section visible-section");
if (section.find(".frappe-control:not(.hide-control)").length) {
section.addClass("visible-section");
} else {
} else if (
section.parent().hasClass("tab-pane") ||
section.parent().hasClass("form-page")
) {
// nothing visible, hide the section
section.addClass("empty-section");
}

View file

@ -602,7 +602,6 @@ frappe.ui.form.MultiSelectDialog = class MultiSelectDialog {
async get_filtered_parents_for_child_search() {
const parent_search_args = this.get_args_for_search();
parent_search_args.filter_fields = ["name"];
// eslint-disable-next-line no-unused-vars
const [response, _] = await this.perform_search(parent_search_args);
let parent_names = [];

View file

@ -156,6 +156,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) {
if (error_fields.length) {
let meta = frappe.get_meta(doc.doctype);
let message;
if (meta.istable) {
const table_field = frappe.meta.docfield_map[doc.parenttype][doc.parentfield];
@ -163,12 +164,12 @@ frappe.ui.form.save = function (frm, action, callback, btn) {
table_field.label || frappe.unscrub(table_field.fieldname)
).bold();
var message = __("Mandatory fields required in table {0}, Row {1}", [
message = __("Mandatory fields required in table {0}, Row {1}", [
table_label,
doc.idx,
]);
} else {
var message = __("Mandatory fields required in {0}", [__(doc.doctype)]);
message = __("Mandatory fields required in {0}", [__(doc.doctype)]);
}
message = message + "<br><br><ul><li>" + error_fields.join("</li><li>") + "</ul>";
frappe.msgprint({

View file

@ -1,6 +1,5 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
/* eslint-disable no-console */
window.refresh_many = function (flist, dn, table_field) {
for (var i in flist) {

View file

@ -213,12 +213,7 @@ frappe.ui.form.ScriptManager = class ScriptManager {
df.read_only == 1 ||
df.is_virtual == 1;
if (
is_read_only_field &&
df.fetch_from &&
(!df.fetch_if_empty || (df.fetch_if_empty && !me.frm.doc[df.fieldname])) &&
df.fetch_from.indexOf(".") != -1
) {
if (is_read_only_field && df.fetch_from && df.fetch_from.indexOf(".") != -1) {
var parts = df.fetch_from.split(".");
me.frm.add_fetch(parts[0], parts[1], df.fieldname, df.parent);
}

View file

@ -31,11 +31,12 @@ frappe.ui.form.Toolbar = class Toolbar {
}
}
set_title() {
let title;
if (this.frm.is_new()) {
var title = __("New {0}", [__(this.frm.meta.name)]);
title = __("New {0}", [__(this.frm.meta.name)]);
} else if (this.frm.meta.title_field) {
let title_field = (this.frm.doc[this.frm.meta.title_field] || "").toString().trim();
var title = strip_html(title_field || this.frm.docname);
title = strip_html(title_field || this.frm.docname);
if (
this.frm.doc.__islocal ||
title === this.frm.docname ||
@ -51,7 +52,7 @@ frappe.ui.form.Toolbar = class Toolbar {
});
}
} else {
var title = this.frm.docname;
title = this.frm.docname;
}
var me = this;

View file

@ -668,7 +668,6 @@ class FilterArea {
const fields_dict = this.list_view.page.fields_dict;
let out = filters.reduce((out, filter) => {
// eslint-disable-next-line
const [dt, fieldname, condition, value] = filter;
out.promise = out.promise || Promise.resolve();
out.non_standard_filters = out.non_standard_filters || [];

View file

@ -34,7 +34,7 @@ frappe.views.ListSidebar = class ListSidebar {
) {
this.sidebar.find(".list-tags").remove();
} else {
this.sidebar.find(".list-stats").on("click", (e) => {
this.sidebar.find(".list-stats").on("show.bs.dropdown", (e) => {
this.reload_stats();
});
}
@ -187,6 +187,10 @@ frappe.views.ListSidebar = class ListSidebar {
get_stats() {
var me = this;
let dropdown_options = me.sidebar.find(".list-stats-dropdown .stat-result");
this.set_loading_state(dropdown_options);
frappe.call({
method: "frappe.desk.reportview.get_sidebar_stats",
type: "GET",
@ -208,6 +212,14 @@ frappe.views.ListSidebar = class ListSidebar {
});
}
set_loading_state(dropdown) {
dropdown.html(`<li>
<div class="empty-state">
${__("Loading...")}
</div>
</li>`);
}
render_stat(stats) {
let args = {
stats: stats,

View file

@ -587,9 +587,6 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
</div>
`);
this.setup_new_doc_event();
if (this.list_view_settings && !this.list_view_settings.disable_sidebar_stats) {
this.list_sidebar && this.list_sidebar.reload_stats();
}
this.toggle_paging && this.$paging_area.toggle(true);
}
@ -712,9 +709,10 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
get_column_html(col, doc) {
if (col.type === "Status" || col.df?.options == "Workflow State") {
let show_workflow_state = col.df?.options == "Workflow State";
return `
<div class="list-row-col hidden-xs ellipsis">
${this.get_indicator_html(doc)}
${this.get_indicator_html(doc, show_workflow_state)}
</div>
`;
}
@ -1014,8 +1012,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
return subject_html;
}
get_indicator_html(doc) {
const indicator = frappe.get_indicator(doc, this.doctype);
get_indicator_html(doc, show_workflow_state) {
const indicator = frappe.get_indicator(doc, this.doctype, show_workflow_state);
// sequence is important
const docstatus_description = [
__("Document is in draft state"),
@ -1544,26 +1542,28 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}
get_url_with_filters() {
const query_params = this.get_filters_for_args()
.map((filter) => {
if (filter[2] === "=") {
return `${filter[1]}=${encodeURIComponent(filter[3])}`;
}
return [
filter[1],
"=",
encodeURIComponent(JSON.stringify([filter[2], filter[3]])),
].join("");
})
.join("&");
let search_params = this.get_search_params();
let full_url = window.location.href.replace(window.location.search, "");
if (query_params) {
full_url += "?" + query_params;
if (search_params.size) {
full_url += "?" + search_params.toString();
}
return full_url;
}
get_search_params() {
let search_params = new URLSearchParams();
this.get_filters_for_args().forEach((filter) => {
if (filter[2] === "=") {
search_params.append(filter[1], filter[3]);
} else {
search_params.append(filter[1], JSON.stringify([filter[2], filter[3]]));
}
});
return search_params;
}
get_menu_items() {
const doctype = this.doctype;
const items = [];

View file

@ -2,6 +2,8 @@
// Adapted from John Resig - http://ejohn.org/ - MIT Licensed
frappe.template = { compiled: {}, debug: {} };
/* eslint-disable */
frappe.template.compile = function (str, name) {
var key = name || str;
@ -96,14 +98,17 @@ frappe.template.compile = function (str, name) {
return frappe.template.compiled[key];
};
/* eslint-enable */
frappe.render = function (str, data, name) {
return frappe.template.compile(str, name)(data);
};
frappe.render_template = function (name, data) {
let template;
if (name.indexOf(" ") !== -1) {
var template = name;
template = name;
} else {
var template = frappe.templates[name];
template = frappe.templates[name];
}
if (data === undefined) {
data = {};

View file

@ -23,7 +23,7 @@ frappe.has_indicator = function (doctype) {
return false;
};
frappe.get_indicator = function (doc, doctype) {
frappe.get_indicator = function (doc, doctype, show_workflow_state) {
if (doc.__unsaved) {
return [__("Not Saved"), "orange"];
}
@ -40,13 +40,13 @@ frappe.get_indicator = function (doc, doctype) {
workflow_fieldname = frappe.workflow.get_state_fieldname(doctype);
// workflow
if (workflow_fieldname && !without_workflow) {
if (workflow_fieldname && (!without_workflow || show_workflow_state)) {
var value = doc[workflow_fieldname];
if (value) {
var colour = "";
let colour = "";
if (locals["Workflow State"][value] && locals["Workflow State"][value].style) {
var colour = {
colour = {
Success: "green",
Warning: "orange",
Danger: "red",

View file

@ -173,7 +173,6 @@ $.extend(frappe.meta, {
});
if (!out) {
// eslint-disable-next-line
console.log(
__("Warning: Unable to find {0} in any table related to {1}", [
key,

View file

@ -462,8 +462,9 @@ $.extend(frappe.model, {
var val = locals[dt] && locals[dt][dn] && locals[dt][dn][fn];
var df = frappe.meta.get_docfield(dt, fn, dn);
let ret;
if (frappe.model.table_fields.includes(df.fieldtype)) {
var ret = false;
ret = false;
$.each(locals[df.options] || {}, function (k, d) {
if (d.parent == dn && d.parenttype == dt && d.parentfield == df.fieldname) {
ret = true;
@ -471,7 +472,7 @@ $.extend(frappe.model, {
}
});
} else {
var ret = !is_null(val);
ret = !is_null(val);
}
return ret ? true : false;
},
@ -616,12 +617,13 @@ $.extend(frappe.model, {
},
get_children: function (doctype, parent, parentfield, filters) {
let doc;
if ($.isPlainObject(doctype)) {
var doc = doctype;
var filters = parentfield;
var parentfield = parent;
doc = doctype;
filters = parentfield;
parentfield = parent;
} else {
var doc = frappe.get_doc(doctype, parent);
doc = frappe.get_doc(doctype, parent);
}
var children = doc[parentfield] || [];
@ -652,8 +654,8 @@ $.extend(frappe.model, {
var parent = null;
if (doc.parenttype) {
var parent = doc.parent,
parenttype = doc.parenttype,
parent = doc.parent;
var parenttype = doc.parenttype,
parentfield = doc.parentfield;
}
delete locals[doctype][name];

View file

@ -294,8 +294,8 @@ frappe.request.call = function (opts) {
status_code_handler(data, xhr);
}
} catch (e) {
console.log("Unable to handle success response", data); // eslint-disable-line
console.error(e); // eslint-disable-line
console.log("Unable to handle success response", data);
console.error(e);
}
})
.always(function (data, textStatus, xhr) {
@ -304,7 +304,7 @@ frappe.request.call = function (opts) {
data = JSON.parse(data);
}
if (data.responseText) {
var xhr = data;
var xhr = data; // eslint-disable-line
data = JSON.parse(data.responseText);
}
} catch (e) {
@ -348,8 +348,8 @@ frappe.request.call = function (opts) {
// if not handled by error handler!
opts.error_callback && opts.error_callback(xhr);
} catch (e) {
console.log("Unable to handle failed response"); // eslint-disable-line
console.error(e); // eslint-disable-line
console.log("Unable to handle failed response");
console.error(e);
}
});
};
@ -366,7 +366,6 @@ frappe.request.is_fresh = function (args, threshold) {
new Date() - past_request.timestamp < threshold &&
frappe.utils.deep_equal(args, past_request.args)
) {
// eslint-disable-next-line no-console
console.log("throttled");
return true;
}

View file

@ -37,7 +37,7 @@ frappe.ui.Scanner = class Scanner {
try {
this.options.on_scan(decodedResult);
} catch (error) {
console.error(error); // eslint-disable-line
console.error(error);
}
}
if (!this.options.multiple) {
@ -46,14 +46,13 @@ frappe.ui.Scanner = class Scanner {
}
},
(errorMessage) => {
// eslint-disable-line
// parse error, ignore it.
}
)
.catch((err) => {
this.is_alive = false;
this.hide_dialog();
console.error(err); // eslint-disable-line
console.error(err);
});
this.is_alive = true;
}

View file

@ -14,7 +14,6 @@ frappe.ui.color = {
if (color_names.includes(color_name)) {
return frappe.ui.color_map[color_name];
} else {
// eslint-disable-next-line
console.warn(`'color_name' can be one of ${color_names} and not ${color_name}`);
}
},
@ -52,7 +51,6 @@ frappe.ui.color = {
const color = this.get_color(color_name);
return color ? color[shades[shade]] : color_name;
} else {
// eslint-disable-next-line
console.warn(`'shade' can be one of ${Object.keys(shades)} and not ${shade}`);
}
},

Some files were not shown because too many files have changed in this diff Show more