feat: Feedback feature for Blog Post
This commit is contained in:
parent
878ac658b2
commit
1f83d3d54f
11 changed files with 362 additions and 1 deletions
0
frappe/core/doctype/feedback/__init__.py
Normal file
0
frappe/core/doctype/feedback/__init__.py
Normal file
8
frappe/core/doctype/feedback/feedback.js
Normal file
8
frappe/core/doctype/feedback/feedback.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2021, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Feedback', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
86
frappe/core/doctype/feedback/feedback.json
Normal file
86
frappe/core/doctype/feedback/feedback.json
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2021-06-03 19:02:55.328423",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"reference_doctype",
|
||||
"reference_name",
|
||||
"column_break_3",
|
||||
"email",
|
||||
"rating",
|
||||
"section_break_6",
|
||||
"feedback"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "email",
|
||||
"fieldtype": "Data",
|
||||
"label": "Email",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"in_list_view": 1,
|
||||
"label": "Reference Document Type",
|
||||
"options": "\nBlog Post"
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Reference Name",
|
||||
"options": "reference_doctype",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-14 15:11:26.005805",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Feedback",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "reference_name",
|
||||
"track_changes": 1
|
||||
}
|
||||
8
frappe/core/doctype/feedback/feedback.py
Normal file
8
frappe/core/doctype/feedback/feedback.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2021, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class Feedback(Document):
|
||||
pass
|
||||
8
frappe/core/doctype/feedback/test_feedback.py
Normal file
8
frappe/core/doctype/feedback/test_feedback.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2021, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestFeedback(unittest.TestCase):
|
||||
pass
|
||||
0
frappe/templates/includes/feedback/__init__.py
Normal file
0
frappe/templates/includes/feedback/__init__.py
Normal file
173
frappe/templates/includes/feedback/feedback.html
Normal file
173
frappe/templates/includes/feedback/feedback.html
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<div class="add-feedback-section">
|
||||
<div class="feedback-form-wrapper">
|
||||
<a class="give-feedback btn btn-light btn-sm">{{ _("How would you rate the blog?") }}</a>
|
||||
<div style="display: none;" id="feedback-form">
|
||||
<p>{{ _("How would you rate the blog?") }}</p>
|
||||
<div class="alert" style="display:none;"></div>
|
||||
<form>
|
||||
<fieldset>
|
||||
<div class="row" style="margin-bottom: 15px;">
|
||||
<div class="col-sm-6">
|
||||
<input class="form-control feedback_email" name="feedback_email" placeholder="{{ _("Your Email Address") }}" type="email">
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="rating">
|
||||
<svg class="icon icon-md" data-rating="1">
|
||||
<use href="#icon-star"></use>
|
||||
</svg><svg class="icon icon-md" data-rating="2">
|
||||
<use href="#icon-star"></use>
|
||||
</svg><svg class="icon icon-md" data-rating="3">
|
||||
<use href="#icon-star"></use>
|
||||
</svg><svg class="icon icon-md" data-rating="4">
|
||||
<use href="#icon-star"></use>
|
||||
</svg><svg class="icon icon-md" data-rating="5">
|
||||
<use href="#icon-star"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<textarea class="form-control" name="feedback" rows=10 placeholder="{{ _("Feedback") }}"></textarea>
|
||||
</p>
|
||||
<button class="btn btn-sm" id="toggle-feedback" style="margin-top:10px; margin-right:2px;">
|
||||
{{ _("Back") }}
|
||||
</button>
|
||||
<button class="btn btn-primary btn-sm" id="submit-feedback" style="margin-top:10px">
|
||||
{{ _("Submit") }}
|
||||
</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
frappe.ready(() => {
|
||||
let feedback = "{{ user_feedback.feedback or ''}}"
|
||||
let rating = parseInt("{{ user_feedback.rating or 0 }}")
|
||||
|
||||
feedback && $("#submit-feedback").html(__("Update"));
|
||||
|
||||
if (frappe.is_user_logged_in()) {
|
||||
$(".feedback_email").parent().toggleClass("hidden");
|
||||
if (feedback) {
|
||||
$("[name='feedback']").val(feedback);
|
||||
toggle_feedback();
|
||||
set_rating(rating);
|
||||
}
|
||||
}
|
||||
|
||||
$('.give-feedback').click(() => toggle_feedback());
|
||||
|
||||
|
||||
$('.rating').find('svg').hover((ev) => {
|
||||
const el = $(ev.currentTarget);
|
||||
rating = el.data('rating');
|
||||
el.parent().children('svg').each( function(e) {
|
||||
if (e < rating) {
|
||||
$(this).addClass('star-hover');
|
||||
} else {
|
||||
$(this).removeClass('star-hover');
|
||||
}
|
||||
});
|
||||
}, (ev) => {
|
||||
const el = $(ev.currentTarget);
|
||||
el.parent().children('svg').each( function() {
|
||||
$(this).removeClass('star-hover');
|
||||
});
|
||||
});
|
||||
|
||||
$('.rating').find('svg').click((ev) => {
|
||||
const el = $(ev.currentTarget);
|
||||
rating = el.data('rating');
|
||||
el.parent().children('svg').each( function(e) {
|
||||
if (e < rating) {
|
||||
$(this).addClass('star-click');
|
||||
} else {
|
||||
$(this).removeClass('star-click');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('#submit-feedback').click((ev) => {
|
||||
let update = ev.target.innerText !== __("Submit");
|
||||
let args = {
|
||||
reference_doctype: "{{ reference_doctype or doctype }}",
|
||||
reference_name: "{{ reference_name or name }}",
|
||||
rating: rating,
|
||||
feedback: $("[name='feedback']").val(),
|
||||
feedback_email: $("[name='feedback_email']").val() || frappe.user_id
|
||||
}
|
||||
|
||||
if (args.rating == 0) {
|
||||
frappe.msgprint("{{ _("Rating is required!") }}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args.feedback || !args.feedback.trim()) {
|
||||
frappe.msgprint("{{ _("Please add a valid feedback.") }}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.feedback_email!=='Administrator' && !validate_email(args.feedback_email)) {
|
||||
frappe.msgprint("{{ _("Please enter a valid email address.") }}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!update) {
|
||||
frappe.call({
|
||||
method: "frappe.templates.includes.feedback.feedback.add_feedback",
|
||||
args: args,
|
||||
callback: function(r) {
|
||||
toggle_feedback();
|
||||
if (!frappe.is_user_logged_in()) {
|
||||
$("[name='feedback']").val('');
|
||||
set_rating(0);
|
||||
} else {
|
||||
feedback = $("[name='feedback']").val();
|
||||
$("#submit-feedback").html(__("Update"));
|
||||
}
|
||||
frappe.msgprint({message:__("Thank you for your valuable feedback!"), indicator:'green'});
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (feedback == $("[name='feedback']").val()) {
|
||||
frappe.msgprint({message:__("Please update rating or feedback before saving."), indicator:'red'});
|
||||
return false;
|
||||
}
|
||||
frappe.call({
|
||||
method: "frappe.templates.includes.feedback.feedback.update_feedback",
|
||||
args: args,
|
||||
callback: function(r) {
|
||||
toggle_feedback();
|
||||
feedback = $("[name='feedback']").val();
|
||||
frappe.msgprint({message:__("Feedback updated successfully!"), indicator:'green'});
|
||||
}
|
||||
})
|
||||
}
|
||||
return false;
|
||||
})
|
||||
|
||||
$('#toggle-feedback').click(() => {
|
||||
toggle_feedback();
|
||||
return false;
|
||||
})
|
||||
|
||||
function set_rating(rating) {
|
||||
let el = $('.rating').find('svg');
|
||||
el.children('svg').prevObject.each( function(e) {
|
||||
if (e < rating) {
|
||||
$(this).addClass('star-click');
|
||||
} else {
|
||||
$(this).removeClass('star-click');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggle_feedback() {
|
||||
$(".give-feedback").toggle();
|
||||
$("#feedback-form").toggle();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
53
frappe/templates/includes/feedback/feedback.py
Normal file
53
frappe/templates/includes/feedback/feedback.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
|
||||
from frappe import _
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def add_feedback(reference_doctype, reference_name, rating, feedback, feedback_email):
|
||||
doc = frappe.new_doc('Feedback')
|
||||
doc.reference_doctype = reference_doctype
|
||||
doc.reference_name = reference_name
|
||||
doc.rating = rating
|
||||
doc.feedback = feedback
|
||||
doc.email = feedback_email
|
||||
doc.save(ignore_permissions=True)
|
||||
|
||||
subject = _('New Feedback on {0}: {1}').format(reference_doctype, reference_name)
|
||||
send_mail(doc, subject)
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_feedback(reference_doctype, reference_name, rating, feedback, feedback_email):
|
||||
filters = {
|
||||
"email": feedback_email,
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name
|
||||
}
|
||||
d = frappe.get_all('Feedback', filters=filters, limit=1)
|
||||
doc = frappe.get_doc('Feedback', d[0].name)
|
||||
doc.rating = rating
|
||||
doc.feedback = feedback
|
||||
doc.save(ignore_permissions=True)
|
||||
|
||||
subject = _('Feedback updated on {0}: {1}').format(reference_doctype, reference_name)
|
||||
send_mail(doc, subject)
|
||||
|
||||
def send_mail(feedback, subject):
|
||||
doc = frappe.get_doc(feedback.reference_doctype, feedback.reference_name)
|
||||
|
||||
message = ("<p>{0} ({1})</p>".format(feedback.feedback, feedback.rating)
|
||||
+ "<p><a href='{0}/app/marketing-asset-feedback/{1}' style='font-size: 80%'>{2}</a></p>".format(frappe.utils.get_request_site_address(),
|
||||
feedback.name,
|
||||
_("View Feedback")))
|
||||
|
||||
# notify creator
|
||||
frappe.sendmail(
|
||||
recipients=frappe.db.get_value('User', doc.owner, 'email') or doc.owner,
|
||||
subject=subject,
|
||||
message=message,
|
||||
reference_doctype=doc.doctype,
|
||||
reference_name=doc.name
|
||||
)
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
"featured",
|
||||
"hide_cta",
|
||||
"disable_comments",
|
||||
"disable_feedback",
|
||||
"section_break_5",
|
||||
"blog_intro",
|
||||
"content_type",
|
||||
|
|
@ -191,6 +192,13 @@
|
|||
"fieldtype": "Data",
|
||||
"label": "Meta Title",
|
||||
"length": 60
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Feedback on this blog post will be disabled if checked.",
|
||||
"fieldname": "disable_feedback",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable Feedback"
|
||||
}
|
||||
],
|
||||
"has_web_view": 1,
|
||||
|
|
@ -200,7 +208,7 @@
|
|||
"is_published_field": "published",
|
||||
"links": [],
|
||||
"max_attachments": 5,
|
||||
"modified": "2020-12-23 14:28:36.311389",
|
||||
"modified": "2021-06-14 13:50:02.109719",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Blog Post",
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ class BlogPost(WebsiteGenerator):
|
|||
context.metatags["image"] = self.meta_image or image or None
|
||||
|
||||
self.load_comments(context)
|
||||
self.load_feedback(context)
|
||||
|
||||
context.category = frappe.db.get_value("Blog Category",
|
||||
context.doc.blog_category, ["title", "route"], as_dict=1)
|
||||
|
|
@ -151,6 +152,17 @@ class BlogPost(WebsiteGenerator):
|
|||
else:
|
||||
context.comment_text = _('{0} comments').format(len(context.comment_list))
|
||||
|
||||
def load_feedback(self, context):
|
||||
feedback = frappe.get_all('Feedback',
|
||||
fields=['email', 'feedback', 'rating'],
|
||||
filters=dict(
|
||||
reference_doctype=self.doctype,
|
||||
reference_name=self.name,
|
||||
email=frappe.session.user
|
||||
)
|
||||
)
|
||||
context.user_feedback = feedback[0] if feedback else ''
|
||||
|
||||
def set_read_time(self):
|
||||
content = self.content or self.content_html or ''
|
||||
if self.content_type == "Markdown":
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@
|
|||
{% include 'templates/includes/comments/comments.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not disable_feedback %}
|
||||
<div class="blog-feedback">
|
||||
{% include 'templates/includes/feedback/feedback.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
<script>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue