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/cypress/integration/web_form.js b/cypress/integration/web_form.js index 53f72ab013..fcb24219b9 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,14 @@ 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("not.exist"); }); it("Navigate and Submit a WebForm", () => { diff --git a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py index 8f1773608f..2460c40e8a 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,12 +13,14 @@ class TestAutoAssign(FrappeTestCase): def setUpClass(cls): super().setUpClass() frappe.db.delete("Assignment Rule") + create_test_doctype(TEST_DOCTYPE) @classmethod def tearDownClass(cls): frappe.db.rollback() def setUp(self): + frappe.set_user("Administrator") make_test_records("User") days = [ dict(day="Sunday"), @@ -33,45 +36,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 +88,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): @@ -111,45 +118,36 @@ class TestAutoAssign(FrappeTestCase): self.assignment_rule.field = "owner" self.assignment_rule.save() - frappe.set_user("test1@example.com") - note = make_note(dict(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" - ), - "test1@example.com", - ) - - frappe.set_user("test2@example.com") - note = make_note(dict(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" - ), - "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 - 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 +163,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 +184,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 +206,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 +232,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 +247,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 +266,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 +276,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 +304,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 +324,60 @@ 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: str): + """Create custom doctype.""" + frappe.db.delete("DocType", doctype) + + 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", + }, + ], + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1, + "write": 1, + }, + ], + } + ).insert() diff --git a/frappe/desk/doctype/note/note.js b/frappe/desk/doctype/note/note.js index dd556a3969..567e7534ba 100644 --- a/frappe/desk/doctype/note/note.js +++ b/frappe/desk/doctype/note/note.js @@ -1,41 +1,41 @@ 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 = ""; - } - - // toggle edit - frm.add_custom_button("Edit", function () { - frm.events.set_editable(frm, !frm.is_note_editable); - }); - frm.events.set_editable(frm, false); + if (!frm.is_new()) { + frm.is_note_editable = false; + frm.events.set_editable(frm); } }, - set_editable: function (frm, editable) { - // hide all fields other than content - - // no permission - if (editable && !frm.perm[0].write) return; + 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 - $.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", + 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); - - // set flag for toggle - frm.is_note_editable = editable; + frm.get_field("content").toggle_label(frm.is_note_editable); + frm.get_field("content").toggle_description(frm.is_note_editable); }, }); diff --git a/frappe/desk/doctype/note/note.json b/frappe/desk/doctype/note/note.json index 69a9518ac4..b297e2ada6 100644 --- a/frappe/desk/doctype/note/note.json +++ b/frappe/desk/doctype/note/note.json @@ -1,6 +1,6 @@ { "actions": [], - "allow_rename": 1, + "autoname": "hash", "creation": "2013-05-24 13:41:00", "doctype": "DocType", "document_type": "Document", @@ -19,11 +19,8 @@ { "fieldname": "title", "fieldtype": "Data", - "in_global_search": 1, "in_list_view": 1, "label": "Title", - "no_copy": 1, - "print_hide": 1, "reqd": 1 }, { @@ -32,6 +29,7 @@ "fieldname": "public", "fieldtype": "Check", "label": "Public", + "permlevel": 1, "print_hide": 1 }, { @@ -40,7 +38,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 +48,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,39 +69,80 @@ }, { "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", "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": "2021-09-18 10:57:51.352643", + "modified": "2023-04-24 16:07:03.281184", "modified_by": "Administrator", "module": "Desk", "name": "Note", + "naming_rule": "Random", "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 + }, + { + "permlevel": 2, + "read": 1, + "role": "System Manager", + "write": 1 + }, + { + "export": 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, "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.py b/frappe/desk/doctype/note/note.py index 4e11c1c055..6aba6391cb 100644 --- a/frappe/desk/doctype/note/note.py +++ b/frappe/desk/doctype/note/note.py @@ -1,53 +1,39 @@ # 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, ignore_permissions=True) +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 - 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 + return f"(`tabNote`.owner = {frappe.db.escape(user)} or `tabNote`.public = 1)" 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"]; 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", diff --git a/frappe/hooks.py b/frappe/hooks.py index a1aa2713c3..6d8c00d483 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -114,7 +114,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", 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) 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 diff --git a/frappe/tests/test_frappe_client.py b/frappe/tests/test_frappe_client.py index e80e43f49c..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 @@ -17,33 +18,33 @@ 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 = server.get_list("Note", fields=["title"]) + records = [r.get("title") for r in records] - 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() + response = server.insert({"doctype": "Note", "title": "test_create"}) - server.insert({"doctype": "Note", "public": True, "title": "test_create"}) + for field in default_fields: + self.assertIn(field, response) - self.assertTrue(frappe.db.get_value("Note", {"title": "test_create"})) + 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) @@ -52,37 +53,41 @@ 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): + 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) + 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 +111,13 @@ class TestFrappeClient(FrappeTestCase): def test_update_doc(self): server = FrappeClient(get_url(), "Administrator", self.PASSWORD, verify=False) - frappe.db.delete("Note", {"title": ("in", ("Sing", "sing"))}) - frappe.db.commit() + resp = server.insert({"doctype": "Note", "title": "Sing"}) + doc = server.get_doc("Note", resp.get("name")) - server.insert({"doctype": "Note", "public": True, "title": "Sing"}) - doc = server.get_doc("Note", "Sing") - changed_title = "sing" - doc["title"] = changed_title + CONTENT = "