diff --git a/cypress/integration/recorder.js b/cypress/integration/recorder.js deleted file mode 100644 index de95a852fc..0000000000 --- a/cypress/integration/recorder.js +++ /dev/null @@ -1,72 +0,0 @@ -context.skip("Recorder", () => { - before(() => { - cy.login(); - }); - - beforeEach(() => { - cy.visit("/app/recorder"); - return cy - .window() - .its("frappe") - .then((frappe) => { - // reset recorder - return frappe.xcall("frappe.recorder.stop").then(() => { - return frappe.xcall("frappe.recorder.delete"); - }); - }); - }); - - it("Recorder Empty State", () => { - cy.get(".page-head").findByTitle("Recorder").should("exist"); - - cy.get(".indicator-pill").should("contain", "Inactive").should("have.class", "red"); - - cy.get(".page-actions").findByRole("button", { name: "Start" }).should("exist"); - cy.get(".page-actions").findByRole("button", { name: "Clear" }).should("exist"); - - cy.get(".msg-box").should("contain", "Recorder is Inactive"); - cy.get(".msg-box").findByRole("button", { name: "Start Recording" }).should("exist"); - }); - - it("Recorder Start", () => { - cy.get(".page-actions").findByRole("button", { name: "Start" }).click(); - cy.get(".indicator-pill").should("contain", "Active").should("have.class", "green"); - - cy.get(".msg-box").should("contain", "No Requests found"); - - cy.visit("/app/List/DocType/List"); - cy.intercept("POST", "/api/method/frappe.desk.reportview.get").as("list_refresh"); - cy.wait("@list_refresh"); - - cy.get(".page-head").findByTitle("DocType").should("exist"); - cy.get(".list-count").should("contain", "20 of "); - - cy.visit("/app/recorder"); - cy.get(".page-head").findByTitle("Recorder").should("exist"); - cy.get(".frappe-list .result-list").should( - "contain", - "/api/method/frappe.desk.reportview.get" - ); - }); - - it("Recorder View Request", () => { - cy.get(".page-actions").findByRole("button", { name: "Start" }).click(); - - cy.visit("/app/List/DocType/List"); - cy.intercept("POST", "/api/method/frappe.desk.reportview.get").as("list_refresh"); - cy.wait("@list_refresh"); - - cy.get(".page-head").findByTitle("DocType").should("exist"); - cy.get(".list-count").should("contain", "20 of "); - - cy.visit("/app/recorder"); - - cy.get(".frappe-list .list-row-container span") - .contains("/api/method/frappe") - .should("be.visible") - .click({ force: true }); - - cy.url().should("include", "/recorder/request"); - cy.get("form").should("contain", "/api/method/frappe"); - }); -}); diff --git a/frappe/app.py b/frappe/app.py index 605b7e9fc1..5b6eead1c2 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -36,7 +36,12 @@ _sites_path = os.environ.get("SITES_PATH", ".") # If gc.freeze is done then importing modules before forking allows us to share the memory if frappe._tune_gc: + import gettext + + import babel + import babel.messages import bleach + import num2words import pydantic import frappe.boot diff --git a/frappe/auth.py b/frappe/auth.py index 9420e5e38c..ee4a5df975 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -125,6 +125,8 @@ class LoginManager: self.set_user_info() def login(self): + self.run_trigger("before_login") + if frappe.get_system_settings("disable_user_pass_login"): frappe.throw(_("Login with username and password is not allowed."), frappe.AuthenticationError) diff --git a/frappe/boot.py b/frappe/boot.py index 9c8bd43f60..6fd7c56014 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -4,6 +4,8 @@ bootstrap client session """ +import os + import frappe import frappe.defaults import frappe.desk.desk_page @@ -113,6 +115,9 @@ def get_bootinfo(): "Address Autocomplete Settings", "enabled" ) + if sentry_dsn := get_sentry_dsn(): + bootinfo.sentry_dsn = sentry_dsn + return bootinfo @@ -473,3 +478,10 @@ def add_subscription_conf(): return frappe.conf.subscription except Exception: return "" + + +def get_sentry_dsn(): + if not frappe.get_system_settings("enable_telemetry"): + return + + return os.getenv("FRAPPE_SENTRY_DSN") diff --git a/frappe/commands/site.py b/frappe/commands/site.py index abd5d9464e..641f70555e 100644 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -587,103 +587,6 @@ def add_db_index(context, doctype, column): raise SiteNotSpecifiedError -@click.command("describe-database-table") -@click.option("--doctype", help="DocType to describe") -@click.option( - "--column", - multiple=True, - help="Explicitly fetch accurate cardinality from table data. This can be quite slow on large tables.", -) -@pass_context -def describe_database_table(context, doctype, column): - """Describes various statistics about the table. - - This is useful to build integration like - This includes: - 1. Schema - 2. Indexes - 3. stats - total count of records - 4. if column is specified then extra stats are generated for column: - Distinct values count in column - """ - import json - - for site in context.sites: - frappe.init(site=site) - frappe.connect() - try: - data = _extract_table_stats(doctype, column) - # NOTE: Do not print anything else in this to avoid clobbering the output. - print(json.dumps(data, indent=2)) - finally: - frappe.destroy() - - if not context.sites: - raise SiteNotSpecifiedError - - -def _extract_table_stats(doctype: str, columns: list[str]) -> dict: - from frappe.utils import cint, cstr, get_table_name - - def sql_bool(val): - return cstr(val).lower() in ("yes", "1", "true") - - table = get_table_name(doctype, wrap_in_backticks=True) - - schema = [] - for field in frappe.db.sql(f"describe {table}", as_dict=True): - schema.append( - { - "column": field["Field"], - "type": field["Type"], - "is_nullable": sql_bool(field["Null"]), - "default": field["Default"], - } - ) - - def update_cardinality(column, value): - for col in schema: - if col["column"] == column: - col["cardinality"] = value - break - - indexes = [] - for idx in frappe.db.sql(f"show index from {table}", as_dict=True): - indexes.append( - { - "unique": not sql_bool(idx["Non_unique"]), - "cardinality": idx["Cardinality"], - "name": idx["Key_name"], - "sequence": idx["Seq_in_index"], - "nullable": sql_bool(idx["Null"]), - "column": idx["Column_name"], - "type": idx["Index_type"], - } - ) - if idx["Seq_in_index"] == 1: - update_cardinality(idx["Column_name"], idx["Cardinality"]) - - total_rows = cint( - frappe.db.sql( - f"""select table_rows - from information_schema.tables - where table_name = 'tab{doctype}'""" - )[0][0] - ) - - # fetch accurate cardinality for columns by query. WARN: This can take a lot of time. - for column in columns: - cardinality = frappe.db.sql(f"select count(distinct {column}) from {table}")[0][0] - update_cardinality(column, cardinality) - - return { - "table_name": table.strip("`"), - "total_rows": total_rows, - "schema": schema, - "indexes": indexes, - } - - @click.command("add-system-manager") @click.argument("email") @click.option("--first-name") @@ -1602,7 +1505,6 @@ commands = [ add_system_manager, add_user_for_sites, add_db_index, - describe_database_table, backup, drop_site, install_app, diff --git a/frappe/core/doctype/data_import/data_import.json b/frappe/core/doctype/data_import/data_import.json index 6325e139fd..2c33bb7b98 100644 --- a/frappe/core/doctype/data_import/data_import.json +++ b/frappe/core/doctype/data_import/data_import.json @@ -16,6 +16,8 @@ "google_sheets_url", "refresh_google_sheet", "column_break_5", + "custom_delimiters", + "delimiter_options", "status", "submit_after_import", "mute_emails", @@ -167,11 +169,25 @@ "hidden": 1, "label": "Payload Count", "read_only": 1 + }, + { + "default": ",;\\t|", + "depends_on": "custom_delimiters", + "description": "If your CSV uses a different delimiter, add that character here, ensuring no spaces or additional characters are included.", + "fieldname": "delimiter_options", + "fieldtype": "Data", + "label": "Delimiter Options" + }, + { + "default": "0", + "fieldname": "custom_delimiters", + "fieldtype": "Check", + "label": "Custom Delimiters" } ], "hide_toolbar": 1, "links": [], - "modified": "2024-03-23 16:02:16.953820", + "modified": "2024-04-27 20:42:35.843158", "modified_by": "Administrator", "module": "Core", "name": "Data Import", @@ -195,4 +211,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/frappe/core/doctype/data_import/data_import.py b/frappe/core/doctype/data_import/data_import.py index 7a0397bba6..cfecb3fbda 100644 --- a/frappe/core/doctype/data_import/data_import.py +++ b/frappe/core/doctype/data_import/data_import.py @@ -27,6 +27,8 @@ class DataImport(Document): if TYPE_CHECKING: from frappe.types import DF + custom_delimiters: DF.Check + delimiter_options: DF.Data | None google_sheets_url: DF.Data | None import_file: DF.Attach | None import_type: DF.Literal["", "Insert New Records", "Update Existing Records"] @@ -50,11 +52,16 @@ class DataImport(Document): self.template_options = "" self.template_warnings = "" + self.set_delimiters_flag() self.validate_doctype() self.validate_import_file() self.validate_google_sheets_url() self.set_payload_count() + def set_delimiters_flag(self): + if self.import_file: + frappe.flags.delimiter_options = self.delimiter_options or "," + def validate_doctype(self): if self.reference_doctype in BLOCKED_DOCTYPES: frappe.throw(_("Importing {0} is not allowed.").format(self.reference_doctype)) @@ -79,6 +86,7 @@ class DataImport(Document): def get_preview_from_template(self, import_file=None, google_sheets_url=None): if import_file: self.import_file = import_file + self.set_delimiters_flag() if google_sheets_url: self.google_sheets_url = google_sheets_url diff --git a/frappe/core/doctype/data_import/fixtures/sample_import_file_semicolon.csv b/frappe/core/doctype/data_import/fixtures/sample_import_file_semicolon.csv new file mode 100644 index 0000000000..0696815278 --- /dev/null +++ b/frappe/core/doctype/data_import/fixtures/sample_import_file_semicolon.csv @@ -0,0 +1,5 @@ +Title ;Description ;Number ;another_number ;ID (Table Field 1) ;Child Title (Table Field 1) ;Child Description (Table Field 1) ;Child 2 Title (Table Field 2) ;Child 2 Date (Table Field 2) ;Child 2 Number (Table Field 2) ;Child Title (Table Field 1 Again) ;Child Date (Table Field 1 Again) ;Child Number (Table Field 1 Again) ;table_field_1_again.child_another_number +Test 5 ;test description ;1 ;2 ;"" ; ;"child description with ,comma and" ;child title ;14-08-2019 ;4 ;child title again ;22-09-2020 ;5 ; 7 + ; ; ; ; ;child title 2 ;child description 2 ;title child ;30-10-2019 ;5 ; ;22-09-2021 ; ; + ;test description 2 ;1 ;2 ; ;child mandatory title ; ;title child man ; ; ;child mandatory again ; ; ; +Test 4 ;test description 3 ;4 ;5 ;"" ;child title asdf ;child description asdf ;child title asdf adsf ;15-08-2019 ;6 ;child title again asdf ;22-09-2022 ;9 ; 71 diff --git a/frappe/core/doctype/data_import/importer.py b/frappe/core/doctype/data_import/importer.py index a177d85c66..0db3d77e7d 100644 --- a/frappe/core/doctype/data_import/importer.py +++ b/frappe/core/doctype/data_import/importer.py @@ -1012,7 +1012,13 @@ class Column: ) elif self.df.fieldtype in ("Date", "Time", "Datetime"): # guess date/time format + # TODO: add possibility for user, to define the date format explicitly in the Data Import UI + # for example, if date column in file is in %d-%m-%y format -> 23-04-24. + # The date guesser might fail, as, this can be also parsed as %y-%m-%d, as both 23 and 24 are valid for year & for day + # This is an issue that cannot be handled automatically, no matter how we try, as it completely depends on the user's input. + # Defining an explicit value which surely recognizes self.date_format = self.guess_date_format_for_column() + if not self.date_format: if self.df.fieldtype == "Time": self.date_format = "%H:%M:%S" diff --git a/frappe/core/doctype/data_import/test_importer.py b/frappe/core/doctype/data_import/test_importer.py index 965e34c3e6..8e7ae548ab 100644 --- a/frappe/core/doctype/data_import/test_importer.py +++ b/frappe/core/doctype/data_import/test_importer.py @@ -50,6 +50,25 @@ class TestImporter(FrappeTestCase): self.assertEqual(doc3.another_number, 5) self.assertEqual(format_duration(doc3.duration), "5d 5h 45m") + def test_data_validation_semicolon_success(self): + import_file = get_import_file("sample_import_file_semicolon") + data_import = self.get_importer(doctype_name, import_file, update=True) + + doc = data_import.get_preview_from_template().get("data", [{}]) + + self.assertEqual(doc[0][7], "child description with ,comma and") + # Column count should be 14 (+1 ID) + self.assertEqual(len(doc[0]), 15) + + def test_data_validation_semicolon_failure(self): + import_file = get_import_file("sample_import_file_semicolon") + + data_import = self.get_importer_semicolon(doctype_name, import_file) + doc = data_import.get_preview_from_template().get("data", [{}]) + # if semicolon delimiter detection fails, and falls back to comma, + # column number will be less than 15 -> 2 (+1 id) + self.assertLessEqual(len(doc[0]), 15) + def test_data_import_preview(self): import_file = get_import_file("sample_import_file") data_import = self.get_importer(doctype_name, import_file) @@ -138,6 +157,18 @@ class TestImporter(FrappeTestCase): return data_import + def get_importer_semicolon(self, doctype, import_file, update=False): + data_import = frappe.new_doc("Data Import") + data_import.import_type = "Insert New Records" if not update else "Update Existing Records" + data_import.reference_doctype = doctype + data_import.import_file = import_file.file_url + # deliberately overwrite default delimiter options here, causing to fail when parsing `;` + data_import.delimiter_options = "," + data_import.insert() + frappe.db.commit() # nosemgrep + + return data_import + def create_doctype_if_not_exists(doctype_name, force=False): if force: diff --git a/frappe/core/doctype/data_import_log/data_import_log.json b/frappe/core/doctype/data_import_log/data_import_log.json index c184df193d..59f1bb1983 100644 --- a/frappe/core/doctype/data_import_log/data_import_log.json +++ b/frappe/core/doctype/data_import_log/data_import_log.json @@ -82,4 +82,4 @@ "sort_field": "creation", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/frappe/core/doctype/file/file.json b/frappe/core/doctype/file/file.json index 5c762c0120..166c3fa4be 100644 --- a/frappe/core/doctype/file/file.json +++ b/frappe/core/doctype/file/file.json @@ -107,6 +107,7 @@ "fieldtype": "Link", "hidden": 1, "label": "Folder", + "length": 255, "options": "File", "read_only": 1 }, @@ -189,7 +190,7 @@ "icon": "fa fa-file", "idx": 1, "links": [], - "modified": "2024-03-23 16:03:25.814224", + "modified": "2024-05-09 11:46:42.917146", "modified_by": "Administrator", "module": "Core", "name": "File", diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index 6e03909c1c..449fc572f4 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -31,6 +31,7 @@ from .utils import * exclude_from_linked_with = True ImageFile.LOAD_TRUNCATED_IMAGES = True URL_PREFIXES = ("http://", "https://") +FILE_ENCODING_OPTIONS = ("utf-8-sig", "utf-8", "windows-1250", "windows-1252") class File(Document): @@ -515,10 +516,11 @@ class File(Document): def exists_on_disk(self): return os.path.exists(self.get_full_path()) - def get_content(self) -> bytes: + def get_content(self, encodings=None) -> bytes | str: if self.is_folder: frappe.throw(_("Cannot get file contents of a Folder")) + # if doc was just created, content field is already populated, return it as-is if self.get("content"): self._content = self.content if self.decode: @@ -531,15 +533,20 @@ class File(Document): self.validate_file_url() file_path = self.get_full_path() - # read the file + if encodings is None: + encodings = FILE_ENCODING_OPTIONS with open(file_path, mode="rb") as f: self._content = f.read() - try: - # for plain text files - self._content = self._content.decode() - except UnicodeDecodeError: - # for .png, .jpg, etc - pass + # looping will not result in slowdown, as the content is usually utf-8 or utf-8-sig + # encoded so the first iteration will be enough most of the time + for encoding in encodings: + try: + # read file with proper encoding + self._content = self._content.decode(encoding) + break + except UnicodeDecodeError: + # for .png, .jpg, etc + continue return self._content diff --git a/frappe/core/doctype/file/test_file.py b/frappe/core/doctype/file/test_file.py index ecd431436f..6c9b9f5872 100644 --- a/frappe/core/doctype/file/test_file.py +++ b/frappe/core/doctype/file/test_file.py @@ -1,7 +1,6 @@ # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE import base64 -import json import os import shutil import tempfile @@ -111,7 +110,7 @@ class TestBase64File(FrappeTestCase): def setUp(self): self.attached_to_doctype, self.attached_to_docname = make_test_doc() self.test_content = base64.b64encode(test_content1.encode("utf-8")) - _file: "File" = frappe.get_doc( + _file: frappe.Document = frappe.get_doc( { "doctype": "File", "file_name": "test_base64.txt", @@ -125,7 +124,7 @@ class TestBase64File(FrappeTestCase): self.saved_file_url = _file.file_url def test_saved_content(self): - _file = frappe.get_doc("File", {"file_url": self.saved_file_url}) + _file: frappe.Document = frappe.get_doc("File", {"file_url": self.saved_file_url}) content = _file.get_content() self.assertEqual(content, test_content1) @@ -255,6 +254,25 @@ class TestSameContent(FrappeTestCase): limit_property.delete() frappe.clear_cache(doctype="ToDo") + def test_utf8_bom_content_decoding(self): + utf8_bom_content = test_content1.encode("utf-8-sig") + _file: frappe.Document = frappe.get_doc( + { + "doctype": "File", + "file_name": "utf8bom.txt", + "attached_to_doctype": self.attached_to_doctype1, + "attached_to_name": self.attached_to_docname1, + "content": utf8_bom_content, + "decode": False, + } + ) + _file.save() + saved_file = frappe.get_doc("File", _file.name) + file_content_decoded = saved_file.get_content(encodings=["utf-8"]) + self.assertEqual(file_content_decoded[0], "\ufeff") + file_content_properly_decoded = saved_file.get_content(encodings=["utf-8-sig", "utf-8"]) + self.assertEqual(file_content_properly_decoded, test_content1) + class TestFile(FrappeTestCase): def setUp(self): diff --git a/frappe/core/doctype/recorder/db_optimizer.py b/frappe/core/doctype/recorder/db_optimizer.py new file mode 100644 index 0000000000..28b6beca5d --- /dev/null +++ b/frappe/core/doctype/recorder/db_optimizer.py @@ -0,0 +1,283 @@ +"""Basic DB optimizer for Frappe Framework based app. + +This is largely based on heuristics and known good practices for indexing. +""" + +from collections import defaultdict +from dataclasses import dataclass +from typing import TypeVar + +from sql_metadata import Parser + +import frappe +from frappe.utils import flt + +# Any index that reads more than 30% table on average is not "useful" +INDEX_SCORE_THRESHOLD = 0.3 +# Anything reading less than this percent of table is considered optimal +OPTIMIZATION_THRESHOLD = 0.1 + +T = TypeVar("T") + + +@dataclass +class DBColumn: + name: str + cardinality: int | None + is_nullable: bool + default: str + data_type: str + + @classmethod + def from_frappe_ouput(cls, data) -> "DBColumn": + "Parse DBColumn from output of describe-database-table command in Frappe" + return cls( + name=data["column"], + cardinality=data.get("cardinality"), + is_nullable=data["is_nullable"], + default=data["default"], + data_type=data["type"], + ) + + +@dataclass +class DBIndex: + name: str + column: str + table: str + unique: bool | None = None + cardinality: int | None = None + sequence: int = 1 + nullable: bool = True + _score: float = 0.0 + + def __eq__(self, other: "DBIndex") -> bool: + return self.column == other.column and self.sequence == other.sequence and self.table == other.table + + def __repr__(self): + return f"DBIndex(`{self.table}`.`{self.column}`)" + + @classmethod + def from_frappe_ouput(cls, data, table) -> "DBIndex": + "Parse DBIndex from output of describe-database-table command in Frappe" + return cls( + name=data["name"], + table=table, + unique=data["unique"], + cardinality=data["cardinality"], + sequence=data["sequence"], + nullable=data["nullable"], + column=data["column"], + ) + + +@dataclass +class ColumnStat: + column_name: str + avg_frequency: float + avg_length: float + nulls_ratio: float | None = None + histogram: list[float] = None + + def __post_init__(self): + if not self.histogram: + self.histogram = [] + + @classmethod + def from_frappe_ouput(cls, data) -> "ColumnStat": + return cls( + column_name=data["column_name"], + avg_frequency=data["avg_frequency"], + avg_length=data["avg_length"], + nulls_ratio=data["nulls_ratio"], + histogram=[flt(bin) for bin in data["histogram"].split(",")] if data["histogram"] else [], + ) + + +@dataclass +class DBTable: + name: str + total_rows: int + schema: list[DBColumn] | None = None + indexes: list[DBIndex] | None = None + + def __post_init__(self): + if not self.schema: + self.schema = [] + if not self.indexes: + self.indexes = [] + + def update_cardinality(self, column_stats: list[ColumnStat]) -> None: + """Estimate cardinality using mysql.column_stat""" + for column_stat in column_stats: + for col in self.schema: + if col.name == column_stat.column_name and not col.cardinality and column_stat.avg_frequency: + # "hack" or "math" - average frequency is on average how frequently a row value appears. + # Avg = total_rows / cardinality, so... + col.cardinality = self.total_rows / column_stat.avg_frequency + + @classmethod + def from_frappe_ouput(cls, data) -> "DBTable": + "Parse DBTable from output of describe-database-table command in Frappe" + table_name = data["table_name"] + return cls( + name=table_name, + total_rows=data["total_rows"], + schema=[DBColumn.from_frappe_ouput(c) for c in data["schema"]], + indexes=[DBIndex.from_frappe_ouput(i, table_name) for i in data["indexes"]], + ) + + def has_column(self, column: str) -> bool: + for col in self.schema: + if col.name == column: + return True + return False + + +@dataclass +class DBOptimizer: + query: str # raw query in string format + tables: dict[str, DBTable] = None + parsed_query: Parser = None + + def __post_init__(self): + if not self.tables: + self.tables = {} + self.parsed_query = Parser(self.query) + + def tables_examined(self) -> list[str]: + return self.parsed_query.tables + + def update_table_data(self, table: DBTable): + self.tables[table.name] = table + + def _convert_to_db_index(self, column: str) -> DBIndex: + column_name, table = None, None + + if "." in column: + table, column_name = column.split(".") + else: + column_name = column + for table_name, db_table in self.tables.items(): + if db_table.has_column(column): + table = table_name + break + return DBIndex(column=column_name, name=column_name, table=table) + + def _remove_existing_indexes(self, potential_indexes: list[DBIndex]) -> list[DBIndex]: + """Given list of potential index candidates remove the ones that already exist. + + This also removes multi-column indexes for parts that are applicable to query. + Example: If multi-col index A+B+C exists and query utilizes A+B then + A+B are removed from potential indexes. + """ + + def remove_maximum_indexes(idx: list[DBIndex]): + """Try to remove entire index from potential indexes, if not possible, reduce one part and try again until no parts are left.""" + if not idx: + return + matched_sub_index = [] + for idx_part in list(idx): + matching_part = [ + i for i in potential_indexes if i.column == idx_part.column and i.table == idx_part.table + ] + if not matching_part: + # pop and recurse + idx.pop() + return remove_maximum_indexes(idx) + else: + matched_sub_index.extend(matching_part) + + # Every part matched now, lets remove those parts + for i in matched_sub_index: + potential_indexes.remove(i) + + # Reconstruct multi-col index + for table in self.tables.values(): + merged_indexes = defaultdict(list) + for index in table.indexes: + merged_indexes[index.name].append(index) + + for idx in merged_indexes.values(): + idx.sort(key=lambda x: x.sequence) + + for idx in merged_indexes.values(): + remove_maximum_indexes(idx) + return potential_indexes + + def potential_indexes(self) -> list[DBIndex]: + """Get all columns that can potentially be indexed to speed up this query.""" + + possible_indexes = [] + + # Where claus columns using these operators benefit from index + # 1. = (equality) + # 2. >, <, >=, <= + # 3. LIKE 'xyz%' (Prefix search) + # 4. BETWEEN (for date[time] fields) + # 5. IN (similar to equality) + + if not self.parsed_query.columns_dict: + return [] + + if where_columns := self.parsed_query.columns_dict.get("where"): + # TODO: Apply some heuristics here, not all columns in where clause are actually useful + possible_indexes.extend(where_columns) + + # Join clauses - Both sides of join should ideally be indexed. One will *usually* be primary key. + if join_columns := self.parsed_query.columns_dict.get("join"): + possible_indexes.extend(join_columns) + + # Top N query variant - Order by column can possibly speed up the query + if order_by_columns := self.parsed_query.columns_dict.get("order_by"): + if self.parsed_query.limit_and_offset: + possible_indexes.extend(order_by_columns) + + possible_db_indexes = [self._convert_to_db_index(i) for i in possible_indexes] + possible_db_indexes = [i for i in possible_db_indexes if i.column not in ("*", "name")] + possible_db_indexes.sort(key=lambda i: (i.table, i.column)) + + return self._remove_existing_indexes(possible_db_indexes) + + def suggest_index(self) -> DBIndex | None: + """Suggest best possible column to index given query and table stats.""" + if missing_tables := (set(self.tables_examined()) - set(self.tables.keys())): + frappe.throw("DBTable infomation missing for: " + ", ".join(missing_tables)) + + potential_indexes = self.potential_indexes() + + for index in list(potential_indexes): + table = self.tables[index.table] + + # Data type is not easily indexable - skip + column = next(c for c in table.schema if c.name == index.column) + if "text" in column.data_type.lower() or "json" in column.data_type.lower(): + potential_indexes.remove(index) + # Update cardinality from column so scoring can be done + index.cardinality = column.cardinality + + for index in potential_indexes: + index._score = self.index_score(index) + + potential_indexes.sort(key=lambda i: i._score) + if ( + potential_indexes + and (best_index := potential_indexes[0]) + and best_index._score < INDEX_SCORE_THRESHOLD + ): + return best_index + + def index_score(self, index: DBIndex) -> float: + """Score an index from 0 to 1 based on usefulness. + + A score of 0.5 indicates on average this index will read 50% of the table. (e.g. checkboxes)""" + table = self.tables[index.table] + + cardinality = index.cardinality or 2 + total_rows = table.total_rows or cardinality or 1 + + # We assume most unique values are evenly distributed, this is + # definitely not the case IRL but it should be good enough assumptions + # Score is rouhgly what percentage of table we will end up reading on typical query + rows_fetched_on_average = (table.total_rows or cardinality) / cardinality + return rows_fetched_on_average / total_rows diff --git a/frappe/core/doctype/recorder/recorder.js b/frappe/core/doctype/recorder/recorder.js index 37d387b711..735511f5b1 100644 --- a/frappe/core/doctype/recorder/recorder.js +++ b/frappe/core/doctype/recorder/recorder.js @@ -9,6 +9,39 @@ frappe.ui.form.on("Recorder", { frm.disable_save(); frm._sort_order = {}; frm.trigger("setup_sort"); + frm.fields_dict.sql_queries.grid.grid_pagination.page_length = 500; + refresh_field("sql_queries"); + frm.trigger("format_grid"); + frm.add_custom_button(__("Suggest Optimizations"), () => { + frappe.xcall("frappe.core.doctype.recorder.recorder.optimize", { + recorder_id: frm.doc.name, + }); + }); + + frappe.realtime.on("recorder-analysis-complete", () => { + frm.reload_doc(); + setTimeout(() => frm.scroll_to_field("suggested_indexes"), 1500); + }); + + let index_grid = frm.fields_dict.suggested_indexes.grid; + index_grid.wrapper.find(".grid-footer").toggle(true); + index_grid.toggle_checkboxes(true); + index_grid.df.cannot_delete_rows = true; + index_grid.add_custom_button(__("Add Indexes"), function () { + let indexes_to_add = index_grid.get_selected_children().map((row) => { + return { + column: row.column, + table: row.table, + }; + }); + if (!indexes_to_add.length) { + frappe.toast(__("You need to select indexes you want to add first.")); + return; + } + frappe.xcall("frappe.core.doctype.recorder.recorder.add_indexes", { + indexes: indexes_to_add, + }); + }); }, setup_sort: function (frm) { @@ -22,9 +55,25 @@ frappe.ui.form.on("Recorder", { frm._sort_order[field] = -1 * sort_order; // reverse for next click grid.refresh(); frm.trigger("setup_sort"); // grid creates new elements again, resetup listeners. + frm.trigger("format_grid"); }); }); }, + + /// Format duration and copy cells + format_grid(frm) { + const max_duration = Math.max(20, ...frm.doc.sql_queries.map((d) => d.duration)); + + const heatmap = (table, field, max) => { + frm.fields_dict[table].grid.grid_rows.forEach((row) => { + const percent = Math.round((row.doc[field] / max) * 100); + $(row.columns[field]).css({ + "background-color": `color-mix(in srgb, var(--bg-red) ${percent}%, var(--bg-color))`, + }); + }); + }; + heatmap("sql_queries", "duration", max_duration); + }, }); frappe.ui.form.on("Recorder Query", "form_render", function (frm, cdt, cdn) { diff --git a/frappe/core/doctype/recorder/recorder.json b/frappe/core/doctype/recorder/recorder.json index 72291dbfe2..efb6c1d065 100644 --- a/frappe/core/doctype/recorder/recorder.json +++ b/frappe/core/doctype/recorder/recorder.json @@ -20,6 +20,7 @@ "section_break_sgro", "form_dict", "section_break_9jhm", + "suggested_indexes", "sql_queries", "section_break_optn", "profile" @@ -119,6 +120,13 @@ "fieldtype": "Code", "label": "cProfile Output", "read_only": 1 + }, + { + "description": "Disclaimer: These indexes are suggested based on data and queries performed during this recording. These suggestions may or may not help.", + "fieldname": "suggested_indexes", + "fieldtype": "Table", + "label": "Suggested Indexes", + "options": "Recorder Suggested Index" } ], "hide_toolbar": 1, @@ -126,7 +134,7 @@ "index_web_pages_for_search": 1, "is_virtual": 1, "links": [], - "modified": "2024-02-01 22:13:26.505174", + "modified": "2024-05-14 15:16:55.626656", "modified_by": "Administrator", "module": "Core", "name": "Recorder", diff --git a/frappe/core/doctype/recorder/recorder.py b/frappe/core/doctype/recorder/recorder.py index 317bd9c148..fa8b1d14a7 100644 --- a/frappe/core/doctype/recorder/recorder.py +++ b/frappe/core/doctype/recorder/recorder.py @@ -1,10 +1,18 @@ # Copyright (c) 2023, Frappe Technologies and contributors # For license information, please see license.txt +import json +from collections import Counter, defaultdict + import frappe +from frappe import _ +from frappe.core.doctype.recorder.db_optimizer import DBOptimizer, DBTable +from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.model.document import Document +from frappe.recorder import RECORDER_REQUEST_HASH from frappe.recorder import get as get_recorder_data -from frappe.utils import cint, evaluate_filters +from frappe.utils import cint, cstr, evaluate_filters, get_table_name +from frappe.utils.caching import redis_cache class Recorder(Document): @@ -15,6 +23,9 @@ class Recorder(Document): if TYPE_CHECKING: from frappe.core.doctype.recorder_query.recorder_query import RecorderQuery + from frappe.core.doctype.recorder_suggested_index.recorder_suggested_index import ( + RecorderSuggestedIndex, + ) from frappe.types import DF cmd: DF.Data | None @@ -27,6 +38,7 @@ class Recorder(Document): profile: DF.Code | None request_headers: DF.Code | None sql_queries: DF.Table[RecorderQuery] + suggested_indexes: DF.Table[RecorderSuggestedIndex] time: DF.Datetime | None time_in_queries: DF.Float # end: auto-generated types @@ -95,8 +107,192 @@ def serialize_request(request): request_headers=frappe.as_json(request.get("headers"), indent=4), form_dict=frappe.as_json(request.get("form_dict"), indent=4), sql_queries=request.get("calls"), + suggested_indexes=request.get("suggested_indexes"), modified=request.get("time"), creation=request.get("time"), ) return request + + +@frappe.whitelist() +def add_indexes(indexes): + frappe.only_for("Administrator") + indexes = json.loads(indexes) + + for index in indexes: + frappe.enqueue(_add_index, table=index["table"], column=index["column"]) + frappe.msgprint(_("Enqueued creation of indexes"), alert=True) + + +def _add_index(table, column): + doctype = get_doctype_name(table) + frappe.db.add_index(doctype, [column]) + make_property_setter( + doctype, + column, + property="search_index", + value="1", + property_type="Check", + for_doctype=False, # Applied on docfield + ) + frappe.msgprint( + _("Index created successfully on column {0} of doctype {1}").format(column, doctype), + alert=True, + realtime=True, + ) + + +@frappe.whitelist() +def optimize(recorder_id: str): + frappe.only_for("Administrator") + frappe.enqueue(_optimize, recorder_id=recorder_id, queue="long") + + +def _optimize(recorder_id): + record: Recorder = frappe.get_doc("Recorder", recorder_id) + total_duration = record.time_in_queries + + # Any index with query time less than 5% of total time is not suggested + PERCENT_DURATION_THRESHOLD_OVERALL = 0.05 + # Any query with duration less than 0.5% of total duration is not analyzed + PERCENT_DURATION_THRESHOLD_QUERY = 0.005 + + # Index suggestion -> Query duration + index_suggestions = Counter() + for idx, captured_query in enumerate(record.sql_queries, start=1): + query = cstr(captured_query.query) + frappe.publish_progress( + idx / len(record.sql_queries) * 100, + title="Analyzing Queries", + doctype=record.doctype, + docname=record.name, + description=f"Analyzing query: {query[:140]}", + ) + if captured_query.duration < total_duration * PERCENT_DURATION_THRESHOLD_QUERY: + continue + if not query.lower().strip().startswith(("select", "update", "delete")): + continue + if index := _optimize_query(query): + index_suggestions[(index.table, index.column)] += captured_query.duration + + suggested_indexes = index_suggestions.most_common(3) + suggested_indexes = [ + idx for idx in suggested_indexes if idx[1] > total_duration * PERCENT_DURATION_THRESHOLD_OVERALL + ] + + if not suggested_indexes: + frappe.msgprint( + _("No automatic optimization suggestions available."), + title=_("No Suggestions"), + realtime=True, + ) + return + + data = frappe.cache.hget(RECORDER_REQUEST_HASH, record.name) + data["suggested_indexes"] = [{"table": idx[0][0], "column": idx[0][1]} for idx in suggested_indexes] + frappe.cache.hset(RECORDER_REQUEST_HASH, record.name, data) + frappe.publish_realtime("recorder-analysis-complete", user=frappe.session.user) + frappe.msgprint(_("Query analysis complete. Check suggested indexes."), realtime=True, alert=True) + + +def _optimize_query(query): + optimizer = DBOptimizer(query=query) + tables = optimizer.tables_examined() + + # Note: Two passes are required here because we first need basic data to understand which + # columns need to be analyzed to get accurate cardinality. + for table in tables: + doctype = get_doctype_name(table) + stats = _fetch_table_stats(doctype, columns=[]) + if not stats: + return + db_table = DBTable.from_frappe_ouput(stats) + optimizer.update_table_data(db_table) + + potential_indexes = optimizer.potential_indexes() + tablewise_columns = defaultdict(list) + for idx in potential_indexes: + tablewise_columns[idx.table].append(idx.column) + + for table in tables: + doctype = get_doctype_name(table) + stats = _fetch_table_stats(doctype, columns=tablewise_columns[table]) + if not stats: + return + db_table = DBTable.from_frappe_ouput(stats) + optimizer.update_table_data(db_table) + + return optimizer.suggest_index() + + +def _fetch_table_stats(doctype: str, columns: list[str]) -> dict | None: + def sql_bool(val): + return cstr(val).lower() in ("yes", "1", "true") + + if not frappe.db.table_exists(doctype): + return + + table = get_table_name(doctype, wrap_in_backticks=True) + + schema = [] + for field in frappe.db.sql(f"describe {table}", as_dict=True): + schema.append( + { + "column": field["Field"], + "type": field["Type"], + "is_nullable": sql_bool(field["Null"]), + "default": field["Default"], + } + ) + + def update_cardinality(column, value): + for col in schema: + if col["column"] == column: + col["cardinality"] = value + break + + indexes = [] + for idx in frappe.db.sql(f"show index from {table}", as_dict=True): + indexes.append( + { + "unique": not sql_bool(idx["Non_unique"]), + "cardinality": idx["Cardinality"], + "name": idx["Key_name"], + "sequence": idx["Seq_in_index"], + "nullable": sql_bool(idx["Null"]), + "column": idx["Column_name"], + "type": idx["Index_type"], + } + ) + if idx["Seq_in_index"] == 1: + update_cardinality(idx["Column_name"], idx["Cardinality"]) + + total_rows = cint( + frappe.db.sql( + f"""select table_rows + from information_schema.tables + where table_name = 'tab{doctype}'""" + )[0][0] + ) + + # fetch accurate cardinality for columns by query. WARN: This can take A LOT of time. + for column in columns: + cardinality = _get_column_cardinality(table, column) + update_cardinality(column, cardinality) + + return { + "table_name": table.strip("`"), + "total_rows": total_rows, + "schema": schema, + "indexes": indexes, + } + + +@redis_cache +def _get_column_cardinality(table, column): + return frappe.db.sql(f"select count(distinct {column}) from {table}")[0][0] + + +def get_doctype_name(table_name: str) -> str: + return table_name.removeprefix("tab") diff --git a/frappe/core/doctype/recorder/test_recorder.py b/frappe/core/doctype/recorder/test_recorder.py index aad47cadf5..3a35925c75 100644 --- a/frappe/core/doctype/recorder/test_recorder.py +++ b/frappe/core/doctype/recorder/test_recorder.py @@ -5,8 +5,10 @@ import re import frappe import frappe.recorder -from frappe.core.doctype.recorder.recorder import serialize_request +from frappe.core.doctype.recorder.recorder import _optimize_query, serialize_request +from frappe.query_builder.utils import db_type_is from frappe.recorder import get as get_recorder_data +from frappe.tests.test_query_builder import run_only_if from frappe.tests.utils import FrappeTestCase from frappe.utils import set_request @@ -75,3 +77,20 @@ class TestRecorder(FrappeTestCase): requests = frappe.get_all("Recorder") request_doc = get_recorder_data(requests[0].name) self.assertIsInstance(serialize_request(request_doc), dict) + + +class TestQueryOptimization(FrappeTestCase): + @run_only_if(db_type_is.MARIADB) + def test_query_optimizer(self): + suggested_index = _optimize_query( + """select name from + `tabUser` u + join `tabHas Role` r + on r.parent = u.name + where email='xyz' + and creation > '2023' + and bio like '%xyz%' + """ + ) + self.assertEqual(suggested_index.table, "tabUser") + self.assertEqual(suggested_index.column, "email") diff --git a/frappe/core/doctype/recorder_query/recorder_query.json b/frappe/core/doctype/recorder_query/recorder_query.json index 75be1cfe09..b6bf44c891 100644 --- a/frappe/core/doctype/recorder_query/recorder_query.json +++ b/frappe/core/doctype/recorder_query/recorder_query.json @@ -33,20 +33,24 @@ "label": "Normalized Query" }, { + "columns": 1, "fieldname": "duration", "fieldtype": "Float", "in_list_view": 1, "label": "Duration" }, { + "columns": 1, "fieldname": "exact_copies", "fieldtype": "Int", "in_list_view": 1, "label": "Exact Copies" }, { + "columns": 1, "fieldname": "normalized_copies", "fieldtype": "Int", + "in_list_view": 1, "label": "Normalized Copies" }, { @@ -84,6 +88,7 @@ "label": "SQL Explain" }, { + "columns": 1, "fieldname": "index", "fieldtype": "Int", "in_list_view": 1, @@ -94,7 +99,7 @@ "is_virtual": 1, "istable": 1, "links": [], - "modified": "2024-03-23 16:03:36.052756", + "modified": "2024-05-13 17:13:20.785329", "modified_by": "Administrator", "module": "Core", "name": "Recorder Query", diff --git a/frappe/core/doctype/recorder_suggested_index/__init__.py b/frappe/core/doctype/recorder_suggested_index/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/core/doctype/recorder_suggested_index/recorder_suggested_index.json b/frappe/core/doctype/recorder_suggested_index/recorder_suggested_index.json new file mode 100644 index 0000000000..eb6f4b1e4d --- /dev/null +++ b/frappe/core/doctype/recorder_suggested_index/recorder_suggested_index.json @@ -0,0 +1,39 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-05-14 16:23:33.466465", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "table", + "column" + ], + "fields": [ + { + "fieldname": "table", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Table" + }, + { + "fieldname": "column", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Column" + } + ], + "index_web_pages_for_search": 1, + "is_virtual": 1, + "istable": 1, + "links": [], + "modified": "2024-05-14 17:43:57.231051", + "modified_by": "Administrator", + "module": "Core", + "name": "Recorder Suggested Index", + "owner": "Administrator", + "permissions": [], + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/frappe/core/doctype/recorder_suggested_index/recorder_suggested_index.py b/frappe/core/doctype/recorder_suggested_index/recorder_suggested_index.py new file mode 100644 index 0000000000..50e634174c --- /dev/null +++ b/frappe/core/doctype/recorder_suggested_index/recorder_suggested_index.py @@ -0,0 +1,46 @@ +# Copyright (c) 2024, Frappe Technologies and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class RecorderSuggestedIndex(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + column: DF.Data | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + table: DF.Data | None + # end: auto-generated types + + def db_insert(self, *args, **kwargs): + raise NotImplementedError + + def load_from_db(self): + raise NotImplementedError + + def db_update(self): + raise NotImplementedError + + def delete(self): + raise NotImplementedError + + @staticmethod + def get_list(filters=None, page_length=20, **kwargs): + pass + + @staticmethod + def get_count(filters=None, **kwargs): + pass + + @staticmethod + def get_stats(**kwargs): + pass diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index d3af5d1fc6..54b650871f 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -3,6 +3,7 @@ from functools import partial from types import FunctionType, MethodType, ModuleType +from typing import TYPE_CHECKING import frappe from frappe import _ @@ -16,6 +17,9 @@ from frappe.utils.safe_exec import ( safe_exec, ) +if TYPE_CHECKING: + from frappe.core.doctype.scheduled_job_type.scheduled_job_type import ScheduledJobType + class ServerScript(Document): # begin: auto-generated types @@ -77,12 +81,10 @@ class ServerScript(Document): def validate(self): frappe.only_for("Script Manager", True) - self.sync_scheduled_jobs() - self.clear_scheduled_events() self.check_if_compilable_in_restricted_context() def on_update(self): - self.sync_scheduler_events() + self.sync_scheduled_job_type() def clear_cache(self): frappe.cache.delete_value("server_script_map") @@ -92,7 +94,10 @@ class ServerScript(Document): frappe.cache.delete_value("server_script_map") if self.script_type == "Scheduler Event": for job in self.scheduled_jobs: - frappe.delete_doc("Scheduled Job Type", job.name) + scheduled_job_type: "ScheduledJobType" = frappe.get_doc("Scheduled Job Type", job.name) + scheduled_job_type.stopped = True + scheduled_job_type.server_script = None + scheduled_job_type.save() def get_code_fields(self): return {"script": "py"} @@ -105,33 +110,35 @@ class ServerScript(Document): fields=["name", "stopped"], ) - def sync_scheduled_jobs(self): - """Sync Scheduled Job Type statuses if Server Script's disabled status is changed""" - if self.script_type != "Scheduler Event" or not self.has_value_changed("disabled"): + def sync_scheduled_job_type(self): + """Create or update Scheduled Job Type documents for Scheduler Event Server Scripts""" + if self.script_type != "Scheduler Event" or ( + (previous_script_type := self.has_value_changed("script_type")) + # True will be sent if its a new record + and previous_script_type.value not in (True, "Scheduler Event") + ): return - for scheduled_job in self.scheduled_jobs: - if bool(scheduled_job.stopped) != bool(self.disabled): - job = frappe.get_doc("Scheduled Job Type", scheduled_job.name) - job.stopped = self.disabled - job.save() - - def sync_scheduler_events(self): - """Create or update Scheduled Job Type documents for Scheduler Event Server Scripts""" - if not self.disabled and self.event_frequency and self.script_type == "Scheduler Event": - cron_format = self.cron_format if self.event_frequency == "Cron" else None - setup_scheduler_events( - script_name=self.name, frequency=self.event_frequency, cron_format=cron_format + if scheduled_script := frappe.db.get_value("Scheduled Job Type", {"server_script": self.name}): + scheduled_job_type: "ScheduledJobType" = frappe.get_doc("Scheduled Job Type", scheduled_script) + else: + scheduled_job_type: "ScheduledJobType" = frappe.get_doc( + { + "doctype": "Scheduled Job Type", + "server_script": self.name, + } ) - def clear_scheduled_events(self): - """Deletes existing scheduled jobs by Server Script if self.event_frequency or self.cron_format has changed""" - if ( - self.script_type == "Scheduler Event" - and (self.has_value_changed("event_frequency") or self.has_value_changed("cron_format")) - ) or (self.has_value_changed("script_type") and self.script_type != "Scheduler Event"): - for scheduled_job in self.scheduled_jobs: - frappe.delete_doc("Scheduled Job Type", scheduled_job.name, delete_permanently=1) + scheduled_job_type.update( + { + "method": frappe.scrub(f"{self.name}-{self.event_frequency}"), + "frequency": self.event_frequency, + "cron_format": self.cron_format, + "stopped": self.disabled, + } + ).save() + + frappe.msgprint(_("Scheduled execution for script {0} has updated").format(self.name)) def check_if_compilable_in_restricted_context(self): """Check compilation errors and send them back as warnings.""" @@ -247,43 +254,7 @@ class ServerScript(Document): return items -def setup_scheduler_events(script_name: str, frequency: str, cron_format: str | None = None): - """Creates or Updates Scheduled Job Type documents based on the specified script name and frequency - - Args: - script_name (str): Name of the Server Script document - frequency (str): Event label compatible with the Frappe scheduler - """ - method = frappe.scrub(f"{script_name}-{frequency}") - scheduled_script = frappe.db.get_value("Scheduled Job Type", {"method": method}) - - if not scheduled_script: - frappe.get_doc( - { - "doctype": "Scheduled Job Type", - "method": method, - "frequency": frequency, - "server_script": script_name, - "cron_format": cron_format, - } - ).insert() - - frappe.msgprint(_("Enabled scheduled execution for script {0}").format(script_name)) - - else: - doc = frappe.get_doc("Scheduled Job Type", scheduled_script) - - if doc.frequency == frequency: - return - - doc.frequency = frequency - doc.cron_format = cron_format - doc.save() - - frappe.msgprint(_("Scheduled execution for script {0} has updated").format(script_name)) - - -def execute_api_server_script(script=None, *args, **kwargs): +def execute_api_server_script(script: ServerScript, *args, **kwargs): # These are only added for compatibility with rate limiter. del args del kwargs diff --git a/frappe/core/doctype/server_script/server_script_utils.py b/frappe/core/doctype/server_script/server_script_utils.py index 418f3aea83..9237b66de5 100644 --- a/frappe/core/doctype/server_script/server_script_utils.py +++ b/frappe/core/doctype/server_script/server_script_utils.py @@ -43,7 +43,7 @@ def run_server_script_for_doc_event(doc, event): if scripts: # run all scripts for this doctype + event for script_name in scripts: - frappe.get_doc("Server Script", script_name).execute_doc(doc) + frappe.get_cached_doc("Server Script", script_name).execute_doc(doc) def get_server_script_map(): diff --git a/frappe/database/database.py b/frappe/database/database.py index cb47d91d44..1edd61926d 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -1105,7 +1105,7 @@ class Database: """Return True if at least one row exists.""" return frappe.get_all(doctype, limit=1, order_by=None, as_list=True) - def exists(self, dt, dn=None, cache=False): + def exists(self, dt, dn=None, cache=False, *, debug=False): """Return the document name of a matching document, or None. Note: `cache` only works if `dt` and `dn` are of type `str`. @@ -1138,7 +1138,7 @@ class Database: dt = dt.copy() # don't modify the original dict dt, dn = dt.pop("doctype"), dt - return self.get_value(dt, dn, ignore=True, cache=cache, order_by=None) + return self.get_value(dt, dn, ignore=True, cache=cache, order_by=None, debug=debug) def count(self, dt, filters=None, debug=False, cache=False, distinct: bool = True): """Return `COUNT(*)` for given DocType and filters.""" @@ -1268,7 +1268,7 @@ class Database: def delete(self, doctype: str, filters: dict | list | None = None, debug=False, **kwargs): """Delete rows from a table in site which match the passed filters. This - does trigger DocType hooks. Simply runs a DELETE query in the database. + does not trigger DocType hooks. Simply runs a DELETE query in the database. Doctype name can be passed directly, it will be pre-pended with `tab`. """ diff --git a/frappe/desk/doctype/event/event.js b/frappe/desk/doctype/event/event.js index ef2b2eb7e1..85f1bb4156 100644 --- a/frappe/desk/doctype/event/event.js +++ b/frappe/desk/doctype/event/event.js @@ -71,7 +71,7 @@ frappe.ui.form.on("Event", { frappe.ui.form.on("Event Participants", { event_participants_remove: function (frm, cdt, cdn) { - if (cdt && !cdn.includes("New Event Participants")) { + if (cdt && !cdn.includes("new-event-participants")) { frappe.call({ type: "POST", method: "frappe.desk.doctype.event.event.delete_communication", diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py index 13a23f185f..2e380c9afd 100644 --- a/frappe/desk/doctype/todo/todo.py +++ b/frappe/desk/doctype/todo/todo.py @@ -106,7 +106,7 @@ class ToDo(Document): frappe.db.set_single_value( self.reference_type, "_assign", - json.dumps(assignments), + json.dumps(assignments) if assignments else "", update_modified=False, ) else: @@ -114,7 +114,7 @@ class ToDo(Document): self.reference_type, self.reference_name, "_assign", - json.dumps(assignments), + json.dumps(assignments) if assignments else "", update_modified=False, ) diff --git a/frappe/desk/form/document_follow.py b/frappe/desk/form/document_follow.py index ca301ffac9..b55762dc4e 100644 --- a/frappe/desk/form/document_follow.py +++ b/frappe/desk/form/document_follow.py @@ -10,7 +10,7 @@ from frappe.utils import get_url_to_form @frappe.whitelist() -def update_follow(doctype, doc_name, following): +def update_follow(doctype: str, doc_name: str, following: bool): if following: return follow_document(doctype, doc_name, frappe.session.user) else: diff --git a/frappe/desk/listview.py b/frappe/desk/listview.py index cc32e4ab06..843d8c695b 100644 --- a/frappe/desk/listview.py +++ b/frappe/desk/listview.py @@ -60,10 +60,12 @@ def get_group_by_count(doctype: str, current_filters: str, field: str) -> list[d .run(as_dict=True) ) - if not frappe.get_meta(doctype).has_field(field) and not is_default_field(field): + meta = frappe.get_meta(doctype) + + if not meta.has_field(field) and not is_default_field(field): raise ValueError("Field does not belong to doctype") - return frappe.get_list( + data = frappe.get_list( doctype, filters=current_filters, group_by=f"`tab{doctype}`.{field}", @@ -71,3 +73,13 @@ def get_group_by_count(doctype: str, current_filters: str, field: str) -> list[d order_by="count desc", limit=50, ) + + # Add in title if it's a link field and `show_title_field_in_link` is set + if (field_meta := meta.get_field(field)) and field_meta.fieldtype == "Link": + link_meta = frappe.get_meta(field_meta.options) + if link_meta.show_title_field_in_link: + title_field = link_meta.get_title_field() + for item in data: + item.title = frappe.get_value(field_meta.options, item.name, title_field) + + return data diff --git a/frappe/desk/treeview.py b/frappe/desk/treeview.py index 6967143602..2f315924b5 100644 --- a/frappe/desk/treeview.py +++ b/frappe/desk/treeview.py @@ -82,6 +82,8 @@ def make_tree_args(**kwarg): if kwarg["is_root"] == "true": kwarg["is_root"] = True - kwarg.update({parent_field: kwarg.get("parent") or kwarg.get(parent_field)}) + parent = kwarg.get("parent") or kwarg.get(parent_field) + if doctype != parent: + kwarg.update({parent_field: parent}) return frappe._dict(kwarg) diff --git a/frappe/gettext/translate.py b/frappe/gettext/translate.py index a7a3ddf607..bf5b60f0b8 100644 --- a/frappe/gettext/translate.py +++ b/frappe/gettext/translate.py @@ -299,9 +299,6 @@ def get_translations_from_mo(lang, app): if m.context: context = m.context.decode() # context is encoded as bytes translations[f"{key}:{context}"] = m.string - if m.id not in translations: - # better a translation with context than no translation - translations[m.id] = m.string else: translations[m.id] = m.string return translations diff --git a/frappe/hooks.py b/frappe/hooks.py index c4d07b4632..fdc0afa1ac 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -442,7 +442,6 @@ after_job = [ extend_bootinfo = [ "frappe.utils.telemetry.add_bootinfo", "frappe.core.doctype.user_permission.user_permission.send_user_permissions", - "frappe.utils.sentry.add_bootinfo", ] get_changelog_feed = "frappe.desk.doctype.changelog_feed.changelog_feed.get_feed" @@ -549,7 +548,7 @@ default_log_clearing_doctypes = { # These keys will not be erased when doing frappe.clear_cache() persistent_cache_keys = [ - "update-user-set", - "update-info", + "changelog-*", # version update notifications "insert_queue_for_*", # Deferred Insert + "recorder-*", # Recorder ] diff --git a/frappe/locale/main.pot b/frappe/locale/main.pot index 554a705bd8..71bd9c1257 100644 --- a/frappe/locale/main.pot +++ b/frappe/locale/main.pot @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Frappe Framework VERSION\n" "Report-Msgid-Bugs-To: developers@frappe.io\n" -"POT-Creation-Date: 2024-04-14 10:31+0000\n" -"PO-Revision-Date: 2024-04-14 10:31+0000\n" +"POT-Creation-Date: 2024-05-05 09:33+0000\n" +"PO-Revision-Date: 2024-05-05 09:33+0000\n" "Last-Translator: developers@frappe.io\n" "Language-Team: developers@frappe.io\n" "MIME-Version: 1.0\n" @@ -45,7 +45,7 @@ msgctxt "About Us Settings" msgid "\"Team Members\" or \"Management\"" msgstr "" -#: public/js/frappe/form/form.js:1027 +#: public/js/frappe/form/form.js:1084 msgid "\"amended_from\" field must be present to do an amendment." msgstr "" @@ -58,6 +58,10 @@ msgstr "" msgid "#{0}" msgstr "" +#: core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js:36 +msgid "${values.doctype_name} has been added to queue for optimization" +msgstr "" + #: public/js/frappe/ui/toolbar/about.js:8 msgid "© Frappe Technologies Pvt. Ltd. and contributors" msgstr "" @@ -100,7 +104,7 @@ msgstr "" msgid "(Mandatory)" msgstr "" -#: model/rename_doc.py:681 +#: model/rename_doc.py:686 msgid "** Failed: {0} to {1}: {2}" msgstr "" @@ -122,7 +126,7 @@ msgctxt "Web Page" msgid "0 is highest" msgstr "" -#: public/js/frappe/form/grid_row.js:807 +#: public/js/frappe/form/grid_row.js:808 msgid "1 = True & 0 = False" msgstr "" @@ -142,7 +146,7 @@ msgstr "" msgid "1 Google Calendar Event synced." msgstr "" -#: public/js/frappe/views/reports/query_report.js:882 +#: public/js/frappe/views/reports/query_report.js:883 msgid "1 Report" msgstr "" @@ -150,7 +154,7 @@ msgstr "" msgid "1 comment" msgstr "" -#: tests/test_utils.py:677 +#: tests/test_utils.py:676 msgid "1 day ago" msgstr "" @@ -158,15 +162,15 @@ msgstr "" msgid "1 hour" msgstr "" -#: public/js/frappe/utils/pretty_date.js:52 tests/test_utils.py:675 +#: public/js/frappe/utils/pretty_date.js:52 tests/test_utils.py:674 msgid "1 hour ago" msgstr "" -#: public/js/frappe/utils/pretty_date.js:48 tests/test_utils.py:673 +#: public/js/frappe/utils/pretty_date.js:48 tests/test_utils.py:672 msgid "1 minute ago" msgstr "" -#: public/js/frappe/utils/pretty_date.js:66 tests/test_utils.py:681 +#: public/js/frappe/utils/pretty_date.js:66 tests/test_utils.py:680 msgid "1 month ago" msgstr "" @@ -174,35 +178,35 @@ msgstr "" msgid "1 record will be exported" msgstr "" -#: tests/test_utils.py:672 +#: tests/test_utils.py:671 msgid "1 second ago" msgstr "" -#: public/js/frappe/utils/pretty_date.js:62 tests/test_utils.py:679 +#: public/js/frappe/utils/pretty_date.js:62 tests/test_utils.py:678 msgid "1 week ago" msgstr "" -#: public/js/frappe/utils/pretty_date.js:70 tests/test_utils.py:683 +#: public/js/frappe/utils/pretty_date.js:70 tests/test_utils.py:682 msgid "1 year ago" msgstr "" -#: tests/test_utils.py:676 +#: tests/test_utils.py:675 msgid "2 hours ago" msgstr "" -#: tests/test_utils.py:682 +#: tests/test_utils.py:681 msgid "2 months ago" msgstr "" -#: tests/test_utils.py:680 +#: tests/test_utils.py:679 msgid "2 weeks ago" msgstr "" -#: tests/test_utils.py:684 +#: tests/test_utils.py:683 msgid "2 years ago" msgstr "" -#: tests/test_utils.py:674 +#: tests/test_utils.py:673 msgid "3 minutes ago" msgstr "" @@ -218,7 +222,7 @@ msgstr "" msgid "5 Records" msgstr "" -#: tests/test_utils.py:678 +#: tests/test_utils.py:677 msgid "5 days ago" msgstr "" @@ -979,7 +983,7 @@ msgstr "" msgid "Action Complete" msgstr "" -#: model/document.py:1687 +#: model/document.py:1707 msgid "Action Failed" msgstr "" @@ -1017,6 +1021,7 @@ msgstr "" #: core/doctype/communication/communication.js:108 #: core/doctype/communication/communication.js:131 #: core/doctype/rq_job/rq_job_list.js:14 core/doctype/rq_job/rq_job_list.js:39 +#: core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js:48 #: custom/doctype/customize_form/customize_form.js:108 #: custom/doctype/customize_form/customize_form.js:116 #: custom/doctype/customize_form/customize_form.js:124 @@ -1025,10 +1030,10 @@ msgstr "" #: custom/doctype/customize_form/customize_form.js:148 #: custom/doctype/customize_form/customize_form.js:283 #: public/js/frappe/ui/page.html:56 -#: public/js/frappe/views/reports/query_report.js:190 -#: public/js/frappe/views/reports/query_report.js:203 -#: public/js/frappe/views/reports/query_report.js:213 -#: public/js/frappe/views/reports/query_report.js:776 +#: public/js/frappe/views/reports/query_report.js:191 +#: public/js/frappe/views/reports/query_report.js:204 +#: public/js/frappe/views/reports/query_report.js:214 +#: public/js/frappe/views/reports/query_report.js:777 msgid "Actions" msgstr "" @@ -1091,6 +1096,12 @@ msgstr "" msgid "Active Sessions" msgstr "" +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Active Sessions" +msgstr "" + #: public/js/frappe/form/dashboard.js:22 #: public/js/frappe/form/footer/form_timeline.js:58 msgid "Activity" @@ -1122,13 +1133,13 @@ msgstr "" #: core/page/permission_manager/permission_manager.js:476 #: email/doctype/email_group/email_group.js:60 -#: public/js/frappe/form/grid_row.js:470 +#: public/js/frappe/form/grid_row.js:471 #: public/js/frappe/form/sidebar/assign_to.js:100 #: public/js/frappe/form/templates/set_sharing.html:68 #: public/js/frappe/list/bulk_operations.js:407 #: public/js/frappe/views/dashboard/dashboard_view.js:440 -#: public/js/frappe/views/reports/query_report.js:265 -#: public/js/frappe/views/reports/query_report.js:293 +#: public/js/frappe/views/reports/query_report.js:266 +#: public/js/frappe/views/reports/query_report.js:294 #: public/js/frappe/widgets/widget_dialog.js:30 msgid "Add" msgstr "" @@ -1138,7 +1149,7 @@ msgctxt "Primary action in list view" msgid "Add" msgstr "" -#: public/js/frappe/form/grid_row.js:430 +#: public/js/frappe/form/grid_row.js:431 msgid "Add / Remove Columns" msgstr "" @@ -1178,7 +1189,7 @@ msgctxt "Web Page Block" msgid "Add Border at Top" msgstr "" -#: public/js/frappe/views/reports/query_report.js:209 +#: public/js/frappe/views/reports/query_report.js:210 msgid "Add Chart to Dashboard" msgstr "" @@ -1226,7 +1237,7 @@ msgid "Add Gray Background" msgstr "" #: public/js/frappe/ui/group_by/group_by.js:230 -#: public/js/frappe/ui/group_by/group_by.js:415 +#: public/js/frappe/ui/group_by/group_by.js:418 msgid "Add Group" msgstr "" @@ -1252,7 +1263,7 @@ msgstr "" msgid "Add Review" msgstr "" -#: core/doctype/user/user.py:810 +#: core/doctype/user/user.py:811 msgid "Add Roles" msgstr "" @@ -1291,7 +1302,7 @@ msgstr "" msgid "Add Tags" msgstr "" -#: public/js/frappe/list/list_view.js:1899 +#: public/js/frappe/list/list_view.js:1903 msgctxt "Button in list view actions menu" msgid "Add Tags" msgstr "" @@ -1368,7 +1379,7 @@ msgid "Add script for Child Table" msgstr "" #: public/js/frappe/utils/dashboard_utils.js:263 -#: public/js/frappe/views/reports/query_report.js:251 +#: public/js/frappe/views/reports/query_report.js:252 msgid "Add to Dashboard" msgstr "" @@ -1408,7 +1419,7 @@ msgstr "" msgid "Added {0} ({1})" msgstr "" -#: core/doctype/user/user.py:307 +#: core/doctype/user/user.py:308 msgid "Adding System Manager to this User as there must be atleast one System Manager" msgstr "" @@ -1545,11 +1556,11 @@ msgstr "" msgid "Administrator" msgstr "" -#: core/doctype/user/user.py:1214 +#: core/doctype/user/user.py:1215 msgid "Administrator Logged In" msgstr "" -#: core/doctype/user/user.py:1208 +#: core/doctype/user/user.py:1209 msgid "Administrator accessed {0} on {1} via IP Address {2}." msgstr "" @@ -1571,8 +1582,8 @@ msgctxt "User Permission" msgid "Advanced Control" msgstr "" -#: public/js/frappe/form/controls/link.js:316 -#: public/js/frappe/form/controls/link.js:318 +#: public/js/frappe/form/controls/link.js:319 +#: public/js/frappe/form/controls/link.js:321 msgid "Advanced Search" msgstr "" @@ -1594,6 +1605,12 @@ msgctxt "Server Script" msgid "After Delete" msgstr "" +#. Option for the 'DocType Event' (Select) field in DocType 'Server Script' +#: core/doctype/server_script/server_script.json +msgctxt "Server Script" +msgid "After Discard" +msgstr "" + #. Option for the 'DocType Event' (Select) field in DocType 'Server Script' #: core/doctype/server_script/server_script.json msgctxt "Server Script" @@ -1738,7 +1755,7 @@ msgstr "" msgid "All Records" msgstr "" -#: public/js/frappe/form/form.js:2139 +#: public/js/frappe/form/form.js:2205 msgid "All Submissions" msgstr "" @@ -2127,11 +2144,17 @@ msgctxt "User Type" msgid "Allowed Modules" msgstr "" -#: public/js/frappe/form/form.js:1193 +#. Label of a Table MultiSelect field in DocType 'OAuth Client' +#: integrations/doctype/oauth_client/oauth_client.json +msgctxt "OAuth Client" +msgid "Allowed Roles" +msgstr "" + +#: public/js/frappe/form/form.js:1250 msgid "Allowing DocType, DocType. Be careful!" msgstr "" -#: core/doctype/user/user.py:1017 +#: core/doctype/user/user.py:1018 msgid "Already Registered" msgstr "" @@ -2353,7 +2376,7 @@ msgctxt "Google Settings" msgid "App ID" msgstr "" -#: public/js/frappe/ui/toolbar/navbar.html:9 +#: public/js/frappe/ui/toolbar/navbar.html:8 msgid "App Logo" msgstr "" @@ -2401,7 +2424,7 @@ msgstr "" msgid "App not found for module: {0}" msgstr "" -#: __init__.py:1789 +#: __init__.py:1791 msgid "App {0} is not installed" msgstr "" @@ -2480,7 +2503,7 @@ msgctxt "Property Setter" msgid "Applied On" msgstr "" -#: public/js/frappe/list/list_view.js:1884 +#: public/js/frappe/list/list_view.js:1888 msgctxt "Button in list view actions menu" msgid "Apply Assignment Rule" msgstr "" @@ -2586,7 +2609,7 @@ msgstr "" msgid "Archived Columns" msgstr "" -#: public/js/frappe/list/list_view.js:1863 +#: public/js/frappe/list/list_view.js:1867 msgid "Are you sure you want to clear the assignments?" msgstr "" @@ -2606,7 +2629,7 @@ msgstr "" msgid "Are you sure you want to discard the changes?" msgstr "" -#: public/js/frappe/views/reports/query_report.js:896 +#: public/js/frappe/views/reports/query_report.js:897 msgid "Are you sure you want to generate a new report?" msgstr "" @@ -2685,7 +2708,7 @@ msgstr "" msgid "Assign To" msgstr "" -#: public/js/frappe/list/list_view.js:1845 +#: public/js/frappe/list/list_view.js:1849 msgctxt "Button in list view actions menu" msgid "Assign To" msgstr "" @@ -2850,7 +2873,7 @@ msgctxt "Notification Settings" msgid "Assignments" msgstr "" -#: public/js/frappe/form/grid_row.js:650 +#: public/js/frappe/form/grid_row.js:651 msgid "At least one column is required to show in the grid." msgstr "" @@ -3021,7 +3044,6 @@ msgctxt "Communication" msgid "Attachment Removed" msgstr "" -#: core/doctype/file/utils.py:37 #: email/doctype/newsletter/templates/newsletter.html:47 #: public/js/frappe/form/templates/form_sidebar.html:65 #: website/doctype/web_form/templates/web_form.html:103 @@ -3191,7 +3213,7 @@ msgstr "" msgid "Authors" msgstr "" -#: www/attribution.html:36 +#: www/attribution.html:37 msgid "Authors / Maintainers" msgstr "" @@ -3344,6 +3366,11 @@ msgctxt "DocType" msgid "Autoincrement" msgstr "" +#. Description of a Card Break in the Build Workspace +#: core/workspace/build/build.json +msgid "Automate processes and extend standard functionality using scripts and background jobs" +msgstr "" + #. Option for the 'Communication Type' (Select) field in DocType #. 'Communication' #: core/doctype/communication/communication.json @@ -3409,7 +3436,7 @@ msgctxt "Number Card" msgid "Average" msgstr "" -#: public/js/frappe/ui/group_by/group_by.js:330 +#: public/js/frappe/ui/group_by/group_by.js:333 msgid "Average of {0}" msgstr "" @@ -3580,12 +3607,30 @@ msgctxt "RQ Job" msgid "Background Jobs" msgstr "" +#. Label of a Section Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Background Jobs" +msgstr "" + +#. Label of a Data field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Background Jobs Check" +msgstr "" + #. Label of a Autocomplete field in DocType 'Webhook' #: integrations/doctype/webhook/webhook.json msgctxt "Webhook" msgid "Background Jobs Queue" msgstr "" +#. Label of a Table field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Background Workers" +msgstr "" + #. Label of a Section Break field in DocType 'System Settings' #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" @@ -3645,7 +3690,7 @@ msgctxt "S3 Backup Settings" msgid "Backup Frequency" msgstr "" -#: desk/page/backups/backups.py:98 +#: desk/page/backups/backups.py:95 msgid "Backup job is already queued. You will receive an email with the download link" msgstr "" @@ -3656,12 +3701,24 @@ msgctxt "S3 Backup Settings" msgid "Backup public and private files along with the database." msgstr "" +#. Label of a Section Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Backups" +msgstr "" + #. Label of a Tab Break field in DocType 'System Settings' #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" msgid "Backups" msgstr "" +#. Label of a Float field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Backups (MB)" +msgstr "" + #: core/doctype/scheduled_job_type/scheduled_job_type.py:64 msgid "Bad Cron Expression" msgstr "" @@ -3790,6 +3847,12 @@ msgctxt "Server Script" msgid "Before Delete" msgstr "" +#. Option for the 'DocType Event' (Select) field in DocType 'Server Script' +#: core/doctype/server_script/server_script.json +msgctxt "Server Script" +msgid "Before Discard" +msgstr "" + #. Option for the 'DocType Event' (Select) field in DocType 'Server Script' #: core/doctype/server_script/server_script.json msgctxt "Server Script" @@ -3866,6 +3929,12 @@ msgstr "" msgid "Billing Contact" msgstr "" +#. Label of a Data field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Binary Logging" +msgstr "" + #. Label of a Small Text field in DocType 'About Us Team Member' #: website/doctype/about_us_team_member/about_us_team_member.json msgctxt "About Us Team Member" @@ -4200,11 +4269,22 @@ msgstr "" msgid "Bucket {0} not found." msgstr "" +#. Label of a Data field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Bufferpool Size" +msgstr "" + #. Name of a Workspace #: core/workspace/build/build.json msgid "Build" msgstr "" +#. Description of a Card Break in the Build Workspace +#: core/workspace/build/build.json +msgid "Build your own reports, print formats, and dashboards. Create personalized workspaces for easier navigation" +msgstr "" + #: workflow/doctype/workflow/workflow_list.js:18 msgid "Build {0}" msgstr "" @@ -4231,6 +4311,14 @@ msgstr "" msgid "Bulk Edit {0}" msgstr "" +#: desk/reportview.py:524 +msgid "Bulk Operation Failed" +msgstr "" + +#: desk/reportview.py:528 +msgid "Bulk Operation Successful" +msgstr "" + #: public/js/frappe/list/bulk_operations.js:122 msgid "Bulk PDF Export" msgstr "" @@ -4455,7 +4543,13 @@ msgctxt "Blog Settings" msgid "CTA URL" msgstr "" -#: sessions.py:31 +#. Label of a Section Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Cache" +msgstr "" + +#: sessions.py:32 msgid "Cache Cleared" msgstr "" @@ -4609,7 +4703,7 @@ msgstr "" msgid "Cancel" msgstr "" -#: public/js/frappe/list/list_view.js:1954 +#: public/js/frappe/list/list_view.js:1958 msgctxt "Button in list view actions menu" msgid "Cancel" msgstr "" @@ -4650,11 +4744,11 @@ msgctxt "User Document Type" msgid "Cancel" msgstr "" -#: public/js/frappe/form/form.js:962 +#: public/js/frappe/form/form.js:973 msgid "Cancel All" msgstr "" -#: public/js/frappe/form/form.js:949 +#: public/js/frappe/form/form.js:960 msgid "Cancel All Documents" msgstr "" @@ -4662,7 +4756,7 @@ msgstr "" msgid "Cancel Scheduling" msgstr "" -#: public/js/frappe/list/list_view.js:1959 +#: public/js/frappe/list/list_view.js:1963 msgctxt "Title of confirmation dialog" msgid "Cancel {0} documents?" msgstr "" @@ -4731,7 +4825,7 @@ msgstr "" msgid "Cannot Remove" msgstr "" -#: model/base_document.py:1062 +#: model/base_document.py:1070 msgid "Cannot Update After Submit" msgstr "" @@ -4751,11 +4845,11 @@ msgstr "" msgid "Cannot cancel {0}." msgstr "" -#: model/document.py:852 +#: model/document.py:853 msgid "Cannot change docstatus from 0 (Draft) to 2 (Cancelled)" msgstr "" -#: model/document.py:866 +#: model/document.py:867 msgid "Cannot change docstatus from 1 (Submitted) to 0 (Draft)" msgstr "" @@ -4839,7 +4933,7 @@ msgstr "" msgid "Cannot edit a standard report. Please duplicate and create a new report" msgstr "" -#: model/document.py:872 +#: model/document.py:873 msgid "Cannot edit cancelled document" msgstr "" @@ -4863,11 +4957,11 @@ msgstr "" msgid "Cannot get file contents of a Folder" msgstr "" -#: printing/page/print/print.js:824 +#: printing/page/print/print.js:842 msgid "Cannot have multiple printers mapped to a single print format." msgstr "" -#: model/document.py:940 +#: model/document.py:941 msgid "Cannot link cancelled document: {0}" msgstr "" @@ -4944,7 +5038,7 @@ msgctxt "Workspace Link" msgid "Card Break" msgstr "" -#: public/js/frappe/views/reports/query_report.js:261 +#: public/js/frappe/views/reports/query_report.js:262 msgid "Card Label" msgstr "" @@ -5221,7 +5315,7 @@ msgstr "" msgid "Checking broken links..." msgstr "" -#: public/js/frappe/desk.js:214 +#: public/js/frappe/desk.js:220 msgid "Checking one moment" msgstr "" @@ -5322,7 +5416,7 @@ msgstr "" msgid "Clear & Add template" msgstr "" -#: public/js/frappe/list/list_view.js:1860 +#: public/js/frappe/list/list_view.js:1864 msgctxt "Button in list view actions menu" msgid "Clear Assignment" msgstr "" @@ -5422,7 +5516,7 @@ msgstr "" msgid "Click to Set Filters" msgstr "" -#: public/js/frappe/list/list_view.js:679 +#: public/js/frappe/list/list_view.js:680 msgid "Click to sort by {0}" msgstr "" @@ -5624,6 +5718,12 @@ msgctxt "OAuth Authorization Code" msgid "Code Challenge" msgstr "" +#. Label of a Select field in DocType 'User' +#: core/doctype/user/user.json +msgctxt "User" +msgid "Code Editor Type" +msgstr "" + #. Label of a Select field in DocType 'OAuth Authorization Code' #: integrations/doctype/oauth_authorization_code/oauth_authorization_code.json msgctxt "OAuth Authorization Code" @@ -5635,7 +5735,7 @@ msgstr "" msgid "Collapse" msgstr "" -#: public/js/frappe/form/controls/code.js:146 +#: public/js/frappe/form/controls/code.js:179 msgctxt "Shrink code field." msgid "Collapse" msgstr "" @@ -5682,7 +5782,7 @@ msgid "Collapsible Depends On (JS)" msgstr "" #. Name of a DocType -#: public/js/frappe/views/reports/query_report.js:1155 +#: public/js/frappe/views/reports/query_report.js:1156 #: public/js/frappe/widgets/widget_dialog.js:544 #: public/js/frappe/widgets/widget_dialog.js:696 #: website/doctype/color/color.json @@ -5835,11 +5935,11 @@ msgstr "" msgid "Column Name cannot be empty" msgstr "" -#: public/js/frappe/form/grid_row.js:430 +#: public/js/frappe/form/grid_row.js:431 msgid "Column Width" msgstr "" -#: public/js/frappe/form/grid_row.js:614 +#: public/js/frappe/form/grid_row.js:615 msgid "Column width cannot be zero." msgstr "" @@ -5888,7 +5988,7 @@ msgstr "" msgid "Columns based on" msgstr "" -#: integrations/doctype/oauth_client/oauth_client.py:44 +#: integrations/doctype/oauth_client/oauth_client.py:48 msgid "Combination of Grant Type ({0}) and Response Type ({1}) not allowed" msgstr "" @@ -6073,7 +6173,7 @@ msgstr "" msgid "Compare Versions" msgstr "" -#: core/doctype/server_script/server_script.py:141 +#: core/doctype/server_script/server_script.py:143 msgid "Compilation warning" msgstr "" @@ -6095,7 +6195,7 @@ msgstr "" msgid "Complete By" msgstr "" -#: core/doctype/user/user.py:474 templates/emails/new_user.html:10 +#: core/doctype/user/user.py:475 templates/emails/new_user.html:10 msgid "Complete Registration" msgstr "" @@ -6244,7 +6344,7 @@ msgstr "" msgid "Configure Chart" msgstr "" -#: public/js/frappe/form/grid_row.js:382 +#: public/js/frappe/form/grid_row.js:383 msgid "Configure Columns" msgstr "" @@ -6269,7 +6369,7 @@ msgstr "" msgid "Configure various aspects of how document naming works like naming series, current counter." msgstr "" -#: core/doctype/user/user.js:384 public/js/frappe/dom.js:332 +#: core/doctype/user/user.js:384 public/js/frappe/dom.js:345 #: www/update-password.html:30 msgid "Confirm" msgstr "" @@ -6356,7 +6456,7 @@ msgstr "" msgid "Connection Success" msgstr "" -#: public/js/frappe/dom.js:433 +#: public/js/frappe/dom.js:446 msgid "Connection lost. Some features might not work." msgstr "" @@ -6463,6 +6563,14 @@ msgctxt "Contact Us Settings" msgid "Contact options, like \"Sales Query, Support Query\" etc each on a new line or separated by commas." msgstr "" +#: utils/change_log.py:341 +msgid "Contains {0} security fix" +msgstr "" + +#: utils/change_log.py:339 +msgid "Contains {0} security fixes" +msgstr "" + #: public/js/frappe/utils/utils.js:1729 #: website/report/website_analytics/website_analytics.js:41 msgid "Content" @@ -6611,7 +6719,7 @@ msgstr "" #. Description of the 'Sign ups' (Select) field in DocType 'Social Login Key' #: integrations/doctype/social_login_key/social_login_key.json msgctxt "Social Login Key" -msgid "Controls whether new users can sign up using this Social Login Key. If unset, Website Settings is respected. " +msgid "Controls whether new users can sign up using this Social Login Key. If unset, Website Settings is respected." msgstr "" #: public/js/frappe/utils/utils.js:1031 @@ -6630,7 +6738,7 @@ msgstr "" msgid "Copy error to clipboard" msgstr "" -#: public/js/frappe/form/toolbar.js:388 +#: public/js/frappe/form/toolbar.js:398 msgid "Copy to Clipboard" msgstr "" @@ -6648,11 +6756,15 @@ msgstr "" msgid "Core Modules {0} cannot be searched in Global Search." msgstr "" +#: printing/page/print/print.js:617 +msgid "Correct version :" +msgstr "" + #: email/smtp.py:78 msgid "Could not connect to outgoing email server" msgstr "" -#: model/document.py:936 +#: model/document.py:937 msgid "Could not find {0}" msgstr "" @@ -6660,7 +6772,7 @@ msgstr "" msgid "Could not map column {0} to field {1}" msgstr "" -#: public/js/frappe/web_form/web_form.js:355 +#: public/js/frappe/web_form/web_form.js:359 msgid "Couldn't save, please check the data you have entered" msgstr "" @@ -6683,6 +6795,12 @@ msgctxt "Number Card" msgid "Count" msgstr "" +#. Label of a Int field in DocType 'System Health Report Workers' +#: desk/doctype/system_health_report_workers/system_health_report_workers.json +msgctxt "System Health Report Workers" +msgid "Count" +msgstr "" + #: public/js/frappe/widgets/widget_dialog.js:538 msgid "Count Customizations" msgstr "" @@ -6761,7 +6879,7 @@ msgstr "" #: public/js/frappe/form/reminders.js:49 #: public/js/frappe/views/file/file_view.js:112 #: public/js/frappe/views/interaction.js:18 -#: public/js/frappe/views/reports/query_report.js:1187 +#: public/js/frappe/views/reports/query_report.js:1188 #: public/js/frappe/views/workspace/workspace.js:1228 #: workflow/page/workflow_builder/workflow_builder.js:46 msgid "Create" @@ -6794,13 +6912,13 @@ msgstr "" msgid "Create Blogger" msgstr "" -#: public/js/frappe/views/reports/query_report.js:186 -#: public/js/frappe/views/reports/query_report.js:231 +#: public/js/frappe/views/reports/query_report.js:187 +#: public/js/frappe/views/reports/query_report.js:232 msgid "Create Card" msgstr "" -#: public/js/frappe/views/reports/query_report.js:284 -#: public/js/frappe/views/reports/query_report.js:1114 +#: public/js/frappe/views/reports/query_report.js:285 +#: public/js/frappe/views/reports/query_report.js:1115 msgid "Create Chart" msgstr "" @@ -6832,12 +6950,12 @@ msgid "Create Log" msgstr "" #: printing/page/print_format_builder_beta/print_format_builder_beta.js:41 -#: public/js/frappe/views/treeview.js:361 +#: public/js/frappe/views/treeview.js:362 #: workflow/page/workflow_builder/workflow_builder.js:41 msgid "Create New" msgstr "" -#: public/js/frappe/list/list_view.js:486 +#: public/js/frappe/list/list_view.js:487 msgctxt "Create a new document from list view" msgid "Create New" msgstr "" @@ -6874,10 +6992,10 @@ msgstr "" msgid "Create a new record" msgstr "" -#: public/js/frappe/form/controls/link.js:292 -#: public/js/frappe/form/controls/link.js:294 +#: public/js/frappe/form/controls/link.js:295 +#: public/js/frappe/form/controls/link.js:297 #: public/js/frappe/form/link_selector.js:139 -#: public/js/frappe/list/list_view.js:475 +#: public/js/frappe/list/list_view.js:476 #: public/js/frappe/web_form/web_form_list.js:225 msgid "Create a new {0}" msgstr "" @@ -6891,6 +7009,11 @@ msgstr "" msgid "Create and send emails to a specific group of subscribers periodically." msgstr "" +#. Description of a Card Break in the Build Workspace +#: core/workspace/build/build.json +msgid "Create new forms and views with doctypes. Set up multi-level workflows for approval" +msgstr "" + #: printing/page/print_format_builder_beta/print_format_builder_beta.js:34 msgid "Create or Edit Print Format" msgstr "" @@ -6899,7 +7022,7 @@ msgstr "" msgid "Create or Edit Workflow" msgstr "" -#: public/js/frappe/list/list_view.js:478 +#: public/js/frappe/list/list_view.js:479 msgid "Create your first {0}" msgstr "" @@ -6907,7 +7030,7 @@ msgstr "" msgid "Create your workflow visually using the Workflow Builder." msgstr "" -#: public/js/frappe/views/file/file_view.js:318 +#: public/js/frappe/views/file/file_view.js:337 msgid "Created" msgstr "" @@ -6945,7 +7068,7 @@ msgstr "" msgid "Created On" msgstr "" -#: public/js/frappe/desk.js:497 public/js/frappe/views/treeview.js:376 +#: public/js/frappe/desk.js:500 public/js/frappe/views/treeview.js:377 msgid "Creating {0}" msgstr "" @@ -7441,12 +7564,12 @@ msgstr "" #: printing/page/print/print.js:171 #: public/js/frappe/form/templates/print_layout.html:39 -#: public/js/frappe/form/toolbar.js:527 +#: public/js/frappe/form/toolbar.js:537 #: public/js/frappe/views/dashboard/dashboard_view.js:196 msgid "Customize" msgstr "" -#: public/js/frappe/list/list_view.js:1705 +#: public/js/frappe/list/list_view.js:1709 msgctxt "Button in list view menu" msgid "Customize" msgstr "" @@ -7487,6 +7610,11 @@ msgstr "" msgid "Customize Print Formats" msgstr "" +#. Description of a Card Break in the Build Workspace +#: core/workspace/build/build.json +msgid "Customize properties, naming, fields and more for standard doctypes" +msgstr "" + #: public/js/frappe/views/file/file_view.js:144 msgid "Cut" msgstr "" @@ -7853,10 +7981,17 @@ msgstr "" msgid "Data Too Long" msgstr "" -#: model/base_document.py:723 +#: model/base_document.py:731 msgid "Data missing in table" msgstr "" +#. Label of a Data field in DocType 'System Health Report' +#. Label of a Section Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Database" +msgstr "" + #. Label of a Select field in DocType 'DocType' #: core/doctype/doctype/doctype.json msgctxt "DocType" @@ -7886,6 +8021,12 @@ msgstr "" msgid "Database Table Row Size Utilization: {0}%, this limits number of fields you can add." msgstr "" +#. Label of a Data field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Database Version" +msgstr "" + #: desk/report/todo/todo.py:38 email/doctype/newsletter/newsletter.js:109 #: public/js/frappe/views/interaction.js:80 msgid "Date" @@ -8078,6 +8219,14 @@ msgctxt "Scheduled Job Log" msgid "Debug Log" msgstr "" +#: public/js/frappe/views/reports/report_utils.js:308 +msgid "Decimal Separator must be '.' when Quoting is set to Non-numeric" +msgstr "" + +#: public/js/frappe/views/reports/report_utils.js:300 +msgid "Decimal Separator must be a single character" +msgstr "" + #: templates/form_grid/fields.html:30 msgid "Default" msgstr "" @@ -8349,8 +8498,8 @@ msgstr "" #: core/doctype/user_permission/user_permission_list.js:189 #: public/js/frappe/form/footer/form_timeline.js:613 -#: public/js/frappe/form/grid.js:63 public/js/frappe/form/toolbar.js:423 -#: public/js/frappe/views/reports/report_view.js:1643 +#: public/js/frappe/form/grid.js:63 public/js/frappe/form/toolbar.js:433 +#: public/js/frappe/views/reports/report_view.js:1654 #: public/js/frappe/views/treeview.js:313 #: public/js/frappe/views/workspace/workspace.js:834 #: templates/discussions/reply_card.html:35 @@ -8358,7 +8507,7 @@ msgstr "" msgid "Delete" msgstr "" -#: public/js/frappe/list/list_view.js:1922 +#: public/js/frappe/list/list_view.js:1926 msgctxt "Button in list view actions menu" msgid "Delete" msgstr "" @@ -8401,7 +8550,7 @@ msgstr "" msgid "Delete Workspace" msgstr "" -#: public/js/frappe/views/reports/query_report.js:863 +#: public/js/frappe/views/reports/query_report.js:864 msgid "Delete and Generate New" msgstr "" @@ -8413,12 +8562,12 @@ msgstr "" msgid "Delete this record to allow sending to this email address" msgstr "" -#: public/js/frappe/list/list_view.js:1927 +#: public/js/frappe/list/list_view.js:1931 msgctxt "Title of confirmation dialog" msgid "Delete {0} item permanently?" msgstr "" -#: public/js/frappe/list/list_view.js:1933 +#: public/js/frappe/list/list_view.js:1937 msgctxt "Title of confirmation dialog" msgid "Delete {0} items permanently?" msgstr "" @@ -8472,6 +8621,10 @@ msgctxt "Deleted Document" msgid "Deleted Name" msgstr "" +#: desk/reportview.py:528 +msgid "Deleted all documents successfully" +msgstr "" + #: desk/reportview.py:506 msgid "Deleting {0}" msgstr "" @@ -8495,7 +8648,7 @@ msgstr "" msgid "Deletion of this document is only permitted in developer mode." msgstr "" -#: public/js/frappe/views/reports/report_utils.js:276 +#: public/js/frappe/views/reports/report_utils.js:296 msgid "Delimiter must be a single character" msgstr "" @@ -8521,7 +8674,7 @@ msgctxt "Contact" msgid "Department" msgstr "" -#: www/attribution.html:28 +#: www/attribution.html:29 msgid "Dependencies" msgstr "" @@ -8965,10 +9118,11 @@ msgctxt "Server Script" msgid "Disabled" msgstr "" -#: email/doctype/email_account/email_account.js:237 +#: email/doctype/email_account/email_account.js:232 msgid "Disabled Auto Reply" msgstr "" +#: public/js/frappe/form/toolbar.js:315 #: public/js/frappe/views/communication.js:30 #: public/js/frappe/views/dashboard/dashboard_view.js:70 #: public/js/frappe/views/workspace/workspace.js:513 @@ -8981,10 +9135,18 @@ msgctxt "Button in web form" msgid "Discard" msgstr "" +#: public/js/frappe/form/form.js:839 +msgid "Discard {0}" +msgstr "" + #: public/js/frappe/web_form/web_form.js:184 msgid "Discard?" msgstr "" +#: desk/form/save.py:70 +msgid "Discarded" +msgstr "" + #. Name of a DocType #: website/doctype/discussion_reply/discussion_reply.json msgid "Discussion Reply" @@ -9055,7 +9217,7 @@ msgstr "" msgid "Do you still want to proceed?" msgstr "" -#: public/js/frappe/form/form.js:941 +#: public/js/frappe/form/form.js:952 msgid "Do you want to cancel all linked documents?" msgstr "" @@ -9487,7 +9649,7 @@ msgstr "" msgid "Document Naming Settings" msgstr "" -#: model/document.py:1549 +#: model/document.py:1569 msgid "Document Queued" msgstr "" @@ -9734,19 +9896,19 @@ msgctxt "User Type" msgid "Document Types and Permissions" msgstr "" -#: core/doctype/submission_queue/submission_queue.py:163 model/document.py:1751 +#: core/doctype/submission_queue/submission_queue.py:163 model/document.py:1771 msgid "Document Unlocked" msgstr "" -#: public/js/frappe/list/list_view.js:1077 +#: public/js/frappe/list/list_view.js:1081 msgid "Document has been cancelled" msgstr "" -#: public/js/frappe/list/list_view.js:1076 +#: public/js/frappe/list/list_view.js:1080 msgid "Document has been submitted" msgstr "" -#: public/js/frappe/list/list_view.js:1075 +#: public/js/frappe/list/list_view.js:1079 msgid "Document is in draft state" msgstr "" @@ -9919,7 +10081,7 @@ msgstr "" msgid "Download" msgstr "" -#: public/js/frappe/views/reports/report_utils.js:229 +#: public/js/frappe/views/reports/report_utils.js:237 msgctxt "Export report" msgid "Download" msgstr "" @@ -9945,7 +10107,7 @@ msgstr "" msgid "Download PDF" msgstr "" -#: public/js/frappe/views/reports/query_report.js:766 +#: public/js/frappe/views/reports/query_report.js:767 msgid "Download Report" msgstr "" @@ -10024,7 +10186,7 @@ msgid "Due Date Based On" msgstr "" #: public/js/frappe/form/grid_row_form.js:42 -#: public/js/frappe/form/toolbar.js:377 +#: public/js/frappe/form/toolbar.js:387 #: public/js/frappe/views/workspace/workspace.js:819 #: public/js/frappe/views/workspace/workspace.js:986 msgid "Duplicate" @@ -10038,7 +10200,7 @@ msgstr "" msgid "Duplicate Filter Name" msgstr "" -#: model/base_document.py:582 model/rename_doc.py:111 +#: model/base_document.py:590 model/rename_doc.py:111 msgid "Duplicate Name" msgstr "" @@ -10192,8 +10354,8 @@ msgstr "" #: public/js/frappe/form/footer/form_timeline.js:661 #: public/js/frappe/form/templates/address_list.html:7 #: public/js/frappe/form/templates/contact_list.html:7 -#: public/js/frappe/form/toolbar.js:672 -#: public/js/frappe/views/reports/query_report.js:814 +#: public/js/frappe/form/toolbar.js:680 +#: public/js/frappe/views/reports/query_report.js:815 #: public/js/frappe/views/reports/query_report.js:1635 #: public/js/frappe/views/workspace/workspace.js:459 #: public/js/frappe/views/workspace/workspace.js:813 @@ -10207,7 +10369,7 @@ msgstr "" msgid "Edit" msgstr "" -#: public/js/frappe/list/list_view.js:2008 +#: public/js/frappe/list/list_view.js:2012 msgctxt "Button in list view actions menu" msgid "Edit" msgstr "" @@ -10218,7 +10380,7 @@ msgctxt "Comment" msgid "Edit" msgstr "" -#: public/js/frappe/form/grid_row.js:337 +#: public/js/frappe/form/grid_row.js:338 msgctxt "Edit grid row" msgid "Edit" msgstr "" @@ -10239,11 +10401,11 @@ msgstr "" msgid "Edit Custom HTML" msgstr "" -#: public/js/frappe/form/toolbar.js:546 +#: public/js/frappe/form/toolbar.js:556 msgid "Edit DocType" msgstr "" -#: public/js/frappe/list/list_view.js:1732 +#: public/js/frappe/list/list_view.js:1736 msgctxt "Button in list view menu" msgid "Edit DocType" msgstr "" @@ -10340,7 +10502,7 @@ msgstr "" msgid "Edit to add content" msgstr "" -#: public/js/frappe/web_form/web_form.js:442 +#: public/js/frappe/web_form/web_form.js:446 msgctxt "Button in web form" msgid "Edit your response" msgstr "" @@ -10401,9 +10563,9 @@ msgstr "" #: core/doctype/success_action/success_action.js:57 #: email/doctype/newsletter/newsletter.js:156 #: public/js/frappe/form/success_action.js:85 -#: public/js/frappe/form/toolbar.js:341 +#: public/js/frappe/form/toolbar.js:351 #: templates/includes/comments/comments.html:25 templates/signup.html:9 -#: www/login.html:7 www/login.py:93 +#: www/login.html:7 www/login.py:97 msgid "Email" msgstr "" @@ -10528,7 +10690,7 @@ msgctxt "Email Account" msgid "Email Account Name" msgstr "" -#: core/doctype/user/user.py:743 +#: core/doctype/user/user.py:744 msgid "Email Account added multiple times" msgstr "" @@ -10575,13 +10737,6 @@ msgstr "" msgid "Email Addresses" msgstr "" -#. Description of the 'Send Notification to' (Small Text) field in DocType -#. 'Email Account' -#: email/doctype/email_account/email_account.json -msgctxt "Email Account" -msgid "Email Addresses" -msgstr "" - #. Name of a DocType #: email/doctype/email_domain/email_domain.json msgid "Email Domain" @@ -10821,6 +10976,12 @@ msgstr "" msgid "Email not verified with {0}" msgstr "" +#. Label of a Section Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Emails" +msgstr "" + #: email/queue.py:137 msgid "Emails are muted" msgstr "" @@ -11150,7 +11311,7 @@ msgstr "" msgid "Enabled email inbox for user {0}" msgstr "" -#: core/doctype/server_script/server_script.py:269 +#: core/doctype/server_script/server_script.py:271 msgid "Enabled scheduled execution for script {0}" msgstr "" @@ -11168,7 +11329,7 @@ msgctxt "DocType" msgid "Enables Calendar and Gantt views." msgstr "" -#: email/doctype/email_account/email_account.js:232 +#: email/doctype/email_account/email_account.js:227 msgid "Enabling auto reply on an incoming email account will send automated replies to all the synchronized emails. Do you wish to continue?" msgstr "" @@ -11407,8 +11568,8 @@ msgstr "" msgid "Equals" msgstr "" -#: desk/page/backups/backups.js:35 model/base_document.py:723 -#: model/base_document.py:729 public/js/frappe/ui/messages.js:22 +#: desk/page/backups/backups.js:35 model/base_document.py:731 +#: model/base_document.py:737 public/js/frappe/ui/messages.js:22 msgid "Error" msgstr "" @@ -11521,7 +11682,7 @@ msgstr "" msgid "Error in Notification" msgstr "" -#: utils/pdf.py:52 +#: utils/pdf.py:53 msgid "Error in print format on line {0}: {1}" msgstr "" @@ -11533,14 +11694,20 @@ msgstr "" msgid "Error while evaluating Notification {0}. Please fix your template." msgstr "" -#: model/document.py:822 +#: model/document.py:823 msgid "Error: Document has been modified after you have opened it" msgstr "" -#: model/base_document.py:737 +#: model/base_document.py:745 msgid "Error: Value missing for {0}: {1}" msgstr "" +#. Label of a Section Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Errors" +msgstr "" + #. Name of a DocType #: desk/doctype/event/event.json msgid "Event" @@ -11743,7 +11910,7 @@ msgstr "" msgid "Expand" msgstr "" -#: public/js/frappe/form/controls/code.js:147 +#: public/js/frappe/form/controls/code.js:180 msgctxt "Enlarge code field." msgid "Expand" msgstr "" @@ -11819,7 +11986,7 @@ msgstr "" msgid "Export" msgstr "" -#: public/js/frappe/list/list_view.js:2030 +#: public/js/frappe/list/list_view.js:2034 msgctxt "Button in list view actions menu" msgid "Export" msgstr "" @@ -11840,10 +12007,6 @@ msgstr "" msgid "Export 1 record" msgstr "" -#: public/js/frappe/views/reports/report_view.js:1561 -msgid "Export All {0} rows?" -msgstr "" - #: custom/doctype/customize_form/customize_form.js:262 msgid "Export Custom Permissions" msgstr "" @@ -11873,11 +12036,11 @@ msgctxt "Access Log" msgid "Export From" msgstr "" -#: core/doctype/data_import/data_import.js:524 +#: core/doctype/data_import/data_import.js:518 msgid "Export Import Log" msgstr "" -#: public/js/frappe/views/reports/report_utils.js:227 +#: public/js/frappe/views/reports/report_utils.js:235 msgctxt "Export report" msgid "Export Report: {0}" msgstr "" @@ -11886,6 +12049,14 @@ msgstr "" msgid "Export Type" msgstr "" +#: public/js/frappe/views/reports/report_view.js:1561 +msgid "Export all matching rows?" +msgstr "" + +#: public/js/frappe/views/reports/report_view.js:1571 +msgid "Export all {0} rows?" +msgstr "" + #: public/js/frappe/views/file/file_view.js:154 msgid "Export as zip" msgstr "" @@ -11965,6 +12136,13 @@ msgctxt "Social Login Key" msgid "Facebook" msgstr "" +#. Option for the 'SocketIO Ping Check' (Select) field in DocType 'System +#. Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Fail" +msgstr "" + #. Option for the 'Status' (Select) field in DocType 'Activity Log' #: core/doctype/activity_log/activity_log.json msgctxt "Activity Log" @@ -11989,12 +12167,30 @@ msgctxt "Submission Queue" msgid "Failed" msgstr "" +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Failed Emails" +msgstr "" + #. Label of a Int field in DocType 'RQ Worker' #: core/doctype/rq_worker/rq_worker.json msgctxt "RQ Worker" msgid "Failed Job Count" msgstr "" +#. Label of a Int field in DocType 'System Health Report Workers' +#: desk/doctype/system_health_report_workers/system_health_report_workers.json +msgctxt "System Health Report Workers" +msgid "Failed Jobs" +msgstr "" + +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Failed Logins (Last 30 days)" +msgstr "" + #: model/workflow.py:298 msgid "Failed Transactions" msgstr "" @@ -12008,6 +12204,7 @@ msgid "Failed to change password." msgstr "" #: desk/page/setup_wizard/setup_wizard.js:220 +#: desk/page/setup_wizard/setup_wizard.py:36 msgid "Failed to complete setup" msgstr "" @@ -12024,6 +12221,10 @@ msgstr "" msgid "Failed to decode token, please provide a valid base64-encoded token." msgstr "" +#: desk/reportview.py:522 +msgid "Failed to delete {0} documents: {1}" +msgstr "" + #: core/doctype/rq_job/rq_job_list.js:33 msgid "Failed to enable scheduler: {0}" msgstr "" @@ -12072,10 +12273,22 @@ msgstr "" msgid "Failed to update global settings" msgstr "" -#: core/doctype/data_import/data_import.js:465 +#. Label of a Table field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Failing Scheduled Jobs (last 7 days)" +msgstr "" + +#: core/doctype/data_import/data_import.js:459 msgid "Failure" msgstr "" +#. Label of a Percent field in DocType 'System Health Report Failing Jobs' +#: desk/doctype/system_health_report_failing_jobs/system_health_report_failing_jobs.json +msgctxt "System Health Report Failing Jobs" +msgid "Failure Rate" +msgstr "" + #. Label of a Attach field in DocType 'Website Settings' #: website/doctype/website_settings/website_settings.json msgctxt "Website Settings" @@ -12167,7 +12380,7 @@ msgstr "" #: desk/page/leaderboard/leaderboard.js:131 #: public/js/frappe/list/bulk_operations.js:297 #: public/js/frappe/list/list_view_permission_restrictions.html:3 -#: public/js/frappe/views/reports/query_report.js:235 +#: public/js/frappe/views/reports/query_report.js:236 #: public/js/frappe/views/reports/query_report.js:1724 msgid "Field" msgstr "" @@ -12296,12 +12509,12 @@ msgstr "" msgid "Field {0} is referring to non-existing doctype {1}." msgstr "" -#: public/js/frappe/form/form.js:1694 +#: public/js/frappe/form/form.js:1760 msgid "Field {0} not found." msgstr "" #: custom/doctype/custom_field/custom_field.js:120 -#: public/js/frappe/form/grid_row.js:430 +#: public/js/frappe/form/grid_row.js:431 msgid "Fieldname" msgstr "" @@ -12514,7 +12727,7 @@ msgctxt "Form Tour" msgid "File" msgstr "" -#: core/doctype/file/utils.py:128 +#: core/doctype/file/utils.py:127 msgid "File '{0}' not found" msgstr "" @@ -12552,6 +12765,12 @@ msgctxt "File" msgid "File Size" msgstr "" +#. Label of a Section Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "File Storage" +msgstr "" + #: public/js/frappe/data_import/data_exporter.js:19 msgid "File Type" msgstr "" @@ -12580,7 +12799,7 @@ msgctxt "File" msgid "File URL" msgstr "" -#: desk/page/backups/backups.py:107 +#: desk/page/backups/backups.py:104 msgid "File backup is ready" msgstr "" @@ -12671,11 +12890,11 @@ msgctxt "Prepared Report" msgid "Filter Values" msgstr "" -#: utils/data.py:2022 +#: utils/data.py:2025 msgid "Filter must be a tuple or list (in a list)" msgstr "" -#: utils/data.py:2030 +#: utils/data.py:2033 msgid "Filter must have 4 values (doctype, fieldname, operator, value): {0}" msgstr "" @@ -12748,10 +12967,6 @@ msgctxt "Report" msgid "Filters" msgstr "" -#: public/js/frappe/ui/filters/filter_list.js:133 -msgid "Filters {0}" -msgstr "" - #. Label of a Code field in DocType 'Number Card' #: desk/doctype/number_card/number_card.json msgctxt "Number Card" @@ -12782,7 +12997,7 @@ msgctxt "Number Card" msgid "Filters Section" msgstr "" -#: public/js/frappe/form/controls/link.js:488 +#: public/js/frappe/form/controls/link.js:491 msgid "Filters applied for {0}" msgstr "" @@ -12796,6 +13011,10 @@ msgctxt "Report" msgid "Filters will be accessible via filters.

Send output as result = [result], or for old style data = [columns], [result]" msgstr "" +#: public/js/frappe/ui/filters/filter_list.js:133 +msgid "Filters {0}" +msgstr "" + #: public/js/frappe/views/reports/report_view.js:1350 msgid "Filters:" msgstr "" @@ -13460,7 +13679,7 @@ msgctxt "Currency" msgid "Fraction Units" msgstr "" -#: www/login.html:61 www/login.html:142 www/login.py:44 www/login.py:133 +#: www/login.html:61 www/login.html:142 www/login.py:48 www/login.py:137 msgid "Frappe" msgstr "" @@ -13659,7 +13878,7 @@ msgctxt "Web Page" msgid "Full Width" msgstr "" -#: public/js/frappe/views/reports/query_report.js:245 +#: public/js/frappe/views/reports/query_report.js:246 #: public/js/frappe/widgets/widget_dialog.js:705 msgid "Function" msgstr "" @@ -13674,11 +13893,11 @@ msgstr "" msgid "Function Based On" msgstr "" -#: __init__.py:936 +#: __init__.py:934 msgid "Function {0} is not whitelisted." msgstr "" -#: public/js/frappe/views/treeview.js:402 +#: public/js/frappe/views/treeview.js:403 msgid "Further nodes can be only created under 'Group' type nodes" msgstr "" @@ -13758,7 +13977,7 @@ msgctxt "User" msgid "Generate Keys" msgstr "" -#: public/js/frappe/views/reports/query_report.js:808 +#: public/js/frappe/views/reports/query_report.js:809 msgid "Generate New Report" msgstr "" @@ -13894,7 +14113,7 @@ msgid "Global Unsubscribe" msgstr "" #: desk/page/user_profile/user_profile_controller.js:68 -#: public/js/frappe/form/toolbar.js:767 +#: public/js/frappe/form/toolbar.js:775 msgid "Go" msgstr "" @@ -14255,7 +14474,7 @@ msgstr "" msgid "Group By field is required to create a dashboard chart" msgstr "" -#: public/js/frappe/views/treeview.js:401 +#: public/js/frappe/views/treeview.js:402 msgid "Group Node" msgstr "" @@ -14265,7 +14484,12 @@ msgctxt "LDAP Settings" msgid "Group Object Class" msgstr "" -#: public/js/frappe/ui/group_by/group_by.js:413 +#. Description of a Card Break in the Build Workspace +#: core/workspace/build/build.json +msgid "Group your custom doctypes under modules" +msgstr "" + +#: public/js/frappe/ui/group_by/group_by.js:416 msgid "Grouped by {0}" msgstr "" @@ -14428,6 +14652,12 @@ msgctxt "Auto Repeat" msgid "Half-yearly" msgstr "" +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Handled Emails" +msgstr "" + #. Label of a Check field in DocType 'Communication' #: core/doctype/communication/communication.json msgctxt "Communication" @@ -14574,7 +14804,7 @@ msgstr "" #: public/js/frappe/form/templates/form_sidebar.html:40 #: public/js/frappe/form/workflow.js:23 -#: public/js/frappe/ui/toolbar/navbar.html:88 public/js/frappe/utils/help.js:27 +#: public/js/frappe/ui/toolbar/navbar.html:87 public/js/frappe/utils/help.js:27 msgid "Help" msgstr "" @@ -14618,7 +14848,7 @@ msgctxt "Help Category" msgid "Help Category" msgstr "" -#: public/js/frappe/ui/toolbar/navbar.html:85 +#: public/js/frappe/ui/toolbar/navbar.html:84 msgid "Help Dropdown" msgstr "" @@ -14884,7 +15114,7 @@ msgctxt "Portal Settings" msgid "Hide Standard Menu" msgstr "" -#: public/js/frappe/list/list_view.js:1607 +#: public/js/frappe/list/list_view.js:1611 msgid "Hide Tags" msgstr "" @@ -14945,7 +15175,7 @@ msgstr "" msgid "Hint: Include symbols, numbers and capital letters in the password" msgstr "" -#: core/doctype/file/utils.py:28 public/js/frappe/views/file/file_view.js:67 +#: public/js/frappe/views/file/file_view.js:67 #: public/js/frappe/views/file/file_view.js:88 #: public/js/frappe/views/pageview.js:153 templates/doc.html:19 #: templates/includes/navbar/navbar.html:9 @@ -15044,8 +15274,8 @@ msgstr "" #: model/meta.py:45 public/js/frappe/data_import/data_exporter.js:329 #: public/js/frappe/data_import/data_exporter.js:344 #: public/js/frappe/list/list_settings.js:334 -#: public/js/frappe/list/list_view.js:357 -#: public/js/frappe/list/list_view.js:421 public/js/frappe/model/meta.js:197 +#: public/js/frappe/list/list_view.js:358 +#: public/js/frappe/list/list_view.js:422 public/js/frappe/model/meta.js:197 #: public/js/frappe/model/model.js:122 msgid "ID" msgstr "" @@ -15596,7 +15826,7 @@ msgstr "" msgid "Image field must be of type Attach Image" msgstr "" -#: core/doctype/file/utils.py:136 +#: core/doctype/file/utils.py:135 msgid "Image link '{0}' is not valid" msgstr "" @@ -15626,7 +15856,7 @@ msgstr "" msgid "Impersonated by {0}" msgstr "" -#: public/js/frappe/ui/toolbar/navbar.html:22 +#: public/js/frappe/ui/toolbar/navbar.html:21 msgid "Impersonating {0}" msgstr "" @@ -15645,7 +15875,7 @@ msgstr "" msgid "Import" msgstr "" -#: public/js/frappe/list/list_view.js:1669 +#: public/js/frappe/list/list_view.js:1673 msgctxt "Button in list view menu" msgid "Import" msgstr "" @@ -15944,11 +16174,11 @@ msgctxt "System Settings" msgid "Include Web View Link in Email" msgstr "" -#: public/js/frappe/views/reports/query_report.js:1506 +#: public/js/frappe/views/reports/query_report.js:1507 msgid "Include filters" msgstr "" -#: public/js/frappe/views/reports/query_report.js:1498 +#: public/js/frappe/views/reports/query_report.js:1499 msgid "Include indentation" msgstr "" @@ -15956,12 +16186,24 @@ msgstr "" msgid "Include symbols, numbers and capital letters in the password" msgstr "" +#. Label of a Tab Break field in DocType 'Email Account' +#: email/doctype/email_account/email_account.json +msgctxt "Email Account" +msgid "Incoming (POP/IMAP)" +msgstr "" + #. Label of a Section Break field in DocType 'Email Account' #: email/doctype/email_account/email_account.json msgctxt "Email Account" msgid "Incoming (POP/IMAP) Settings" msgstr "" +#. Label of a Column Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Incoming Emails (Last 7 days)" +msgstr "" + #. Label of a Data field in DocType 'Email Account' #: email/doctype/email_account/email_account.json msgctxt "Email Account" @@ -16008,11 +16250,11 @@ msgstr "" msgid "Incorrect Verification code" msgstr "" -#: model/document.py:1364 +#: model/document.py:1384 msgid "Incorrect value in row {0}: {1} must be {2} {3}" msgstr "" -#: model/document.py:1368 +#: model/document.py:1388 msgid "Incorrect value: {0} must be {1} {2}" msgstr "" @@ -16373,16 +16615,16 @@ msgctxt "OAuth Authorization Code" msgid "Invalid" msgstr "" -#: public/js/form_builder/utils.js:221 public/js/frappe/form/grid_row.js:769 +#: public/js/form_builder/utils.js:221 public/js/frappe/form/grid_row.js:770 #: public/js/frappe/form/layout.js:782 msgid "Invalid \"depends_on\" expression" msgstr "" -#: public/js/frappe/views/reports/query_report.js:510 +#: public/js/frappe/views/reports/query_report.js:512 msgid "Invalid \"depends_on\" expression set in filter {0}" msgstr "" -#: public/js/frappe/form/save.js:206 +#: public/js/frappe/form/save.js:159 msgid "Invalid \"mandatory_depends_on\" expression" msgstr "" @@ -16438,7 +16680,7 @@ msgstr "" msgid "Invalid Link" msgstr "" -#: www/login.py:112 +#: www/login.py:116 msgid "Invalid Login Token" msgstr "" @@ -16446,7 +16688,7 @@ msgstr "" msgid "Invalid Login. Try again." msgstr "" -#: email/receive.py:104 email/receive.py:141 +#: email/receive.py:108 email/receive.py:145 msgid "Invalid Mail Server. Please rectify and try again." msgstr "" @@ -16470,11 +16712,15 @@ msgstr "" msgid "Invalid Output Format" msgstr "" +#: model/base_document.py:104 +msgid "Invalid Override" +msgstr "" + #: integrations/doctype/connected_app/connected_app.py:167 msgid "Invalid Parameters." msgstr "" -#: core/doctype/user/user.py:1229 www/update-password.html:121 +#: core/doctype/user/user.py:1230 www/update-password.html:121 #: www/update-password.html:142 www/update-password.html:144 #: www/update-password.html:245 msgid "Invalid Password" @@ -16484,7 +16730,7 @@ msgstr "" msgid "Invalid Phone Number" msgstr "" -#: auth.py:93 utils/oauth.py:179 utils/oauth.py:186 www/login.py:112 +#: auth.py:93 utils/oauth.py:179 utils/oauth.py:186 www/login.py:116 msgid "Invalid Request" msgstr "" @@ -16505,7 +16751,7 @@ msgstr "" msgid "Invalid URL" msgstr "" -#: email/receive.py:149 +#: email/receive.py:153 msgid "Invalid User Name or Support Password. Please rectify and try again." msgstr "" @@ -16521,7 +16767,7 @@ msgstr "" msgid "Invalid column" msgstr "" -#: model/document.py:855 model/document.py:869 +#: model/document.py:856 model/document.py:870 msgid "Invalid docstatus" msgstr "" @@ -16533,7 +16779,7 @@ msgstr "" msgid "Invalid expression set in filter {0} ({1})" msgstr "" -#: utils/data.py:2129 +#: utils/data.py:2132 msgid "Invalid field name {0}" msgstr "" @@ -16596,6 +16842,10 @@ msgctxt "Error message in web form" msgid "Invalid values for fields:" msgstr "" +#: printing/page/print/print.js:611 +msgid "Invalid wkhtmltopdf version" +msgstr "" + #: core/doctype/doctype/doctype.py:1531 msgid "Invalid {0} condition" msgstr "" @@ -16968,7 +17218,7 @@ msgctxt "DocType" msgid "Is Virtual" msgstr "" -#: core/doctype/file/utils.py:157 utils/file_manager.py:311 +#: core/doctype/file/utils.py:156 utils/file_manager.py:311 msgid "It is risky to delete this file: {0}. Please contact your System Manager." msgstr "" @@ -17128,7 +17378,7 @@ msgstr "" msgid "Join video conference with {0}" msgstr "" -#: public/js/frappe/form/toolbar.js:355 public/js/frappe/form/toolbar.js:757 +#: public/js/frappe/form/toolbar.js:365 public/js/frappe/form/toolbar.js:765 msgid "Jump to field" msgstr "" @@ -17619,6 +17869,12 @@ msgctxt "Language" msgid "Language Name" msgstr "" +#. Label of a Code field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Last 10 active users" +msgstr "" + #. Label of a Datetime field in DocType 'User' #: core/doctype/user/user.json msgctxt "User" @@ -18009,7 +18265,7 @@ msgctxt "Review Level" msgid "Level Name" msgstr "" -#: www/attribution.html:35 +#: www/attribution.html:36 msgid "License" msgstr "" @@ -18362,7 +18618,7 @@ msgid "Linked With" msgstr "" #: contacts/doctype/address/address.js:39 -#: contacts/doctype/contact/contact.js:87 public/js/frappe/form/toolbar.js:366 +#: contacts/doctype/contact/contact.js:87 public/js/frappe/form/toolbar.js:376 msgid "Links" msgstr "" @@ -18438,7 +18694,7 @@ msgctxt "Web Form" msgid "List Setting Message" msgstr "" -#: public/js/frappe/list/list_view.js:1749 +#: public/js/frappe/list/list_view.js:1753 msgctxt "Button in list view menu" msgid "List Settings" msgstr "" @@ -18482,6 +18738,13 @@ msgctxt "Web Page" msgid "List as [{\"label\": _(\"Jobs\"), \"route\":\"jobs\"}]" msgstr "" +#. Description of the 'Send Notification to' (Small Text) field in DocType +#. 'Email Account' +#: email/doctype/email_account/email_account.json +msgctxt "Email Account" +msgid "List of email addresses, separated by comma or new line." +msgstr "" + #. Description of a DocType #: core/doctype/patch_log/patch_log.json msgid "List of patches executed" @@ -18512,8 +18775,8 @@ msgstr "" #: public/js/frappe/form/controls/multicheck.js:13 #: public/js/frappe/form/linked_with.js:13 #: public/js/frappe/list/base_list.js:490 -#: public/js/frappe/list/list_view.js:334 public/js/frappe/ui/listing.html:16 -#: public/js/frappe/views/reports/query_report.js:1016 +#: public/js/frappe/list/list_view.js:335 public/js/frappe/ui/listing.html:16 +#: public/js/frappe/views/reports/query_report.js:1017 msgid "Loading" msgstr "" @@ -18567,7 +18830,7 @@ msgctxt "Logs To Clear" msgid "Log DocType" msgstr "" -#: templates/emails/login_with_email_link.html:28 +#: templates/emails/login_with_email_link.html:27 msgid "Log In To {0}" msgstr "" @@ -18634,7 +18897,7 @@ msgctxt "User" msgid "Login Before" msgstr "" -#: public/js/frappe/desk.js:235 +#: public/js/frappe/desk.js:241 msgid "Login Failed please try again" msgstr "" @@ -18660,7 +18923,7 @@ msgctxt "Web Form" msgid "Login Required" msgstr "" -#: www/login.py:136 +#: www/login.py:140 msgid "Login To {0}" msgstr "" @@ -18728,12 +18991,6 @@ msgstr "" msgid "Login with username and password is not allowed." msgstr "" -#. Label of a Int field in DocType 'Navbar Settings' -#: core/doctype/navbar_settings/navbar_settings.json -msgctxt "Navbar Settings" -msgid "Logo Width" -msgstr "" - #. Option for the 'Operation' (Select) field in DocType 'Activity Log' #: core/doctype/activity_log/activity_log.json msgctxt "Activity Log" @@ -18978,11 +19235,11 @@ msgstr "" msgid "Mandatory field: {0}" msgstr "" -#: public/js/frappe/form/save.js:167 +#: public/js/frappe/form/save.js:120 msgid "Mandatory fields required in table {0}, Row {1}" msgstr "" -#: public/js/frappe/form/save.js:172 +#: public/js/frappe/form/save.js:125 msgid "Mandatory fields required in {0}" msgstr "" @@ -19044,6 +19301,12 @@ msgctxt "Print Format" msgid "Margin Top" msgstr "" +#. Label of a Section Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "MariaDB Variables" +msgstr "" + #: public/js/frappe/ui/notifications/notifications.js:45 msgid "Mark all as read" msgstr "" @@ -19218,7 +19481,7 @@ msgid "" "(Note: For no limit leave this field empty or set 0)" msgstr "" -#: model/rename_doc.py:667 +#: model/rename_doc.py:672 msgid "Maximum {0} rows allowed" msgstr "" @@ -19272,6 +19535,12 @@ msgctxt "Email Group" msgid "Members" msgstr "" +#. Label of a Data field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Memory Usage" +msgstr "" + #. Option for the 'Type' (Select) field in DocType 'Notification Log' #: desk/doctype/notification_log/notification_log.json msgctxt "Notification Log" @@ -19296,7 +19565,7 @@ msgstr "" msgid "Merging is only possible between Group-to-Group or Leaf Node-to-Leaf Node" msgstr "" -#: core/doctype/data_import/data_import.js:489 +#: core/doctype/data_import/data_import.js:483 #: public/js/frappe/ui/messages.js:175 #: public/js/frappe/views/communication.js:114 www/message.html:3 #: www/message.html:25 @@ -19328,7 +19597,7 @@ msgctxt "Communication" msgid "Message" msgstr "" -#: __init__.py:620 public/js/frappe/ui/messages.js:265 +#: __init__.py:618 public/js/frappe/ui/messages.js:265 msgctxt "Default title of the message dialog" msgid "Message" msgstr "" @@ -19638,7 +19907,7 @@ msgstr "" msgid "Missing Field" msgstr "" -#: public/js/frappe/form/save.js:178 +#: public/js/frappe/form/save.js:131 msgid "Missing Fields" msgstr "" @@ -19661,7 +19930,7 @@ msgstr "" msgid "Missing Values Required" msgstr "" -#: www/login.py:96 +#: www/login.py:100 msgid "Mobile" msgstr "" @@ -19969,6 +20238,11 @@ msgctxt "System Settings" msgid "Monday" msgstr "" +#. Description of a Card Break in the Build Workspace +#: core/workspace/build/build.json +msgid "Monitor logs for errors, background jobs, communications, and user activity" +msgstr "" + #. Option for the 'Font' (Select) field in DocType 'Print Settings' #: printing/doctype/print_settings/print_settings.json msgctxt "Print Settings" @@ -20119,7 +20393,7 @@ msgstr "" msgid "Move" msgstr "" -#: public/js/frappe/form/grid_row.js:189 +#: public/js/frappe/form/grid_row.js:190 msgid "Move To" msgstr "" @@ -20248,7 +20522,7 @@ msgstr "" #: public/js/frappe/form/layout.js:75 #: public/js/frappe/form/multi_select_dialog.js:239 -#: public/js/frappe/form/save.js:154 +#: public/js/frappe/form/save.js:107 #: public/js/frappe/views/file/file_view.js:97 #: website/doctype/website_slideshow/website_slideshow.js:25 msgid "Name" @@ -20410,12 +20684,12 @@ msgstr "" msgid "Navigate Home" msgstr "" -#: public/js/frappe/list/list_view.js:1157 +#: public/js/frappe/list/list_view.js:1161 msgctxt "Description of a list view shortcut" msgid "Navigate list down" msgstr "" -#: public/js/frappe/list/list_view.js:1164 +#: public/js/frappe/list/list_view.js:1168 msgctxt "Description of a list view shortcut" msgid "Navigate list up" msgstr "" @@ -20454,7 +20728,7 @@ msgstr "" #: core/doctype/success_action/success_action.js:55 #: core/page/dashboard_view/dashboard_view.js:173 desk/doctype/todo/todo.js:46 #: public/js/frappe/form/success_action.js:77 -#: public/js/frappe/views/treeview.js:454 +#: public/js/frappe/views/treeview.js:455 #: website/doctype/web_form/templates/web_list.html:15 www/list.html:19 msgid "New" msgstr "" @@ -20597,6 +20871,12 @@ msgstr "" msgid "New Shortcut" msgstr "" +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "New Users (Last 30 days)" +msgstr "" + #: core/doctype/version/version_view.html:14 #: core/doctype/version/version_view.html:76 msgid "New Value" @@ -20614,7 +20894,7 @@ msgstr "" msgid "New password cannot be same as old password" msgstr "" -#: utils/change_log.py:320 +#: utils/change_log.py:372 msgid "New updates are available" msgstr "" @@ -20634,7 +20914,7 @@ msgstr "" #: public/js/frappe/form/quick_entry.js:129 public/js/frappe/form/toolbar.js:36 #: public/js/frappe/form/toolbar.js:196 public/js/frappe/form/toolbar.js:209 -#: public/js/frappe/form/toolbar.js:490 +#: public/js/frappe/form/toolbar.js:500 #: public/js/frappe/ui/toolbar/search_utils.js:167 #: public/js/frappe/ui/toolbar/search_utils.js:168 #: public/js/frappe/ui/toolbar/search_utils.js:217 @@ -20645,16 +20925,16 @@ msgstr "" msgid "New {0}" msgstr "" -#: public/js/frappe/views/reports/query_report.js:392 +#: public/js/frappe/views/reports/query_report.js:393 msgid "New {0} Created" msgstr "" -#: public/js/frappe/views/reports/query_report.js:384 +#: public/js/frappe/views/reports/query_report.js:385 msgid "New {0} {1} added to Dashboard {2}" msgstr "" #: public/js/frappe/form/quick_entry.js:172 -#: public/js/frappe/views/reports/query_report.js:389 +#: public/js/frappe/views/reports/query_report.js:390 msgid "New {0} {1} created" msgstr "" @@ -20662,11 +20942,11 @@ msgstr "" msgid "New {0}: {1}" msgstr "" -#: utils/change_log.py:312 +#: utils/change_log.py:354 msgid "New {} releases for the following apps are available" msgstr "" -#: core/doctype/user/user.py:806 +#: core/doctype/user/user.py:807 msgid "Newly created user {0} has no roles enabled." msgstr "" @@ -20805,9 +21085,9 @@ msgstr "" #: integrations/doctype/webhook/webhook.py:140 #: public/js/form_builder/utils.js:341 -#: public/js/frappe/form/controls/link.js:472 +#: public/js/frappe/form/controls/link.js:475 #: public/js/frappe/list/list_sidebar_group_by.js:223 -#: public/js/frappe/views/reports/query_report.js:1531 +#: public/js/frappe/views/reports/query_report.js:1527 #: website/doctype/help_article/templates/help_article.html:26 msgid "No" msgstr "" @@ -20929,7 +21209,7 @@ msgstr "" msgid "No Label" msgstr "" -#: printing/page/print/print.js:682 printing/page/print/print.js:764 +#: printing/page/print/print.js:700 printing/page/print/print.js:782 #: public/js/frappe/list/bulk_operations.js:90 #: public/js/frappe/list/bulk_operations.js:140 utils/weasyprint.py:52 msgid "No Letterhead" @@ -20963,11 +21243,11 @@ msgstr "" msgid "No Preview" msgstr "" -#: printing/page/print/print.js:686 +#: printing/page/print/print.js:704 msgid "No Preview Available" msgstr "" -#: printing/page/print/print.js:842 +#: printing/page/print/print.js:860 msgid "No Printer is Available." msgstr "" @@ -20983,7 +21263,7 @@ msgstr "" msgid "No Results found" msgstr "" -#: core/doctype/user/user.py:807 +#: core/doctype/user/user.py:808 msgid "No Roles Specified" msgstr "" @@ -20991,7 +21271,7 @@ msgstr "" msgid "No Select Field Found" msgstr "" -#: desk/reportview.py:584 +#: desk/reportview.py:594 msgid "No Tags" msgstr "" @@ -21015,7 +21295,7 @@ msgstr "" msgid "No broken links found in the email content" msgstr "" -#: public/js/frappe/form/save.js:38 +#: public/js/frappe/form/save.js:36 msgid "No changes in document" msgstr "" @@ -21067,7 +21347,7 @@ msgstr "" msgid "No email account associated with the User. Please add an account under User > Email Inbox." msgstr "" -#: core/doctype/data_import/data_import.js:484 +#: core/doctype/data_import/data_import.js:478 msgid "No failed logs" msgstr "" @@ -21107,7 +21387,7 @@ msgstr "" msgid "No new Google Contacts synced." msgstr "" -#: public/js/frappe/ui/toolbar/navbar.html:47 +#: public/js/frappe/ui/toolbar/navbar.html:46 msgid "No new notifications" msgstr "" @@ -21133,11 +21413,11 @@ msgctxt "SMS Log" msgid "No of Sent SMS" msgstr "" -#: __init__.py:1124 client.py:109 client.py:151 +#: __init__.py:1126 client.py:109 client.py:151 msgid "No permission for {0}" msgstr "" -#: public/js/frappe/form/form.js:1079 +#: public/js/frappe/form/form.js:1136 msgctxt "{0} = verb, {1} = object" msgid "No permission to '{0}' {1}" msgstr "" @@ -21186,7 +21466,7 @@ msgstr "" msgid "No {0} found" msgstr "" -#: public/js/frappe/list/list_view.js:468 +#: public/js/frappe/list/list_view.js:469 msgid "No {0} found with matching filters. Clear filters to see all {0}." msgstr "" @@ -21194,7 +21474,7 @@ msgstr "" msgid "No {0} mail" msgstr "" -#: public/js/form_builder/utils.js:117 public/js/frappe/form/grid_row.js:252 +#: public/js/form_builder/utils.js:117 public/js/frappe/form/grid_row.js:253 msgctxt "Title of the 'row number' column" msgid "No." msgstr "" @@ -21240,7 +21520,7 @@ msgctxt "Recorder Query" msgid "Normalized Query" msgstr "" -#: core/doctype/user/user.py:1012 templates/includes/login/login.js:258 +#: core/doctype/user/user.py:1013 templates/includes/login/login.js:258 #: utils/oauth.py:265 msgid "Not Allowed" msgstr "" @@ -21289,10 +21569,10 @@ msgctxt "DocField" msgid "Not Nullable" msgstr "" -#: __init__.py:1020 app.py:353 desk/calendar.py:26 geo/utils.py:97 +#: __init__.py:1018 app.py:353 desk/calendar.py:26 geo/utils.py:97 #: public/js/frappe/web_form/webform_script.js:15 #: website/doctype/web_form/web_form.py:602 -#: website/page_renderers/not_permitted_page.py:20 www/login.py:178 +#: website/page_renderers/not_permitted_page.py:20 www/login.py:181 #: www/qrcode.py:22 www/qrcode.py:25 www/qrcode.py:37 msgid "Not Permitted" msgstr "" @@ -21307,7 +21587,7 @@ msgstr "" msgid "Not Published" msgstr "" -#: public/js/frappe/form/toolbar.js:260 public/js/frappe/form/toolbar.js:740 +#: public/js/frappe/form/toolbar.js:260 public/js/frappe/form/toolbar.js:748 #: public/js/frappe/model/indicator.js:28 #: public/js/frappe/views/kanban/kanban_view.js:167 #: public/js/frappe/views/reports/report_view.js:173 @@ -21349,7 +21629,7 @@ msgstr "" msgid "Not a valid Comma Separated Value (CSV File)" msgstr "" -#: core/doctype/user/user.py:234 +#: core/doctype/user/user.py:235 msgid "Not a valid User Image." msgstr "" @@ -21496,7 +21776,7 @@ msgid "Nothing left to undo" msgstr "" #: public/js/frappe/list/base_list.js:362 -#: public/js/frappe/views/reports/query_report.js:104 +#: public/js/frappe/views/reports/query_report.js:105 #: templates/includes/list/list.html:7 #: website/doctype/blog_post/templates/blog_post_list.html:41 #: website/doctype/help_article/templates/help_article_list.html:21 @@ -21751,6 +22031,18 @@ msgctxt "System Settings" msgid "Number of days after which the document Web View link shared on email will be expired" msgstr "" +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Number of keys" +msgstr "" + +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Number of onsite backups" +msgstr "" + #. Option for the 'Method' (Select) field in DocType 'Email Account' #: email/doctype/email_account/email_account.json msgctxt "Email Account" @@ -21784,6 +22076,11 @@ msgctxt "Google Settings" msgid "OAuth Client ID" msgstr "" +#. Name of a DocType +#: integrations/doctype/oauth_client_role/oauth_client_role.json +msgid "OAuth Client Role" +msgstr "" + #: email/oauth.py:30 msgid "OAuth Error" msgstr "" @@ -21804,7 +22101,7 @@ msgstr "" msgid "OAuth Scope" msgstr "" -#: email/doctype/email_account/email_account.js:187 +#: email/doctype/email_account/email_account.js:182 msgid "OAuth has been enabled but not authorised. Please use \"Authorise API Access\" button to do the same." msgstr "" @@ -21843,6 +22140,12 @@ msgstr "" msgid "OTP setup using OTP App was not completed. Please contact Administrator." msgstr "" +#. Label of a Int field in DocType 'System Health Report Errors' +#: desk/doctype/system_health_report_errors/system_health_report_errors.json +msgctxt "System Health Report Errors" +msgid "Occurrences" +msgstr "" + #. Option for the 'SSL/TLS Mode' (Select) field in DocType 'LDAP Settings' #: integrations/doctype/ldap_settings/ldap_settings.json msgctxt "LDAP Settings" @@ -21893,6 +22196,12 @@ msgctxt "System Settings" msgid "Older backups will be automatically deleted" msgstr "" +#. Label of a Link field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Oldest Unscheduled Job" +msgstr "" + #. Option for the 'Status' (Select) field in DocType 'Personal Data Deletion #. Request' #: website/doctype/personal_data_deletion_request/personal_data_deletion_request.json @@ -22050,6 +22359,10 @@ msgctxt "S3 Backup Settings" msgid "Only change this if you want to use other S3 compatible object storage backends." msgstr "" +#: model/document.py:1072 +msgid "Only draft documents can be discarded" +msgstr "" + #. Label of a Link field in DocType 'Workspace Link' #: desk/doctype/workspace_link/workspace_link.json msgctxt "Workspace Link" @@ -22185,7 +22498,7 @@ msgstr "" msgid "Open a module or tool" msgstr "" -#: public/js/frappe/list/list_view.js:1210 +#: public/js/frappe/list/list_view.js:1214 msgctxt "Description of a list view shortcut" msgid "Open list item" msgstr "" @@ -22231,11 +22544,12 @@ msgctxt "Activity Log" msgid "Operation" msgstr "" -#: utils/data.py:2065 +#: utils/data.py:2068 msgid "Operator must be one of {0}" msgstr "" #: core/doctype/file/file.js:24 +#: core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js:8 msgid "Optimize" msgstr "" @@ -22343,7 +22657,7 @@ msgstr "" msgid "Options is required for field {0} of type {1}" msgstr "" -#: model/base_document.py:786 +#: model/base_document.py:794 msgid "Options not set for link field {0}" msgstr "" @@ -22411,12 +22725,24 @@ msgctxt "Event" msgid "Other" msgstr "" +#. Label of a Tab Break field in DocType 'Email Account' +#: email/doctype/email_account/email_account.json +msgctxt "Email Account" +msgid "Outgoing (SMTP)" +msgstr "" + #. Label of a Section Break field in DocType 'Email Account' #: email/doctype/email_account/email_account.json msgctxt "Email Account" msgid "Outgoing (SMTP) Settings" msgstr "" +#. Label of a Column Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Outgoing Emails (Last 7 days)" +msgstr "" + #. Label of a Data field in DocType 'Email Account' #: email/doctype/email_account/email_account.json msgctxt "Email Account" @@ -22517,10 +22843,14 @@ msgstr "" msgid "PDF generation failed" msgstr "" -#: utils/pdf.py:97 +#: utils/pdf.py:98 msgid "PDF generation failed because of broken image links" msgstr "" +#: printing/page/print/print.js:613 +msgid "PDF generation may not work as expected." +msgstr "" + #: printing/page/print/print.js:531 msgid "PDF printing via \"Raw Print\" is not supported." msgstr "" @@ -22556,7 +22886,7 @@ msgid "PUT" msgstr "" #. Name of a DocType -#: core/doctype/package/package.json www/attribution.html:33 +#: core/doctype/package/package.json www/attribution.html:34 msgid "Package" msgstr "" @@ -22611,6 +22941,11 @@ msgstr "" msgid "Packages" msgstr "" +#. Description of a Card Break in the Build Workspace +#: core/workspace/build/build.json +msgid "Packages are lightweight apps (collection of Module Defs) that can be created, imported, or released right from the UI" +msgstr "" + #. Name of a DocType #: core/doctype/page/page.json msgid "Page" @@ -22880,6 +23215,13 @@ msgctxt "Event" msgid "Participants" msgstr "" +#. Option for the 'SocketIO Ping Check' (Select) field in DocType 'System +#. Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Pass" +msgstr "" + #. Option for the 'Status' (Select) field in DocType 'Contact' #: contacts/doctype/contact/contact.json msgctxt "Contact" @@ -22929,11 +23271,11 @@ msgctxt "Web Form Field" msgid "Password" msgstr "" -#: core/doctype/user/user.py:1075 +#: core/doctype/user/user.py:1076 msgid "Password Email Sent" msgstr "" -#: core/doctype/user/user.py:454 +#: core/doctype/user/user.py:455 msgid "Password Reset" msgstr "" @@ -22943,7 +23285,7 @@ msgctxt "System Settings" msgid "Password Reset Link Generation Limit" msgstr "" -#: public/js/frappe/form/grid_row.js:811 +#: public/js/frappe/form/grid_row.js:812 msgid "Password cannot be filtered" msgstr "" @@ -22961,7 +23303,7 @@ msgstr "" msgid "Password is required or select Awaiting Password" msgstr "" -#: public/js/frappe/desk.js:191 +#: public/js/frappe/desk.js:197 msgid "Password missing in Email Account" msgstr "" @@ -22969,7 +23311,7 @@ msgstr "" msgid "Password not found for {0} {1} {2}" msgstr "" -#: core/doctype/user/user.py:1074 +#: core/doctype/user/user.py:1075 msgid "Password reset instructions have been sent to your email" msgstr "" @@ -22981,7 +23323,7 @@ msgstr "" msgid "Password size exceeded the maximum allowed size" msgstr "" -#: core/doctype/user/user.py:870 +#: core/doctype/user/user.py:871 msgid "Password size exceeded the maximum allowed size." msgstr "" @@ -23068,7 +23410,7 @@ msgctxt "LDAP Settings" msgid "Path to private Key File" msgstr "" -#: website/path_resolver.py:197 +#: website/path_resolver.py:202 msgid "Path {0} it not a valid path" msgstr "" @@ -23104,6 +23446,18 @@ msgctxt "Personal Data Deletion Request" msgid "Pending Approval" msgstr "" +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Pending Emails" +msgstr "" + +#. Label of a Int field in DocType 'System Health Report Queue' +#: desk/doctype/system_health_report_queue/system_health_report_queue.json +msgctxt "System Health Report Queue" +msgid "Pending Jobs" +msgstr "" + #. Option for the 'Status' (Select) field in DocType 'Personal Data Deletion #. Request' #: website/doctype/personal_data_deletion_request/personal_data_deletion_request.json @@ -23159,11 +23513,15 @@ msgctxt "Address" msgid "Permanent" msgstr "" -#: public/js/frappe/form/form.js:1011 +#: public/js/frappe/form/form.js:1022 msgid "Permanently Cancel {0}?" msgstr "" -#: public/js/frappe/form/form.js:841 +#: public/js/frappe/form/form.js:1068 +msgid "Permanently Discard {0}?" +msgstr "" + +#: public/js/frappe/form/form.js:852 msgid "Permanently Submit {0}?" msgstr "" @@ -23442,7 +23800,7 @@ msgstr "" msgid "Please Install the ldap3 library via pip to use ldap functionality." msgstr "" -#: public/js/frappe/views/reports/query_report.js:307 +#: public/js/frappe/views/reports/query_report.js:308 msgid "Please Set Chart" msgstr "" @@ -23458,7 +23816,7 @@ msgstr "" msgid "Please add a valid comment." msgstr "" -#: core/doctype/user/user.py:1057 +#: core/doctype/user/user.py:1058 msgid "Please ask your administrator to verify your sign-up" msgstr "" @@ -23486,11 +23844,11 @@ msgstr "" msgid "Please check the filter values set for Dashboard Chart: {}" msgstr "" -#: model/base_document.py:862 +#: model/base_document.py:870 msgid "Please check the value of \"Fetch From\" set for field {0}" msgstr "" -#: core/doctype/user/user.py:1055 +#: core/doctype/user/user.py:1056 msgid "Please check your email for verification" msgstr "" @@ -23522,6 +23880,10 @@ msgstr "" msgid "Please confirm your action to {0} this document." msgstr "" +#: printing/page/print/print.js:615 +msgid "Please contact your system manager to install correct version." +msgstr "" + #: desk/doctype/number_card/number_card.js:44 msgid "Please create Card first" msgstr "" @@ -23548,7 +23910,7 @@ msgstr "" #: desk/doctype/notification_log/notification_log.js:45 #: email/doctype/auto_email_report/auto_email_report.js:17 -#: printing/page/print/print.js:618 printing/page/print/print.js:647 +#: printing/page/print/print.js:635 printing/page/print/print.js:665 #: public/js/frappe/utils/utils.js:1417 msgid "Please enable pop-ups" msgstr "" @@ -23601,7 +23963,7 @@ msgstr "" msgid "Please enter the password" msgstr "" -#: public/js/frappe/desk.js:196 +#: public/js/frappe/desk.js:202 msgctxt "Email Account" msgid "Please enter the password for: {0}" msgstr "" @@ -23622,7 +23984,7 @@ msgstr "" msgid "Please find attached {0}: {1}" msgstr "" -#: core/doctype/navbar_settings/navbar_settings.py:44 +#: core/doctype/navbar_settings/navbar_settings.py:43 msgid "Please hide the standard navbar items instead of deleting them" msgstr "" @@ -23634,7 +23996,7 @@ msgstr "" msgid "Please make sure the Reference Communication Docs are not circularly linked." msgstr "" -#: model/document.py:824 +#: model/document.py:825 msgid "Please refresh to get the latest document." msgstr "" @@ -23658,7 +24020,7 @@ msgstr "" msgid "Please save the document before removing assignment" msgstr "" -#: public/js/frappe/views/reports/report_view.js:1612 +#: public/js/frappe/views/reports/report_view.js:1623 msgid "Please save the report first" msgstr "" @@ -23682,7 +24044,7 @@ msgstr "" msgid "Please select Minimum Password Score" msgstr "" -#: public/js/frappe/views/reports/query_report.js:1107 +#: public/js/frappe/views/reports/query_report.js:1108 msgid "Please select X and Y fields" msgstr "" @@ -23694,7 +24056,7 @@ msgstr "" msgid "Please select a file or url" msgstr "" -#: model/rename_doc.py:662 +#: model/rename_doc.py:667 msgid "Please select a valid csv file with data" msgstr "" @@ -23741,7 +24103,7 @@ msgstr "" msgid "Please set a printer mapping for this print format in the Printer Settings" msgstr "" -#: public/js/frappe/views/reports/query_report.js:1323 +#: public/js/frappe/views/reports/query_report.js:1324 msgid "Please set filters" msgstr "" @@ -23753,7 +24115,7 @@ msgstr "" msgid "Please set the document name" msgstr "" -#: desk/doctype/dashboard/dashboard.py:122 +#: desk/doctype/dashboard/dashboard.py:120 msgid "Please set the following documents in this Dashboard as standard first." msgstr "" @@ -23773,7 +24135,7 @@ msgstr "" msgid "Please setup default Email Account from Settings > Email Account" msgstr "" -#: core/doctype/user/user.py:405 +#: core/doctype/user/user.py:406 msgid "Please setup default outgoing Email Account from Settings > Email Account" msgstr "" @@ -23840,6 +24202,13 @@ msgstr "" msgid "Points Given" msgstr "" +#. Option for the 'SocketIO Transport Mode' (Select) field in DocType 'System +#. Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Polling" +msgstr "" + #. Label of a Check field in DocType 'Form Tour Step' #: desk/doctype/form_tour_step/form_tour_step.json msgctxt "Form Tour Step" @@ -24027,7 +24396,7 @@ msgstr "" msgid "Prepared report render failed" msgstr "" -#: public/js/frappe/views/reports/query_report.js:469 +#: public/js/frappe/views/reports/query_report.js:471 msgid "Preparing Report" msgstr "" @@ -24141,7 +24510,7 @@ msgctxt "Transaction Log" msgid "Previous Hash" msgstr "" -#: public/js/frappe/form/form.js:2131 +#: public/js/frappe/form/form.js:2197 msgid "Previous Submission" msgstr "" @@ -24184,15 +24553,15 @@ msgstr "" #: core/doctype/success_action/success_action.js:56 #: printing/page/print/print.js:65 public/js/frappe/form/success_action.js:81 #: public/js/frappe/form/templates/print_layout.html:46 -#: public/js/frappe/form/toolbar.js:321 public/js/frappe/form/toolbar.js:333 +#: public/js/frappe/form/toolbar.js:331 public/js/frappe/form/toolbar.js:343 #: public/js/frappe/list/bulk_operations.js:87 #: public/js/frappe/views/reports/query_report.js:1641 #: public/js/frappe/views/reports/report_view.js:1460 -#: public/js/frappe/views/treeview.js:473 www/printview.html:18 +#: public/js/frappe/views/treeview.js:474 www/printview.html:18 msgid "Print" msgstr "" -#: public/js/frappe/list/list_view.js:1914 +#: public/js/frappe/list/list_view.js:1918 msgctxt "Button in list view actions menu" msgid "Print" msgstr "" @@ -24215,7 +24584,7 @@ msgstr "" #. Name of a DocType #: printing/doctype/print_format/print_format.json -#: printing/page/print/print.js:94 printing/page/print/print.js:801 +#: printing/page/print/print.js:94 printing/page/print/print.js:819 #: public/js/frappe/list/bulk_operations.js:58 msgid "Print Format" msgstr "" @@ -24282,7 +24651,7 @@ msgctxt "Print Format" msgid "Print Format Builder Beta" msgstr "" -#: utils/pdf.py:56 +#: utils/pdf.py:57 msgid "Print Format Error" msgstr "" @@ -24454,11 +24823,11 @@ msgctxt "Print Settings" msgid "Print with letterhead" msgstr "" -#: printing/page/print/print.js:810 +#: printing/page/print/print.js:828 msgid "Printer" msgstr "" -#: printing/page/print/print.js:787 +#: printing/page/print/print.js:805 msgid "Printer Mapping" msgstr "" @@ -24468,7 +24837,7 @@ msgctxt "Network Printer Settings" msgid "Printer Name" msgstr "" -#: printing/page/print/print.js:779 +#: printing/page/print/print.js:797 msgid "Printer Settings" msgstr "" @@ -24541,6 +24910,12 @@ msgctxt "Kanban Board" msgid "Private" msgstr "" +#. Label of a Float field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Private Files (MB)" +msgstr "" + #. Description of the 'Auto Reply Message' (Text Editor) field in DocType #. 'Email Account' #: email/doctype/email_account/email_account.json @@ -24552,7 +24927,7 @@ msgstr "" msgid "Proceed" msgstr "" -#: public/js/frappe/views/reports/query_report.js:859 +#: public/js/frappe/views/reports/query_report.js:860 msgid "Proceed Anyway" msgstr "" @@ -24680,6 +25055,12 @@ msgctxt "Workspace" msgid "Public" msgstr "" +#. Label of a Float field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Public Files (MB)" +msgstr "" + #: website/doctype/blog_post/blog_post.js:36 #: website/doctype/web_form/web_form.js:86 msgid "Publish" @@ -24767,7 +25148,7 @@ msgctxt "Web Page" msgid "Publishing Dates" msgstr "" -#: email/doctype/email_account/email_account.js:164 +#: email/doctype/email_account/email_account.js:159 msgid "Pull Emails" msgstr "" @@ -24956,6 +25337,18 @@ msgctxt "RQ Job" msgid "Queue" msgstr "" +#. Label of a Data field in DocType 'System Health Report Queue' +#: desk/doctype/system_health_report_queue/system_health_report_queue.json +msgctxt "System Health Report Queue" +msgid "Queue" +msgstr "" + +#. Label of a Table field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Queue Status" +msgstr "" + #. Label of a Select field in DocType 'RQ Worker' #: core/doctype/rq_worker/rq_worker.json msgctxt "RQ Worker" @@ -25028,7 +25421,7 @@ msgstr "" msgid "Queued for backup. It may take a few minutes to an hour." msgstr "" -#: desk/page/backups/backups.py:96 +#: desk/page/backups/backups.py:93 msgid "Queued for backup. You will receive an email with the download link" msgstr "" @@ -25036,6 +25429,12 @@ msgstr "" msgid "Queued {0} emails" msgstr "" +#. Label of a Data field in DocType 'System Health Report Workers' +#: desk/doctype/system_health_report_workers/system_health_report_workers.json +msgctxt "System Health Report Workers" +msgid "Queues" +msgstr "" + #: email/doctype/newsletter/newsletter.js:90 msgid "Queuing emails..." msgstr "" @@ -25073,7 +25472,7 @@ msgctxt "Workspace" msgid "Quick Lists" msgstr "" -#: public/js/frappe/views/reports/report_utils.js:280 +#: public/js/frappe/views/reports/report_utils.js:304 msgid "Quoting must be between 0 and 3" msgstr "" @@ -25306,7 +25705,7 @@ msgctxt "DocField" msgid "Read Only Depends On (JS)" msgstr "" -#: public/js/frappe/ui/toolbar/navbar.html:17 +#: public/js/frappe/ui/toolbar/navbar.html:16 #: templates/includes/navbar/navbar_items.html:97 msgid "Read Only Mode" msgstr "" @@ -25343,6 +25742,12 @@ msgctxt "Package" msgid "Readme" msgstr "" +#. Label of a Section Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Realtime (SocketIO)" +msgstr "" + #: public/js/frappe/form/sidebar/review.js:85 #: social/doctype/energy_point_log/energy_point_log.js:20 msgid "Reason" @@ -25360,11 +25765,11 @@ msgctxt "Unhandled Email" msgid "Reason" msgstr "" -#: public/js/frappe/views/reports/query_report.js:820 +#: public/js/frappe/views/reports/query_report.js:821 msgid "Rebuild" msgstr "" -#: public/js/frappe/views/treeview.js:492 +#: public/js/frappe/views/treeview.js:493 msgid "Rebuild Tree" msgstr "" @@ -25523,15 +25928,15 @@ msgctxt "Website Settings" msgid "Redirects" msgstr "" -#: sessions.py:143 +#: sessions.py:144 msgid "Redis cache server not running. Please contact Administrator / Tech support" msgstr "" -#: public/js/frappe/form/toolbar.js:462 +#: public/js/frappe/form/toolbar.js:472 msgid "Redo" msgstr "" -#: public/js/frappe/form/form.js:163 public/js/frappe/form/toolbar.js:470 +#: public/js/frappe/form/form.js:163 public/js/frappe/form/toolbar.js:480 msgid "Redo last action" msgstr "" @@ -25946,12 +26351,12 @@ msgctxt "Web Page View" msgid "Referrer" msgstr "" -#: printing/page/print/print.js:73 public/js/frappe/desk.js:133 -#: public/js/frappe/form/form.js:1138 +#: printing/page/print/print.js:73 public/js/frappe/desk.js:134 +#: public/js/frappe/desk.js:533 public/js/frappe/form/form.js:1195 #: public/js/frappe/form/templates/print_layout.html:6 #: public/js/frappe/list/base_list.js:66 #: public/js/frappe/views/reports/query_report.js:1630 -#: public/js/frappe/views/treeview.js:479 +#: public/js/frappe/views/treeview.js:480 #: public/js/frappe/widgets/chart_widget.js:290 #: public/js/frappe/widgets/number_card_widget.js:324 msgid "Refresh" @@ -25997,7 +26402,7 @@ msgctxt "Token Cache" msgid "Refresh Token" msgstr "" -#: public/js/frappe/list/list_view.js:506 +#: public/js/frappe/list/list_view.js:507 msgctxt "Document count in list view" msgid "Refreshing" msgstr "" @@ -26007,7 +26412,7 @@ msgstr "" msgid "Refreshing..." msgstr "" -#: core/doctype/user/user.py:1019 +#: core/doctype/user/user.py:1020 msgid "Registered but disabled" msgstr "" @@ -26069,7 +26474,7 @@ msgstr "" #. Label of a standard navbar item #. Type: Action #: custom/doctype/customize_form/customize_form.js:120 hooks.py -#: public/js/frappe/form/toolbar.js:408 +#: public/js/frappe/form/toolbar.js:418 msgid "Reload" msgstr "" @@ -26081,7 +26486,7 @@ msgstr "" msgid "Reload List" msgstr "" -#: public/js/frappe/views/reports/query_report.js:99 +#: public/js/frappe/views/reports/query_report.js:100 msgid "Reload Report" msgstr "" @@ -26107,7 +26512,7 @@ msgctxt "Reminder" msgid "Remind At" msgstr "" -#: public/js/frappe/form/toolbar.js:436 +#: public/js/frappe/form/toolbar.js:446 msgid "Remind Me" msgstr "" @@ -26160,7 +26565,7 @@ msgstr "" #: custom/doctype/custom_field/custom_field.js:137 #: public/js/frappe/form/toolbar.js:234 public/js/frappe/form/toolbar.js:238 -#: public/js/frappe/form/toolbar.js:398 public/js/frappe/model/model.js:752 +#: public/js/frappe/form/toolbar.js:408 public/js/frappe/model/model.js:752 #: public/js/frappe/views/treeview.js:295 msgid "Rename" msgstr "" @@ -26182,7 +26587,7 @@ msgstr "" msgid "Reopen" msgstr "" -#: public/js/frappe/form/toolbar.js:479 +#: public/js/frappe/form/toolbar.js:489 msgid "Repeat" msgstr "" @@ -26335,6 +26740,12 @@ msgctxt "Role Permission for Page and Report" msgid "Report" msgstr "" +#. Label of a Tab Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Report" +msgstr "" + #. Option for the 'Link Type' (Select) field in DocType 'Workspace Link' #: desk/doctype/workspace_link/workspace_link.json msgctxt "Workspace Link" @@ -26495,7 +26906,7 @@ msgstr "" msgid "Report has no numeric fields, please change the Report Name" msgstr "" -#: public/js/frappe/views/reports/query_report.js:940 +#: public/js/frappe/views/reports/query_report.js:941 msgid "Report initiated, click to view status" msgstr "" @@ -26554,7 +26965,7 @@ msgstr "" msgid "Reports & Masters" msgstr "" -#: public/js/frappe/views/reports/query_report.js:856 +#: public/js/frappe/views/reports/query_report.js:857 msgid "Reports already in Queue" msgstr "" @@ -26751,7 +27162,7 @@ msgstr "" msgid "Reset the password for your account" msgstr "" -#: public/js/frappe/form/grid_row.js:410 +#: public/js/frappe/form/grid_row.js:411 msgid "Reset to default" msgstr "" @@ -27075,6 +27486,12 @@ msgctxt "Has Role" msgid "Role" msgstr "" +#. Label of a Link field in DocType 'OAuth Client Role' +#: integrations/doctype/oauth_client_role/oauth_client_role.json +msgctxt "OAuth Client Role" +msgid "Role" +msgstr "" + #. Label of a Link field in DocType 'Onboarding Permission' #: desk/doctype/onboarding_permission/onboarding_permission.json msgctxt "Onboarding Permission" @@ -27165,7 +27582,7 @@ msgstr "" msgid "Role Permissions Manager" msgstr "" -#: public/js/frappe/list/list_view.js:1691 +#: public/js/frappe/list/list_view.js:1695 msgctxt "Button in list view menu" msgid "Role Permissions Manager" msgstr "" @@ -27211,7 +27628,7 @@ msgctxt "DocPerm" msgid "Role and Level" msgstr "" -#: core/doctype/user/user.py:350 +#: core/doctype/user/user.py:351 msgid "Role has been set as per the user type {0}" msgstr "" @@ -27427,7 +27844,7 @@ msgctxt "Role" msgid "Route: Example \"/app\"" msgstr "" -#: model/base_document.py:731 model/base_document.py:772 model/document.py:616 +#: model/base_document.py:739 model/base_document.py:780 model/document.py:616 msgid "Row" msgstr "" @@ -27439,7 +27856,7 @@ msgstr "" msgid "Row # {0}: Non administrator user can not set the role {1} to the custom doctype" msgstr "" -#: model/base_document.py:893 +#: model/base_document.py:901 msgid "Row #{0}:" msgstr "" @@ -27465,7 +27882,7 @@ msgctxt "Property Setter" msgid "Row Name" msgstr "" -#: core/doctype/data_import/data_import.js:489 +#: core/doctype/data_import/data_import.js:483 msgid "Row Number" msgstr "" @@ -27650,13 +28067,6 @@ msgstr "" msgid "SMTP Server is required" msgstr "" -#. Description of the 'Enable Outgoing' (Check) field in DocType 'Email -#. Account' -#: email/doctype/email_account/email_account.json -msgctxt "Email Account" -msgid "SMTP Settings for outgoing emails" -msgstr "" - #. Option for the 'Type' (Select) field in DocType 'System Console' #: desk/doctype/system_console/system_console.json msgctxt "System Console" @@ -27784,7 +28194,7 @@ msgstr "" #: core/doctype/data_import/data_import.js:113 #: desk/page/user_profile/user_profile_controller.js:319 -#: printing/page/print/print.js:838 +#: printing/page/print/print.js:856 #: printing/page/print_format_builder/print_format_builder.js:160 #: public/js/frappe/form/footer/form_timeline.js:661 #: public/js/frappe/form/quick_entry.js:161 @@ -27797,7 +28207,7 @@ msgstr "" #: public/js/frappe/views/kanban/kanban_settings.js:189 #: public/js/frappe/views/kanban/kanban_view.js:340 #: public/js/frappe/views/reports/query_report.js:1803 -#: public/js/frappe/views/reports/report_view.js:1629 +#: public/js/frappe/views/reports/report_view.js:1640 #: public/js/frappe/views/workspace/workspace.js:498 #: public/js/frappe/widgets/base_widget.js:142 #: public/js/frappe/widgets/quick_list_widget.js:117 @@ -27821,7 +28231,7 @@ msgid "Save Anyway" msgstr "" #: public/js/frappe/views/reports/report_view.js:1311 -#: public/js/frappe/views/reports/report_view.js:1636 +#: public/js/frappe/views/reports/report_view.js:1647 msgid "Save As" msgstr "" @@ -27963,6 +28373,12 @@ msgctxt "Server Script" msgid "Scheduled Job Type" msgstr "" +#. Label of a Link field in DocType 'System Health Report Failing Jobs' +#: desk/doctype/system_health_report_failing_jobs/system_health_report_failing_jobs.json +msgctxt "System Health Report Failing Jobs" +msgid "Scheduled Job Type" +msgstr "" + #. Label of a Link in the Build Workspace #: core/workspace/build/build.json msgctxt "Scheduled Job Log" @@ -27981,7 +28397,7 @@ msgctxt "Newsletter" msgid "Scheduled To Send" msgstr "" -#: core/doctype/server_script/server_script.py:281 +#: core/doctype/server_script/server_script.py:283 msgid "Scheduled execution for script {0} has updated" msgstr "" @@ -27989,6 +28405,12 @@ msgstr "" msgid "Scheduled to send" msgstr "" +#. Label of a Section Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Scheduler" +msgstr "" + #. Option for the 'Script Type' (Select) field in DocType 'Server Script' #: core/doctype/server_script/server_script.json msgctxt "Server Script" @@ -27999,7 +28421,13 @@ msgstr "" msgid "Scheduler Inactive" msgstr "" -#: utils/scheduler.py:196 +#. Label of a Data field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Scheduler Status" +msgstr "" + +#: utils/scheduler.py:202 msgid "Scheduler can not be re-enabled when maintenance mode is active." msgstr "" @@ -28198,7 +28626,7 @@ msgstr "" msgid "Search in a document type" msgstr "" -#: public/js/frappe/ui/toolbar/navbar.html:30 +#: public/js/frappe/ui/toolbar/navbar.html:29 msgid "Search or type a command ({0})" msgstr "" @@ -28272,11 +28700,11 @@ msgstr "" msgid "See all Activity" msgstr "" -#: public/js/frappe/views/reports/query_report.js:789 +#: public/js/frappe/views/reports/query_report.js:790 msgid "See all past reports." msgstr "" -#: public/js/frappe/form/form.js:1172 +#: public/js/frappe/form/form.js:1229 #: website/doctype/contact_us_settings/contact_us_settings.js:4 msgid "See on Website" msgstr "" @@ -28479,7 +28907,7 @@ msgstr "" msgid "Select Document Types to set which User Permissions are used to limit access." msgstr "" -#: public/js/frappe/doctype/index.js:200 public/js/frappe/form/toolbar.js:762 +#: public/js/frappe/doctype/index.js:200 public/js/frappe/form/toolbar.js:770 msgid "Select Field" msgstr "" @@ -28488,7 +28916,7 @@ msgstr "" msgid "Select Field..." msgstr "" -#: public/js/frappe/form/grid_row.js:460 +#: public/js/frappe/form/grid_row.js:461 #: public/js/frappe/list/list_settings.js:233 #: public/js/frappe/views/kanban/kanban_settings.js:181 msgid "Select Fields" @@ -28640,13 +29068,13 @@ msgstr "" msgid "Select atleast 2 actions" msgstr "" -#: public/js/frappe/list/list_view.js:1224 +#: public/js/frappe/list/list_view.js:1228 msgctxt "Description of a list view shortcut" msgid "Select list item" msgstr "" -#: public/js/frappe/list/list_view.js:1176 -#: public/js/frappe/list/list_view.js:1192 +#: public/js/frappe/list/list_view.js:1180 +#: public/js/frappe/list/list_view.js:1196 msgctxt "Description of a list view shortcut" msgid "Select multiple list items" msgstr "" @@ -29228,7 +29656,7 @@ msgctxt "Website Settings" msgid "Set Banner from Image" msgstr "" -#: public/js/frappe/views/reports/query_report.js:199 +#: public/js/frappe/views/reports/query_report.js:200 msgid "Set Chart" msgstr "" @@ -29512,7 +29940,7 @@ msgid "Setup Approval Workflows" msgstr "" #: public/js/frappe/views/reports/query_report.js:1676 -#: public/js/frappe/views/reports/report_view.js:1607 +#: public/js/frappe/views/reports/report_view.js:1618 msgid "Setup Auto Email" msgstr "" @@ -29850,7 +30278,7 @@ msgid "Show Sidebar" msgstr "" #: public/js/frappe/list/list_sidebar.html:66 -#: public/js/frappe/list/list_view.js:1607 +#: public/js/frappe/list/list_view.js:1611 msgid "Show Tags" msgstr "" @@ -29880,7 +30308,7 @@ msgstr "" msgid "Show Tour" msgstr "" -#: core/doctype/data_import/data_import.js:454 +#: core/doctype/data_import/data_import.js:448 msgid "Show Traceback" msgstr "" @@ -30006,7 +30434,7 @@ msgctxt "Email Group" msgid "Sign Up and Confirmation" msgstr "" -#: core/doctype/user/user.py:1012 +#: core/doctype/user/user.py:1013 msgid "Sign Up is disabled" msgstr "" @@ -30105,10 +30533,16 @@ msgstr "" msgid "Site is running in read only mode for maintenance or site update, this action can not be performed right now. Please try again later." msgstr "" -#: public/js/frappe/views/file/file_view.js:318 +#: public/js/frappe/views/file/file_view.js:337 msgid "Size" msgstr "" +#. Label of a Float field in DocType 'System Health Report Tables' +#: desk/doctype/system_health_report_tables/system_health_report_tables.json +msgctxt "System Health Report Tables" +msgid "Size (MB)" +msgstr "" + #: public/js/frappe/widgets/onboarding_widget.js:82 #: public/js/onboarding_tours/onboarding_tours.js:18 msgid "Skip" @@ -30294,6 +30728,18 @@ msgctxt "User" msgid "Social Logins" msgstr "" +#. Label of a Select field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "SocketIO Ping Check" +msgstr "" + +#. Label of a Select field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "SocketIO Transport Mode" +msgstr "" + #. Option for the 'Delivery Status' (Select) field in DocType 'Communication' #: core/doctype/communication/communication.json msgctxt "Communication" @@ -30732,7 +31178,7 @@ msgstr "" msgid "Stats based on last week's performance (from {0} to {1})" msgstr "" -#: core/doctype/data_import/data_import.js:489 +#: core/doctype/data_import/data_import.js:483 #: public/js/frappe/list/list_settings.js:356 #: public/js/frappe/views/reports/report_view.js:908 msgid "Status" @@ -30911,6 +31357,18 @@ msgctxt "Scheduled Job Type" msgid "Stopped" msgstr "" +#. Label of a Float field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Storage Usage (MB)" +msgstr "" + +#. Label of a Table field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Storage Usage By Table" +msgstr "" + #. Label of a Check field in DocType 'System Settings' #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" @@ -31089,7 +31547,7 @@ msgstr "" msgid "Submit" msgstr "" -#: public/js/frappe/list/list_view.js:1981 +#: public/js/frappe/list/list_view.js:1985 msgctxt "Button in list view actions menu" msgid "Submit" msgstr "" @@ -31140,7 +31598,7 @@ msgctxt "Primary action of prompt dialog" msgid "Submit" msgstr "" -#: public/js/frappe/desk.js:206 +#: public/js/frappe/desk.js:212 msgctxt "Submit password for Email Account" msgid "Submit" msgstr "" @@ -31182,11 +31640,11 @@ msgstr "" msgid "Submit this document to complete this step." msgstr "" -#: public/js/frappe/form/form.js:1158 +#: public/js/frappe/form/form.js:1215 msgid "Submit this document to confirm" msgstr "" -#: public/js/frappe/list/list_view.js:1986 +#: public/js/frappe/list/list_view.js:1990 msgctxt "Title of confirmation dialog" msgid "Submit {0} documents?" msgstr "" @@ -31244,7 +31702,7 @@ msgctxt "Module Onboarding" msgid "Subtitle" msgstr "" -#: core/doctype/data_import/data_import.js:465 +#: core/doctype/data_import/data_import.js:459 #: desk/doctype/bulk_update/bulk_update.js:31 #: desk/doctype/desktop_icon/desktop_icon.py:446 #: public/js/frappe/form/grid.js:1139 @@ -31329,7 +31787,7 @@ msgstr "" msgid "Successful Transactions" msgstr "" -#: model/rename_doc.py:676 +#: model/rename_doc.py:681 msgid "Successful: {0} to {1}" msgstr "" @@ -31342,7 +31800,7 @@ msgstr "" msgid "Successfully Updated" msgstr "" -#: core/doctype/data_import/data_import.js:429 +#: core/doctype/data_import/data_import.js:423 msgid "Successfully imported {0}" msgstr "" @@ -31358,7 +31816,7 @@ msgstr "" msgid "Successfully updated translations" msgstr "" -#: core/doctype/data_import/data_import.js:437 +#: core/doctype/data_import/data_import.js:431 msgid "Successfully updated {0}" msgstr "" @@ -31366,7 +31824,7 @@ msgstr "" msgid "Successfully updated {0} out of {1} records." msgstr "" -#: core/doctype/user/user.py:727 +#: core/doctype/user/user.py:728 msgid "Suggested Username: {0}" msgstr "" @@ -31434,7 +31892,7 @@ msgstr "" msgid "Switch Camera" msgstr "" -#: public/js/frappe/desk.js:50 public/js/frappe/ui/theme_switcher.js:11 +#: public/js/frappe/desk.js:51 public/js/frappe/ui/theme_switcher.js:11 msgid "Switch Theme" msgstr "" @@ -31509,7 +31967,7 @@ msgstr "" msgid "Syncing {0} of {1}" msgstr "" -#: utils/data.py:2430 +#: utils/data.py:2433 msgid "Syntax Error" msgstr "" @@ -31528,6 +31986,42 @@ msgstr "" msgid "System Generated Fields can not be renamed" msgstr "" +#. Label of a standard help item +#. Type: Action +#: hooks.py +msgid "System Health" +msgstr "" + +#. Name of a DocType +#: desk/doctype/system_health_report/system_health_report.json +msgid "System Health Report" +msgstr "" + +#. Name of a DocType +#: desk/doctype/system_health_report_errors/system_health_report_errors.json +msgid "System Health Report Errors" +msgstr "" + +#. Name of a DocType +#: desk/doctype/system_health_report_failing_jobs/system_health_report_failing_jobs.json +msgid "System Health Report Failing Jobs" +msgstr "" + +#. Name of a DocType +#: desk/doctype/system_health_report_queue/system_health_report_queue.json +msgid "System Health Report Queue" +msgstr "" + +#. Name of a DocType +#: desk/doctype/system_health_report_tables/system_health_report_tables.json +msgid "System Health Report Tables" +msgstr "" + +#. Name of a DocType +#: desk/doctype/system_health_report_workers/system_health_report_workers.json +msgid "System Health Report Workers" +msgstr "" + #. Label of a Card Break in the Build Workspace #: core/workspace/build/build.json msgid "System Logs" @@ -31609,8 +32103,10 @@ msgstr "" #: desk/doctype/module_onboarding/module_onboarding.json #: desk/doctype/note/note.json desk/doctype/number_card/number_card.json #: desk/doctype/route_history/route_history.json -#: desk/doctype/system_console/system_console.json desk/doctype/tag/tag.json -#: desk/doctype/tag_link/tag_link.json desk/doctype/todo/todo.json +#: desk/doctype/system_console/system_console.json +#: desk/doctype/system_health_report/system_health_report.json +#: desk/doctype/tag/tag.json desk/doctype/tag_link/tag_link.json +#: desk/doctype/todo/todo.json #: email/doctype/auto_email_report/auto_email_report.json #: email/doctype/document_follow/document_follow.json #: email/doctype/email_account/email_account.json @@ -31752,6 +32248,12 @@ msgctxt "DocField" msgid "Table" msgstr "" +#. Label of a Data field in DocType 'System Health Report Tables' +#: desk/doctype/system_health_report_tables/system_health_report_tables.json +msgctxt "System Health Report Tables" +msgid "Table" +msgstr "" + #. Option for the 'Fieldtype' (Select) field in DocType 'Web Form Field' #: website/doctype/web_form_field/web_form_field.json msgctxt "Web Form Field" @@ -31810,7 +32312,7 @@ msgstr "" msgid "Table updated" msgstr "" -#: model/document.py:1378 +#: model/document.py:1398 msgid "Table {0} cannot be empty" msgstr "" @@ -31948,10 +32450,16 @@ msgstr "" msgid "Templates" msgstr "" -#: core/doctype/user/user.py:1023 +#: core/doctype/user/user.py:1024 msgid "Temporarily Disabled" msgstr "" +#. Label of a Data field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Test Job ID" +msgstr "" + #: email/doctype/newsletter/newsletter.py:94 msgid "Test email sent to {0}" msgstr "" @@ -32099,7 +32607,7 @@ msgstr "" msgid "The User record for this request has been auto-deleted due to inactivity by system admins." msgstr "" -#: public/js/frappe/desk.js:127 +#: public/js/frappe/desk.js:128 msgid "The application has been updated to a new version, please refresh this page" msgstr "" @@ -32193,7 +32701,7 @@ msgstr "" msgid "The link will expire in {0} minutes" msgstr "" -#: www/login.py:179 +#: www/login.py:182 msgid "The link you trying to login is invalid or expired." msgstr "" @@ -32244,11 +32752,11 @@ msgid "" "" msgstr "" -#: core/doctype/user/user.py:983 +#: core/doctype/user/user.py:984 msgid "The reset password link has been expired" msgstr "" -#: core/doctype/user/user.py:985 +#: core/doctype/user/user.py:986 msgid "The reset password link has either been used before or is invalid" msgstr "" @@ -32272,7 +32780,7 @@ msgstr "" msgid "The system provides many pre-defined roles. You can add new roles to set finer permissions." msgstr "" -#: public/js/frappe/form/grid_row.js:636 +#: public/js/frappe/form/grid_row.js:637 msgid "The total column width cannot be more than 10." msgstr "" @@ -32342,7 +32850,7 @@ msgstr "" msgid "There are no {0} for this {1}, why don't you start one!" msgstr "" -#: public/js/frappe/views/reports/query_report.js:892 +#: public/js/frappe/views/reports/query_report.js:893 msgid "There are {0} with the same filters already in the queue:" msgstr "" @@ -32371,7 +32879,7 @@ msgstr "" msgid "There is some problem with the file url: {0}" msgstr "" -#: public/js/frappe/views/reports/query_report.js:889 +#: public/js/frappe/views/reports/query_report.js:890 msgid "There is {0} with the same filters already in the queue:" msgstr "" @@ -32379,7 +32887,7 @@ msgstr "" msgid "There must be atleast one permission rule." msgstr "" -#: core/doctype/user/user.py:535 +#: core/doctype/user/user.py:536 msgid "There should remain at least one System Manager" msgstr "" @@ -32461,7 +32969,7 @@ msgstr "" msgid "This action is irreversible. Do you wish to continue?" msgstr "" -#: __init__.py:1016 +#: __init__.py:1014 msgid "This action is only allowed for {}" msgstr "" @@ -32509,11 +33017,15 @@ msgstr "" msgid "This document has been reverted" msgstr "" -#: public/js/frappe/form/form.js:1039 +#: public/js/frappe/form/form.js:1303 +msgid "This document has unsaved changes which might not appear in final PDF.
Consider saving the document before printing." +msgstr "" + +#: public/js/frappe/form/form.js:1096 msgid "This document is already amended, you cannot ammend it again" msgstr "" -#: model/document.py:1546 +#: model/document.py:1566 msgid "This document is currently locked and queued for execution. Please try again after some time." msgstr "" @@ -32546,7 +33058,7 @@ msgstr "" msgid "This file is public. It can be accessed without authentication." msgstr "" -#: public/js/frappe/form/form.js:1136 +#: public/js/frappe/form/form.js:1193 msgid "This form has been modified after you have loaded it" msgstr "" @@ -32633,7 +33145,7 @@ msgstr "" msgid "This newsletter was scheduled to send on a later date. Are you sure you want to send it now?" msgstr "" -#: public/js/frappe/views/reports/query_report.js:964 +#: public/js/frappe/views/reports/query_report.js:965 msgid "This report contains {0} rows and is too big to display in browser, you can {1} this report instead." msgstr "" @@ -32641,7 +33153,7 @@ msgstr "" msgid "This report was generated on {0}" msgstr "" -#: public/js/frappe/views/reports/query_report.js:787 +#: public/js/frappe/views/reports/query_report.js:788 msgid "This report was generated {0}." msgstr "" @@ -32699,7 +33211,7 @@ msgstr "" msgid "This will terminate the job immediately and might be dangerous, are you sure? " msgstr "" -#: core/doctype/user/user.py:1243 +#: core/doctype/user/user.py:1244 msgid "Throttled" msgstr "" @@ -33069,6 +33581,12 @@ msgctxt "Portal Menu Item" msgid "Title" msgstr "" +#. Label of a Data field in DocType 'System Health Report Errors' +#: desk/doctype/system_health_report_errors/system_health_report_errors.json +msgctxt "System Health Report Errors" +msgid "Title" +msgstr "" + #. Label of a Data field in DocType 'Web Form' #: website/doctype/web_form/web_form.json msgctxt "Web Form" @@ -33235,7 +33753,7 @@ msgstr "" msgid "To export this step as JSON, link it in a Onboarding document and save the document." msgstr "" -#: public/js/frappe/views/reports/query_report.js:788 +#: public/js/frappe/views/reports/query_report.js:789 msgid "To get the updated report, click on {0}." msgstr "" @@ -33324,7 +33842,7 @@ msgstr "" msgid "Toggle Sidebar" msgstr "" -#: public/js/frappe/list/list_view.js:1722 +#: public/js/frappe/list/list_view.js:1726 msgctxt "Button in list view menu" msgid "Toggle Sidebar" msgstr "" @@ -33386,7 +33904,7 @@ msgstr "" msgid "Too many changes to database in single action." msgstr "" -#: core/doctype/user/user.py:1024 +#: core/doctype/user/user.py:1025 msgid "Too many users signed up recently, so the registration is disabled. Please try back in an hour" msgstr "" @@ -33425,6 +33943,12 @@ msgctxt "Print Format" msgid "Top Center" msgstr "" +#. Label of a Table field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Top Errors" +msgstr "" + #. Option for the 'Page Number' (Select) field in DocType 'Print Format' #: printing/doctype/print_format/print_format.json msgctxt "Print Format" @@ -33466,10 +33990,28 @@ msgstr "" msgid "Total" msgstr "" +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Total Background Workers" +msgstr "" + +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Total Errors (last 1 day)" +msgstr "" + #: public/js/frappe/ui/capture.js:259 msgid "Total Images" msgstr "" +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Total Outgoing Emails" +msgstr "" + #. Label of a Int field in DocType 'Newsletter' #: email/doctype/newsletter/newsletter.json msgctxt "Newsletter" @@ -33488,6 +34030,12 @@ msgctxt "Newsletter Email Group" msgid "Total Subscribers" msgstr "" +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Total Users" +msgstr "" + #. Label of a Int field in DocType 'Newsletter' #: email/doctype/newsletter/newsletter.json msgctxt "Newsletter" @@ -33794,7 +34342,7 @@ msgctxt "System Settings" msgid "Two Factor Authentication method" msgstr "" -#: public/js/frappe/views/file/file_view.js:318 www/attribution.html:34 +#: public/js/frappe/views/file/file_view.js:337 www/attribution.html:35 msgid "Type" msgstr "" @@ -34043,7 +34591,7 @@ msgstr "" msgid "Unable to open attached file. Did you export it as CSV?" msgstr "" -#: core/doctype/file/utils.py:98 core/doctype/file/utils.py:130 +#: core/doctype/file/utils.py:97 core/doctype/file/utils.py:129 msgid "Unable to read file format for {0}" msgstr "" @@ -34073,11 +34621,11 @@ msgstr "" msgid "Unchanged" msgstr "" -#: public/js/frappe/form/toolbar.js:450 +#: public/js/frappe/form/toolbar.js:460 msgid "Undo" msgstr "" -#: public/js/frappe/form/toolbar.js:458 +#: public/js/frappe/form/toolbar.js:468 msgid "Undo last action" msgstr "" @@ -34091,6 +34639,12 @@ msgstr "" msgid "Unhandled Email" msgstr "" +#. Label of a Int field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Unhandled Emails" +msgstr "" + #: public/js/frappe/views/workspace/workspace.js:567 msgid "Unhide Workspace" msgstr "" @@ -34241,7 +34795,7 @@ msgstr "" #: printing/page/print_format_builder/print_format_builder.js:501 #: printing/page/print_format_builder/print_format_builder.js:670 #: printing/page/print_format_builder/print_format_builder.js:757 -#: public/js/frappe/form/grid_row.js:403 +#: public/js/frappe/form/grid_row.js:404 #: public/js/frappe/views/workspace/workspace.js:658 msgid "Update" msgstr "" @@ -34317,12 +34871,16 @@ msgctxt "Workflow Document State" msgid "Update Value" msgstr "" +#: utils/change_log.py:364 +msgid "Update from Frappe Cloud" +msgstr "" + #: public/js/frappe/list/bulk_operations.js:345 msgid "Update {0} records" msgstr "" #: desk/doctype/desktop_icon/desktop_icon.py:446 -#: public/js/frappe/web_form/web_form.js:423 +#: public/js/frappe/web_form/web_form.js:427 msgid "Updated" msgstr "" @@ -34342,7 +34900,7 @@ msgstr "" msgid "Updated Successfully" msgstr "" -#: public/js/frappe/desk.js:420 +#: public/js/frappe/desk.js:426 msgid "Updated To A New Version 🎉" msgstr "" @@ -34754,6 +35312,10 @@ msgctxt "DocType" msgid "User Cannot Search" msgstr "" +#: public/js/frappe/desk.js:531 +msgid "User Changed" +msgstr "" + #. Label of a Table field in DocType 'User' #: core/doctype/user/user.json msgctxt "User" @@ -34847,7 +35409,7 @@ msgctxt "User" msgid "User Image" msgstr "" -#: public/js/frappe/ui/toolbar/navbar.html:116 +#: public/js/frappe/ui/toolbar/navbar.html:115 msgid "User Menu" msgstr "" @@ -34870,11 +35432,11 @@ msgstr "" #: core/page/permission_manager/permission_manager_help.html:30 #: public/js/frappe/views/reports/query_report.js:1790 -#: public/js/frappe/views/reports/report_view.js:1655 +#: public/js/frappe/views/reports/report_view.js:1666 msgid "User Permissions" msgstr "" -#: public/js/frappe/list/list_view.js:1680 +#: public/js/frappe/list/list_view.js:1684 msgctxt "Button in list view menu" msgid "User Permissions" msgstr "" @@ -35001,7 +35563,7 @@ msgstr "" msgid "User permission already exists" msgstr "" -#: www/login.py:151 +#: www/login.py:155 msgid "User with email address {0} does not exist" msgstr "" @@ -35009,15 +35571,15 @@ msgstr "" msgid "User with email: {0} does not exist in the system. Please ask 'System Administrator' to create the user for you." msgstr "" -#: core/doctype/user/user.py:540 +#: core/doctype/user/user.py:541 msgid "User {0} cannot be deleted" msgstr "" -#: core/doctype/user/user.py:279 +#: core/doctype/user/user.py:280 msgid "User {0} cannot be disabled" msgstr "" -#: core/doctype/user/user.py:609 +#: core/doctype/user/user.py:610 msgid "User {0} cannot be renamed" msgstr "" @@ -35034,7 +35596,7 @@ msgstr "" msgid "User {0} has requested for data deletion" msgstr "" -#: core/doctype/user/user.py:1372 +#: core/doctype/user/user.py:1373 msgid "User {0} impersonated as {1}" msgstr "" @@ -35042,6 +35604,10 @@ msgstr "" msgid "User {0} is disabled" msgstr "" +#: sessions.py:222 +msgid "User {0} is disabled. Please contact your System Manager." +msgstr "" + #: desk/form/assign_to.py:101 msgid "User {0} is not permitted to access this document." msgstr "" @@ -35052,7 +35618,7 @@ msgctxt "Connected App" msgid "Userinfo URI" msgstr "" -#: www/login.py:99 +#: www/login.py:103 msgid "Username" msgstr "" @@ -35068,7 +35634,7 @@ msgctxt "User Social Login" msgid "Username" msgstr "" -#: core/doctype/user/user.py:694 +#: core/doctype/user/user.py:695 msgid "Username {0} already exists" msgstr "" @@ -35084,6 +35650,12 @@ msgctxt "Assignment Rule" msgid "Users" msgstr "" +#. Label of a Section Break field in DocType 'System Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Users" +msgstr "" + #. Description of the 'Allot Points To Assigned Users' (Check) field in DocType #. 'Energy Point Rule' #: social/doctype/energy_point_rule/energy_point_rule.json @@ -35099,10 +35671,16 @@ msgstr "" msgid "Uses system's theme to switch between light and dark mode" msgstr "" -#: public/js/frappe/desk.js:112 +#: public/js/frappe/desk.js:113 msgid "Using this console may allow attackers to impersonate you and steal your information. Do not enter or paste code that you do not understand." msgstr "" +#. Label of a Percent field in DocType 'System Health Report Workers' +#: desk/doctype/system_health_report_workers/system_health_report_workers.json +msgctxt "System Health Report Workers" +msgid "Utilization" +msgstr "" + #. Label of a Percent field in DocType 'RQ Worker' #: core/doctype/rq_worker/rq_worker.json msgctxt "RQ Worker" @@ -35142,7 +35720,7 @@ msgctxt "Email Domain" msgid "Validate SSL Certificate" msgstr "" -#: public/js/frappe/web_form/web_form.js:356 +#: public/js/frappe/web_form/web_form.js:360 msgid "Validation Error" msgstr "" @@ -35238,7 +35816,7 @@ msgctxt "Notification" msgid "Value To Be Set" msgstr "" -#: model/base_document.py:955 model/document.py:672 +#: model/base_document.py:963 model/document.py:672 msgid "Value cannot be changed for {0}" msgstr "" @@ -35258,7 +35836,7 @@ msgstr "" msgid "Value for field {0} is too long in {1}. Length should be lesser than {2} characters" msgstr "" -#: model/base_document.py:379 +#: model/base_document.py:387 msgid "Value for {0} cannot be a list" msgstr "" @@ -35269,7 +35847,7 @@ msgctxt "Assignment Rule" msgid "Value from this field will be set as the due date in the ToDo" msgstr "" -#: model/base_document.py:733 +#: model/base_document.py:741 msgid "Value missing for" msgstr "" @@ -35283,7 +35861,7 @@ msgctxt "Onboarding Step" msgid "Value to Validate" msgstr "" -#: model/base_document.py:1025 +#: model/base_document.py:1033 msgid "Value too big" msgstr "" @@ -35348,7 +35926,7 @@ msgstr "" msgid "Version" msgstr "" -#: public/js/frappe/desk.js:131 +#: public/js/frappe/desk.js:132 msgid "Version Updated" msgstr "" @@ -35369,7 +35947,7 @@ msgstr "" msgid "View All" msgstr "" -#: public/js/frappe/form/toolbar.js:507 +#: public/js/frappe/form/toolbar.js:517 msgid "View Audit Trail" msgstr "" @@ -35385,7 +35963,7 @@ msgstr "" msgid "View Full Log" msgstr "" -#: public/js/frappe/views/treeview.js:467 +#: public/js/frappe/views/treeview.js:468 #: public/js/frappe/widgets/quick_list_widget.js:245 msgid "View List" msgstr "" @@ -35456,7 +36034,7 @@ msgstr "" msgid "View this in your browser" msgstr "" -#: public/js/frappe/web_form/web_form.js:450 +#: public/js/frappe/web_form/web_form.js:454 msgctxt "Button in web form" msgid "View your response" msgstr "" @@ -35961,6 +36539,13 @@ msgctxt "Website Settings" msgid "Website Theme image link" msgstr "" +#. Option for the 'SocketIO Transport Mode' (Select) field in DocType 'System +#. Health Report' +#: desk/doctype/system_health_report/system_health_report.json +msgctxt "System Health Report" +msgid "Websocket" +msgstr "" + #. Option for the 'Day' (Select) field in DocType 'Assignment Rule Day' #: automation/doctype/assignment_rule_day/assignment_rule_day.json msgctxt "Assignment Rule Day" @@ -36122,11 +36707,11 @@ msgstr "" msgid "Welcome Workspace" msgstr "" -#: core/doctype/user/user.py:397 +#: core/doctype/user/user.py:398 msgid "Welcome email sent" msgstr "" -#: core/doctype/user/user.py:472 +#: core/doctype/user/user.py:473 msgid "Welcome to {0}" msgstr "" @@ -36497,6 +37082,10 @@ msgctxt "Form Tour" msgid "Workspaces" msgstr "" +#: desk/page/setup_wizard/setup_wizard.py:35 +msgid "Wrapping up" +msgstr "" + #. Label of a Check field in DocType 'Custom DocPerm' #: core/doctype/custom_docperm/custom_docperm.json msgctxt "Custom DocPerm" @@ -36521,7 +37110,7 @@ msgctxt "User Document Type" msgid "Write" msgstr "" -#: model/base_document.py:865 +#: model/base_document.py:873 msgid "Wrong Fetch From value" msgstr "" @@ -36551,7 +37140,7 @@ msgstr "" msgid "Y Axis Fields" msgstr "" -#: public/js/frappe/views/reports/query_report.js:1147 +#: public/js/frappe/views/reports/query_report.js:1148 msgid "Y Field" msgstr "" @@ -36646,9 +37235,9 @@ msgstr "" #: integrations/doctype/webhook/webhook.py:130 #: integrations/doctype/webhook/webhook.py:140 #: public/js/form_builder/utils.js:336 -#: public/js/frappe/form/controls/link.js:472 +#: public/js/frappe/form/controls/link.js:475 #: public/js/frappe/list/list_sidebar_group_by.js:223 -#: public/js/frappe/views/reports/query_report.js:1531 +#: public/js/frappe/views/reports/query_report.js:1527 #: website/doctype/help_article/templates/help_article.html:25 msgid "Yes" msgstr "" @@ -36697,11 +37286,11 @@ msgstr "" msgid "You Liked" msgstr "" -#: public/js/frappe/dom.js:425 +#: public/js/frappe/dom.js:438 msgid "You are connected to internet." msgstr "" -#: public/js/frappe/ui/toolbar/navbar.html:21 +#: public/js/frappe/ui/toolbar/navbar.html:20 msgid "You are impersonating as another user." msgstr "" @@ -36733,7 +37322,7 @@ msgstr "" msgid "You are not allowed to export {} doctype" msgstr "" -#: public/js/frappe/views/treeview.js:431 +#: public/js/frappe/views/treeview.js:432 msgid "You are not allowed to print this report" msgstr "" @@ -36757,7 +37346,7 @@ msgstr "" msgid "You are not permitted to access this page." msgstr "" -#: __init__.py:935 +#: __init__.py:933 msgid "You are not permitted to access this resource." msgstr "" @@ -36769,7 +37358,7 @@ msgstr "" msgid "You are only allowed to update order, do not remove or add apps." msgstr "" -#: email/doctype/email_account/email_account.js:221 +#: email/doctype/email_account/email_account.js:216 msgid "You are selecting Sync Option as ALL, It will resync all read as well as unread message from server. This may also cause the duplication of Communication (emails)." msgstr "" @@ -36810,7 +37399,7 @@ msgstr "" msgid "You can continue with the onboarding after exploring this page" msgstr "" -#: core/doctype/user/user.py:600 +#: core/doctype/user/user.py:601 msgid "You can disable the user instead of deleting it." msgstr "" @@ -36834,7 +37423,7 @@ msgstr "" msgid "You can only set the 3 custom doctypes in the Document Types table." msgstr "" -#: handler.py:225 +#: handler.py:186 msgid "You can only upload JPG, PNG, PDF, TXT or Microsoft documents." msgstr "" @@ -36939,7 +37528,7 @@ msgstr "" msgid "You do not have permission to view this document" msgstr "" -#: public/js/frappe/form/form.js:943 +#: public/js/frappe/form/form.js:954 msgid "You do not have permissions to cancel all linked documents." msgstr "" @@ -36947,7 +37536,7 @@ msgstr "" msgid "You don't have access to Report: {0}" msgstr "" -#: website/doctype/web_form/web_form.py:698 +#: website/doctype/web_form/web_form.py:663 msgid "You don't have permission to access the {0} DocType." msgstr "" @@ -36999,7 +37588,7 @@ msgstr "" msgid "You have unsaved changes in this form. Please save before you continue." msgstr "" -#: public/js/frappe/ui/toolbar/navbar.html:51 +#: public/js/frappe/ui/toolbar/navbar.html:50 msgid "You have unseen notifications" msgstr "" @@ -37011,7 +37600,7 @@ msgstr "" msgid "You haven't added any Dashboard Charts or Number Cards yet." msgstr "" -#: public/js/frappe/list/list_view.js:472 +#: public/js/frappe/list/list_view.js:473 msgid "You haven't created a {0} yet" msgstr "" @@ -37028,7 +37617,7 @@ msgstr "" msgid "You must add atleast one link." msgstr "" -#: website/doctype/web_form/web_form.py:668 +#: website/doctype/web_form/web_form.py:659 msgid "You must be logged in to use this form." msgstr "" @@ -37119,6 +37708,10 @@ msgstr "" msgid "You viewed this" msgstr "" +#: public/js/frappe/desk.js:528 +msgid "You've logged in as another user from another tab. Refresh this page to continue using system." +msgstr "" + #: desk/page/setup_wizard/setup_wizard.js:385 msgid "Your Country" msgstr "" @@ -37168,7 +37761,7 @@ msgstr "" msgid "Your email address" msgstr "" -#: public/js/frappe/web_form/web_form.js:424 +#: public/js/frappe/web_form/web_form.js:428 msgid "Your form has been successfully updated" msgstr "" @@ -37199,7 +37792,7 @@ msgstr "" msgid "Your session has expired, please login again to continue." msgstr "" -#: public/js/frappe/ui/toolbar/navbar.html:16 +#: public/js/frappe/ui/toolbar/navbar.html:15 msgid "Your site is undergoing maintenance or being updated." msgstr "" @@ -37247,42 +37840,12 @@ msgstr "" msgid "added rows for {0}" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "adjust" -msgstr "" - #. Option for the 'Doc Event' (Select) field in DocType 'Webhook' #: integrations/doctype/webhook/webhook.json msgctxt "Webhook" msgid "after_insert" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "align-center" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "align-justify" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "align-left" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "align-right" -msgstr "" - #. Option for the 'Permission Type' (Select) field in DocType 'Permission #. Inspector' #: core/doctype/permission_inspector/permission_inspector.json @@ -37294,105 +37857,21 @@ msgstr "" msgid "and" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "arrow-down" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "arrow-left" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "arrow-right" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "arrow-up" -msgstr "" - #: public/js/frappe/ui/sort_selector.html:5 #: public/js/frappe/ui/sort_selector.js:48 msgid "ascending" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "asterisk" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "backward" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "ban-circle" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "barcode" -msgstr "" - -#: model/document.py:1349 +#: model/document.py:1369 msgid "beginning with" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "bell" -msgstr "" - #. Option for the 'Indicator Color' (Select) field in DocType 'Workspace' #: desk/doctype/workspace/workspace.json msgctxt "Workspace" msgid "blue" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "bold" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "book" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "bookmark" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "briefcase" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "bullhorn" -msgstr "" - #: public/js/frappe/form/workflow.js:35 msgid "by Role" msgstr "" @@ -37407,18 +37886,6 @@ msgstr "" msgid "calendar" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "calendar" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "camera" -msgstr "" - #. Option for the 'Permission Type' (Select) field in DocType 'Permission #. Inspector' #: core/doctype/permission_inspector/permission_inspector.json @@ -37432,82 +37899,10 @@ msgctxt "RQ Job" msgid "canceled" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "certificate" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "check" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "chevron-down" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "chevron-left" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "chevron-right" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "chevron-up" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "circle-arrow-down" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "circle-arrow-left" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "circle-arrow-right" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "circle-arrow-up" -msgstr "" - #: templates/includes/list/filters.html:19 msgid "clear" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "cog" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "comment" -msgstr "" - #: public/js/frappe/form/templates/timeline_message_box.html:33 msgid "commented" msgstr "" @@ -37592,18 +37987,6 @@ msgstr "" msgid "document type..., e.g. customer" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "download" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "download-alt" -msgstr "" - #. Description of the 'Email Account Name' (Data) field in DocType 'Email #. Account' #: email/doctype/email_account/email_account.json @@ -37650,16 +38033,10 @@ msgstr "" msgid "e.g.:" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "edit" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "eject" +#. Option for the 'Code Editor Type' (Select) field in DocType 'User' +#: core/doctype/user/user.json +msgctxt "User" +msgid "emacs" msgstr "" #. Option for the 'Permission Type' (Select) field in DocType 'Permission @@ -37681,22 +38058,10 @@ msgid "email inbox" msgstr "" #: permissions.py:402 permissions.py:413 -#: public/js/frappe/form/controls/link.js:481 +#: public/js/frappe/form/controls/link.js:484 msgid "empty" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "envelope" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "exclamation-sign" -msgstr "" - #. Option for the 'Permission Type' (Select) field in DocType 'Permission #. Inspector' #: core/doctype/permission_inspector/permission_inspector.json @@ -37704,18 +38069,6 @@ msgctxt "Permission Inspector" msgid "export" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "eye-close" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "eye-open" -msgstr "" - #. Option for the 'Social Link Type' (Select) field in DocType 'Social Link #. Settings' #: website/doctype/social_link_settings/social_link_settings.json @@ -37723,12 +38076,6 @@ msgctxt "Social Link Settings" msgid "facebook" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "facetime-video" -msgstr "" - #. Option for the 'Status' (Select) field in DocType 'RQ Job' #: core/doctype/rq_job/rq_job.json msgctxt "RQ Job" @@ -37742,106 +38089,16 @@ msgctxt "Social Login Key" msgid "fairlogin" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "fast-backward" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "fast-forward" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "file" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "film" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "filter" -msgstr "" - #. Option for the 'Status' (Select) field in DocType 'RQ Job' #: core/doctype/rq_job/rq_job.json msgctxt "RQ Job" msgid "finished" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "fire" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "flag" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "folder-close" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "folder-open" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "font" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "forward" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "fullscreen" -msgstr "" - #: public/js/frappe/utils/energy_point_utils.js:61 msgid "gained by {0} via automatic rule {1}" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "gift" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "glass" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "globe" -msgstr "" - #. Option for the 'Indicator Color' (Select) field in DocType 'Workspace' #: desk/doctype/workspace/workspace.json msgctxt "Workspace" @@ -37860,7 +38117,7 @@ msgctxt "Workspace" msgid "grey" msgstr "" -#: utils/backups.py:380 +#: utils/backups.py:378 msgid "gzip not found in PATH! This is required to take a backup." msgstr "" @@ -37869,54 +38126,6 @@ msgctxt "Hours (Field: Duration)" msgid "h" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "hand-down" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "hand-left" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "hand-right" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "hand-up" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "hdd" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "headphones" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "heart" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "home" -msgstr "" - #: public/js/frappe/ui/toolbar/search_utils.js:296 msgid "hub" msgstr "" @@ -37940,36 +38149,6 @@ msgctxt "Blog Post" msgid "in minutes" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "inbox" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "indent-left" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "indent-right" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "info-sign" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "italic" -msgstr "" - #: templates/signup.html:11 www/login.html:10 msgid "jane@example.com" msgstr "" @@ -37982,12 +38161,6 @@ msgstr "" msgid "label" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "leaf" -msgstr "" - #. Option for the 'Indicator Color' (Select) field in DocType 'Workspace' #: desk/doctype/workspace/workspace.json msgctxt "Workspace" @@ -38013,24 +38186,6 @@ msgctxt "Desktop Icon" msgid "list" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "list" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "list-alt" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "lock" -msgstr "" - #: www/third_party_apps.html:41 msgid "logged in" msgstr "" @@ -38056,18 +38211,6 @@ msgctxt "Minutes (Field: Duration)" msgid "m" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "magnet" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "map-marker" -msgstr "" - #: model/rename_doc.py:212 msgid "merged {0} into {1}" msgstr "" @@ -38077,18 +38220,6 @@ msgstr "" msgid "min read" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "minus" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "minus-sign" -msgstr "" - #. Option for the 'Date Format' (Select) field in DocType 'System Settings' #: core/doctype/system_settings/system_settings.json msgctxt "System Settings" @@ -38111,18 +38242,6 @@ msgstr "" msgid "module name..." msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "move" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "music" -msgstr "" - #: public/js/frappe/ui/toolbar/search_utils.js:160 msgid "new" msgstr "" @@ -38143,7 +38262,7 @@ msgctxt "OAuth Authorization Code" msgid "nonce" msgstr "" -#: model/document.py:1348 +#: model/document.py:1368 msgid "none of" msgstr "" @@ -38161,30 +38280,6 @@ msgstr "" msgid "of" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "off" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "ok" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "ok-circle" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "ok-sign" -msgstr "" - #. Label of a Data field in DocType 'File' #: core/doctype/file/file.json msgctxt "File" @@ -38227,11 +38322,11 @@ msgctxt "Webhook" msgid "on_update_after_submit" msgstr "" -#: model/document.py:1347 +#: model/document.py:1367 msgid "one of" msgstr "" -#: public/js/frappe/utils/utils.js:393 www/login.html:87 www/login.py:101 +#: public/js/frappe/utils/utils.js:393 www/login.html:87 www/login.py:105 msgid "or" msgstr "" @@ -38247,24 +38342,6 @@ msgctxt "Desktop Icon" msgid "page" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "pause" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "pencil" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "picture" -msgstr "" - #. Option for the 'Indicator Color' (Select) field in DocType 'Workspace' #: desk/doctype/workspace/workspace.json msgctxt "Workspace" @@ -38278,36 +38355,6 @@ msgctxt "OAuth Authorization Code" msgid "plain" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "plane" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "play" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "play-circle" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "plus" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "plus-sign" -msgstr "" - #. Option for the 'Permission Type' (Select) field in DocType 'Permission #. Inspector' #: core/doctype/permission_inspector/permission_inspector.json @@ -38315,12 +38362,6 @@ msgctxt "Permission Inspector" msgid "print" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "print" -msgstr "" - #. Label of a HTML field in DocType 'System Console' #: desk/doctype/system_console/system_console.json msgctxt "System Console" @@ -38333,36 +38374,18 @@ msgctxt "Workspace" msgid "purple" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "qrcode" -msgstr "" - #. Option for the 'Type' (Select) field in DocType 'Desktop Icon' #: desk/doctype/desktop_icon/desktop_icon.json msgctxt "Desktop Icon" msgid "query-report" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "question-sign" -msgstr "" - #. Option for the 'Status' (Select) field in DocType 'RQ Job' #: core/doctype/rq_job/rq_job.json msgctxt "RQ Job" msgid "queued" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "random" -msgstr "" - #. Option for the 'Permission Type' (Select) field in DocType 'Permission #. Inspector' #: core/doctype/permission_inspector/permission_inspector.json @@ -38376,30 +38399,6 @@ msgctxt "Workspace" msgid "red" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "refresh" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "remove" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "remove-circle" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "remove-sign" -msgstr "" - #: public/js/frappe/form/footer/version_timeline_content_builder.js:221 msgid "removed rows for {0}" msgstr "" @@ -38408,12 +38407,6 @@ msgstr "" msgid "renamed from {0} to {1}" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "repeat" -msgstr "" - #. Option for the 'Permission Type' (Select) field in DocType 'Permission #. Inspector' #: core/doctype/permission_inspector/permission_inspector.json @@ -38421,30 +38414,6 @@ msgctxt "Permission Inspector" msgid "report" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "resize-full" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "resize-horizontal" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "resize-small" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "resize-vertical" -msgstr "" - #. Label of a HTML field in DocType 'Custom Role' #: core/doctype/custom_role/custom_role.json msgctxt "Custom Role" @@ -38455,18 +38424,6 @@ msgstr "" msgid "restored {0} as {1}" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "retweet" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "road" -msgstr "" - #: public/js/frappe/utils/utils.js:1126 msgctxt "Seconds (Field: Duration)" msgid "s" @@ -38485,18 +38442,6 @@ msgctxt "RQ Job" msgid "scheduled" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "screenshot" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "search" -msgstr "" - #. Option for the 'Permission Type' (Select) field in DocType 'Permission #. Inspector' #: core/doctype/permission_inspector/permission_inspector.json @@ -38511,24 +38456,6 @@ msgctxt "Permission Inspector" msgid "share" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "share" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "share-alt" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "shopping-cart" -msgstr "" - #. Option for the 'Queue' (Select) field in DocType 'RQ Job' #: core/doctype/rq_job/rq_job.json msgctxt "RQ Job" @@ -38541,12 +38468,6 @@ msgctxt "RQ Worker" msgid "short" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "signal" -msgstr "" - #: public/js/frappe/widgets/number_card_widget.js:282 msgid "since last month" msgstr "" @@ -38563,18 +38484,6 @@ msgstr "" msgid "since yesterday" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "star" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "star-empty" -msgstr "" - #. Option for the 'Status' (Select) field in DocType 'RQ Job' #: core/doctype/rq_job/rq_job.json msgctxt "RQ Job" @@ -38585,24 +38494,6 @@ msgstr "" msgid "starting the setup..." msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "step-backward" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "step-forward" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "stop" -msgstr "" - #. Description of the 'Group Object Class' (Data) field in DocType 'LDAP #. Settings' #: integrations/doctype/ldap_settings/ldap_settings.json @@ -38631,62 +38522,14 @@ msgctxt "Permission Inspector" msgid "submit" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "tag" -msgstr "" - #: public/js/frappe/ui/toolbar/awesome_bar.js:173 msgid "tag name..., e.g. #tag" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "tags" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "tasks" -msgstr "" - #: public/js/frappe/ui/toolbar/awesome_bar.js:168 msgid "text in document type" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "text-height" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "text-width" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "th" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "th-large" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "th-list" -msgstr "" - #: public/js/frappe/form/controls/data.js:35 msgid "this form" msgstr "" @@ -38695,36 +38538,6 @@ msgstr "" msgid "this shouldn't break" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "thumbs-down" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "thumbs-up" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "time" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "tint" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "trash" -msgstr "" - #. Option for the 'Social Link Type' (Select) field in DocType 'Social Link #. Settings' #: website/doctype/social_link_settings/social_link_settings.json @@ -38736,22 +38549,10 @@ msgstr "" msgid "updated to {0}" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "upload" -msgstr "" - #: public/js/frappe/ui/filters/filter.js:340 msgid "use % as wildcard" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "user" -msgstr "" - #: public/js/frappe/ui/filters/filter.js:339 msgid "values separated by commas" msgstr "" @@ -38789,34 +38590,22 @@ msgstr "" msgid "via {0}" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "volume-down" +#. Option for the 'Code Editor Type' (Select) field in DocType 'User' +#: core/doctype/user/user.json +msgctxt "User" +msgid "vim" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "volume-off" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "volume-up" +#. Option for the 'Code Editor Type' (Select) field in DocType 'User' +#: core/doctype/user/user.json +msgctxt "User" +msgid "vscode" msgstr "" #: templates/includes/oauth_confirmation.html:5 msgid "wants to access the following details from your account" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "warning-sign" -msgstr "" - #. Description of the 'Popover Element' (Check) field in DocType 'Form Tour #. Step' #: desk/doctype/form_tour_step/form_tour_step.json @@ -38824,10 +38613,8 @@ msgctxt "Form Tour Step" msgid "when clicked on element it will focus popover if present." msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "wrench" +#: printing/page/print/print.js:619 +msgid "wkhtmltopdf 0.12.x (with patched qt)." msgstr "" #. Option for the 'Permission Type' (Select) field in DocType 'Permission @@ -38853,18 +38640,6 @@ msgctxt "System Settings" msgid "yyyy-mm-dd" msgstr "" -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "zoom-in" -msgstr "" - -#. Option for the 'Icon' (Select) field in DocType 'Workflow State' -#: workflow/doctype/workflow_state/workflow_state.json -msgctxt "Workflow State" -msgid "zoom-out" -msgstr "" - #: desk/doctype/event/event.js:87 msgid "{0}" msgstr "" @@ -38911,7 +38686,7 @@ msgstr "" msgid "{0} Dashboard" msgstr "" -#: public/js/frappe/form/grid_row.js:457 +#: public/js/frappe/form/grid_row.js:458 #: public/js/frappe/list/list_settings.js:224 #: public/js/frappe/views/kanban/kanban_settings.js:178 msgid "{0} Fields" @@ -38952,7 +38727,7 @@ msgstr "" msgid "{0} Name" msgstr "" -#: model/base_document.py:1055 +#: model/base_document.py:1063 msgid "{0} Not allowed to change {1} after submission from {2} to {3}" msgstr "" @@ -38963,7 +38738,7 @@ msgstr "" msgid "{0} Report" msgstr "" -#: public/js/frappe/views/reports/query_report.js:883 +#: public/js/frappe/views/reports/query_report.js:884 msgid "{0} Reports" msgstr "" @@ -39197,7 +38972,7 @@ msgstr "" msgid "{0} has left the conversation in {1} {2}" msgstr "" -#: __init__.py:2488 +#: __init__.py:2490 msgid "{0} has no versions tracked." msgstr "" @@ -39356,11 +39131,11 @@ msgstr "" msgid "{0} is within {1}" msgstr "" -#: public/js/frappe/list/list_view.js:1597 +#: public/js/frappe/list/list_view.js:1601 msgid "{0} items selected" msgstr "" -#: core/doctype/user/user.py:1381 +#: core/doctype/user/user.py:1382 msgid "{0} just impersonated as you. They gave this reason: {1}" msgstr "" @@ -39393,7 +39168,7 @@ msgstr "" msgid "{0} months ago" msgstr "" -#: model/document.py:1603 +#: model/document.py:1623 msgid "{0} must be after {1}" msgstr "" @@ -39401,11 +39176,11 @@ msgstr "" msgid "{0} must be one of {1}" msgstr "" -#: model/base_document.py:790 +#: model/base_document.py:798 msgid "{0} must be set first" msgstr "" -#: model/base_document.py:648 +#: model/base_document.py:656 msgid "{0} must be unique" msgstr "" @@ -39427,11 +39202,11 @@ msgstr "" msgid "{0} not found" msgstr "" -#: core/doctype/report/report.py:413 public/js/frappe/list/list_view.js:988 +#: core/doctype/report/report.py:413 public/js/frappe/list/list_view.js:992 msgid "{0} of {1}" msgstr "" -#: public/js/frappe/list/list_view.js:990 +#: public/js/frappe/list/list_view.js:994 msgid "{0} of {1} ({2} rows with children)" msgstr "" @@ -39588,11 +39363,11 @@ msgstr "" msgid "{0} {1} added to Dashboard {2}" msgstr "" -#: model/base_document.py:581 model/rename_doc.py:110 +#: model/base_document.py:589 model/rename_doc.py:110 msgid "{0} {1} already exists" msgstr "" -#: model/base_document.py:898 +#: model/base_document.py:906 msgid "{0} {1} cannot be \"{2}\". It should be one of \"{3}\"" msgstr "" @@ -39604,7 +39379,7 @@ msgstr "" msgid "{0} {1} does not exist, select a new target to merge" msgstr "" -#: public/js/frappe/form/form.js:934 +#: public/js/frappe/form/form.js:945 msgid "{0} {1} is linked with the following submitted documents: {2}" msgstr "" @@ -39616,11 +39391,11 @@ msgstr "" msgid "{0} {1}: Submitted Record cannot be deleted. You must {2} Cancel {3} it first." msgstr "" -#: model/base_document.py:1016 +#: model/base_document.py:1024 msgid "{0}, Row {1}" msgstr "" -#: model/base_document.py:1021 +#: model/base_document.py:1029 msgid "{0}: '{1}' ({3}) will get truncated, as max characters allowed is {2}" msgstr "" @@ -39722,7 +39497,7 @@ msgstr "" msgid "{0}: {1} is set to state {2}" msgstr "" -#: public/js/frappe/views/reports/query_report.js:1205 +#: public/js/frappe/views/reports/query_report.js:1206 msgid "{0}: {1} vs {2}" msgstr "" @@ -39750,18 +39525,36 @@ msgstr "" msgid "{{{0}}} is not a valid fieldname pattern. It should be {{field_name}}." msgstr "" +#. Count format of shortcut in the Website Workspace +#: website/workspace/website/website.json +msgctxt "Blogger" +msgid "{} Active" +msgstr "" + #: public/js/frappe/form/form.js:517 msgid "{} Complete" msgstr "" -#: utils/data.py:2424 +#: utils/data.py:2427 msgid "{} Invalid python code on line {}" msgstr "" -#: utils/data.py:2433 +#: utils/data.py:2436 msgid "{} Possibly invalid python code.
{}" msgstr "" +#. Count format of shortcut in the Website Workspace +#: website/workspace/website/website.json +msgctxt "Blog Post" +msgid "{} Published" +msgstr "" + +#. Count format of shortcut in the Website Workspace +#: website/workspace/website/website.json +msgctxt "Web Page" +msgid "{} Published" +msgstr "" + #: core/doctype/log_settings/log_settings.py:55 msgid "{} does not support automated log clearing." msgstr "" @@ -39787,7 +39580,7 @@ msgstr "" msgid "{} not found in PATH! This is required to restore the database." msgstr "" -#: utils/backups.py:447 +#: utils/backups.py:445 msgid "{} not found in PATH! This is required to take a backup." msgstr "" diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index d02e9141eb..249a0a656f 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -803,13 +803,14 @@ class BaseDocument: # that are mapped as link_fieldname.source_fieldname in Options of # Readonly or Data or Text type fields + meta = frappe.get_meta(doctype) fields_to_fetch = [ _df for _df in self.meta.get_fields_to_fetch(df.fieldname) if not _df.get("fetch_if_empty") or (_df.get("fetch_if_empty") and not self.get(_df.fieldname)) ] - if not frappe.get_meta(doctype).get("is_virtual"): + if not meta.get("is_virtual"): if not fields_to_fetch: # cache a single value type values = _dict(name=frappe.db.get_value(doctype, docname, "name", cache=True)) @@ -827,10 +828,10 @@ class BaseDocument: or empty_values ) - if getattr(frappe.get_meta(doctype), "issingle", 0): + if getattr(meta, "issingle", 0): values.name = doctype - if frappe.get_meta(doctype).get("is_virtual"): + if meta.get("is_virtual"): values = frappe.get_doc(doctype, docname).as_dict() if values: @@ -840,7 +841,8 @@ class BaseDocument: if self.is_new() or not self.docstatus.is_submitted() or _df.allow_on_submit: self.set_fetch_from_value(doctype, _df, values) - notify_link_count(doctype, docname) + if not meta.istable: + notify_link_count(doctype, docname) if not values.name: invalid_links.append((df.fieldname, docname, get_msg(df, docname))) @@ -1027,7 +1029,7 @@ class BaseDocument: frappe.throw( _("{0}: '{1}' ({3}) will get truncated, as max characters allowed is {2}").format( - reference, _(df.label, context=df.parent), max_length, value + reference, frappe.bold(_(df.label, context=df.parent)), max_length, value ), frappe.CharacterLengthExceededError, title=_("Value too big"), diff --git a/frappe/model/document.py b/frappe/model/document.py index f9c9ae7ae1..1b0c34f1fd 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -21,7 +21,7 @@ from frappe.model.naming import set_new_name, validate_name from frappe.model.utils import is_virtual_doctype from frappe.model.workflow import set_workflow_state_on_action, validate_workflow from frappe.types import DF -from frappe.utils import compare, cstr, date_diff, file_lock, flt, now +from frappe.utils import Truthy, compare, cstr, date_diff, file_lock, flt, now from frappe.utils.data import get_absolute_url, get_datetime, get_timedelta, getdate from frappe.utils.global_search import update_global_search @@ -468,7 +468,7 @@ class Document(BaseDocument): previous = self.get_doc_before_save() if not previous: - return True + return Truthy(context="New Document") previous_value = previous.get(fieldname) current_value = self.get(fieldname) @@ -480,7 +480,10 @@ class Document(BaseDocument): elif isinstance(previous_value, timedelta): current_value = get_timedelta(current_value) - return previous_value != current_value + if previous_value != current_value: + return Truthy(value=previous_value) + + return False def set_new_name(self, force=False, set_name=None, set_child_names=True): """Calls `frappe.naming.set_new_name` for parent and child docs.""" diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 2000a0b204..9aaf7c3a1b 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -451,27 +451,29 @@ def get_link_fields(doctype: str) -> list[dict]: frappe.flags.link_fields = {} if doctype not in frappe.flags.link_fields: - virtual_doctypes = frappe.get_all("DocType", {"is_virtual": 1}, pluck="name") - dt = frappe.qb.DocType("DocType") df = frappe.qb.DocType("DocField") cf = frappe.qb.DocType("Custom Field") ps = frappe.qb.DocType("Property Setter") - standard_fields = ( + standard_fields_query = ( frappe.qb.from_(df) .inner_join(dt) .on(df.parent == dt.name) .select(df.parent, df.fieldname, dt.issingle.as_("issingle")) - .where( - (df.options == doctype) - & (df.fieldtype == "Link") - & (df.is_virtual == 0) - & (dt.is_virtual == 0) - ) - .run(as_dict=True) + .where((df.options == doctype) & (df.fieldtype == "Link")) ) + if frappe.db.has_column("DocField", "is_virtual"): + standard_fields_query = standard_fields_query.where(df.is_virtual == 0) + + virtual_doctypes = [] + if frappe.db.has_column("DocType", "is_virtual"): + virtual_doctypes = frappe.get_all("DocType", {"is_virtual": 1}, pluck="name") + standard_fields_query = standard_fields_query.where(dt.is_virtual == 0) + + standard_fields = standard_fields_query.run(as_dict=True) + cf_issingle = frappe.qb.from_(dt).select(dt.issingle).where(dt.name == cf.dt).as_("issingle") custom_fields = ( frappe.qb.from_(cf) diff --git a/frappe/public/js/calendar.bundle.js b/frappe/public/js/calendar.bundle.js new file mode 100644 index 0000000000..4c45b24727 --- /dev/null +++ b/frappe/public/js/calendar.bundle.js @@ -0,0 +1,8 @@ +import { Calendar as FullCalendar } from "@fullcalendar/core"; +import dayGridPlugin from "@fullcalendar/daygrid"; +import listPlugin from "@fullcalendar/list"; +import timeGridPlugin from "@fullcalendar/timegrid"; +import interactionPlugin from "@fullcalendar/interaction"; + +frappe.FullCalendar = FullCalendar; +frappe.FullCalendar.Plugins = [listPlugin, dayGridPlugin, timeGridPlugin, interactionPlugin]; diff --git a/frappe/public/js/frappe/form/controls/table.js b/frappe/public/js/frappe/form/controls/table.js index 4ce065ef47..0a93db4363 100644 --- a/frappe/public/js/frappe/form/controls/table.js +++ b/frappe/public/js/frappe/form/controls/table.js @@ -117,7 +117,7 @@ frappe.ui.form.ControlTable = class ControlTable extends frappe.ui.form.Control get_field(field_name) { let fieldname; field_name = field_name.toLowerCase(); - this.grid.meta.fields.some((field) => { + this.grid?.meta?.fields.some((field) => { if (frappe.model.no_value_type.includes(field.fieldtype)) { return false; } diff --git a/frappe/public/js/frappe/form/controls/text_editor.js b/frappe/public/js/frappe/form/controls/text_editor.js index 06d8c53a25..4860ab6aa0 100644 --- a/frappe/public/js/frappe/form/controls/text_editor.js +++ b/frappe/public/js/frappe/form/controls/text_editor.js @@ -196,7 +196,7 @@ frappe.ui.form.ControlTextEditor = class ControlTextEditor extends frappe.ui.for } get_quill_options() { - return { + const options = { modules: { toolbar: Object.keys(this.df).includes("get_toolbar_options") ? this.df.get_toolbar_options() @@ -211,6 +211,14 @@ frappe.ui.form.ControlTextEditor = class ControlTextEditor extends frappe.ui.for bounds: this.quill_container[0], placeholder: this.df.placeholder || "", }; + + // In a grid row where space is constrained, hide the toolbar. + if (this.grid_row) { + options.theme = null; + options.modules.toolbar = []; + } + + return options; } get_mention_options() { diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 3c627cd182..96db33dddd 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -1101,12 +1101,6 @@ export default class GridRow { parent = column.field_area, df = column.df; - // no text editor in grid - if (df.fieldtype == "Text Editor") { - df = Object.assign({}, df); - df.fieldtype = "Text"; - } - var field = frappe.ui.form.make_control({ df: df, parent: parent, diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 78f535e91c..9817f1daa9 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -629,7 +629,10 @@ frappe.ui.form.Layout = class Layout { // show grid row (if exists) field.grid.grid_rows[0].show_form(); return true; - } else if (!frappe.model.no_value_type.includes(field.df.fieldtype)) { + } else if ( + field.df.fieldtype === "Table MultiSelect" || + !frappe.model.no_value_type.includes(field.df.fieldtype) + ) { this.set_focus(field); return true; } diff --git a/frappe/public/js/frappe/form/multi_select_dialog.js b/frappe/public/js/frappe/form/multi_select_dialog.js index 49c443d19d..4e7ba29a17 100644 --- a/frappe/public/js/frappe/form/multi_select_dialog.js +++ b/frappe/public/js/frappe/form/multi_select_dialog.js @@ -16,6 +16,8 @@ frappe.ui.form.MultiSelectDialog = class MultiSelectDialog { this.fields = this.get_fields(); this.make(); + + this.selected_fields = new Set(); } get_fields() { @@ -337,12 +339,25 @@ frappe.ui.form.MultiSelectDialog = class MultiSelectDialog { if (!$(e.target).is(":checkbox") && !$(e.target).is("a")) { $(this).find(":checkbox").trigger("click"); } + let name = $(this).attr("data-item-name").trim(); + if ($(this).find(":checkbox").is(":checked")) { + me.selected_fields.add(name); + } else { + me.selected_fields.delete(name); + } }); this.$results.on("click", ".list-item--head :checkbox", (e) => { - this.$results - .find(".list-item-container .list-row-check") - .prop("checked", $(e.target).is(":checked")); + let checked = $(e.target).is(":checked"); + this.$results.find(".list-item-container .list-row-check").each(function () { + $(this).prop("checked", checked); + const name = $(this).closest(".list-item-container").attr("data-item-name").trim(); + if (checked) { + me.selected_fields.add(name); + } else { + me.selected_fields.delete(name); + } + }); }); this.$parent.find(".input-with-feedback").on("change", () => { @@ -510,12 +525,12 @@ frappe.ui.form.MultiSelectDialog = class MultiSelectDialog { empty_list() { // Store all checked items - let checked = this.get_checked_items().map((item) => { - return { + let checked = this.results + .filter((result) => this.selected_fields.has(result.name)) + .map((item) => ({ ...item, checked: true, - }; - }); + })); // Remove **all** items this.$results.find(".list-item-container").remove(); diff --git a/frappe/public/js/frappe/form/sidebar/assign_to.js b/frappe/public/js/frappe/form/sidebar/assign_to.js index a5741522b7..e33e0868d4 100644 --- a/frappe/public/js/frappe/form/sidebar/assign_to.js +++ b/frappe/public/js/frappe/form/sidebar/assign_to.js @@ -139,6 +139,25 @@ frappe.ui.form.AssignToDialog = class AssignToDialog { me.dialog.set_value("assign_to", assign_to); } + user_group_list() { + let me = this; + let user_group = me.dialog.get_value("assign_to_user_group"); + me.dialog.set_value("assign_to_me", 0); + + if (user_group) { + let user_group_members = []; + frappe.db + .get_list("User Group Member", { + parent_doctype: "User Group", + filters: { parent: user_group }, + fields: ["user"], + }) + .then((response) => { + user_group_members = response.map((group_member) => group_member.user); + me.dialog.set_value("assign_to", user_group_members); + }); + } + } set_description_from_doc() { let me = this; @@ -157,6 +176,13 @@ frappe.ui.form.AssignToDialog = class AssignToDialog { default: 0, onchange: () => me.assign_to_me(), }, + { + label: __("Assign To User Group"), + fieldtype: "Link", + fieldname: "assign_to_user_group", + options: "User Group", + onchange: () => me.user_group_list(), + }, { fieldtype: "MultiSelectPills", fieldname: "assign_to", diff --git a/frappe/public/js/frappe/list/list_sidebar_group_by.js b/frappe/public/js/frappe/list/list_sidebar_group_by.js index 8430b54678..53f5406f4b 100644 --- a/frappe/public/js/frappe/list/list_sidebar_group_by.js +++ b/frappe/public/js/frappe/list/list_sidebar_group_by.js @@ -221,6 +221,8 @@ frappe.views.ListGroupBy = class ListGroupBy { label = __("Me"); } else if (fieldtype && fieldtype == "Check") { label = field.name == "0" ? __("No") : __("Yes"); + } else if (fieldtype && fieldtype == "Link" && field.title) { + label = __(field.title); } else { label = __(field.name); } diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index b57827ce1d..312917696a 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -231,11 +231,15 @@ frappe.router = { } else if (frappe.model.is_single(doctype_route.doctype)) { route = ["Form", doctype_route.doctype, doctype_route.doctype]; } else if (meta.default_view) { - route = [ - "List", - doctype_route.doctype, - this.list_views_route[meta.default_view.toLowerCase()], - ]; + if (meta.default_view === "Tree") { + route = ["Tree", doctype_route.doctype]; + } else { + route = [ + "List", + doctype_route.doctype, + this.list_views_route[meta.default_view.toLowerCase()], + ]; + } } else { route = ["List", doctype_route.doctype, "List"]; } diff --git a/frappe/public/js/frappe/ui/dialog.js b/frappe/public/js/frappe/ui/dialog.js index 515b693482..90e95ef92b 100644 --- a/frappe/public/js/frappe/ui/dialog.js +++ b/frappe/public/js/frappe/ui/dialog.js @@ -127,6 +127,10 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { }); } + get $backdrop() { + return $(this.$wrapper.data("bs.modal")?._backdrop); + } + set_modal_size() { if (!this.fields) { this.size = ""; @@ -241,7 +245,7 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { this.$wrapper.removeClass("modal-minimize"); if (this.minimizable && this.is_minimized) { - $(".modal-backdrop").toggle(); + this.$backdrop.show(); this.is_minimized = false; } @@ -256,7 +260,7 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { hide() { if (this.animate && this.animation_speed === "slow") { this.$wrapper.addClass("slow"); - $(".modal-backdrop").addClass("slow"); + this.$backdrop.addClass("slow"); } this.$wrapper.modal("hide"); this.is_visible = false; @@ -279,7 +283,7 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup { } toggle_minimize() { - $(".modal-backdrop").toggle(); + this.$backdrop.toggle(); let modal = this.$wrapper.closest(".modal").toggleClass("modal-minimize"); modal.attr("tabindex") ? modal.removeAttr("tabindex") : modal.attr("tabindex", -1); this.is_minimized = !this.is_minimized; diff --git a/frappe/public/js/frappe/ui/filters/filter_list.js b/frappe/public/js/frappe/ui/filters/filter_list.js index 100ceecc8e..e88262cce5 100644 --- a/frappe/public/js/frappe/ui/filters/filter_list.js +++ b/frappe/public/js/frappe/ui/filters/filter_list.js @@ -62,7 +62,7 @@ frappe.ui.FilterGroup = class { } set_popover_events() { - $(document.body).on("click", (e) => { + $(document.body).on("mousedown", (e) => { if (this.wrapper && this.wrapper.is(":visible")) { const in_datepicker = $(e.target).is(".datepicker--cell") || diff --git a/frappe/public/js/frappe/views/calendar/calendar.js b/frappe/public/js/frappe/views/calendar/calendar.js index cd0402eda1..d43ae2ff95 100644 --- a/frappe/public/js/frappe/views/calendar/calendar.js +++ b/frappe/public/js/frappe/views/calendar/calendar.js @@ -28,7 +28,7 @@ frappe.views.CalendarView = class CalendarView extends frappe.views.ListView { setup_defaults() { return super.setup_defaults().then(() => { this.page_title = __("{0} Calendar", [this.page_title]); - this.calendar_settings = frappe.views.calendar[this.doctype] || {}; + this.calendar_settings = frappe.views.Calendar[this.doctype] || {}; this.calendar_name = frappe.get_route()[3]; }); } @@ -101,15 +101,7 @@ frappe.views.CalendarView = class CalendarView extends frappe.views.ListView { } get required_libs() { - let assets = [ - "assets/frappe/js/lib/fullcalendar/fullcalendar.min.css", - "assets/frappe/js/lib/fullcalendar/fullcalendar.min.js", - ]; - let user_language = frappe.boot.lang; - if (user_language && user_language !== "en") { - assets.push("assets/frappe/js/lib/fullcalendar/locale-all.js"); - } - return assets; + return "calendar.bundle.js"; } }; @@ -133,10 +125,10 @@ frappe.views.Calendar = class Calendar { } get_default_options() { return new Promise((resolve) => { - let defaultView = localStorage.getItem("cal_defaultView"); + let initialView = localStorage.getItem("cal_initialView"); let weekends = localStorage.getItem("cal_weekends"); let defaults = { - defaultView: defaultView ? defaultView : "month", + initialView: initialView ? initialView : "dayGridMonth", weekends: weekends ? weekends : true, }; resolve(defaults); @@ -162,13 +154,13 @@ frappe.views.Calendar = class Calendar { }); $(this.parent).on("show", function () { - me.$cal.fullCalendar("refetchEvents"); + me.$cal.fullCalendar.refetchEvents(); }); } make() { this.$wrapper = this.parent; - this.$cal = $("
").appendTo(this.$wrapper); + this.$cal = $("
").appendTo(this.$wrapper); this.footnote_area = frappe.utils.set_footnote( this.footnote_area, this.$wrapper, @@ -176,7 +168,9 @@ frappe.views.Calendar = class Calendar { ); this.footnote_area.css({ "border-top": "0px" }); - this.$cal.fullCalendar(this.cal_options); + this.fullCalendar = new frappe.FullCalendar(this.$cal[0], this.cal_options); + this.fullCalendar.render(); + this.set_css(); } setup_view_mode_button(defaults) { @@ -194,143 +188,152 @@ frappe.views.Calendar = class Calendar { const me = this; let btn_group = me.$wrapper.find(".fc-button-group"); btn_group.on("click", ".btn", function () { - let value = $(this).hasClass("fc-agendaWeek-button") - ? "agendaWeek" - : $(this).hasClass("fc-agendaDay-button") - ? "agendaDay" - : "month"; - me.set_localStorage_option("cal_defaultView", value); + let value = $(this).hasClass("fc-dayGridWeek-button") + ? "dayGridWeek" + : $(this).hasClass("fc-dayGridDay-button") + ? "dayGridDay" + : "dayGridMonth"; + me.set_localStorage_option("cal_initialView", value); }); me.$wrapper.on("click", ".btn-weekend", function () { me.cal_options.weekends = !me.cal_options.weekends; - me.$cal.fullCalendar("option", "weekends", me.cal_options.weekends); + me.fullCalendar.setOption("weekends", me.cal_options.weekends); me.set_localStorage_option("cal_weekends", me.cal_options.weekends); me.set_css(); me.setup_view_mode_button(me.cal_options); }); } set_css() { - // flatify buttons + const viewButtons = + ".fc-dayGridMonth-button, .fc-dayGridWeek-button, .fc-dayGridDay-button, .fc-today-button"; + const fcViewButtonClasses = "fc-button fc-button-primary fc-button-active"; + + // remove fc-button styles this.$wrapper - .find("button.fc-state-default") - .removeClass("fc-state-default") + .find("button.fc-button") + .removeClass(fcViewButtonClasses) .addClass("btn btn-default"); - this.$wrapper - .find(".fc-month-button, .fc-agendaWeek-button, .fc-agendaDay-button") - .wrapAll('
'); + // group all view buttons + this.$wrapper.find(viewButtons).wrapAll('
'); + // add icons this.$wrapper - .find(".fc-prev-button span") + .find(`.fc-prev-button span`) .attr("class", "") .html(frappe.utils.icon("left")); this.$wrapper - .find(".fc-next-button span") + .find(`.fc-next-button span`) .attr("class", "") .html(frappe.utils.icon("right")); + if (this.$wrapper.find(".fc-today-button svg").length == 0) + this.$wrapper.find(".fc-today-button").prepend(frappe.utils.icon("today")); - this.$wrapper.find(".fc-today-button").prepend(frappe.utils.icon("today")); - - this.$wrapper.find(".fc-day-number").wrap('
'); - + // v6.x of fc has weird behaviour which removes all the custom classes + // on header buttons on click, event below re-adds all the classes var btn_group = this.$wrapper.find(".fc-button-group"); - btn_group.find(".fc-state-active").addClass("active"); + btn_group.find(".fc-button-active").addClass("active"); btn_group.find(".btn").on("click", function () { - btn_group.find(".btn").removeClass("active"); + btn_group + .find(viewButtons) + .removeClass(`active ${fcViewButtonClasses}`) + .addClass("btn btn-default"); + $(this).addClass("active"); }); } get_system_datetime(date) { - date._offset = moment(date).tz(frappe.sys_defaults.time_zone)._offset; - return frappe.datetime.convert_to_system_tz(moment(date).locale("en")); + return frappe.datetime.convert_to_system_tz(date, true); } setup_options(defaults) { var me = this; defaults.meridiem = "false"; this.cal_options = { + plugins: frappe.FullCalendar.Plugins, + initialView: defaults.initialView || "dayGridMonth", locale: frappe.boot.lang, - header: { - left: "prev, title, next", - right: "today, month, agendaWeek, agendaDay", + firstDay: 1, + headerToolbar: { + left: "prev,title,next", + center: "", + right: "today,dayGridMonth,dayGridWeek,dayGridDay", }, editable: true, + droppable: true, selectable: true, - selectHelper: true, + selectMirror: true, forceEventDuration: true, displayEventTime: true, - defaultView: defaults.defaultView, weekends: defaults.weekends, nowIndicator: true, + themeSystem: null, buttonText: { today: __("Today"), month: __("Month"), week: __("Week"), day: __("Day"), }, - events: function (start, end, timezone, callback) { + events: function (info, successCallback, failureCallback) { return frappe.call({ method: me.get_events_method || "frappe.desk.calendar.get_events", type: "GET", - args: me.get_args(start, end), + args: me.get_args(info.start, info.end), callback: function (r) { var events = r.message || []; events = me.prepare_events(events); - callback(events); + successCallback(events); }, }); }, displayEventEnd: true, - eventRender: function (event, element) { - element.attr("title", event.tooltip); - }, - eventClick: function (event) { + eventClick: function (info) { // edit event description or delete - var doctype = event.doctype || me.doctype; + var doctype = info.doctype || me.doctype; if (frappe.model.can_read(doctype)) { - frappe.set_route("Form", doctype, event.name); + frappe.set_route("Form", doctype, info.event.id); } }, - eventDrop: function (event, delta, revertFunc) { - me.update_event(event, revertFunc); + eventDrop: function (info) { + me.update_event(info.event, info.revert); }, - eventResize: function (event, delta, revertFunc) { - me.update_event(event, revertFunc); + eventResize: function (info) { + me.update_event(info.event, info.revert); }, - select: function (startDate, endDate, jsEvent, view) { - if (view.name === "month" && endDate - startDate === 86400000) { + select: function (info) { + const seconds = info.end - info.start; + const allDay = seconds === 86400000; + + if (info.view.type === "dayGridMonth" && allDay) { // detect single day click in month view return; } var event = frappe.model.get_new_doc(me.doctype); - event[me.field_map.start] = me.get_system_datetime(startDate); + event[me.field_map.start] = me.get_system_datetime(info.start); + if (me.field_map.end) event[me.field_map.end] = me.get_system_datetime(info.end); - if (me.field_map.end) event[me.field_map.end] = me.get_system_datetime(endDate); - - if (me.field_map.allDay) { - var all_day = startDate._ambigTime && endDate._ambigTime ? 1 : 0; - - event[me.field_map.allDay] = all_day; - - if (all_day) - event[me.field_map.end] = me.get_system_datetime( - moment(endDate).subtract(1, "s") - ); + if (seconds >= 86400000) { + if (allDay) { + // all-day click + event[me.field_map.allDay] = 1; + } + // incase of all day or multiple day events -1 sec + event[me.field_map.end] = me.get_system_datetime(info.end - 1); } - frappe.set_route("Form", me.doctype, event.name); }, - dayClick: function (date, jsEvent, view) { - if (view.name === "month") { - const $date_cell = $("td[data-date=" + date.format("YYYY-MM-DD") + "]"); + dateClick: function (info) { + if (info.view.type === "dayGridMonth") { + const $date_cell = $( + "td[data-date=" + info.date.toISOString().slice(0, 10) + "]" + ); if ($date_cell.hasClass("date-clicked")) { - me.$cal.fullCalendar("changeView", "agendaDay"); - me.$cal.fullCalendar("gotoDate", date); + me.fullCalendar.changeView("timeGridDay", info.date); me.$wrapper.find(".date-clicked").removeClass("date-clicked"); // update "active view" btn @@ -340,6 +343,13 @@ frappe.views.Calendar = class Calendar { me.$wrapper.find(".date-clicked").removeClass("date-clicked"); $date_cell.addClass("date-clicked"); + + // explicitly remove the fc primary button styling that is append on view change + // from month -> day + $("#fc-calendar-wrapper") + .find("button.fc-button") + .removeClass("fc-button fc-button-primary fc-button-active") + .addClass("btn btn-default"); } return false; }, @@ -361,7 +371,7 @@ frappe.views.Calendar = class Calendar { return args; } refresh() { - this.$cal.fullCalendar("refetchEvents"); + this.fullCalendar.refetchEvents(); } prepare_events(events) { var me = this; @@ -400,7 +410,6 @@ frappe.views.Calendar = class Calendar { d.end = frappe.datetime.add_days(d.start, 1); } - me.fix_end_date_for_event_render(d); me.prepare_colors(d); d.title = frappe.utils.html2text(d.title); @@ -431,7 +440,7 @@ frappe.views.Calendar = class Calendar { } update_event(event, revertFunc) { var me = this; - frappe.model.remove_from_locals(me.doctype, event.name); + frappe.model.remove_from_locals(me.doctype, event.id); return frappe.call({ method: me.update_event_method || "frappe.desk.calendar.update_event", args: me.get_update_args(event), @@ -449,13 +458,14 @@ frappe.views.Calendar = class Calendar { get_update_args(event) { var me = this; var args = { - name: event[this.field_map.id], + name: event.id, }; args[this.field_map.start] = me.get_system_datetime(event.start); - if (this.field_map.allDay) - args[this.field_map.allDay] = event.start._ambigTime && event.end._ambigTime ? 1 : 0; + if (this.field_map.allDay) { + args[this.field_map.allDay] = event.end - event.start === 86400000 ? 1 : 0; + } if (this.field_map.end) { if (!event.end) { @@ -463,11 +473,8 @@ frappe.views.Calendar = class Calendar { } args[this.field_map.end] = me.get_system_datetime(event.end); - if (args[this.field_map.allDay]) { - args[this.field_map.end] = me.get_system_datetime( - moment(event.end).subtract(1, "s") - ); + args[this.field_map.end] = me.get_system_datetime(new Date(event.end - 1000)); } } @@ -475,14 +482,4 @@ frappe.views.Calendar = class Calendar { return { args: args, field_map: this.field_map }; } - - fix_end_date_for_event_render(event) { - if (event.allDay) { - // We use inclusive end dates. This workaround fixes the rendering of events - event.start = event.start ? $.fullCalendar.moment(event.start).stripTime() : null; - event.end = event.end - ? $.fullCalendar.moment(event.end).add(1, "day").stripTime() - : null; - } - } }; diff --git a/frappe/public/js/frappe/views/treeview.js b/frappe/public/js/frappe/views/treeview.js index 5130863f93..ec43b23696 100644 --- a/frappe/public/js/frappe/views/treeview.js +++ b/frappe/public/js/frappe/views/treeview.js @@ -180,13 +180,8 @@ frappe.views.TreeView = class TreeView { args: me.args, callback: function (r) { if (r.message) { - if (r.message.length > 1) { - me.root_label = me.doctype; - me.root_value = ""; - } else { - me.root_label = r.message[0]["value"]; - me.root_value = me.root_label; - } + me.root_label = me.doctype; + me.root_value = ""; me.make_tree(); } }, diff --git a/frappe/public/js/lib/fullcalendar/fullcalendar.min.css b/frappe/public/js/lib/fullcalendar/fullcalendar.min.css deleted file mode 100644 index ab2403ecb3..0000000000 --- a/frappe/public/js/lib/fullcalendar/fullcalendar.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * FullCalendar v3.10.2 - * Docs & License: https://fullcalendar.io/ - * (c) 2019 Adam Shaw - */.fc button,.fc table,body .fc{font-size:1em}.fc .fc-axis,.fc button,.fc-day-grid-event .fc-content,.fc-list-item-marker,.fc-list-item-time,.fc-time-grid-event .fc-time,.fc-time-grid-event.fc-short .fc-content{white-space:nowrap}.fc-event,.fc-event:hover,.fc-state-hover,.fc.fc-bootstrap3 a,.ui-widget .fc-event,a.fc-more{text-decoration:none}.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}.fc th,.fc-basic-view .fc-day-top .fc-week-number,.fc-basic-view td.fc-week-number,.fc-icon,.fc-toolbar{text-align:center}.fc-highlight{background:#bce8f1;opacity:.3}.fc-bgevent{background:#8fdf82;opacity:.3}.fc-nonbusiness{background:#d7d7d7}.fc button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;height:2.1em;padding:0 .6em;cursor:pointer}.fc button::-moz-focus-inner{margin:0;padding:0}.fc-state-default{border:1px solid;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.fc-state-default.fc-corner-left{border-top-left-radius:4px;border-bottom-left-radius:4px}.fc-state-default.fc-corner-right{border-top-right-radius:4px;border-bottom-right-radius:4px}.fc button .fc-icon{position:relative;top:-.05em;margin:0 .2em;vertical-align:middle}.fc-state-active,.fc-state-disabled,.fc-state-down,.fc-state-hover{color:#333;background-color:#e6e6e6}.fc-state-hover{color:#333;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.fc-state-active,.fc-state-down{background-color:#ccc;background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.fc-state-disabled{cursor:default;background-image:none;opacity:.65;box-shadow:none}.fc-event.fc-draggable,.fc-event[href],.fc-popover .fc-header .fc-close,a[data-goto]{cursor:pointer}.fc-button-group{display:inline-block}.fc .fc-button-group>*{float:left;margin:0 0 0 -1px}.fc .fc-button-group>:first-child{margin-left:0}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{padding:2px 4px}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-ltr .fc-popover .fc-header .fc-title,.fc-rtl .fc-popover .fc-header .fc-close{float:left}.fc-ltr .fc-popover .fc-header .fc-close,.fc-rtl .fc-popover .fc-header .fc-title{float:right}.fc-divider{border-style:solid;border-width:1px}hr.fc-divider{height:0;margin:0;padding:0 0 2px;border-width:1px 0}.fc-bg table,.fc-row .fc-bgevent-skeleton table,.fc-row .fc-highlight-skeleton table{height:100%}.fc-clear{clear:both}.fc-bg,.fc-bgevent-skeleton,.fc-helper-skeleton,.fc-highlight-skeleton{position:absolute;top:0;left:0;right:0}.fc-bg{bottom:0}.fc table{width:100%;box-sizing:border-box;table-layout:fixed;border-collapse:collapse;border-spacing:0}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}a[data-goto]:hover{text-decoration:underline}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-bgevent-skeleton,.fc-row .fc-highlight-skeleton{bottom:0}.fc-row .fc-bgevent-skeleton td,.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-bgevent-skeleton{z-index:2}.fc-row .fc-highlight-skeleton{z-index:3}.fc-row .fc-content-skeleton{position:relative;z-index:4;padding-bottom:2px}.fc-row .fc-helper-skeleton{z-index:5}.fc .fc-row .fc-content-skeleton table,.fc .fc-row .fc-content-skeleton td,.fc .fc-row .fc-helper-skeleton td{background:0 0;border-color:transparent}.fc-row .fc-content-skeleton td,.fc-row .fc-helper-skeleton td{border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-helper-skeleton tbody td{border-top:0}.fc-scroller{-webkit-overflow-scrolling:touch}.fc-day-grid-event .fc-content,.fc-icon,.fc-row.fc-rigid,.fc-time-grid-event{overflow:hidden}.fc-scroller>.fc-day-grid,.fc-scroller>.fc-time-grid{position:relative;width:100%}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.3;border-radius:3px;border:1px solid #3a87ad}.fc-event,.fc-event-dot{background-color:#3a87ad}.fc-event,.fc-event:hover{color:#fff}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc-event .fc-bg{z-index:1;background:#fff;opacity:.25}.fc-event .fc-content{position:relative;z-index:2}.fc-event .fc-resizer{position:absolute;z-index:4;display:none}.fc-event.fc-allow-mouse-resize .fc-resizer,.fc-event.fc-selected .fc-resizer{display:block}.fc-event.fc-selected .fc-resizer:before{content:"";position:absolute;z-index:9999;top:50%;left:50%;width:40px;height:40px;margin-left:-20px;margin-top:-20px}.fc-event.fc-selected{z-index:9999!important;box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event.fc-selected.fc-dragging{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-h-event.fc-selected:before{content:"";position:absolute;z-index:3;top:-10px;bottom:-10px;left:0;right:0}.fc-ltr .fc-h-event.fc-not-start,.fc-rtl .fc-h-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-h-event.fc-not-end,.fc-rtl .fc-h-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-ltr .fc-h-event .fc-start-resizer,.fc-rtl .fc-h-event .fc-end-resizer{cursor:w-resize;left:-1px}.fc-ltr .fc-h-event .fc-end-resizer,.fc-rtl .fc-h-event .fc-start-resizer{cursor:e-resize;right:-1px}.fc-h-event.fc-allow-mouse-resize .fc-resizer{width:7px;top:-1px;bottom:-1px}.fc-h-event.fc-selected .fc-resizer{border-radius:4px;border-width:1px;width:6px;height:6px;border-style:solid;border-color:inherit;background:#fff;top:50%;margin-top:-4px}.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,.fc-rtl .fc-h-event.fc-selected .fc-end-resizer{margin-left:-4px}.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,.fc-rtl .fc-h-event.fc-selected .fc-start-resizer{margin-right:-4px}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}tr:first-child>td>.fc-day-grid-event{margin-top:2px}.fc-day-grid-event.fc-selected:after{content:"";position:absolute;z-index:1;top:-1px;right:-1px;bottom:-1px;left:-1px;background:#000;opacity:.25}.fc-day-grid-event .fc-time{font-weight:700}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer{margin-left:-2px}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer{margin-right:-2px}a.fc-more{margin:1px 3px;font-size:.85em;cursor:pointer}a.fc-more:hover{text-decoration:underline}.fc-limited{display:none}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-bootstrap3 .fc-popover .panel-body,.fc-bootstrap4 .fc-popover .card-body{padding:0}.fc-now-indicator{position:absolute;border:0 solid red}.fc-bootstrap3 .fc-today.alert,.fc-bootstrap4 .fc-today.alert{border-radius:0}.fc-unselectable{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}.fc-unthemed .fc-content,.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-list-view,.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff;border-width:1px;border-style:solid}.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-popover .fc-header{background:#eee}.fc-unthemed td.fc-today{background:#fcf8e3}.fc-unthemed .fc-disabled-day{background:#d7d7d7;opacity:.3}.fc-icon{display:inline-block;height:1em;line-height:1em;font-size:1em;font-family:"Courier New",Courier,monospace;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fc-icon:after{position:relative}.fc-icon-left-single-arrow:after{content:"\2039";font-weight:700;font-size:200%;top:-7%}.fc-icon-right-single-arrow:after{content:"\203A";font-weight:700;font-size:200%;top:-7%}.fc-icon-left-double-arrow:after{content:"\AB";font-size:160%;top:-7%}.fc-icon-right-double-arrow:after{content:"\BB";font-size:160%;top:-7%}.fc-icon-left-triangle:after{content:"\25C4";font-size:125%;top:3%}.fc-icon-right-triangle:after{content:"\25BA";font-size:125%;top:3%}.fc-icon-down-triangle:after{content:"\25BC";font-size:125%;top:2%}.fc-icon-x:after{content:"\D7";font-size:200%;top:6%}.fc-unthemed .fc-popover .fc-header .fc-close{color:#666;font-size:.9em;margin-top:2px}.fc-unthemed .fc-list-item:hover td{background-color:#f5f5f5}.ui-widget .fc-disabled-day{background-image:none}.fc-bootstrap3 .fc-time-grid .fc-slats table,.fc-bootstrap4 .fc-time-grid .fc-slats table,.fc-time-grid .fc-slats .ui-widget-content{background:0 0}.fc-popover>.ui-widget-header+.ui-widget-content{border-top:0}.fc-bootstrap3 hr.fc-divider,.fc-bootstrap4 hr.fc-divider{border-color:inherit}.ui-widget .fc-event{color:#fff;font-weight:400}.ui-widget td.fc-axis{font-weight:400}.fc.fc-bootstrap3 a[data-goto]:hover{text-decoration:underline}.fc.fc-bootstrap4 a{text-decoration:none}.fc.fc-bootstrap4 a[data-goto]:hover{text-decoration:underline}.fc-bootstrap4 a.fc-event:not([href]):not([tabindex]){color:#fff}.fc-bootstrap4 .fc-popover.card{position:absolute}.fc-toolbar.fc-header-toolbar{margin-bottom:1em}.fc-toolbar.fc-footer-toolbar{margin-top:1em}.fc-toolbar .fc-left{float:left}.fc-toolbar .fc-right{float:right}.fc-toolbar .fc-center{display:inline-block}.fc .fc-toolbar>*>*{float:left;margin-left:.75em}.fc .fc-toolbar>*>:first-child{margin-left:0}.fc-toolbar h2{margin:0}.fc-toolbar button{position:relative}.fc-toolbar .fc-state-hover,.fc-toolbar .ui-state-hover{z-index:2}.fc-toolbar .fc-state-down{z-index:3}.fc-toolbar .fc-state-active,.fc-toolbar .ui-state-active{z-index:4}.fc-toolbar button:focus{z-index:5}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}.fc-basicDay-view .fc-content-skeleton,.fc-basicWeek-view .fc-content-skeleton{padding-bottom:1em}.fc-basic-view .fc-body .fc-row{min-height:4em}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-day-top.fc-other-month{opacity:.3}.fc-basic-view .fc-day-number,.fc-basic-view .fc-week-number{padding:2px}.fc-basic-view th.fc-day-number,.fc-basic-view th.fc-week-number{padding:0 2px}.fc-ltr .fc-basic-view .fc-day-top .fc-day-number{float:right}.fc-rtl .fc-basic-view .fc-day-top .fc-day-number{float:left}.fc-ltr .fc-basic-view .fc-day-top .fc-week-number{float:left;border-radius:0 0 3px}.fc-rtl .fc-basic-view .fc-day-top .fc-week-number{float:right;border-radius:0 0 0 3px}.fc-basic-view .fc-day-top .fc-week-number{min-width:1.5em;background-color:#f2f2f2;color:grey}.fc-basic-view td.fc-week-number>*{display:inline-block;min-width:1.25em}.fc-agenda-view .fc-day-grid{position:relative;z-index:2}.fc-agenda-view .fc-day-grid .fc-row{min-height:3em}.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton{padding-bottom:1em}.fc .fc-axis{vertical-align:middle;padding:0 4px}.fc-ltr .fc-axis{text-align:right}.fc-rtl .fc-axis{text-align:left}.fc-time-grid,.fc-time-grid-container{position:relative;z-index:1}.fc-time-grid{min-height:100%}.fc-time-grid table{border:0 hidden transparent}.fc-time-grid>.fc-bg{z-index:1}.fc-time-grid .fc-slats,.fc-time-grid>hr{position:relative;z-index:2}.fc-time-grid .fc-content-col{position:relative}.fc-time-grid .fc-content-skeleton{position:absolute;z-index:3;top:0;left:0;right:0}.fc-time-grid .fc-business-container{position:relative;z-index:1}.fc-time-grid .fc-bgevent-container{position:relative;z-index:2}.fc-time-grid .fc-highlight-container{z-index:3;position:relative}.fc-time-grid .fc-event-container{position:relative;z-index:4}.fc-time-grid .fc-now-indicator-line{z-index:5}.fc-time-grid .fc-helper-container{position:relative;z-index:6}.fc-time-grid .fc-slats td{height:1.5em;border-bottom:0}.fc-time-grid .fc-slats .fc-minor td{border-top-style:dotted}.fc-time-grid .fc-highlight{position:absolute;left:0;right:0}.fc-ltr .fc-time-grid .fc-event-container{margin:0 2.5% 0 2px}.fc-rtl .fc-time-grid .fc-event-container{margin:0 2px 0 2.5%}.fc-time-grid .fc-bgevent,.fc-time-grid .fc-event{position:absolute;z-index:1}.fc-time-grid .fc-bgevent{left:0;right:0}.fc-v-event.fc-not-start{border-top-width:0;padding-top:1px;border-top-left-radius:0;border-top-right-radius:0}.fc-v-event.fc-not-end{border-bottom-width:0;padding-bottom:1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-time-grid-event.fc-selected{overflow:visible}.fc-time-grid-event.fc-selected .fc-bg{display:none}.fc-time-grid-event .fc-content{overflow:hidden}.fc-time-grid-event .fc-time,.fc-time-grid-event .fc-title{padding:0 1px}.fc-time-grid-event .fc-time{font-size:.85em}.fc-time-grid-event.fc-short .fc-time,.fc-time-grid-event.fc-short .fc-title{display:inline-block;vertical-align:top}.fc-time-grid-event.fc-short .fc-time span{display:none}.fc-time-grid-event.fc-short .fc-time:before{content:attr(data-start)}.fc-time-grid-event.fc-short .fc-time:after{content:"\A0-\A0"}.fc-time-grid-event.fc-short .fc-title{font-size:.85em;padding:0}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer{left:0;right:0;bottom:0;height:8px;overflow:hidden;line-height:8px;font-size:11px;font-family:monospace;text-align:center;cursor:s-resize}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after{content:"="}.fc-time-grid-event.fc-selected .fc-resizer{border-radius:5px;border-width:1px;width:8px;height:8px;border-style:solid;border-color:inherit;background:#fff;left:50%;margin-left:-5px;bottom:-5px}.fc-time-grid .fc-now-indicator-line{border-top-width:1px;left:0;right:0}.fc-time-grid .fc-now-indicator-arrow{margin-top:-5px}.fc-ltr .fc-time-grid .fc-now-indicator-arrow{left:0;border-width:5px 0 5px 6px;border-top-color:transparent;border-bottom-color:transparent}.fc-rtl .fc-time-grid .fc-now-indicator-arrow{right:0;border-width:5px 6px 5px 0;border-top-color:transparent;border-bottom-color:transparent}.fc-event-dot{display:inline-block;width:10px;height:10px;border-radius:5px}.fc-rtl .fc-list-view{direction:rtl}.fc-list-view{border-width:1px;border-style:solid}.fc .fc-list-table{table-layout:auto}.fc-list-table td{border-width:1px 0 0;padding:8px 14px}.fc-list-table tr:first-child td{border-top-width:0}.fc-list-heading{border-bottom-width:1px}.fc-list-heading td{font-weight:700}.fc-ltr .fc-list-heading-main{float:left}.fc-ltr .fc-list-heading-alt,.fc-rtl .fc-list-heading-main{float:right}.fc-rtl .fc-list-heading-alt{float:left}.fc-list-item.fc-has-url{cursor:pointer}.fc-list-item-marker,.fc-list-item-time{width:1px}.fc-ltr .fc-list-item-marker{padding-right:0}.fc-rtl .fc-list-item-marker{padding-left:0}.fc-list-item-title a{text-decoration:none;color:inherit}.fc-list-item-title a[href]:hover{text-decoration:underline}.fc-list-empty-wrap2{position:absolute;top:0;left:0;right:0;bottom:0}.fc-list-empty-wrap1{width:100%;height:100%;display:table}.fc-list-empty{display:table-cell;vertical-align:middle;text-align:center}.fc-unthemed .fc-list-empty{background-color:#eee} \ No newline at end of file diff --git a/frappe/public/js/lib/fullcalendar/fullcalendar.min.js b/frappe/public/js/lib/fullcalendar/fullcalendar.min.js deleted file mode 100644 index b5c219bc23..0000000000 --- a/frappe/public/js/lib/fullcalendar/fullcalendar.min.js +++ /dev/null @@ -1,12 +0,0 @@ -/*! - * FullCalendar v3.10.2 - * Docs & License: https://fullcalendar.io/ - * (c) 2019 Adam Shaw - */ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("moment"),require("jquery")):"function"==typeof define&&define.amd?define(["moment","jquery"],e):"object"==typeof exports?exports.FullCalendar=e(require("moment"),require("jquery")):t.FullCalendar=e(t.moment,t.jQuery)}("undefined"!=typeof self?self:this,function(t,e){return function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=256)}([function(e,n){e.exports=t},,function(t,e){var n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])};e.__extends=function(t,e){function r(){this.constructor=t}n(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}},function(t,n){t.exports=e},function(t,e,n){function r(t,e){e.left&&t.css({"border-left-width":1,"margin-left":e.left-1}),e.right&&t.css({"border-right-width":1,"margin-right":e.right-1})}function i(t){t.css({"margin-left":"","margin-right":"","border-left-width":"","border-right-width":""})}function o(){ht("body").addClass("fc-not-allowed")}function s(){ht("body").removeClass("fc-not-allowed")}function a(t,e,n){var r=Math.floor(e/t.length),i=Math.floor(e-r*(t.length-1)),o=[],s=[],a=[],u=0;l(t),t.each(function(e,n){var l=e===t.length-1?i:r,d=ht(n).outerHeight(!0);d *").each(function(t,n){var r=ht(n).outerWidth();r>e&&(e=r)}),e++,t.width(e),e}function d(t,e){var n,r=t.add(e);return r.css({position:"relative",left:-1}),n=t.outerHeight()-e.outerHeight(),r.css({position:"",left:""}),n}function c(t){var e=t.css("position"),n=t.parents().filter(function(){var t=ht(this);return/(auto|scroll)/.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==e&&n.length?n:ht(t[0].ownerDocument||document)}function p(t,e){var n=t.offset(),r=n.left-(e?e.left:0),i=n.top-(e?e.top:0);return{left:r,right:r+t.outerWidth(),top:i,bottom:i+t.outerHeight()}}function h(t,e){var n=t.offset(),r=g(t),i=n.left+b(t,"border-left-width")+r.left-(e?e.left:0),o=n.top+b(t,"border-top-width")+r.top-(e?e.top:0);return{left:i,right:i+t[0].clientWidth,top:o,bottom:o+t[0].clientHeight}}function f(t,e){var n=t.offset(),r=n.left+b(t,"border-left-width")+b(t,"padding-left")-(e?e.left:0),i=n.top+b(t,"border-top-width")+b(t,"padding-top")-(e?e.top:0);return{left:r,right:r+t.width(),top:i,bottom:i+t.height()}}function g(t){var e,n=t[0].offsetWidth-t[0].clientWidth,r=t[0].offsetHeight-t[0].clientHeight;return n=v(n),r=v(r),e={left:0,right:0,top:0,bottom:r},y()&&"rtl"===t.css("direction")?e.left=n:e.right=n,e}function v(t){return t=Math.max(0,t),t=Math.round(t)}function y(){return null===ft&&(ft=m()),ft}function m(){var t=ht("
").css({position:"absolute",top:-1e3,left:0,border:0,padding:0,overflow:"scroll",direction:"rtl"}).appendTo("body"),e=t.children(),n=e.offset().left>t.offset().left;return t.remove(),n}function b(t,e){return parseFloat(t.css(e))||0}function w(t){return 1===t.which&&!t.ctrlKey}function D(t){var e=t.originalEvent.touches;return e&&e.length?e[0].pageX:t.pageX}function E(t){var e=t.originalEvent.touches;return e&&e.length?e[0].pageY:t.pageY}function S(t){return/^touch/.test(t.type)}function C(t){t.addClass("fc-unselectable").on("selectstart",T)}function R(t){t.removeClass("fc-unselectable").off("selectstart",T)}function T(t){t.preventDefault()}function M(t,e){var n={left:Math.max(t.left,e.left),right:Math.min(t.right,e.right),top:Math.max(t.top,e.top),bottom:Math.min(t.bottom,e.bottom)};return n.left=1&&ut(o)));r++);return i}function L(t,e){var n=k(t);return"week"===n&&"object"==typeof e&&e.days&&(n="day"),n}function V(t,e,n){return null!=n?n.diff(e,t,!0):pt.isDuration(e)?e.as(t):e.end.diff(e.start,t,!0)}function G(t,e,n){var r;return U(n)?(e-t)/n:(r=n.asMonths(),Math.abs(r)>=1&&ut(r)?e.diff(t,"months",!0)/r:e.diff(t,"days",!0)/n.asDays())}function N(t,e){var n,r;return U(t)||U(e)?t/e:(n=t.asMonths(),r=e.asMonths(),Math.abs(n)>=1&&ut(n)&&Math.abs(r)>=1&&ut(r)?n/r:t.asDays()/e.asDays())}function j(t,e){var n;return U(t)?pt.duration(t*e):(n=t.asMonths(),Math.abs(n)>=1&&ut(n)?pt.duration({months:n*e}):pt.duration({days:t.asDays()*e}))}function U(t){return Boolean(t.hours()||t.minutes()||t.seconds()||t.milliseconds())}function W(t){return"[object Date]"===Object.prototype.toString.call(t)||t instanceof Date}function q(t){return"string"==typeof t&&/^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(t)}function Y(){for(var t=[],e=0;e=0;o--)if("object"==typeof(s=t[o][r]))i.unshift(s);else if(void 0!==s){l[r]=s;break}i.length&&(l[r]=X(i))}for(n=t.length-1;n>=0;n--){a=t[n];for(r in a)r in l||(l[r]=a[r])}return l}function Q(t,e){for(var n in t)$(t,n)&&(e[n]=t[n])}function $(t,e){return gt.call(t,e)}function K(t,e,n){if(ht.isFunction(t)&&(t=[t]),t){var r=void 0,i=void 0;for(r=0;r/g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\n/g,"
")}function it(t){return t.replace(/&.*?;/g,"")}function ot(t){var e=[];return ht.each(t,function(t,n){null!=n&&e.push(t+":"+n)}),e.join(";")}function st(t){var e=[];return ht.each(t,function(t,n){null!=n&&e.push(t+'="'+rt(n)+'"')}),e.join(" ")}function at(t){return t.charAt(0).toUpperCase()+t.slice(1)}function lt(t,e){return t-e}function ut(t){return t%1==0}function dt(t,e){var n=t[e];return function(){return n.apply(t,arguments)}}function ct(t,e,n){void 0===n&&(n=!1);var r,i,o,s,a,l=function(){var u=+new Date-s;ua&&s.push(new t(a,o.startMs)),o.endMs>a&&(a=o.endMs);return at.startMs)&&(null==this.startMs||null==t.endMs||this.startMs=this.startMs)&&(null==this.endMs||null!=t.endMs&&t.endMs<=this.endMs)},t.prototype.containsDate=function(t){var e=t.valueOf();return(null==this.startMs||e>=this.startMs)&&(null==this.endMs||e=this.endMs&&(e=this.endMs-1),e},t.prototype.equals=function(t){return this.startMs===t.startMs&&this.endMs===t.endMs},t.prototype.clone=function(){var e=new t(this.startMs,this.endMs);return e.isStart=this.isStart,e.isEnd=this.isEnd,e},t.prototype.getStart=function(){return null!=this.startMs?o.default.utc(this.startMs).stripZone():null},t.prototype.getEnd=function(){return null!=this.endMs?o.default.utc(this.endMs).stripZone():null},t.prototype.as=function(t){return i.utc(this.endMs).diff(i.utc(this.startMs),t,!0)},t}();e.default=s},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(3),o=n(52),s=n(35),a=n(36),l=function(t){function e(n){var r=t.call(this)||this;return r.calendar=n,r.className=[],r.uid=String(e.uuid++),r}return r.__extends(e,t),e.parse=function(t,e){var n=new this(e);return!("object"!=typeof t||!n.applyProps(t))&&n},e.normalizeId=function(t){return t?String(t):null},e.prototype.fetch=function(t,e,n){},e.prototype.removeEventDefsById=function(t){},e.prototype.removeAllEventDefs=function(){},e.prototype.getPrimitive=function(t){},e.prototype.parseEventDefs=function(t){var e,n,r=[];for(e=0;e0},e}(o.default);e.default=s},function(t,e){Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t){this.view=t._getView(),this.component=t}return t.prototype.opt=function(t){return this.view.opt(t)},t.prototype.end=function(){},t}();e.default=n},function(t,e){Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(){}return t.mixInto=function(t){var e=this;Object.getOwnPropertyNames(this.prototype).forEach(function(n){t.prototype[n]||(t.prototype[n]=e.prototype[n])})},t.mixOver=function(t){var e=this;Object.getOwnPropertyNames(this.prototype).forEach(function(n){t.prototype[n]=e.prototype[n]})},t}();e.default=n},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(5),i=function(){function t(t,e,n){this.start=t,this.end=e||null,this.unzonedRange=this.buildUnzonedRange(n)}return t.parse=function(e,n){var r=e.start||e.date,i=e.end;if(!r)return!1;var o=n.calendar,s=o.moment(r),a=i?o.moment(i):null,l=e.allDay,u=o.opt("forceEventDuration");return!!s.isValid()&&(null==l&&null==(l=n.allDayDefault)&&(l=o.opt("allDayDefault")),!0===l?(s.stripTime(),a&&a.stripTime()):!1===l&&(s.hasTime()||s.time(0),a&&!a.hasTime()&&a.time(0)),!a||a.isValid()&&a.isAfter(s)||(a=null),!a&&u&&(a=o.getDefaultEventEnd(!s.hasTime(),s)),new t(s,a,o))},t.isStandardProp=function(t){return"start"===t||"date"===t||"end"===t||"allDay"===t},t.prototype.isAllDay=function(){return!(this.start.hasTime()||this.end&&this.end.hasTime())},t.prototype.buildUnzonedRange=function(t){var e=this.start.clone().stripZone().valueOf(),n=this.getEnd(t).stripZone().valueOf();return new r.default(e,n)},t.prototype.getEnd=function(t){return this.end?this.end.clone():t.getDefaultEventEnd(this.isAllDay(),this.start)},t}();e.default=i},function(t,e,n){function r(t,e){return!t&&!e||!(!t||!e)&&(t.component===e.component&&i(t,e)&&i(e,t))}function i(t,e){for(var n in t)if(!/^(component|left|right|top|bottom)$/.test(n)&&t[n]!==e[n])return!1;return!0}Object.defineProperty(e,"__esModule",{value:!0});var o=n(2),s=n(4),a=n(59),l=function(t){function e(e,n){var r=t.call(this,n)||this;return r.component=e,r}return o.__extends(e,t),e.prototype.handleInteractionStart=function(e){var n,r,i,o=this.subjectEl;this.component.hitsNeeded(),this.computeScrollBounds(),e?(r={left:s.getEvX(e),top:s.getEvY(e)},i=r,o&&(n=s.getOuterRect(o),i=s.constrainPoint(i,n)),this.origHit=this.queryHit(i.left,i.top),o&&this.options.subjectCenter&&(this.origHit&&(n=s.intersectRects(this.origHit,n)||n),i=s.getRectCenter(n)),this.coordAdjust=s.diffPoints(i,r)):(this.origHit=null,this.coordAdjust=null),t.prototype.handleInteractionStart.call(this,e)},e.prototype.handleDragStart=function(e){var n;t.prototype.handleDragStart.call(this,e),(n=this.queryHit(s.getEvX(e),s.getEvY(e)))&&this.handleHitOver(n)},e.prototype.handleDrag=function(e,n,i){var o;t.prototype.handleDrag.call(this,e,n,i),o=this.queryHit(s.getEvX(i),s.getEvY(i)),r(o,this.hit)||(this.hit&&this.handleHitOut(),o&&this.handleHitOver(o))},e.prototype.handleDragEnd=function(e){this.handleHitDone(),t.prototype.handleDragEnd.call(this,e)},e.prototype.handleHitOver=function(t){var e=r(t,this.origHit);this.hit=t,this.trigger("hitOver",this.hit,e,this.origHit)},e.prototype.handleHitOut=function(){this.hit&&(this.trigger("hitOut",this.hit),this.handleHitDone(),this.hit=null)},e.prototype.handleHitDone=function(){this.hit&&this.trigger("hitDone",this.hit)},e.prototype.handleInteractionEnd=function(e,n){t.prototype.handleInteractionEnd.call(this,e,n),this.origHit=null,this.hit=null,this.component.hitsNotNeeded()},e.prototype.handleScrollEnd=function(){t.prototype.handleScrollEnd.call(this),this.isDragging&&(this.component.releaseHits(),this.component.prepareHits())},e.prototype.queryHit=function(t,e){return this.coordAdjust&&(t+=this.coordAdjust.left,e+=this.coordAdjust.top),this.component.queryHit(t,e)},e}(a.default);e.default=l},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0}),e.version="3.10.2",e.internalApiVersion=12;var r=n(4);e.applyAll=r.applyAll,e.debounce=r.debounce,e.isInt=r.isInt,e.htmlEscape=r.htmlEscape,e.cssToStr=r.cssToStr,e.proxy=r.proxy,e.capitaliseFirstLetter=r.capitaliseFirstLetter,e.getOuterRect=r.getOuterRect,e.getClientRect=r.getClientRect,e.getContentRect=r.getContentRect,e.getScrollbarWidths=r.getScrollbarWidths,e.preventDefault=r.preventDefault,e.parseFieldSpecs=r.parseFieldSpecs,e.compareByFieldSpecs=r.compareByFieldSpecs,e.compareByFieldSpec=r.compareByFieldSpec,e.flexibleCompare=r.flexibleCompare,e.computeGreatestUnit=r.computeGreatestUnit,e.divideRangeByDuration=r.divideRangeByDuration,e.divideDurationByDuration=r.divideDurationByDuration,e.multiplyDuration=r.multiplyDuration,e.durationHasTime=r.durationHasTime,e.log=r.log,e.warn=r.warn,e.removeExact=r.removeExact,e.intersectRects=r.intersectRects,e.allowSelection=r.allowSelection,e.attrsToStr=r.attrsToStr,e.compareNumbers=r.compareNumbers,e.compensateScroll=r.compensateScroll,e.computeDurationGreatestUnit=r.computeDurationGreatestUnit,e.constrainPoint=r.constrainPoint,e.copyOwnProps=r.copyOwnProps,e.diffByUnit=r.diffByUnit,e.diffDay=r.diffDay,e.diffDayTime=r.diffDayTime,e.diffPoints=r.diffPoints,e.disableCursor=r.disableCursor,e.distributeHeight=r.distributeHeight,e.enableCursor=r.enableCursor,e.firstDefined=r.firstDefined,e.getEvIsTouch=r.getEvIsTouch,e.getEvX=r.getEvX,e.getEvY=r.getEvY,e.getRectCenter=r.getRectCenter,e.getScrollParent=r.getScrollParent,e.hasOwnProp=r.hasOwnProp,e.isArraysEqual=r.isArraysEqual,e.isNativeDate=r.isNativeDate,e.isPrimaryMouseButton=r.isPrimaryMouseButton,e.isTimeString=r.isTimeString,e.matchCellWidths=r.matchCellWidths,e.mergeProps=r.mergeProps,e.preventSelection=r.preventSelection,e.removeMatching=r.removeMatching,e.stripHtmlEntities=r.stripHtmlEntities,e.subtractInnerElHeight=r.subtractInnerElHeight,e.uncompensateScroll=r.uncompensateScroll,e.undistributeHeight=r.undistributeHeight,e.dayIDs=r.dayIDs,e.unitsDesc=r.unitsDesc;var i=n(49);e.formatDate=i.formatDate,e.formatRange=i.formatRange,e.queryMostGranularFormatUnit=i.queryMostGranularFormatUnit;var o=n(32);e.datepickerLocale=o.datepickerLocale,e.locale=o.locale,e.getMomentLocaleData=o.getMomentLocaleData,e.populateInstanceComputableOptions=o.populateInstanceComputableOptions;var s=n(19);e.eventDefsToEventInstances=s.eventDefsToEventInstances,e.eventFootprintToComponentFootprint=s.eventFootprintToComponentFootprint,e.eventInstanceToEventRange=s.eventInstanceToEventRange,e.eventInstanceToUnzonedRange=s.eventInstanceToUnzonedRange,e.eventRangeToEventFootprint=s.eventRangeToEventFootprint;var a=n(11);e.moment=a.default;var l=n(13);e.EmitterMixin=l.default;var u=n(7);e.ListenerMixin=u.default;var d=n(51);e.Model=d.default;var c=n(217);e.Constraints=c.default;var p=n(55);e.DateProfileGenerator=p.default;var h=n(5);e.UnzonedRange=h.default;var f=n(12);e.ComponentFootprint=f.default;var g=n(218);e.BusinessHourGenerator=g.default;var v=n(219);e.EventPeriod=v.default;var y=n(220);e.EventManager=y.default;var m=n(37);e.EventDef=m.default;var b=n(39);e.EventDefMutation=b.default;var w=n(36);e.EventDefParser=w.default;var D=n(53);e.EventInstance=D.default;var E=n(50);e.EventRange=E.default;var S=n(54);e.RecurringEventDef=S.default;var C=n(9);e.SingleEventDef=C.default;var R=n(40);e.EventDefDateMutation=R.default;var T=n(16);e.EventDateProfile=T.default;var M=n(38);e.EventSourceParser=M.default;var I=n(6);e.EventSource=I.default;var H=n(57);e.defineThemeSystem=H.defineThemeSystem,e.getThemeSystemClass=H.getThemeSystemClass;var P=n(20);e.EventInstanceGroup=P.default;var _=n(56);e.ArrayEventSource=_.default;var x=n(223);e.FuncEventSource=x.default;var O=n(224);e.JsonFeedEventSource=O.default;var F=n(34);e.EventFootprint=F.default;var z=n(35);e.Class=z.default;var B=n(15);e.Mixin=B.default;var A=n(58);e.CoordCache=A.default;var k=n(225);e.Iterator=k.default;var L=n(59);e.DragListener=L.default;var V=n(17);e.HitDragListener=V.default;var G=n(226);e.MouseFollower=G.default;var N=n(52);e.ParsableModelMixin=N.default;var j=n(227);e.Popover=j.default;var U=n(21);e.Promise=U.default;var W=n(228);e.TaskQueue=W.default;var q=n(229);e.RenderQueue=q.default;var Y=n(41);e.Scroller=Y.default;var Z=n(22);e.Theme=Z.default;var X=n(230);e.Component=X.default;var Q=n(231);e.DateComponent=Q.default;var $=n(42);e.InteractiveDateComponent=$.default;var K=n(232);e.Calendar=K.default;var J=n(43);e.View=J.default;var tt=n(24);e.defineView=tt.defineView,e.getViewConfig=tt.getViewConfig;var et=n(60);e.DayTableMixin=et.default;var nt=n(61);e.BusinessHourRenderer=nt.default;var rt=n(44);e.EventRenderer=rt.default;var it=n(62);e.FillRenderer=it.default;var ot=n(63);e.HelperRenderer=ot.default;var st=n(233);e.ExternalDropping=st.default;var at=n(234);e.EventResizing=at.default;var lt=n(64);e.EventPointing=lt.default;var ut=n(235);e.EventDragging=ut.default;var dt=n(236);e.DateSelecting=dt.default;var ct=n(237);e.DateClicking=ct.default;var pt=n(14);e.Interaction=pt.default;var ht=n(65);e.StandardInteractionsMixin=ht.default;var ft=n(238);e.AgendaView=ft.default;var gt=n(239);e.TimeGrid=gt.default;var vt=n(240);e.TimeGridEventRenderer=vt.default;var yt=n(242);e.TimeGridFillRenderer=yt.default;var mt=n(241);e.TimeGridHelperRenderer=mt.default;var bt=n(66);e.DayGrid=bt.default;var wt=n(243);e.DayGridEventRenderer=wt.default;var Dt=n(245);e.DayGridFillRenderer=Dt.default;var Et=n(244);e.DayGridHelperRenderer=Et.default;var St=n(67);e.BasicView=St.default;var Ct=n(68);e.BasicViewDateProfileGenerator=Ct.default;var Rt=n(246);e.MonthView=Rt.default;var Tt=n(247);e.MonthViewDateProfileGenerator=Tt.default;var Mt=n(248);e.ListView=Mt.default;var It=n(250);e.ListEventPointing=It.default;var Ht=n(249);e.ListEventRenderer=Ht.default},function(t,e,n){function r(t,e){var n,r=[];for(n=0;n
')},e.prototype.clear=function(){this.setHeight("auto"),this.applyOverflow()},e.prototype.destroy=function(){this.el.remove()},e.prototype.applyOverflow=function(){this.scrollEl.css({"overflow-x":this.overflowX,"overflow-y":this.overflowY})},e.prototype.lockOverflow=function(t){var e=this.overflowX,n=this.overflowY;t=t||this.getScrollbarWidths(),"auto"===e&&(e=t.top||t.bottom||this.scrollEl[0].scrollWidth-1>this.scrollEl[0].clientWidth?"scroll":"hidden"),"auto"===n&&(n=t.left||t.right||this.scrollEl[0].scrollHeight-1>this.scrollEl[0].clientHeight?"scroll":"hidden"),this.scrollEl.css({"overflow-x":e,"overflow-y":n})},e.prototype.setHeight=function(t){this.scrollEl.height(t)},e.prototype.getScrollTop=function(){return this.scrollEl.scrollTop()},e.prototype.setScrollTop=function(t){this.scrollEl.scrollTop(t)},e.prototype.getClientWidth=function(){return this.scrollEl[0].clientWidth},e.prototype.getClientHeight=function(){return this.scrollEl[0].clientHeight},e.prototype.getScrollbarWidths=function(){return o.getScrollbarWidths(this.scrollEl)},e}(s.default);e.default=a},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(3),o=n(4),s=n(231),a=n(23),l=function(t){function e(e,n){var r=t.call(this,e,n)||this;return r.segSelector=".fc-event-container > *",r.dateSelectingClass&&(r.dateClicking=new r.dateClickingClass(r)),r.dateSelectingClass&&(r.dateSelecting=new r.dateSelectingClass(r)),r.eventPointingClass&&(r.eventPointing=new r.eventPointingClass(r)),r.eventDraggingClass&&r.eventPointing&&(r.eventDragging=new r.eventDraggingClass(r,r.eventPointing)),r.eventResizingClass&&r.eventPointing&&(r.eventResizing=new r.eventResizingClass(r,r.eventPointing)),r.externalDroppingClass&&(r.externalDropping=new r.externalDroppingClass(r)),r}return r.__extends(e,t),e.prototype.setElement=function(e){t.prototype.setElement.call(this,e),this.dateClicking&&this.dateClicking.bindToEl(e),this.dateSelecting&&this.dateSelecting.bindToEl(e),this.bindAllSegHandlersToEl(e)},e.prototype.removeElement=function(){this.endInteractions(),t.prototype.removeElement.call(this)},e.prototype.executeEventUnrender=function(){this.endInteractions(),t.prototype.executeEventUnrender.call(this)},e.prototype.bindGlobalHandlers=function(){t.prototype.bindGlobalHandlers.call(this),this.externalDropping&&this.externalDropping.bindToDocument()},e.prototype.unbindGlobalHandlers=function(){t.prototype.unbindGlobalHandlers.call(this),this.externalDropping&&this.externalDropping.unbindFromDocument()},e.prototype.bindDateHandlerToEl=function(t,e,n){var r=this;this.el.on(e,function(t){if(!i(t.target).is(r.segSelector+":not(.fc-helper),"+r.segSelector+":not(.fc-helper) *,.fc-more,a[data-goto]"))return n.call(r,t)})},e.prototype.bindAllSegHandlersToEl=function(t){[this.eventPointing,this.eventDragging,this.eventResizing].forEach(function(e){e&&e.bindToEl(t)})},e.prototype.bindSegHandlerToEl=function(t,e,n){var r=this;t.on(e,this.segSelector,function(t){var e=i(t.currentTarget);if(!e.is(".fc-helper")){var o=e.data("fc-seg");if(o&&!r.shouldIgnoreEventPointing())return n.call(r,o,t)}})},e.prototype.shouldIgnoreMouse=function(){return a.default.get().shouldIgnoreMouse()},e.prototype.shouldIgnoreTouch=function(){var t=this._getView();return t.isSelected||t.selectedEvent},e.prototype.shouldIgnoreEventPointing=function(){return this.eventDragging&&this.eventDragging.isDragging||this.eventResizing&&this.eventResizing.isResizing},e.prototype.canStartSelection=function(t,e){return o.getEvIsTouch(e)&&!this.canStartResize(t,e)&&(this.isEventDefDraggable(t.footprint.eventDef)||this.isEventDefResizable(t.footprint.eventDef))},e.prototype.canStartDrag=function(t,e){return!this.canStartResize(t,e)&&this.isEventDefDraggable(t.footprint.eventDef)},e.prototype.canStartResize=function(t,e){var n=this._getView(),r=t.footprint.eventDef;return(!o.getEvIsTouch(e)||n.isEventDefSelected(r))&&this.isEventDefResizable(r)&&i(e.target).is(".fc-resizer")},e.prototype.endInteractions=function(){[this.dateClicking,this.dateSelecting,this.eventPointing,this.eventDragging,this.eventResizing].forEach(function(t){t&&t.end()})},e.prototype.isEventDefDraggable=function(t){return this.isEventDefStartEditable(t)},e.prototype.isEventDefStartEditable=function(t){var e=t.isStartExplicitlyEditable();return null==e&&null==(e=this.opt("eventStartEditable"))&&(e=this.isEventDefGenerallyEditable(t)),e},e.prototype.isEventDefGenerallyEditable=function(t){var e=t.isExplicitlyEditable();return null==e&&(e=this.opt("editable")),e},e.prototype.isEventDefResizableFromStart=function(t){return this.opt("eventResizableFromStart")&&this.isEventDefResizable(t)},e.prototype.isEventDefResizableFromEnd=function(t){return this.isEventDefResizable(t)},e.prototype.isEventDefResizable=function(t){var e=t.isDurationExplicitlyEditable();return null==e&&null==(e=this.opt("eventDurationEditable"))&&(e=this.isEventDefGenerallyEditable(t)),e},e.prototype.diffDates=function(t,e){return this.largeUnit?o.diffByUnit(t,e,this.largeUnit):o.diffDayTime(t,e)},e.prototype.isEventInstanceGroupAllowed=function(t){var e,n=this._getView(),r=this.dateProfile,i=this.eventRangesToEventFootprints(t.getAllEventRanges());for(e=0;e1?"ll":"LL"},e.prototype.setDate=function(t){var e=this.get("dateProfile"),n=this.dateProfileGenerator.build(t,void 0,!0);e&&e.activeUnzonedRange.equals(n.activeUnzonedRange)||this.set("dateProfile",n)},e.prototype.unsetDate=function(){this.unset("dateProfile")},e.prototype.fetchInitialEvents=function(t){var e=this.calendar,n=t.isRangeAllDay&&!this.usesMinMaxTime;return e.requestEvents(e.msToMoment(t.activeUnzonedRange.startMs,n),e.msToMoment(t.activeUnzonedRange.endMs,n))},e.prototype.bindEventChanges=function(){this.listenTo(this.calendar,"eventsReset",this.resetEvents)},e.prototype.unbindEventChanges=function(){this.stopListeningTo(this.calendar,"eventsReset")},e.prototype.setEvents=function(t){this.set("currentEvents",t),this.set("hasEvents",!0)},e.prototype.unsetEvents=function(){this.unset("currentEvents"),this.unset("hasEvents")},e.prototype.resetEvents=function(t){this.startBatchRender(),this.unsetEvents(),this.setEvents(t),this.stopBatchRender()},e.prototype.requestDateRender=function(t){var e=this;this.requestRender(function(){e.executeDateRender(t)},"date","init")},e.prototype.requestDateUnrender=function(){var t=this;this.requestRender(function(){t.executeDateUnrender()},"date","destroy")},e.prototype.executeDateRender=function(e){t.prototype.executeDateRender.call(this,e),this.render&&this.render(),this.trigger("datesRendered"),this.addScroll({isDateInit:!0}),this.startNowIndicator()},e.prototype.executeDateUnrender=function(){this.unselect(),this.stopNowIndicator(),this.trigger("before:datesUnrendered"),this.destroy&&this.destroy(),t.prototype.executeDateUnrender.call(this)},e.prototype.bindBaseRenderHandlers=function(){var t=this;this.on("datesRendered",function(){t.whenSizeUpdated(t.triggerViewRender)}),this.on("before:datesUnrendered",function(){t.triggerViewDestroy()})},e.prototype.triggerViewRender=function(){this.publiclyTrigger("viewRender",{context:this,args:[this,this.el]})},e.prototype.triggerViewDestroy=function(){this.publiclyTrigger("viewDestroy",{context:this,args:[this,this.el]})},e.prototype.requestEventsRender=function(t){var e=this;this.requestRender(function(){e.executeEventRender(t),e.whenSizeUpdated(e.triggerAfterEventsRendered)},"event","init")},e.prototype.requestEventsUnrender=function(){var t=this;this.requestRender(function(){t.triggerBeforeEventsDestroyed(),t.executeEventUnrender()},"event","destroy")},e.prototype.requestBusinessHoursRender=function(t){var e=this;this.requestRender(function(){e.renderBusinessHours(t)},"businessHours","init")},e.prototype.requestBusinessHoursUnrender=function(){var t=this;this.requestRender(function(){t.unrenderBusinessHours()},"businessHours","destroy")},e.prototype.bindGlobalHandlers=function(){t.prototype.bindGlobalHandlers.call(this),this.listenTo(d.default.get(),{touchstart:this.processUnselect,mousedown:this.handleDocumentMousedown})},e.prototype.unbindGlobalHandlers=function(){t.prototype.unbindGlobalHandlers.call(this),this.stopListeningTo(d.default.get())},e.prototype.startNowIndicator=function(){var t,e,n,r=this;this.opt("nowIndicator")&&(t=this.getNowIndicatorUnit())&&(e=s.proxy(this,"updateNowIndicator"),this.initialNowDate=this.calendar.getNow(),this.initialNowQueriedMs=(new Date).valueOf(),n=this.initialNowDate.clone().startOf(t).add(1,t).valueOf()-this.initialNowDate.valueOf(),this.nowIndicatorTimeoutID=setTimeout(function(){r.nowIndicatorTimeoutID=null,e(),n=+o.duration(1,t),n=Math.max(100,n),r.nowIndicatorIntervalID=setInterval(e,n)},n))},e.prototype.updateNowIndicator=function(){this.isDatesRendered&&this.initialNowDate&&(this.unrenderNowIndicator(),this.renderNowIndicator(this.initialNowDate.clone().add((new Date).valueOf()-this.initialNowQueriedMs)),this.isNowIndicatorRendered=!0)},e.prototype.stopNowIndicator=function(){this.isNowIndicatorRendered&&(this.nowIndicatorTimeoutID&&(clearTimeout(this.nowIndicatorTimeoutID),this.nowIndicatorTimeoutID=null),this.nowIndicatorIntervalID&&(clearInterval(this.nowIndicatorIntervalID),this.nowIndicatorIntervalID=null),this.unrenderNowIndicator(),this.isNowIndicatorRendered=!1)},e.prototype.updateSize=function(e,n,r){this.setHeight?this.setHeight(e,n):t.prototype.updateSize.call(this,e,n,r),this.updateNowIndicator()},e.prototype.addScroll=function(t){var e=this.queuedScroll||(this.queuedScroll={});i.extend(e,t)},e.prototype.popScroll=function(){this.applyQueuedScroll(),this.queuedScroll=null},e.prototype.applyQueuedScroll=function(){this.queuedScroll&&this.applyScroll(this.queuedScroll)},e.prototype.queryScroll=function(){var t={};return this.isDatesRendered&&i.extend(t,this.queryDateScroll()),t},e.prototype.applyScroll=function(t){t.isDateInit&&this.isDatesRendered&&i.extend(t,this.computeInitialDateScroll()),this.isDatesRendered&&this.applyDateScroll(t)},e.prototype.computeInitialDateScroll=function(){return{}},e.prototype.queryDateScroll=function(){return{}},e.prototype.applyDateScroll=function(t){},e.prototype.reportEventDrop=function(t,e,n,r){var i=this.calendar.eventManager,s=i.mutateEventsWithId(t.def.id,e),a=e.dateMutation;a&&(t.dateProfile=a.buildNewDateProfile(t.dateProfile,this.calendar)),this.triggerEventDrop(t,a&&a.dateDelta||o.duration(),s,n,r)},e.prototype.triggerEventDrop=function(t,e,n,r,i){this.publiclyTrigger("eventDrop",{context:r[0],args:[t.toLegacy(),e,n,i,{},this]})},e.prototype.reportExternalDrop=function(t,e,n,r,i,o){e&&this.calendar.eventManager.addEventDef(t,n),this.triggerExternalDrop(t,e,r,i,o)},e.prototype.triggerExternalDrop=function(t,e,n,r,i){this.publiclyTrigger("drop",{context:n[0],args:[t.dateProfile.start.clone(),r,i,this]}),e&&this.publiclyTrigger("eventReceive",{context:this,args:[t.buildInstance().toLegacy(),this]})},e.prototype.reportEventResize=function(t,e,n,r){var i=this.calendar.eventManager,o=i.mutateEventsWithId(t.def.id,e);t.dateProfile=e.dateMutation.buildNewDateProfile(t.dateProfile,this.calendar);var s=e.dateMutation.endDelta||e.dateMutation.startDelta;this.triggerEventResize(t,s,o,n,r)},e.prototype.triggerEventResize=function(t,e,n,r,i){this.publiclyTrigger("eventResize",{context:r[0],args:[t.toLegacy(),e,n,i,{},this]})},e.prototype.select=function(t,e){this.unselect(e),this.renderSelectionFootprint(t),this.reportSelection(t,e)},e.prototype.renderSelectionFootprint=function(e){this.renderSelection?this.renderSelection(e.toLegacy(this.calendar)):t.prototype.renderSelectionFootprint.call(this,e)},e.prototype.reportSelection=function(t,e){this.isSelected=!0,this.triggerSelect(t,e)},e.prototype.triggerSelect=function(t,e){var n=this.calendar.footprintToDateProfile(t);this.publiclyTrigger("select",{context:this,args:[n.start,n.end,e,this]})},e.prototype.unselect=function(t){this.isSelected&&(this.isSelected=!1,this.destroySelection&&this.destroySelection(),this.unrenderSelection(),this.publiclyTrigger("unselect",{context:this,args:[t,this]}))},e.prototype.selectEventInstance=function(t){this.selectedEventInstance&&this.selectedEventInstance===t||(this.unselectEventInstance(),this.getEventSegs().forEach(function(e){e.footprint.eventInstance===t&&e.el&&e.el.addClass("fc-selected")}),this.selectedEventInstance=t)},e.prototype.unselectEventInstance=function(){this.selectedEventInstance&&(this.getEventSegs().forEach(function(t){t.el&&t.el.removeClass("fc-selected")}),this.selectedEventInstance=null)},e.prototype.isEventDefSelected=function(t){return this.selectedEventInstance&&this.selectedEventInstance.def.id===t.id},e.prototype.handleDocumentMousedown=function(t){s.isPrimaryMouseButton(t)&&this.processUnselect(t)},e.prototype.processUnselect=function(t){this.processRangeUnselect(t),this.processEventUnselect(t)},e.prototype.processRangeUnselect=function(t){var e;this.isSelected&&this.opt("unselectAuto")&&((e=this.opt("unselectCancel"))&&i(t.target).closest(e).length||this.unselect(t))},e.prototype.processEventUnselect=function(t){this.selectedEventInstance&&(i(t.target).closest(".fc-selected").length||this.unselectEventInstance())},e.prototype.triggerBaseRendered=function(){this.publiclyTrigger("viewRender",{context:this,args:[this,this.el]})},e.prototype.triggerBaseUnrendered=function(){this.publiclyTrigger("viewDestroy",{context:this,args:[this,this.el]})},e.prototype.triggerDayClick=function(t,e,n){var r=this.calendar.footprintToDateProfile(t);this.publiclyTrigger("dayClick",{context:e,args:[r.start,n,this]})},e.prototype.isDateInOtherMonth=function(t,e){return!1},e.prototype.getUnzonedRangeOption=function(t){var e=this.opt(t);if("function"==typeof e&&(e=e.apply(null,Array.prototype.slice.call(arguments,1))),e)return this.calendar.parseUnzonedRange(e)},e.prototype.initHiddenDays=function(){var t,e=this.opt("hiddenDays")||[],n=[],r=0;for(!1===this.opt("weekends")&&e.push(0,6),t=0;t<7;t++)(n[t]=-1!==i.inArray(t,e))||r++;if(!r)throw new Error("invalid hiddenDays");this.isHiddenDayHash=n},e.prototype.trimHiddenDays=function(t){var e=t.getStart(),n=t.getEnd();return e&&(e=this.skipHiddenDays(e)),n&&(n=this.skipHiddenDays(n,-1,!0)),null===e||null===n||eo&&(!l[s]||u.isSame(d,l[s]))&&(s-1!==o||"."!==c[s]);s--)v=c[s]+v;for(a=o;a<=s;a++)y+=c[a],m+=p[a];return(y||m)&&(b=i?m+r+y:y+r+m),g(h+b+v)}function a(t){return C[t]||(C[t]=l(t))}function l(t){var e=u(t);return{fakeFormatString:c(e),sameUnits:p(e)}}function u(t){for(var e,n=[],r=/\[([^\]]*)\]|\(([^\)]*)\)|(LTS|LT|(\w)\4*o?)|([^\w\[\(]+)/g;e=r.exec(t);)e[1]?n.push.apply(n,d(e[1])):e[2]?n.push({maybe:u(e[2])}):e[3]?n.push({token:e[3]}):e[5]&&n.push.apply(n,d(e[5]));return n}function d(t){return". "===t?["."," "]:[t]}function c(t){var e,n,r=[];for(e=0;ei.value)&&(i=r);return i?i.unit:null}Object.defineProperty(e,"__esModule",{value:!0});var y=n(11);y.newMomentProto.format=function(){return this._fullCalendar&&arguments[0]?i(this,arguments[0]):this._ambigTime?y.oldMomentFormat(r(this),"YYYY-MM-DD"):this._ambigZone?y.oldMomentFormat(r(this),"YYYY-MM-DD[T]HH:mm:ss"):this._fullCalendar?y.oldMomentFormat(r(this)):y.oldMomentProto.format.apply(this,arguments)},y.newMomentProto.toISOString=function(){return this._ambigTime?y.oldMomentFormat(r(this),"YYYY-MM-DD"):this._ambigZone?y.oldMomentFormat(r(this),"YYYY-MM-DD[T]HH:mm:ss"):this._fullCalendar?y.oldMomentProto.toISOString.apply(r(this),arguments):y.oldMomentProto.toISOString.apply(this,arguments)};var m="\v",b="",w="",D=new RegExp(w+"([^"+w+"]*)"+w,"g"),E={t:function(t){return y.oldMomentFormat(t,"a").charAt(0)},T:function(t){return y.oldMomentFormat(t,"A").charAt(0)}},S={Y:{value:1,unit:"year"},M:{value:2,unit:"month"},W:{value:3,unit:"week"},w:{value:3,unit:"week"},D:{value:4,unit:"day"},d:{value:4,unit:"day"}};e.formatDate=i,e.formatRange=o;var C={};e.queryMostGranularFormatUnit=v},function(t,e){Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t,e,n){this.unzonedRange=t,this.eventDef=e,n&&(this.eventInstance=n)}return t}();e.default=n},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(35),o=n(13),s=n(7),a=function(t){function e(){var e=t.call(this)||this;return e._watchers={},e._props={},e.applyGlobalWatchers(),e.constructed(),e}return r.__extends(e,t),e.watch=function(t){for(var e=[],n=1;n864e5&&i.time(n-864e5)),new o.default(r,i)},t.prototype.buildRangeFromDuration=function(t,e,n,s){function a(){d=t.clone().startOf(h),c=d.clone().add(n),p=new o.default(d,c)}var l,u,d,c,p,h=this.opt("dateAlignment");return h||(l=this.opt("dateIncrement"),l?(u=r.duration(l),h=u0&&(t=this.els.eq(0).offsetParent()),this.origin=t?t.offset():null,this.boundingRect=this.queryBoundingRect(),this.isHorizontal&&this.buildElHorizontals(),this.isVertical&&this.buildElVerticals()},t.prototype.clear=function(){this.origin=null,this.boundingRect=null,this.lefts=null,this.rights=null,this.tops=null,this.bottoms=null},t.prototype.ensureBuilt=function(){this.origin||this.build()},t.prototype.buildElHorizontals=function(){var t=[],e=[];this.els.each(function(n,i){var o=r(i),s=o.offset().left,a=o.outerWidth();t.push(s),e.push(s+a)}),this.lefts=t,this.rights=e},t.prototype.buildElVerticals=function(){var t=[],e=[];this.els.each(function(n,i){var o=r(i),s=o.offset().top,a=o.outerHeight();t.push(s),e.push(s+a)}),this.tops=t,this.bottoms=e},t.prototype.getHorizontalIndex=function(t){this.ensureBuilt();var e,n=this.lefts,r=this.rights,i=n.length;for(e=0;e=n[e]&&t=n[e]&&t0&&(t=i.getScrollParent(this.els.eq(0)),!t.is(document)&&!t.is("html,body"))?i.getClientRect(t):null},t.prototype.isPointInBounds=function(t,e){return this.isLeftInBounds(t)&&this.isTopInBounds(e)},t.prototype.isLeftInBounds=function(t){return!this.boundingRect||t>=this.boundingRect.left&&t=this.boundingRect.top&&t=r*r&&this.handleDistanceSurpassed(t),this.isDragging&&this.handleDrag(e,n,t)},t.prototype.handleDrag=function(t,e,n){this.trigger("drag",t,e,n),this.updateAutoScroll(n)},t.prototype.endDrag=function(t){this.isDragging&&(this.isDragging=!1,this.handleDragEnd(t))},t.prototype.handleDragEnd=function(t){this.trigger("dragEnd",t)},t.prototype.startDelay=function(t){var e=this;this.delay?this.delayTimeoutId=setTimeout(function(){e.handleDelayEnd(t)},this.delay):this.handleDelayEnd(t)},t.prototype.handleDelayEnd=function(t){this.isDelayEnded=!0,this.isDistanceSurpassed&&this.startDrag(t)},t.prototype.handleDistanceSurpassed=function(t){this.isDistanceSurpassed=!0,this.isDelayEnded&&this.startDrag(t)},t.prototype.handleTouchMove=function(t){this.isDragging&&this.shouldCancelTouchScroll&&t.preventDefault(),this.handleMove(t)},t.prototype.handleMouseMove=function(t){this.handleMove(t)},t.prototype.handleTouchScroll=function(t){this.isDragging&&!this.scrollAlwaysKills||this.endInteraction(t,!0)},t.prototype.trigger=function(t){for(var e=[],n=1;n=0&&e<=1?l=e*this.scrollSpeed*-1:n>=0&&n<=1&&(l=n*this.scrollSpeed),r>=0&&r<=1?u=r*this.scrollSpeed*-1:o>=0&&o<=1&&(u=o*this.scrollSpeed)),this.setScrollVel(l,u)},t.prototype.setScrollVel=function(t,e){this.scrollTopVel=t,this.scrollLeftVel=e,this.constrainScrollVel(),!this.scrollTopVel&&!this.scrollLeftVel||this.scrollIntervalId||(this.scrollIntervalId=setInterval(i.proxy(this,"scrollIntervalFunc"),this.scrollIntervalMs))},t.prototype.constrainScrollVel=function(){var t=this.scrollEl;this.scrollTopVel<0?t.scrollTop()<=0&&(this.scrollTopVel=0):this.scrollTopVel>0&&t.scrollTop()+t[0].clientHeight>=t[0].scrollHeight&&(this.scrollTopVel=0),this.scrollLeftVel<0?t.scrollLeft()<=0&&(this.scrollLeftVel=0):this.scrollLeftVel>0&&t.scrollLeft()+t[0].clientWidth>=t[0].scrollWidth&&(this.scrollLeftVel=0)},t.prototype.scrollIntervalFunc=function(){var t=this.scrollEl,e=this.scrollIntervalMs/1e3;this.scrollTopVel&&t.scrollTop(t.scrollTop()+this.scrollTopVel*e),this.scrollLeftVel&&t.scrollLeft(t.scrollLeft()+this.scrollLeftVel*e),this.constrainScrollVel(),this.scrollTopVel||this.scrollLeftVel||this.endAutoScroll()},t.prototype.endAutoScroll=function(){this.scrollIntervalId&&(clearInterval(this.scrollIntervalId),this.scrollIntervalId=null,this.handleScrollEnd())},t.prototype.handleDebouncedScroll=function(){this.scrollIntervalId||this.handleScrollEnd()},t.prototype.handleScrollEnd=function(){},t}();e.default=a,o.default.mixInto(a)},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(4),o=n(15),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r.__extends(e,t),e.prototype.updateDayTable=function(){for(var t,e,n,r=this,i=r.view,o=i.calendar,s=o.msToUtcMoment(r.dateProfile.renderUnzonedRange.startMs,!0),a=o.msToUtcMoment(r.dateProfile.renderUnzonedRange.endMs,!0),l=-1,u=[],d=[];s.isBefore(a);)i.isHiddenDay(s)?u.push(l+.5):(l++,u.push(l),d.push(s.clone())),s.add(1,"days");if(this.breakOnWeeks){for(e=d[0].day(),t=1;t=e.length?e[e.length-1]+1:e[n]},e.prototype.computeColHeadFormat=function(){return this.rowCnt>1||this.colCnt>10?"ddd":this.colCnt>1?this.opt("dayOfMonthFormat"):"dddd"},e.prototype.sliceRangeByRow=function(t){var e,n,r,i,o,s=this.daysPerRow,a=this.view.computeDayRange(t),l=this.getDateDayIndex(a.start),u=this.getDateDayIndex(a.end.clone().subtract(1,"days")),d=[];for(e=0;e'+this.renderHeadTrHtml()+"
"},e.prototype.renderHeadIntroHtml=function(){return this.renderIntroHtml()},e.prototype.renderHeadTrHtml=function(){return""+(this.isRTL?"":this.renderHeadIntroHtml())+this.renderHeadDateCellsHtml()+(this.isRTL?this.renderHeadIntroHtml():"")+""},e.prototype.renderHeadDateCellsHtml=function(){var t,e,n=[];for(t=0;t1?' colspan="'+e+'"':"")+(n?" "+n:"")+">"+(a?s.buildGotoAnchorHtml({date:t,forceOff:o.rowCnt>1||1===o.colCnt},r):r)+""},e.prototype.renderBgTrHtml=function(t){return""+(this.isRTL?"":this.renderBgIntroHtml(t))+this.renderBgCellsHtml(t)+(this.isRTL?this.renderBgIntroHtml(t):"")+""},e.prototype.renderBgIntroHtml=function(t){return this.renderIntroHtml()},e.prototype.renderBgCellsHtml=function(t){var e,n,r=[];for(e=0;e"},e.prototype.renderIntroHtml=function(){},e.prototype.bookendCells=function(t){var e=this.renderIntroHtml();e&&(this.isRTL?t.append(e):t.prepend(e))},e}(o.default);e.default=s},function(t,e){Object.defineProperty(e,"__esModule",{value:!0});var n=function(){function t(t,e){this.component=t,this.fillRenderer=e} -return t.prototype.render=function(t){var e=this.component,n=e._getDateProfile().activeUnzonedRange,r=t.buildEventInstanceGroup(e.hasAllDayBusinessHours,n),i=r?e.eventRangesToEventFootprints(r.sliceRenderRanges(n)):[];this.renderEventFootprints(i)},t.prototype.renderEventFootprints=function(t){var e=this.component.eventFootprintsToSegs(t);this.renderSegs(e),this.segs=e},t.prototype.renderSegs=function(t){this.fillRenderer&&this.fillRenderer.renderSegs("businessHours",t,{getClasses:function(t){return["fc-nonbusiness","fc-bgevent"]}})},t.prototype.unrender=function(){this.fillRenderer&&this.fillRenderer.unrender("businessHours"),this.segs=null},t.prototype.getSegs=function(){return this.segs||[]},t}();e.default=n},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(3),i=n(4),o=function(){function t(t){this.fillSegTag="div",this.component=t,this.elsByFill={}}return t.prototype.renderFootprint=function(t,e,n){this.renderSegs(t,this.component.componentFootprintToSegs(e),n)},t.prototype.renderSegs=function(t,e,n){var r;return e=this.buildSegEls(t,e,n),r=this.attachSegEls(t,e),r&&this.reportEls(t,r),e},t.prototype.unrender=function(t){var e=this.elsByFill[t];e&&(e.remove(),delete this.elsByFill[t])},t.prototype.buildSegEls=function(t,e,n){var i,o=this,s="",a=[];if(e.length){for(i=0;i"},t.prototype.attachSegEls=function(t,e){},t.prototype.reportEls=function(t,e){this.elsByFill[t]?this.elsByFill[t]=this.elsByFill[t].add(e):this.elsByFill[t]=r(e)},t}();e.default=o},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(34),o=n(6),s=function(){function t(t,e){this.view=t._getView(),this.component=t,this.eventRenderer=e}return t.prototype.renderComponentFootprint=function(t){this.renderEventFootprints([this.fabricateEventFootprint(t)])},t.prototype.renderEventDraggingFootprints=function(t,e,n){this.renderEventFootprints(t,e,"fc-dragging",n?null:this.view.opt("dragOpacity"))},t.prototype.renderEventResizingFootprints=function(t,e,n){this.renderEventFootprints(t,e,"fc-resizing")},t.prototype.renderEventFootprints=function(t,e,n,r){var i,o=this.component.eventFootprintsToSegs(t),s="fc-helper "+(n||"");for(o=this.eventRenderer.renderFgSegEls(o),i=0;i
'+this.renderBgTrHtml(t)+'
'+(this.getIsNumbersVisible()?""+this.renderNumberTrHtml(t)+"":"")+"
"},e.prototype.getIsNumbersVisible=function(){return this.getIsDayNumbersVisible()||this.cellWeekNumbersVisible},e.prototype.getIsDayNumbersVisible=function(){return this.rowCnt>1},e.prototype.renderNumberTrHtml=function(t){return""+(this.isRTL?"":this.renderNumberIntroHtml(t))+this.renderNumberCellsHtml(t)+(this.isRTL?this.renderNumberIntroHtml(t):"")+""},e.prototype.renderNumberIntroHtml=function(t){return this.renderIntroHtml()},e.prototype.renderNumberCellsHtml=function(t){var e,n,r=[];for(e=0;e",this.cellWeekNumbersVisible&&t.day()===n&&(i+=r.buildGotoAnchorHtml({date:t,type:"week"},{class:"fc-week-number"},t.format("w"))),s&&(i+=r.buildGotoAnchorHtml(t,{class:"fc-day-number"},t.format("D"))),i+=""):""},e.prototype.prepareHits=function(){this.colCoordCache.build(),this.rowCoordCache.build(),this.rowCoordCache.bottoms[this.rowCnt-1]+=this.bottomCoordPadding},e.prototype.releaseHits=function(){this.colCoordCache.clear(),this.rowCoordCache.clear()},e.prototype.queryHit=function(t,e){if(this.colCoordCache.isLeftInBounds(t)&&this.rowCoordCache.isTopInBounds(e)){var n=this.colCoordCache.getHorizontalIndex(t),r=this.rowCoordCache.getVerticalIndex(e);if(null!=r&&null!=n)return this.getCellHit(r,n)}},e.prototype.getHitFootprint=function(t){var e=this.getCellRange(t.row,t.col);return new u.default(new l.default(e.start,e.end),!0)},e.prototype.getHitEl=function(t){return this.getCellEl(t.row,t.col)},e.prototype.getCellHit=function(t,e){return{row:t,col:e,component:this,left:this.colCoordCache.getLeftOffset(e),right:this.colCoordCache.getRightOffset(e),top:this.rowCoordCache.getTopOffset(t),bottom:this.rowCoordCache.getBottomOffset(t)}},e.prototype.getCellEl=function(t,e){return this.cellEls.eq(t*this.colCnt+e)},e.prototype.executeEventUnrender=function(){this.removeSegPopover(),t.prototype.executeEventUnrender.call(this)},e.prototype.getOwnEventSegs=function(){return t.prototype.getOwnEventSegs.call(this).concat(this.popoverSegs||[])},e.prototype.renderDrag=function(t,e,n){var r;for(r=0;r td > :first-child").each(e),r.position().top+o>a)return n;return!1},e.prototype.limitRow=function(t,e){var n,r,o,s,a,l,u,d,c,p,h,f,g,v,y,m=this,b=this.eventRenderer.rowStructs[t],w=[],D=0,E=function(n){for(;D").append(y),c.append(v),w.push(v[0])),D++};if(e&&e').attr("rowspan",p),l=d[f],y=this.renderMoreLink(t,a.leftCol+f,[a].concat(l)),v=i("
").append(y),g.append(v),h.push(g[0]),w.push(g[0]);c.addClass("fc-limited").after(i(h)),o.push(c[0])}}E(this.colCnt),b.moreEls=i(w),b.limitedEls=i(o)}},e.prototype.unlimitRow=function(t){var e=this.eventRenderer.rowStructs[t];e.moreEls&&(e.moreEls.remove(),e.moreEls=null),e.limitedEls&&(e.limitedEls.removeClass("fc-limited"),e.limitedEls=null)},e.prototype.renderMoreLink=function(t,e,n){var r=this,o=this.view;return i('').text(this.getMoreLinkText(n.length)).on("click",function(s){var a=r.opt("eventLimitClick"),l=r.getCellDate(t,e),u=i(s.currentTarget),d=r.getCellEl(t,e),c=r.getCellSegs(t,e),p=r.resliceDaySegs(c,l),h=r.resliceDaySegs(n,l);"function"==typeof a&&(a=r.publiclyTrigger("eventLimitClick",{context:o,args:[{date:l.clone(),dayEl:d,moreEl:u,segs:p,hiddenSegs:h},s,o]})),"popover"===a?r.showSegPopover(t,e,u,p):"string"==typeof a&&o.calendar.zoomTo(l,a)})},e.prototype.showSegPopover=function(t,e,n,r){var i,o,s=this,l=this.view,u=n.parent();i=1===this.rowCnt?l.el:this.rowEls.eq(t),o={className:"fc-more-popover "+l.calendar.theme.getClass("popover"),content:this.renderSegPopoverContent(t,e,r),parentEl:l.el,top:i.offset().top,autoHide:!0,viewportConstrain:this.opt("popoverViewportConstrain"),hide:function(){s.popoverSegs&&s.triggerBeforeEventSegsDestroyed(s.popoverSegs),s.segPopover.removeElement(),s.segPopover=null,s.popoverSegs=null}},this.isRTL?o.right=u.offset().left+u.outerWidth()+1:o.left=u.offset().left-1,this.segPopover=new a.default(o),this.segPopover.show(),this.bindAllSegHandlersToEl(this.segPopover.el),this.triggerAfterEventSegsRendered(r)},e.prototype.renderSegPopoverContent=function(t,e,n){var r,s=this.view,a=s.calendar.theme,l=this.getCellDate(t,e).format(this.opt("dayPopoverFormat")),u=i('
'+o.htmlEscape(l)+'
'),d=u.find(".fc-event-container");for(n=this.eventRenderer.renderFgSegEls(n,!0),this.popoverSegs=n,r=0;r"+s.htmlEscape(this.opt("weekNumberTitle"))+"":""},e.prototype.renderNumberIntroHtml=function(t){var e=this.view,n=this.getCellDate(t,0);return this.colWeekNumbersVisible?'"+e.buildGotoAnchorHtml({date:n,type:"week",forceOff:1===this.colCnt},n.format("w"))+"":""},e.prototype.renderBgIntroHtml=function(){var t=this.view;return this.colWeekNumbersVisible?'":""},e.prototype.renderIntroHtml=function(){var t=this.view;return this.colWeekNumbersVisible?'":""},e.prototype.getIsNumbersVisible=function(){return d.default.prototype.getIsNumbersVisible.apply(this,arguments)||this.colWeekNumbersVisible},e}(t)}Object.defineProperty(e,"__esModule",{value:!0});var i=n(2),o=n(3),s=n(4),a=n(41),l=n(43),u=n(68),d=n(66),c=function(t){function e(e,n){var r=t.call(this,e,n)||this;return r.dayGrid=r.instantiateDayGrid(),r.dayGrid.isRigid=r.hasRigidRows(),r.opt("weekNumbers")&&(r.opt("weekNumbersWithinDays")?(r.dayGrid.cellWeekNumbersVisible=!0,r.dayGrid.colWeekNumbersVisible=!1):(r.dayGrid.cellWeekNumbersVisible=!1,r.dayGrid.colWeekNumbersVisible=!0)),r.addChild(r.dayGrid),r.scroller=new a.default({overflowX:"hidden",overflowY:"auto"}),r}return i.__extends(e,t),e.prototype.instantiateDayGrid=function(){return new(r(this.dayGridClass))(this)},e.prototype.executeDateRender=function(e){this.dayGrid.breakOnWeeks=/year|month|week/.test(e.currentRangeUnit),t.prototype.executeDateRender.call(this,e)},e.prototype.renderSkeleton=function(){var t,e;this.el.addClass("fc-basic-view").html(this.renderSkeletonHtml()),this.scroller.render(),t=this.scroller.el.addClass("fc-day-grid-container"),e=o('
').appendTo(t),this.el.find(".fc-body > tr > td").append(t),this.dayGrid.headContainerEl=this.el.find(".fc-head-container"),this.dayGrid.setElement(e)},e.prototype.unrenderSkeleton=function(){this.dayGrid.removeElement(),this.scroller.destroy()},e.prototype.renderSkeletonHtml=function(){var t=this.calendar.theme;return''+(this.opt("columnHeader")?'':"")+'
 
'},e.prototype.weekNumberStyleAttr=function(){return null!=this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},e.prototype.hasRigidRows=function(){var t=this.opt("eventLimit");return t&&"number"!=typeof t},e.prototype.updateSize=function(e,n,r){var i,o,a=this.opt("eventLimit"),l=this.dayGrid.headContainerEl.find(".fc-row");if(!this.dayGrid.rowEls)return void(n||(i=this.computeScrollerHeight(e),this.scroller.setHeight(i)));t.prototype.updateSize.call(this,e,n,r),this.dayGrid.colWeekNumbersVisible&&(this.weekNumberWidth=s.matchCellWidths(this.el.find(".fc-week-number"))),this.scroller.clear(),s.uncompensateScroll(l),this.dayGrid.removeSegPopover(),a&&"number"==typeof a&&this.dayGrid.limitRows(a),i=this.computeScrollerHeight(e),this.setGridHeight(i,n),a&&"number"!=typeof a&&this.dayGrid.limitRows(a),n||(this.scroller.setHeight(i),o=this.scroller.getScrollbarWidths(),(o.left||o.right)&&(s.compensateScroll(l,o),i=this.computeScrollerHeight(e),this.scroller.setHeight(i)),this.scroller.lockOverflow(o))},e.prototype.computeScrollerHeight=function(t){return t-s.subtractInnerElHeight(this.el,this.scroller.el)},e.prototype.setGridHeight=function(t,e){e?s.undistributeHeight(this.dayGrid.rowEls):s.distributeHeight(this.dayGrid.rowEls,t,!0)},e.prototype.computeInitialDateScroll=function(){return{top:0}},e.prototype.queryDateScroll=function(){return{top:this.scroller.getScrollTop()}},e.prototype.applyDateScroll=function(t){void 0!==t.top&&this.scroller.setScrollTop(t.top)},e}(l.default);e.default=c,c.prototype.dateProfileGeneratorClass=u.default,c.prototype.dayGridClass=d.default},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(5),o=n(55),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r.__extends(e,t),e.prototype.buildRenderRange=function(e,n,r){var o=t.prototype.buildRenderRange.call(this,e,n,r),s=this.msToUtcMoment(o.startMs,r),a=this.msToUtcMoment(o.endMs,r);return/^(year|month)$/.test(n)&&(s.startOf("week"),a.weekday()&&a.add(1,"week").startOf("week")),new i.default(s,a)},e}(o.default);e.default=s},,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,function(t,e,n){function r(t,e,n){var r;for(r=0;r').addClass(e.className||"").css({top:0,left:0}).append(e.content).appendTo(e.parentEl),this.el.on("click",".fc-close",function(){t.hide()}),e.autoHide&&this.listenTo(r(document),"mousedown",this.documentMousedown)},t.prototype.documentMousedown=function(t){this.el&&!r(t.target).closest(this.el).length&&this.hide()},t.prototype.removeElement=function(){this.hide(),this.el&&(this.el.remove(),this.el=null),this.stopListeningTo(r(document),"mousedown")},t.prototype.position=function(){var t,e,n,o,s,a=this.options,l=this.el.offsetParent().offset(),u=this.el.outerWidth(),d=this.el.outerHeight(),c=r(window),p=i.getScrollParent(this.el);o=a.top||0,s=void 0!==a.left?a.left:void 0!==a.right?a.right-u:0,p.is(window)||p.is(document)?(p=c,t=0,e=0):(n=p.offset(),t=n.top,e=n.left),t+=c.scrollTop(),e+=c.scrollLeft(),!1!==a.viewportConstrain&&(o=Math.min(o,t+p.outerHeight()-d-this.margin),o=Math.max(o,t+this.margin),s=Math.min(s,e+p.outerWidth()-u-this.margin),s=Math.max(s,e+this.margin)),this.el.css({top:o-l.top,left:s-l.left})},t.prototype.trigger=function(t){this.options[t]&&this.options[t].apply(this,Array.prototype.slice.call(arguments,1))},t}();e.default=s,o.default.mixInto(s)},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(13),i=function(){function t(){this.q=[],this.isPaused=!1,this.isRunning=!1}return t.prototype.queue=function(){for(var t=[],e=0;e=0;e--)if(n=r[e],n.namespace===t.namespace)switch(n.type){case"init":i=!1;case"add":case"remove":r.splice(e,1)}return i&&r.push(t),i},e}(i.default);e.default=o},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(51),o=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r.__extends(e,t),e.prototype.setElement=function(t){this.el=t,this.bindGlobalHandlers(),this.renderSkeleton(),this.set("isInDom",!0)},e.prototype.removeElement=function(){this.unset("isInDom"),this.unrenderSkeleton(),this.unbindGlobalHandlers(),this.el.remove()},e.prototype.bindGlobalHandlers=function(){},e.prototype.unbindGlobalHandlers=function(){},e.prototype.renderSkeleton=function(){},e.prototype.unrenderSkeleton=function(){},e}(i.default);e.default=o},function(t,e,n){function r(t){var e,n,r,i=[];for(e in t)for(n=t[e].eventInstances,r=0;r'+n+"
":""+n+""},e.prototype.getAllDayHtml=function(){return this.opt("allDayHtml")||a.htmlEscape(this.opt("allDayText"))},e.prototype.getDayClasses=function(t,e){var n,r=this._getView(),i=[];return this.dateProfile.activeUnzonedRange.containsDate(t)?(i.push("fc-"+a.dayIDs[t.day()]),r.isDateInOtherMonth(t,this.dateProfile)&&i.push("fc-other-month"),n=r.calendar.getNow(),t.isSame(n,"day")?(i.push("fc-today"),!0!==e&&i.push(r.calendar.theme.getClass("today"))):t=this.nextDayThreshold&&o.add(1,"days"),o<=n&&(o=n.clone().add(1,"days")),{start:n,end:o}},e.prototype.isMultiDayRange=function(t){var e=this.computeDayRange(t);return e.end.diff(e.start,"days")>1},e.guid=0,e}(d.default);e.default=p},function(t,e,n){function r(t,e){return null==e?t:i.isFunction(e)?t.filter(e):(e+="",t.filter(function(t){return t.id==e||t._id===e}))}Object.defineProperty(e,"__esModule",{value:!0});var i=n(3),o=n(0),s=n(4),a=n(33),l=n(225),u=n(23),d=n(13),c=n(7),p=n(257),h=n(258),f=n(259),g=n(217),v=n(32),y=n(11),m=n(5),b=n(12),w=n(16),D=n(220),E=n(218),S=n(38),C=n(36),R=n(9),T=n(39),M=n(6),I=n(57),H=function(){function t(t,e){this.loadingLevel=0,this.ignoreUpdateViewSize=0,this.freezeContentHeightDepth=0,u.default.needed(),this.el=t,this.viewsByType={},this.optionsManager=new h.default(this,e),this.viewSpecManager=new f.default(this.optionsManager,this),this.initMomentInternals(),this.initCurrentDate(),this.initEventManager(),this.constraints=new g.default(this.eventManager,this),this.constructed()}return t.prototype.constructed=function(){},t.prototype.getView=function(){return this.view},t.prototype.publiclyTrigger=function(t,e){var n,r,o=this.opt(t);if(i.isPlainObject(e)?(n=e.context,r=e.args):i.isArray(e)&&(r=e),null==n&&(n=this.el[0]),r||(r=[]),this.triggerWith(t,n,r),o)return o.apply(n,r)},t.prototype.hasPublicHandlers=function(t){return this.hasHandlers(t)||this.opt(t)},t.prototype.option=function(t,e){var n;if("string"==typeof t){if(void 0===e)return this.optionsManager.get(t);n={},n[t]=e,this.optionsManager.add(n)}else"object"==typeof t&&this.optionsManager.add(t)},t.prototype.opt=function(t){return this.optionsManager.get(t)},t.prototype.instantiateView=function(t){var e=this.viewSpecManager.getViewSpec(t);if(!e)throw new Error('View type "'+t+'" is not valid');return new e.class(this,e)},t.prototype.isValidViewType=function(t){return Boolean(this.viewSpecManager.getViewSpec(t))},t.prototype.changeView=function(t,e){e&&(e.start&&e.end?this.optionsManager.recordOverrides({visibleRange:e}):this.currentDate=this.moment(e).stripZone()),this.renderView(t)},t.prototype.zoomTo=function(t,e){var n;e=e||"day",n=this.viewSpecManager.getViewSpec(e)||this.viewSpecManager.getUnitViewSpec(e),this.currentDate=t.clone(),this.renderView(n?n.type:null)},t.prototype.initCurrentDate=function(){var t=this.opt("defaultDate");this.currentDate=null!=t?this.moment(t).stripZone():this.getNow()},t.prototype.prev=function(){var t=this.view,e=t.dateProfileGenerator.buildPrev(t.get("dateProfile"));e.isValid&&(this.currentDate=e.date,this.renderView())},t.prototype.next=function(){var t=this.view,e=t.dateProfileGenerator.buildNext(t.get("dateProfile"));e.isValid&&(this.currentDate=e.date,this.renderView())},t.prototype.prevYear=function(){this.currentDate.add(-1,"years"),this.renderView()},t.prototype.nextYear=function(){this.currentDate.add(1,"years"),this.renderView()},t.prototype.today=function(){this.currentDate=this.getNow(),this.renderView()},t.prototype.gotoDate=function(t){this.currentDate=this.moment(t).stripZone(),this.renderView()},t.prototype.incrementDate=function(t){this.currentDate.add(o.duration(t)),this.renderView()},t.prototype.getDate=function(){return this.applyTimezone(this.currentDate)},t.prototype.pushLoading=function(){this.loadingLevel++||this.publiclyTrigger("loading",[!0,this.view])},t.prototype.popLoading=function(){--this.loadingLevel||this.publiclyTrigger("loading",[!1,this.view])},t.prototype.render=function(){this.contentEl?this.elementVisible()&&(this.calcSize(),this.updateViewSize()):this.initialRender()},t.prototype.initialRender=function(){var t=this,e=this.el;e.addClass("fc"),e.on("click.fc","a[data-goto]",function(e){var n=i(e.currentTarget),r=n.data("goto"),o=t.moment(r.date),a=r.type,l=t.view.opt("navLink"+s.capitaliseFirstLetter(a)+"Click");"function"==typeof l?l(o,e):("string"==typeof l&&(a=l),t.zoomTo(o,a))}),this.optionsManager.watch("settingTheme",["?theme","?themeSystem"],function(n){var r=I.getThemeSystemClass(n.themeSystem||n.theme),i=new r(t.optionsManager),o=i.getClass("widget");t.theme=i,o&&e.addClass(o)},function(){var n=t.theme.getClass("widget");t.theme=null,n&&e.removeClass(n)}),this.optionsManager.watch("settingBusinessHourGenerator",["?businessHours"],function(e){t.businessHourGenerator=new E.default(e.businessHours,t),t.view&&t.view.set("businessHourGenerator",t.businessHourGenerator)},function(){t.businessHourGenerator=null}),this.optionsManager.watch("applyingDirClasses",["?isRTL","?locale"],function(t){e.toggleClass("fc-ltr",!t.isRTL),e.toggleClass("fc-rtl",t.isRTL)}),this.contentEl=i("
").prependTo(e),this.initToolbars(),this.renderHeader(),this.renderFooter(),this.renderView(this.opt("defaultView")),this.opt("handleWindowResize")&&i(window).resize(this.windowResizeProxy=s.debounce(this.windowResize.bind(this),this.opt("windowResizeDelay")))},t.prototype.destroy=function(){this.view&&this.clearView(),this.toolbarsManager.proxyCall("removeElement"),this.contentEl.remove(),this.el.removeClass("fc fc-ltr fc-rtl"),this.optionsManager.unwatch("settingTheme"),this.optionsManager.unwatch("settingBusinessHourGenerator"),this.el.off(".fc"),this.windowResizeProxy&&(i(window).unbind("resize",this.windowResizeProxy),this.windowResizeProxy=null),u.default.unneeded()},t.prototype.elementVisible=function(){return this.el.is(":visible")},t.prototype.bindViewHandlers=function(t){var e=this;t.watch("titleForCalendar",["title"],function(n){t===e.view&&e.setToolbarsTitle(n.title)}),t.watch("dateProfileForCalendar",["dateProfile"],function(n){t===e.view&&(e.currentDate=n.dateProfile.date,e.updateToolbarButtons(n.dateProfile))})},t.prototype.unbindViewHandlers=function(t){t.unwatch("titleForCalendar"),t.unwatch("dateProfileForCalendar")},t.prototype.renderView=function(t){var e,n=this.view;this.freezeContentHeight(),n&&t&&n.type!==t&&this.clearView(),!this.view&&t&&(e=this.view=this.viewsByType[t]||(this.viewsByType[t]=this.instantiateView(t)),this.bindViewHandlers(e),e.startBatchRender(),e.setElement(i("
").appendTo(this.contentEl)),this.toolbarsManager.proxyCall("activateButton",t)),this.view&&(this.view.get("businessHourGenerator")!==this.businessHourGenerator&&this.view.set("businessHourGenerator",this.businessHourGenerator),this.view.setDate(this.currentDate),e&&e.stopBatchRender()),this.thawContentHeight()},t.prototype.clearView=function(){var t=this.view;this.toolbarsManager.proxyCall("deactivateButton",t.type),this.unbindViewHandlers(t),t.removeElement(),t.unsetDate(),this.view=null},t.prototype.reinitView=function(){var t=this.view,e=t.queryScroll();this.freezeContentHeight(),this.clearView(),this.calcSize(),this.renderView(t.type),this.view.applyScroll(e),this.thawContentHeight()},t.prototype.getSuggestedViewHeight=function(){return null==this.suggestedViewHeight&&this.calcSize(),this.suggestedViewHeight},t.prototype.isHeightAuto=function(){return"auto"===this.opt("contentHeight")||"auto"===this.opt("height")},t.prototype.updateViewSize=function(t){void 0===t&&(t=!1);var e,n=this.view;if(!this.ignoreUpdateViewSize&&n)return t&&(this.calcSize(),e=n.queryScroll()),this.ignoreUpdateViewSize++,n.updateSize(this.getSuggestedViewHeight(),this.isHeightAuto(),t),this.ignoreUpdateViewSize--,t&&n.applyScroll(e),!0},t.prototype.calcSize=function(){this.elementVisible()&&this._calcSize()},t.prototype._calcSize=function(){var t=this.opt("contentHeight"),e=this.opt("height");this.suggestedViewHeight="number"==typeof t?t:"function"==typeof t?t():"number"==typeof e?e-this.queryToolbarsHeight():"function"==typeof e?e()-this.queryToolbarsHeight():"parent"===e?this.el.parent().height()-this.queryToolbarsHeight():Math.round(this.contentEl.width()/Math.max(this.opt("aspectRatio"),.5))},t.prototype.windowResize=function(t){t.target===window&&this.view&&this.view.isDatesRendered&&this.updateViewSize(!0)&&this.publiclyTrigger("windowResize",[this.view])},t.prototype.freezeContentHeight=function(){this.freezeContentHeightDepth++||this.forceFreezeContentHeight()},t.prototype.forceFreezeContentHeight=function(){this.contentEl.css({width:"100%",height:this.contentEl.height(),overflow:"hidden"})},t.prototype.thawContentHeight=function(){this.freezeContentHeightDepth--,this.contentEl.css({width:"",height:"",overflow:""}),this.freezeContentHeightDepth&&this.forceFreezeContentHeight()},t.prototype.initToolbars=function(){this.header=new p.default(this,this.computeHeaderOptions()),this.footer=new p.default(this,this.computeFooterOptions()),this.toolbarsManager=new l.default([this.header,this.footer])},t.prototype.computeHeaderOptions=function(){return{extraClasses:"fc-header-toolbar",layout:this.opt("header")}},t.prototype.computeFooterOptions=function(){return{extraClasses:"fc-footer-toolbar",layout:this.opt("footer")}},t.prototype.renderHeader=function(){var t=this.header;t.setToolbarOptions(this.computeHeaderOptions()),t.render(),t.el&&this.el.prepend(t.el)},t.prototype.renderFooter=function(){var t=this.footer;t.setToolbarOptions(this.computeFooterOptions()),t.render(),t.el&&this.el.append(t.el)},t.prototype.setToolbarsTitle=function(t){this.toolbarsManager.proxyCall("updateTitle",t)},t.prototype.updateToolbarButtons=function(t){var e=this.getNow(),n=this.view,r=n.dateProfileGenerator.build(e),i=n.dateProfileGenerator.buildPrev(n.get("dateProfile")),o=n.dateProfileGenerator.buildNext(n.get("dateProfile"));this.toolbarsManager.proxyCall(r.isValid&&!t.currentUnzonedRange.containsDate(e)?"enableButton":"disableButton","today"),this.toolbarsManager.proxyCall(i.isValid?"enableButton":"disableButton","prev"),this.toolbarsManager.proxyCall(o.isValid?"enableButton":"disableButton","next")},t.prototype.queryToolbarsHeight=function(){return this.toolbarsManager.items.reduce(function(t,e){return t+(e.el?e.el.outerHeight(!0):0)},0)},t.prototype.select=function(t,e){this.view.select(this.buildSelectFootprint.apply(this,arguments))},t.prototype.unselect=function(){this.view&&this.view.unselect()},t.prototype.buildSelectFootprint=function(t,e){var n,r=this.moment(t).stripZone();return n=e?this.moment(e).stripZone():r.hasTime()?r.clone().add(this.defaultTimedEventDuration):r.clone().add(this.defaultAllDayEventDuration),new b.default(new m.default(r,n),!r.hasTime())},t.prototype.initMomentInternals=function(){var t=this;this.defaultAllDayEventDuration=o.duration(this.opt("defaultAllDayEventDuration")),this.defaultTimedEventDuration=o.duration(this.opt("defaultTimedEventDuration")),this.optionsManager.watch("buildingMomentLocale",["?locale","?monthNames","?monthNamesShort","?dayNames","?dayNamesShort","?firstDay","?weekNumberCalculation"],function(e){var n,r=e.weekNumberCalculation,i=e.firstDay;"iso"===r&&(r="ISO");var o=Object.create(v.getMomentLocaleData(e.locale));e.monthNames&&(o._months=e.monthNames),e.monthNamesShort&&(o._monthsShort=e.monthNamesShort),e.dayNames&&(o._weekdays=e.dayNames),e.dayNamesShort&&(o._weekdaysShort=e.dayNamesShort),null==i&&"ISO"===r&&(i=1),null!=i&&(n=Object.create(o._week),n.dow=i,o._week=n),"ISO"!==r&&"local"!==r&&"function"!=typeof r||(o._fullCalendar_weekCalc=r),t.localeData=o,t.currentDate&&t.localizeMoment(t.currentDate)})},t.prototype.moment=function(){for(var t=[],e=0;eo.getStart()&&(r=new a.default,r.setEndDelta(l),i=new s.default,i.setDateMutation(r),i)},e}(u.default);e.default=d},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(4),o=n(39),s=n(40),a=n(59),l=n(17),u=n(226),d=n(14),c=function(t){function e(e,n){var r=t.call(this,e)||this;return r.isDragging=!1,r.eventPointing=n,r}return r.__extends(e,t),e.prototype.end=function(){this.dragListener&&this.dragListener.endInteraction()},e.prototype.getSelectionDelay=function(){var t=this.opt("eventLongPressDelay");return null==t&&(t=this.opt("longPressDelay")),t},e.prototype.bindToEl=function(t){var e=this.component;e.bindSegHandlerToEl(t,"mousedown",this.handleMousedown.bind(this)),e.bindSegHandlerToEl(t,"touchstart",this.handleTouchStart.bind(this))},e.prototype.handleMousedown=function(t,e){!this.component.shouldIgnoreMouse()&&this.component.canStartDrag(t,e)&&this.buildDragListener(t).startInteraction(e,{distance:5})},e.prototype.handleTouchStart=function(t,e){var n=this.component,r={delay:this.view.isEventDefSelected(t.footprint.eventDef)?0:this.getSelectionDelay()};n.canStartDrag(t,e)?this.buildDragListener(t).startInteraction(e,r):n.canStartSelection(t,e)&&this.buildSelectListener(t).startInteraction(e,r)},e.prototype.buildSelectListener=function(t){var e=this,n=this.view,r=t.footprint.eventDef,i=t.footprint.eventInstance;if(this.dragListener)return this.dragListener;var o=this.dragListener=new a.default({dragStart:function(t){o.isTouch&&!n.isEventDefSelected(r)&&i&&n.selectEventInstance(i)},interactionEnd:function(t){e.dragListener=null}});return o},e.prototype.buildDragListener=function(t){var e,n,r,o=this,s=this.component,a=this.view,d=a.calendar,c=d.eventManager,p=t.el,h=t.footprint.eventDef,f=t.footprint.eventInstance;if(this.dragListener)return this.dragListener;var g=this.dragListener=new l.default(a,{scroll:this.opt("dragScroll"),subjectEl:p,subjectCenter:!0,interactionStart:function(r){t.component=s,e=!1,n=new u.default(t.el,{additionalClass:"fc-dragging",parentEl:a.el,opacity:g.isTouch?null:o.opt("dragOpacity"),revertDuration:o.opt("dragRevertDuration"),zIndex:2}),n.hide(),n.start(r)},dragStart:function(n){g.isTouch&&!a.isEventDefSelected(h)&&f&&a.selectEventInstance(f),e=!0,o.eventPointing.handleMouseout(t,n),o.segDragStart(t,n),a.hideEventsWithId(t.footprint.eventDef.id)},hitOver:function(e,l,u){var p,f,v,y=!0;t.hit&&(u=t.hit),p=u.component.getSafeHitFootprint(u),f=e.component.getSafeHitFootprint(e),p&&f?(r=o.computeEventDropMutation(p,f,h),r?(v=c.buildMutatedEventInstanceGroup(h.id,r),y=s.isEventInstanceGroupAllowed(v)):y=!1):y=!1,y||(r=null,i.disableCursor()),r&&a.renderDrag(s.eventRangesToEventFootprints(v.sliceRenderRanges(s.dateProfile.renderUnzonedRange,d)),t,g.isTouch)?n.hide():n.show(),l&&(r=null)},hitOut:function(){a.unrenderDrag(t),n.show(),r=null},hitDone:function(){i.enableCursor()},interactionEnd:function(i){delete t.component,n.stop(!r,function(){e&&(a.unrenderDrag(t),o.segDragStop(t,i)),a.showEventsWithId(t.footprint.eventDef.id),r&&a.reportEventDrop(f,r,p,i)}),o.dragListener=null}});return g},e.prototype.segDragStart=function(t,e){this.isDragging=!0,this.component.publiclyTrigger("eventDragStart",{context:t.el[0],args:[t.footprint.getEventLegacy(),e,{},this.view]})},e.prototype.segDragStop=function(t,e){this.isDragging=!1,this.component.publiclyTrigger("eventDragStop",{context:t.el[0],args:[t.footprint.getEventLegacy(),e,{},this.view]})},e.prototype.computeEventDropMutation=function(t,e,n){var r=new o.default;return r.setDateMutation(this.computeEventDateMutation(t,e)),r},e.prototype.computeEventDateMutation=function(t,e){var n,r,i=t.unzonedRange.getStart(),o=e.unzonedRange.getStart(),a=!1,l=!1,u=!1;return t.isAllDay!==e.isAllDay&&(a=!0,e.isAllDay?(u=!0,i.stripTime()):l=!0),n=this.component.diffDates(o,i),r=new s.default,r.clearEnd=a,r.forceTimed=l,r.forceAllDay=u,r.setDateDelta(n),r},e}(d.default);e.default=c},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(4),o=n(17),s=n(12),a=n(5),l=n(14),u=function(t){function e(e){var n=t.call(this,e)||this;return n.dragListener=n.buildDragListener(),n}return r.__extends(e,t),e.prototype.end=function(){this.dragListener.endInteraction()},e.prototype.getDelay=function(){var t=this.opt("selectLongPressDelay");return null==t&&(t=this.opt("longPressDelay")),t},e.prototype.bindToEl=function(t){var e=this,n=this.component,r=this.dragListener;n.bindDateHandlerToEl(t,"mousedown",function(t){e.opt("selectable")&&!n.shouldIgnoreMouse()&&r.startInteraction(t,{distance:e.opt("selectMinDistance")})}),n.bindDateHandlerToEl(t,"touchstart",function(t){e.opt("selectable")&&!n.shouldIgnoreTouch()&&r.startInteraction(t,{delay:e.getDelay()})}),i.preventSelection(t)},e.prototype.buildDragListener=function(){var t,e=this,n=this.component;return new o.default(n,{scroll:this.opt("dragScroll"),interactionStart:function(){t=null},dragStart:function(t){e.view.unselect(t)},hitOver:function(r,o,s){var a,l;s&&(a=n.getSafeHitFootprint(s),l=n.getSafeHitFootprint(r),t=a&&l?e.computeSelection(a,l):null,t?n.renderSelectionFootprint(t):!1===t&&i.disableCursor())},hitOut:function(){t=null,n.unrenderSelection()},hitDone:function(){i.enableCursor()},interactionEnd:function(n,r){!r&&t&&e.view.reportSelection(t,n)}})},e.prototype.computeSelection=function(t,e){var n=this.computeSelectionFootprint(t,e);return!(n&&!this.isSelectionFootprintAllowed(n))&&n},e.prototype.computeSelectionFootprint=function(t,e){var n=[t.unzonedRange.startMs,t.unzonedRange.endMs,e.unzonedRange.startMs,e.unzonedRange.endMs];return n.sort(i.compareNumbers),new s.default(new a.default(n[0],n[3]),t.isAllDay)},e.prototype.isSelectionFootprintAllowed=function(t){return this.component.dateProfile.validUnzonedRange.containsRange(t.unzonedRange)&&this.view.calendar.constraints.isSelectionFootprintAllowed(t)},e}(l.default);e.default=u},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(17),o=n(14),s=function(t){function e(e){var n=t.call(this,e)||this;return n.dragListener=n.buildDragListener(),n}return r.__extends(e,t),e.prototype.end=function(){this.dragListener.endInteraction()},e.prototype.bindToEl=function(t){var e=this.component,n=this.dragListener;e.bindDateHandlerToEl(t,"mousedown",function(t){e.shouldIgnoreMouse()||n.startInteraction(t)}),e.bindDateHandlerToEl(t,"touchstart",function(t){e.shouldIgnoreTouch()||n.startInteraction(t)})},e.prototype.buildDragListener=function(){var t,e=this,n=this.component,r=new i.default(n,{scroll:this.opt("dragScroll"),interactionStart:function(){t=r.origHit},hitOver:function(e,n,r){n||(t=null)},hitOut:function(){t=null},interactionEnd:function(r,i){var o;!i&&t&&(o=n.getSafeHitFootprint(t))&&e.view.triggerDayClick(o,n.getHitEl(t),r)}});return r.shouldCancelTouchScroll=!1,r.scrollAlwaysKills=!0,r},e}(o.default);e.default=s},function(t,e,n){function r(t){var e,n=[],r=[];for(e=0;e').appendTo(t),this.el.find(".fc-body > tr > td").append(t),this.timeGrid.headContainerEl=this.el.find(".fc-head-container"),this.timeGrid.setElement(e),this.dayGrid&&(this.dayGrid.setElement(this.el.find(".fc-day-grid")),this.dayGrid.bottomCoordPadding=this.dayGrid.el.next("hr").outerHeight())},e.prototype.unrenderSkeleton=function(){this.timeGrid.removeElement(),this.dayGrid&&this.dayGrid.removeElement(),this.scroller.destroy()},e.prototype.renderSkeletonHtml=function(){var t=this.calendar.theme;return''+(this.opt("columnHeader")?'':"")+'
 
'+(this.dayGrid?'

':"")+"
"},e.prototype.axisStyleAttr=function(){return null!=this.axisWidth?'style="width:'+this.axisWidth+'px"':""},e.prototype.getNowIndicatorUnit=function(){return this.timeGrid.getNowIndicatorUnit()},e.prototype.updateSize=function(e,n,r){var i,o,s;if(t.prototype.updateSize.call(this,e,n,r),this.axisWidth=u.matchCellWidths(this.el.find(".fc-axis")),!this.timeGrid.colEls)return void(n||(o=this.computeScrollerHeight(e),this.scroller.setHeight(o)));var a=this.el.find(".fc-row:not(.fc-scroller *)");this.timeGrid.bottomRuleEl.hide(),this.scroller.clear(),u.uncompensateScroll(a),this.dayGrid&&(this.dayGrid.removeSegPopover(),i=this.opt("eventLimit"),i&&"number"!=typeof i&&(i=5),i&&this.dayGrid.limitRows(i)),n||(o=this.computeScrollerHeight(e),this.scroller.setHeight(o),s=this.scroller.getScrollbarWidths(),(s.left||s.right)&&(u.compensateScroll(a,s),o=this.computeScrollerHeight(e),this.scroller.setHeight(o)),this.scroller.lockOverflow(s),this.timeGrid.getTotalSlatHeight()"+e.buildGotoAnchorHtml({date:r,type:"week",forceOff:this.colCnt>1},u.htmlEscape(t))+""):'"},renderBgIntroHtml:function(){var t=this.view;return'"},renderIntroHtml:function(){return'"}},o={renderBgIntroHtml:function(){var t=this.view;return'"+t.getAllDayHtml()+""},renderIntroHtml:function(){return'"}}},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(3),o=n(0),s=n(4),a=n(42),l=n(61),u=n(65),d=n(60),c=n(58),p=n(5),h=n(12),f=n(240),g=n(241),v=n(242),y=[{hours:1},{minutes:30},{minutes:15},{seconds:30},{seconds:15}],m=function(t){function e(e){var n=t.call(this,e)||this;return n.processOptions(),n}return r.__extends(e,t),e.prototype.componentFootprintToSegs=function(t){var e,n=this.sliceRangeByTimes(t.unzonedRange);for(e=0;e=0;e--)if(n=o.duration(y[e]),r=s.divideDurationByDuration(n,t),s.isInt(r)&&r>1)return n;return o.duration(t)},e.prototype.renderDates=function(t){this.dateProfile=t,this.updateDayTable(),this.renderSlats(),this.renderColumns()},e.prototype.unrenderDates=function(){this.unrenderColumns()},e.prototype.renderSkeleton=function(){var t=this.view.calendar.theme;this.el.html('
'),this.bottomRuleEl=this.el.find("hr")},e.prototype.renderSlats=function(){var t=this.view.calendar.theme;this.slatContainerEl=this.el.find("> .fc-slats").html(''+this.renderSlatRowHtml()+"
"),this.slatEls=this.slatContainerEl.find("tr"),this.slatCoordCache=new c.default({els:this.slatEls,isVertical:!0})},e.prototype.renderSlatRowHtml=function(){for(var t,e,n,r=this.view,i=r.calendar,a=i.theme,l=this.isRTL,u=this.dateProfile,d="",c=o.duration(+u.minTime),p=o.duration(0);c"+(e?""+s.htmlEscape(t.format(this.labelFormat))+"":"")+"",d+='"+(l?"":n)+''+(l?n:"")+"",c.add(this.slotDuration),p.add(this.slotDuration);return d},e.prototype.renderColumns=function(){var t=this.dateProfile,e=this.view.calendar.theme;this.dayRanges=this.dayDates.map(function(e){return new p.default(e.clone().add(t.minTime),e.clone().add(t.maxTime))}),this.headContainerEl&&this.headContainerEl.html(this.renderHeadHtml()),this.el.find("> .fc-bg").html(''+this.renderBgTrHtml(0)+"
"),this.colEls=this.el.find(".fc-day, .fc-disabled-day"),this.colCoordCache=new c.default({els:this.colEls,isHorizontal:!0}),this.renderContentSkeleton()},e.prototype.unrenderColumns=function(){this.unrenderContentSkeleton()},e.prototype.renderContentSkeleton=function(){var t,e,n="";for(t=0;t
';e=this.contentSkeletonEl=i('
'+n+"
"),this.colContainerEls=e.find(".fc-content-col"),this.helperContainerEls=e.find(".fc-helper-container"),this.fgContainerEls=e.find(".fc-event-container:not(.fc-helper-container)"),this.bgContainerEls=e.find(".fc-bgevent-container"),this.highlightContainerEls=e.find(".fc-highlight-container"),this.businessContainerEls=e.find(".fc-business-container"),this.bookendCells(e.find("tr")),this.el.append(e)},e.prototype.unrenderContentSkeleton=function(){this.contentSkeletonEl&&(this.contentSkeletonEl.remove(),this.contentSkeletonEl=null,this.colContainerEls=null,this.helperContainerEls=null,this.fgContainerEls=null,this.bgContainerEls=null,this.highlightContainerEls=null,this.businessContainerEls=null)},e.prototype.groupSegsByCol=function(t){var e,n=[];for(e=0;e
').css("top",r).appendTo(this.colContainerEls.eq(n[e].col))[0]);n.length>0&&o.push(i('
').css("top",r).appendTo(this.el.find(".fc-content-skeleton"))[0]),this.nowIndicatorEls=i(o)}},e.prototype.unrenderNowIndicator=function(){this.nowIndicatorEls&&(this.nowIndicatorEls.remove(),this.nowIndicatorEls=null)},e.prototype.updateSize=function(e,n,r){t.prototype.updateSize.call(this,e,n,r),this.slatCoordCache.build(),r&&this.updateSegVerticals([].concat(this.eventRenderer.getSegs(),this.businessSegs||[]))},e.prototype.getTotalSlatHeight=function(){return this.slatContainerEl.outerHeight()},e.prototype.computeDateTop=function(t,e){return this.computeTimeTop(o.duration(t-e.clone().stripTime()))},e.prototype.computeTimeTop=function(t){var e,n,r=this.slatEls.length,i=this.dateProfile,o=(t-i.minTime)/this.slotDuration;return o=Math.max(0,o),o=Math.min(r,o),e=Math.floor(o),e=Math.min(e,r-1),n=o-e,this.slatCoordCache.getTopPosition(e)+this.slatCoordCache.getHeight(e)*n},e.prototype.updateSegVerticals=function(t){this.computeSegVerticals(t),this.assignSegVerticals(t)},e.prototype.computeSegVerticals=function(t){var e,n,r,i=this.opt("agendaEventMinHeight");for(e=0;ee.top&&t.top
'+(n?'
'+u.htmlEscape(n)+"
":"")+(d.title?'
'+u.htmlEscape(d.title)+"
":"")+'
'+(h?'
':"")+""},e.prototype.updateFgSegCoords=function(t){this.timeGrid.computeSegVerticals(t),this.computeFgSegHorizontals(t),this.timeGrid.assignSegVerticals(t),this.assignFgSegHorizontals(t)},e.prototype.computeFgSegHorizontals=function(t){var e,n,s;if(this.sortEventSegs(t),e=r(t),i(e),n=e[0]){for(s=0;s=t.leftCol)return!0;return!1}function i(t,e){return t.leftCol-e.leftCol}Object.defineProperty(e,"__esModule",{value:!0});var o=n(2),s=n(3),a=n(4),l=n(44),u=function(t){function e(e,n){var r=t.call(this,e,n)||this;return r.dayGrid=e,r}return o.__extends(e,t),e.prototype.renderBgRanges=function(e){e=s.grep(e,function(t){return t.eventDef.isAllDay()}),t.prototype.renderBgRanges.call(this,e)},e.prototype.renderFgSegs=function(t){var e=this.rowStructs=this.renderSegRows(t);this.dayGrid.rowEls.each(function(t,n){s(n).find(".fc-content-skeleton > table").append(e[t].tbodyEl)})},e.prototype.unrenderFgSegs=function(){for(var t,e=this.rowStructs||[];t=e.pop();)t.tbodyEl.remove();this.rowStructs=null},e.prototype.renderSegRows=function(t){var e,n,r=[];for(e=this.groupSegRows(t),n=0;n"),a.append(d)),v[r][o]=d,y[r][o]=d,o++}var r,i,o,a,l,u,d,c=this.dayGrid.colCnt,p=this.buildSegLevels(e),h=Math.max(1,p.length),f=s(""),g=[],v=[],y=[];for(r=0;r"),g.push([]),v.push([]),y.push([]),i)for(l=0;l').append(u.el),u.leftCol!==u.rightCol?d.attr("colspan",u.rightCol-u.leftCol+1):y[r][o]=d;o<=u.rightCol;)v[r][o]=d,g[r][o]=u,o++;a.append(d)}n(c),this.dayGrid.bookendCells(a),f.append(a)}return{row:t,tbodyEl:f,cellMatrix:v,segMatrix:g,segLevels:p,segs:e}},e.prototype.buildSegLevels=function(t){var e,n,o,s=[];for(this.sortEventSegs(t),e=0;e'+a.htmlEscape(n)+""),r=''+(a.htmlEscape(o.title||"")||" ")+"",'
'+(this.dayGrid.isRTL?r+" "+h:h+" "+r)+"
"+(u?'
':"")+(d?'
':"")+"
"},e}(l.default);e.default=u},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(3),o=n(63),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r.__extends(e,t),e.prototype.renderSegs=function(t,e){var n,r=[];return n=this.eventRenderer.renderSegRows(t),this.component.rowEls.each(function(t,o){var s,a,l=i(o),u=i('
');e&&e.row===t?a=e.el.position().top:(s=l.find(".fc-content-skeleton tbody"),s.length||(s=l.find(".fc-content-skeleton table")),a=s.position().top),u.css("top",a).find("table").append(n[t].tbodyEl),l.append(u),r.push(u[0])}),i(r)},e}(o.default);e.default=s},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(3),o=n(62),s=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.fillSegTag="td",e}return r.__extends(e,t),e.prototype.attachSegEls=function(t,e){var n,r,i,o=[];for(n=0;n
'),o=r.find("tr"),a>0&&o.append(new Array(a+1).join("")),o.append(e.el.attr("colspan",l-a)),l")),this.component.bookendCells(o),r},e}(o.default);e.default=s},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(0),o=n(4),s=n(67),a=n(247),l=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r.__extends(e,t),e.prototype.setGridHeight=function(t,e){e&&(t*=this.dayGrid.rowCnt/6),o.distributeHeight(this.dayGrid.rowEls,t,!e)},e.prototype.isDateInOtherMonth=function(t,e){return t.month()!==i.utc(e.currentUnzonedRange.startMs).month()},e}(s.default);e.default=l,l.prototype.dateProfileGeneratorClass=a.default},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(68),o=n(5),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r.__extends(e,t),e.prototype.buildRenderRange=function(e,n,r){var i,s=t.prototype.buildRenderRange.call(this,e,n,r),a=this.msToUtcMoment(s.startMs,r),l=this.msToUtcMoment(s.endMs,r);return this.opt("fixedWeekCount")&&(i=Math.ceil(l.diff(a,"weeks",!0)),l.add(6-i,"weeks")),new o.default(a,l)},e}(i.default);e.default=s},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(3),o=n(4),s=n(5),a=n(43),l=n(41),u=n(249),d=n(250),c=function(t){function e(e,n){var r=t.call(this,e,n)||this;return r.segSelector=".fc-list-item",r.scroller=new l.default({overflowX:"hidden",overflowY:"auto"}),r}return r.__extends(e,t),e.prototype.renderSkeleton=function(){this.el.addClass("fc-list-view "+this.calendar.theme.getClass("listView")),this.scroller.render(),this.scroller.el.appendTo(this.el),this.contentEl=this.scroller.scrollEl},e.prototype.unrenderSkeleton=function(){this.scroller.destroy()},e.prototype.updateSize=function(e,n,r){t.prototype.updateSize.call(this,e,n,r),this.scroller.clear(),n||this.scroller.setHeight(this.computeScrollerHeight(e))},e.prototype.computeScrollerHeight=function(t){return t-o.subtractInnerElHeight(this.el,this.scroller.el)},e.prototype.renderDates=function(t){for(var e=this.calendar,n=e.msToUtcMoment(t.renderUnzonedRange.startMs,!0),r=e.msToUtcMoment(t.renderUnzonedRange.endMs,!0),i=[],o=[];n
'+o.htmlEscape(this.opt("noEventsMessage"))+"
")},e.prototype.renderSegList=function(t){var e,n,r,o=this.groupSegsByDay(t),s=i('
'),a=s.find("tbody");for(e=0;e'+(e?this.buildGotoAnchorHtml(t,{class:"fc-list-heading-main"},o.htmlEscape(t.format(e))):"")+(n?this.buildGotoAnchorHtml(t,{class:"fc-list-heading-alt"},o.htmlEscape(t.format(n))):"")+""},e}(a.default);e.default=c,c.prototype.eventRendererClass=u.default,c.prototype.eventPointingClass=d.default},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(4),o=n(44),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r.__extends(e,t),e.prototype.renderFgSegs=function(t){t.length?this.component.renderSegList(t):this.component.renderEmptyMessage()},e.prototype.fgSegHtml=function(t){var e,n=this.view,r=n.calendar,o=r.theme,s=t.footprint,a=s.eventDef,l=s.componentFootprint,u=a.url,d=["fc-list-item"].concat(this.getClasses(a)),c=this.getBgColor(a);return e=l.isAllDay?n.getAllDayHtml():n.isMultiDayRange(l.unzonedRange)?t.isStart||t.isEnd?i.htmlEscape(this._getTimeText(r.msToMoment(t.startMs),r.msToMoment(t.endMs),l.isAllDay)):n.getAllDayHtml():i.htmlEscape(this.getTimeText(s)),u&&d.push("fc-has-url"),''+(this.displayEventTime?''+(e||"")+"":"")+'"+i.htmlEscape(a.title||"")+""},e.prototype.computeEventTimeFormat=function(){return this.opt("mediumTimeFormat")},e}(o.default);e.default=s},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(3),o=n(64),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return r.__extends(e,t),e.prototype.handleClick=function(e,n){var r;t.prototype.handleClick.call(this,e,n),i(n.target).closest("a[href]").length||(r=e.footprint.eventDef.url)&&!n.isDefaultPrevented()&&(window.location.href=r)},e}(o.default);e.default=s},,,,,,function(t,e,n){var r=n(3),i=n(18),o=n(4),s=n(232);n(11),n(49),n(260),n(261),n(264),n(265),n(266),n(267),r.fullCalendar=i,r.fn.fullCalendar=function(t){var e=Array.prototype.slice.call(arguments,1),n=this;return this.each(function(i,a){var l,u=r(a),d=u.data("fullCalendar");"string"==typeof t?"getCalendar"===t?i||(n=d):"destroy"===t?d&&(d.destroy(),u.removeData("fullCalendar")):d?r.isFunction(d[t])?(l=d[t].apply(d,e),i||(n=l),"destroy"===t&&u.removeData("fullCalendar")):o.warn("'"+t+"' is an unknown FullCalendar method."):o.warn("Attempting to call a FullCalendar method on an element with no calendar."):d||(d=new s.default(u,t),u.data("fullCalendar",d),d.render())}),n},t.exports=i},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(3),i=n(4),o=function(){function t(t,e){this.el=null,this.viewsWithButtons=[],this.calendar=t,this.toolbarOptions=e}return t.prototype.setToolbarOptions=function(t){this.toolbarOptions=t},t.prototype.render=function(){var t=this.toolbarOptions.layout,e=this.el;t?(e?e.empty():e=this.el=r("
"),e.append(this.renderSection("left")).append(this.renderSection("right")).append(this.renderSection("center")).append('
')):this.removeElement()},t.prototype.removeElement=function(){this.el&&(this.el.remove(),this.el=null)},t.prototype.renderSection=function(t){var e=this,n=this.calendar,o=n.theme,s=n.optionsManager,a=n.viewSpecManager,l=r('
'),u=this.toolbarOptions.layout[t],d=s.get("customButtons")||{},c=s.overrides.buttonText||{},p=s.get("buttonText")||{};return u&&r.each(u.split(" "),function(t,s){var u,h=r(),f=!0;r.each(s.split(","),function(t,s){var l,u,g,v,y,m,b,w,D;"title"===s?(h=h.add(r("

 

")),f=!1):((l=d[s])?(g=function(t){l.click&&l.click.call(w[0],t)},(v=o.getCustomButtonIconClass(l))||(v=o.getIconClass(s))||(y=l.text)):(u=a.getViewSpec(s))?(e.viewsWithButtons.push(s),g=function(){n.changeView(s)},(y=u.buttonTextOverride)||(v=o.getIconClass(s))||(y=u.buttonTextDefault)):n[s]&&(g=function(){n[s]()},(y=c[s])||(v=o.getIconClass(s))||(y=p[s])),g&&(b=["fc-"+s+"-button",o.getClass("button"),o.getClass("stateDefault")],y?(m=i.htmlEscape(y),D=""):v&&(m="",D=' aria-label="'+s+'"'),w=r('").click(function(t){w.hasClass(o.getClass("stateDisabled"))||(g(t),(w.hasClass(o.getClass("stateActive"))||w.hasClass(o.getClass("stateDisabled")))&&w.removeClass(o.getClass("stateHover")))}).mousedown(function(){w.not("."+o.getClass("stateActive")).not("."+o.getClass("stateDisabled")).addClass(o.getClass("stateDown"))}).mouseup(function(){w.removeClass(o.getClass("stateDown"))}).hover(function(){w.not("."+o.getClass("stateActive")).not("."+o.getClass("stateDisabled")).addClass(o.getClass("stateHover"))},function(){w.removeClass(o.getClass("stateHover")).removeClass(o.getClass("stateDown"))}),h=h.add(w)))}),f&&h.first().addClass(o.getClass("cornerLeft")).end().last().addClass(o.getClass("cornerRight")).end(),h.length>1?(u=r("
"),f&&u.addClass(o.getClass("buttonGroup")),u.append(h),l.append(u)):l.append(h)}),l},t.prototype.updateTitle=function(t){this.el&&this.el.find("h2").text(t)},t.prototype.activateButton=function(t){this.el&&this.el.find(".fc-"+t+"-button").addClass(this.calendar.theme.getClass("stateActive"))},t.prototype.deactivateButton=function(t){this.el&&this.el.find(".fc-"+t+"-button").removeClass(this.calendar.theme.getClass("stateActive"))},t.prototype.disableButton=function(t){this.el&&this.el.find(".fc-"+t+"-button").prop("disabled",!0).addClass(this.calendar.theme.getClass("stateDisabled"))},t.prototype.enableButton=function(t){this.el&&this.el.find(".fc-"+t+"-button").prop("disabled",!1).removeClass(this.calendar.theme.getClass("stateDisabled"))},t.prototype.getViewsWithButtons=function(){return this.viewsWithButtons},t}();e.default=o},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(2),i=n(3),o=n(4),s=n(33),a=n(32),l=n(51),u=function(t){function e(e,n){var r=t.call(this)||this;return r._calendar=e,r.overrides=i.extend({},n),r.dynamicOverrides={},r.compute(),r}return r.__extends(e,t),e.prototype.add=function(t){var e,n=0;this.recordOverrides(t);for(e in t)n++;if(1===n){if("height"===e||"contentHeight"===e||"aspectRatio"===e)return void this._calendar.updateViewSize(!0);if("defaultDate"===e)return;if("businessHours"===e)return;if(/^(event|select)(Overlap|Constraint|Allow)$/.test(e))return;if("timezone"===e)return void this._calendar.view.flash("initialEvents")}this._calendar.renderHeader(),this._calendar.renderFooter(),this._calendar.viewsByType={},this._calendar.reinitView()},e.prototype.compute=function(){var t,e,n,r,i;t=o.firstDefined(this.dynamicOverrides.locale,this.overrides.locale),e=a.localeOptionHash[t],e||(t=s.globalDefaults.locale,e=a.localeOptionHash[t]||{}),n=o.firstDefined(this.dynamicOverrides.isRTL,this.overrides.isRTL,e.isRTL,s.globalDefaults.isRTL),r=n?s.rtlDefaults:{},this.dirDefaults=r,this.localeDefaults=e,i=s.mergeOptions([s.globalDefaults,r,e,this.overrides,this.dynamicOverrides]),a.populateInstanceComputableOptions(i),this.reset(i)},e.prototype.recordOverrides=function(t){var e;for(e in t)this.dynamicOverrides[e]=t[e];this._calendar.viewSpecManager.clearCache(),this.compute()},e}(l.default);e.default=u},function(t,e,n){Object.defineProperty(e,"__esModule",{value:!0});var r=n(0),i=n(3),o=n(24),s=n(4),a=n(33),l=n(32),u=function(){function t(t,e){this.optionsManager=t,this._calendar=e,this.clearCache()}return t.prototype.clearCache=function(){this.viewSpecCache={}},t.prototype.getViewSpec=function(t){var e=this.viewSpecCache;return e[t]||(e[t]=this.buildViewSpec(t))},t.prototype.getUnitViewSpec=function(t){var e,n,r;if(-1!==i.inArray(t,s.unitsDesc))for(e=this._calendar.header.getViewsWithButtons(),i.each(o.viewHash,function(t){e.push(t)}),n=0;n .modal:nth-child(5n + #{$i} of .show.modal-minimize) { + --minimized-modal-index: #{$i}; + } +} +.modal-minimize ~ .modal-minimize { + .modal-dialog { + bottom: calc(44px * (var(--minimized-modal-index) - 1)); + } + .modal-header { + border-bottom: 0px; + } + .modal-content { + // Rounded chip style + border-radius: var(--border-radius-md); + overflow: hidden; + } +} diff --git a/frappe/public/scss/desk/calendar.scss b/frappe/public/scss/desk/calendar.scss index 804006fe64..810b0e4563 100644 --- a/frappe/public/scss/desk/calendar.scss +++ b/frappe/public/scss/desk/calendar.scss @@ -1,52 +1,53 @@ -.fc-unthemed { +.fc-theme-standard { padding: 20px; + color: var(--text-light) !important; +} + +.fc-theme-standard a { color: var(--text-light); } .fc-toolbar { - // padding-top: 30px; padding-bottom: 15px; margin-bottom: 0px !important; } +.fc-toolbar-chunk div { + display: flex; +} + .fc-view-container { margin-left: -1px; margin-right: -1px; } .fc-head-container { - // border-top: 0 !important; border: none !important; } -th.fc-widget-header { - border: none !important; +th.fc-col-header-cell { color: var(--gray-500); font-weight: 600; } -// th { -// border: none !important; -// } - -.fc-unthemed td, -.fc-unthemed hr, -.fc-unthemed thead, -.fc-unthemed tbody, -.fc-unthemed .fc-row, -.fc-unthemed .fc-popover { +.fc-theme-standard td, +.fc-theme-standard hr, +.fc-theme-standard thead, +.fc-theme-standard tbody, +.fc-theme-standard .fc-row, +.fc-theme-standard .fc-popover { border-color: var(--gray-300) !important; } -.fc-unthemed td.fc-sun { +.fc-theme-standard td.fc-day-sun { background: var(--highlight-color); } -.fc-unthemed .fc-today { +.fc-theme-standard .fc-day-today { background-color: var(--fg-color) !important; - .fc-day-number { - background-color: var(--blue-500); + .fc-daygrid-day-number { + background-color: var(--gray-700); border-radius: 50%; color: $white; height: 22px; @@ -55,61 +56,64 @@ th.fc-widget-header { display: flex; justify-content: center; text-align: center; + padding: 0; } } -// .fc-highlight { -// background-color: $light-yellow !important; -// } - .fc-event { - // border: 1px solid #E8DDFF; /* default BORDER color */ - background-color: #e8ddff; + background-color: rgb(237, 246, 253); + border: none !important; +} + +.fc-event-main .fc-event-time { + display: none; } .fc-time-grid-event { border: none !important; } -// @media (max-width: $screen-xs) { -// .fc-scroller { -// height: auto !important; -// } -// } .fc-day-top { padding: 5px 10px 0 0 !important; } -.fc-day { - margin-left: 10px; - .fc-day-number { +.fc-daygrid-day-top { + margin: 5px 0 0 10px; + flex-direction: row !important; + .fc-daygrid-day-number { float: left !important; } } -th.fc-day-header { +th.fc-col-header-cell { padding: 10px 12px 10px 0 !important; text-transform: uppercase; font-size: 12px; } -.fc-event-container .fc-content { +.fc-daygrid-dot-event { padding: 3px; display: flex; flex-direction: column-reverse; + align-items: normal; + color: rgb(0, 112, 204) !important; - .fc-time { + .fc-event-time { font-weight: normal; margin-top: 2px; } - .fc-title { + .fc-event-title { font-weight: 600; } + + .fc-daygrid-event-dot { + display: none; + } } -.fc-left h2 { - font-size: $font-size-lg; +.fc-toolbar-title { + font-size: $font-size-lg !important; font-weight: 500; line-height: 28px; height: 28px; @@ -120,9 +124,6 @@ th.fc-day-header { font-size: var(--text-md) !important; outline: none !important; text-transform: capitalize; - // .fc-icon { - // top: -1px !important; - // } } .fc-right button { @@ -131,29 +132,51 @@ th.fc-day-header { .fc-left button { width: 80px; - - // svg { - // margin-right: 5px; - // } } -.fc-state-active { +.fc-button-active { box-shadow: none !important; background: var(--gray-500) !important; color: var(--fg-color) !important; z-index: 0 !important; } -.fc-day-grid-event { +//override default and fc-button styles +.fc-dayGridMonth-button, +.fc-dayGridWeek-button, +.fc-dayGridDay-button { + border: none !important; + border-radius: 0; + background-color: var(--control-bg); + color: var(--text-color); +} + +.fc-dayGridMonth-button { + border-top-left-radius: var(--border-radius) !important; + border-bottom-left-radius: var(--border-radius) !important; +} +.fc-dayGridDay-button { + border-top-right-radius: var(--border-radius) !important; + border-bottom-right-radius: var(--border-radius) !important; +} + +.fc-prev-button { + margin-right: 10px !important; +} +.fc-next-button { + margin-left: 10px; +} +.fc-today-button { + margin-right: 10px; + border-radius: var(--border-radius) !important; +} + +.fc-daygrid-event { border: none !important; margin: 5px 4px 0 !important; padding: 1px 5px !important; } -// .result .footnote-area { -// padding: 15px 10px 0 30px; -// } - .fc-time-grid .fc-slats .fc-minor td { border-top-style: none !important; } @@ -185,7 +208,6 @@ th.fc-day-header { .fc-day-grid { border-bottom: 1px solid var(--gray-300); - // height: 2em !important; } .fc-divider { diff --git a/frappe/query_builder/terms.py b/frappe/query_builder/terms.py index c33f890a39..46c73dd518 100644 --- a/frappe/query_builder/terms.py +++ b/frappe/query_builder/terms.py @@ -1,10 +1,11 @@ -from datetime import time, timedelta +from datetime import datetime, time, timedelta from typing import Any from pypika.queries import QueryBuilder from pypika.terms import Criterion, Function, ValueWrapper from pypika.utils import format_alias_sql +import frappe from frappe.utils.data import format_time, format_timedelta @@ -56,6 +57,8 @@ class ParameterizedValueWrapper(ValueWrapper): self.value = format_timedelta(self.value) elif isinstance(self.value, time): self.value = format_time(self.value) + elif isinstance(self.value, datetime): + self.value = frappe.db.format_datetime(self.value) sql = self.get_value_sql( quote_char=quote_char, diff --git a/frappe/test_runner.py b/frappe/test_runner.py index 4001aa9130..6f20de3d87 100644 --- a/frappe/test_runner.py +++ b/frappe/test_runner.py @@ -446,6 +446,9 @@ def make_test_objects(doctype, test_records=None, verbose=None, reset=False, com test_records = frappe.get_test_records(doctype) for doc in test_records: + if not reset: + frappe.db.savepoint("creating_test_record") + if not doc.get("doctype"): doc["doctype"] = doctype @@ -461,7 +464,7 @@ def make_test_objects(doctype, test_records=None, verbose=None, reset=False, com d.set_new_name() if frappe.db.exists(d.doctype, d.name) and not reset: - frappe.db.rollback() + frappe.db.rollback(save_point="creating_test_record") # do not create test records, if already exists continue diff --git a/frappe/tests/test_caching.py b/frappe/tests/test_caching.py index 9a0da1935f..3eeefaa67d 100644 --- a/frappe/tests/test_caching.py +++ b/frappe/tests/test_caching.py @@ -185,6 +185,34 @@ class TestRedisCache(FrappeAPITestCase): calculate_area(10) self.assertEqual(function_call_count, 2) + def test_user_cache(self): + function_call_count = 0 + PI = 3.1415 + ENGINEERING_PI = _E = 3 + + @redis_cache(user=True) + def calculate_area(radius: float) -> float: + nonlocal function_call_count + PI_APPROX = ENGINEERING_PI if frappe.session.user == "Engineer" else PI + function_call_count += 1 + return PI_APPROX * radius**2 + + with self.set_user("Engineer"): + self.assertEqual(calculate_area(1), ENGINEERING_PI) + self.assertEqual(function_call_count, 1) + + with self.set_user("Mathematician"): + self.assertEqual(calculate_area(1), PI) + self.assertEqual(function_call_count, 2) + + with self.set_user("Engineer"): + self.assertEqual(calculate_area(1), ENGINEERING_PI) + self.assertEqual(function_call_count, 2) + + with self.set_user("Mathematician"): + self.assertEqual(calculate_area(1), PI) + self.assertEqual(function_call_count, 2) + class TestDocumentCache(FrappeAPITestCase): TEST_DOCTYPE = "User" diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py index 89790c600a..59f4c7c8fa 100644 --- a/frappe/tests/test_commands.py +++ b/frappe/tests/test_commands.py @@ -892,12 +892,12 @@ class TestAddNewUser(BaseTestCommands): class TestBenchBuild(BaseTestCommands): def test_build_assets_size_check(self): - with cli(frappe.commands.utils.build, "--force --production") as result: + with cli(frappe.commands.utils.build, "--force --production --app frappe") as result: self.assertEqual(result.exit_code, 0) self.assertEqual(result.exception, None) - CURRENT_SIZE = 3.5 # MB - JS_ASSET_THRESHOLD = 0.1 + CURRENT_SIZE = 3.3 # MB + JS_ASSET_THRESHOLD = 0.01 hooks = frappe.get_hooks() default_bundle = hooks["app_include_js"] @@ -925,15 +925,6 @@ class TestDBUtils(BaseTestCommands): meta = frappe.get_meta("User", cached=False) self.assertTrue(meta.get_field(field).search_index) - @run_only_if(db_type_is.MARIADB) - def test_describe_table(self): - self.execute("bench --site {site} describe-database-table --doctype User", {}) - self.assertIn("user_type", self.stdout) - - # Ensure that output is machine parseable - stats = json.loads(self.stdout) - self.assertIn("total_rows", stats) - class TestSchedulerUtils(BaseTestCommands): # Retry just in case there are stuck queued jobs diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 9469abb6b7..715522d868 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -17,6 +17,7 @@ from frappe.query_builder.functions import Concat_ws from frappe.tests.test_query_builder import db_type_is, run_only_if from frappe.tests.utils import FrappeTestCase, patch_hooks, timeout from frappe.utils import add_days, now, random_string, set_request +from frappe.utils.data import now_datetime from frappe.utils.testutils import clear_custom_fields @@ -504,6 +505,19 @@ class TestDB(FrappeTestCase): self.assertEqual(frappe.db.exists(dt, [["name", "=", dn]]), dn) + def test_datetime_serialization(self): + dt = now_datetime() + dt = dt.replace(microsecond=0) + self.assertEqual(str(dt), str(frappe.db.sql("select %s", dt)[0][0])) + + frappe.db.exists("User", {"creation": (">", dt)}) + self.assertIn(str(dt), str(frappe.db.last_query)) + + before = now_datetime() + note = frappe.get_doc(doctype="Note", title=frappe.generate_hash(), content="something").insert() + after = now_datetime() + self.assertEqual(note.name, frappe.db.exists("Note", {"creation": ("between", (before, after))})) + def test_bulk_insert(self): current_count = frappe.db.count("ToDo") test_body = f"test_bulk_insert - {random_string(10)}" diff --git a/frappe/tests/test_translate.py b/frappe/tests/test_translate.py index d1f152b72c..b586885d62 100644 --- a/frappe/tests/test_translate.py +++ b/frappe/tests/test_translate.py @@ -17,6 +17,7 @@ from frappe.translate import ( extract_messages_from_javascript_code, extract_messages_from_python_code, get_language, + get_messages_for_app, get_parent_language, get_translation_dict_from_file, ) @@ -314,6 +315,8 @@ def verify_translation_files(app): lang = file.stem # basename of file = lang get_translation_dict_from_file(file, lang, app, throw=True) + get_messages_for_app(app) + expected_output = [ ("Warning: Unable to find {0} in any table related to {1}", "This is some context", 2), diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 70a5e2223b..ceba8bab05 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -42,6 +42,8 @@ EMAIL_MATCH_PATTERN = re.compile( re.IGNORECASE, ) +UNSET = object() + def get_fullname(user=None): """get the full name (first name + last name) of the user from User""" @@ -1166,3 +1168,21 @@ class CallbackManager: def reset(self): self._functions.clear() + + +class Truthy: + def __init__(self, value=True, context=UNSET): + self.value = value + self.context = context + + def __bool__(self): + return True + + def __eq__(self, other: object) -> bool: + return True == other # noqa: E712 + + def __repr__(self) -> str: + _val = "UNSET" if self.value is UNSET else self.value + _ctx = "UNSET" if self.context is UNSET else self.context + + return f"Truthy(value={_val}, context={_ctx})" diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 808ed35d5e..56accfb7e0 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -659,6 +659,11 @@ jobs: - name: Clone uses: actions/checkout@v3 + - name: Find tests + run: | + echo "Finding tests" + grep -rn "def test" > /dev/null + - name: Setup Python uses: actions/setup-python@v4 with: diff --git a/frappe/utils/caching.py b/frappe/utils/caching.py index 40e2c488f1..2fc9dfaf4d 100644 --- a/frappe/utils/caching.py +++ b/frappe/utils/caching.py @@ -132,12 +132,13 @@ def site_cache(ttl: int | None = None, maxsize: int | None = None) -> Callable: return time_cache_wrapper -def redis_cache(ttl: int | None = 3600, user: str | bool | None = None) -> Callable: +def redis_cache(ttl: int | None = 3600, user: str | bool | None = None, shared: bool = False) -> Callable: """Decorator to cache method calls and its return values in Redis args: ttl: time to expiry in seconds, defaults to 1 hour user: `true` should cache be specific to session user. + shared: `true` should cache be shared across sites """ def wrapper(func: Callable | None = None) -> Callable: @@ -152,11 +153,11 @@ def redis_cache(ttl: int | None = 3600, user: str | bool | None = None) -> Calla @wraps(func) def redis_cache_wrapper(*args, **kwargs): func_call_key = func_key + "::" + str(__generate_request_cache_key(args, kwargs)) - if frappe.cache.exists(func_call_key): - return frappe.cache.get_value(func_call_key, user=user) + if frappe.cache.exists(func_call_key, user=user, shared=shared): + return frappe.cache.get_value(func_call_key, user=user, shared=shared) val = func(*args, **kwargs) ttl = getattr(func, "ttl", 3600) - frappe.cache.set_value(func_call_key, val, expires_in_sec=ttl, user=user) + frappe.cache.set_value(func_call_key, val, expires_in_sec=ttl, user=user, shared=shared) return val return redis_cache_wrapper diff --git a/frappe/utils/change_log.py b/frappe/utils/change_log.py index d20ead2489..cf77a76d9c 100644 --- a/frappe/utils/change_log.py +++ b/frappe/utils/change_log.py @@ -11,6 +11,7 @@ from semantic_version import SimpleSpec, Version import frappe from frappe import _, safe_decode from frappe.utils import cstr +from frappe.utils.caching import redis_cache from frappe.utils.frappecloud import on_frappecloud @@ -211,7 +212,7 @@ def check_for_update(): def has_app_update_notifications() -> bool: - return bool(frappe.cache.sismember("update-user-set", frappe.session.user)) + return bool(frappe.cache.sismember("changelog-update-user-set", frappe.session.user)) def parse_latest_non_beta_release(response: list, current_version: Version) -> list | None: @@ -250,22 +251,16 @@ def check_release_on_github( raise ValueError("Repo cannot be empty") # Get latest version from GitHub - r = requests.get(f"https://api.github.com/repos/{owner}/{repo}/releases") - if r.ok: - latest_non_beta_release = parse_latest_non_beta_release(r.json(), current_version) - if latest_non_beta_release: - return Version(latest_non_beta_release), owner + releases = _get_latest_releases(owner, repo) + latest_non_beta_release = parse_latest_non_beta_release(releases, current_version) + if latest_non_beta_release: + return Version(latest_non_beta_release), owner return None, None def security_issues_count(owner: str, repo: str, current_version: Version, target_version: Version) -> int: - import requests - - r = requests.get(f"https://api.github.com/repos/{owner}/{repo}/security-advisories") - if not r.ok: - return 0 - advisories = r.json() + advisories = _get_security_issues(owner, repo) def applicable(advisory) -> bool: # Current version is in vulnerable range @@ -285,6 +280,28 @@ def security_issues_count(owner: str, repo: str, current_version: Version, targe return len([sa for sa in advisories if applicable(sa)]) +@redis_cache(ttl=6 * 24 * 60 * 60, shared=True) +def _get_latest_releases(owner, repo): + import requests + + r = requests.get(f"https://api.github.com/repos/{owner}/{repo}/releases") + if not r.ok: + return [] + + return r.json() + + +@redis_cache(ttl=6 * 24 * 60 * 60, shared=True) +def _get_security_issues(owner, repo): + import requests + + r = requests.get(f"https://api.github.com/repos/{owner}/{repo}/security-advisories") + if not r.ok: + return [] + + return r.json() + + def parse_github_url(remote_url: str) -> tuple[str, str] | tuple[None, None]: """Parse the remote URL to get the owner and repo name.""" import re @@ -307,11 +324,11 @@ def get_source_url(app: str) -> str | None: def add_message_to_redis(update_json): # "update-message" will store the update message string - # "update-user-set" will be a set of users - frappe.cache.set_value("update-info", json.dumps(update_json)) + # "changelog-update-user-set" will be a set of users + frappe.cache.set_value("changelog-update-info", json.dumps(update_json)) user_list = [x.name for x in frappe.get_all("User", filters={"enabled": True})] system_managers = [user for user in user_list if "System Manager" in frappe.get_roles(user)] - frappe.cache.sadd("update-user-set", *system_managers) + frappe.cache.sadd("changelog-update-user-set", *system_managers) @frappe.whitelist() @@ -320,7 +337,7 @@ def show_update_popup(): return user = frappe.session.user - update_info = frappe.cache.get_value("update-info") + update_info = frappe.cache.get_value("changelog-update-info") if not update_info: return @@ -328,7 +345,7 @@ def show_update_popup(): # Check if user is int the set of users to send update message to update_message = "" - if frappe.cache.sismember("update-user-set", user): + if frappe.cache.sismember("changelog-update-user-set", user): for update_type in updates: release_links = "" for app in updates[update_type]: @@ -373,7 +390,7 @@ def show_update_popup(): indicator="green", primary_action=primary_action, ) - frappe.cache.srem("update-user-set", user) + frappe.cache.srem("changelog-update-user-set", user) def get_pyproject(app: str) -> dict | None: diff --git a/frappe/utils/csvutils.py b/frappe/utils/csvutils.py index 8a31a94326..fd3a20b8ba 100644 --- a/frappe/utils/csvutils.py +++ b/frappe/utils/csvutils.py @@ -2,12 +2,14 @@ # License: MIT. See LICENSE import csv import json +from csv import Sniffer from io import StringIO import requests import frappe from frappe import _, msgprint +from frappe.core.doctype.file.file import FILE_ENCODING_OPTIONS from frappe.utils import cint, comma_or, cstr, flt @@ -39,7 +41,7 @@ def read_csv_content_from_attached_file(doc): def read_csv_content(fcontent): if not isinstance(fcontent, str): decoded = False - for encoding in ["utf-8", "windows-1250", "windows-1252"]: + for encoding in FILE_ENCODING_OPTIONS: try: fcontent = str(fcontent, encoding) decoded = True @@ -49,15 +51,35 @@ def read_csv_content(fcontent): if not decoded: frappe.msgprint( - _("Unknown file encoding. Tried utf-8, windows-1250, windows-1252."), raise_exception=True + _("Unknown file encoding. Tried to use: {0}").format(", ".join(FILE_ENCODING_OPTIONS)), + raise_exception=True, ) fcontent = fcontent.encode("utf-8") content = [frappe.safe_decode(line) for line in fcontent.splitlines(True)] + sniffer = Sniffer() + # Don't need to use whole csv, if more than 20 rows, use just first 20 + sample_content = content[:20] if len(content) > 20 else content + # only testing for most common delimiter types, this later can be extended + # init default dialect, to avoid lint errors + dialect = csv.get_dialect("excel") + try: + # csv by default uses excel dialect, which is not always correct + dialect = sniffer.sniff(sample="\n".join(sample_content), delimiters=frappe.flags.delimiter_options) + except csv.Error: + # if sniff fails, show alert on user interface. Fall back to use default dialect (excel) + frappe.msgprint( + _( + "Delimiter detection failed. Try to enable custom delimiters and adjust the delimiter options as per your data." + ), + indicator="orange", + alert=True, + ) + try: rows = [] - for row in csv.reader(content): + for row in csv.reader(content, dialect=dialect): r = [] for val in row: # decode everything diff --git a/frappe/utils/jinja.py b/frappe/utils/jinja.py index 21cdb29d0c..5720697338 100644 --- a/frappe/utils/jinja.py +++ b/frappe/utils/jinja.py @@ -66,7 +66,7 @@ def validate_template(html): try: jenv.from_string(html) except TemplateSyntaxError as e: - frappe.throw(frappe._(f"Syntax error in template as line {e.lineno}: {e.message}")) + frappe.throw(f"Syntax error in template as line {e.lineno}: {e.message}") def render_template(template, context=None, is_path=None, safe_render=True): diff --git a/frappe/utils/sentry.py b/frappe/utils/sentry.py index aa159d7084..cb37c57fe5 100644 --- a/frappe/utils/sentry.py +++ b/frappe/utils/sentry.py @@ -140,13 +140,3 @@ def capture_exception(message: str | None = None) -> None: except Exception: frappe.logger().error("Failed to capture exception", exc_info=True) - pass - - -def add_bootinfo(bootinfo): - """Called from hook, sends DSN so client side can setup error monitoring.""" - if not frappe.get_system_settings("enable_telemetry"): - return - - if sentry_dsn := os.getenv("FRAPPE_SENTRY_DSN"): - bootinfo.sentry_dsn = sentry_dsn diff --git a/package.json b/package.json index 3a4673c298..539b275164 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,11 @@ "dependencies": { "@editorjs/editorjs": "^2.28.2", "@frappe/esbuild-plugin-postcss2": "^0.1.3", + "@fullcalendar/core": "^6.1.11", + "@fullcalendar/daygrid": "^6.1.11", + "@fullcalendar/list": "^6.1.11", + "@fullcalendar/timegrid": "^6.1.11", + "@fullcalendar/interaction": "^6.1.11", "@headlessui/vue": "^1.7.16", "@popperjs/core": "^2.11.2", "@redis/client": "^1.5.8", @@ -31,7 +36,7 @@ "@vue/component-compiler": "^4.2.4", "@vueuse/core": "^9.5.0", "ace-builds": "^1.4.8", - "air-datepicker": "github:frappe/air-datepicker", + "air-datepicker": "git+https://github.com/frappe/air-datepicker", "autoprefixer": "10", "awesomplete": "^1.1.5", "bootstrap": "4.6.2", @@ -87,4 +92,4 @@ "bufferutil": "^4.0.8", "utf-8-validate": "^6.0.3" } -} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 50ae33c1ca..a6ab16a10f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -55,6 +55,35 @@ stylus "^0.x" tmp "^0.2.1" +"@fullcalendar/core@^6.1.11": + version "6.1.11" + resolved "https://registry.yarnpkg.com/@fullcalendar/core/-/core-6.1.11.tgz#f9630e83ae977e774992507635b1e7af4c339d37" + integrity sha512-TjG7c8sUz+Vkui2FyCNJ+xqyu0nq653Ibe99A66LoW95oBo6tVhhKIaG1Wh0GVKymYiqAQN/OEdYTuj4ay27kA== + dependencies: + preact "~10.12.1" + +"@fullcalendar/daygrid@^6.1.11", "@fullcalendar/daygrid@~6.1.11": + version "6.1.11" + resolved "https://registry.yarnpkg.com/@fullcalendar/daygrid/-/daygrid-6.1.11.tgz#83a5d4a94c314cf3a14b06bebba03b1b40e6d2ba" + integrity sha512-hF5jJB7cgUIxWD5MVjj8IU407HISyLu7BWXcEIuTytkfr8oolOXeCazqnnjmRbnFOncoJQVstTtq6SIhaT32Xg== + +"@fullcalendar/interaction@^6.1.11": + version "6.1.11" + resolved "https://registry.yarnpkg.com/@fullcalendar/interaction/-/interaction-6.1.11.tgz#baa3beec8f5c489fb6904973b175a5f4797abdf3" + integrity sha512-ynOKjzuPwEAMgTQ6R/Z2zvzIIqG4p8/Qmnhi1q0vzPZZxSIYx3rlZuvpEK2WGBZZ1XEafDOP/LGfbWoNZe+qdg== + +"@fullcalendar/list@^6.1.11": + version "6.1.11" + resolved "https://registry.yarnpkg.com/@fullcalendar/list/-/list-6.1.11.tgz#4cd23700ea48b382b37387e29a706f2da692e174" + integrity sha512-9Qx8uvik9pXD12u50FiHwNzlHv4wkhfsr+r03ycahW7vEeIAKCsIZGTkUfFP+96I5wHihrfLazu1cFQG4MPiuw== + +"@fullcalendar/timegrid@^6.1.11": + version "6.1.11" + resolved "https://registry.yarnpkg.com/@fullcalendar/timegrid/-/timegrid-6.1.11.tgz#76b2fc4446d1e97819a4395dab4f3a7e44c7a9eb" + integrity sha512-0seUHK/ferH89IeuCvV4Bib0zWjgK0nsptNdmAc9wDBxD/d9hm5Mdti0URJX6bDoRtsSfRDu5XsRcrzwoc+AUQ== + dependencies: + "@fullcalendar/daygrid" "~6.1.11" + "@headlessui/vue@^1.7.16": version "1.7.16" resolved "https://registry.yarnpkg.com/@headlessui/vue/-/vue-1.7.16.tgz#bdc9d32d329248910325539b99e6abfce0c69f89" @@ -398,9 +427,9 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -"air-datepicker@github:frappe/air-datepicker": +"air-datepicker@git+https://github.com/frappe/air-datepicker": version "2.2.3" - resolved "https://codeload.github.com/frappe/air-datepicker/tar.gz/ed37b94d95c68d8544357e330be0c89d044a3eea" + resolved "git+https://github.com/frappe/air-datepicker#ed37b94d95c68d8544357e330be0c89d044a3eea" dependencies: jquery ">=2.0.0 <4.0.0" @@ -2666,6 +2695,11 @@ postcss@^7.0.36: picocolors "^0.2.1" source-map "^0.6.1" +preact@~10.12.1: + version "10.12.1" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.12.1.tgz#8f9cb5442f560e532729b7d23d42fd1161354a21" + integrity sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg== + "prettier@^1.18.2 || ^2.0.0": version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"