From fdafce9b63e79731a0b80944151c417b015c4b72 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Tue, 20 Dec 2022 14:44:05 +0100
Subject: [PATCH 01/20] fix: only System Manager can work with public Notes
---
frappe/desk/doctype/note/note.js | 33 +++++++++------------
frappe/desk/doctype/note/note.json | 41 ++++++++++++++++++++++----
frappe/desk/doctype/note/note.py | 47 +++++++++---------------------
frappe/hooks.py | 2 --
4 files changed, 63 insertions(+), 60 deletions(-)
diff --git a/frappe/desk/doctype/note/note.js b/frappe/desk/doctype/note/note.js
index dd556a3969..b8f3b02262 100644
--- a/frappe/desk/doctype/note/note.js
+++ b/frappe/desk/doctype/note/note.js
@@ -1,41 +1,36 @@
frappe.ui.form.on("Note", {
refresh: function (frm) {
- if (frm.doc.__islocal) {
- frm.events.set_editable(frm, true);
- } else {
- if (!frm.doc.content) {
- frm.doc.content = "";
- }
+ if (!frm.is_new()) {
+ frm.is_note_editable = false;
+ frm.events.set_editable(frm, frm.is_note_editable);
// toggle edit
frm.add_custom_button("Edit", function () {
- frm.events.set_editable(frm, !frm.is_note_editable);
+ frm.is_note_editable = !frm.is_note_editable;
+ frm.events.set_editable(frm, frm.is_note_editable);
});
- frm.events.set_editable(frm, false);
}
},
set_editable: function (frm, editable) {
- // hide all fields other than content
-
- // no permission
- if (editable && !frm.perm[0].write) return;
+ // toggle "read_only" for content and "hidden" of all other fields
// content read_only
frm.set_df_property("content", "read_only", editable ? 0 : 1);
// hide all other fields
- $.each(frm.fields_dict, function (fieldname) {
- if (fieldname !== "content") {
- frm.set_df_property(fieldname, "hidden", editable ? 0 : 1);
+ for (const field of frm.meta.fields) {
+ if (field.fieldname !== "content") {
+ frm.set_df_property(
+ field.fieldname,
+ "hidden",
+ editable && !field.hidden && frm.get_perm(field.permlevel, "write") ? 0 : 1
+ );
}
- });
+ }
// no label, description for content either
frm.get_field("content").toggle_label(editable);
frm.get_field("content").toggle_description(editable);
-
- // set flag for toggle
- frm.is_note_editable = editable;
},
});
diff --git a/frappe/desk/doctype/note/note.json b/frappe/desk/doctype/note/note.json
index 69a9518ac4..e4a19a4750 100644
--- a/frappe/desk/doctype/note/note.json
+++ b/frappe/desk/doctype/note/note.json
@@ -1,6 +1,7 @@
{
"actions": [],
"allow_rename": 1,
+ "autoname": "field:title",
"creation": "2013-05-24 13:41:00",
"doctype": "DocType",
"document_type": "Document",
@@ -24,7 +25,8 @@
"label": "Title",
"no_copy": 1,
"print_hide": 1,
- "reqd": 1
+ "reqd": 1,
+ "unique": 1
},
{
"bold": 1,
@@ -32,6 +34,7 @@
"fieldname": "public",
"fieldtype": "Check",
"label": "Public",
+ "permlevel": 1,
"print_hide": 1
},
{
@@ -40,7 +43,8 @@
"depends_on": "public",
"fieldname": "notify_on_login",
"fieldtype": "Check",
- "label": "Notify users with a popup when they log in"
+ "label": "Notify users with a popup when they log in",
+ "permlevel": 1
},
{
"bold": 1,
@@ -49,13 +53,15 @@
"description": "If enabled, users will be notified every time they login. If not enabled, users will only be notified once.",
"fieldname": "notify_on_every_login",
"fieldtype": "Check",
- "label": "Notify Users On Every Login"
+ "label": "Notify Users On Every Login",
+ "permlevel": 1
},
{
"depends_on": "eval:doc.notify_on_login && doc.public",
"fieldname": "expire_notification_on",
"fieldtype": "Date",
"label": "Expire Notification On",
+ "permlevel": 1,
"search_index": 1
},
{
@@ -68,9 +74,11 @@
},
{
"collapsible": 1,
+ "depends_on": "notify_on_login",
"fieldname": "seen_by_section",
"fieldtype": "Section Break",
- "label": "Seen By"
+ "label": "Seen By",
+ "permlevel": 1
},
{
"fieldname": "seen_by",
@@ -82,18 +90,40 @@
"icon": "fa fa-file-text",
"idx": 1,
"links": [],
- "modified": "2021-09-18 10:57:51.352643",
+ "modified": "2022-12-20 14:35:23.257985",
"modified_by": "Administrator",
"module": "Desk",
"name": "Note",
+ "naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
+ "export": 1,
"print": 1,
"read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "permlevel": 1,
+ "read": 1,
+ "role": "System Manager",
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
"role": "All",
"share": 1,
"write": 1
@@ -102,5 +132,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "ASC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/frappe/desk/doctype/note/note.py b/frappe/desk/doctype/note/note.py
index c0a37d5f44..85ff95d52e 100644
--- a/frappe/desk/doctype/note/note.py
+++ b/frappe/desk/doctype/note/note.py
@@ -1,53 +1,32 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
-import re
-
import frappe
from frappe.model.document import Document
-NAME_PATTERN = re.compile("[%'\"#*?`]")
-
class Note(Document):
- def autoname(self):
- # replace forbidden characters
- self.name = NAME_PATTERN.sub("", self.title.strip())
-
def validate(self):
if self.notify_on_login and not self.expire_notification_on:
-
# expire this notification in a week (default)
self.expire_notification_on = frappe.utils.add_days(self.creation, 7)
+ if not self.content:
+ self.content = ""
+
def before_print(self, settings=None):
self.print_heading = self.name
self.sub_heading = ""
+ def mark_seen_by(self, user: str) -> None:
+ if user in [d.user for d in self.seen_by]:
+ return
+
+ self.append("seen_by", {"user": user})
+
@frappe.whitelist()
-def mark_as_seen(note):
- note = frappe.get_doc("Note", note)
- if frappe.session.user not in [d.user for d in note.seen_by]:
- note.append("seen_by", {"user": frappe.session.user})
- note.save(ignore_version=True)
-
-
-def get_permission_query_conditions(user):
- if not user:
- user = frappe.session.user
-
- if user == "Administrator":
- return ""
-
- return f"""(`tabNote`.public=1 or `tabNote`.owner={frappe.db.escape(user)})"""
-
-
-def has_permission(doc, ptype, user):
- if doc.public == 1 or user == "Administrator":
- return True
-
- if user == doc.owner:
- return True
-
- return False
+def mark_as_seen(note: str):
+ note: Note = frappe.get_doc("Note", note)
+ note.mark_seen_by(frappe.session.user)
+ note.save(ignore_permissions=True, ignore_version=True)
diff --git a/frappe/hooks.py b/frappe/hooks.py
index beff7d2de2..03eed0da49 100644
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -95,7 +95,6 @@ permission_query_conditions = {
"Dashboard Chart": "frappe.desk.doctype.dashboard_chart.dashboard_chart.get_permission_query_conditions",
"Number Card": "frappe.desk.doctype.number_card.number_card.get_permission_query_conditions",
"Notification Settings": "frappe.desk.doctype.notification_settings.notification_settings.get_permission_query_conditions",
- "Note": "frappe.desk.doctype.note.note.get_permission_query_conditions",
"Kanban Board": "frappe.desk.doctype.kanban_board.kanban_board.get_permission_query_conditions",
"Contact": "frappe.contacts.address_and_contact.get_permission_query_conditions_for_contact",
"Address": "frappe.contacts.address_and_contact.get_permission_query_conditions_for_address",
@@ -108,7 +107,6 @@ has_permission = {
"Event": "frappe.desk.doctype.event.event.has_permission",
"ToDo": "frappe.desk.doctype.todo.todo.has_permission",
"User": "frappe.core.doctype.user.user.has_permission",
- "Note": "frappe.desk.doctype.note.note.has_permission",
"Dashboard Chart": "frappe.desk.doctype.dashboard_chart.dashboard_chart.has_permission",
"Number Card": "frappe.desk.doctype.number_card.number_card.has_permission",
"Kanban Board": "frappe.desk.doctype.kanban_board.kanban_board.has_permission",
From ebce8ee002712cb502b5c0d78e7d25ecb4f38541 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Tue, 20 Dec 2022 14:54:43 +0100
Subject: [PATCH 02/20] fix: button label
---
frappe/desk/doctype/note/note.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/desk/doctype/note/note.js b/frappe/desk/doctype/note/note.js
index b8f3b02262..fe6e234187 100644
--- a/frappe/desk/doctype/note/note.js
+++ b/frappe/desk/doctype/note/note.js
@@ -5,7 +5,7 @@ frappe.ui.form.on("Note", {
frm.events.set_editable(frm, frm.is_note_editable);
// toggle edit
- frm.add_custom_button("Edit", function () {
+ frm.add_custom_button(__("Editing mode"), function () {
frm.is_note_editable = !frm.is_note_editable;
frm.events.set_editable(frm, frm.is_note_editable);
});
From 5ab75908ee74f6fcc3b4659362cb2ac8c4a29b42 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Tue, 20 Dec 2022 15:04:13 +0100
Subject: [PATCH 03/20] fix: permlevel 1 on seen_by
---
frappe/desk/doctype/note/note.json | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/frappe/desk/doctype/note/note.json b/frappe/desk/doctype/note/note.json
index e4a19a4750..eaad75d79d 100644
--- a/frappe/desk/doctype/note/note.json
+++ b/frappe/desk/doctype/note/note.json
@@ -84,13 +84,14 @@
"fieldname": "seen_by",
"fieldtype": "Table",
"label": "Seen By Table",
- "options": "Note Seen By"
+ "options": "Note Seen By",
+ "permlevel": 1
}
],
"icon": "fa fa-file-text",
"idx": 1,
"links": [],
- "modified": "2022-12-20 14:35:23.257985",
+ "modified": "2022-12-20 15:03:43.581298",
"modified_by": "Administrator",
"module": "Desk",
"name": "Note",
From 3afdb39372dfbb96feace426938a07897844c2fe Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Tue, 20 Dec 2022 15:30:15 +0100
Subject: [PATCH 04/20] fix: test update with content instead of title
---
frappe/tests/test_frappe_client.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/frappe/tests/test_frappe_client.py b/frappe/tests/test_frappe_client.py
index e80e43f49c..925d795ffe 100644
--- a/frappe/tests/test_frappe_client.py
+++ b/frappe/tests/test_frappe_client.py
@@ -105,16 +105,16 @@ class TestFrappeClient(FrappeTestCase):
frappe.db.set_value("Website Settings", None, "title_prefix", "")
def test_update_doc(self):
- server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
- frappe.db.delete("Note", {"title": ("in", ("Sing", "sing"))})
+ server = FrappeClient(get_url(), "Administrator", "3zF2-89X4-AYSm-JA9M", verify=False)
+ frappe.db.delete("Note", {"title": "Sing"})
frappe.db.commit()
- server.insert({"doctype": "Note", "public": True, "title": "Sing"})
+ server.insert({"doctype": "Note", "title": "Sing"})
doc = server.get_doc("Note", "Sing")
- changed_title = "sing"
- doc["title"] = changed_title
+ new_content = "
Hello, World!
"
+ doc["content"] = new_content
doc = server.update(doc)
- self.assertTrue(doc["title"] == changed_title)
+ self.assertTrue(doc["content"] == new_content)
def test_update_child_doc(self):
server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
From d0f75363a5d54b2f55993a9acb950ab871b37848 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Tue, 20 Dec 2022 16:30:11 +0100
Subject: [PATCH 05/20] refactor: make test for assignment independent of Note
---
.../assignment_rule/test_assignment_rule.py | 159 +++++++++++-------
frappe/tests/test_assign.py | 27 ++-
2 files changed, 120 insertions(+), 66 deletions(-)
diff --git a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py
index 8f1773608f..45a1b85291 100644
--- a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py
+++ b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py
@@ -4,7 +4,8 @@
import frappe
from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase
-from frappe.utils import random_string
+
+TEST_DOCTYPE = "Assignment Test"
class TestAutoAssign(FrappeTestCase):
@@ -12,6 +13,7 @@ class TestAutoAssign(FrappeTestCase):
def setUpClass(cls):
super().setUpClass()
frappe.db.delete("Assignment Rule")
+ create_test_doctype(TEST_DOCTYPE)
@classmethod
def tearDownClass(cls):
@@ -33,45 +35,49 @@ class TestAutoAssign(FrappeTestCase):
clear_assignments()
def test_round_robin(self):
- note = make_note(dict(public=1))
-
# check if auto assigned to first user
+ record = _make_test_record(public=1)
self.assertEqual(
frappe.db.get_value(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), "allocated_to"
+ "ToDo",
+ dict(reference_type=TEST_DOCTYPE, reference_name=record.name, status="Open"),
+ "allocated_to",
),
"test@example.com",
)
- note = make_note(dict(public=1))
-
# check if auto assigned to second user
+ record = _make_test_record(public=1)
self.assertEqual(
frappe.db.get_value(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), "allocated_to"
+ "ToDo",
+ dict(reference_type=TEST_DOCTYPE, reference_name=record.name, status="Open"),
+ "allocated_to",
),
"test1@example.com",
)
clear_assignments()
- note = make_note(dict(public=1))
-
# check if auto assigned to third user, even if
# previous assignments where closed
+ record = _make_test_record(public=1)
self.assertEqual(
frappe.db.get_value(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), "allocated_to"
+ "ToDo",
+ dict(reference_type=TEST_DOCTYPE, reference_name=record.name, status="Open"),
+ "allocated_to",
),
"test2@example.com",
)
# check loop back to first user
- note = make_note(dict(public=1))
-
+ record = _make_test_record(public=1)
self.assertEqual(
frappe.db.get_value(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), "allocated_to"
+ "ToDo",
+ dict(reference_type=TEST_DOCTYPE, reference_name=record.name, status="Open"),
+ "allocated_to",
),
"test@example.com",
)
@@ -81,29 +87,29 @@ class TestAutoAssign(FrappeTestCase):
self.assignment_rule.save()
for _ in range(30):
- note = make_note(dict(public=1))
+ _make_test_record(public=1)
# check if each user has 10 assignments (?)
for user in ("test@example.com", "test1@example.com", "test2@example.com"):
self.assertEqual(
- len(frappe.get_all("ToDo", dict(allocated_to=user, reference_type="Note"))), 10
+ len(frappe.get_all("ToDo", dict(allocated_to=user, reference_type=TEST_DOCTYPE))), 10
)
# clear 5 assignments for first user
# can't do a limit in "delete" since postgres does not support it
for d in frappe.get_all(
- "ToDo", dict(reference_type="Note", allocated_to="test@example.com"), limit=5
+ "ToDo", dict(reference_type=TEST_DOCTYPE, allocated_to="test@example.com"), limit=5
):
frappe.db.delete("ToDo", {"name": d.name})
# add 5 more assignments
for i in range(5):
- make_note(dict(public=1))
+ _make_test_record(public=1)
# check if each user still has 10 assignments
for user in ("test@example.com", "test1@example.com", "test2@example.com"):
self.assertEqual(
- len(frappe.get_all("ToDo", dict(allocated_to=user, reference_type="Note"))), 10
+ len(frappe.get_all("ToDo", dict(allocated_to=user, reference_type=TEST_DOCTYPE))), 10
)
def test_based_on_field(self):
@@ -112,21 +118,21 @@ class TestAutoAssign(FrappeTestCase):
self.assignment_rule.save()
frappe.set_user("test1@example.com")
- note = make_note(dict(public=1))
+ note = _make_test_record(public=1)
# check if auto assigned to doc owner, test1@example.com
self.assertEqual(
frappe.db.get_value(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), "owner"
+ "ToDo", dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), "owner"
),
"test1@example.com",
)
frappe.set_user("test2@example.com")
- note = make_note(dict(public=1))
+ note = _make_test_record(public=1)
# check if auto assigned to doc owner, test2@example.com
self.assertEqual(
frappe.db.get_value(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), "owner"
+ "ToDo", dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), "owner"
),
"test2@example.com",
)
@@ -135,21 +141,23 @@ class TestAutoAssign(FrappeTestCase):
def test_assign_condition(self):
# check condition
- note = make_note(dict(public=0))
+ note = _make_test_record(public=0)
self.assertEqual(
frappe.db.get_value(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), "allocated_to"
+ "ToDo",
+ dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"),
+ "allocated_to",
),
None,
)
def test_clear_assignment(self):
- note = make_note(dict(public=1))
+ note = _make_test_record(public=1)
# check if auto assigned to first user
todo = frappe.get_list(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), limit=1
+ "ToDo", dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), limit=1
)[0]
todo = frappe.get_doc("ToDo", todo["name"])
@@ -165,11 +173,11 @@ class TestAutoAssign(FrappeTestCase):
self.assertEqual(todo.status, "Cancelled")
def test_close_assignment(self):
- note = make_note(dict(public=1, content="valid"))
+ note = _make_test_record(public=1, content="valid")
# check if auto assigned
todo = frappe.get_list(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), limit=1
+ "ToDo", dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), limit=1
)[0]
todo = frappe.get_doc("ToDo", todo["name"])
@@ -186,12 +194,14 @@ class TestAutoAssign(FrappeTestCase):
self.assertEqual(todo.allocated_to, "test@example.com")
def check_multiple_rules(self):
- note = make_note(dict(public=1, notify_on_login=1))
+ note = _make_test_record(public=1, notify_on_login=1)
# check if auto assigned to test3 (2nd rule is applied, as it has higher priority)
self.assertEqual(
frappe.db.get_value(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), "allocated_to"
+ "ToDo",
+ dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"),
+ "allocated_to",
),
"test@example.com",
)
@@ -206,21 +216,25 @@ class TestAutoAssign(FrappeTestCase):
get_assignment_rule([days_1, days_2], ["public == 1", "public == 1"])
frappe.flags.assignment_day = "Monday"
- note = make_note(dict(public=1))
+ note = _make_test_record(public=1)
self.assertIn(
frappe.db.get_value(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), "allocated_to"
+ "ToDo",
+ dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"),
+ "allocated_to",
),
["test@example.com", "test1@example.com", "test2@example.com"],
)
frappe.flags.assignment_day = "Friday"
- note = make_note(dict(public=1))
+ note = _make_test_record(public=1)
self.assertIn(
frappe.db.get_value(
- "ToDo", dict(reference_type="Note", reference_name=note.name, status="Open"), "allocated_to"
+ "ToDo",
+ dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"),
+ "allocated_to",
),
["test3@example.com"],
)
@@ -228,17 +242,11 @@ class TestAutoAssign(FrappeTestCase):
def test_assignment_rule_condition(self):
frappe.db.delete("Assignment Rule")
- # Add expiry_date custom field
- from frappe.custom.doctype.custom_field.custom_field import create_custom_field
-
- df = dict(fieldname="expiry_date", label="Expiry Date", fieldtype="Date")
- create_custom_field("Note", df)
-
assignment_rule = frappe.get_doc(
dict(
name="Assignment with Due Date",
doctype="Assignment Rule",
- document_type="Note",
+ document_type=TEST_DOCTYPE,
assign_condition="public == 0",
due_date_based_on="expiry_date",
assignment_days=self.days,
@@ -249,11 +257,11 @@ class TestAutoAssign(FrappeTestCase):
).insert()
expiry_date = frappe.utils.add_days(frappe.utils.nowdate(), 2)
- note1 = make_note({"expiry_date": expiry_date})
- note2 = make_note({"expiry_date": expiry_date})
+ note1 = _make_test_record(expiry_date=expiry_date)
+ note2 = _make_test_record(expiry_date=expiry_date)
note1_todo = frappe.get_all(
- "ToDo", filters=dict(reference_type="Note", reference_name=note1.name, status="Open")
+ "ToDo", filters=dict(reference_type=TEST_DOCTYPE, reference_name=note1.name, status="Open")
)[0]
note1_todo_doc = frappe.get_doc("ToDo", note1_todo.name)
@@ -268,7 +276,7 @@ class TestAutoAssign(FrappeTestCase):
# saving one note's expiry should not update other note todo's due date
note2_todo = frappe.get_all(
"ToDo",
- filters=dict(reference_type="Note", reference_name=note2.name, status="Open"),
+ filters=dict(reference_type=TEST_DOCTYPE, reference_name=note2.name, status="Open"),
fields=["name", "date"],
)[0]
self.assertNotEqual(frappe.utils.get_date_str(note2_todo.date), note1.expiry_date)
@@ -278,21 +286,21 @@ class TestAutoAssign(FrappeTestCase):
def clear_assignments():
- frappe.db.delete("ToDo", {"reference_type": "Note"})
+ frappe.db.delete("ToDo", {"reference_type": TEST_DOCTYPE})
def get_assignment_rule(days, assign=None):
- frappe.delete_doc_if_exists("Assignment Rule", "For Note 1")
+ frappe.delete_doc_if_exists("Assignment Rule", f"For {TEST_DOCTYPE} 1")
if not assign:
assign = ["public == 1", "notify_on_login == 1"]
assignment_rule = frappe.get_doc(
dict(
- name="For Note 1",
+ name=f"For {TEST_DOCTYPE} 1",
doctype="Assignment Rule",
priority=0,
- document_type="Note",
+ document_type=TEST_DOCTYPE,
assign_condition=assign[0],
unassign_condition="public == 0 or notify_on_login == 1",
close_condition='"Closed" in content',
@@ -306,15 +314,15 @@ def get_assignment_rule(days, assign=None):
)
).insert()
- frappe.delete_doc_if_exists("Assignment Rule", "For Note 2")
+ frappe.delete_doc_if_exists("Assignment Rule", f"For {TEST_DOCTYPE} 2")
# 2nd rule
frappe.get_doc(
dict(
- name="For Note 2",
+ name=f"For {TEST_DOCTYPE} 2",
doctype="Assignment Rule",
priority=1,
- document_type="Note",
+ document_type=TEST_DOCTYPE,
assign_condition=assign[1],
unassign_condition="notify_on_login == 0",
rule="Round Robin",
@@ -326,12 +334,47 @@ def get_assignment_rule(days, assign=None):
return assignment_rule
-def make_note(values=None):
- note = frappe.get_doc(dict(doctype="Note", title=random_string(10), content=random_string(20)))
+def _make_test_record(**kwargs):
+ doc = frappe.new_doc(TEST_DOCTYPE)
- if values:
- note.update(values)
+ if kwargs:
+ doc.update(kwargs)
- note.insert()
+ return doc.insert()
- return note
+
+def create_test_doctype(doctype):
+ """Create custom doctype."""
+ if frappe.db.exists("DocType", doctype):
+ return
+
+ frappe.get_doc(
+ {
+ "doctype": "DocType",
+ "name": doctype,
+ "module": "Custom",
+ "custom": 1,
+ "fields": [
+ {
+ "fieldname": "expiry_date",
+ "label": "Expiry Date",
+ "fieldtype": "Date",
+ },
+ {
+ "fieldname": "notify_on_login",
+ "label": "Notify on Login",
+ "fieldtype": "Check",
+ },
+ {
+ "fieldname": "public",
+ "label": "Public",
+ "fieldtype": "Check",
+ },
+ {
+ "fieldname": "content",
+ "label": "Content",
+ "fieldtype": "Text",
+ },
+ ],
+ }
+ ).insert()
diff --git a/frappe/tests/test_assign.py b/frappe/tests/test_assign.py
index f3b579c028..4243bd080a 100644
--- a/frappe/tests/test_assign.py
+++ b/frappe/tests/test_assign.py
@@ -2,13 +2,22 @@
# License: MIT. See LICENSE
import frappe
import frappe.desk.form.assign_to
-from frappe.automation.doctype.assignment_rule.test_assignment_rule import make_note
+from frappe.automation.doctype.assignment_rule.test_assignment_rule import (
+ TEST_DOCTYPE,
+ _make_test_record,
+ create_test_doctype,
+)
from frappe.desk.form.load import get_assignments
from frappe.desk.listview import get_group_by_count
from frappe.tests.utils import FrappeTestCase
class TestAssign(FrappeTestCase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ create_test_doctype(TEST_DOCTYPE)
+
def test_assign(self):
todo = frappe.get_doc({"doctype": "ToDo", "description": "test"}).insert()
if not frappe.db.exists("User", "test@example.com"):
@@ -18,7 +27,7 @@ class TestAssign(FrappeTestCase):
self.assertTrue("test@example.com" in [d.owner for d in added])
- removed = frappe.desk.form.assign_to.remove(todo.doctype, todo.name, "test@example.com")
+ frappe.desk.form.assign_to.remove(todo.doctype, todo.name, "test@example.com")
# assignment is cleared
assignments = frappe.desk.form.assign_to.get(dict(doctype=todo.doctype, name=todo.name))
@@ -47,25 +56,27 @@ class TestAssign(FrappeTestCase):
}
).insert()
- note = make_note()
+ note = _make_test_record()
assign(note, "test_assign1@example.com")
- note = make_note(dict(public=1))
+ note = _make_test_record(public=1)
assign(note, "test_assign2@example.com")
- note = make_note(dict(public=1))
+ note = _make_test_record(public=1)
assign(note, "test_assign2@example.com")
- note = make_note()
+ note = _make_test_record()
assign(note, "test_assign2@example.com")
- data = {d.name: d.count for d in get_group_by_count("Note", "[]", "assigned_to")}
+ data = {d.name: d.count for d in get_group_by_count(TEST_DOCTYPE, "[]", "assigned_to")}
self.assertTrue("test_assign1@example.com" in data)
self.assertEqual(data["test_assign1@example.com"], 1)
self.assertEqual(data["test_assign2@example.com"], 3)
- data = {d.name: d.count for d in get_group_by_count("Note", '[{"public": 1}]', "assigned_to")}
+ data = {
+ d.name: d.count for d in get_group_by_count(TEST_DOCTYPE, '[{"public": 1}]', "assigned_to")
+ }
self.assertFalse("test_assign1@example.com" in data)
self.assertEqual(data["test_assign2@example.com"], 2)
From 636b85174e6e98f4ee89bebefe9695f3036784a1 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Tue, 17 Jan 2023 19:49:43 +0100
Subject: [PATCH 06/20] fix: test assignment rule
---
.../assignment_rule/test_assignment_rule.py | 51 ++++++++++---------
1 file changed, 27 insertions(+), 24 deletions(-)
diff --git a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py
index 45a1b85291..2460c40e8a 100644
--- a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py
+++ b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py
@@ -20,6 +20,7 @@ class TestAutoAssign(FrappeTestCase):
frappe.db.rollback()
def setUp(self):
+ frappe.set_user("Administrator")
make_test_records("User")
days = [
dict(day="Sunday"),
@@ -117,27 +118,16 @@ class TestAutoAssign(FrappeTestCase):
self.assignment_rule.field = "owner"
self.assignment_rule.save()
- frappe.set_user("test1@example.com")
- note = _make_test_record(public=1)
- # check if auto assigned to doc owner, test1@example.com
- self.assertEqual(
- frappe.db.get_value(
- "ToDo", dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), "owner"
- ),
- "test1@example.com",
- )
-
- frappe.set_user("test2@example.com")
- note = _make_test_record(public=1)
- # check if auto assigned to doc owner, test2@example.com
- self.assertEqual(
- frappe.db.get_value(
- "ToDo", dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), "owner"
- ),
- "test2@example.com",
- )
-
- frappe.set_user("Administrator")
+ for test_user in ("test1@example.com", "test2@example.com"):
+ frappe.set_user(test_user)
+ note = _make_test_record(public=1)
+ # check if auto assigned to doc owner, test1@example.com
+ self.assertEqual(
+ frappe.db.get_value(
+ "ToDo", dict(reference_type=TEST_DOCTYPE, reference_name=note.name, status="Open"), "owner"
+ ),
+ test_user,
+ )
def test_assign_condition(self):
# check condition
@@ -343,10 +333,9 @@ def _make_test_record(**kwargs):
return doc.insert()
-def create_test_doctype(doctype):
+def create_test_doctype(doctype: str):
"""Create custom doctype."""
- if frappe.db.exists("DocType", doctype):
- return
+ frappe.db.delete("DocType", doctype)
frappe.get_doc(
{
@@ -376,5 +365,19 @@ def create_test_doctype(doctype):
"fieldtype": "Text",
},
],
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "All",
+ "share": 1,
+ "write": 1,
+ },
+ ],
}
).insert()
From ecb54ff066d4ea3945b49ac792a702955837e7c1 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Tue, 17 Jan 2023 20:07:37 +0100
Subject: [PATCH 07/20] feat: hash naming for note
---
frappe/desk/doctype/note/note.json | 14 +++++---------
frappe/desk/doctype/note/note_list.js | 6 ++----
2 files changed, 7 insertions(+), 13 deletions(-)
diff --git a/frappe/desk/doctype/note/note.json b/frappe/desk/doctype/note/note.json
index eaad75d79d..471e457a5d 100644
--- a/frappe/desk/doctype/note/note.json
+++ b/frappe/desk/doctype/note/note.json
@@ -1,7 +1,6 @@
{
"actions": [],
- "allow_rename": 1,
- "autoname": "field:title",
+ "autoname": "hash",
"creation": "2013-05-24 13:41:00",
"doctype": "DocType",
"document_type": "Document",
@@ -20,13 +19,9 @@
{
"fieldname": "title",
"fieldtype": "Data",
- "in_global_search": 1,
"in_list_view": 1,
"label": "Title",
- "no_copy": 1,
- "print_hide": 1,
- "reqd": 1,
- "unique": 1
+ "reqd": 1
},
{
"bold": 1,
@@ -91,11 +86,11 @@
"icon": "fa fa-file-text",
"idx": 1,
"links": [],
- "modified": "2022-12-20 15:03:43.581298",
+ "modified": "2023-01-17 20:04:01.789170",
"modified_by": "Administrator",
"module": "Desk",
"name": "Note",
- "naming_rule": "By fieldname",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [
{
@@ -134,5 +129,6 @@
"sort_field": "modified",
"sort_order": "ASC",
"states": [],
+ "title_field": "title",
"track_changes": 1
}
\ No newline at end of file
diff --git a/frappe/desk/doctype/note/note_list.js b/frappe/desk/doctype/note/note_list.js
index 1e0ed40880..a8e948bc94 100644
--- a/frappe/desk/doctype/note/note_list.js
+++ b/frappe/desk/doctype/note/note_list.js
@@ -1,8 +1,6 @@
frappe.listview_settings["Note"] = {
- onload: function (me) {
- me.page.set_title(__("Notes"));
- },
- add_fields: ["title", "public"],
+ hide_name_column: true,
+ add_fields: ["public"],
get_indicator: function (doc) {
if (doc.public) {
return [__("Public"), "green", "public,=,Yes"];
From 88937883659a215c815cc6a7c1ee011a031c59f4 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Tue, 17 Jan 2023 20:14:50 +0100
Subject: [PATCH 08/20] fix: undo temp constant in test
---
frappe/tests/test_frappe_client.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/tests/test_frappe_client.py b/frappe/tests/test_frappe_client.py
index 925d795ffe..e3a95af006 100644
--- a/frappe/tests/test_frappe_client.py
+++ b/frappe/tests/test_frappe_client.py
@@ -105,7 +105,7 @@ class TestFrappeClient(FrappeTestCase):
frappe.db.set_value("Website Settings", None, "title_prefix", "")
def test_update_doc(self):
- server = FrappeClient(get_url(), "Administrator", "3zF2-89X4-AYSm-JA9M", verify=False)
+ server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
frappe.db.delete("Note", {"title": "Sing"})
frappe.db.commit()
From da925ce681d95d76bc1c67db6944737cdf6661f9 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Tue, 17 Jan 2023 21:00:05 +0100
Subject: [PATCH 09/20] fix: test cases for frappe client
---
frappe/tests/test_frappe_client.py | 102 +++++++++++++----------------
1 file changed, 44 insertions(+), 58 deletions(-)
diff --git a/frappe/tests/test_frappe_client.py b/frappe/tests/test_frappe_client.py
index e3a95af006..4d7897b344 100644
--- a/frappe/tests/test_frappe_client.py
+++ b/frappe/tests/test_frappe_client.py
@@ -17,31 +17,26 @@ class TestFrappeClient(FrappeTestCase):
def test_insert_many(self):
server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
- frappe.db.delete("Note", {"title": ("in", ("Sing", "a", "song", "of", "sixpence"))})
- frappe.db.commit()
-
server.insert_many(
[
- {"doctype": "Note", "public": True, "title": "Sing"},
- {"doctype": "Note", "public": True, "title": "a"},
- {"doctype": "Note", "public": True, "title": "song"},
- {"doctype": "Note", "public": True, "title": "of"},
- {"doctype": "Note", "public": True, "title": "sixpence"},
+ {"doctype": "Note", "title": "Sing"},
+ {"doctype": "Note", "title": "a"},
+ {"doctype": "Note", "title": "song"},
+ {"doctype": "Note", "title": "of"},
+ {"doctype": "Note", "title": "sixpence"},
]
)
+ records = frappe.get_all("Note", pluck="title")
- self.assertTrue(frappe.db.get_value("Note", {"title": "Sing"}))
- self.assertTrue(frappe.db.get_value("Note", {"title": "a"}))
- self.assertTrue(frappe.db.get_value("Note", {"title": "song"}))
- self.assertTrue(frappe.db.get_value("Note", {"title": "of"}))
- self.assertTrue(frappe.db.get_value("Note", {"title": "sixpence"}))
+ self.assertIn("Sing", records)
+ self.assertIn("a", records)
+ self.assertIn("song", records)
+ self.assertIn("of", records)
+ self.assertIn("sixpence", records)
def test_create_doc(self):
server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
- frappe.db.delete("Note", {"title": "test_create"})
- frappe.db.commit()
-
- server.insert({"doctype": "Note", "public": True, "title": "test_create"})
+ server.insert({"doctype": "Note", "title": "test_create"})
self.assertTrue(frappe.db.get_value("Note", {"title": "test_create"}))
@@ -52,37 +47,38 @@ class TestFrappeClient(FrappeTestCase):
self.assertTrue(len(doc_list))
def test_get_doc(self):
+ USER = "Administrator"
+ TITLE = "get_this"
+ DOCTYPE = "Note"
server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
- frappe.db.delete("Note", {"title": "get_this"})
- frappe.db.commit()
- server.insert_many(
- [
- {"doctype": "Note", "public": True, "title": "get_this"},
- ]
- )
- doc = server.get_doc("Note", "get_this")
- self.assertTrue(doc)
+ NAME = server.insert({"doctype": DOCTYPE, "title": TITLE}).get("name")
+ doc = server.get_doc(DOCTYPE, NAME)
- def test_get_value(self):
+ self.assertEqual(doc.get("doctype"), DOCTYPE)
+ self.assertEqual(doc.get("name"), NAME)
+ self.assertEqual(doc.get("title"), TITLE)
+ self.assertEqual(doc.get("owner"), USER)
+
+ def test_get_value_by_filters(self):
+ CONTENT = "test get value"
server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
- frappe.db.delete("Note", {"title": "get_value"})
- frappe.db.commit()
+ server.insert({"doctype": "Note", "title": "get_value", "content": CONTENT}).get("name")
- test_content = "test get value"
-
- server.insert_many(
- [
- {"doctype": "Note", "public": True, "title": "get_value", "content": test_content},
- ]
- )
self.assertEqual(
- server.get_value("Note", "content", {"title": "get_value"}).get("content"), test_content
+ server.get_value("Note", "content", {"title": "get_value"}).get("content"), CONTENT
)
- name = server.get_value("Note", "name", {"title": "get_value"}).get("name")
- # test by name
- self.assertEqual(server.get_value("Note", "content", name).get("content"), test_content)
+ def test_get_value_by_name(self):
+ server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
+ CONTENT = "test get value"
+ NAME = server.insert({"doctype": "Note", "title": "get_value", "content": CONTENT}).get("name")
+
+ self.assertEqual(server.get_value("Note", "content", NAME).get("content"), CONTENT)
+
+ def test_get_value_with_malicious_query(self):
+ server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
+ server.insert({"doctype": "Note", "title": "get_value"})
self.assertRaises(
FrappeException,
@@ -106,15 +102,13 @@ class TestFrappeClient(FrappeTestCase):
def test_update_doc(self):
server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
- frappe.db.delete("Note", {"title": "Sing"})
- frappe.db.commit()
+ resp = server.insert({"doctype": "Note", "title": "Sing"})
+ doc = server.get_doc("Note", resp.get("name"))
- server.insert({"doctype": "Note", "title": "Sing"})
- doc = server.get_doc("Note", "Sing")
- new_content = "Hello, World!
"
- doc["content"] = new_content
+ CONTENT = "Hello, World!
"
+ doc["content"] = CONTENT
doc = server.update(doc)
- self.assertTrue(doc["content"] == new_content)
+ self.assertTrue(doc["content"] == CONTENT)
def test_update_child_doc(self):
server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
@@ -160,17 +154,9 @@ class TestFrappeClient(FrappeTestCase):
def test_delete_doc(self):
server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
- frappe.db.delete("Note", {"title": "delete"})
- frappe.db.commit()
-
- server.insert_many(
- [
- {"doctype": "Note", "public": True, "title": "delete"},
- ]
- )
- server.delete("Note", "delete")
-
- self.assertFalse(frappe.db.get_value("Note", {"title": "delete"}))
+ NAME_TO_DELETE = server.insert({"doctype": "Note", "title": "Sing"}).get("name")
+ server.delete("Note", NAME_TO_DELETE)
+ self.assertFalse(frappe.db.get_value("Note", NAME_TO_DELETE))
def test_auth_via_api_key_secret(self):
# generate API key and API secret for administrator
From 913417e1dccb7fb698fd04f4c7433169cfa89b4c Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Tue, 17 Jan 2023 23:51:58 +0100
Subject: [PATCH 10/20] fix: naming test cases
... that used to rely on title based naming of Note
---
frappe/tests/test_naming.py | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py
index 9f5b46a4d1..da750554d7 100644
--- a/frappe/tests/test_naming.py
+++ b/frappe/tests/test_naming.py
@@ -31,16 +31,13 @@ class TestNaming(FrappeTestCase):
if Bottle-1 exists
Bottle -> Bottle-2
"""
+ TITLE = "Bottle"
+ DOCTYPE = "Note"
- note = frappe.new_doc("Note")
- note.title = "Test"
- note.insert()
+ note = frappe.get_doc({"doctype": DOCTYPE, "title": TITLE}).insert()
- title2 = append_number_if_name_exists("Note", "Test")
- self.assertEqual(title2, "Test-1")
-
- title2 = append_number_if_name_exists("Note", "Test", "title", "_")
- self.assertEqual(title2, "Test_1")
+ self.assertEqual(append_number_if_name_exists(DOCTYPE, note.name), f"{note.name}-1")
+ self.assertEqual(append_number_if_name_exists(DOCTYPE, TITLE, "title", "_"), f"{TITLE}_1")
def test_field_autoname_name_sync(self):
@@ -276,8 +273,8 @@ class TestNaming(FrappeTestCase):
# set by passing set_name as ToDo
self.assertRaises(frappe.NameError, make_invalid_todo)
- # set new name - Note
- note = frappe.get_doc({"doctype": "Note", "title": "Note"})
+ # name (via title field) cannot be the same as the doctype
+ note = frappe.get_doc({"doctype": "Currency", "currency_name": "Currency"})
self.assertRaises(frappe.NameError, note.insert)
# case 2: set name with "New ---"
From ce545c807d2c1ffab8d299073866c49375c5ab2a Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Wed, 18 Jan 2023 13:24:13 +0100
Subject: [PATCH 11/20] fix: test frappe client
---
frappe/tests/test_frappe_client.py | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/frappe/tests/test_frappe_client.py b/frappe/tests/test_frappe_client.py
index 4d7897b344..40ba854b82 100644
--- a/frappe/tests/test_frappe_client.py
+++ b/frappe/tests/test_frappe_client.py
@@ -8,6 +8,7 @@ import requests
import frappe
from frappe.core.doctype.user.user import generate_keys
from frappe.frappeclient import FrappeClient, FrappeException
+from frappe.model import default_fields
from frappe.tests.utils import FrappeTestCase
from frappe.utils.data import get_url
@@ -26,7 +27,8 @@ class TestFrappeClient(FrappeTestCase):
{"doctype": "Note", "title": "sixpence"},
]
)
- records = frappe.get_all("Note", pluck="title")
+ records = server.get_list("Note", fields=["title"])
+ records = [r.get("title") for r in records]
self.assertIn("Sing", records)
self.assertIn("a", records)
@@ -36,9 +38,13 @@ class TestFrappeClient(FrappeTestCase):
def test_create_doc(self):
server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
- server.insert({"doctype": "Note", "title": "test_create"})
+ response = server.insert({"doctype": "Note", "title": "test_create"})
- self.assertTrue(frappe.db.get_value("Note", {"title": "test_create"}))
+ for field in default_fields:
+ self.assertIn(field, response)
+
+ self.assertEqual(response.get("doctype"), "Note")
+ self.assertEqual(response.get("title"), "test_create")
def test_list_docs(self):
server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False)
@@ -55,6 +61,9 @@ class TestFrappeClient(FrappeTestCase):
NAME = server.insert({"doctype": DOCTYPE, "title": TITLE}).get("name")
doc = server.get_doc(DOCTYPE, NAME)
+ for field in default_fields:
+ self.assertIn(field, doc)
+
self.assertEqual(doc.get("doctype"), DOCTYPE)
self.assertEqual(doc.get("name"), NAME)
self.assertEqual(doc.get("title"), TITLE)
From 2e816ed68cd163f0058af8795fdde966293ae554 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Wed, 18 Jan 2023 13:49:57 +0100
Subject: [PATCH 12/20] fix: test_client_insert_many
---
frappe/tests/test_client.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frappe/tests/test_client.py b/frappe/tests/test_client.py
index 485c023bc4..febf3412ad 100644
--- a/frappe/tests/test_client.py
+++ b/frappe/tests/test_client.py
@@ -237,8 +237,8 @@ class TestClient(FrappeTestCase):
docs = insert_many(doc_list)
self.assertEqual(len(docs), 7)
- self.assertEqual(docs[3], "not-a-random-title")
- self.assertEqual(docs[6], "another-note-title")
+ self.assertEqual(frappe.db.get_value("Note", docs[3], "title"), "not-a-random-title")
+ self.assertEqual(frappe.db.get_value("Note", docs[6], "title"), "another-note-title")
self.assertIn(note1.name, docs)
# cleanup
From f3c30065a2e4b8e8edb6e77bcf249b55f9ba2cf4 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Wed, 8 Mar 2023 17:17:14 +0100
Subject: [PATCH 13/20] test: web form with new Note behaviour
---
cypress/integration/web_form.js | 33 ++++++++++++++++++++-------------
frappe/tests/ui_test_helpers.py | 4 +++-
2 files changed, 23 insertions(+), 14 deletions(-)
diff --git a/cypress/integration/web_form.js b/cypress/integration/web_form.js
index 53f72ab013..8dd16389e7 100644
--- a/cypress/integration/web_form.js
+++ b/cypress/integration/web_form.js
@@ -6,7 +6,7 @@ context("Web Form", () => {
.window()
.its("frappe")
.then((frappe) => {
- return frappe.xcall("frappe.tests.ui_test_helpers.clear_notes");
+ return frappe.xcall("frappe.tests.ui_test_helpers.prepare_webform_test");
});
});
@@ -99,7 +99,7 @@ context("Web Form", () => {
cy.visit("/note");
cy.url().should("include", "/note/list");
- cy.get(".web-list-table thead th").contains("Name");
+ cy.get(".web-list-table thead th").contains("Sr.");
cy.get(".web-list-table thead th").contains("Title");
cy.visit("/app/web-form/note");
@@ -133,13 +133,17 @@ context("Web Form", () => {
cy.visit("/note");
cy.url().should("include", "/note/list");
+ cy.get(".web-list-table thead th").contains("Sr.");
cy.get(".web-list-table thead th").contains("Title");
cy.get(".web-list-table thead th").contains("Public");
cy.get(".web-list-table thead th").contains("Content");
});
it("Breadcrumbs", () => {
- cy.visit("/note/Note 1");
+ cy.visit("/note");
+ cy.url().should("include", "/note/list");
+ cy.get(".web-list-table tbody tr:last").click();
+
cy.get(".breadcrumb-container .breadcrumb .breadcrumb-item:first a")
.should("contain.text", "Note")
.click();
@@ -154,7 +158,9 @@ context("Web Form", () => {
cy.get(".form-tabs .nav-item .nav-link").contains("Customization").click();
cy.save();
- cy.visit("/note/Note 1");
+ cy.visit("/note");
+ cy.url().should("include", "/note/list");
+ cy.get(".web-list-table tbody tr:last").click();
cy.get(".breadcrumb-container .breadcrumb .breadcrumb-item:first a").should(
"contain.text",
"Notes"
@@ -167,7 +173,7 @@ context("Web Form", () => {
cy.url().should("include", "/note/list");
// Read Only Field
- cy.get('.web-list-table tbody tr[id="Note 1"]').click();
+ cy.get(".web-list-table tbody tr:last").click();
cy.get('.frappe-control[data-fieldname="title"] .control-input').should(
"have.css",
"display",
@@ -183,11 +189,12 @@ context("Web Form", () => {
cy.save();
- cy.visit("/note/Note 1");
- cy.url().should("include", "/note/Note%201");
+ cy.visit("/note");
+ cy.url().should("include", "/note/list");
+ cy.get(".web-list-table tbody tr:last").click();
cy.get(".web-form-actions a").contains("Edit Response").click();
- cy.url().should("include", "/note/Note%201/edit");
+ cy.url().should("include", "/edit");
// Editable Field
cy.get_field("title").should("have.value", "Note 1");
@@ -227,16 +234,16 @@ context("Web Form", () => {
cy.visit("/note");
cy.url().should("include", "/note/list");
- cy.get('.web-list-table tbody tr[id="Note 1"] .list-col-checkbox input').click();
- cy.get('.web-list-table tbody tr[id="Note 2"] .list-col-checkbox input').click();
+ cy.get(".web-list-table tbody tr:nth-child(1) .list-col-checkbox input").click();
+ cy.get(".web-list-table tbody tr:nth-child(2) .list-col-checkbox input").click();
cy.get(".web-list-actions button:visible").contains("Delete").click({ force: true });
cy.get(".web-list-actions button").contains("Delete").should("not.be.visible");
cy.visit("/note");
- cy.get('.web-list-table tbody tr[id="Note 1"]').should("not.exist");
- cy.get('.web-list-table tbody tr[id="Note 2"]').should("not.exist");
- cy.get('.web-list-table tbody tr[id="Guest Note 1"]').should("exist");
+ cy.get(".web-list-table tbody tr:nth-child(1)").should("exist");
+ cy.get(".web-list-table tbody tr:nth-child(2)").should("not.exist");
+ cy.get(".web-list-table tbody tr:nth-child(3)").should("not.exist");
});
it("Navigate and Submit a WebForm", () => {
diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py
index 2846b33eb9..26d6b2d5da 100644
--- a/frappe/tests/ui_test_helpers.py
+++ b/frappe/tests/ui_test_helpers.py
@@ -77,10 +77,12 @@ def create_todo_records():
@whitelist_for_tests
-def clear_notes():
+def prepare_webform_test():
for note in frappe.get_all("Note", pluck="name"):
frappe.delete_doc("Note", note, force=True)
+ frappe.delete_doc_if_exists("Web Form", "note")
+
@whitelist_for_tests
def create_communication_record():
From cff29e23b0aee1198120f1b415af26db53f8af17 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Wed, 8 Mar 2023 18:21:25 +0100
Subject: [PATCH 14/20] test: route to form
Test routing to User form instead of Note
---
cypress/integration/view_routing.js | 15 +++++++--------
frappe/tests/ui_test_helpers.py | 6 ------
2 files changed, 7 insertions(+), 14 deletions(-)
diff --git a/cypress/integration/view_routing.js b/cypress/integration/view_routing.js
index 9267974154..72fb6836ec 100644
--- a/cypress/integration/view_routing.js
+++ b/cypress/integration/view_routing.js
@@ -215,14 +215,13 @@ context("View", () => {
});
it("Route to Form", () => {
- cy.call("frappe.tests.ui_test_helpers.create_note").then(() => {
- cy.visit("/app/note/Routing Test");
- cy.window()
- .its("cur_frm")
- .then((frm) => {
- expect(frm.doc.title).to.equal("Routing Test");
- });
- });
+ const test_user = cy.config("testUser");
+ cy.visit(`/app/user/${test_user}`);
+ cy.window()
+ .its("cur_frm")
+ .then((frm) => {
+ expect(frm.doc.name).to.equal(test_user);
+ });
});
it("Route to Settings Workspace", () => {
diff --git a/frappe/tests/ui_test_helpers.py b/frappe/tests/ui_test_helpers.py
index 26d6b2d5da..b153181b27 100644
--- a/frappe/tests/ui_test_helpers.py
+++ b/frappe/tests/ui_test_helpers.py
@@ -535,12 +535,6 @@ def setup_default_view(view, force_reroute=None):
).insert()
-@whitelist_for_tests
-def create_note():
- if not frappe.db.exists("Note", "Routing Test"):
- frappe.get_doc({"doctype": "Note", "title": "Routing Test"}).insert()
-
-
@whitelist_for_tests
def create_kanban():
if not frappe.db.exists("Custom Field", "Note-kanban"):
From 9579ab7be92cb44ca9e1c7bfc4e124ee3751c5b0 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Mon, 24 Apr 2023 16:12:28 +0200
Subject: [PATCH 15/20] fix: show edit button only if write permitted
---
frappe/desk/doctype/note/note.js | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/frappe/desk/doctype/note/note.js b/frappe/desk/doctype/note/note.js
index fe6e234187..e55a1ec724 100644
--- a/frappe/desk/doctype/note/note.js
+++ b/frappe/desk/doctype/note/note.js
@@ -4,11 +4,12 @@ frappe.ui.form.on("Note", {
frm.is_note_editable = false;
frm.events.set_editable(frm, frm.is_note_editable);
- // toggle edit
- frm.add_custom_button(__("Editing mode"), function () {
- frm.is_note_editable = !frm.is_note_editable;
- frm.events.set_editable(frm, frm.is_note_editable);
- });
+ if (frm.has_perm("write")) {
+ frm.add_custom_button(__("Editing mode"), function () {
+ frm.is_note_editable = !frm.is_note_editable;
+ frm.events.set_editable(frm, frm.is_note_editable);
+ });
+ }
}
},
set_editable: function (frm, editable) {
From 20a08757e5109d91cd1f6713c738b1b935043839 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Mon, 24 Apr 2023 16:18:28 +0200
Subject: [PATCH 16/20] fix: role permissions for Note
---
frappe/desk/doctype/note/note.json | 24 +++++++++++++++----
.../doctype/note_seen_by/note_seen_by.json | 5 ++--
2 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/frappe/desk/doctype/note/note.json b/frappe/desk/doctype/note/note.json
index 471e457a5d..b297e2ada6 100644
--- a/frappe/desk/doctype/note/note.json
+++ b/frappe/desk/doctype/note/note.json
@@ -86,7 +86,7 @@
"icon": "fa fa-file-text",
"idx": 1,
"links": [],
- "modified": "2023-01-17 20:04:01.789170",
+ "modified": "2023-04-24 16:07:03.281184",
"modified_by": "Administrator",
"module": "Desk",
"name": "Note",
@@ -112,17 +112,31 @@
"write": 1
},
{
- "create": 1,
- "delete": 1,
- "email": 1,
+ "permlevel": 2,
+ "read": 1,
+ "role": "System Manager",
+ "write": 1
+ },
+ {
"export": 1,
- "if_owner": 1,
"print": 1,
"read": 1,
"report": 1,
+ "role": "All"
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "if_owner": 1,
"role": "All",
"share": 1,
"write": 1
+ },
+ {
+ "permlevel": 1,
+ "read": 1,
+ "role": "All"
}
],
"quick_entry": 1,
diff --git a/frappe/desk/doctype/note_seen_by/note_seen_by.json b/frappe/desk/doctype/note_seen_by/note_seen_by.json
index f54559d2f5..905a043284 100644
--- a/frappe/desk/doctype/note_seen_by/note_seen_by.json
+++ b/frappe/desk/doctype/note_seen_by/note_seen_by.json
@@ -13,12 +13,13 @@
"fieldtype": "Link",
"in_list_view": 1,
"label": "User",
- "options": "User"
+ "options": "User",
+ "permlevel": 2
}
],
"istable": 1,
"links": [],
- "modified": "2022-08-03 12:20:51.030908",
+ "modified": "2023-04-24 16:14:53.684098",
"modified_by": "Administrator",
"module": "Desk",
"name": "Note Seen By",
From bc287ab857215b94fc6e4bcdbec20429a6c7424d Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Mon, 24 Apr 2023 16:18:38 +0200
Subject: [PATCH 17/20] feat: permission query for Note
---
frappe/desk/doctype/note/note.py | 7 +++++++
frappe/hooks.py | 1 +
2 files changed, 8 insertions(+)
diff --git a/frappe/desk/doctype/note/note.py b/frappe/desk/doctype/note/note.py
index 85ff95d52e..bbe5c1c0fc 100644
--- a/frappe/desk/doctype/note/note.py
+++ b/frappe/desk/doctype/note/note.py
@@ -30,3 +30,10 @@ def mark_as_seen(note: str):
note: Note = frappe.get_doc("Note", note)
note.mark_seen_by(frappe.session.user)
note.save(ignore_permissions=True, ignore_version=True)
+
+
+def get_permission_query_conditions(user):
+ if not user:
+ user = frappe.session.user
+
+ return f"(`tabNote`.owner = '{user}' or `tabNote`.public = 1)"
diff --git a/frappe/hooks.py b/frappe/hooks.py
index a5edcc49e1..03c4c701cb 100644
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -100,6 +100,7 @@ permission_query_conditions = {
"Dashboard Chart": "frappe.desk.doctype.dashboard_chart.dashboard_chart.get_permission_query_conditions",
"Number Card": "frappe.desk.doctype.number_card.number_card.get_permission_query_conditions",
"Notification Settings": "frappe.desk.doctype.notification_settings.notification_settings.get_permission_query_conditions",
+ "Note": "frappe.desk.doctype.note.note.get_permission_query_conditions",
"Kanban Board": "frappe.desk.doctype.kanban_board.kanban_board.get_permission_query_conditions",
"Contact": "frappe.contacts.address_and_contact.get_permission_query_conditions_for_contact",
"Address": "frappe.contacts.address_and_contact.get_permission_query_conditions_for_address",
From 3a35a22d5a9cbee7e1d827cf41512f535e59670a Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Mon, 24 Apr 2023 16:33:08 +0200
Subject: [PATCH 18/20] fix: escape user in db query
---
frappe/desk/doctype/note/note.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frappe/desk/doctype/note/note.py b/frappe/desk/doctype/note/note.py
index bbe5c1c0fc..6aba6391cb 100644
--- a/frappe/desk/doctype/note/note.py
+++ b/frappe/desk/doctype/note/note.py
@@ -36,4 +36,4 @@ def get_permission_query_conditions(user):
if not user:
user = frappe.session.user
- return f"(`tabNote`.owner = '{user}' or `tabNote`.public = 1)"
+ return f"(`tabNote`.owner = {frappe.db.escape(user)} or `tabNote`.public = 1)"
From df22b98f0c7b89f172f902007b3fc7555c51c2ab Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Mon, 24 Apr 2023 16:33:34 +0200
Subject: [PATCH 19/20] feat: toggle between read and edit mode
---
frappe/desk/doctype/note/note.js | 30 +++++++++++++++++-------------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/frappe/desk/doctype/note/note.js b/frappe/desk/doctype/note/note.js
index e55a1ec724..567e7534ba 100644
--- a/frappe/desk/doctype/note/note.js
+++ b/frappe/desk/doctype/note/note.js
@@ -2,21 +2,23 @@ frappe.ui.form.on("Note", {
refresh: function (frm) {
if (!frm.is_new()) {
frm.is_note_editable = false;
- frm.events.set_editable(frm, frm.is_note_editable);
-
- if (frm.has_perm("write")) {
- frm.add_custom_button(__("Editing mode"), function () {
- frm.is_note_editable = !frm.is_note_editable;
- frm.events.set_editable(frm, frm.is_note_editable);
- });
- }
+ frm.events.set_editable(frm);
}
},
- set_editable: function (frm, editable) {
+ set_editable: function (frm) {
+ if (frm.has_perm("write")) {
+ const read_label = __("Read mode");
+ const edit_label = __("Edit mode");
+ frm.remove_custom_button(frm.is_note_editable ? edit_label : read_label);
+ frm.add_custom_button(frm.is_note_editable ? read_label : edit_label, function () {
+ frm.is_note_editable = !frm.is_note_editable;
+ frm.events.set_editable(frm);
+ });
+ }
// toggle "read_only" for content and "hidden" of all other fields
// content read_only
- frm.set_df_property("content", "read_only", editable ? 0 : 1);
+ frm.set_df_property("content", "read_only", frm.is_note_editable ? 0 : 1);
// hide all other fields
for (const field of frm.meta.fields) {
@@ -24,14 +26,16 @@ frappe.ui.form.on("Note", {
frm.set_df_property(
field.fieldname,
"hidden",
- editable && !field.hidden && frm.get_perm(field.permlevel, "write") ? 0 : 1
+ frm.is_note_editable && !field.hidden && frm.get_perm(field.permlevel, "write")
+ ? 0
+ : 1
);
}
}
// no label, description for content either
- frm.get_field("content").toggle_label(editable);
- frm.get_field("content").toggle_description(editable);
+ frm.get_field("content").toggle_label(frm.is_note_editable);
+ frm.get_field("content").toggle_description(frm.is_note_editable);
},
});
From e133124e71b6204f3ec840a2ec761ed1b6133314 Mon Sep 17 00:00:00 2001
From: barredterra <14891507+barredterra@users.noreply.github.com>
Date: Mon, 24 Apr 2023 18:47:31 +0200
Subject: [PATCH 20/20] test: web form
---
cypress/integration/web_form.js | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/cypress/integration/web_form.js b/cypress/integration/web_form.js
index 8dd16389e7..fcb24219b9 100644
--- a/cypress/integration/web_form.js
+++ b/cypress/integration/web_form.js
@@ -241,9 +241,7 @@ context("Web Form", () => {
cy.get(".web-list-actions button").contains("Delete").should("not.be.visible");
cy.visit("/note");
- cy.get(".web-list-table tbody tr:nth-child(1)").should("exist");
- cy.get(".web-list-table tbody tr:nth-child(2)").should("not.exist");
- cy.get(".web-list-table tbody tr:nth-child(3)").should("not.exist");
+ cy.get(".web-list-table tbody tr:nth-child(1)").should("not.exist");
});
it("Navigate and Submit a WebForm", () => {