Merge branch 'frappe:develop' into integrate_frappe_notification
This commit is contained in:
commit
a084f91e18
49 changed files with 217 additions and 178 deletions
|
|
@ -224,8 +224,8 @@ context("View", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("Route to Settings Workspace", () => {
|
||||
cy.visit("/app/settings");
|
||||
cy.get(".title-text").should("contain", "Settings");
|
||||
it("Route to Website Workspace", () => {
|
||||
cy.visit("/app/website");
|
||||
cy.get(".title-text").should("contain", "Website");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ context("Workspace 2.0", () => {
|
|||
it("Navigate to page from sidebar", () => {
|
||||
cy.visit("/app/build");
|
||||
cy.get(".codex-editor__redactor .ce-block");
|
||||
cy.get('.sidebar-item-container[item-name="Settings"]').first().click();
|
||||
cy.location("pathname").should("eq", "/app/settings");
|
||||
cy.get('.sidebar-item-container[item-name="Website"]').first().click();
|
||||
cy.location("pathname").should("eq", "/app/website");
|
||||
});
|
||||
|
||||
it("Create Private Page", () => {
|
||||
|
|
|
|||
|
|
@ -1002,19 +1002,11 @@ def has_permission(
|
|||
)
|
||||
|
||||
if throw and not out:
|
||||
# mimics frappe.throw
|
||||
document_label = (
|
||||
f"{_(doctype)} {doc if isinstance(doc, str) else doc.name}" if doc else _(doctype)
|
||||
)
|
||||
msgprint(
|
||||
_("No permission for {0}").format(document_label),
|
||||
raise_exception=ValidationError,
|
||||
title=None,
|
||||
indicator="red",
|
||||
is_minimizable=None,
|
||||
wide=None,
|
||||
as_list=False,
|
||||
)
|
||||
frappe.flags.error_message = _("No permission for {0}").format(document_label)
|
||||
raise frappe.PermissionError
|
||||
|
||||
return out
|
||||
|
||||
|
|
|
|||
|
|
@ -270,9 +270,6 @@ def get_user_info():
|
|||
user_info = frappe._dict()
|
||||
add_user_info(frappe.session.user, user_info)
|
||||
|
||||
if frappe.session.user == "Administrator" and user_info.Administrator.email:
|
||||
user_info[user_info.Administrator.email] = user_info.Administrator
|
||||
|
||||
return user_info
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -267,6 +267,7 @@ frappe.ui.form.on("Communication", {
|
|||
$.extend(args, {
|
||||
subject: __("Re: {0}", [frm.doc.subject]),
|
||||
recipients: frm.doc.sender,
|
||||
is_a_reply: true,
|
||||
});
|
||||
|
||||
new frappe.views.CommunicationComposer(args);
|
||||
|
|
@ -278,6 +279,7 @@ frappe.ui.form.on("Communication", {
|
|||
subject: __("Res: {0}", [frm.doc.subject]),
|
||||
recipients: frm.doc.sender,
|
||||
cc: frm.doc.cc,
|
||||
is_a_reply: true,
|
||||
});
|
||||
new frappe.views.CommunicationComposer(args);
|
||||
},
|
||||
|
|
@ -287,6 +289,7 @@ frappe.ui.form.on("Communication", {
|
|||
$.extend(args, {
|
||||
forward: true,
|
||||
subject: __("Fw: {0}", [frm.doc.subject]),
|
||||
is_a_reply: true,
|
||||
});
|
||||
|
||||
new frappe.views.CommunicationComposer(args);
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ class DocType(Document):
|
|||
"DocPerm",
|
||||
"Custom Field",
|
||||
"Customize Form Field",
|
||||
"Web Form Field",
|
||||
"DocField",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"cmd",
|
||||
"time",
|
||||
"duration",
|
||||
"event_type",
|
||||
"section_break_1skt",
|
||||
"request_headers",
|
||||
"section_break_sgro",
|
||||
|
|
@ -30,6 +31,7 @@
|
|||
"label": "Path"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.event_type==\"HTTP Request\"",
|
||||
"fieldname": "cmd",
|
||||
"fieldtype": "Data",
|
||||
"in_standard_filter": 1,
|
||||
|
|
@ -67,6 +69,7 @@
|
|||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.event_type==\"HTTP Request\"",
|
||||
"fieldname": "request_headers",
|
||||
"fieldtype": "Code",
|
||||
"label": "Request Headers"
|
||||
|
|
@ -76,11 +79,13 @@
|
|||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.event_type==\"HTTP Request\"",
|
||||
"fieldname": "form_dict",
|
||||
"fieldtype": "Code",
|
||||
"label": "Form Dict"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.event_type==\"HTTP Request\"",
|
||||
"fieldname": "method",
|
||||
"fieldtype": "Select",
|
||||
"in_standard_filter": 1,
|
||||
|
|
@ -96,6 +101,12 @@
|
|||
{
|
||||
"fieldname": "section_break_9jhm",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "event_type",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Event Type"
|
||||
}
|
||||
],
|
||||
"hide_toolbar": 1,
|
||||
|
|
@ -103,7 +114,7 @@
|
|||
"index_web_pages_for_search": 1,
|
||||
"is_virtual": 1,
|
||||
"links": [],
|
||||
"modified": "2023-08-10 12:01:03.456643",
|
||||
"modified": "2024-01-03 16:45:47.110048",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Recorder",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class Recorder(Document):
|
|||
|
||||
cmd: DF.Data | None
|
||||
duration: DF.Float
|
||||
event_type: DF.Data | None
|
||||
form_dict: DF.Code | None
|
||||
method: DF.Literal["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]
|
||||
number_of_queries: DF.Int
|
||||
|
|
@ -27,7 +28,6 @@ class Recorder(Document):
|
|||
sql_queries: DF.Table[RecorderQuery]
|
||||
time: DF.Datetime | None
|
||||
time_in_queries: DF.Float
|
||||
|
||||
# end: auto-generated types
|
||||
|
||||
def load_from_db(self):
|
||||
|
|
|
|||
|
|
@ -362,7 +362,8 @@ def rename_fieldname(custom_field: str, fieldname: str):
|
|||
frappe.msgprint(_("Old and new fieldnames are same."), alert=True)
|
||||
return
|
||||
|
||||
frappe.db.rename_column(parent_doctype, old_fieldname, new_fieldname)
|
||||
if frappe.db.has_column(field.dt, old_fieldname):
|
||||
frappe.db.rename_column(parent_doctype, old_fieldname, new_fieldname)
|
||||
|
||||
# Update in DB after alter column is successful, alter column will implicitly commit, so it's
|
||||
# best to commit change on field too to avoid any possible mismatch between two.
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from frappe.model.naming import append_number_if_name_exists
|
|||
from frappe.modules.export_file import export_to_files
|
||||
from frappe.query_builder import Criterion
|
||||
from frappe.query_builder.utils import DocType
|
||||
from frappe.utils import cint
|
||||
from frappe.utils import cint, flt
|
||||
|
||||
|
||||
class NumberCard(Document):
|
||||
|
|
@ -165,7 +165,7 @@ def get_result(doc, filters, to_date=None):
|
|||
)
|
||||
number = res[0]["result"] if res else 0
|
||||
|
||||
return cint(number)
|
||||
return flt(number)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
|
|||
|
|
@ -268,15 +268,15 @@ class EmailAccount(Document):
|
|||
if not in_receive and self.use_imap:
|
||||
email_server.imap.logout()
|
||||
|
||||
# reset failed attempts count
|
||||
self.set_failed_attempts_count(0)
|
||||
|
||||
return email_server
|
||||
|
||||
def check_email_server_connection(self, email_server, in_receive):
|
||||
# tries to connect to email server and handles failure
|
||||
try:
|
||||
email_server.connect()
|
||||
|
||||
# reset failed attempts count - do it after succesful connection
|
||||
self.set_failed_attempts_count(0)
|
||||
except (error_proto, imaplib.IMAP4.error) as e:
|
||||
message = cstr(e).lower().replace(" ", "")
|
||||
auth_error_codes = [
|
||||
|
|
@ -294,6 +294,8 @@ class EmailAccount(Document):
|
|||
error_message = _(
|
||||
"Authentication failed while receiving emails from Email Account: {0}."
|
||||
).format(self.name)
|
||||
|
||||
error_message = _("Email Account Disabled.") + " " + error_message
|
||||
error_message += "<br>" + _("Message from server: {0}").format(cstr(e))
|
||||
self.handle_incoming_connect_error(description=error_message)
|
||||
return None
|
||||
|
|
@ -489,31 +491,35 @@ class EmailAccount(Document):
|
|||
state.pop("_smtp_server_instance", None)
|
||||
|
||||
def handle_incoming_connect_error(self, description):
|
||||
if self.get_failed_attempts_count() > 2:
|
||||
self.db_set("enable_incoming", 0)
|
||||
|
||||
for user in get_system_managers(only_name=True):
|
||||
try:
|
||||
assign_to.add(
|
||||
{
|
||||
"assign_to": user,
|
||||
"doctype": self.doctype,
|
||||
"name": self.name,
|
||||
"description": description,
|
||||
"priority": "High",
|
||||
"notify": 1,
|
||||
}
|
||||
)
|
||||
except assign_to.DuplicateToDoError:
|
||||
frappe.clear_last_message()
|
||||
if self.get_failed_attempts_count() > 5:
|
||||
# This is done in background to avoid committing here.
|
||||
frappe.enqueue(self._disable_broken_incoming_account, description=description)
|
||||
else:
|
||||
self.set_failed_attempts_count(self.get_failed_attempts_count() + 1)
|
||||
|
||||
def _disable_broken_incoming_account(self, description):
|
||||
self.db_set("enable_incoming", 0)
|
||||
|
||||
for user in get_system_managers(only_name=True):
|
||||
try:
|
||||
assign_to.add(
|
||||
{
|
||||
"assign_to": [user],
|
||||
"doctype": self.doctype,
|
||||
"name": self.name,
|
||||
"description": description,
|
||||
"priority": "High",
|
||||
"notify": 1,
|
||||
}
|
||||
)
|
||||
except assign_to.DuplicateToDoError:
|
||||
pass
|
||||
|
||||
def set_failed_attempts_count(self, value):
|
||||
frappe.cache.set(f"{self.name}:email-account-failed-attempts", value)
|
||||
frappe.cache.set_value(f"{self.name}:email-account-failed-attempts", value)
|
||||
|
||||
def get_failed_attempts_count(self):
|
||||
return cint(frappe.cache.get(f"{self.name}:email-account-failed-attempts"))
|
||||
return cint(frappe.cache.get_value(f"{self.name}:email-account-failed-attempts"))
|
||||
|
||||
def receive(self):
|
||||
"""Called by scheduler to receive emails from this EMail account using POP3/IMAP."""
|
||||
|
|
|
|||
|
|
@ -428,6 +428,7 @@ before_request = [
|
|||
|
||||
# Background Job Hooks
|
||||
before_job = [
|
||||
"frappe.recorder.record",
|
||||
"frappe.monitor.start",
|
||||
]
|
||||
|
||||
|
|
@ -438,6 +439,7 @@ if os.getenv("FRAPPE_SENTRY_DSN") and (
|
|||
before_job.append("frappe.utils.sentry.set_sentry_context")
|
||||
|
||||
after_job = [
|
||||
"frappe.recorder.dump",
|
||||
"frappe.monitor.stop",
|
||||
"frappe.utils.file_lock.release_document_locks",
|
||||
"frappe.utils.telemetry.flush",
|
||||
|
|
|
|||
|
|
@ -153,15 +153,14 @@ def get_context(doc):
|
|||
|
||||
|
||||
def enqueue_webhook(doc, webhook) -> None:
|
||||
request_url = headers = data = None
|
||||
try:
|
||||
webhook: Webhook = frappe.get_doc("Webhook", webhook.get("name"))
|
||||
headers = get_webhook_headers(doc, webhook)
|
||||
data = get_webhook_data(doc, webhook)
|
||||
|
||||
request_url = webhook.request_url
|
||||
if webhook.is_dynamic_url:
|
||||
request_url = frappe.render_template(webhook.request_url, get_context(doc))
|
||||
else:
|
||||
request_url = webhook.request_url
|
||||
headers = get_webhook_headers(doc, webhook)
|
||||
data = get_webhook_data(doc, webhook)
|
||||
|
||||
except Exception as e:
|
||||
frappe.logger().debug({"enqueue_webhook_error": e})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
import copy
|
||||
import functools
|
||||
|
||||
import frappe
|
||||
import frappe.share
|
||||
|
|
@ -37,17 +38,23 @@ AUTOMATIC_ROLES = (GUEST_ROLE, ALL_USER_ROLE, SYSTEM_USER_ROLE, ADMIN_ROLE)
|
|||
|
||||
|
||||
def print_has_permission_check_logs(func):
|
||||
@functools.wraps(func)
|
||||
def inner(*args, **kwargs):
|
||||
frappe.flags["has_permission_check_logs"] = []
|
||||
result = func(*args, **kwargs)
|
||||
self_perm_check = True if not kwargs.get("user") else kwargs.get("user") == frappe.session.user
|
||||
raise_exception = kwargs.get("raise_exception", True)
|
||||
self_perm_check = True if not kwargs.get("user") else kwargs.get("user") == frappe.session.user
|
||||
|
||||
if raise_exception:
|
||||
frappe.flags["has_permission_check_logs"] = []
|
||||
|
||||
result = func(*args, **kwargs)
|
||||
|
||||
# print only if access denied
|
||||
# and if user is checking his own permission
|
||||
if not result and self_perm_check and raise_exception:
|
||||
msgprint(("<br>").join(frappe.flags.get("has_permission_check_logs", [])))
|
||||
frappe.flags.pop("has_permission_check_logs", None)
|
||||
|
||||
if raise_exception:
|
||||
frappe.flags.pop("has_permission_check_logs", None)
|
||||
return result
|
||||
|
||||
return inner
|
||||
|
|
@ -163,9 +170,6 @@ def get_doc_permissions(doc, user=None, ptype=None):
|
|||
if not user:
|
||||
user = frappe.session.user
|
||||
|
||||
if frappe.is_table(doc.doctype):
|
||||
return {"read": 1, "write": 1}
|
||||
|
||||
meta = frappe.get_meta(doc.doctype)
|
||||
|
||||
def is_user_owner():
|
||||
|
|
|
|||
|
|
@ -85,33 +85,15 @@ frappe.ui.form.ControlAutocomplete = class ControlAutoComplete extends frappe.ui
|
|||
};
|
||||
}
|
||||
|
||||
init_option_cache() {
|
||||
if (!this.$input.cache) {
|
||||
this.$input.cache = {};
|
||||
}
|
||||
if (!this.$input.cache[this.doctype]) {
|
||||
this.$input.cache[this.doctype] = {};
|
||||
}
|
||||
if (!this.$input.cache[this.doctype][this.df.fieldname]) {
|
||||
this.$input.cache[this.doctype][this.df.fieldname] = {};
|
||||
}
|
||||
}
|
||||
|
||||
setup_awesomplete() {
|
||||
this.awesomplete = new Awesomplete(this.input, this.get_awesomplete_settings());
|
||||
|
||||
$(this.input_area).find(".awesomplete ul").css("min-width", "100%");
|
||||
|
||||
this.init_option_cache();
|
||||
|
||||
this.$input.on(
|
||||
"input",
|
||||
frappe.utils.debounce((e) => {
|
||||
const cached_options =
|
||||
this.$input.cache[this.doctype][this.df.fieldname][e.target.value];
|
||||
if (cached_options && cached_options.length) {
|
||||
this.set_data(cached_options);
|
||||
} else if (this.get_query || this.df.get_query) {
|
||||
if (this.get_query || this.df.get_query) {
|
||||
this.execute_query_if_exists(e.target.value);
|
||||
} else {
|
||||
this.awesomplete.list = this.get_data();
|
||||
|
|
@ -245,7 +227,6 @@ frappe.ui.form.ControlAutocomplete = class ControlAutoComplete extends frappe.ui
|
|||
if (!this.$input.is(":focus")) {
|
||||
return;
|
||||
}
|
||||
this.$input.cache[this.doctype][this.df.fieldname][term] = message;
|
||||
this.set_data(message);
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -429,10 +429,10 @@ export default class GridRow {
|
|||
$(`
|
||||
<div class='form-group'>
|
||||
<div class='row' style='margin:0px; margin-bottom:10px;'>
|
||||
<div class='col-md-8'>
|
||||
<div class='col-6 col-md-8'>
|
||||
${__("Fieldname").bold()}
|
||||
</div>
|
||||
<div class='col-md-4' style='padding-left:5px;'>
|
||||
<div class='col-6 col-md-4' style='padding-left:5px;'>
|
||||
${__("Column Width").bold()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -522,13 +522,13 @@ export default class GridRow {
|
|||
data-label='${docfield.label}' data-type='${docfield.fieldtype}'>
|
||||
|
||||
<div class='row'>
|
||||
<div class='col-md-1' style='padding-top: 4px;'>
|
||||
<div class='col-1' style='padding-top: 4px;'>
|
||||
<a style='cursor: grabbing;'>${frappe.utils.icon("drag", "xs")}</a>
|
||||
</div>
|
||||
<div class='col-md-8' style='padding-right:0px; padding-top: 5px;'>
|
||||
<div class='col-6 col-md-8' style='padding-right:0px; padding-top: 5px;'>
|
||||
${__(docfield.label)}
|
||||
</div>
|
||||
<div class='col-md-2' style='padding-left:0px; padding-top: 2px; margin-top:-2px;' title='${__(
|
||||
<div class='col-3 col-md-2' style='padding-left:0px; padding-top: 2px; margin-top:-2px;' title='${__(
|
||||
"Columns"
|
||||
)}'>
|
||||
<input class='form-control column-width my-1 input-xs text-right'
|
||||
|
|
@ -536,7 +536,7 @@ export default class GridRow {
|
|||
value='${docfield.columns || cint(d.columns)}'
|
||||
data-fieldname='${docfield.fieldname}' style='background-color: var(--modal-bg); display: inline'>
|
||||
</div>
|
||||
<div class='col-md-1' style='padding-top: 3px;'>
|
||||
<div class='col-1' style='padding-top: 3px;'>
|
||||
<a class='text-muted remove-field' data-fieldname='${docfield.fieldname}'>
|
||||
<i class='fa fa-trash-o' aria-hidden='true'></i>
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -114,13 +114,13 @@ export default class ListSettings {
|
|||
data-label="${me.fields[idx].label}" data-type="${me.fields[idx].type}">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-1">
|
||||
<div class="col-1">
|
||||
${frappe.utils.icon("drag", "xs", "", "", "sortable-handle " + show_sortable_handle)}
|
||||
</div>
|
||||
<div class="col-md-10" style="padding-left:0px;">
|
||||
<div class="col-10" style="padding-left:0px;">
|
||||
${me.fields[idx].label}
|
||||
</div>
|
||||
<div class="col-md-1 ${can_remove}">
|
||||
<div class="col-1 ${can_remove}">
|
||||
<a class="text-muted remove-field" data-fieldname="${me.fields[idx].fieldname}">
|
||||
${frappe.utils.icon("delete", "xs")}
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ $("body").on("click", "a", function (e) {
|
|||
const href = target_element.getAttribute("href");
|
||||
const is_on_same_host = target_element.hostname === window.location.hostname;
|
||||
|
||||
if (target_element.getAttribute("target") === "_blank") {
|
||||
return;
|
||||
}
|
||||
|
||||
const override = (route) => {
|
||||
e.preventDefault();
|
||||
frappe.set_route(route);
|
||||
|
|
|
|||
|
|
@ -111,10 +111,9 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
|
|||
|
||||
//Setup groupby for reports
|
||||
this.group_by_control = new frappe.ui.GroupBy(this);
|
||||
if (this.report_doc && this.report_doc.json.group_by) {
|
||||
if (this.report_doc?.json?.group_by) {
|
||||
this.group_by_control.apply_settings(this.report_doc.json.group_by);
|
||||
}
|
||||
if (this.view_user_settings && this.view_user_settings.group_by) {
|
||||
} else if (this.view_user_settings?.group_by) {
|
||||
this.group_by_control.apply_settings(this.view_user_settings.group_by);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -235,7 +235,6 @@ frappe.views.TreeView = class TreeView {
|
|||
method: "frappe.utils.nestedset.rebuild_tree",
|
||||
args: {
|
||||
doctype: me.doctype,
|
||||
parent_field: "parent_" + me.doctype.toLowerCase().replace(/ /g, "_"),
|
||||
},
|
||||
callback: function (r) {
|
||||
if (!r.exc) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default class Card extends Block {
|
|||
|
||||
if (this.data && this.data.card_name) {
|
||||
let has_data = this.make("card", this.data.card_name, "links");
|
||||
if (!has_data) return;
|
||||
if (!has_data) return this.wrapper;
|
||||
}
|
||||
|
||||
if (!this.readOnly) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default class Chart extends Block {
|
|||
|
||||
if (this.data && this.data.chart_name) {
|
||||
let has_data = this.make("chart", this.data.chart_name);
|
||||
if (!has_data) return;
|
||||
if (!has_data) return this.wrapper;
|
||||
}
|
||||
|
||||
if (!this.readOnly) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default class CustomBlock extends Block {
|
|||
|
||||
if (this.data && this.data.custom_block_name) {
|
||||
let has_data = this.make("custom_block", this.data.custom_block_name);
|
||||
if (!has_data) return;
|
||||
if (!has_data) return this.wrapper;
|
||||
}
|
||||
|
||||
if (!this.readOnly) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default class NumberCard extends Block {
|
|||
|
||||
if (this.data && this.data.number_card_name) {
|
||||
let has_data = this.make("number_card", this.data.number_card_name);
|
||||
if (!has_data) return;
|
||||
if (!has_data) return this.wrapper;
|
||||
}
|
||||
|
||||
if (!this.readOnly) {
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export default class Onboarding extends Block {
|
|||
|
||||
if (this.data && this.data.onboarding_name) {
|
||||
let has_data = this.make("onboarding", this.data.onboarding_name);
|
||||
if (!has_data) return;
|
||||
if (!has_data) return this.wrapper;
|
||||
}
|
||||
|
||||
if (!this.readOnly) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default class QuickList extends Block {
|
|||
|
||||
if (this.data && this.data.quick_list_name) {
|
||||
let has_data = this.make("quick_list", this.data.quick_list_name);
|
||||
if (!has_data) return;
|
||||
if (!has_data) return this.wrapper;
|
||||
}
|
||||
|
||||
if (!this.readOnly) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export default class Shortcut extends Block {
|
|||
|
||||
if (this.data && this.data.shortcut_name) {
|
||||
let has_data = this.make("shortcut", this.data.shortcut_name);
|
||||
if (!has_data) return;
|
||||
if (!has_data) return this.wrapper;
|
||||
}
|
||||
|
||||
if (!this.readOnly) {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ class TelemetryManager {
|
|||
|
||||
disable() {
|
||||
this.enabled = false;
|
||||
posthog.opt_out_capturing();
|
||||
}
|
||||
|
||||
can_enable() {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ body {
|
|||
@include media-breakpoint-up(sm) {
|
||||
background-color: var(--bg-light-gray);
|
||||
}
|
||||
.page-content-wrapper {
|
||||
min-height: calc(100vh - 220px);
|
||||
}
|
||||
.web-footer {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import re
|
|||
import time
|
||||
from collections import Counter
|
||||
from collections.abc import Callable
|
||||
from enum import Enum
|
||||
|
||||
import sqlparse
|
||||
|
||||
|
|
@ -21,6 +22,12 @@ RECORDER_REQUEST_HASH = "recorder-requests"
|
|||
TRACEBACK_PATH_PATTERN = re.compile(".*/apps/")
|
||||
|
||||
|
||||
class RecorderEvent(str, Enum):
|
||||
HTTP_REQUEST = "HTTP Request"
|
||||
BACKGROUND_JOB = "Background Job"
|
||||
INVALID = "Invalid"
|
||||
|
||||
|
||||
def sql(*args, **kwargs):
|
||||
start_time = time.monotonic()
|
||||
result = frappe.db._sql(*args, **kwargs)
|
||||
|
|
@ -121,7 +128,10 @@ def normalize_query(query: str) -> str:
|
|||
for token in q.flatten():
|
||||
if "Token.Literal" in str(token.ttype):
|
||||
token.value = "?"
|
||||
return str(q)
|
||||
|
||||
# Transform IN parts like this: IN (?, ?, ?) -> IN (?)
|
||||
q = re.sub(r"( IN )\(\?[\s\n\?\,]*\)", r"\1(?)", str(q), flags=re.IGNORECASE)
|
||||
return q
|
||||
except Exception as e:
|
||||
print("Failed to normalize query ", e)
|
||||
|
||||
|
|
@ -151,7 +161,16 @@ class Recorder:
|
|||
self.method = frappe.request.method
|
||||
self.headers = dict(frappe.local.request.headers)
|
||||
self.form_dict = frappe.local.form_dict
|
||||
self.event_type = RecorderEvent.HTTP_REQUEST
|
||||
elif frappe.job:
|
||||
self.event_type = RecorderEvent.BACKGROUND_JOB
|
||||
self.path = frappe.job.method
|
||||
self.cmd = None
|
||||
self.method = None
|
||||
self.headers = None
|
||||
self.form_dict = None
|
||||
else:
|
||||
self.event_type = RecorderEvent.INVALID
|
||||
self.path = None
|
||||
self.cmd = None
|
||||
self.method = None
|
||||
|
|
@ -173,6 +192,7 @@ class Recorder:
|
|||
"time_queries": float("{:0.3f}".format(sum(call["duration"] for call in self.calls))),
|
||||
"duration": float(f"{(datetime.datetime.now() - self.time).total_seconds() * 1000:0.3f}"),
|
||||
"method": self.method,
|
||||
"event_type": self.event_type,
|
||||
}
|
||||
frappe.cache.hset(RECORDER_REQUEST_SPARSE_HASH, self.uuid, request_data)
|
||||
frappe.publish_realtime(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// don't remove this line (used in test)
|
||||
|
||||
window.disable_signup = {{ disable_signup and "true" or "false" }};
|
||||
window.show_footer_on_login = {{ show_footer_on_login and "true" or "false" }};
|
||||
|
||||
window.login = {};
|
||||
|
||||
|
|
@ -305,6 +306,10 @@ frappe.ready(function () {
|
|||
$(window).trigger("hashchange");
|
||||
}
|
||||
|
||||
if (window.show_footer_on_login) {
|
||||
$("body .web-footer").show();
|
||||
}
|
||||
|
||||
$(".form-signup, .form-forgot, .form-login-with-email-link").removeClass("hide");
|
||||
$(document).trigger('login_rendered');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -361,8 +361,10 @@ class TestEmailIntegrationTest(FrappeTestCase):
|
|||
subject = "checking if email works"
|
||||
content = "is email working?"
|
||||
|
||||
frappe.sendmail(sender=sender, recipients=recipients, subject=subject, content=content, now=True)
|
||||
email = frappe.get_last_doc("Email Queue")
|
||||
email = frappe.sendmail(
|
||||
sender=sender, recipients=recipients, subject=subject, content=content, now=True
|
||||
)
|
||||
email.reload()
|
||||
self.assertEqual(email.sender, sender)
|
||||
self.assertEqual(len(email.recipients), 2)
|
||||
self.assertEqual(email.status, "Sent")
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ class TestNestedSet(FrappeTestCase):
|
|||
leaf_node.reload()
|
||||
|
||||
def test_rebuild_tree(self):
|
||||
rebuild_tree(TEST_DOCTYPE, "parent_test_tree_doctype")
|
||||
rebuild_tree(TEST_DOCTYPE)
|
||||
self.test_basic_tree()
|
||||
|
||||
def test_move_group_into_another(self):
|
||||
|
|
|
|||
|
|
@ -122,7 +122,13 @@ class TestRecorder(FrappeTestCase):
|
|||
frappe.recorder.post_process()
|
||||
|
||||
requests = frappe.recorder.get()
|
||||
request = frappe.recorder.get(requests[0]["uuid"])
|
||||
request = frappe.recorder.get(
|
||||
next(
|
||||
request
|
||||
for request in requests
|
||||
if request["event_type"] == frappe.recorder.RecorderEvent.HTTP_REQUEST
|
||||
)["uuid"]
|
||||
)
|
||||
|
||||
for query, call in zip(queries, request["calls"]):
|
||||
self.assertEqual(call["exact_copies"], query[1])
|
||||
|
|
@ -152,6 +158,7 @@ class TestQueryNormalization(FrappeTestCase):
|
|||
"select * from `user` where a > 5": "select * from `user` where a > ?",
|
||||
"select `name` from `user`": "select `name` from `user`",
|
||||
"select `name` from `user` limit 10": "select `name` from `user` limit ?",
|
||||
"select `name` from `user` where name in ('a', 'b', 'c')": "select `name` from `user` where name in (?)",
|
||||
}
|
||||
|
||||
for query, normalized in test_cases.items():
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ def execute_job(site, method, event, job_name, kwargs, user=None, is_async=True,
|
|||
method_name = method
|
||||
method = frappe.get_attr(method)
|
||||
else:
|
||||
method_name = cstr(method.__name__)
|
||||
method_name = f"{method.__module__}.{method.__qualname__}"
|
||||
|
||||
actual_func_name = kwargs.get("job_type") if "run_scheduled_job" in method_name else method_name
|
||||
setproctitle.setproctitle(f"rq: Started running {actual_func_name} at {time.time()}")
|
||||
|
|
|
|||
|
|
@ -168,11 +168,8 @@ def update_move_node(doc: Document, parent_field: str):
|
|||
|
||||
|
||||
@frappe.whitelist()
|
||||
def rebuild_tree(doctype, parent_field):
|
||||
"""
|
||||
call rebuild_node for all root nodes
|
||||
"""
|
||||
|
||||
def rebuild_tree(doctype: str) -> None:
|
||||
"""Call rebuild_node for all root nodes."""
|
||||
# Check for perm if called from client-side
|
||||
if frappe.request and frappe.local.form_dict.cmd == "rebuild_tree":
|
||||
frappe.only_for("System Manager")
|
||||
|
|
@ -184,6 +181,8 @@ def rebuild_tree(doctype, parent_field):
|
|||
title=_("Invalid Action"),
|
||||
)
|
||||
|
||||
parent_field = meta.nsm_parent_field or f"parent_{frappe.scrub(doctype)}"
|
||||
|
||||
# get all roots
|
||||
right = 1
|
||||
table = DocType(doctype)
|
||||
|
|
@ -327,7 +326,7 @@ class NestedSet(Document):
|
|||
)
|
||||
|
||||
if merge:
|
||||
rebuild_tree(self.doctype, parent_field)
|
||||
rebuild_tree(self.doctype)
|
||||
|
||||
def validate_one_root(self):
|
||||
if not self.get(self.nsm_parent_field):
|
||||
|
|
|
|||
|
|
@ -39,6 +39,18 @@ class TestHelpArticle(FrappeTestCase):
|
|||
self.assertEqual(self.help_article.helpful, 1)
|
||||
self.assertEqual(self.help_article.not_helpful, 1)
|
||||
|
||||
def test_category_disable(self):
|
||||
self.help_article.load_from_db()
|
||||
self.help_article.published = 1
|
||||
self.help_article.save()
|
||||
|
||||
self.help_category.load_from_db()
|
||||
self.help_category.published = 0
|
||||
self.help_category.save()
|
||||
|
||||
self.help_article.load_from_db()
|
||||
self.assertEqual(self.help_article.published, 0)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls) -> None:
|
||||
frappe.delete_doc(cls.help_article.doctype, cls.help_article.name)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ class HelpCategory(WebsiteGenerator):
|
|||
def validate(self):
|
||||
self.set_route()
|
||||
|
||||
# disable help articles of this category
|
||||
if not self.published:
|
||||
for d in frappe.get_all("Help Article", dict(category=self.name)):
|
||||
frappe.db.set_value("Help Article", d.name, "published", 0)
|
||||
|
||||
def set_route(self):
|
||||
if not self.route:
|
||||
self.route = "kb/" + self.scrub(self.category_name)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@
|
|||
"open_in_new_tab",
|
||||
"right",
|
||||
"column_break_5",
|
||||
"parent_label",
|
||||
"icon"
|
||||
"parent_label"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
|
|
@ -50,12 +49,6 @@
|
|||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"description": "If Icon is set, it will be shown instead of Label",
|
||||
"fieldname": "icon",
|
||||
"fieldtype": "Attach Image",
|
||||
"label": "Icon"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.url",
|
||||
|
|
@ -68,12 +61,13 @@
|
|||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-26 20:59:42.142208",
|
||||
"modified": "2024-01-08 12:05:25.782635",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Top Bar Item",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC"
|
||||
"sort_order": "ASC",
|
||||
"states": []
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ class TopBarItem(Document):
|
|||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
icon: DF.AttachImage | None
|
||||
label: DF.Data
|
||||
open_in_new_tab: DF.Check
|
||||
parent: DF.Data
|
||||
|
|
@ -24,4 +23,5 @@ class TopBarItem(Document):
|
|||
right: DF.Check
|
||||
url: DF.Data | None
|
||||
# end: auto-generated types
|
||||
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
{% block header_buttons %}
|
||||
{% if allow_edit and in_view_mode %}
|
||||
<!-- edit button -->
|
||||
<a href="/{{ route }}/{{ doc_name }}/edit" class="edit-button btn btn-default btn-sm">{{ _("Edit Response", null, "Button in web form") }}</a>
|
||||
<a href="/{{ route }}/{{ doc_name }}/edit" class="edit-button btn btn-default btn-sm">{{ _("Edit Response", context="Button in web form") }}</a>
|
||||
{% endif %}
|
||||
|
||||
{% if allow_print and in_view_mode %}
|
||||
|
|
@ -38,10 +38,10 @@
|
|||
{% if not in_view_mode %}
|
||||
<!-- discard button -->
|
||||
<button class="discard-btn btn btn-default btn-sm">
|
||||
{{ _("Discard", null, "Button in web form") }}
|
||||
{{ _("Discard", context="Button in web form") }}
|
||||
</button>
|
||||
<!-- submit button -->
|
||||
<button type="submit" class="submit-btn btn btn-primary btn-sm ml-2">{{ button_label or _("Submit", null, "Button in web form") }}</button>
|
||||
<button type="submit" class="submit-btn btn btn-primary btn-sm ml-2">{{ _(button_label, context="Button in web form") or _("Submit", context="Button in web form") }}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -147,10 +147,10 @@
|
|||
</div>
|
||||
{% else %}
|
||||
{% if show_list %}
|
||||
<a href="/{{ route }}/list" class="show-list-button btn btn-default btn-md">{{ _("See previous responses", null, "Button in web form") }}</a>
|
||||
<a href="/{{ route }}/list" class="show-list-button btn btn-default btn-md">{{ _("See previous responses", context="Button in web form") }}</a>
|
||||
{% endif %}
|
||||
{% if not login_required or allow_multiple %}
|
||||
<a href="/{{ route }}/new" class="new-btn btn btn-default btn-md">{{ _("Submit another response", null, "Button in web form") }}</a>
|
||||
<a href="/{{ route }}/new" class="new-btn btn btn-default btn-md">{{ _("Submit another response", context="Button in web form") }}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ frappe.ui.form.on("Web Form", {
|
|||
reqd: df.reqd,
|
||||
default: df.default,
|
||||
read_only: df.read_only,
|
||||
precision: df.precision,
|
||||
depends_on: df.depends_on,
|
||||
mandatory_depends_on: df.mandatory_depends_on,
|
||||
read_only_depends_on: df.read_only_depends_on,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
"options",
|
||||
"max_length",
|
||||
"max_value",
|
||||
"precision",
|
||||
"property_depends_on_section",
|
||||
"depends_on",
|
||||
"mandatory_depends_on",
|
||||
|
|
@ -143,11 +144,19 @@
|
|||
"fieldtype": "Code",
|
||||
"label": "Read Only Depends On",
|
||||
"options": "JS"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)",
|
||||
"description": "Set non-standard precision for a Float or Currency field",
|
||||
"fieldname": "precision",
|
||||
"fieldtype": "Select",
|
||||
"label": "Precision",
|
||||
"options": "\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-11-21 17:41:52.139191",
|
||||
"modified": "2024-01-08 13:21:06.272248",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Web Form Field",
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ class WebFormField(Document):
|
|||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
precision: DF.Literal["", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
||||
read_only: DF.Check
|
||||
read_only_depends_on: DF.Code | None
|
||||
reqd: DF.Check
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
"misc_section",
|
||||
"app_name",
|
||||
"disable_signup",
|
||||
"show_footer_on_login",
|
||||
"column_break_9",
|
||||
"app_logo",
|
||||
"section_break_6",
|
||||
|
|
@ -49,9 +50,9 @@
|
|||
"footer",
|
||||
"footer_items",
|
||||
"footer_details_section",
|
||||
"hide_footer_signup",
|
||||
"copyright",
|
||||
"footer_logo",
|
||||
"hide_footer_signup",
|
||||
"column_break_37",
|
||||
"address",
|
||||
"footer_powered",
|
||||
|
|
@ -126,7 +127,7 @@
|
|||
"fieldname": "website_theme_image_link",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 1,
|
||||
"label": "Website Theme Image Link"
|
||||
"label": "Website Theme image link"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
|
|
@ -211,7 +212,7 @@
|
|||
"default": "0",
|
||||
"fieldname": "hide_footer_signup",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hide Footer Signup"
|
||||
"label": "Hide footer signup"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
|
|
@ -248,10 +249,10 @@
|
|||
},
|
||||
{
|
||||
"default": "1",
|
||||
"description": "Disable Signups on site. New users will have to be manually registered by system managers.",
|
||||
"description": "New users will have to be manually registered by system managers.",
|
||||
"fieldname": "disable_signup",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable Signup"
|
||||
"label": "Disable signups"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
|
|
@ -282,20 +283,20 @@
|
|||
"description": "To use Google Indexing, enable <a href=\"/app/google-settings\">Google Settings</a>.",
|
||||
"fieldname": "enable_google_indexing",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Google Indexing"
|
||||
"label": "Enable Google indexing"
|
||||
},
|
||||
{
|
||||
"fieldname": "indexing_refresh_token",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Indexing Refresh Token",
|
||||
"label": "Indexing refresh token",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "indexing_authorization_code",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Indexing Authorization Code",
|
||||
"label": "Indexing authorization code",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
|
|
@ -308,7 +309,7 @@
|
|||
"default": "0",
|
||||
"fieldname": "enable_view_tracking",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable In App Website Tracking"
|
||||
"label": "Enable in-app website tracking"
|
||||
},
|
||||
{
|
||||
"fieldname": "footer_logo",
|
||||
|
|
@ -373,7 +374,7 @@
|
|||
"default": "1",
|
||||
"fieldname": "google_analytics_anonymize_ip",
|
||||
"fieldtype": "Check",
|
||||
"label": "Google Analytics Anonymize IP"
|
||||
"label": "Google Analytics anonymise IP"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
|
|
@ -408,13 +409,13 @@
|
|||
"default": "0",
|
||||
"fieldname": "show_account_deletion_link",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Account Deletion Link in My Account Page"
|
||||
"label": "Show account deletion link in My Account page"
|
||||
},
|
||||
{
|
||||
"default": "72",
|
||||
"fieldname": "auto_account_deletion",
|
||||
"fieldtype": "Int",
|
||||
"label": "Auto Account Deletion within (Hours)"
|
||||
"label": "Automatically delete account within (hours)"
|
||||
},
|
||||
{
|
||||
"fieldname": "footer_powered",
|
||||
|
|
@ -469,6 +470,12 @@
|
|||
"fieldname": "analytics_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Analytics"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "show_footer_on_login",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show footer on login"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-cog",
|
||||
|
|
@ -476,7 +483,7 @@
|
|||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2023-12-08 15:52:37.525003",
|
||||
"modified": "2024-01-08 11:50:34.750809",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Website Settings",
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ class WebsiteSettings(Document):
|
|||
robots_txt: DF.Code | None
|
||||
route_redirects: DF.Table[WebsiteRouteRedirect]
|
||||
show_account_deletion_link: DF.Check
|
||||
show_footer_on_login: DF.Check
|
||||
show_language_picker: DF.Check
|
||||
splash_image: DF.AttachImage | None
|
||||
subdomain: DF.SmallText | None
|
||||
|
|
@ -63,8 +64,8 @@ class WebsiteSettings(Document):
|
|||
top_bar_items: DF.Table[TopBarItem]
|
||||
website_theme: DF.Link | None
|
||||
website_theme_image_link: DF.Code | None
|
||||
|
||||
# end: auto-generated types
|
||||
|
||||
def validate(self):
|
||||
self.validate_top_bar_items()
|
||||
self.validate_footer_items()
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ def get_context(context):
|
|||
context["hide_login"] = True # dont show login link on login page again.
|
||||
context["provider_logins"] = []
|
||||
context["disable_signup"] = cint(frappe.get_website_settings("disable_signup"))
|
||||
context["show_footer_on_login"] = cint(frappe.get_website_settings("show_footer_on_login"))
|
||||
context["disable_user_pass_login"] = cint(frappe.get_system_settings("disable_user_pass_login"))
|
||||
context["logo"] = frappe.get_website_settings("app_logo") or frappe.get_hooks("app_logo_url")[-1]
|
||||
context["app_name"] = (
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
},
|
||||
"homepage": "https://frappeframework.com",
|
||||
"dependencies": {
|
||||
"@editorjs/editorjs": "~2.26.3",
|
||||
"@editorjs/editorjs": "^2.28.2",
|
||||
"@frappe/esbuild-plugin-postcss2": "^0.1.3",
|
||||
"@headlessui/vue": "^1.7.16",
|
||||
"@popperjs/core": "^2.11.2",
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-glob": "^3.2.5",
|
||||
"frappe-charts": "2.0.0-rc22",
|
||||
"frappe-datatable": "1.17.13",
|
||||
"frappe-datatable": "1.17.14",
|
||||
"frappe-gantt": "^0.6.0",
|
||||
"highlight.js": "^10.4.1",
|
||||
"html5-qrcode": "^2.3.8",
|
||||
|
|
|
|||
44
yarn.lock
44
yarn.lock
|
|
@ -31,21 +31,10 @@
|
|||
"@babel/helper-validator-identifier" "^7.22.20"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@codexteam/icons@0.1.0":
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.1.0.tgz#a02885fe8699f69902d05b077b5f1cd48a2ca6b9"
|
||||
integrity sha512-jW1fWnwtWzcP4FBGsaodbJY3s1ZaRU+IJy1pvJ7ygNQxkQinybJcwXoyt0a5mWwu/4w30A42EWhCrZn8lp4fdw==
|
||||
|
||||
"@editorjs/editorjs@~2.26.3":
|
||||
version "2.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@editorjs/editorjs/-/editorjs-2.26.5.tgz#ee0f1dbd3a3c6ba97d3ed30f13ab7d2e7b29dbe4"
|
||||
integrity sha512-imwXZi9NmzxKjNosa1xQf286liJYsTe2J2DWCiV5TwKhvYZ1INg5Y+FietcM2v65QmeLqP7wgBUhoI7wiCB+yQ==
|
||||
dependencies:
|
||||
"@codexteam/icons" "0.1.0"
|
||||
codex-notifier "^1.1.2"
|
||||
codex-tooltip "^1.0.5"
|
||||
html-janitor "^2.0.4"
|
||||
nanoid "^3.1.22"
|
||||
"@editorjs/editorjs@^2.28.2":
|
||||
version "2.28.2"
|
||||
resolved "https://registry.yarnpkg.com/@editorjs/editorjs/-/editorjs-2.28.2.tgz#a265c7d10e83adef81813e4dc0f01fe3464dff50"
|
||||
integrity sha512-g6V0Nd3W9IIWMpvxDNTssQ6e4kxBp1Y0W4GIf8cXRlmcBp3TUjrgCYJQmNy3l2a6ZzhyBAoVSe8krJEq4g7PQw==
|
||||
|
||||
"@esbuild/linux-loong64@0.14.54":
|
||||
version "0.14.54"
|
||||
|
|
@ -680,16 +669,6 @@ cluster-key-slot@1.1.2:
|
|||
resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac"
|
||||
integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==
|
||||
|
||||
codex-notifier@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/codex-notifier/-/codex-notifier-1.1.2.tgz#a733079185f4c927fa296f1d71eb8753fe080895"
|
||||
integrity sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg==
|
||||
|
||||
codex-tooltip@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/codex-tooltip/-/codex-tooltip-1.0.5.tgz#ba25fd5b3a58ba2f73fd667c2b46987ffd1edef2"
|
||||
integrity sha512-IuA8LeyLU5p1B+HyhOsqR6oxyFQ11k3i9e9aXw40CrHFTRO2Y1npNBVU3W1SvhKAbUU7R/YikUBdcYFP0RcJag==
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
|
|
@ -1497,10 +1476,10 @@ frappe-charts@2.0.0-rc22:
|
|||
resolved "https://registry.yarnpkg.com/frappe-charts/-/frappe-charts-2.0.0-rc22.tgz#9a5a747febdc381a1d4d7af96e89cf519dfba8c0"
|
||||
integrity sha512-N7f/8979wJCKjusOinaUYfMxB80YnfuVLrSkjpj4LtyqS0BGS6SuJxUnb7Jl4RWUFEIs7zEhideIKnyLeFZF4Q==
|
||||
|
||||
frappe-datatable@1.17.13:
|
||||
version "1.17.13"
|
||||
resolved "https://registry.yarnpkg.com/frappe-datatable/-/frappe-datatable-1.17.13.tgz#349a15fd102b8abe55ab903c65de074aa66a26d6"
|
||||
integrity sha512-rHzLjuAbWdbqEZbU6RCcimyeTswdIKtl8WJLjctj5zze7w5DFSQX0iDTbKNBG7TehS7YqP4k+ySIhvkHRELq3w==
|
||||
frappe-datatable@1.17.14:
|
||||
version "1.17.14"
|
||||
resolved "https://registry.yarnpkg.com/frappe-datatable/-/frappe-datatable-1.17.14.tgz#5fe99fa45089d6f2a54d13215608ef777bc947ec"
|
||||
integrity sha512-rrUyk+8ueX9ADDXwaHobBGmAWK86lF3P3yc3KYGHyhNiNTwKpUW08zQeuTUzJnWv0OSZ/zXYePzrjFKG7ZR4Wg==
|
||||
dependencies:
|
||||
hyperlist "^1.0.0-beta"
|
||||
lodash "^4.17.5"
|
||||
|
|
@ -1704,11 +1683,6 @@ homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1:
|
|||
dependencies:
|
||||
parse-passwd "^1.0.0"
|
||||
|
||||
html-janitor@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/html-janitor/-/html-janitor-2.0.4.tgz#ae5a115cdf3331cd5501edd7b5471b18ea44cdbb"
|
||||
integrity sha512-92J5h9jNZRk30PMHapjHEJfkrBWKCOy0bq3oW2pBungky6lzYSoboBGPMvxl1XRKB2q+kniQmsLsPbdpY7RM2g==
|
||||
|
||||
html5-qrcode@^2.3.8:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/html5-qrcode/-/html5-qrcode-2.3.8.tgz#0b0cdf7a9926cfd4be530e13a51db47592adfa0d"
|
||||
|
|
@ -2263,7 +2237,7 @@ ms@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
nanoid@^3.1.22, nanoid@^3.3.6:
|
||||
nanoid@^3.3.6:
|
||||
version "3.3.7"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
|
||||
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue