Merge branch 'frappe:develop' into multiple_imap_folder
This commit is contained in:
commit
2eb5fb9230
13 changed files with 270 additions and 9 deletions
|
|
@ -10,6 +10,13 @@ frappe.ui.form.on('Server Script', {
|
|||
frm.dashboard.hide();
|
||||
}
|
||||
|
||||
if (!frm.is_new()) {
|
||||
frm.add_custom_button(__('Compare Versions'), () => {
|
||||
new frappe.ui.DiffView("Server Script", "script", frm.doc.name);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
frm.call('get_autocompletion_items')
|
||||
.then(r => r.message)
|
||||
.then(items => {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@ frappe.ui.form.on('Client Script', {
|
|||
d.show();
|
||||
});
|
||||
});
|
||||
|
||||
if (!frm.is_new()) {
|
||||
frm.add_custom_button(__('Compare Versions'), () => {
|
||||
new frappe.ui.DiffView("Client Script", "script", frm.doc.name);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
frm.set_query('dt', {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from frappe import _
|
|||
from frappe.model.document import Document
|
||||
from frappe.utils.user import get_enabled_system_users
|
||||
from frappe.desk.reportview import get_filters_cond
|
||||
from frappe.desk.doctype.notification_settings.notification_settings import is_email_notifications_enabled_for_type
|
||||
|
||||
weekdays = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]
|
||||
communication_mapping = {"": "Event", "Event": "Event", "Meeting": "Meeting", "Call": "Phone", "Sent/Received Email": "Email", "Other": "Other"}
|
||||
|
|
@ -141,7 +142,12 @@ def has_permission(doc, user):
|
|||
|
||||
def send_event_digest():
|
||||
today = nowdate()
|
||||
for user in get_enabled_system_users():
|
||||
|
||||
# select only those users that have event reminder email notifications enabled
|
||||
users = [user for user in get_enabled_system_users() if
|
||||
is_email_notifications_enabled_for_type(user.name, 'Event Reminders')]
|
||||
|
||||
for user in users:
|
||||
events = get_events(today, today, user.name, for_reminder=True)
|
||||
if events:
|
||||
frappe.set_user_lang(user.name, user.language)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"enable_email_assignment",
|
||||
"enable_email_energy_point",
|
||||
"enable_email_share",
|
||||
"enable_email_event_reminders",
|
||||
"user",
|
||||
"seen",
|
||||
"system_notifications_section",
|
||||
|
|
@ -97,12 +98,19 @@
|
|||
"fieldname": "energy_points_system_notifications",
|
||||
"fieldtype": "Check",
|
||||
"label": "Energy Points"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "enable_email_notifications",
|
||||
"fieldname": "enable_email_event_reminders",
|
||||
"fieldtype": "Check",
|
||||
"label": "Event Reminders"
|
||||
}
|
||||
],
|
||||
"in_create": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-11-16 12:18:46.955501",
|
||||
"modified": "2021-11-24 14:45:31.931154",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Notification Settings",
|
||||
|
|
@ -125,4 +133,4 @@
|
|||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2021, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestNotificationSettings(unittest.TestCase):
|
||||
pass
|
||||
6
frappe/email/doctype/newsletter/newsletter.py
Executable file → Normal file
6
frappe/email/doctype/newsletter/newsletter.py
Executable file → Normal file
|
|
@ -159,11 +159,10 @@ class Newsletter(WebsiteGenerator):
|
|||
def send_newsletter(self, emails: List[str]):
|
||||
"""Trigger email generation for `emails` and add it in Email Queue.
|
||||
"""
|
||||
# TODO: get rid of this maybe?
|
||||
message = self.get_message()
|
||||
attachments = self.get_newsletter_attachments()
|
||||
sender = self.send_from or frappe.utils.get_formatted_email(self.owner)
|
||||
args = {"message": message, "name": self.name}
|
||||
args = self.as_dict()
|
||||
args["message"] = self.get_message()
|
||||
|
||||
is_auto_commit_set = bool(frappe.db.auto_commit_on_many_writes)
|
||||
frappe.db.auto_commit_on_many_writes = not frappe.flags.in_test
|
||||
|
|
@ -172,7 +171,6 @@ class Newsletter(WebsiteGenerator):
|
|||
subject=self.subject,
|
||||
sender=sender,
|
||||
recipients=emails,
|
||||
message=message,
|
||||
attachments=attachments,
|
||||
template="newsletter",
|
||||
add_unsubscribe_link=self.send_unsubscribe_link,
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ import "./frappe/utils/help_links.js";
|
|||
import "./frappe/utils/address_and_contact.js";
|
||||
import "./frappe/utils/preview_email.js";
|
||||
import "./frappe/utils/file_manager.js";
|
||||
import "./frappe/utils/diffview";
|
||||
|
||||
import "./frappe/upload.js";
|
||||
import "./frappe/ui/tree.js";
|
||||
|
|
|
|||
100
frappe/public/js/frappe/utils/diffview.js
Normal file
100
frappe/public/js/frappe/utils/diffview.js
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
frappe.provide("frappe.ui");
|
||||
|
||||
frappe.ui.DiffView = class DiffView {
|
||||
constructor(doctype, fieldname, docname) {
|
||||
this.dialog = null;
|
||||
this.handler = null;
|
||||
this.doctype = doctype;
|
||||
this.fieldname = fieldname;
|
||||
this.docname = docname;
|
||||
|
||||
this.dialog = this.make_dialog();
|
||||
this.set_empty_state();
|
||||
this.dialog.show();
|
||||
}
|
||||
|
||||
make_dialog() {
|
||||
const get_query = () => ({
|
||||
query: "frappe.utils.diff.version_query",
|
||||
filters: { docname: this.docname, ref_doctype: this.doctype },
|
||||
});
|
||||
const onchange = () => this.compute_diff();
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __("Compare Versions"),
|
||||
fields: [
|
||||
{
|
||||
label: __("From version"),
|
||||
fieldtype: "Link",
|
||||
fieldname: "from_version",
|
||||
options: "Version",
|
||||
reqd: 1,
|
||||
get_query,
|
||||
onchange,
|
||||
},
|
||||
{
|
||||
fieldtype: "Column Break",
|
||||
fieldname: "cb",
|
||||
},
|
||||
{
|
||||
label: __("To version"),
|
||||
fieldtype: "Link",
|
||||
fieldname: "to_version",
|
||||
options: "Version",
|
||||
reqd: 1,
|
||||
get_query,
|
||||
onchange,
|
||||
},
|
||||
{
|
||||
fieldtype: "Section Break",
|
||||
fieldname: "sb",
|
||||
},
|
||||
{
|
||||
label: __("Diff"),
|
||||
fieldtype: "HTML",
|
||||
fieldname: "diff",
|
||||
},
|
||||
],
|
||||
size: "large",
|
||||
});
|
||||
return dialog;
|
||||
}
|
||||
|
||||
compute_diff() {
|
||||
const from_version = this.dialog.get_value("from_version");
|
||||
const to_version = this.dialog.get_value("to_version");
|
||||
const fieldname = this.fieldname;
|
||||
|
||||
if (from_version && to_version) {
|
||||
frappe
|
||||
.xcall("frappe.utils.diff.get_version_diff", {
|
||||
from_version,
|
||||
to_version,
|
||||
fieldname,
|
||||
})
|
||||
.then((data) => {
|
||||
this.dialog.set_value("diff", this.prettify_diff(data));
|
||||
});
|
||||
} else {
|
||||
this.set_empty_state();
|
||||
}
|
||||
}
|
||||
|
||||
prettify_diff(diff) {
|
||||
let html = ``;
|
||||
|
||||
diff.forEach((line) => {
|
||||
let line_class = "";
|
||||
if (line.startsWith("+")) {
|
||||
line_class = "insert";
|
||||
} else if (line.startsWith("-")) {
|
||||
line_class = "delete";
|
||||
}
|
||||
html += `<div class=${line_class}>${line}</div>`;
|
||||
});
|
||||
return `<div class='diffview'>${html}</div>`;
|
||||
}
|
||||
|
||||
set_empty_state() {
|
||||
this.dialog.set_value("diff", __("Select two versions to view the diff."));
|
||||
}
|
||||
};
|
||||
|
|
@ -388,6 +388,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
|
|||
this.$charts_wrapper.addClass('hidden');
|
||||
this.save_view_user_settings(
|
||||
{ chart_args: null });
|
||||
this.chart_args = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -574,6 +574,31 @@ details > summary:focus {
|
|||
}
|
||||
}
|
||||
|
||||
.diffview {
|
||||
font-family: monospace;
|
||||
white-space: pre;
|
||||
word-wrap: break-word;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
|
||||
.diffview .insert {
|
||||
background-color: var(--green-100);
|
||||
}
|
||||
|
||||
.diffview .delete {
|
||||
background-color: var(--red-100);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
.diffview .insert {
|
||||
background-color: var(--green-800);
|
||||
}
|
||||
.diffview .delete {
|
||||
background-color: var(--red-800);
|
||||
}
|
||||
}
|
||||
|
||||
// REDESIGN TODO: Handling of broken images?
|
||||
// img.no-image:before {
|
||||
// .img-background();
|
||||
|
|
@ -603,4 +628,4 @@ details > summary:focus {
|
|||
.chart-container {
|
||||
direction: ltr;
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@
|
|||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if published and send_webview_link %}
|
||||
<div style="font-size: 12px; line-height: 20px;">
|
||||
<div>
|
||||
Open in <a style="color: #687178; text-decoration: underline;" href="/newsletters/{{ name }}" target="_blank">web</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
@ -7,6 +7,7 @@ from frappe.utils import evaluate_filters, money_in_words, scrub_urls, get_url
|
|||
from frappe.utils import validate_url, validate_email_address
|
||||
from frappe.utils import ceil, floor
|
||||
from frappe.utils.data import cast, validate_python_code
|
||||
from frappe.utils.diff import get_version_diff, version_query, _get_value_from_version
|
||||
|
||||
from PIL import Image
|
||||
from frappe.utils.image import strip_exif_data, optimize_image
|
||||
|
|
@ -269,3 +270,39 @@ class TestPythonExpressions(unittest.TestCase):
|
|||
]
|
||||
for expr in invalid_expressions:
|
||||
self.assertRaises(frappe.ValidationError, validate_python_code, expr)
|
||||
|
||||
|
||||
class TestDiffUtils(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.doc = frappe.get_doc(doctype="Client Script", dt="Client Script")
|
||||
cls.doc.save(ignore_version=False)
|
||||
cls.doc.script = "2;"
|
||||
cls.doc.save(ignore_version=False)
|
||||
cls.doc.script = "42;"
|
||||
cls.doc.save(ignore_version=False)
|
||||
|
||||
cls.versions = version_query(doctype="Version", txt="", searchfield="name", start=0,
|
||||
page_len=20, filters={"ref_doctype": cls.doc.doctype, "docname": cls.doc.name})
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.doc.delete()
|
||||
|
||||
def test_version_query(self):
|
||||
self.assertGreaterEqual(len(self.versions), 2)
|
||||
|
||||
def test_get_field_value_from_version(self):
|
||||
latest_version = self.versions[0][0]
|
||||
self.assertEqual("42;", _get_value_from_version(latest_version, fieldname="script")[0])
|
||||
old_version = self.versions[1][0]
|
||||
self.assertEqual("2;", _get_value_from_version(old_version, fieldname="script")[0])
|
||||
|
||||
def test_get_version_diff(self):
|
||||
old_version = self.versions[1][0]
|
||||
latest_version = self.versions[0][0]
|
||||
|
||||
diff = get_version_diff(old_version, latest_version)
|
||||
self.assertIn('-2;', diff)
|
||||
self.assertIn('+42;', diff)
|
||||
|
|
|
|||
61
frappe/utils/diff.py
Normal file
61
frappe/utils/diff.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import json
|
||||
from difflib import unified_diff
|
||||
from typing import List
|
||||
|
||||
import frappe
|
||||
from frappe.utils import pretty_date
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_version_diff(
|
||||
from_version: str, to_version: str, fieldname: str = "script"
|
||||
) -> List[str]:
|
||||
|
||||
before, before_timestamp = _get_value_from_version(from_version, fieldname)
|
||||
after, after_timestamp = _get_value_from_version(to_version, fieldname)
|
||||
|
||||
if not (before and after):
|
||||
return ["Values not available for diff"]
|
||||
|
||||
before = before.split("\n")
|
||||
after = after.split("\n")
|
||||
|
||||
diff = unified_diff(
|
||||
before,
|
||||
after,
|
||||
fromfile=from_version,
|
||||
tofile=to_version,
|
||||
fromfiledate=before_timestamp,
|
||||
tofiledate=after_timestamp,
|
||||
)
|
||||
return list(diff)
|
||||
|
||||
|
||||
def _get_value_from_version(version_name: str, fieldname: str):
|
||||
version = frappe.get_list(
|
||||
"Version", fields=["data", "modified"], filters={"name": version_name}
|
||||
)
|
||||
if version:
|
||||
data = json.loads(version[0].data)
|
||||
changed_fields = data.get("changed", [])
|
||||
|
||||
# data structure of field: [fieldname, before_save, after_save]
|
||||
for field in changed_fields:
|
||||
if field[0] == fieldname:
|
||||
return field[2], str(version[0].modified)
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def version_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
results = frappe.get_list(
|
||||
"Version",
|
||||
fields=["name", "modified"],
|
||||
filters=filters,
|
||||
limit_start=start,
|
||||
limit_page_length=page_len,
|
||||
order_by="modified desc",
|
||||
)
|
||||
return [(d.name, pretty_date(d.modified), d.modified) for d in results]
|
||||
Loading…
Add table
Reference in a new issue