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 %}
-
+
\ 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 @@