Merge branch 'develop' into filter-depends-on-fix

This commit is contained in:
Suraj Shetty 2020-07-24 11:55:32 +05:30 committed by GitHub
commit 227f49110a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 375 additions and 551 deletions

View file

@ -146,6 +146,7 @@ def delete_contact_and_address(doctype, docname):
if len(doc.links)==1:
doc.delete()
@frappe.whitelist()
def filter_dynamic_link_doctypes(doctype, txt, searchfield, start, page_len, filters):
if not txt: txt = ""

View file

@ -230,6 +230,7 @@ def get_company_address(company):
return ret
@frappe.whitelist()
def address_query(doctype, txt, searchfield, start, page_len, filters):
from frappe.desk.reportview import get_match_cond
@ -237,16 +238,17 @@ def address_query(doctype, txt, searchfield, start, page_len, filters):
link_name = filters.pop('link_name')
condition = ""
for fieldname, value in iteritems(filters):
condition += " and {field}={value}".format(
field=fieldname,
value=value
)
meta = frappe.get_meta("Address")
for fieldname, value in iteritems(filters):
if meta.get_field(fieldname) or fieldname in frappe.db.DEFAULT_COLUMNS:
condition += " and {field}={value}".format(
field=fieldname,
value=frappe.db.escape(value))
searchfields = meta.get_search_fields()
if searchfield:
if searchfield and (meta.get_field(searchfield)\
or searchfield in frappe.db.DEFAULT_COLUMNS):
searchfields.append(searchfield)
search_condition = ''
@ -289,4 +291,4 @@ def get_condensed_address(doc):
return ", ".join([doc.get(d) for d in fields if doc.get(d)])
def update_preferred_address(address, field):
frappe.db.set_value('Address', address, field, 0)
frappe.db.set_value('Address', address, field, 0)

View file

