Merge branch 'develop' into fix-20689
This commit is contained in:
commit
c4ecd33357
28 changed files with 283 additions and 115 deletions
|
|
@ -149,18 +149,26 @@ def get_permitted_and_not_permitted_links(doctype):
|
|||
return {"permitted_links": permitted_links, "not_permitted_links": not_permitted_links}
|
||||
|
||||
|
||||
def delete_contact_and_address(doctype, docname):
|
||||
def delete_contact_and_address(doctype: str, docname: str) -> None:
|
||||
for parenttype in ("Contact", "Address"):
|
||||
items = frappe.db.sql_list(
|
||||
"""select parent from `tabDynamic Link`
|
||||
where parenttype=%s and link_doctype=%s and link_name=%s""",
|
||||
(parenttype, doctype, docname),
|
||||
)
|
||||
|
||||
for name in items:
|
||||
for name in frappe.get_all(
|
||||
"Dynamic Link",
|
||||
filters={
|
||||
"parenttype": parenttype,
|
||||
"link_doctype": doctype,
|
||||
"link_name": docname,
|
||||
},
|
||||
pluck="parent",
|
||||
):
|
||||
doc = frappe.get_doc(parenttype, name)
|
||||
if len(doc.links) == 1:
|
||||
doc.delete()
|
||||
else:
|
||||
for link in doc.links:
|
||||
if link.link_doctype == doctype and link.link_name == docname:
|
||||
doc.remove(link)
|
||||
doc.save()
|
||||
break
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,24 @@
|
|||
frappe.ui.form.on("Document Naming Rule", {
|
||||
refresh: function (frm) {
|
||||
frm.trigger("document_type");
|
||||
if (!frm.doc.__islocal) frm.trigger("add_update_counter_button");
|
||||
frm.last_counter_value = frm.doc.counter;
|
||||
frm.skip_before_save = false;
|
||||
},
|
||||
before_save: function (frm) {
|
||||
if (frm.is_new() || frm.skip_before_save || frm.last_counter_value === frm.doc.counter)
|
||||
return;
|
||||
|
||||
frappe.validated = false;
|
||||
frappe.warn(
|
||||
__("Are you sure?"),
|
||||
__("Updating counter may lead to document name conflicts if not done properly"),
|
||||
() => {
|
||||
frm.skip_before_save = true;
|
||||
frm.save();
|
||||
},
|
||||
__("Proceed"),
|
||||
false
|
||||
);
|
||||
},
|
||||
document_type: (frm) => {
|
||||
// update the select field options with fieldnames
|
||||
|
|
@ -26,45 +43,4 @@ frappe.ui.form.on("Document Naming Rule", {
|
|||
});
|
||||
}
|
||||
},
|
||||
add_update_counter_button: (frm) => {
|
||||
frm.add_custom_button(__("Update Counter"), function () {
|
||||
const fields = [
|
||||
{
|
||||
fieldtype: "Data",
|
||||
fieldname: "new_counter",
|
||||
label: __("New Counter"),
|
||||
default: frm.doc.counter,
|
||||
reqd: 1,
|
||||
description: __(
|
||||
"Warning: Updating counter may lead to document name conflicts if not done properly"
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
let primary_action_label = __("Save");
|
||||
|
||||
let primary_action = (fields) => {
|
||||
frappe.call({
|
||||
method: "frappe.core.doctype.document_naming_rule.document_naming_rule.update_current",
|
||||
args: {
|
||||
name: frm.doc.name,
|
||||
new_counter: fields.new_counter,
|
||||
},
|
||||
callback: function () {
|
||||
frm.set_value("counter", fields.new_counter);
|
||||
dialog.hide();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __("Update Counter Value for Prefix: {0}", [frm.doc.prefix]),
|
||||
fields,
|
||||
primary_action_label,
|
||||
primary_action,
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@
|
|||
"conditions",
|
||||
"naming_section",
|
||||
"prefix",
|
||||
"prefix_digits",
|
||||
"counter"
|
||||
"counter",
|
||||
"column_break_xfqa",
|
||||
"prefix_digits"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
|
|
@ -22,7 +23,8 @@
|
|||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Document Type",
|
||||
"options": "DocType"
|
||||
"options": "DocType",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
|
|
@ -38,11 +40,12 @@
|
|||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Warning: Updating counter may lead to document name conflicts if not done properly",
|
||||
"fieldname": "counter",
|
||||
"fieldtype": "Int",
|
||||
"label": "Counter",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
"no_copy": 1
|
||||
},
|
||||
{
|
||||
"default": "5",
|
||||
|
|
@ -76,11 +79,15 @@
|
|||
"fieldname": "priority",
|
||||
"fieldtype": "Int",
|
||||
"label": "Priority"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_xfqa",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-09-13 20:07:47.617615",
|
||||
"modified": "2023-04-24 15:14:32.054272",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Document Naming Rule",
|
||||
|
|
@ -102,6 +109,7 @@
|
|||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "document_type",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -47,9 +47,3 @@ class DocumentNamingRule(Document):
|
|||
|
||||
doc.name = naming_series + ("%0" + str(self.prefix_digits) + "d") % (counter + 1)
|
||||
frappe.db.set_value(self.doctype, self.name, "counter", counter + 1)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_current(name, new_counter):
|
||||
frappe.only_for("System Manager")
|
||||
frappe.db.set_value("Document Naming Rule", name, "counter", new_counter)
|
||||
|
|
|
|||
|
|
@ -76,7 +76,9 @@
|
|||
"max_auto_email_report_per_user",
|
||||
"system_updates_section",
|
||||
"disable_system_update_notification",
|
||||
"disable_change_log_notification"
|
||||
"disable_change_log_notification",
|
||||
"telemetry_section",
|
||||
"enable_telemetry"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
|
|
@ -535,12 +537,24 @@
|
|||
"fieldname": "disable_document_sharing",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable Document Sharing"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "telemetry_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Telemetry"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "enable_telemetry",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Sending Usage Data for Improving applications"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-cog",
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2023-03-14 11:30:56.465653",
|
||||
"modified": "2023-04-23 11:14:59.302851",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "System Settings",
|
||||
|
|
|
|||
|
|
@ -114,9 +114,9 @@ frappe.ui.form.on("User", {
|
|||
return;
|
||||
}
|
||||
|
||||
function hasChanged(doc_attr, boot_attr) {
|
||||
return (doc_attr || boot_attr) && doc_attr !== boot_attr;
|
||||
}
|
||||
const hasChanged = (doc_attr, boot_attr) => {
|
||||
return doc_attr && boot_attr && doc_attr !== boot_attr;
|
||||
};
|
||||
|
||||
if (
|
||||
doc.name === frappe.session.user &&
|
||||
|
|
|
|||
|
|
@ -147,35 +147,36 @@ def add_multiple(args=None):
|
|||
def close_all_assignments(doctype, name):
|
||||
assignments = frappe.get_all(
|
||||
"ToDo",
|
||||
fields=["allocated_to"],
|
||||
fields=["allocated_to", "name"],
|
||||
filters=dict(reference_type=doctype, reference_name=name, status=("!=", "Cancelled")),
|
||||
)
|
||||
if not assignments:
|
||||
return False
|
||||
|
||||
for assign_to in assignments:
|
||||
set_status(doctype, name, assign_to.allocated_to, status="Closed")
|
||||
set_status(doctype, name, todo=assign_to.name, assign_to=assign_to.allocated_to, status="Closed")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def remove(doctype, name, assign_to):
|
||||
return set_status(doctype, name, assign_to, status="Cancelled")
|
||||
return set_status(doctype, name, "", assign_to, status="Cancelled")
|
||||
|
||||
|
||||
def set_status(doctype, name, assign_to, status="Cancelled"):
|
||||
def set_status(doctype, name, todo=None, assign_to=None, status="Cancelled"):
|
||||
"""remove from todo"""
|
||||
try:
|
||||
todo = frappe.db.get_value(
|
||||
"ToDo",
|
||||
{
|
||||
"reference_type": doctype,
|
||||
"reference_name": name,
|
||||
"allocated_to": assign_to,
|
||||
"status": ("!=", status),
|
||||
},
|
||||
)
|
||||
if not todo:
|
||||
todo = frappe.db.get_value(
|
||||
"ToDo",
|
||||
{
|
||||
"reference_type": doctype,
|
||||
"reference_name": name,
|
||||
"allocated_to": assign_to,
|
||||
"status": ("!=", status),
|
||||
},
|
||||
)
|
||||
if todo:
|
||||
todo = frappe.get_doc("ToDo", todo)
|
||||
todo.status = status
|
||||
|
|
@ -197,13 +198,17 @@ def clear(doctype, name):
|
|||
Clears assignments, return False if not assigned.
|
||||
"""
|
||||
assignments = frappe.get_all(
|
||||
"ToDo", fields=["allocated_to"], filters=dict(reference_type=doctype, reference_name=name)
|
||||
"ToDo",
|
||||
fields=["allocated_to", "name"],
|
||||
filters=dict(reference_type=doctype, reference_name=name),
|
||||
)
|
||||
if not assignments:
|
||||
return False
|
||||
|
||||
for assign_to in assignments:
|
||||
set_status(doctype, name, assign_to.allocated_to, "Cancelled")
|
||||
set_status(
|
||||
doctype, name, todo=assign_to.name, assign_to=assign_to.allocated_to, status="Cancelled"
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ frappe.setup.SetupWizard = class SetupWizard extends frappe.ui.Slides {
|
|||
}
|
||||
|
||||
action_on_complete() {
|
||||
frappe.telemetry.capture("initated_client_side", "setup");
|
||||
if (!this.current_slide.set_values()) return;
|
||||
this.update_values();
|
||||
this.show_working_state();
|
||||
|
|
@ -325,6 +326,7 @@ frappe.setup.SetupWizardSlide = class SetupWizardSlide extends frappe.ui.Slide {
|
|||
make() {
|
||||
super.make();
|
||||
this.set_init_values();
|
||||
this.setup_telemetry_events();
|
||||
this.reset_action_button_state();
|
||||
}
|
||||
|
||||
|
|
@ -340,6 +342,18 @@ frappe.setup.SetupWizardSlide = class SetupWizardSlide extends frappe.ui.Slide {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
setup_telemetry_events() {
|
||||
let me = this;
|
||||
this.fields.filter(frappe.model.is_value_type).forEach((field) => {
|
||||
me.get_input(field.fieldname).on("change", function () {
|
||||
frappe.telemetry.capture(`${field.fieldname}_set`, "setup");
|
||||
if (field.fieldname == "enable_telemetry" && !me.get_value("enable_telemetry")) {
|
||||
frappe.telemetry.disable();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Frappe slides settings
|
||||
|
|
@ -384,6 +398,15 @@ frappe.setup.slides_settings = [
|
|||
fieldtype: "Select",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldtype: "Section Break",
|
||||
},
|
||||
{
|
||||
fieldname: "enable_telemetry",
|
||||
label: __("Allow Sending Usage Data for Improving applications"),
|
||||
fieldtype: "Check",
|
||||
default: 1,
|
||||
},
|
||||
],
|
||||
|
||||
onload: function (slide) {
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ def setup_complete(args):
|
|||
|
||||
@frappe.task()
|
||||
def process_setup_stages(stages, user_input, is_background_task=False):
|
||||
from frappe.utils.telemetry import capture
|
||||
|
||||
capture("initated_server_side", "setup")
|
||||
try:
|
||||
frappe.flags.in_setup_wizard = True
|
||||
current_task = None
|
||||
|
|
@ -88,6 +91,7 @@ def process_setup_stages(stages, user_input, is_background_task=False):
|
|||
)
|
||||
else:
|
||||
run_setup_success(user_input)
|
||||
capture("completed_server_side", "setup")
|
||||
if not is_background_task:
|
||||
return {"status": "ok"}
|
||||
frappe.publish_realtime("setup_task", {"status": "ok"}, user=frappe.session.user)
|
||||
|
|
@ -171,6 +175,7 @@ def update_system_settings(args):
|
|||
"number_format": number_format,
|
||||
"enable_scheduler": 1 if not frappe.flags.in_test else 0,
|
||||
"backup_limit": 3, # Default for downloadable backups
|
||||
"enable_telemetry": cint(args.get("enable_telemetry")),
|
||||
}
|
||||
)
|
||||
system_settings.save()
|
||||
|
|
|
|||
|
|
@ -81,7 +81,15 @@ def search_widget(
|
|||
try:
|
||||
is_whitelisted(frappe.get_attr(query))
|
||||
frappe.response["values"] = frappe.call(
|
||||
query, doctype, txt, searchfield, start, page_length, filters, as_dict=as_dict
|
||||
query,
|
||||
doctype,
|
||||
txt,
|
||||
searchfield,
|
||||
start,
|
||||
page_length,
|
||||
filters,
|
||||
as_dict=as_dict,
|
||||
reference_doctype=reference_doctype,
|
||||
)
|
||||
except frappe.exceptions.PermissionError as e:
|
||||
if frappe.local.conf.developer_mode:
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ class SendMailContext:
|
|||
# Note: smtp session will have to be manually closed
|
||||
self.retain_smtp_session = bool(smtp_server_instance)
|
||||
|
||||
self.sent_to = [rec.recipient for rec in self.queue_doc.recipients if rec.is_main_sent()]
|
||||
self.sent_to = [rec.recipient for rec in self.queue_doc.recipients if rec.is_mail_sent()]
|
||||
|
||||
def __enter__(self):
|
||||
self.queue_doc.update_status(status="Sending", commit=True)
|
||||
|
|
@ -213,7 +213,6 @@ class SendMailContext:
|
|||
exceptions = [
|
||||
smtplib.SMTPServerDisconnected,
|
||||
smtplib.SMTPAuthenticationError,
|
||||
smtplib.SMTPRecipientsRefused,
|
||||
smtplib.SMTPConnectError,
|
||||
smtplib.SMTPHeloError,
|
||||
JobTimeoutException,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class EmailQueueRecipient(Document):
|
|||
def is_mail_to_be_sent(self):
|
||||
return self.status == "Not Sent"
|
||||
|
||||
def is_main_sent(self):
|
||||
def is_mail_sent(self):
|
||||
return self.status == "Sent"
|
||||
|
||||
def update_db(self, commit=False, **kwargs):
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ app_include_js = [
|
|||
"form.bundle.js",
|
||||
"controls.bundle.js",
|
||||
"report.bundle.js",
|
||||
"telemetry.bundle.js",
|
||||
]
|
||||
app_include_css = [
|
||||
"desk.bundle.css",
|
||||
|
|
@ -419,3 +420,7 @@ after_job = [
|
|||
"frappe.monitor.stop",
|
||||
"frappe.utils.file_lock.release_document_locks",
|
||||
]
|
||||
|
||||
extend_bootinfo = [
|
||||
"frappe.utils.telemetry.add_bootinfo",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ def authorize_access(g_calendar, reauthorize=None):
|
|||
"""
|
||||
google_settings = frappe.get_doc("Google Settings")
|
||||
google_calendar = frappe.get_doc("Google Calendar", g_calendar)
|
||||
google_calendar.check_permission("write")
|
||||
|
||||
redirect_uri = (
|
||||
get_request_site_address(True)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"actions": [],
|
||||
"autoname": "format:GC-{email_id}",
|
||||
"creation": "2019-06-14 00:09:39.441961",
|
||||
"doctype": "DocType",
|
||||
|
|
@ -97,10 +98,12 @@
|
|||
"label": "Push to Google Contacts"
|
||||
}
|
||||
],
|
||||
"modified": "2020-09-18 17:26:09.703215",
|
||||
"links": [],
|
||||
"modified": "2023-03-30 11:25:48.832384",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Integrations",
|
||||
"name": "Google Contacts",
|
||||
"naming_rule": "Expression",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
|
|
@ -116,17 +119,14 @@
|
|||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"if_owner": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -36,10 +36,10 @@ def authorize_access(g_contact, reauthorize=False, code=None):
|
|||
If no Authorization code get it from Google and then request for Refresh Token.
|
||||
Google Contact Name is set to flags to set_value after Authorization Code is obtained.
|
||||
"""
|
||||
contact = frappe.get_doc("Google Contacts", g_contact)
|
||||
contact.check_permission("write")
|
||||
|
||||
oauth_code = (
|
||||
frappe.db.get_value("Google Contacts", g_contact, "authorization_code") if not code else code
|
||||
)
|
||||
oauth_code = code or contact.get_password("authorization_code")
|
||||
oauth_obj = GoogleOAuth("contacts")
|
||||
|
||||
if not oauth_code or reauthorize:
|
||||
|
|
@ -51,11 +51,9 @@ def authorize_access(g_contact, reauthorize=False, code=None):
|
|||
)
|
||||
|
||||
r = oauth_obj.authorize(oauth_code)
|
||||
frappe.db.set_value(
|
||||
"Google Contacts",
|
||||
g_contact,
|
||||
{"authorization_code": oauth_code, "refresh_token": r.get("refresh_token")},
|
||||
)
|
||||
contact.authorization_code = oauth_code
|
||||
contact.refresh_token = r.get("refresh_token")
|
||||
contact.save()
|
||||
|
||||
|
||||
def get_google_contacts_object(g_contact):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2023, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
|
||||
class TestGoogleContacts(FrappeTestCase):
|
||||
pass
|
||||
|
|
@ -348,11 +348,9 @@ $.extend(frappe.model, {
|
|||
},
|
||||
|
||||
unscrub: function (txt) {
|
||||
return __(txt || "")
|
||||
.replace(/-|_/g, " ")
|
||||
.replace(/\w*/g, function (keywords) {
|
||||
return keywords.charAt(0).toUpperCase() + keywords.substr(1).toLowerCase();
|
||||
});
|
||||
return (txt || "").replace(/-|_/g, " ").replace(/\w*/g, function (keywords) {
|
||||
return keywords.charAt(0).toUpperCase() + keywords.substr(1).toLowerCase();
|
||||
});
|
||||
},
|
||||
|
||||
can_create: function (doctype) {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ frappe.RoleEditor = class {
|
|||
const $body = $(this.perm_dialog.body);
|
||||
if (!permissions.length) {
|
||||
$body.append(`<div class="text-muted text-center padding">
|
||||
${__("{0} role does not have permission on any doctype", [role])}
|
||||
${__("{0} role does not have permission on any doctype", [__(role)])}
|
||||
</div>`);
|
||||
} else {
|
||||
$body.append(`
|
||||
|
|
@ -68,7 +68,7 @@ frappe.RoleEditor = class {
|
|||
<tr>
|
||||
<th> ${__("Document Type")} </th>
|
||||
<th> ${__("Level")} </th>
|
||||
${frappe.perm.rights.map((p) => `<th> ${frappe.unscrub(p)}</th>`).join("")}
|
||||
${frappe.perm.rights.map((p) => `<th> ${__(frappe.unscrub(p))}</th>`).join("")}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
|
|
@ -77,7 +77,7 @@ frappe.RoleEditor = class {
|
|||
permissions.forEach((perm) => {
|
||||
$body.find("tbody").append(`
|
||||
<tr>
|
||||
<td>${perm.parent}</td>
|
||||
<td>${__(perm.parent)}</td>
|
||||
<td>${perm.permlevel}</td>
|
||||
${frappe.perm.rights
|
||||
.map(
|
||||
|
|
@ -91,7 +91,7 @@ frappe.RoleEditor = class {
|
|||
`);
|
||||
});
|
||||
}
|
||||
this.perm_dialog.set_title(role);
|
||||
this.perm_dialog.set_title(__(role));
|
||||
this.perm_dialog.show();
|
||||
});
|
||||
}
|
||||
|
|
@ -102,8 +102,10 @@ frappe.RoleEditor = class {
|
|||
|
||||
this.perm_dialog.$wrapper
|
||||
.find(".modal-dialog")
|
||||
.css("width", "1200px")
|
||||
.css("max-width", "80vw");
|
||||
.css("width", "auto")
|
||||
.css("max-width", "1200px");
|
||||
|
||||
this.perm_dialog.$wrapper.find(".modal-body").css("overflow", "overlay");
|
||||
}
|
||||
show() {
|
||||
this.reset();
|
||||
|
|
|
|||
1
frappe/public/js/lib/posthog.js
Normal file
1
frappe/public/js/lib/posthog.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
|
||||
|
|
@ -130,7 +130,7 @@ onMounted(() => {
|
|||
margin-top: auto;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
.preview-control >>> .form-control {
|
||||
.preview-control :deep(.form-control) {
|
||||
background: var(--control-bg-on-gray);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ watch(print_format, () => (store.dirty.value = true), { deep: true });
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.control-font >>> .frappe-control[data-fieldname="font"] label {
|
||||
.control-font :deep(.frappe-control[data-fieldname="font"] label) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
1
frappe/public/js/telemetry.bundle.js
Normal file
1
frappe/public/js/telemetry.bundle.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import "./telemetry/index.js";
|
||||
44
frappe/public/js/telemetry/index.js
Normal file
44
frappe/public/js/telemetry/index.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import "../lib/posthog.js";
|
||||
|
||||
class TelemetryManager {
|
||||
constructor() {
|
||||
this.enabled = false;
|
||||
|
||||
this.project_id = frappe.boot.posthog_project_id;
|
||||
this.telemetry_host = frappe.boot.posthog_host;
|
||||
|
||||
if (cint(frappe.boot.enable_telemetry) && this.project_id && this.telemetry_host) {
|
||||
this.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
initialize() {
|
||||
if (!this.enabled) return;
|
||||
try {
|
||||
posthog.init(this.project_id, {
|
||||
api_host: this.telemetry_host,
|
||||
autocapture: false,
|
||||
capture_pageview: false,
|
||||
capture_pageleave: false,
|
||||
advanced_disable_decide: true,
|
||||
});
|
||||
posthog.identify(frappe.boot.sitename);
|
||||
} catch (e) {
|
||||
console.trace("Failed to initialize telemetry", e);
|
||||
this.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
capture(event, app) {
|
||||
if (!this.enabled) return;
|
||||
posthog.capture(`${app}_${event}`);
|
||||
}
|
||||
|
||||
disable() {
|
||||
this.enabled = false;
|
||||
posthog.opt_out_capturing();
|
||||
}
|
||||
}
|
||||
|
||||
frappe.telemetry = new TelemetryManager();
|
||||
frappe.telemetry.initialize();
|
||||
|
|
@ -130,12 +130,32 @@ class TestSearch(FrappeTestCase):
|
|||
search_link("User", "user@random", searchfield="name")
|
||||
self.assertListEqual(frappe.response["results"], [])
|
||||
|
||||
def test_reference_doctype(self):
|
||||
"""search query methods should get reference_doctype if they want"""
|
||||
search_link(
|
||||
doctype="User",
|
||||
txt="",
|
||||
filters=None,
|
||||
page_length=20,
|
||||
reference_doctype="ToDo",
|
||||
query="frappe.tests.test_search.query_with_reference_doctype",
|
||||
)
|
||||
self.assertListEqual(frappe.response["results"], [])
|
||||
|
||||
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_data(doctype, txt, searchfield, start, page_len, filters):
|
||||
return [doctype, txt, searchfield, start, page_len, filters]
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def query_with_reference_doctype(
|
||||
doctype, txt, searchfield, start, page_len, filters, reference_doctype=None
|
||||
):
|
||||
return []
|
||||
|
||||
|
||||
def setup_test_link_field_order(TestCase):
|
||||
TestCase.tree_doctype_name = "Test Tree Order"
|
||||
TestCase.child_doctype_list = []
|
||||
|
|
|
|||
|
|
@ -2290,8 +2290,8 @@ Setup Auto Email,Einstellungen Auto E-Mail,
|
|||
Setup Complete,Einrichtung abgeschlossen,
|
||||
Setup Notifications based on various criteria.,Setup Benachrichtigungen basierend auf verschiedenen Kriterien.,
|
||||
Setup Reports to be emailed at regular intervals,Berichte regelmäßig per E-Mail senden,
|
||||
"Setup of top navigation bar, footer and logo.","Einrichten der oberen Navigationsleiste, der Fußzeile und des Logos",
|
||||
Share,Aktie,
|
||||
"Setup of top navigation bar, footer and logo.","Einrichten der oberen Navigationsleiste, der Fußzeile und des Logos",
|
||||
Share,Freigeben,
|
||||
Share URL,URL teilen,
|
||||
Share With,Freigeben für,
|
||||
Share this document with,Dieses Dokument teilen mit,
|
||||
|
|
@ -4840,3 +4840,4 @@ Non-numeric,Nicht-numerische,
|
|||
Minimal,Minimal,
|
||||
This value is fetched from {0}'s {1} field,Dieser Wert ergibt sich aus dem Feld {1} von {0},
|
||||
This form is not editable due to a Workflow.,Dieses Formular kann in diesem Workflow-Status nicht bearbeitet werden.,
|
||||
{0} role does not have permission on any doctype,Die Rolle {0} hat auf keinen DocType Zugriff,
|
||||
|
|
|
|||
|
47
frappe/utils/telemetry.py
Normal file
47
frappe/utils/telemetry.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
""" Basic telemetry for improving apps.
|
||||
|
||||
WARNING: Everything in this file should be treated "internal" and is subjected to change or get
|
||||
removed without any warning.
|
||||
"""
|
||||
from contextlib import suppress
|
||||
|
||||
from posthog import Posthog
|
||||
|
||||
import frappe
|
||||
|
||||
POSTHOG_PROJECT_FIELD = "posthog_project_id"
|
||||
POSTHOG_HOST_FIELD = "posthog_host"
|
||||
|
||||
|
||||
def add_bootinfo(bootinfo):
|
||||
if not frappe.get_system_settings("enable_telemetry"):
|
||||
return
|
||||
|
||||
bootinfo.posthog_host = frappe.conf.get(POSTHOG_HOST_FIELD)
|
||||
bootinfo.posthog_project_id = frappe.conf.get(POSTHOG_PROJECT_FIELD)
|
||||
bootinfo.enable_telemetry = True
|
||||
|
||||
|
||||
def init_telemetry():
|
||||
"""Init posthog for server side telemetry."""
|
||||
if hasattr(frappe.local, "posthog"):
|
||||
return
|
||||
|
||||
if not frappe.get_system_settings("enable_telemetry"):
|
||||
return
|
||||
|
||||
posthog_host = frappe.conf.get(POSTHOG_HOST_FIELD)
|
||||
posthog_project_id = frappe.conf.get(POSTHOG_PROJECT_FIELD)
|
||||
|
||||
if not posthog_host or not posthog_project_id:
|
||||
return
|
||||
|
||||
with suppress(Exception):
|
||||
frappe.local.posthog = Posthog(posthog_project_id, host=posthog_host)
|
||||
|
||||
|
||||
def capture(event, app):
|
||||
init_telemetry()
|
||||
ph: Posthog = getattr(frappe.local, "posthog", None)
|
||||
with suppress(Exception):
|
||||
ph and ph.capture(frappe.local.site, f"{app}_{event}")
|
||||
|
|
@ -79,6 +79,7 @@ dependencies = [
|
|||
"google-api-python-client~=2.2.0",
|
||||
"google-auth-oauthlib~=0.4.4",
|
||||
"google-auth~=1.29.0",
|
||||
"posthog~=3.0.1",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue