diff --git a/frappe/boot.py b/frappe/boot.py index efa9ac2afe..50888fcdd2 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -82,6 +82,7 @@ def get_bootinfo(): bootinfo.energy_points_enabled = is_energy_point_enabled() bootinfo.points = get_energy_points(frappe.session.user) bootinfo.frequently_visited_links = frequently_visited_links() + bootinfo.link_preview_doctypes = get_link_preview_doctypes() return bootinfo @@ -262,3 +263,6 @@ def get_gsuite_status(): def get_success_action(): return frappe.get_all("Success Action", fields=["*"]) + +def get_link_preview_doctypes(): + return [d.name for d in frappe.db.get_all('DocType', {'show_preview_popup': 1})] \ No newline at end of file diff --git a/frappe/desk/link_preview.py b/frappe/desk/link_preview.py index f8252f20bc..f82a83a673 100644 --- a/frappe/desk/link_preview.py +++ b/frappe/desk/link_preview.py @@ -3,26 +3,41 @@ from frappe.model import no_value_fields import json @frappe.whitelist() -def get_preview_data(doctype, docname, fields): - fields = json.loads(fields) - preview_fields = [field['name'] for field in fields if field['type'] not in no_value_fields] - preview_fields.append(frappe.get_meta(doctype).get_title_field()) - if 'name' not in fields: - preview_fields.append('name') - preview_fields.append(frappe.get_meta(doctype).image_field) +def get_preview_data(doctype, docname): + preview_fields = [] + meta = frappe.get_meta(doctype) + if not meta.show_preview_popup: return + + preview_fields = [field.fieldname for field in meta.fields \ + if field.in_preview and field.fieldtype not in no_value_fields] + + # no preview fields defined, build list from mandatory fields + if not preview_fields: + preview_fields = [field.name for field in meta.fields if field.reqd] + + title_field = meta.get_title_field() + image_field = meta.image_field + + preview_fields.append(title_field) + preview_fields.append(image_field) + preview_fields.append('name') preview_data = frappe.get_list(doctype, filters={ 'name': docname }, fields=preview_fields, limit=1) - if preview_data: - preview_data = preview_data[0] - preview_data = {k: v for k, v in preview_data.items() if v is not None} - for k,v in preview_data.items(): - if frappe.get_meta(doctype).has_field(k): - preview_data[k] = frappe.format(v,frappe.get_meta(doctype).get_field(k).fieldtype) + if not preview_data: return + + preview_data = preview_data[0] + + formatted_preview_data = { + 'preview_image': preview_data.get(image_field), + 'preview_title': preview_data.get(title_field), + 'name': preview_data.get('name') + } - if not preview_data: - return None - return preview_data + for key, val in preview_data.items(): + if val and meta.has_field(key): + formatted_preview_data[meta.get_field(key).label] = frappe.format(val, meta.get_field(key).fieldtype) + return formatted_preview_data \ No newline at end of file diff --git a/frappe/public/images/fallback-thumbnail.jpg b/frappe/public/images/fallback-thumbnail.jpg new file mode 100644 index 0000000000..68a62d7766 Binary files /dev/null and b/frappe/public/images/fallback-thumbnail.jpg differ diff --git a/frappe/public/js/frappe/ui/link_preview.js b/frappe/public/js/frappe/ui/link_preview.js index 39b47f65b1..fe3cdfab52 100644 --- a/frappe/public/js/frappe/ui/link_preview.js +++ b/frappe/public/js/frappe/ui/link_preview.js @@ -13,10 +13,10 @@ frappe.ui.LinkPreview = class { this.element = $(e.currentTarget); this.is_link = this.element.get(0).tagName.toLowerCase() === 'a'; - if(!this.element.parents().find('.popover').length) { + if (!this.element.parents().find('.popover').length) { this.identify_doc(); this.popover = this.element.data("bs.popover"); - if(this.name && this.doctype) { + if (this.name && this.doctype) { this.setup_popover_control(e); } } @@ -39,18 +39,18 @@ frappe.ui.LinkPreview = class { } setup_popover_control(e) { + if (!(frappe.boot.link_preview_doctypes || []).includes(this.doctype)) { + return; + } //If control field value is changed, new popover has to be created - this.element.on('change',()=> { + this.element.on('change', () => { this.new_popover = true; }); - if(!this.popover || this.new_popover) { - this.get_preview_fields().then(preview_fields => { - if(preview_fields.length) { - this.data_timeout = setTimeout(() => { - this.create_popover(e, preview_fields); - }, 100); - } - }); + if (!this.popover || this.new_popover) { + this.data_timeout = setTimeout(() => { + this.create_popover(e); + }, 100); + } else { this.popover_timeout = setTimeout(() => { if (this.element.is(':focus')) { @@ -61,20 +61,20 @@ frappe.ui.LinkPreview = class { } } - create_popover(e, preview_fields) { + create_popover(e) { this.new_popover = false; if (this.element.is(':focus')) { return; } - this.get_preview_fields_value(preview_fields).then((preview_data)=> { - if(preview_data) { - if(this.popover_timeout) { + this.get_preview_data().then(preview_data => { + if (preview_data) { + if (this.popover_timeout) { clearTimeout(this.popover_timeout); } this.popover_timeout = setTimeout(() => { - if(this.popover) { + if (this.popover) { let new_content = this.get_popover_html(preview_data); this.popover.options.content = new_content; } else { @@ -88,16 +88,15 @@ frappe.ui.LinkPreview = class { } show_popover(e) { - - this.default_timeout = setTimeout(()=> { + this.default_timeout = setTimeout(() => { this.clear_all_popovers(); }, 10000); - if(!this.is_link) { + if (!this.is_link) { var left = e.pageX; this.element.popover('show'); var width = $('.popover').width(); - $('.control-field-popover').css('left', (left-(width/2)) + 'px'); + $('.control-field-popover').css('left', (left - (width / 2)) + 'px'); } else { this.element.popover('show'); } @@ -109,14 +108,14 @@ frappe.ui.LinkPreview = class { if (!$('.popover:hover').length) { this.link_hovered = false; } - if(!this.link_hovered) { - if(this.data_timeout) { + if (!this.link_hovered) { + if (this.data_timeout) { clearTimeout(this.data_timeout); } if (this.popover_timeout) { clearTimeout(this.popover_timeout); } - if(this.default_timeout) { + if (this.default_timeout) { clearTimeout(this.default_timeout); } this.clear_all_popovers(); @@ -132,45 +131,10 @@ frappe.ui.LinkPreview = class { this.popovers_list.forEach($el => $el.hide()); } - get_preview_fields() { - return new Promise((resolve) => { - let dt = this.doctype; - let fields = []; - frappe.model.with_doctype(dt, () => { - let meta = frappe.get_meta(dt); - let meta_fields = meta.fields; - - if (!meta.show_preview_popup) { - // no preview - resolve([]); - return; - } - - meta_fields.filter((field) => { - // build list of fields to fetch - if(field.in_preview) { - fields.push({'name':field.fieldname,'type':field.fieldtype}); - } - }); - - // no preview fields defined, build list from mandatory fields - if(!fields.length) { - meta_fields.filter((field) => { - if(field.reqd) { - fields.push({'name':field.fieldname,'type':field.fieldtype}); - } - }); - } - resolve(fields); - }); - }); - } - - get_preview_fields_value(field_list) { + get_preview_data() { return frappe.xcall('frappe.desk.link_preview.get_preview_data', { 'doctype': this.doctype, 'docname': this.name, - 'fields': field_list, }); } @@ -195,56 +159,51 @@ frappe.ui.LinkPreview = class { } get_popover_html(preview_data) { - if(!this.href) { + if (!this.href) { this.href = window.location.href; } - if(this.href && this.href.includes(' ')) { + if (this.href && this.href.includes(' ')) { this.href = this.href.replace(new RegExp(' ', 'g'), '%20'); } let image_html = ''; let id_html = ''; let content_html = ''; - let meta = frappe.get_meta(this.doctype); - let title = preview_data.title; - if(preview_data[meta.image_field]) { - let image_url = encodeURI(preview_data[meta.image_field]); - image_html += ` -
- -
`; + if (preview_data.preview_image) { + let image_url = encodeURI(preview_data.preview_image); + image_html = ` +
+ +
+ `; } - - if(title && title != preview_data.name) { - id_html+= `${preview_data.name}`; - } - if(!title) { - title = preview_data.name; + if (preview_data.preview_title != preview_data.name) { + id_html = `${preview_data.name}`; } Object.keys(preview_data).forEach(key => { - if(key!=meta.image_field && key!='name' && key!=meta.title_field) { - let value = this.truncate_value(preview_data[key]); - let label = this.truncate_value(frappe.meta.get_label(this.doctype, key)); - content_html += ` -
-
${label}
-
${value}
-
+ if (!['preview_image', 'preview_title', 'name'].includes(key)) { + let value = frappe.ellipsis(preview_data[key], 280); + let label = key; + content_html = ` +
+
+
${label}
+
${value}
+
+
`; } }); - content_html = `
${content_html}
`; - - let popover_content = - `
${image_html} + let popover_content =` +
${image_html}
- ${title} + ${preview_data.preview_title} ${this.doctype} ${id_html}
@@ -252,16 +211,10 @@ frappe.ui.LinkPreview = class {
${content_html} -
`; +
+ `; return popover_content; } - truncate_value(value) { - if (value.length > 280) { - value = value.slice(0,280) + '...'; - } - return value; - } - };