feat: show title links in Link Fields

This commit is contained in:
hrwx 2021-08-03 15:55:32 +05:30
parent 757a07251a
commit d056beb0ef
18 changed files with 404 additions and 61 deletions

View file

@ -65,16 +65,49 @@ context('Control Link', () => {
cy.intercept('POST', '/api/method/frappe.desk.search.search_link').as('search_link');
cy.get('@todos').then(todos => {
cy.get('.frappe-control[data-fieldname=link] input').as('input');
cy.get('@input').focus();
cy.get('.frappe-control[data-fieldname=link] input').focus().as('input');
cy.wait('@search_link');
cy.get('@input').type(todos[0]).blur();
cy.get('@input').type(todos[0]);
cy.wait('@search_link');
cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible');
cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 });
cy.wait('@validate_link');
cy.get('@input').focus();
cy.get('.frappe-control[data-fieldname=link] .link-btn')
.should('be.visible')
.click();
cy.location('pathname').should('eq', `/app/todo/${todos[0]}`);
cy.location('hash').should('eq', `#Form/ToDo/${todos[0]}`);
});
});
it('show title field in link', () => {
get_dialog_with_link().as('dialog');
cy.server();
cy.insert_doc("Property Setter", {
property: "show_title_field_in_link",
doc_type: "ToDo",
value: 1,
doctype_or_field: "DocType"
}, true);
cy.route('POST', '/api/method/frappe.desk.search.search_link').as('search_link');
cy.get('.frappe-control[data-fieldname=link] input').focus().as('input');
cy.wait('@search_link');
cy.get('@input').type('todo for link');
cy.wait('@search_link');
cy.get('.frappe-control[data-fieldname=link] ul').should('be.visible');
cy.get('.frappe-control[data-fieldname=link] input').type('{enter}', { delay: 100 });
cy.get('.frappe-control[data-fieldname=link] input').blur();
cy.get('@dialog').then(dialog => {
cy.get('@todos').then(todos => {
let field = dialog.get_field('link');
let value = field.get_value();
let label = field.get_label_value();
expect(value).to.eq(todos[0]);
expect(label).to.eq('this is a test todo for link');
});
});
});
});

View file

@ -87,6 +87,7 @@ def get_bootinfo():
bootinfo.additional_filters_config = get_additional_filters_from_hooks()
bootinfo.desk_settings = get_desk_settings()
bootinfo.app_logo_url = get_app_logo()
bootinfo.doctypes_with_show_link_field_title = doctypes_with_show_link_field_title()
return bootinfo
@ -324,3 +325,9 @@ def get_desk_settings():
def get_notification_settings():
return frappe.get_cached_doc('Notification Settings', frappe.session.user)
def doctypes_with_show_link_field_title():
dts = frappe.get_all("DocType", {"show_title_field_in_link": 1})
custom_dts = frappe.get_all("Property Setter", {"field_name": "show_title_field_in_link", "value": 1})
return [d.name for d in dts + custom_dts if d]

View file

@ -45,6 +45,7 @@
"allow_auto_repeat",
"view_settings",
"title_field",
"show_title_field_in_link",
"search_fields",
"default_print_format",
"sort_field",
@ -554,6 +555,12 @@
"fieldname": "website_search_field",
"fieldtype": "Data",
"label": "Website Search Field"
},
{
"default": "0",
"fieldname": "show_title_field_in_link",
"fieldtype": "Check",
"label": "Show Title in Link and Table MultiSelect Field"
}
],
"icon": "fa fa-bolt",
@ -635,7 +642,7 @@
"link_fieldname": "reference_doctype"
}
],
"modified": "2021-06-17 23:31:44.974199",
"modified": "2021-08-03 13:41:50.319555",
"modified_by": "Administrator",
"module": "Core",
"name": "DocType",

View file

@ -27,6 +27,7 @@
"autoname",
"view_settings_section",
"title_field",
"show_title_field_in_link",
"image_field",
"default_print_format",
"column_break_29",
@ -280,6 +281,12 @@
"fieldname": "autoname",
"fieldtype": "Data",
"label": "Auto Name"
},
{
"default": "0",
"fieldname": "show_title_field_in_link",
"fieldtype": "Check",
"label": "Show Title in Link and Table MultiSelect Field"
}
],
"hide_toolbar": 1,
@ -288,7 +295,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2021-06-21 19:01:06.920663",
"modified": "2021-08-03 13:43:27.938781",
"modified_by": "Administrator",
"module": "Custom",
"name": "Customize Form",