@ -182,19 +182,17 @@ def update_contact(doc, method):
contact.flags.ignore_mandatory = True
contact.save(ignore_permissions=True)
@frappe.whitelist()
def contact_query(doctype, txt, searchfield, start, page_len, filters):
from frappe.desk.reportview import get_match_cond
if not frappe.get_meta("Contact").get_field(searchfield)\
or searchfield not in frappe.db.DEFAULT_COLUMNS:
return []
link_doctype = filters.pop('link_doctype')
link_name = filters.pop('link_name')
condition = ""
for fieldname, value in iteritems(filters):
condition += " and {field}={value}".format(
field=fieldname,
value=value
)
return frappe.db.sql("""select
`tabContact`.name, `tabContact`.first_name, `tabContact`.last_name
from
@ -209,9 +207,7 @@ def contact_query(doctype, txt, searchfield, start, page_len, filters):
order by
if(locate(%(_txt)s, `tabContact`.name), locate(%(_txt)s, `tabContact`.name), 99999),
`tabContact`.idx desc, `tabContact`.name
limit %(start)s, %(page_len)s """.format(
mcond=get_match_cond(doctype),
key=searchfield), {
limit %(start)s, %(page_len)s """.format(mcond=get_match_cond(doctype), key=searchfield), {
'txt': '%' + txt + '%',
'_txt': txt.replace("%", ""),
'start': start,

View file

@ -176,7 +176,7 @@ frappe.ui.form.on('Dashboard Chart', {
set_chart_field_options: function(frm) {
let filters = frm.doc.filters_json.length > 2 ? JSON.parse(frm.doc.filters_json) : null;
if (frm.doc.dynamic_filters_json.length > 2) {
if (frm.doc.dynamic_filters_json && frm.doc.dynamic_filters_json.length > 2) {
filters = frappe.dashboard_utils.get_all_filters(frm.doc);
}
frappe.xcall(

View file

@ -215,7 +215,7 @@ frappe.ui.form.on('Number Card', {
set_report_field_options: function(frm) {
let filters = frm.doc.filters_json.length > 2 ? JSON.parse(frm.doc.filters_json) : null;
if (frm.doc.dynamic_filters_json.length > 2) {
if (frm.doc.dynamic_filters_json && frm.doc.dynamic_filters_json.length > 2) {
filters = frappe.dashboard_utils.get_all_filters(frm.doc);
}
frappe.xcall(

View file

@ -95,6 +95,11 @@ frappe.ui.form.on("Email Account", {
enable_incoming: function(frm) {
frm.doc.no_remaining = null; //perform full sync
//frm.set_df_property("append_to", "reqd", frm.doc.enable_incoming);
frm.trigger("warn_autoreply_on_incoming");
},
enable_auto_reply: function(frm) {
frm.trigger("warn_autoreply_on_incoming");
},
notify_if_unreplied: function(frm) {
@ -184,7 +189,18 @@ frappe.ui.form.on("Email Account", {
read as well as unread message from server. This may also cause the duplication\
of Communication (emails).");
frappe.confirm(msg, null, function() {
frm.set_value("email_sync_option", "ALL");
frm.set_value("email_sync_option", "UNSEEN");
});
}
},
warn_autoreply_on_incoming: function(frm) {
if (frm.doc.enable_incoming && frm.doc.enable_auto_reply && frm.doc.__islocal) {
var msg = __("Enabling auto reply on an incoming email account will send automated replies \
to all the synchronized emails. Do you wish to continue?");
frappe.confirm(msg, null, function() {
frm.set_value("enable_auto_reply", 0);
frappe.show_alert({message: __("Disabled Auto Reply"), indicator: "blue"});
});
}
}

View file

@ -7,6 +7,12 @@ body {
p {
margin: 1em 0 !important;
}
.ql-editor {
white-space: normal;
}
.ql-editor p {
margin: 0 !important;
}
hr {
border-top: 1px solid #d1d8dd;
}

View file

@ -69,7 +69,6 @@ Quill.register(CustomColor, true);
frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({
make_wrapper() {
this._super();
this.$wrapper.find(".like-disabled-input").addClass('ql-editor');
},
make_input() {
@ -201,6 +200,10 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({
let value = this.quill ? this.quill.root.innerHTML : '';
// hack to retain space sequence.
value = value.replace(/(\s)(\s)/g, '  ');
if (!$(value).find('.ql-editor').length) {
value = `<div class="ql-editor read-mode">${value}</div>`;
}
return value;
},

View file

@ -228,7 +228,12 @@ frappe.form.formatters = {
return frappe.form.formatters.Text(value);
},
TextEditor: function(value) {
return frappe.form.formatters.Text(value);
let formatted_value = frappe.form.formatters.Text(value);
// to use ql-editor styles
if (!$(formatted_value).find('.ql-editor').length) {
formatted_value = `<div class="ql-editor read-mode">${formatted_value}</div>`;
}
return formatted_value;
},
Code: function(value) {
return "<pre>" + (value==null ? "" : $("<div>").text(value).html()) + "</pre>"

View file

@ -392,8 +392,11 @@ export default class GridRow {
// sync get_query
field.get_query = this.grid.get_field(df.fieldname).get_query;
field.df.onchange = function() {
me.grid.grid_rows[this.doc.idx-1].refresh_field(this.df.fieldname);
var field_on_change_function = field.df.onchange;
field.df.onchange = function(e) {
field_on_change_function && field_on_change_function(e);
me.grid.grid_rows[this.doc.idx - 1].refresh_field(this.df.fieldname);
};
field.refresh();
if(field.$input) {

View file

@ -111,7 +111,7 @@
<li class="created-by"></li>
</ul>
{% if(frappe.get_form_sidebar_extension) { %}
{{ frappe.get_form_sidebar_extension() }}
{{ frappe.get_form_sidebar_extension() }}
{% } %}
<ul class="list-unstyled visible-xs visible-sm">

View file

@ -44,26 +44,6 @@ frappe.views.CommunicationComposer = Class.extend({
}
});
$(document).on("upload_complete", function(event, attachment) {
if(me.dialog.display) {
var wrapper = $(me.dialog.fields_dict.select_attachments.wrapper);
// find already checked items
var checked_items = wrapper.find('[data-file-name]:checked').map(function() {
return $(this).attr("data-file-name");
});
// reset attachment list
me.render_attach();
// check latest added
checked_items.push(attachment.name);
$.each(checked_items, function(i, filename) {
wrapper.find('[data-file-name="'+ filename +'"]').prop("checked", true);
});
}
})
this.prepare();
this.dialog.show();
@ -387,77 +367,86 @@ frappe.views.CommunicationComposer = Class.extend({
folder: 'Home/Attachments',
on_success: attachment => {
this.attachments.push(attachment);
this.render_attach();
this.render_attachment_rows(attachment);
}
};
if(this.frm) {
if (this.frm) {
args = {
doctype: this.frm.doctype,
docname: this.frm.docname,
folder: 'Home/Attachments',
on_success: attachment => {
this.frm.attachments.attachment_uploaded(attachment);
this.render_attach();
this.render_attachment_rows(attachment);
}
}
};
}
$("<h6 class='text-muted add-attachment' style='margin-top: 12px; cursor:pointer;'>"
+__("Select Attachments")+"</h6><div class='attach-list'></div>\
<p class='add-more-attachments'>\
<a class='text-muted small'><i class='octicon octicon-plus' style='font-size: 12px'></i> "
+__("Add Attachment")+"</a></p>").appendTo(attach.empty())
$(`
<h6 class='text-muted add-attachment' style='margin-top: 12px; cursor:pointer;'>
${__("Select Attachments")}
</h6>
<div class='attach-list'></div>
<p class='add-more-attachments'>
<a class='text-muted small'>
<i class='octicon octicon-plus' style='font-size: 12px'></i>
${__("Add Attachment")}
</a>
</p>
`).appendTo(attach.empty());
attach
.find(".add-more-attachments a")
.on('click',() => new frappe.ui.FileUploader(args));
this.render_attach();
.on('click', () => new frappe.ui.FileUploader(args));
this.render_attachment_rows();
},
render_attach:function(){
var fields = this.dialog.fields_dict;
var attach = $(fields.select_attachments.wrapper).find(".attach-list").empty();
var files = [];
if (this.attachments && this.attachments.length) {
files = files.concat(this.attachments);
}
if (cur_frm) {
files = files.concat(cur_frm.get_files());
}
render_attachment_rows: function(attachment) {
const select_attachments = this.dialog.fields_dict.select_attachments;
const attachment_rows = $(select_attachments.wrapper).find(".attach-list");
if (attachment) {
attachment_rows.append(this.get_attachment_row(attachment, true));
} else {
let files = [];
if (this.attachments && this.attachments.length) {
files = files.concat(this.attachments);
}
if (this.frm) {
files = files.concat(this.frm.get_files());
}
if(files.length) {
$.each(files, function(i, f) {
if (!f.file_name) return;
f.file_url = frappe.urllib.get_full_url(f.file_url);
$(repl('<p class="checkbox">'
+ '<label><span><input type="checkbox" data-file-name="%(name)s"></input></span>'
+ '<span class="small">%(file_name)s</span>'
+ ' <a href="%(file_url)s" target="_blank" class="text-muted small">'
+ '<i class="fa fa-share" style="vertical-align: middle; margin-left: 3px;"></i>'
+ '</label></p>', f))
.appendTo(attach)
});
}
this.select_attachments();
},
select_attachments:function(){
let me = this;
if(me.dialog.display) {
let wrapper = $(me.dialog.fields_dict.select_attachments.wrapper);
let unchecked_items = wrapper.find('[data-file-name]:not(:checked)').map(function() {
return $(this).attr("data-file-name");
});
$.each(unchecked_items, function(i, filename) {
wrapper.find('[data-file-name="'+ filename +'"]').prop("checked", true);
});
if (files.length) {
$.each(files, (i, f) => {
if (!f.file_name) return;
if (!attachment_rows.find(`[data-file-name="${f.name}"]`).length) {
f.file_url = frappe.urllib.get_full_url(f.file_url);
attachment_rows.append(this.get_attachment_row(f));
}
});
}
}
},
get_attachment_row(attachment, checked) {
return $(`<p class="checkbox">
<label>
<span>
<input
type="checkbox"
data-file-name="${attachment.name}"
${checked ? 'checked': ''}>
</input>
</span>
<span class="small">${attachment.file_name}</span>
<a href="${attachment.file_url}" target="_blank" class="text-muted small">
<i class="fa fa-share" style="vertical-align: middle; margin-left: 3px;"></i>
</label>
</p>`);
},
setup_email: function() {
// email
var me = this;
var fields = this.dialog.fields_dict;
if(this.attach_document_print) {

View file

@ -768,10 +768,12 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
const index = this.fields.findIndex(f => column.field === f[0]);
if (index === -1) return;
const field = this.fields[index];
if (field[0] === 'name' && this.group_by === null) {
if (field[0] === 'name') {
this.refresh();
frappe.throw(__('Cannot remove ID field'));
}
this.fields.splice(index, 1);
this.build_fields();
this.setup_columns();
@ -850,7 +852,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
columns: 2,
options: columns[this.doctype]
.filter(df => {
return !df.hidden;
return !df.hidden && df.fieldname !== 'name';
})
.map(df => ({
label: __(df.label),

View file

@ -10,6 +10,13 @@ p {
margin: 1em 0 !important;
}
.ql-editor {
white-space: normal;
p {
margin: 0 !important;
}
}
hr {
border-top: 1px solid @border-color;
}
@ -210,4 +217,4 @@ hr {
.report-title {
margin-bottom: 20px;
}
/* csslint ignore:end */
/* csslint ignore:end */

View file

@ -98,6 +98,10 @@
text-align: right;
}
.grid-row .grid-row-check {
margin-top: 12px;
}
.grid-row > .row {
.col:last-child {
margin-right: -10px;

View file

@ -78,7 +78,7 @@
}
}
.ql-editor .mention {
.ql-editor:not(.read-mode) .mention {
height: auto;
width: auto;
border-radius: 10px;
@ -133,3 +133,7 @@
margin-top: 0px;
margin-bottom: 0px;
}
.ql-editor.read-mode {
padding: 0;
}

View file

@ -1,3 +1,4 @@
@import '~quill/dist/quill.core';
@import 'variables';
@import 'frappe/public/css/font-awesome';
@import '~bootstrap/scss/bootstrap';
@ -11,6 +12,20 @@
@import 'portal';
@import 'doc';
.ql-editor.read-mode {
padding: 0;
line-height: 1.6;
h1,
h2,
h3,
h4,
h5 {
margin-top: 0.5em;
margin-bottom: 0.25em;
}
}
.container {
padding-left: 1.25rem;
padding-right: 1.25rem;
@ -338,4 +353,4 @@ h5.modal-title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}

View file

@ -42,7 +42,11 @@
{{ head_include or "" }}
{% endblock -%}
{%- block style %}{%- endblock -%}
{%- block style %}
{% if colocated_css -%}
<style>{{ colocated_css }}</style>
{%- endif %}
{%- endblock -%}
<script>
window.frappe = {};
@ -86,7 +90,11 @@
<script type="text/javascript" src="{{ link | abs_url }}"></script>
{%- endfor -%}
{%- block script %}{%- endblock %}
{%- block script %}
{% if colocated_js -%}
<script>{{ colocated_js }}</script>
{%- endif %}
{%- endblock %}
<!-- csrf_token -->
{%- block body_include %}{{ body_include or "" }}{% endblock -%}
</body>

View file

@ -7,8 +7,6 @@
{%- endif -%}
</a>
{% endmacro %}
{% if footer_items -%}
<div class="footer-links">
<div class="row">
<div class="footer-col-left col-sm-6">
@ -24,4 +22,3 @@
</div>
</div>
</div>
{% endif %}

View file

@ -99,9 +99,7 @@ data-fieldname="{{ df.fieldname }}" data-fieldtype="{{ df.fieldtype }}"
{%- if df.fieldtype=="Code" %}
<pre class="value">{{ doc.get(df.fieldname) }}</pre>
{% else -%}
{%- if df.fieldtype=="Text Editor" -%}<div class='ql-editor'>{%- endif -%}
{{ doc.get_formatted(df.fieldname, parent_doc or doc, translated=df.translatable) }}
{%- if df.fieldtype=="Text Editor" -%}</div>{%- endif -%}
{% endif -%}
</div>
{%- endif -%}

View file

@ -104,11 +104,11 @@ class BackupGenerator:
this_file = cstr(this_file)
this_file_path = os.path.join(get_backup_path(), this_file)
if not is_file_old(this_file_path, older_than):
if "_private_files" in this_file_path:
if "-private-files" in this_file_path:
backup_path_private_files = this_file_path
elif "_files" in this_file_path:
elif "-files" in this_file_path:
backup_path_files = this_file_path
elif "_database" in this_file_path:
elif "-database" in this_file_path:
backup_path_db = this_file_path
elif "site_config" in this_file_path:
site_config_backup_path = this_file_path

View file

@ -3,6 +3,21 @@
frappe.ui.form.on('Help Article', {
refresh: function(frm) {
frm.dashboard.clear_headline();
frm.dashboard.set_headline_alert(`
<div class="row">
<div class="col-md-6 col-xs-12">
<span class="indicator whitespace-nowrap green">
<span>Helpful ${frm.doc.helpful}</span>
</span>
</div>
<div class="col-md-6 col-xs-12">
<span class="indicator whitespace-nowrap red">
<span>Not Helpful ${frm.doc.not_helpful}</span>
</span>
</div>
</div>
`);
}
});

View file

@ -1,445 +1,158 @@
{
"allow_copy": 0,
"allow_guest_to_view": 1,
"allow_import": 1,
"allow_rename": 0,
"beta": 0,
"creation": "2014-10-30 14:25:53.780105",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 0,
"actions": [],
"allow_guest_to_view": 1,
"allow_import": 1,
"creation": "2014-10-30 14:25:53.780105",
"doctype": "DocType",
"field_order": [
"title",
"category",
"published",
"column_break_4",
"author",
"level",
"section_break_7",
"content",
"likes",
"route",
"owner",
"feedback",
"helpful",
"cb_00",
"not_helpful"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "title",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Title",
"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,
"translatable": 0,
"unique": 0
},
"fieldname": "title",
"fieldtype": "Data",
"in_global_search": 1,
"label": "Title",
"reqd": 1,
"search_index": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "category",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Category",
"length": 0,
"no_copy": 0,
"options": "Help Category",
"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,
"translatable": 0,
"unique": 0
},
"fieldname": "category",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Category",
"options": "Help Category",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "published",
"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": "Published",
"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
},
"default": "0",
"fieldname": "published",
"fieldtype": "Check",
"label": "Published"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column 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
},
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "user_fullname",
"fieldname": "author",
"fieldtype": "Data",
"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": "Author",
"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
},
"default": "user_fullname",
"fieldname": "author",
"fieldtype": "Data",
"label": "Author"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "level",
"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": "Level",
"length": 0,
"no_copy": 0,
"options": "Beginner\nIntermediate\nExpert",
"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
},
"fieldname": "level",
"fieldtype": "Select",
"label": "Level",
"options": "Beginner\nIntermediate\nExpert"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_7",
"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
},
"fieldname": "section_break_7",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "content",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Content",
"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,
"translatable": 0,
"unique": 0
},
"fieldname": "content",
"fieldtype": "Text Editor",
"in_global_search": 1,
"label": "Content",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "likes",
"fieldtype": "Int",
"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": "Likes",
"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
},
"fieldname": "likes",
"fieldtype": "Int",
"label": "Likes",
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "route",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Route",
"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
},
"fieldname": "route",
"fieldtype": "Data",
"in_global_search": 1,
"label": "Route"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "user",
"fieldname": "owner",
"fieldtype": "Link",
"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": "Owner",
"length": 0,
"no_copy": 0,
"options": "User",
"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
"default": "user",
"fieldname": "owner",
"fieldtype": "Link",
"label": "Owner",
"options": "User"
},
{
"collapsible": 1,
"fieldname": "feedback",
"fieldtype": "Section Break",
"label": "Feedback"
},
{
"default": "0",
"fieldname": "helpful",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Helpful",
"read_only": 1
},
{
"fieldname": "cb_00",
"fieldtype": "Column Break"
},
{
"default": "0",
"fieldname": "not_helpful",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Not Helpful",
"read_only": 1
}
],
"has_web_view": 1,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "icon-file-alt",
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_published_field": "published",
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-05-18 17:49:52.912440",
"modified_by": "Administrator",
"module": "Website",
"name": "Help Article",
"name_case": "",
"owner": "Administrator",
],
"has_web_view": 1,
"icon": "icon-file-alt",
"is_published_field": "published",
"links": [],
"modified": "2020-05-08 10:48:19.997789",
"modified_by": "Administrator",
"module": "Website",
"name": "Help Article",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Knowledge Base Editor",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"import": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Knowledge Base Editor",
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Knowledge Base Contributor",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"create": 1,
"read": 1,
"role": "Knowledge Base Contributor",
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Guest",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
"read": 1,
"role": "Guest"
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "title",
"track_changes": 1,
"track_seen": 0
],
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "title",
"track_changes": 1
}

View file

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe.website.website_generator import WebsiteGenerator
from frappe.utils import is_markdown, markdown
from frappe.utils import is_markdown, markdown, cint
from frappe.website.utils import get_comment_list
from frappe import _
@ -100,3 +100,11 @@ def clear_website_cache(path=None):
frappe.cache().delete_value("knowledge_base:category_sidebar")
frappe.cache().delete_value("knowledge_base:faq")
@frappe.whitelist(allow_guest=True)
def add_feedback(article, helpful):
field = "helpful"
if helpful == "No":
field = "not_helpful"
value = cint(frappe.db.get_value("Help Article", article, field))
frappe.db.set_value("Help Article", article, field, value+1, update_modified=False)

View file

@ -10,10 +10,24 @@
<h6 class='text-muted'>By {{ author }} on {{ frappe.format_date(creation) }}</h6>
<span class="indicator {{ level_class }}">{{ level }}</span>
</div>
<div class="from-markdown my-4" itemprop="articleBody">
{{ content }}
</div>
<p><br><a href="/{{ category.route }}" class='text-muted small'>
{{ _("More articles on {0}").format(category.name) }}</a></p>
</article>
<div class="help-article-feedback mb-4">
<hr />
<div class="feedback-view ">
<div class="text-muted small mr-2 mb-2">{{ _("Was this article helpful?") }}</div>
<button class="feedback btn btn-outline-primary btn-sm" data-value="Yes" style="width: 50px;">{{ _("Yes") }}</button>
<button class="feedback btn btn-outline-primary btn-sm" data-value="No" style="width: 50px;">{{ _("No") }}</button>
<span class="feedback-msg small hidden">{{ _("Thank you for your feedback!") }}</span>
</div>
</div>
<div class="help-article-comments">
<hr>
<a href="/{{ category.route }}">
{{ _("More articles on {0}").format(category.name) }}
@ -25,4 +39,26 @@
<h5>Comments</h5>
{% include 'templates/includes/comments/comments.html' %}
</div>
<script>
frappe.ready(function() {
frappe.set_search_path("/kb");
$(".feedback").click(function() {
let args = {
article: "{{ reference_name or name }}",
helpful: this.getAttribute("data-value"),
}
frappe.call({
btn: this,
method: "frappe.website.doctype.help_article.help_article.add_feedback",
args: args,
callback: function(r) {
$(".feedback")[0].classList.add("hidden");
$(".feedback")[1].classList.add("hidden");
$(".feedback-msg")[0].classList.remove("hidden");
}
})
});
});
</script>
{% endblock %}

View file

@ -283,22 +283,18 @@ def setup_source(page_info):
# set the source only if it contains raw content
html = source
# load css/js files
js, css = '', ''
# load css/js files
js_path = os.path.join(page_info.basepath, (page_info.basename or 'index') + '.js')
if os.path.exists(js_path) and '{% block script %}' not in html:
with io.open(js_path, 'r', encoding = 'utf-8') as f:
js = f.read()
page_info.colocated_js = js
js_path = os.path.join(page_info.basepath, (page_info.basename or 'index') + '.js')
if os.path.exists(js_path):
if not '{% block script %}' in html:
with io.open(js_path, 'r', encoding = 'utf-8') as f:
js = f.read()
html += '\n{% block script %}<script>' + js + '\n</script>\n{% endblock %}'
css_path = os.path.join(page_info.basepath, (page_info.basename or 'index') + '.css')
if os.path.exists(css_path):
if not '{% block style %}' in html:
with io.open(css_path, 'r', encoding='utf-8') as f:
css = f.read()
html += '\n{% block style %}\n<style>\n' + css + '\n</style>\n{% endblock %}'
css_path = os.path.join(page_info.basepath, (page_info.basename or 'index') + '.css')
if os.path.exists(css_path) and '{% block style %}' not in html:
with io.open(css_path, 'r', encoding='utf-8') as f:
css = f.read()
page_info.colocated_css = css
if html:
page_info.source = html