* [wip] feedback * [WIP] Feedback Trigger, Feedback Request, Feedback & Rating Timeline and Form Sidebar * [minor] minor fixes in feedback trigger * [Test Cases] Test Cases for Feedback Trigger * [minor] replaced sql_list by get_all changed the button label * [minor] removed whitespaces, fixed spelling mistakesversion-14
@@ -31,6 +31,12 @@ frappe.ui.form.on("Communication", { | |||||
} | } | ||||
} | } | ||||
if(frm.doc.communication_type == "Feedback") { | |||||
frm.add_custom_button(__("Resend"), function() { | |||||
frm.events.resend_feedback(frm); | |||||
}); | |||||
} | |||||
if(frm.doc.status==="Open") { | if(frm.doc.status==="Open") { | ||||
frm.add_custom_button(__("Close"), function() { | frm.add_custom_button(__("Close"), function() { | ||||
frm.set_value("status", "Closed"); | frm.set_value("status", "Closed"); | ||||
@@ -93,6 +99,60 @@ frappe.ui.form.on("Communication", { | |||||
} | } | ||||
}); | }); | ||||
d.show(); | d.show(); | ||||
} | |||||
}, | |||||
resend_feedback: function(frm) { | |||||
/* resend the feedback request email */ | |||||
return frappe.call({ | |||||
method: "frappe.core.doctype.feedback_trigger.feedback_trigger.get_feedback_alert_details", | |||||
args: { | |||||
request: frm.doc.feedback_request, | |||||
reference_name: frm.doc.reference_name, | |||||
reference_doctype: frm.doc.reference_doctype | |||||
}, | |||||
callback: function(r) { | |||||
if(r.message) { | |||||
details = r.message; | |||||
dialog = new frappe.ui.Dialog({ | |||||
title: __("Resend Feedback Request"), | |||||
fields: [ | |||||
{ | |||||
"reqd": 1, | |||||
"label": __("Message"), | |||||
"fieldname": "message", | |||||
"fieldtype": "Text Editor", | |||||
"default": details.message | |||||
} | |||||
], | |||||
}); | |||||
dialog.set_primary_action(__("Send"), function() { | |||||
args = dialog.get_values(); | |||||
if(!args) | |||||
return; | |||||
else | |||||
details.message = args.message | |||||
dialog.hide(); | |||||
return frappe.call({ | |||||
method: "frappe.core.doctype.feedback_trigger.feedback_trigger.send_feedback_alert", | |||||
args: { | |||||
reference_name: frm.doc.reference_name, | |||||
reference_doctype: frm.doc.reference_doctype, | |||||
alert_details: details, | |||||
}, | |||||
freeze: true, | |||||
callback: function(r) { | |||||
frappe.msgprint(__("Feedback Alert for {0} is sent to {1}", | |||||
[frm.doc.reference_name, frm.doc.sender])); | |||||
} | |||||
}) | |||||
}); | |||||
dialog.show(); | |||||
} | |||||
} | |||||
}); | |||||
} | |||||
}); | }); |
@@ -400,7 +400,7 @@ | |||||
"label": "Communication Type", | "label": "Communication Type", | ||||
"length": 0, | "length": 0, | ||||
"no_copy": 0, | "no_copy": 0, | ||||
"options": "Communication\nComment\nChat\nBot\nNotification", | |||||
"options": "Communication\nComment\nChat\nBot\nNotification\nFeedback", | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"precision": "", | "precision": "", | ||||
"print_hide": 0, | "print_hide": 0, | ||||
@@ -1208,6 +1208,118 @@ | |||||
"search_index": 0, | "search_index": 0, | ||||
"set_only_once": 0, | "set_only_once": 0, | ||||
"unique": 0 | "unique": 0 | ||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "feedback_section", | |||||
"fieldtype": "Section Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Feedback", | |||||
"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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "feedback", | |||||
"fieldtype": "Text Editor", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Feedback", | |||||
"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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "rating", | |||||
"fieldtype": "Int", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Rating", | |||||
"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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "feedback_request", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Feedback Request", | |||||
"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, | |||||
"unique": 0 | |||||
} | } | ||||
], | ], | ||||
"hide_heading": 0, | "hide_heading": 0, | ||||
@@ -1221,7 +1333,7 @@ | |||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2017-01-20 05:20:58.187840", | |||||
"modified": "2017-01-27 16:11:37.344686", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "Communication", | "name": "Communication", | ||||
@@ -0,0 +1,8 @@ | |||||
// Copyright (c) 2016, Frappe Technologies and contributors | |||||
// For license information, please see license.txt | |||||
frappe.ui.form.on('Feedback Request', { | |||||
refresh: function(frm) { | |||||
} | |||||
}); |
@@ -0,0 +1,312 @@ | |||||
{ | |||||
"allow_copy": 0, | |||||
"allow_import": 0, | |||||
"allow_rename": 0, | |||||
"autoname": "field:key", | |||||
"beta": 0, | |||||
"creation": "2017-01-27 15:43:33.780808", | |||||
"custom": 0, | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "Setup", | |||||
"editable_grid": 1, | |||||
"engine": "InnoDB", | |||||
"fields": [ | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "is_sent", | |||||
"fieldtype": "Check", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Is Sent", | |||||
"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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "column_break_2", | |||||
"fieldtype": "Column Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "is_feedback_submitted", | |||||
"fieldtype": "Check", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Feedback Submitted", | |||||
"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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "key", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Key", | |||||
"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": 1, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "reference", | |||||
"fieldtype": "Section Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Reference", | |||||
"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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "reference_doctype", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 1, | |||||
"in_standard_filter": 0, | |||||
"label": "Reference DocType", | |||||
"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": 1, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "reference_name", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 1, | |||||
"in_standard_filter": 0, | |||||
"label": "Reference Name", | |||||
"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": 1, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "column_break_5", | |||||
"fieldtype": "Column Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "feedback_trigger", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Feedback Trigger", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "Feedback Trigger", | |||||
"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, | |||||
"unique": 0 | |||||
} | |||||
], | |||||
"hide_heading": 0, | |||||
"hide_toolbar": 0, | |||||
"idx": 0, | |||||
"image_view": 0, | |||||
"in_create": 1, | |||||
"in_dialog": 0, | |||||
"is_submittable": 0, | |||||
"issingle": 0, | |||||
"istable": 0, | |||||
"max_attachments": 0, | |||||
"modified": "2017-01-30 16:37:34.346362", | |||||
"modified_by": "Administrator", | |||||
"module": "Core", | |||||
"name": "Feedback Request", | |||||
"name_case": "", | |||||
"owner": "Administrator", | |||||
"permissions": [ | |||||
{ | |||||
"amend": 0, | |||||
"apply_user_permissions": 0, | |||||
"cancel": 0, | |||||
"create": 0, | |||||
"delete": 0, | |||||
"email": 1, | |||||
"export": 1, | |||||
"if_owner": 0, | |||||
"import": 0, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "System Manager", | |||||
"set_user_permissions": 0, | |||||
"share": 1, | |||||
"submit": 0, | |||||
"write": 0 | |||||
} | |||||
], | |||||
"quick_entry": 0, | |||||
"read_only": 0, | |||||
"read_only_onload": 0, | |||||
"sort_field": "modified", | |||||
"sort_order": "DESC", | |||||
"track_changes": 1, | |||||
"track_seen": 0 | |||||
} |
@@ -0,0 +1,28 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2015, Frappe Technologies and contributors | |||||
# For license information, please see license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
from frappe.model.document import Document | |||||
class FeedbackRequest(Document): | |||||
def before_insert(self): | |||||
from frappe.utils import random_string | |||||
self.key = random_string(32) | |||||
@frappe.whitelist(allow_guest=True) | |||||
def is_valid_feedback_request(key=None): | |||||
if not key: | |||||
return False | |||||
is_feedback_submitted = frappe.db.get_value("Feedback Request", key, "is_feedback_submitted") | |||||
if is_feedback_submitted: | |||||
return False | |||||
else: | |||||
return True | |||||
def delete_feedback_request(): | |||||
""" clear 100 days old feedback request """ | |||||
frappe.db.sql("""delete from `tabFeedback Request` where creation<DATE_SUB(NOW(), INTERVAL 100 DAY)""") |
@@ -0,0 +1,12 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2015, Frappe Technologies and Contributors | |||||
# See license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
import unittest | |||||
# test_records = frappe.get_test_records('Feedback Request') | |||||
class TestFeedbackRequest(unittest.TestCase): | |||||
pass |
@@ -0,0 +1,50 @@ | |||||
// Copyright (c) 2016, Frappe Technologies and contributors | |||||
// For license information, please see license.txt | |||||
frappe.ui.form.on('Feedback Trigger', { | |||||
onload: function(frm) { | |||||
frm.set_query("document_type", function() { | |||||
return { | |||||
"filters": { | |||||
"istable": 0 | |||||
} | |||||
} | |||||
}) | |||||
}, | |||||
refresh: function(frm) { | |||||
frm.events.setup_field_options(frm); | |||||
}, | |||||
document_type: function(frm) { | |||||
frm.set_value('email_field', ''); | |||||
frm.set_value('email_fieldname'); | |||||
frm.events.setup_field_options(frm); | |||||
}, | |||||
email_field: function(frm) { | |||||
frm.set_value('email_fieldname', frm.fieldname_mapper[frm.doc.email_field]); | |||||
}, | |||||
setup_field_options: function(frm) { | |||||
frm.fieldname_mapper = {}; | |||||
frm.options = []; | |||||
if(!frm.doc.document_type) | |||||
return | |||||
frappe.model.with_doctype(frm.doc.document_type, function() { | |||||
var fields = frappe.get_doc("DocType", frm.doc.document_type).fields; | |||||
$.each(fields, function(idx, field) { | |||||
if(!inList(frappe.model.no_value_type, field.fieldtype) && field.options == "Email") { | |||||
frm.options.push(field.label); | |||||
frm.fieldname_mapper[field.label] = field.fieldname; | |||||
} | |||||
}) | |||||
frm.set_df_property("email_field", "options", [""].concat(frm.options)); | |||||
frm.refresh_fields(); | |||||
}); | |||||
} | |||||
}); |
@@ -0,0 +1,455 @@ | |||||
{ | |||||
"allow_copy": 0, | |||||
"allow_import": 0, | |||||
"allow_rename": 0, | |||||
"autoname": "field:document_type", | |||||
"beta": 0, | |||||
"creation": "2017-01-24 15:46:38.366213", | |||||
"custom": 0, | |||||
"docstatus": 0, | |||||
"doctype": "DocType", | |||||
"document_type": "Setup", | |||||
"editable_grid": 1, | |||||
"engine": "InnoDB", | |||||
"fields": [ | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "enabled", | |||||
"fieldtype": "Check", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "section_break_2", | |||||
"fieldtype": "Section Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "document_type", | |||||
"fieldtype": "Link", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Document Type", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "DocType", | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_if_no_value": 0, | |||||
"read_only": 0, | |||||
"remember_last_selected_value": 0, | |||||
"report_hide": 0, | |||||
"reqd": 1, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"depends_on": "eval: doc.document_type", | |||||
"fieldname": "email_field", | |||||
"fieldtype": "Select", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Email Field", | |||||
"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": 1, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "email_fieldname", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Email Fieldname", | |||||
"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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "column_break_5", | |||||
"fieldtype": "Column Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"description": "To add dynamic subject, use jinja tags like\n\n<div><pre><code>{{ doc.name }} Delivered</code></pre></div>", | |||||
"fieldname": "subject", | |||||
"fieldtype": "Data", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Subject", | |||||
"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": 1, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "section_break_5", | |||||
"fieldtype": "Section Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"description": "Optional: The alert will be sent if this expression is true", | |||||
"fieldname": "condition", | |||||
"fieldtype": "Small Text", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Condition", | |||||
"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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "column_break_7", | |||||
"fieldtype": "Column Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "html_8", | |||||
"fieldtype": "HTML", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "<p><strong>Condition Examples:</strong></p>\n<pre>doc.status==\"Closed\"\ndoc.due_date==nowdate()\ndoc.total > 40000\n</pre>", | |||||
"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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "section_break_9", | |||||
"fieldtype": "Section Break", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Message", | |||||
"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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "message", | |||||
"fieldtype": "Code", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Message", | |||||
"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, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "example", | |||||
"fieldtype": "HTML", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | |||||
"label": "Example", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "<h5>Message Example</h5>\n\n<pre><h3>Issue Resolved</h3>\n\n<p>Issue {{ doc.name }} Is resolved. Please check and confirm the same.</p>\n\n<p> Your Feedback is important for us. Please give us your Feedback for {{ doc.name }}</p>\n\n<p> Please visit the following url for feedback.</p>\n\n{{ feedback_url }}\n\n<h4>Details</h4></pre>", | |||||
"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, | |||||
"unique": 0 | |||||
} | |||||
], | |||||
"hide_heading": 0, | |||||
"hide_toolbar": 0, | |||||
"idx": 0, | |||||
"image_view": 0, | |||||
"in_create": 0, | |||||
"in_dialog": 0, | |||||
"is_submittable": 0, | |||||
"issingle": 0, | |||||
"istable": 0, | |||||
"max_attachments": 0, | |||||
"modified": "2017-01-29 10:45:11.175365", | |||||
"modified_by": "Administrator", | |||||
"module": "Core", | |||||
"name": "Feedback Trigger", | |||||
"name_case": "", | |||||
"owner": "Administrator", | |||||
"permissions": [ | |||||
{ | |||||
"amend": 0, | |||||
"apply_user_permissions": 0, | |||||
"cancel": 0, | |||||
"create": 1, | |||||
"delete": 1, | |||||
"email": 1, | |||||
"export": 1, | |||||
"if_owner": 0, | |||||
"import": 0, | |||||
"permlevel": 0, | |||||
"print": 1, | |||||
"read": 1, | |||||
"report": 1, | |||||
"role": "System Manager", | |||||
"set_user_permissions": 0, | |||||
"share": 1, | |||||
"submit": 0, | |||||
"write": 1 | |||||
} | |||||
], | |||||
"quick_entry": 0, | |||||
"read_only": 0, | |||||
"read_only_onload": 0, | |||||
"sort_field": "modified", | |||||
"sort_order": "DESC", | |||||
"title_field": "document_type", | |||||
"track_changes": 1, | |||||
"track_seen": 0 | |||||
} |
@@ -0,0 +1,109 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2015, Frappe Technologies and contributors | |||||
# For license information, please see license.txt | |||||
from __future__ import unicode_literals | |||||
import json | |||||
import frappe | |||||
from frappe import _ | |||||
from frappe.utils import get_url | |||||
from frappe.model.document import Document | |||||
from frappe.utils.jinja import validate_template | |||||
class FeedbackTrigger(Document): | |||||
def validate(self): | |||||
validate_template(self.subject) | |||||
validate_template(self.message) | |||||
self.validate_condition() | |||||
def validate_condition(self): | |||||
temp_doc = frappe.new_doc(self.document_type) | |||||
if self.condition: | |||||
try: | |||||
eval(self.condition, get_context(temp_doc)) | |||||
except: | |||||
frappe.throw(_("The Condition '{0}' is invalid").format(self.condition)) | |||||
@frappe.whitelist() | |||||
def send_feedback_alert(reference_doctype, reference_name, trigger=None, alert_details=None): | |||||
""" send feedback alert """ | |||||
details = json.loads(alert_details) if alert_details else \ | |||||
get_feedback_alert_details(reference_doctype, reference_name, trigger=trigger) | |||||
if details: | |||||
feedback_request = details.pop("feedback_request") | |||||
frappe.sendmail(**details) | |||||
frappe.db.set_value("Feedback Request", feedback_request, "is_sent", 1) | |||||
def trigger_feedback_alert(doc, method): | |||||
""" trigger the feedback alert""" | |||||
feedback_trigger = frappe.db.get_value("Feedback Trigger", { "enabled": 1, "document_type": doc.doctype }) | |||||
if feedback_trigger: | |||||
frappe.enqueue('frappe.core.doctype.feedback_trigger.feedback_trigger.send_feedback_alert', | |||||
trigger=feedback_trigger, reference_doctype=doc.doctype, reference_name=doc.name, now=frappe.flags.in_test) | |||||
@frappe.whitelist() | |||||
def get_feedback_alert_details(reference_doctype, reference_name, trigger=None, request=None): | |||||
feedback_url = "" | |||||
if not trigger and not request: | |||||
frappe.throw("Can not find Feedback Alert for {0}".format(reference_name)) | |||||
elif not trigger and request: | |||||
trigger = frappe.db.get_value("Feedback Request", request, "feedback_trigger") | |||||
# check if feedback mail alert is already sent but feedback is not submitted | |||||
# to avoid sending multiple feedback mail alerts | |||||
feedback_requests = frappe.get_all("Feedback Request", { | |||||
"is_sent": 1, | |||||
"is_feedback_submitted": 0, | |||||
"reference_name": reference_name, | |||||
"reference_doctype": reference_doctype | |||||
}, ["name"]) | |||||
if feedback_requests: | |||||
frappe.throw(_("Feedback Alert Mail has been already sent to the recipient")) | |||||
feedback_trigger = frappe.get_doc("Feedback Trigger", trigger) | |||||
doc = frappe.get_doc(reference_doctype, reference_name) | |||||
context = get_context(doc) | |||||
recipients = doc.get(feedback_trigger.email_fieldname, None) | |||||
if recipients and eval(feedback_trigger.condition, context): | |||||
subject = feedback_trigger.subject | |||||
feedback_request = frappe.get_doc({ | |||||
"doctype": "Feedback Request", | |||||
"reference_name": doc.name, | |||||
"reference_doctype": doc.doctype, | |||||
"feedback_trigger": feedback_trigger.name | |||||
}).insert(ignore_permissions=True) | |||||
feedback_url = "{base_url}/feedback?reference_doctype={doctype}&reference_name={docname}&email={email_id}&key={nonce}".format( | |||||
base_url=get_url(), | |||||
doctype=doc.doctype, | |||||
docname=doc.name, | |||||
email_id=recipients, | |||||
nonce=feedback_request.name | |||||
) | |||||
context.update({ "alert": feedback_trigger, "feedback_url": feedback_url }) | |||||
if "{" in subject: | |||||
subject = frappe.render_template(feedback_trigger.subject, context) | |||||
feedback_alert_message = frappe.render_template(feedback_trigger.message, context) | |||||
return { | |||||
"subject": subject, | |||||
"recipients": recipients, | |||||
"reference_name":doc.name, | |||||
"reference_doctype":doc.doctype, | |||||
"message": feedback_alert_message, | |||||
"feedback_request": feedback_request.name | |||||
} | |||||
else: | |||||
return None | |||||
def get_context(doc): | |||||
return { "doc": doc } |
@@ -0,0 +1,102 @@ | |||||
# -*- coding: utf-8 -*- | |||||
# Copyright (c) 2015, Frappe Technologies and Contributors | |||||
# See license.txt | |||||
from __future__ import unicode_literals | |||||
import frappe | |||||
import unittest | |||||
# test_records = frappe.get_test_records('Feedback Trigger') | |||||
def get_feedback_request(todo, feedback_trigger): | |||||
return frappe.db.get_value("Feedback Request", { | |||||
"is_sent": 1, | |||||
"is_feedback_submitted": 0, | |||||
"reference_doctype": "ToDo", | |||||
"reference_name": todo, | |||||
"feedback_trigger": feedback_trigger | |||||
}) | |||||
class TestFeedbackTrigger(unittest.TestCase): | |||||
def setUp(self): | |||||
new_user = frappe.get_doc(dict(doctype='User', email='test-feedback@example.com', | |||||
first_name='Tester')).insert(ignore_permissions=True) | |||||
new_user.add_roles("System Manager") | |||||
def tearDown(self): | |||||
frappe.delete_doc("User", "test-feedback@example.com") | |||||
frappe.delete_doc("Feedback Trigger", "ToDo") | |||||
frappe.db.sql('delete from `tabEmail Queue`') | |||||
frappe.db.sql('delete from `tabFeedback Request`') | |||||
def test_feedback_trigger(self): | |||||
""" Test feedback trigger """ | |||||
from frappe.www.feedback import accept | |||||
frappe.delete_doc("Feedback Trigger", "ToDo") | |||||
frappe.db.sql('delete from `tabEmail Queue`') | |||||
frappe.db.sql('delete from `tabFeedback Request`') | |||||
feedback_trigger = frappe.get_doc({ | |||||
"enabled": 1, | |||||
"doctype": "Feedback Trigger", | |||||
"document_type": "ToDo", | |||||
"email_field": "assigned_by", | |||||
"subject": "{{ doc.name }} Task Completed", | |||||
"condition": "doc.status == 'Closed'", | |||||
"message": """Task {{ doc.name }} is Completed by {{ doc.owner }}. | |||||
<br>Please visit the {{ feedback_url }} and give your feedback | |||||
regarding the Task {{ doc.name }}""" | |||||
}).insert(ignore_permissions=True) | |||||
# create a todo | |||||
todo = frappe.get_doc({ | |||||
"doctype": "ToDo", | |||||
"owner": "test-feedback@example.com", | |||||
"allocated_by": "test-feedback@example.com", | |||||
"description": "Unable To Submit Sales Order #SO-00001" | |||||
}).insert(ignore_permissions=True) | |||||
email_queue = frappe.db.sql("""select name from `tabEmail Queue` where | |||||
reference_doctype='ToDo' and reference_name='{0}'""".format(todo.name)) | |||||
# feedback alert mail should be sent only on 'Closed' status | |||||
self.assertFalse(email_queue) | |||||
# check if feedback mail alert is triggered | |||||
todo.status = "Closed" | |||||
todo.save(ignore_permissions=True) | |||||
email_queue = frappe.db.sql("""select name from `tabEmail Queue` where | |||||
reference_doctype='ToDo' and reference_name='{0}'""".format(todo.name)) | |||||
self.assertTrue(email_queue) | |||||
frappe.db.sql('delete from `tabEmail Queue`') | |||||
# test if feedback is submitted for the todo | |||||
feedback_request = get_feedback_request(todo.name, feedback_trigger.name) | |||||
self.assertTrue(feedback_request) | |||||
# test if mail alerts are triggered multiple times for same document | |||||
self.assertRaises(Exception, todo.save, ignore_permissions=True) | |||||
# Test if feedback is submitted sucessfully | |||||
result = accept(feedback_request, "test-feedback@example.com", "ToDo", todo.name, "Great Work !!", 4) | |||||
self.assertTrue(result) | |||||
# test if feedback is saved in Communication | |||||
docname = frappe.db.get_value("Communication", { | |||||
"reference_doctype": "ToDo", | |||||
"reference_name": todo.name, | |||||
"communication_type": "Feedback", | |||||
"feedback_request": feedback_request | |||||
}) | |||||
communication = frappe.get_doc("Communication", docname) | |||||
self.assertEqual(communication.rating, 4) | |||||
self.assertEqual(communication.feedback, "Great Work !!") | |||||
# test if link expired after feedback submission | |||||
self.assertRaises(Exception, accept, key=feedback_request, sender="test-feedback@example.com", | |||||
reference_doctype="ToDo", reference_name=todo.name, feedback="Thank You !!", rating=4) | |||||
frappe.delete_doc("ToDo", todo.name) |
@@ -97,7 +97,8 @@ def get_docinfo(doc=None, doctype=None, name=None): | |||||
'versions': get_versions(doc), | 'versions': get_versions(doc), | ||||
"assignments": get_assignments(doc.doctype, doc.name), | "assignments": get_assignments(doc.doctype, doc.name), | ||||
"permissions": get_doc_permissions(doc), | "permissions": get_doc_permissions(doc), | ||||
"shared": frappe.share.get_users(doc.doctype, doc.name) | |||||
"shared": frappe.share.get_users(doc.doctype, doc.name), | |||||
"rating": get_feedback_rating(doc.doctype, doc.name) | |||||
} | } | ||||
def get_user_permissions(meta): | def get_user_permissions(meta): | ||||
@@ -139,7 +140,6 @@ def _get_communications(doctype, name, start=0, limit=20): | |||||
elif c.communication_type=="Comment" and c.comment_type=="Comment": | elif c.communication_type=="Comment" and c.comment_type=="Comment": | ||||
c.content = frappe.utils.markdown(c.content) | c.content = frappe.utils.markdown(c.content) | ||||
return communications | return communications | ||||
def get_communication_data(doctype, name, start=0, limit=20, after=None, fields=None, | def get_communication_data(doctype, name, start=0, limit=20, after=None, fields=None, | ||||
@@ -152,9 +152,9 @@ def get_communication_data(doctype, name, start=0, limit=20, after=None, fields= | |||||
timeline_doctype, timeline_name, | timeline_doctype, timeline_name, | ||||
reference_doctype, reference_name, | reference_doctype, reference_name, | ||||
link_doctype, link_name, | link_doctype, link_name, | ||||
"Communication" as doctype''' | |||||
rating, feedback, "Communication" as doctype''' | |||||
conditions = '''communication_type in ("Communication", "Comment") | |||||
conditions = '''communication_type in ("Communication", "Comment", "Feedback") | |||||
and ( | and ( | ||||
(reference_doctype=%(doctype)s and reference_name=%(name)s) | (reference_doctype=%(doctype)s and reference_name=%(name)s) | ||||
or ( | or ( | ||||
@@ -206,3 +206,17 @@ def get_badge_info(doctypes, filters): | |||||
def run_onload(doc): | def run_onload(doc): | ||||
doc.set("__onload", frappe._dict()) | doc.set("__onload", frappe._dict()) | ||||
doc.run_method("onload") | doc.run_method("onload") | ||||
def get_feedback_rating(doctype, docname): | |||||
""" get and return the latest feedback rating if available """ | |||||
rating= frappe.get_all("Communication", filters={ | |||||
"reference_doctype": doctype, | |||||
"reference_name": docname, | |||||
"communication_type": "Feedback" | |||||
}, fields=["rating"], order_by="creation desc", as_list=True) | |||||
if not rating: | |||||
return 0 | |||||
else: | |||||
return rating[0][0] |
@@ -210,5 +210,4 @@ def evaluate_alert(doc, alert, event): | |||||
frappe.throw(_("Error while evaluating Email Alert {0}. Please fix your template.").format(alert)) | frappe.throw(_("Error while evaluating Email Alert {0}. Please fix your template.").format(alert)) | ||||
def get_context(doc): | def get_context(doc): | ||||
return {"doc": doc, "nowdate": nowdate} | |||||
return {"doc": doc, "nowdate": nowdate} |
@@ -113,7 +113,8 @@ doc_events = { | |||||
"on_cancel": [ | "on_cancel": [ | ||||
"frappe.desk.notifications.clear_doctype_notifications", | "frappe.desk.notifications.clear_doctype_notifications", | ||||
], | ], | ||||
"on_trash": "frappe.desk.notifications.clear_doctype_notifications" | |||||
"on_trash": "frappe.desk.notifications.clear_doctype_notifications", | |||||
"on_change": "frappe.core.doctype.feedback_trigger.feedback_trigger.trigger_feedback_alert" | |||||
}, | }, | ||||
"Email Group Member": { | "Email Group Member": { | ||||
"validate": "frappe.email.doctype.email_group.email_group.restrict_email_group" | "validate": "frappe.email.doctype.email_group.email_group.restrict_email_group" | ||||
@@ -145,7 +146,8 @@ scheduler_events = { | |||||
"frappe.utils.scheduler.restrict_scheduler_events_if_dormant", | "frappe.utils.scheduler.restrict_scheduler_events_if_dormant", | ||||
"frappe.limits.update_space_usage", | "frappe.limits.update_space_usage", | ||||
"frappe.email.doctype.auto_email_report.auto_email_report.send_daily", | "frappe.email.doctype.auto_email_report.auto_email_report.send_daily", | ||||
"frappe.desk.page.backups.backups.delete_downloadable_backups" | |||||
"frappe.desk.page.backups.backups.delete_downloadable_backups", | |||||
"frappe.core.doctype.feedback_request.feedback_request.delete_feedback_request" | |||||
], | ], | ||||
"monthly": [ | "monthly": [ | ||||
"frappe.email.doctype.auto_email_report.auto_email_report.send_monthly" | "frappe.email.doctype.auto_email_report.auto_email_report.send_monthly" | ||||
@@ -19,7 +19,8 @@ | |||||
"public/js/frappe/class.js", | "public/js/frappe/class.js", | ||||
"public/js/lib/microtemplate.js", | "public/js/lib/microtemplate.js", | ||||
"public/js/frappe/query_string.js", | "public/js/frappe/query_string.js", | ||||
"website/js/website.js" | |||||
"website/js/website.js", | |||||
"public/js/frappe/misc/rating_icons.html" | |||||
], | ], | ||||
"js/editor.min.js": [ | "js/editor.min.js": [ | ||||
"public/js/lib/jquery/jquery.hotkeys.js", | "public/js/lib/jquery/jquery.hotkeys.js", | ||||
@@ -150,7 +151,8 @@ | |||||
"public/js/frappe/desk.js", | "public/js/frappe/desk.js", | ||||
"public/js/frappe/query_string.js", | "public/js/frappe/query_string.js", | ||||
"public/js/frappe/ui/charts.js" | |||||
"public/js/frappe/ui/charts.js", | |||||
"public/js/frappe/misc/rating_icons.html" | |||||
], | ], | ||||
"js/d3.min.js": [ | "js/d3.min.js": [ | ||||
"public/js/lib/d3.min.js", | "public/js/lib/d3.min.js", | ||||
@@ -98,7 +98,7 @@ frappe.ui.form.Timeline = Class.extend({ | |||||
$.each(communications.sort(function(a, b) { return a.creation > b.creation ? -1 : 1 }), | $.each(communications.sort(function(a, b) { return a.creation > b.creation ? -1 : 1 }), | ||||
function(i, c) { | function(i, c) { | ||||
if(c.content) { | |||||
if(c.content || c.feedback) { | |||||
c.frm = me.frm; | c.frm = me.frm; | ||||
me.render_timeline_item(c); | me.render_timeline_item(c); | ||||
} | } | ||||
@@ -267,6 +267,11 @@ frappe.ui.form.Timeline = Class.extend({ | |||||
c.original_content = c.content; | c.original_content = c.content; | ||||
c.content = frappe.utils.toggle_blockquote(c.content); | c.content = frappe.utils.toggle_blockquote(c.content); | ||||
} else if (c.communication_type==="Feedback") { | |||||
c.content = frappe.utils.strip_original_content(c.feedback); | |||||
c.original_content = c.feedback; | |||||
c.content = frappe.utils.toggle_blockquote(c.feedback); | |||||
} | } | ||||
if(!frappe.utils.is_html(c.content)) { | if(!frappe.utils.is_html(c.content)) { | ||||
@@ -307,57 +312,64 @@ frappe.ui.form.Timeline = Class.extend({ | |||||
}, | }, | ||||
is_communication_or_comment: function(c) { | is_communication_or_comment: function(c) { | ||||
return c.communication_type==="Communication" || (c.communication_type==="Comment" && (c.comment_type==="Comment"||c.comment_type==="Relinked")); | |||||
return c.communication_type==="Communication" | |||||
|| c.communication_type==="Feedback" | |||||
|| (c.communication_type==="Comment" && (c.comment_type==="Comment"||c.comment_type==="Relinked")); | |||||
}, | }, | ||||
set_icon_and_color: function(c) { | set_icon_and_color: function(c) { | ||||
c.icon = { | |||||
"Email": "octicon octicon-mail", | |||||
"Chat": "octicon octicon-comment-discussion", | |||||
"Phone": "octicon octicon-device-mobile", | |||||
"SMS": "octicon octicon-comment", | |||||
"Created": "octicon octicon-plus", | |||||
"Submitted": "octicon octicon-lock", | |||||
"Cancelled": "octicon octicon-x", | |||||
"Assigned": "octicon octicon-person", | |||||
"Assignment Completed": "octicon octicon-check", | |||||
"Comment": "octicon octicon-comment-discussion", | |||||
"Workflow": "octicon octicon-git-branch", | |||||
"Label": "octicon octicon-tag", | |||||
"Attachment": "octicon octicon-cloud-upload", | |||||
"Attachment Removed": "octicon octicon-trashcan", | |||||
"Shared": "octicon octicon-eye", | |||||
"Unshared": "octicon octicon-circle-slash", | |||||
"Like": "octicon octicon-heart", | |||||
"Edit": "octicon octicon-pencil", | |||||
"Relinked": "octicon octicon-check" | |||||
}[c.comment_type || c.communication_medium] | |||||
c.color = { | |||||
"Email": "#3498db", | |||||
"Chat": "#3498db", | |||||
"Phone": "#3498db", | |||||
"SMS": "#3498db", | |||||
"Created": "#1abc9c", | |||||
"Submitted": "#1abc9c", | |||||
"Cancelled": "#c0392b", | |||||
"Assigned": "#f39c12", | |||||
"Assignment Completed": "#16a085", | |||||
"Comment": "#f39c12", | |||||
"Workflow": "#2c3e50", | |||||
"Label": "#2c3e50", | |||||
"Attachment": "#7f8c8d", | |||||
"Attachment Removed": "#eee", | |||||
"Relinked": "#16a085" | |||||
}[c.comment_type || c.communication_medium]; | |||||
c.icon_fg = { | |||||
"Attachment Removed": "#333", | |||||
}[c.comment_type || c.communication_medium] | |||||
if(c.communication_type == "Feedback"){ | |||||
c.icon = "octicon octicon-comment-discussion" | |||||
c.rating_icons = frappe.render_template("rating_icons", {rating: c.rating, show_label: true}) | |||||
c.color = "#f39c12" | |||||
} else { | |||||
c.icon = { | |||||
"Email": "octicon octicon-mail", | |||||
"Chat": "octicon octicon-comment-discussion", | |||||
"Phone": "octicon octicon-device-mobile", | |||||
"SMS": "octicon octicon-comment", | |||||
"Created": "octicon octicon-plus", | |||||
"Submitted": "octicon octicon-lock", | |||||
"Cancelled": "octicon octicon-x", | |||||
"Assigned": "octicon octicon-person", | |||||
"Assignment Completed": "octicon octicon-check", | |||||
"Comment": "octicon octicon-comment-discussion", | |||||
"Workflow": "octicon octicon-git-branch", | |||||
"Label": "octicon octicon-tag", | |||||
"Attachment": "octicon octicon-cloud-upload", | |||||
"Attachment Removed": "octicon octicon-trashcan", | |||||
"Shared": "octicon octicon-eye", | |||||
"Unshared": "octicon octicon-circle-slash", | |||||
"Like": "octicon octicon-heart", | |||||
"Edit": "octicon octicon-pencil", | |||||
"Relinked": "octicon octicon-check" | |||||
}[c.comment_type || c.communication_medium] | |||||
c.color = { | |||||
"Email": "#3498db", | |||||
"Chat": "#3498db", | |||||
"Phone": "#3498db", | |||||
"SMS": "#3498db", | |||||
"Created": "#1abc9c", | |||||
"Submitted": "#1abc9c", | |||||
"Cancelled": "#c0392b", | |||||
"Assigned": "#f39c12", | |||||
"Assignment Completed": "#16a085", | |||||
"Comment": "#f39c12", | |||||
"Workflow": "#2c3e50", | |||||
"Label": "#2c3e50", | |||||
"Attachment": "#7f8c8d", | |||||
"Attachment Removed": "#eee", | |||||
"Relinked": "#16a085" | |||||
}[c.comment_type || c.communication_medium]; | |||||
c.icon_fg = { | |||||
"Attachment Removed": "#333", | |||||
}[c.comment_type || c.communication_medium] | |||||
} | |||||
if(!c.icon_fg) | if(!c.icon_fg) | ||||
c.icon_fg = "#fff"; | c.icon_fg = "#fff"; | ||||
}, | }, | ||||
get_communications: function(with_versions) { | get_communications: function(with_versions) { | ||||
var docinfo = this.frm.get_docinfo(), | var docinfo = this.frm.get_docinfo(), | ||||
@@ -23,6 +23,7 @@ | |||||
</span> | </span> | ||||
</div> | </div> | ||||
{% if(data.communication_type==="Communication" | {% if(data.communication_type==="Communication" | ||||
|| data.communication_type==="Feedback" | |||||
|| (data.communication_type==="Comment" | || (data.communication_type==="Comment" | ||||
&& data.comment_type==="Comment")) { %} | && data.comment_type==="Comment")) { %} | ||||
<div class="comment-header small" style="cursor: pointer;"> | <div class="comment-header small" style="cursor: pointer;"> | ||||
@@ -40,11 +41,11 @@ | |||||
</span> | </span> | ||||
<span class="text-muted" style="font-weight: normal;"> | <span class="text-muted" style="font-weight: normal;"> | ||||
– {%= data.comment_on %}</span> | – {%= data.comment_on %}</span> | ||||
{% if(data.communication_type==="Communication") { %} | |||||
{% if(inList(["Communication", "Feedback"], data.communication_type)) { %} | |||||
{% if (frappe.model.can_read(\'Communication\')) { %} | {% if (frappe.model.can_read(\'Communication\')) { %} | ||||
<a href="#Form/{%= data.doctype %}/{%= data.name %}" | <a href="#Form/{%= data.doctype %}/{%= data.name %}" | ||||
class="text-muted"> | class="text-muted"> | ||||
{% } %} | |||||
{% } %} | |||||
{% if (data.delivery_status) { | {% if (data.delivery_status) { | ||||
if (in_list(["Sent", "Opened", "Clicked"], data.delivery_status)) { | if (in_list(["Sent", "Opened", "Clicked"], data.delivery_status)) { | ||||
@@ -68,15 +69,15 @@ | |||||
{% } %} | {% } %} | ||||
{% } %} | {% } %} | ||||
{% if (frappe.model.can_read(\'Communication\')) { %} | |||||
</a> | |||||
{% } %} | |||||
{% if (frappe.model.can_read(\'Communication\')) { %} | |||||
</a> | |||||
{% } %} | |||||
{% if (data.communication_medium === "Email") { %} | |||||
<a class="text-muted reply-link pull-right timeline-content-show" | |||||
data-name="{%= data.name %}">{%= __("Reply") %}</a> | |||||
{% if (data.communication_medium === "Email") { %} | |||||
<a class="text-muted reply-link pull-right timeline-content-show" | |||||
data-name="{%= data.name %}">{%= __("Reply") %}</a> | |||||
{% } %} | |||||
{% } %} | {% } %} | ||||
{% } %} | |||||
<span class="comment-likes" | <span class="comment-likes" | ||||
data-liked-by=\'{{ JSON.stringify(data._liked_by) }}\'> | data-liked-by=\'{{ JSON.stringify(data._liked_by) }}\'> | ||||
<i class="octicon octicon-heart like-action | <i class="octicon octicon-heart like-action | ||||
@@ -97,25 +98,30 @@ | |||||
<hr> | <hr> | ||||
{% endif %} | {% endif %} | ||||
{% if data.communication_type == "Feedback" && data.rating_icons %} | |||||
<p class="text-muted small">{{ data.rating_icons }}</p> | |||||
<hr> | |||||
{% endif %} | |||||
{%= data.content_html %} | {%= data.content_html %} | ||||
</div> | </div> | ||||
{% if(data.attachments && data.attachments.length) { %} | {% if(data.attachments && data.attachments.length) { %} | ||||
<div style="margin: 10px 0px"> | |||||
{% $.each(data.attachments, function(i, a) { %} | |||||
<div class="ellipsis"> | |||||
<a href="{%= encodeURI(a.file_url).replace(/#/g, \'%23\') %}" | |||||
class="text-muted small" target="_blank"> | |||||
<i class="fa fa-paperclip"></i> | |||||
{%= a.file_url.split("/").slice(-1)[0] %} | |||||
{% if (a.is_private) { %} | |||||
<i class="fa fa-lock text-warning"></i> | |||||
{% } %} | |||||
</a> | |||||
<div style="margin: 10px 0px"> | |||||
{% $.each(data.attachments, function(i, a) { %} | |||||
<div class="ellipsis"> | |||||
<a href="{%= encodeURI(a.file_url).replace(/#/g, \'%23\') %}" | |||||
class="text-muted small" target="_blank"> | |||||
<i class="fa fa-paperclip"></i> | |||||
{%= a.file_url.split("/").slice(-1)[0] %} | |||||
{% if (a.is_private) { %} | |||||
<i class="fa fa-lock text-warning"></i> | |||||
{% } %} | |||||
</a> | |||||
</div> | |||||
{% }); %} | |||||
</div> | </div> | ||||
{% }); %} | |||||
{% } %} | |||||
</div> | </div> | ||||
{% } %} | |||||
</div> | |||||
{% } else if(in_list(["Assignment Completed", "Assigned", "Shared", | {% } else if(in_list(["Assignment Completed", "Assigned", "Shared", | ||||
"Unshared"], data.comment_type)) { %} | "Unshared"], data.comment_type)) { %} | ||||
@@ -855,6 +855,7 @@ frappe.ui.form.GridRow = Class.extend({ | |||||
column.static_area.toggle(false); | column.static_area.toggle(false); | ||||
column.field_area.toggle(true); | column.field_area.toggle(true); | ||||
}); | }); | ||||
frappe.ui.form.editable_row = this; | frappe.ui.form.editable_row = this; | ||||
return false; | return false; | ||||
} else { | } else { | ||||
@@ -899,7 +900,13 @@ frappe.ui.form.GridRow = Class.extend({ | |||||
field.$input | field.$input | ||||
.attr('data-col-idx', column.column_index) | .attr('data-col-idx', column.column_index) | ||||
.attr('placeholder', __(df.label)); | .attr('placeholder', __(df.label)); | ||||
// flag list input | |||||
if (this.columns_list && this.columns_list.slice(-1)[0]===column) { | |||||
field.$input.attr('data-last-input', 1); | |||||
} | |||||
} | } | ||||
this.set_arrow_keys(field); | this.set_arrow_keys(field); | ||||
column.field = field; | column.field = field; | ||||
this.on_grid_fields_dict[df.fieldname] = field; | this.on_grid_fields_dict[df.fieldname] = field; | ||||
@@ -915,14 +922,26 @@ frappe.ui.form.GridRow = Class.extend({ | |||||
var fieldname = $(this).attr('data-fieldname'); | var fieldname = $(this).attr('data-fieldname'); | ||||
var fieldtype = $(this).attr('data-fieldtype'); | var fieldtype = $(this).attr('data-fieldtype'); | ||||
// TAB | |||||
if(in_list(['Text', 'Small Text'], fieldtype)) { | |||||
return; | |||||
var move_up_down = function(base) { | |||||
if(in_list(['Text', 'Small Text'], fieldtype)) { | |||||
return; | |||||
} | |||||
base.toggle_editable_row(); | |||||
setTimeout(function() { | |||||
var input = base.columns[fieldname].field.$input; | |||||
if(input) { | |||||
input.focus(); | |||||
} | |||||
}, 400) | |||||
} | } | ||||
// TAB | |||||
if(e.which==TAB) { | if(e.which==TAB) { | ||||
// last column | // last column | ||||
if(me.grid.wrapper.find('input:enabled:last').get(0)===this) { | |||||
if($(this).attr('data-last-input') || | |||||
me.grid.wrapper.find(':input:enabled:last').get(0)===this) { | |||||
setTimeout(function() { | setTimeout(function() { | ||||
if(me.doc.idx === values.length) { | if(me.doc.idx === values.length) { | ||||
// last row | // last row | ||||
@@ -938,24 +957,12 @@ frappe.ui.form.GridRow = Class.extend({ | |||||
} else if(e.which==UP_ARROW) { | } else if(e.which==UP_ARROW) { | ||||
if(me.doc.idx > 1) { | if(me.doc.idx > 1) { | ||||
var prev = me.grid.grid_rows[me.doc.idx-2]; | var prev = me.grid.grid_rows[me.doc.idx-2]; | ||||
prev.toggle_editable_row(); | |||||
setTimeout(function() { | |||||
var input = prev.columns[fieldname].field.$input; | |||||
if(input) { | |||||
input.focus(); | |||||
} | |||||
}, 400) | |||||
move_up_down(prev); | |||||
} | } | ||||
} else if(e.which==DOWN_ARROW) { | } else if(e.which==DOWN_ARROW) { | ||||
if(me.doc.idx < values.length) { | if(me.doc.idx < values.length) { | ||||
var next = me.grid.grid_rows[me.doc.idx]; | var next = me.grid.grid_rows[me.doc.idx]; | ||||
next.toggle_editable_row(); | |||||
setTimeout(function() { | |||||
var input = next.columns[fieldname].field.$input; | |||||
if(input) { | |||||
input.focus(); | |||||
} | |||||
}, 400) | |||||
move_up_down(next); | |||||
} | } | ||||
} | } | ||||
@@ -11,6 +11,7 @@ frappe.ui.form.Sidebar = Class.extend({ | |||||
.html(sidebar_content) | .html(sidebar_content) | ||||
.appendTo(this.page.sidebar.empty()); | .appendTo(this.page.sidebar.empty()); | ||||
this.ratings = this.sidebar.find(".sidebar-rating"); | |||||
this.comments = this.sidebar.find(".sidebar-comments"); | this.comments = this.sidebar.find(".sidebar-comments"); | ||||
this.user_actions = this.sidebar.find(".user-actions"); | this.user_actions = this.sidebar.find(".user-actions"); | ||||
this.image_section = this.sidebar.find(".sidebar-image-section"); | this.image_section = this.sidebar.find(".sidebar-image-section"); | ||||
@@ -63,6 +64,7 @@ frappe.ui.form.Sidebar = Class.extend({ | |||||
"<br>" + comment_when(this.frm.doc.creation)])); | "<br>" + comment_when(this.frm.doc.creation)])); | ||||
this.refresh_like(); | this.refresh_like(); | ||||
this.setup_ratings(); | |||||
frappe.ui.form.set_user_image(this.frm); | frappe.ui.form.set_user_image(this.frm); | ||||
} | } | ||||
}, | }, | ||||
@@ -146,5 +148,16 @@ frappe.ui.form.Sidebar = Class.extend({ | |||||
}, | }, | ||||
refresh_image: function() { | refresh_image: function() { | ||||
}, | |||||
setup_ratings: function() { | |||||
_ratings = this.frm.get_docinfo().rating || 0; | |||||
if(_ratings) { | |||||
this.ratings.removeClass("hide"); | |||||
rating_icons = frappe.render_template("rating_icons", {rating: _ratings, show_label: false}); | |||||
this.ratings.find(".rating-icons").html(rating_icons); | |||||
} | |||||
} | } | ||||
}); | }); |
@@ -12,8 +12,14 @@ | |||||
<label class="label label-warning" title="{{ __("This feature is brand new and still experimental") }}">{{ __("Experimental Feature") }}</label> | <label class="label label-warning" title="{{ __("This feature is brand new and still experimental") }}">{{ __("Experimental Feature") }}</label> | ||||
</div> | </div> | ||||
{% endif %} | {% endif %} | ||||
<ul class="list-unstyled sidebar-menu user-actions hide"> | |||||
<ul class="list-unstyled sidebar-menu sidebar-rating hide"> | |||||
<li class="divider"></li> | <li class="divider"></li> | ||||
<li style="position: relative;"> | |||||
<a class="strong badge-hover"> | |||||
<span>{%= __("Feedback Rating") %}</span> | |||||
</a> | |||||
</li> | |||||
<li class="rating-icons"></li> | |||||
</ul> | </ul> | ||||
<ul class="list-unstyled sidebar-menu"> | <ul class="list-unstyled sidebar-menu"> | ||||
<li class="divider"></li> | <li class="divider"></li> | ||||
@@ -0,0 +1,6 @@ | |||||
{% if show_label %} | |||||
{{ __("Rating: ") }} | |||||
{% endif %} | |||||
{% for(var i=1, l=6; i<l; i++) { %} | |||||
<i class="fa fa-fw {{ i<=rating? "fa-star": "fa-star-o" }} star-icon" data-idx=1></i> | |||||
{% } %} |
@@ -0,0 +1,8 @@ | |||||
<div> | |||||
{% if label && data.show_label %} | |||||
{{ __("{0}: ", label) }} | |||||
{% endif %} | |||||
{% for(var i=1, l=6; i<l; i++) { %} | |||||
<i class="fa fa-fw {{ i<=rating? "fa-star": "fa-star-o" }} star-icon" data-idx=1></i> | |||||
{% } %} | |||||
</div> |
@@ -31,7 +31,7 @@ def validate_template(html): | |||||
try: | try: | ||||
jenv.from_string(html) | jenv.from_string(html) | ||||
except TemplateSyntaxError, e: | except TemplateSyntaxError, e: | ||||
frappe.msgprint('Line {}: {}'.format(e.lineno, e.message)) | |||||
frappe.msgprint('Line {}: {}'.format(e.lineno, e.message)) | |||||
frappe.throw(frappe._("Syntax error in template")) | frappe.throw(frappe._("Syntax error in template")) | ||||
def render_template(template, context, is_path=None): | def render_template(template, context, is_path=None): | ||||
@@ -0,0 +1,94 @@ | |||||
{% extends "templates/web.html" %} | |||||
{% block title %}{{ _("Feedback") }}{% endblock %} | |||||
{% block page_content %} | |||||
<div class="feedback"> | |||||
<p class='lead' id="feedback-msg"></p> | |||||
<div> | |||||
{{ _("Your rating: ") }} | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=1></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=2></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=3></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=4></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=5></i> | |||||
</div> | |||||
<div style='max-width: 500px;'> | |||||
<p>{{ _("Detailed feedback") }}</p> | |||||
<textarea class='form-control feedback-text' style='min-height: 300px;'></textarea> | |||||
</div> | |||||
<p><button class='btn btn-primary btn-sm btn-submit'>{{ _("Submit") }}</button></p> | |||||
</div> | |||||
<div class="feedback-result" style="display: none"> | |||||
<div class='page-card'> | |||||
<div class='page-card-head'> | |||||
<span class='indicator darkgrey'>{{_("Thank You !!")}}</span> | |||||
</div> | |||||
<p id="feedback-result"></p> | |||||
<div><a href='/' class='btn btn-primary btn-sm'>{{ _("Home") }}</a></div> | |||||
</div> | |||||
</div> | |||||
<script> | |||||
window.feedback = { | |||||
init: function() { | |||||
var me = this; | |||||
this.key = get_url_arg("key"); | |||||
this.reference_name = get_url_arg("reference_name"); | |||||
this.reference_doctype = get_url_arg("reference_doctype"); | |||||
this.sender = get_url_arg("email") | |||||
me.bind_events(); | |||||
$("#feedback-msg").empty().html(__("Please share your feedback for {0}", [me.reference_name])); | |||||
}, | |||||
bind_events: function() { | |||||
// bind ratings on hover | |||||
var me = this; | |||||
$('.star-icon').hover(function() { | |||||
var idx = $(this).attr('data-idx'); | |||||
$('.star-icon.fa-star').removeClass('fa-star').addClass('fa-star-o'); | |||||
for(var i=0; i<parseInt(idx); i++) { | |||||
$($('.star-icon').get(i)).removeClass('fa-star-o').addClass('fa-star'); | |||||
} | |||||
}); | |||||
// bind submit button | |||||
$('.btn-submit').on('click', function() { | |||||
if(!$('.star-icon.fa-star').length) { | |||||
frappe.msgprint(__("Please give a rating.")); | |||||
return; | |||||
} | |||||
frappe.call({ | |||||
method: 'frappe.www.feedback.accept', | |||||
args: { | |||||
key: me.key, | |||||
sender: me.sender, | |||||
reference_name: me.reference_name, | |||||
reference_doctype: me.reference_doctype, | |||||
feedback: $('.feedback-text').val(), | |||||
rating: $('.star-icon.fa-star').length | |||||
}, | |||||
callback: function(r) { | |||||
if(r.message) { | |||||
$(".feedback, .feedback-result").toggle(); | |||||
$("#feedback-result").empty().html(__("Your Feedback for document {0} is saved successfully", | |||||
[me.reference_name])); | |||||
} | |||||
} | |||||
}) | |||||
}); | |||||
} | |||||
}; | |||||
frappe.ready(function() { | |||||
feedback.init(); | |||||
}) | |||||
</script> | |||||
{% endblock %} |
@@ -0,0 +1,28 @@ | |||||
import frappe | |||||
from frappe.core.doctype.feedback_request.feedback_request import is_valid_feedback_request | |||||
@frappe.whitelist(allow_guest=True) | |||||
def accept(key, sender, reference_doctype, reference_name, feedback, rating): | |||||
""" save the feedback in communication """ | |||||
if not reference_doctype and not reference_name: | |||||
frappe.throw("Invalid Reference Doctype, Reference Name") | |||||
if not is_valid_feedback_request(key): | |||||
frappe.throw("Link is Expired") | |||||
frappe.get_doc({ | |||||
"rating": rating, | |||||
"sender": sender, | |||||
"status": "Closed", | |||||
"feedback": feedback, | |||||
"feedback_request": key, | |||||
"doctype": "Communication", | |||||
"sent_or_received": "Received", | |||||
"communication_type": "Feedback", | |||||
"reference_name": reference_name, | |||||
"reference_doctype": reference_doctype, | |||||
"subject": "Feedback for {0} {1}".format(reference_doctype, reference_name), | |||||
}).insert(ignore_permissions=True) | |||||
frappe.db.set_value("Feedback Request", key, "is_feedback_submitted", 1) | |||||
return True |