diff --git a/.github/workflows/patch-mariadb-tests.yml b/.github/workflows/patch-mariadb-tests.yml index d9a6ca6f59..52fa987994 100644 --- a/.github/workflows/patch-mariadb-tests.yml +++ b/.github/workflows/patch-mariadb-tests.yml @@ -10,6 +10,7 @@ concurrency: jobs: test: runs-on: ubuntu-latest + timeout-minutes: 60 name: Patch Test diff --git a/.github/workflows/server-mariadb-tests.yml b/.github/workflows/server-mariadb-tests.yml index 588f357f26..4edf74ba71 100644 --- a/.github/workflows/server-mariadb-tests.yml +++ b/.github/workflows/server-mariadb-tests.yml @@ -14,6 +14,7 @@ concurrency: jobs: test: runs-on: ubuntu-latest + timeout-minutes: 60 strategy: fail-fast: false @@ -128,4 +129,4 @@ jobs: fail_ci_if_error: true files: /home/runner/frappe-bench/sites/coverage.xml verbose: true - flags: server \ No newline at end of file + flags: server diff --git a/.github/workflows/server-postgres-tests.yml b/.github/workflows/server-postgres-tests.yml index 78f379837b..895af5184e 100644 --- a/.github/workflows/server-postgres-tests.yml +++ b/.github/workflows/server-postgres-tests.yml @@ -13,6 +13,7 @@ concurrency: jobs: test: runs-on: ubuntu-latest + timeout-minutes: 60 strategy: fail-fast: false diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index fcc53ba59c..cb502f68a7 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -13,6 +13,7 @@ concurrency: jobs: test: runs-on: ubuntu-latest + timeout-minutes: 60 strategy: fail-fast: false diff --git a/cypress/integration/grid_pagination.js b/cypress/integration/grid_pagination.js index c07230d2b8..84b3320282 100644 --- a/cypress/integration/grid_pagination.js +++ b/cypress/integration/grid_pagination.js @@ -13,7 +13,7 @@ context('Grid Pagination', () => { it('creates pages for child table', () => { cy.visit('/app/contact/Test Contact'); cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); - cy.get('@table').find('.current-page-number').should('contain', '1'); + cy.get('@table').find('.current-page-number').should('have.value', '1'); cy.get('@table').find('.total-page-number').should('contain', '20'); cy.get('@table').find('.grid-body .grid-row').should('have.length', 50); }); @@ -21,10 +21,10 @@ context('Grid Pagination', () => { cy.visit('/app/contact/Test Contact'); cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); cy.get('@table').find('.next-page').click(); - cy.get('@table').find('.current-page-number').should('contain', '2'); + cy.get('@table').find('.current-page-number').should('have.value', '2'); cy.get('@table').find('.grid-body .grid-row').first().should('have.attr', 'data-idx', '51'); cy.get('@table').find('.prev-page').click(); - cy.get('@table').find('.current-page-number').should('contain', '1'); + cy.get('@table').find('.current-page-number').should('have.value', '1'); cy.get('@table').find('.grid-body .grid-row').first().should('have.attr', 'data-idx', '1'); }); it('adds and deletes rows and changes page', () => { @@ -32,14 +32,35 @@ context('Grid Pagination', () => { cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); cy.get('@table').findByRole('button', {name: 'Add Row'}).click(); cy.get('@table').find('.grid-body .row-index').should('contain', 1001); - cy.get('@table').find('.current-page-number').should('contain', '21'); + cy.get('@table').find('.current-page-number').should('have.value', '21'); cy.get('@table').find('.total-page-number').should('contain', '21'); cy.get('@table').find('.grid-body .grid-row .grid-row-check').click({ force: true }); cy.get('@table').findByRole('button', {name: 'Delete'}).click(); cy.get('@table').find('.grid-body .row-index').last().should('contain', 1000); - cy.get('@table').find('.current-page-number').should('contain', '20'); + cy.get('@table').find('.current-page-number').should('have.value', '20'); cy.get('@table').find('.total-page-number').should('contain', '20'); }); + it('go to specific page, use up and down arrow, type characters, 0 page and more than existing page', () => { + cy.visit('/app/contact/Test Contact'); + cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); + cy.get('@table').find('.current-page-number').focus().clear().type('17').blur(); + cy.get('@table').find('.grid-body .row-index').should('contain', 801); + + cy.get('@table').find('.current-page-number').focus().type('{uparrow}{uparrow}'); + cy.get('@table').find('.current-page-number').should('have.value', '19'); + + cy.get('@table').find('.current-page-number').focus().type('{downarrow}{downarrow}'); + cy.get('@table').find('.current-page-number').should('have.value', '17'); + + cy.get('@table').find('.current-page-number').focus().clear().type('700').blur(); + cy.get('@table').find('.current-page-number').should('have.value', '20'); + + cy.get('@table').find('.current-page-number').focus().clear().type('0').blur(); + cy.get('@table').find('.current-page-number').should('have.value', '1'); + + cy.get('@table').find('.current-page-number').focus().clear().type('abc').blur(); + cy.get('@table').find('.current-page-number').should('have.value', '1'); + }); // it('deletes all rows', ()=> { // cy.visit('/app/contact/Test Contact'); // cy.get('.frappe-control[data-fieldname="phone_nos"]').as('table'); diff --git a/cypress/integration/report_view.js b/cypress/integration/report_view.js index 0253e8fd43..629ae72eb8 100644 --- a/cypress/integration/report_view.js +++ b/cypress/integration/report_view.js @@ -7,6 +7,8 @@ context('Report View', () => { cy.visit('/app/website'); cy.insert_doc('DocType', custom_submittable_doctype, true); cy.clear_cache(); + }); + it('Field with enabled allow_on_submit should be editable.', () => { cy.insert_doc(doctype_name, { 'title': 'Doc 1', 'description': 'Random Text', @@ -14,8 +16,6 @@ context('Report View', () => { // submit document 'docstatus': 1 }, true).as('doc'); - }); - it('Field with enabled allow_on_submit should be editable.', () => { cy.intercept('POST', 'api/method/frappe.client.set_value').as('value-update'); cy.visit(`/app/List/${doctype_name}/Report`); // check status column added from docstatus diff --git a/frappe/core/doctype/comment/test_comment.py b/frappe/core/doctype/comment/test_comment.py index 99bd19c106..cd9af498aa 100644 --- a/frappe/core/doctype/comment/test_comment.py +++ b/frappe/core/doctype/comment/test_comment.py @@ -5,6 +5,15 @@ import frappe, json import unittest class TestComment(unittest.TestCase): + def tearDown(self): + frappe.form_dict.comment = None + frappe.form_dict.comment_email = None + frappe.form_dict.comment_by = None + frappe.form_dict.reference_doctype = None + frappe.form_dict.reference_name = None + frappe.form_dict.route = None + frappe.local.request_ip = None + def test_comment_creation(self): test_doc = frappe.get_doc(dict(doctype = 'ToDo', description = 'test')) test_doc.insert() @@ -33,8 +42,16 @@ class TestComment(unittest.TestCase): frappe.db.delete("Comment", {"reference_doctype": "Blog Post"}) from frappe.templates.includes.comments.comments import add_comment - add_comment('Good comment with 10 chars', 'test@test.com', 'Good Tester', - 'Blog Post', test_blog.name, test_blog.route) + + frappe.form_dict.comment = 'Good comment with 10 chars' + frappe.form_dict.comment_email = 'test@test.com' + frappe.form_dict.comment_by = 'Good Tester' + frappe.form_dict.reference_doctype = 'Blog Post' + frappe.form_dict.reference_name = test_blog.name + frappe.form_dict.route = test_blog.route + frappe.local.request_ip = '127.0.0.1' + + add_comment() self.assertEqual(frappe.get_all('Comment', fields = ['*'], filters = dict( reference_doctype = test_blog.doctype, @@ -43,8 +60,10 @@ class TestComment(unittest.TestCase): frappe.db.delete("Comment", {"reference_doctype": "Blog Post"}) - add_comment('pleez vizits my site http://mysite.com', 'test@test.com', 'bad commentor', - 'Blog Post', test_blog.name, test_blog.route) + frappe.form_dict.comment = 'pleez vizits my site http://mysite.com' + frappe.form_dict.comment_by = 'bad commentor' + + add_comment() self.assertEqual(len(frappe.get_all('Comment', fields = ['*'], filters = dict( reference_doctype = test_blog.doctype, diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 4d22075b78..54ddbce2c4 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -146,25 +146,43 @@ def add_attachments(name, attachments): }) _file.save(ignore_permissions=True) -@frappe.whitelist(allow_guest=True) -def mark_email_as_seen(name=None): +@frappe.whitelist(allow_guest=True, methods=("GET",)) +def mark_email_as_seen(name: str = None): try: - if name and frappe.db.exists("Communication", name) and not frappe.db.get_value("Communication", name, "read_by_recipient"): - frappe.db.set_value("Communication", name, "read_by_recipient", 1) - frappe.db.set_value("Communication", name, "delivery_status", "Read") - frappe.db.set_value("Communication", name, "read_by_recipient_on", get_datetime()) - frappe.db.commit() + update_communication_as_read(name) + frappe.db.commit() # nosemgrep: this will be called in a GET request + except Exception: frappe.log_error(frappe.get_traceback()) + finally: - # Return image as response under all circumstances - from PIL import Image - import io - im = Image.new('RGBA', (1, 1)) - im.putdata([(255,255,255,0)]) - buffered_obj = io.BytesIO() - im.save(buffered_obj, format="PNG") - - frappe.response["type"] = 'binary' - frappe.response["filename"] = "imaginary_pixel.png" - frappe.response["filecontent"] = buffered_obj.getvalue() + frappe.response.update({ + "type": "binary", + "filename": "imaginary_pixel.png", + "filecontent": ( + b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00" + b"\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\r" + b"IDATx\x9cc\xf8\xff\xff?\x03\x00\x08\xfc\x02\xfe\xa7\x9a\xa0" + b"\xa0\x00\x00\x00\x00IEND\xaeB`\x82" + ) + }) + +def update_communication_as_read(name): + if not name or not isinstance(name, str): + return + + communication = frappe.db.get_value( + "Communication", + name, + "read_by_recipient", + as_dict=True + ) + + if not communication or communication.read_by_recipient: + return + + frappe.db.set_value("Communication", name, { + "read_by_recipient": 1, + "delivery_status": "Read", + "read_by_recipient_on": get_datetime() + }) diff --git a/frappe/core/doctype/feedback/feedback.json b/frappe/core/doctype/feedback/feedback.json index b77e7a6677..f8380cfda6 100644 --- a/frappe/core/doctype/feedback/feedback.json +++ b/frappe/core/doctype/feedback/feedback.json @@ -8,34 +8,14 @@ "reference_doctype", "reference_name", "column_break_3", - "rating", - "ip_address", - "section_break_6", - "feedback" + "like", + "ip_address" ], "fields": [ { "fieldname": "column_break_3", "fieldtype": "Column Break" }, - { - "fieldname": "rating", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Rating", - "precision": "1", - "reqd": 1 - }, - { - "fieldname": "section_break_6", - "fieldtype": "Section Break" - }, - { - "fieldname": "feedback", - "fieldtype": "Small Text", - "label": "Feedback", - "reqd": 1 - }, { "fieldname": "reference_doctype", "fieldtype": "Select", @@ -57,11 +37,17 @@ "hidden": 1, "label": "IP Address", "read_only": 1 + }, + { + "default": "0", + "fieldname": "like", + "fieldtype": "Check", + "label": "Like" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-06-23 12:45:42.045696", + "modified": "2021-11-10 20:53:21.255593", "modified_by": "Administrator", "module": "Core", "name": "Feedback", diff --git a/frappe/core/doctype/feedback/test_feedback.py b/frappe/core/doctype/feedback/test_feedback.py index f3cf8dfe6b..66f644ccd3 100644 --- a/frappe/core/doctype/feedback/test_feedback.py +++ b/frappe/core/doctype/feedback/test_feedback.py @@ -8,8 +8,7 @@ class TestFeedback(unittest.TestCase): def tearDown(self): frappe.form_dict.reference_doctype = None frappe.form_dict.reference_name = None - frappe.form_dict.rating = None - frappe.form_dict.feedback = None + frappe.form_dict.like = None frappe.local.request_ip = None def test_feedback_creation_updation(self): @@ -18,23 +17,22 @@ class TestFeedback(unittest.TestCase): frappe.db.delete("Feedback", {"reference_doctype": "Blog Post"}) - from frappe.templates.includes.feedback.feedback import add_feedback, update_feedback + from frappe.templates.includes.feedback.feedback import give_feedback frappe.form_dict.reference_doctype = 'Blog Post' frappe.form_dict.reference_name = test_blog.name - frappe.form_dict.rating = 5 - frappe.form_dict.feedback = 'New feedback' + frappe.form_dict.like = True frappe.local.request_ip = '127.0.0.1' - feedback = add_feedback() + feedback = give_feedback() - self.assertEqual(feedback.feedback, 'New feedback') - self.assertEqual(feedback.rating, 5) + self.assertEqual(feedback.like, True) - updated_feedback = update_feedback('Blog Post', test_blog.name, 6, 'Updated feedback') + frappe.form_dict.like = False - self.assertEqual(updated_feedback.feedback, 'Updated feedback') - self.assertEqual(updated_feedback.rating, 6) + updated_feedback = give_feedback() + + self.assertEqual(updated_feedback.like, False) frappe.db.delete("Feedback", {"reference_doctype": "Blog Post"}) diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index ea31e76a57..cf05ce0c15 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -599,7 +599,7 @@ "fieldname": "desk_theme", "fieldtype": "Select", "label": "Desk Theme", - "options": "Light\nDark" + "options": "Light\nDark\nAutomatic" }, { "fieldname": "module_profile", @@ -669,7 +669,7 @@ } ], "max_attachments": 5, - "modified": "2021-10-27 17:17:16.098457", + "modified": "2021-11-17 17:17:16.098457", "modified_by": "Administrator", "module": "Core", "name": "User", diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 86fd1cb4a6..b127cf5f0c 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -1046,7 +1046,7 @@ def generate_keys(user): @frappe.whitelist() def switch_theme(theme): - if theme in ["Dark", "Light"]: + if theme in ["Dark", "Light", "Automatic"]: frappe.db.set_value("User", frappe.session.user, "desk_theme", theme) def get_enabled_users(): diff --git a/frappe/database/database.py b/frappe/database/database.py index 18f5b4dda9..6703ae0ff3 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -570,12 +570,11 @@ class Database(object): def _get_value_for_many_names(self, doctype, names, field, debug=False, run=True, **kwargs): names = list(filter(None, names)) - if names: return self.get_all(doctype, - fields=['name', field], - filters=[['name', 'in', names]], - debug=debug, as_list=1, run=run, **kwargs), + fields=field, + filters=names, + debug=debug, as_list=1, run=run) else: return {} diff --git a/frappe/desk/doctype/notification_settings/notification_settings.json b/frappe/desk/doctype/notification_settings/notification_settings.json index fc12022e89..fc535fa405 100644 --- a/frappe/desk/doctype/notification_settings/notification_settings.json +++ b/frappe/desk/doctype/notification_settings/notification_settings.json @@ -15,7 +15,9 @@ "enable_email_energy_point", "enable_email_share", "user", - "seen" + "seen", + "system_notifications_section", + "energy_points_system_notifications" ], "fields": [ { @@ -84,15 +86,27 @@ "fieldtype": "Check", "hidden": 1, "label": "Seen" + }, + { + "fieldname": "system_notifications_section", + "fieldtype": "Section Break", + "label": "System Notifications" + }, + { + "default": "1", + "fieldname": "energy_points_system_notifications", + "fieldtype": "Check", + "label": "Energy Points" } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-11-04 12:54:57.989317", + "modified": "2021-11-16 12:18:46.955501", "modified_by": "Administrator", "module": "Desk", "name": "Notification Settings", + "naming_rule": "Set by user", "owner": "Administrator", "permissions": [ { @@ -111,4 +125,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} diff --git a/frappe/patches/v13_0/rename_custom_client_script.py b/frappe/patches/v13_0/rename_custom_client_script.py index 718f1f6a46..b74c518aeb 100644 --- a/frappe/patches/v13_0/rename_custom_client_script.py +++ b/frappe/patches/v13_0/rename_custom_client_script.py @@ -1,9 +1,13 @@ import frappe +from frappe.model.rename_doc import rename_doc def execute(): if frappe.db.exists("DocType", "Client Script"): return - frappe.rename_doc("DocType", "Custom Script", "Client Script") + frappe.flags.ignore_route_conflict_validation = True + rename_doc("DocType", "Custom Script", "Client Script") + frappe.flags.ignore_route_conflict_validation = False + frappe.reload_doctype("Client Script", force=True) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index a53368d67a..2855c6ae7c 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -64,6 +64,19 @@ frappe.Application = class Application { } }); + frappe.ui.add_system_theme_switch_listener(); + const root = document.documentElement; + + const observer = new MutationObserver(() => { + frappe.ui.set_theme(); + }); + observer.observe(root, { + attributes: true, + attributeFilter: ['data-theme-mode'] + }); + + frappe.ui.set_theme(); + // page container this.make_page_container(); this.set_route(); diff --git a/frappe/public/js/frappe/form/dashboard.js b/frappe/public/js/frappe/form/dashboard.js index 9d5e7cbe09..df4dbf09e7 100644 --- a/frappe/public/js/frappe/form/dashboard.js +++ b/frappe/public/js/frappe/form/dashboard.js @@ -14,6 +14,7 @@ frappe.ui.form.Dashboard = class FormDashboard { this.progress_area = this.make_section({ css_class: 'progress-area', hidden: 1, + collapsible: 1, is_dashboard_section: 1, }); @@ -21,6 +22,7 @@ frappe.ui.form.Dashboard = class FormDashboard { label: __("Overview"), css_class: 'form-heatmap', hidden: 1, + collapsible: 1, is_dashboard_section: 1, body_html: `
@@ -32,6 +34,7 @@ frappe.ui.form.Dashboard = class FormDashboard { label: __("Graph"), css_class: 'form-graph', hidden: 1, + collapsible: 1, is_dashboard_section: 1 }); @@ -40,6 +43,7 @@ frappe.ui.form.Dashboard = class FormDashboard { label: __("Stats"), css_class: 'form-stats', hidden: 1, + collapsible: 1, is_dashboard_section: 1, body_html: this.stats_area_row }); @@ -50,6 +54,7 @@ frappe.ui.form.Dashboard = class FormDashboard { label: __("Connections"), css_class: 'form-links', hidden: 1, + collapsible: 1, is_dashboard_section: 1, body_html: this.transactions_area }); @@ -84,9 +89,10 @@ frappe.ui.form.Dashboard = class FormDashboard { hidden, body_html, make_card: true, + collapsible: 1, is_dashboard_section: 1 }; - return new Section(this.frm.layout.wrapper, options).body; + return new Section(this.parent, options).body; } add_progress(title, percent, message) { @@ -203,7 +209,7 @@ frappe.ui.form.Dashboard = class FormDashboard { after_refresh() { // show / hide new buttons (if allowed) this.links_area.body.find('.btn-new').each((i, el) => { - if (this.frm.can_create($(this).attr('data-doctype'))) { + if (this.frm.can_create($(el).attr('data-doctype'))) { $(el).removeClass('hidden'); } }); diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 75d68b12db..27281d8927 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -156,8 +156,11 @@ frappe.ui.form.Form = class FrappeForm { let dashboard_parent = $('
'); - let main_page = this.layout.tabs.length ? this.layout.tabs[0].wrapper : this.layout.wrapper; - main_page.prepend(dashboard_parent); + if (this.layout.tabs.length) { + this.layout.tabs[0].wrapper.prepend(dashboard_parent); + } else { + dashboard_parent.insertAfter(this.layout.wrapper.find('.form-message')); + } this.dashboard = new frappe.ui.form.Dashboard(dashboard_parent, this); this.tour = new frappe.ui.form.FormTour({ diff --git a/frappe/public/js/frappe/form/grid_pagination.js b/frappe/public/js/frappe/form/grid_pagination.js index 35daafe89d..76a5f7b50b 100644 --- a/frappe/public/js/frappe/form/grid_pagination.js +++ b/frappe/public/js/frappe/form/grid_pagination.js @@ -46,8 +46,55 @@ export default class GridPagination { this.last_page_button.on('click', () => { this.go_to_page(this.total_pages); }); + + this.$page_number.on('keyup', (e) => { + e.currentTarget.style.width = ((e.currentTarget.value.length + 1) * 8) + 'px'; + }); + + this.$page_number.on('keydown', (e) => { + e = (e) ? e : window.event; + var charCode = (e.which) ? e.which : e.keyCode; + let arrow = { up: 38, down: 40 }; + + switch (charCode) { + case arrow.up: + this.inc_dec_number(true); + break; + case arrow.down: + this.inc_dec_number(false); + break; + } + + // only allow numbers from 0-9 and up, down, left, right arrow keys + if (charCode > 31 && (charCode < 48 || charCode > 57) && + ![37, 38, 39, 40].includes(charCode)) { + return false; + } + return true; + }); + + this.$page_number.on('focusout', (e) => { + if (this.page_index == e.currentTarget.value) return; + this.page_index = e.currentTarget.value; + + if (this.page_index < 1) { + this.page_index = 1; + } else if (this.page_index > this.total_pages) { + this.page_index = this.total_pages; + } + + this.go_to_page(); + }); } + inc_dec_number(increment) { + let new_value = parseInt(this.$page_number.val()); + increment ? new_value++ : new_value--; + + if (new_value < 1 || new_value > this.total_pages) return; + + this.$page_number.val(new_value); + } update_page_numbers() { let total_pages = Math.ceil(this.grid.data.length/this.page_length); @@ -65,7 +112,7 @@ export default class GridPagination { get_pagination_html() { let page_text_html = `
- ${__(this.page_index)} + ${__('of')} ${__(this.total_pages)}
`; @@ -104,7 +151,8 @@ export default class GridPagination { let $rows = $(this.grid.parent).find(".rows").empty(); this.grid.render_result_rows($rows, true); if (this.$page_number) { - this.$page_number.text(index); + this.$page_number.val(index); + this.$page_number.css('width', ((index.toString().length + 1) * 8) + 'px'); } this.update_page_numbers(); diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 7710c82ee7..0de6b1db0d 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -245,7 +245,7 @@ frappe.ui.form.Layout = class Layout { } make_section(df) { - this.section = new Section(this.current_tab ? this.current_tab.wrapper : this.page, df, this.card_layout); + this.section = new Section(this.current_tab ? this.current_tab.wrapper : this.page, df, this.card_layout, this); // append to layout fields if (df) { diff --git a/frappe/public/js/frappe/form/section.js b/frappe/public/js/frappe/form/section.js index e0120f6afc..b0ec491ce6 100644 --- a/frappe/public/js/frappe/form/section.js +++ b/frappe/public/js/frappe/form/section.js @@ -1,5 +1,6 @@ export default class Section { - constructor(parent, df, card_layout) { + constructor(parent, df, card_layout, layout) { + this.layout = layout; this.card_layout = card_layout; this.parent = parent; this.df = df || {}; @@ -25,6 +26,7 @@ export default class Section { ${this.df.is_dashboard_section ? "form-dashboard-section" : "form-section"} ${ make_card ? "card-section" : "" }"> `).appendTo(this.parent); + this.layout && this.layout.sections.push(this); if (this.df) { if (this.df.label) { diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 07c8acef27..64530e15ef 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -307,6 +307,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { } update_checkbox(target) { + if (!this.$checkbox_actions) return; + let $check_all_checkbox = this.$checkbox_actions.find(".list-check-all"); if ($check_all_checkbox.prop("checked") && target && !target.prop("checked")) { diff --git a/frappe/public/js/frappe/ui/theme_switcher.js b/frappe/public/js/frappe/ui/theme_switcher.js index 4524472415..2c1d93a2ec 100644 --- a/frappe/public/js/frappe/ui/theme_switcher.js +++ b/frappe/public/js/frappe/ui/theme_switcher.js @@ -42,7 +42,7 @@ frappe.ui.ThemeSwitcher = class ThemeSwitcher { } refresh() { - this.current_theme = document.documentElement.getAttribute("data-theme") || "light"; + this.current_theme = document.documentElement.getAttribute("data-theme-mode") || "light"; this.fetch_themes().then(() => { this.render(); }); @@ -54,10 +54,17 @@ frappe.ui.ThemeSwitcher = class ThemeSwitcher { { name: "light", label: __("Frappe Light"), + info: __("Light Theme") }, { name: "dark", label: __("Timeless Night"), + info: __("Dark Theme") + }, + { + name: "automatic", + label: __("Automatic"), + info: __("Uses system's theme to switch between light and dark mode") } ]; @@ -74,11 +81,15 @@ frappe.ui.ThemeSwitcher = class ThemeSwitcher { } get_preview_html(theme) { + const is_auto_theme = theme.name === "automatic"; const preview = $(`
-
+
-
${frappe.utils.icon('tick', 'xs')}
+
+ ${frappe.utils.icon('tick', 'xs')} +
@@ -112,13 +123,14 @@ frappe.ui.ThemeSwitcher = class ThemeSwitcher { toggle_theme(theme) { this.current_theme = theme.toLowerCase(); - document.documentElement.setAttribute("data-theme", this.current_theme); + document.documentElement.setAttribute("data-theme-mode", this.current_theme); frappe.show_alert("Theme Changed", 3); frappe.xcall("frappe.core.doctype.user.user.switch_theme", { theme: toTitle(theme) }); } + show() { this.dialog.show(); } @@ -127,3 +139,22 @@ frappe.ui.ThemeSwitcher = class ThemeSwitcher { this.dialog.hide(); } }; + +frappe.ui.add_system_theme_switch_listener = () => { + frappe.ui.dark_theme_media_query.addEventListener('change', () => { + frappe.ui.set_theme(); + }); +}; + +frappe.ui.dark_theme_media_query = window.matchMedia("(prefers-color-scheme: dark)"); + +frappe.ui.set_theme = (theme) => { + const root = document.documentElement; + let theme_mode = root.getAttribute("data-theme-mode"); + if (!theme) { + if (theme_mode === "automatic") { + theme = frappe.ui.dark_theme_media_query.matches ? 'dark' : 'light'; + } + } + root.setAttribute("data-theme", theme || theme_mode); +}; \ No newline at end of file diff --git a/frappe/public/js/frappe/utils/user.js b/frappe/public/js/frappe/utils/user.js index aeeb83f630..3eb73b21e5 100644 --- a/frappe/public/js/frappe/utils/user.js +++ b/frappe/public/js/frappe/utils/user.js @@ -12,7 +12,7 @@ frappe.user_info = function(uid) { if(!(frappe.boot.user_info && frappe.boot.user_info[uid])) { var user_info = { - fullname: frappe.utils.capitalize(uid.split("@")[0]) || "Unknown" + fullname: frappe.utils.to_title_case(uid.split("@")[0]) || "Unknown" }; } else { var user_info = frappe.boot.user_info[uid]; diff --git a/frappe/public/js/frappe/views/interaction.js b/frappe/public/js/frappe/views/interaction.js index 119eba13fb..a1f5947e11 100644 --- a/frappe/public/js/frappe/views/interaction.js +++ b/frappe/public/js/frappe/views/interaction.js @@ -224,6 +224,9 @@ frappe.views.InteractionComposer = class InteractionComposer { if (!("owner" in interaction_values)){ interaction_values["owner"] = frappe.session.user; } + if (!("assigned_by" in interaction_values) && interaction_values["doctype"] == "ToDo") { + interaction_values["assigned_by"] = frappe.session.user; + } return frappe.call({ method:"frappe.client.insert", args: { doc: interaction_values}, diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 04cc1b9880..dbf33d7058 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -634,6 +634,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { this.render_datatable(); this.add_chart_buttons_to_toolbar(true); this.add_card_button_to_toolbar(); + this.$report.show(); } else { this.data = []; this.toggle_nothing_to_show(true); @@ -882,7 +883,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { hide_loading_screen() { this.$loading.hide(); - this.$report.show(); } get_chart_options(data) { diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index 8866a4b2af..86b46d77a3 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -106,6 +106,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { get_args() { const args = super.get_args(); + args.group_by = null; this.group_by_control.set_args(args); return args; diff --git a/frappe/public/scss/common/grid.scss b/frappe/public/scss/common/grid.scss index 57d0583b35..3014211222 100644 --- a/frappe/public/scss/common/grid.scss +++ b/frappe/public/scss/common/grid.scss @@ -373,6 +373,18 @@ .page-text { display: inline-block; + cursor: default; +} + +.current-page-number { + width: 16px; + text-align: center; + border: none; + cursor: text; + + &:focus { + outline: none; + } } .prev-page, diff --git a/frappe/public/scss/desk/theme_switcher.scss b/frappe/public/scss/desk/theme_switcher.scss index 00e3f35be8..924c2edd9d 100644 --- a/frappe/public/scss/desk/theme_switcher.scss +++ b/frappe/public/scss/desk/theme_switcher.scss @@ -1,6 +1,6 @@ .modal-body .theme-grid { display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-columns: repeat(3, minmax(0, 1fr)); grid-gap: 18px; .background { @@ -9,7 +9,7 @@ border-radius: var(--border-radius-lg); overflow: hidden; cursor: pointer; - height: 160px; + height: 120px; position: relative; &:hover { @@ -28,6 +28,7 @@ margin-right: var(--margin-sm); border-radius: var(--border-radius-full); + z-index: 1; } } @@ -72,6 +73,7 @@ border-radius: var(--border-radius-sm); height: 10px; width: 20px; + z-index: 1; } .text { @@ -80,4 +82,17 @@ height: 10px; width: 40px; } +} + +// TODO: Replace with better alternative +[data-is-auto-theme="true"] { + .background::after { + content: ""; + top: 0; + right: 0; + height: 100%; + width: 50%; + background: var(--gray-900); + position: absolute; + } } \ No newline at end of file diff --git a/frappe/public/scss/website/blog.scss b/frappe/public/scss/website/blog.scss index ea82efed21..e599210435 100644 --- a/frappe/public/scss/website/blog.scss +++ b/frappe/public/scss/website/blog.scss @@ -1,3 +1,8 @@ +:root { + --comment-timeline-bottom: 60px; + --comment-timeline-top: 8px; +} + .blog-list { display: flex; flex-wrap: wrap; @@ -96,4 +101,124 @@ margin-top: 3rem; } } + + + .feedback-item svg { + vertical-align: sub; + } + + .blog-feedback { + display: flex; + + .like-icon { + cursor: pointer; + + &.gray use { + fill: var(--gray-600); + stroke: none; + } + } + } + + .add-comment-button { + margin-left: 35px; + } + + .timeline-dot { + width: 16px; + height: 16px; + border-radius: 50%; + position: absolute; + top: 8px; + left: 22px; + background-color: var(--fg-color); + border: 1px solid var(--dark-border-color); + + &:before { + content: ' '; + background: var(--gray-600); + position: absolute; + top: 5px; + left: 5px; + border-radius: 50%; + height: 4px; + width: 4px; + } + } + + .blog-comments { + .comment-form-wrapper { + display: none; + } + + .add-comment-section { + .login-required { + padding: var(--padding-sm); + border-radius: var(--border-radius-sm); + box-shadow: var(--card-shadow); + } + + .new-comment { + display: flex; + padding: var(--padding-lg); + box-shadow: var(--card-shadow); + border-radius: var(--border-radius-md); + + .new-comment-fields { + flex: 1; + + .form-label { + font-weight: var(--text-bold); + } + + .comment-text-area textarea { + resize: none; + } + + @media (min-width: 576px) { + .comment-by { + padding-right: 0px !important; + padding-bottom: 0px !important; + } + } + } + } + } + + + #comment-list { + position: relative; + padding-left: var(--padding-xl); + + &:before { + content: " "; + position: absolute; + top: var(--comment-timeline-top); + bottom: var(--comment-timeline-bottom); + border-left: 1px solid var(--dark-border-color); + } + + .comment-row { + position: relative; + + .comment-avatar { + position: absolute; + top: 10px; + left: -17px; + } + + .comment-content { + box-shadow: var(--card-shadow); + border-radius: var(--border-radius-md); + padding: var(--padding-md); + margin-left: 35px; + flex: 1; + + .content p{ + margin-bottom: 0px; + } + } + } + } + } } diff --git a/frappe/public/scss/website/index.scss b/frappe/public/scss/website/index.scss index 8e98333306..2957a0b499 100644 --- a/frappe/public/scss/website/index.scss +++ b/frappe/public/scss/website/index.scss @@ -99,6 +99,12 @@ } } +.page-header-wrapper { + display: flex; + justify-content: space-between; + align-items: center; +} + .breadcrumb-container { margin-top: 1rem; padding-top: 0.25rem; diff --git a/frappe/sessions.py b/frappe/sessions.py index 9b96435093..cbe20cffb3 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -171,6 +171,8 @@ def get(): bootinfo["setup_complete"] = cint(frappe.db.get_single_value('System Settings', 'setup_complete')) bootinfo["is_first_startup"] = cint(frappe.db.get_single_value('System Settings', 'is_first_startup')) + bootinfo['desk_theme'] = frappe.db.get_value("User", frappe.session.user, "desk_theme") or 'Light' + return bootinfo @frappe.whitelist() diff --git a/frappe/social/doctype/energy_point_log/energy_point_log.py b/frappe/social/doctype/energy_point_log/energy_point_log.py index 3ffabcd241..86843302e9 100644 --- a/frappe/social/doctype/energy_point_log/energy_point_log.py +++ b/frappe/social/doctype/energy_point_log/energy_point_log.py @@ -32,7 +32,9 @@ class EnergyPointLog(Document): frappe.cache().hdel('energy_points', self.user) frappe.publish_realtime('update_points', after_commit=True) - if self.type != 'Review': + if self.type != 'Review' and \ + frappe.get_cached_value('Notification Settings', self.user, 'energy_points_system_notifications'): + reference_user = self.user if self.type == 'Auto' else self.owner notification_doc = { 'type': 'Energy Point', diff --git a/frappe/social/doctype/energy_point_log/test_energy_point_log.py b/frappe/social/doctype/energy_point_log/test_energy_point_log.py index c2bcbde825..a1f4503c34 100644 --- a/frappe/social/doctype/energy_point_log/test_energy_point_log.py +++ b/frappe/social/doctype/energy_point_log/test_energy_point_log.py @@ -8,6 +8,18 @@ from frappe.utils.testutils import add_custom_field, clear_custom_fields from frappe.desk.form.assign_to import add as assign_to class TestEnergyPointLog(unittest.TestCase): + @classmethod + def setUpClass(cls): + settings = frappe.get_single('Energy Point Settings') + settings.enabled = 1 + settings.save() + + @classmethod + def tearDownClass(cls): + settings = frappe.get_single('Energy Point Settings') + settings.enabled = 0 + settings.save() + def setUp(self): frappe.cache().delete_value('energy_point_rule_map') @@ -336,4 +348,4 @@ def assign_users_to_todo(todo_name, users): 'assign_to': [user], 'doctype': 'ToDo', 'name': todo_name - }) \ No newline at end of file + }) diff --git a/frappe/social/doctype/energy_point_settings/energy_point_settings.json b/frappe/social/doctype/energy_point_settings/energy_point_settings.json index 0001b26529..d1f9aea3d0 100644 --- a/frappe/social/doctype/energy_point_settings/energy_point_settings.json +++ b/frappe/social/doctype/energy_point_settings/energy_point_settings.json @@ -1,229 +1,70 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, + "actions": [], "creation": "2019-03-19 13:17:51.710241", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "enabled", + "section_break_2", + "review_levels", + "point_allocation_periodicity", + "last_point_allocation_date" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fetch_if_empty": 0, + "default": "0", "fieldname": "enabled", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Enabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Enabled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "enabled", - "fetch_if_empty": 0, "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "review_levels", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Review Levels", - "length": 0, - "no_copy": 0, - "options": "Review Level", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Review Level" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Weekly", - "fetch_if_empty": 0, "fieldname": "point_allocation_periodicity", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Point Allocation Periodicity", - "length": 0, - "no_copy": 0, - "options": "Daily\nWeekly\nMonthly", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Daily\nWeekly\nMonthly" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "last_point_allocation_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Last Point Allocation Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 } ], - "has_web_view": 0, "hide_toolbar": 1, - "idx": 0, - "in_create": 0, - "is_submittable": 0, "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2019-03-26 19:10:14.087840", + "links": [], + "modified": "2021-11-16 23:24:01.366928", "modified_by": "Administrator", "module": "Social", "name": "Energy Point Settings", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, - "report": 0, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], "quick_entry": 1, - "read_only": 0, - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "ASC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/frappe/social/doctype/energy_point_settings/test_energy_point_settings.py b/frappe/social/doctype/energy_point_settings/test_energy_point_settings.py new file mode 100644 index 0000000000..3b0a756878 --- /dev/null +++ b/frappe/social/doctype/energy_point_settings/test_energy_point_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies and Contributors +# See license.txt + +# import frappe +import unittest + +class TestEnergyPointSettings(unittest.TestCase): + pass diff --git a/frappe/templates/includes/avatar_macro.html b/frappe/templates/includes/avatar_macro.html index 6983477f9c..b652b573b3 100644 --- a/frappe/templates/includes/avatar_macro.html +++ b/frappe/templates/includes/avatar_macro.html @@ -1,6 +1,6 @@ -{% macro avatar(user_id=None, css_style=None) %} +{% macro avatar(user_id=None, css_style=None, size="avatar-small") %} {% set user_info = frappe.utils.get_user_info_for_avatar(user_id) %} - + {% if user_info.image %} - {{ frappe.utils.get_abbr(user_info.name) }} + {{ frappe.utils.get_abbr(user_info.name).upper() }} {% endif %} diff --git a/frappe/templates/includes/comments/comment.html b/frappe/templates/includes/comments/comment.html index 08a2b79ee6..e0fc1c3c54 100644 --- a/frappe/templates/includes/comments/comment.html +++ b/frappe/templates/includes/comments/comment.html @@ -1,18 +1,14 @@ -{% from "frappe/templates/includes/macros.html" import square_image_with_fallback %} +{% from "frappe/templates/includes/avatar_macro.html" import avatar %} -
- {{ square_image_with_fallback(src=frappe.get_gravatar(comment.comment_email or comment.sender), size='extra-small', alt=comment.sender_full_name, class='align-self-start mr-4') }} -
-
- - {{ comment.sender_full_name or comment.comment_by }} - - - {{ comment.creation | global_date_format }} - -
-
- {{ comment.content | markdown }} -
-
-
+
+
+ {{ avatar(user_id=(comment.comment_email or comment.sender), size='avatar-medium') }} +
+
+
+ {{ comment.sender_full_name or comment.comment_by }} + {{ frappe.utils.pretty_date(comment.creation) }} +
+
{{ comment.content | markdown }}
+
+
\ No newline at end of file diff --git a/frappe/templates/includes/comments/comments.html b/frappe/templates/includes/comments/comments.html index 935fa5367e..8a9058bb4d 100644 --- a/frappe/templates/includes/comments/comments.html +++ b/frappe/templates/includes/comments/comments.html @@ -1,86 +1,146 @@
- {% if comment_text %} -
{{ comment_text }}
- {% endif %} {% if not comment_list %} -
-

{{ _("No comments yet. Start a new discussion.") }}

-
+
+

{{ _("No comments yet. ") }} + + +

+
{% endif %} -
- {% for comment in comment_list %} -
- {% include "templates/includes/comments/comment.html" %} + {% if not is_communication %} +
+
+
+
+
+ +
+
{{ _("Add a comment") }}
+ +
{{ _("Ctrl+Enter to add comment") }}
+
+ +
+
+
+
- {% endfor %} -
-
+ {% endif %} -{% if not is_communication %} -
- +
-
- {{ _("Add Comment") }} - -{% endif %} + diff --git a/frappe/templates/includes/comments/comments.py b/frappe/templates/includes/comments/comments.py index 3bba388ac2..e352c7368b 100644 --- a/frappe/templates/includes/comments/comments.py +++ b/frappe/templates/includes/comments/comments.py @@ -3,11 +3,14 @@ import frappe import re from frappe.website.utils import clear_cache +from frappe.rate_limiter import rate_limit from frappe.utils import add_to_date, now +from frappe.website.doctype.blog_settings.blog_settings import get_comment_limit from frappe import _ @frappe.whitelist(allow_guest=True) +@rate_limit(key='reference_name', limit=get_comment_limit, seconds=60*60) def add_comment(comment, comment_email, comment_by, reference_doctype, reference_name, route): doc = frappe.get_doc(reference_doctype, reference_name) diff --git a/frappe/templates/includes/feedback/feedback.html b/frappe/templates/includes/feedback/feedback.html index f180fa5e42..7925d4dccd 100644 --- a/frappe/templates/includes/feedback/feedback.html +++ b/frappe/templates/includes/feedback/feedback.html @@ -1,160 +1,43 @@ -
- + \ No newline at end of file diff --git a/frappe/templates/includes/feedback/feedback.py b/frappe/templates/includes/feedback/feedback.py index e1a6f8e881..62fdc3f746 100644 --- a/frappe/templates/includes/feedback/feedback.py +++ b/frappe/templates/includes/feedback/feedback.py @@ -10,25 +10,8 @@ from frappe.website.doctype.blog_settings.blog_settings import get_feedback_limi @frappe.whitelist(allow_guest=True) @rate_limit(key='reference_name', limit=get_feedback_limit, seconds=60*60) -def add_feedback(reference_doctype, reference_name, rating, feedback): - doc = frappe.get_doc(reference_doctype, reference_name) - if doc.disable_feedback == 1: - return - - doc = frappe.new_doc('Feedback') - doc.reference_doctype = reference_doctype - doc.reference_name = reference_name - doc.rating = rating - doc.feedback = feedback - doc.ip_address = frappe.local.request_ip - doc.save(ignore_permissions=True) - - subject = _('New Feedback on {0}: {1}').format(reference_doctype, reference_name) - send_mail(doc, subject) - return doc - -@frappe.whitelist() -def update_feedback(reference_doctype, reference_name, rating, feedback): +def give_feedback(reference_doctype, reference_name, like): + like = frappe.parse_json(like) doc = frappe.get_doc(reference_doctype, reference_name) if doc.disable_feedback == 1: return @@ -39,22 +22,26 @@ def update_feedback(reference_doctype, reference_name, rating, feedback): "reference_name": reference_name } d = frappe.get_all('Feedback', filters=filters, limit=1) - doc = frappe.get_doc('Feedback', d[0].name) - doc.rating = rating - doc.feedback = feedback + if d: + doc = frappe.get_doc('Feedback', d[0].name) + else: + doc = doc = frappe.new_doc('Feedback') + doc.reference_doctype = reference_doctype + doc.reference_name = reference_name + doc.ip_address = frappe.local.request_ip + doc.like = like doc.save(ignore_permissions=True) - subject = _('Feedback updated on {0}: {1}').format(reference_doctype, reference_name) + subject = _('Feedback on {0}: {1}').format(reference_doctype, reference_name) send_mail(doc, subject) return doc def send_mail(feedback, subject): doc = frappe.get_doc(feedback.reference_doctype, feedback.reference_name) - - message = ("

{0} ({1})

".format(feedback.feedback, feedback.rating) - + "

{2}

".format(frappe.utils.get_request_site_address(), - feedback.name, - _("View Feedback"))) + if feedback.like: + message = "

Hey,

You have received a ❤️ heart on your blog post {0}

".format(feedback.reference_name) + else: + return # notify creator frappe.sendmail( diff --git a/frappe/templates/web.html b/frappe/templates/web.html index 57fefa333d..48fbce7cd1 100644 --- a/frappe/templates/web.html +++ b/frappe/templates/web.html @@ -14,7 +14,7 @@ {% block page_container %}
-
+