View file

@ -495,7 +495,8 @@ doctype_properties = {
'email_append_to': 'Check',
'subject_field': 'Data',
'sender_field': 'Data',
'autoname': 'Data'
'autoname': 'Data',
'show_title_field_in_link': 'Check'
}
docfield_properties = {

View file

@ -224,6 +224,7 @@ CREATE TABLE `tabDocType` (
`email_append_to` int(1) NOT NULL DEFAULT 0,
`subject_field` varchar(255) DEFAULT NULL,
`sender_field` varchar(255) DEFAULT NULL,
`show_title_field_in_link` int(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`name`),
KEY `parent` (`parent`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View file

@ -229,6 +229,7 @@ CREATE TABLE "tabDocType" (
"email_append_to" smallint NOT NULL DEFAULT 0,
"subject_field" varchar(255) DEFAULT NULL,
"sender_field" varchar(255) DEFAULT NULL,
"show_title_field_in_link" smallint NOT NULL DEFAULT 0,
PRIMARY KEY ("name")
) ;

View file

@ -48,7 +48,7 @@ def getdoc(doctype, name, user=None):
raise
doc.add_seen()
set_link_titles(doc)
frappe.response.docs.append(doc)
@frappe.whitelist(allow_guest=True)
@ -310,3 +310,55 @@ def get_additional_timeline_content(doctype, docname):
contents.extend(frappe.get_attr(method)(doctype, docname) or [])
return contents
def set_link_titles(doc):
meta = frappe.get_meta(doc.doctype)
link_titles = {}
link_titles.update(get_title_values_for_link_and_dynamic_link_fields(meta, doc))
link_titles.update(get_title_values_for_table_and_multiselect_fields(meta, doc))
send_link_titles(link_titles)
def get_title_values_for_link_and_dynamic_link_fields(meta, doc, link_fields=None):
link_titles = {}
if not link_fields:
link_fields = meta.get_link_fields() + meta.get_dynamic_link_fields()
for field in link_fields:
if not doc.get(field.fieldname):
continue
doctype = field.options if field.fieldtype == "Link" else doc.get(field.options)
meta = frappe.get_meta(doctype)
if not meta or not (meta.title_field and meta.show_title_field_in_link):
continue
link_title = frappe.get_cached_value(doctype, doc.get(field.fieldname), meta.title_field)
link_titles.update({doctype + "::" + doc.get(field.fieldname): link_title})
return link_titles
def get_title_values_for_table_and_multiselect_fields(meta, doc, table_fields=None):
link_titles = {}
if not table_fields:
table_fields = meta.get_table_fields()
for field in table_fields:
if not doc.get(field.fieldname):
continue
_meta = frappe.get_meta(field.options)
for value in doc.get(field.fieldname):
link_titles.update(get_title_values_for_link_and_dynamic_link_fields(_meta, value))
return link_titles
def send_link_titles(link_titles):
"""Append link titles dict in `frappe.local.response`."""
if "_link_titles" not in frappe.local.response:
frappe.local.response["_link_titles"] = {}
frappe.local.response["_link_titles"].update(link_titles)

View file

@ -49,8 +49,11 @@ def sanitize_searchfield(searchfield):
# this is called by the Link Field
@frappe.whitelist()
def search_link(doctype, txt, query=None, filters=None, page_length=20, searchfield=None, reference_doctype=None, ignore_user_permissions=False):
search_widget(doctype, txt.strip(), query, searchfield=searchfield, page_length=page_length, filters=filters, reference_doctype=reference_doctype, ignore_user_permissions=ignore_user_permissions)
frappe.response['results'] = build_for_autosuggest(frappe.response["values"])
search_widget(doctype, txt.strip(), query, searchfield=searchfield, page_length=page_length, filters=filters,
reference_doctype=reference_doctype, ignore_user_permissions=ignore_user_permissions)
frappe.response["results"] = build_for_autosuggest(frappe.response["values"], doctype=doctype,
is_query=True if query else False)
del frappe.response["values"]
# this is called by the search box
@ -138,6 +141,11 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0,
fields = list(set(fields + json.loads(filter_fields)))
formatted_fields = ['`tab%s`.`%s`' % (meta.name, f.strip()) for f in fields]
title_field_query = get_title_field_query(meta)
# Insert title field query after name
formatted_fields.insert(1, title_field_query)
# find relevance as location of search term from the beginning of string `name`. used for sorting results.
formatted_fields.append("""locate({_txt}, `tab{doctype}`.`name`) as `_relevance`""".format(
_txt=frappe.db.escape((txt or "").replace("%", "").replace("@", "")), doctype=doctype))
@ -205,10 +213,32 @@ def get_std_fields_list(meta, key):
return sflist
def build_for_autosuggest(res):
def get_title_field_query(meta):
title_field = meta.title_field if meta.title_field else None
show_title_field_in_link = meta.show_title_field_in_link if meta.show_title_field_in_link else None
field = "NULL as `label`"
if title_field and show_title_field_in_link:
field = "`tab{0}`.{1} as `label`".format(meta.name, title_field)
return field
def build_for_autosuggest(res, doctype, is_query):
results = []
for r in res:
out = {"value": r[0], "description": ", ".join(unique(cstr(d) for d in r if d)[1:])}
r = list(r)
if is_query or doctype in (frappe.get_hooks().standard_queries or {}):
out = {
"value": r[0],
"description": ", ".join(unique(cstr(d) for d in r[1:] if d))
}
else:
out = {
"value": r[0],
"label": r[1],
"description": ", ".join(unique(cstr(d) for d in r[2:] if d))
}
results.append(out)
return results
@ -271,3 +301,12 @@ def get_user_groups():
return frappe.get_all('User Group', fields=['name as id', 'name as value'], update={
'is_group': True
})
@frappe.whitelist()
def get_link_title(doctype, docname):
meta = frappe.get_meta(doctype)
if meta.title_field and meta.show_title_field_in_link:
return frappe.get_cached_value(doctype, docname, meta.title_field)
return docname

View file

@ -16,3 +16,4 @@ import "air-datepicker/dist/js/i18n/datepicker.sk.js";
import "air-datepicker/dist/js/i18n/datepicker.zh.js";
import "./frappe/ui/capture.js";
import "./frappe/form/controls/control.js";
import "./frappe/link_title.js";

View file

@ -29,12 +29,14 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
setTimeout(function() {
if(me.$input.val() && me.get_options()) {
let doctype = me.get_options();
let name = me.$input.val();
let name = me.get_input_value();
me.$link.toggle(true);
me.$link_open.attr('href', frappe.utils.get_form_link(doctype, name));
}
if(!me.$input.val()) {
me.reset_value();
me.reset_fetch_values(me.df, me.docname);
me.$input.val("").trigger("input");
}
}, 500);
@ -69,6 +71,76 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
this.$input_area.find(".link-btn").remove();
}
}
set_formatted_input(value) {
super.set_formatted_input();
if (!value) return;
let doctype = this.get_options();
this.set_data_value(frappe.get_link_title(doctype, value) || value, value);
}
set_data_value(link_display, value) {
if (!this.$input) {
return;
}
this.$input.val(__(link_display));
this.data_value = value;
}
parse_validate_and_set_in_model(value, label, e) {
if (this.parse) value = this.parse(value, label);
if (label) {
this.label = label;
frappe.add_link_title(this.doctype, value, label);
}
return this.validate_and_set_in_model(value, e);
}
validate_and_set_in_model(value, e) {
var me = this;
if (this.inside_change_event) {
return Promise.resolve();
}
this.inside_change_event = true;
var set = function(value) {
me.inside_change_event = false;
return frappe.run_serially([
() => me.set_model_value(value),
() => {
me.set_mandatory && me.set_mandatory(value);
if (me.df.change || me.df.onchange) {
// onchange event specified in df
frappe.set_link_title(me);
return (me.df.change || me.df.onchange).apply(me, [e]);
}
}
]);
};
value = this.validate(value);
if (value && value.then) {
// got a promise
return value.then((value) => set(value));
} else {
// all clear
return set(value);
}
}
get_input_value() {
return (this.$input && this.data_value && this.$input.val()) ? this.data_value : "";
}
get_label_value() {
return this.$input ? this.$input.val() : "";
}
set_input_label(label) {
this.$input && this.$input.val(__(label));
}
reset_value() {
if (!this.$input) {
return;
}
this.$input.val("");
this.data_value = null;
}
open_advanced_search() {
var doctype = this.get_options();
if(!doctype) return;
@ -98,7 +170,7 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
}
// partially entered name field
frappe.route_options.name_field = this.get_value();
frappe.route_options.name_field = this.get_label_value();
// reference to calling link
frappe._from_link = this;
@ -120,6 +192,11 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
maxItems: 99,
autoFirst: true,
list: [],
replace: function (suggestion) {
// Override Awesomeplete replace function as it is used to set the input value
// https://github.com/LeaVerou/awesomplete/issues/17104#issuecomment-359185403
this.input.value = suggestion.label || suggestion.value;
},
data: function (item) {
return {
label: item.label || item.value,
@ -236,9 +313,11 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
me.selected = false;
return;
}
var value = me.get_input_value();
if(value!==me.last_value) {
me.parse_validate_and_set_in_model(value);
let value = me.get_input_value();
let label = me.get_label_value();
if (value !== me.last_value || me.label !== label) {
me.parse_validate_and_set_in_model(value, label);
}
});
@ -258,14 +337,15 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
// prevent selection on tab
var TABKEY = 9;
if(e.keyCode === TABKEY) {
if (e.keyCode === TABKEY) {
e.preventDefault();
me.awesomplete.close();
return false;
}
if(item.action) {
if (item.action) {
item.value = "";
item.label = "";
item.action.apply(me);
}
@ -277,13 +357,14 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
frappe.boot.user.last_selected_values[me.df.options] = item.value;
}
me.parse_validate_and_set_in_model(item.value);
me.parse_validate_and_set_in_model(item.value, item.label);
});
this.$input.on("awesomplete-selectcomplete", function(e) {
var o = e.originalEvent;
if(o.text.value.indexOf("__link_option") !== -1) {
me.$input.val("");
let o = e.originalEvent;
if (o.text.value.indexOf("__link_option") !== -1) {
me.reset_value();
me.reset_fetch_values(me.df, me.docname);
}
});
}
@ -452,10 +533,13 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
this.docname, value);
}
validate_link_and_fetch(df, doctype, docname, value) {
if(value) {
let me = this;
if (value) {
return new Promise((resolve) => {
var fetch = '';
if(this.frm && this.frm.fetch_dict[df.fieldname]) {
let fetch = '';
if (this.frm && this.frm.fetch_dict[df.fieldname]) {
fetch = this.frm.fetch_dict[df.fieldname].columns.join(', ');
}
// if default and no fetch, no need to validate
@ -465,10 +549,14 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
this.fetch_and_validate_link(resolve, df, doctype, docname, value, fetch);
});
} else {
me.reset_value();
me.reset_fetch_values(df, docname);
}
}
fetch_and_validate_link(resolve, df, doctype, docname, value, fetch) {
let me = this;
frappe.call({
method: 'frappe.desk.form.utils.validate_link',
type: "GET",
@ -485,18 +573,27 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat
}
resolve(r.valid_value);
} else {
me.reset_value();
me.reset_fetch_values(df, docname);
resolve("");
}
}
});
}
set_fetch_values(df, docname, fetch_values) {
var fl = this.frm.fetch_dict[df.fieldname].fields;
for(var i=0; i < fl.length; i++) {
let fl = this.frm.fetch_dict[df.fieldname].fields;
for (var i=0; i < fl.length; i++) {
frappe.model.set_value(df.parent, docname, fl[i], fetch_values[i], df.fieldtype);
}
}
reset_fetch_values(df, docname) {
let fields = this.frm && this.frm.fetch_dict && this.frm.fetch_dict[df.fieldname] ? this.frm.fetch_dict[df.fieldname].fields : [];
fields.forEach(field => {
frappe.model.set_value(df.parent, docname, field, null, df.fieldtype);
});
}
};
if (Awesomplete) {

View file

@ -62,6 +62,7 @@ frappe.ui.form.ControlTableMultiSelect = class ControlTableMultiSelect extends f
[link_field.fieldname]: value
});
}
frappe.add_link_title(link_field.options, value, label);
}
this._rows_list = this.rows.map(row => row[link_field.fieldname]);
return this.rows;
@ -126,10 +127,12 @@ frappe.ui.form.ControlTableMultiSelect = class ControlTableMultiSelect extends f
this.$input_area.prepend(html);
}
get_pill_html(value) {
const encoded_value = encodeURIComponent(value);
const link_field = this.get_link_field();
const encoded_value = encodeURIComponent(value.name);
const pill_name = frappe.get_link_title(link_field.options, value[link_field.fieldname]) || value.name;
return `
<button class="data-pill btn tb-selected-value" data-value="${encoded_value}">
<span class="btn-link-to-form">${__(value)}</span>
<span class="btn-link-to-form">${__(pill_name)}</span>
<span class="btn-remove">${frappe.utils.icon('close')}</span>
</button>
`;

View file

@ -106,12 +106,14 @@ frappe.form.formatters = {
Link: function(value, docfield, options, doc) {
var doctype = docfield._options || docfield.options;
var original_value = value;
let link_title = frappe.get_link_title(doctype, value);
if(value && value.match && value.match(/^['"].*['"]$/)) {
value.replace(/^.(.*).$/, "$1");
}
if(options && (options.for_print || options.only_value)) {
return value;
return link_title || value;
}
if(frappe.form.link_formatters[doctype]) {
@ -135,13 +137,14 @@ frappe.form.formatters = {
return `<a
href="/app/${encodeURIComponent(frappe.router.slug(doctype))}/${encodeURIComponent(original_value)}"
data-doctype="${doctype}"
data-name="${original_value}">
${__(options && options.label || value)}</a>`;
data-name="${original_value}"
data-value="${original_value}">
${__(options && options.label || link_title || value)}</a>`;
} else {
return value;
return link_title || value;
}
} else {
return value;
return link_title || value;
}
},
Date: function(value) {

View file

@ -249,31 +249,40 @@ frappe.ui.form.update_calling_link = (newdoc) => {
};
if (is_valid_doctype()) {
// set value
if (doc && doc.parentfield) {
//update values for child table
$.each(frappe._from_link.frm.fields_dict[doc.parentfield].grid.grid_rows, function (index, field) {
if (field.doc && field.doc.name === frappe._from_link.docname) {
frappe._from_link.set_value(newdoc.name);
}
});
} else {
frappe._from_link.set_value(newdoc.name);
}
// refresh field
frappe._from_link.refresh();
// if from form, switch
if (frappe._from_link.frm) {
frappe.set_route("Form",
frappe._from_link.frm.doctype, frappe._from_link.frm.docname)
.then(() => {
frappe.utils.scroll_to(frappe._from_link_scrollY);
frappe.model.with_doctype(newdoc.doctype, () => {
let meta = frappe.get_meta(newdoc.doctype);
// set value
if (doc && doc.parentfield) {
//update values for child table
$.each(frappe._from_link.frm.fields_dict[doc.parentfield].grid.grid_rows, function (index, field) {
if (field.doc && field.doc.name === frappe._from_link.docname) {
if (meta.title_field && meta.show_title_field_in_link) {
frappe.add_link_title(newdoc.doctype, newdoc.name, newdoc[meta.title_field]);
}
frappe._from_link.set_value(newdoc.name);
}
});
}
} else {
if (meta.title_field && meta.show_title_field_in_link) {
frappe.add_link_title(newdoc.doctype, newdoc.name, newdoc[meta.title_field]);
}
frappe._from_link.set_value(newdoc.name);
}
frappe._from_link = null;
// refresh field
frappe._from_link.refresh();
// if from form, switch
if (frappe._from_link.frm) {
frappe.set_route("Form",
frappe._from_link.frm.doctype, frappe._from_link.frm.docname)
.then(() => {
frappe.utils.scroll_to(frappe._from_link_scrollY);
});
}
frappe._from_link = null;
});
}
}

View file

@ -0,0 +1,34 @@
// for link titles
frappe._link_titles = {};
frappe.get_link_title = function(doctype, name) {
if (!doctype || !name) {
return;
}
return frappe._link_titles[doctype + "::" + name];
};
frappe.add_link_title = function (doctype, name, value) {
if (!doctype || !name) {
return;
}
frappe._link_titles[doctype + "::" + name] = value;
};
frappe.set_link_title = function(f) {
let doctype = f.get_options();
let docname = f.get_input_value();
if ((!in_list(frappe.boot.doctypes_with_show_link_field_title, doctype)) || (!doctype || !docname) ||
(frappe.get_link_title(doctype, docname))) {
return;
}
frappe.xcall("frappe.desk.search.get_link_title", {"doctype": doctype, "docname": docname}).then((r) => {
if (r && docname !== r) {
f.set_input_label(r);
}
});
};

View file

@ -241,6 +241,11 @@ frappe.request.call = function(opts) {
$.extend(frappe._messages, data.__messages);
}
// sync link titles
if (data._link_titles) {
$.extend(frappe._link_titles, data._link_titles);
}
// callbacks
var status_code_handler = statusCode[xhr.statusCode().status];
if (status_code_handler) {

View file

@ -313,6 +313,10 @@ frappe.ui.Filter = class {
return this.utils.get_selected_value(this.field, this.get_condition());
}
get_selected_label() {
return this.utils.get_selected_label(this.field);
}
get_condition() {
return this.filter_edit_area.find('.condition').val();
}
@ -360,7 +364,7 @@ frappe.ui.Filter = class {
get_filter_button_text() {
let value = this.utils.get_formatted_value(
this.field,
this.get_selected_value()
this.get_selected_label() || this.get_selected_value()
);
return `${__(this.field.df.label)} ${__(this.get_condition())} ${__(
value
@ -448,6 +452,12 @@ frappe.ui.filter_utils = {
return val;
},
get_selected_label(field) {
if (in_list(["Link", "Dynamic Link"], field.df.fieldtype)) {
return field.get_label_value();
}
},
get_default_condition(df) {
if (df.fieldtype == 'Data') {
return 'like';

View file

@ -170,6 +170,38 @@ def get_rendered_template(doc, name=None, print_format=None, meta=None,
return html
def set_link_titles(doc):
# Replaces name with title of link field doctype
meta = frappe.get_meta(doc.doctype)
set_title_values_for_link_and_dynamic_link_fields(meta, doc)
set_title_values_for_table_and_multiselect_fields(meta, doc)
def set_title_values_for_link_and_dynamic_link_fields(meta, doc):
for field in meta.get_link_fields() + meta.get_dynamic_link_fields():
if not doc.get(field.fieldname):
continue
# If link field, then get doctype from options
# If dynamic link field, then get doctype from dependent field
doctype = field.options if field.fieldtype == "Link" else doc.get(field.options)
meta = frappe.get_meta(doctype)
if not meta or not (meta.title_field and meta.show_title_field_in_link):
continue
link_title = frappe.get_cached_value(doctype, doc.get(field.fieldname), meta.title_field)
setattr(doc, field.fieldname, link_title)
def set_title_values_for_table_and_multiselect_fields(meta, doc):
for field in meta.get_table_fields():
if not doc.get(field.fieldname):
continue
_meta = frappe.get_meta(field.options)
for value in doc.get(field.fieldname):
set_title_values_for_link_and_dynamic_link_fields(_meta, value)
def convert_markdown(doc, meta):
'''Convert text field values to markdown if necessary'''
for field in meta.fields:
@ -191,6 +223,7 @@ def get_html_and_style(doc, name=None, print_format=None, meta=None,
doc = frappe.get_doc(json.loads(doc))
print_format = get_print_format_doc(print_format, meta=meta or frappe.get_meta(doc.doctype))
set_link_titles(doc)
try:
html = get_rendered_template(doc, name=name, print_format=print_format, meta=meta,