ソースを参照

fix(link-preview): Get preview fields on server side

- Show a fallback image if image URL is invalid
- Fixes unexpected "Unknown column" errors
version-14
Suraj Shetty 6年前
コミット
b23fea9d17
4個のファイルの変更84行の追加112行の削除
  1. +4
    -0
      frappe/boot.py
  2. +31
    -16
      frappe/desk/link_preview.py
  3. バイナリ
      frappe/public/images/fallback-thumbnail.jpg
  4. +49
    -96
      frappe/public/js/frappe/ui/link_preview.js

+ 4
- 0
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})]

+ 31
- 16
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

バイナリ
frappe/public/images/fallback-thumbnail.jpg ファイルの表示

変更前 変更後
幅: 600  |  高さ: 450  |  サイズ: 2.5 KiB

+ 49
- 96
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 += `
<div class="preview-header">
<img src=${image_url} class="preview-image"></img>
</div>`;
if (preview_data.preview_image) {
let image_url = encodeURI(preview_data.preview_image);
image_html = `
<div class="preview-header">
<img src="${image_url}" onerror="this.src='/assets/frappe/images/fallback-thumbnail.jpg'" class="preview-image"></img>
</div>
`;
}


if(title && title != preview_data.name) {
id_html+= `<a class="text-muted" href=${this.href}>${preview_data.name}</a>`;
}
if(!title) {
title = preview_data.name;
if (preview_data.preview_title != preview_data.name) {
id_html = `<a class="text-muted" href=${this.href}>${preview_data.name}</a>`;
}

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 += `
<div class="preview-field">
<div class='small preview-label text-muted bold'>${label}</div>
<div class="small preview-value"> ${value} </div>
</div>
if (!['preview_image', 'preview_title', 'name'].includes(key)) {
let value = frappe.ellipsis(preview_data[key], 280);
let label = key;
content_html = `
<div class="preview-table">
<div class="preview-field">
<div class='small preview-label text-muted bold'>${label}</div>
<div class="small preview-value">${value}</div>
</div>
</div>
`;
}
});

content_html = `<div class="preview-table">${content_html}</div>`;

let popover_content =
`<div class="preview-popover-header">${image_html}
let popover_content =`
<div class="preview-popover-header">${image_html}
<div class="preview-header">
<div class="preview-main">
<a class="preview-name bold" href=${this.href}>${title}</a>
<a class="preview-name bold" href=${this.href}>${preview_data.preview_title}</a>
<span class="text-muted small">${this.doctype} ${id_html}</span>
</div>
</div>
@@ -252,16 +211,10 @@ frappe.ui.LinkPreview = class {
<hr>
<div class="popover-body">
${content_html}
</div>`;
</div>
`;

return popover_content;
}

truncate_value(value) {
if (value.length > 280) {
value = value.slice(0,280) + '...';
}
return value;
}

};

読み込み中…
キャンセル
保存