This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
app_include_js = "/assets/subscription/js/widgets.js" | |
override_whitelisted_methods = { | |
"frappe.desk.search.search_link": "routes.search_link", | |
"frappe.desk.search.search_title": "routes.search_title" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
# Copyright (c) 2015, SAN Brasil S/A and contributors | |
# For license information, please see license.txt | |
import frappe | |
from frappe.utils import cstr, unique | |
@frappe.whitelist() | |
def search_link(doctype, txt, query=None, filters=None, page_len=20, searchfield=None): | |
search_widget(doctype, txt, query, searchfield=searchfield, page_len=page_len, filters=filters) | |
frappe.response['results'] = build_for_autosuggest(frappe.response['values']) | |
del frappe.response['values'] | |
@frappe.whitelist() | |
def search_title(doctype, name): | |
meta = frappe.get_meta(doctype) | |
if meta.title_field: | |
return name, frappe.db.get_value(doctype, name, meta.title_field or 'name'), meta.title_field | |
else: | |
return name, name, meta.title_field | |
@frappe.whitelist() | |
def search_widget(doctype, txt, query=None, searchfield=None, start=0, | |
page_len=10, filters=None, as_dict=False): | |
if isinstance(filters, basestring): | |
import json | |
filters = json.loads(filters) | |
meta = frappe.get_meta(doctype) | |
if not searchfield: | |
searchfield = "name" | |
standard_queries = frappe.get_hooks().standard_queries or {} | |
if query and query.split()[0].lower()!="select": | |
# by method | |
frappe.response["values"] = frappe.call(query, doctype, txt, | |
searchfield, start, page_len, filters, as_dict=as_dict) | |
elif not query and doctype in standard_queries: | |
# from standard queries | |
search_widget(doctype, txt, standard_queries[doctype][0], | |
searchfield, start, page_len, filters) | |
else: | |
if query: | |
frappe.throw("This query style is discontinued") | |
# custom query | |
# frappe.response["values"] = frappe.db.sql(scrub_custom_query(query, searchfield, txt)) | |
else: | |
if isinstance(filters, dict): | |
filters_items = filters.items() | |
filters = [] | |
for f in filters_items: | |
if isinstance(f[1], (list, tuple)): | |
filters.append([doctype, f[0], f[1][0], f[1][1]]) | |
else: | |
filters.append([doctype, f[0], "=", f[1]]) | |
if filters==None: | |
filters = [] | |
or_filters = [] | |
if meta.title_field: | |
title_field = meta.title_field | |
else: | |
title_field = None | |
# build from doctype | |
if txt: | |
search_fields = ["name"] | |
if meta.search_fields: | |
search_fields.extend(meta.get_search_fields()) | |
for f in search_fields: | |
fmeta = meta.get_field(f.strip()) | |
if f == "name" or (fmeta and fmeta.fieldtype in ["Data", "Text", "Small Text", "Long Text", | |
"Link", "Select", "Read Only", "Text Editor"]): | |
or_filters.append([doctype, f.strip(), "like", "%{0}%".format(txt)]) | |
if meta.get("fields", {"fieldname":"enabled", "fieldtype":"Check"}): | |
filters.append([doctype, "enabled", "=", 1]) | |
if meta.get("fields", {"fieldname":"disabled", "fieldtype":"Check"}): | |
filters.append([doctype, "disabled", "!=", 1]) | |
fields = get_std_fields_list(meta, searchfield or "name") | |
if title_field: | |
fields.insert(0, "name") | |
fields.append("{} as `title`".format(frappe.db.escape(title_field))) | |
else: | |
fields.append("NULL as `title`") | |
# find relevance as location of search term from the beginning of string `name`. used for sorting results. | |
fields.append("""locate("{_txt}", `tab{doctype}`.`name`) as `_relevance`""".format( | |
_txt=frappe.db.escape((txt or "").replace("%", "")), doctype=frappe.db.escape(doctype))) | |
values = frappe.get_list(doctype, | |
filters=filters, fields=fields, | |
or_filters = or_filters, limit_start = start, | |
limit_page_length=page_len, | |
order_by="if(_relevance, _relevance, 99999), idx desc, modified desc".format(doctype), | |
ignore_permissions = True if doctype == "DocType" else False, # for dynamic links | |
as_list=not as_dict) | |
# remove _relevance from results | |
frappe.response["values"] = [r[:-1] for r in values] | |
def get_std_fields_list(meta, key): | |
# get additional search fields | |
sflist = meta.search_fields and meta.search_fields.split(",") or [] | |
title_field = [meta.title_field] if (meta.title_field and meta.title_field not in sflist) else [] | |
sflist = ['name'] + sflist + title_field | |
if not key in sflist: | |
sflist = sflist + [key] | |
return ['`tab%s`.`%s`' % (meta.name, f.strip()) for f in sflist] | |
def build_for_autosuggest(res): | |
results = [] | |
for r in res: | |
out = {'value': r[0], | |
"description": ",".join(unique(cstr(d) for d in r)[1:-1]), | |
"title": r[-1]} | |
results.append(out) | |
return results | |
def scrub_custom_query(query, key, txt): | |
if '%(key)s' in query: | |
query = query.replace('%(key)s', key) | |
if '%s' in query: | |
query = query.replace('%s', ((txt or '') + '%')) | |
return query |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
frappe.ui.form.ControlLink = frappe.ui.form.ControlLink.extend({ | |
format_for_input: function(value){ | |
var me = this, su = this._super; | |
if (this.selected){ | |
return this.selected_title; | |
} else { | |
var ret; | |
frappe.call({ | |
'async': false, | |
'method': 'frappe.desk.search.search_title', | |
'args': { | |
doctype: me.df.options, | |
name: value | |
}, | |
'callback': function(res){ | |
if (!res.exc){ | |
me.selected_title = res.message[1]; | |
ret = res.message[1]; | |
} | |
} | |
}); | |
return ret; | |
} | |
}, | |
get_value: function(){ | |
if (this.doctype && this.docname){ | |
return frappe.model.get_value(this.doctype, this.docname, this.df.fieldname); | |
} else { | |
return this._super(); | |
} | |
}, | |
setup_autocomplete: function() { | |
var me = this; | |
this.$input.on("blur", function() { | |
if(me.selected) { | |
me.selected = false; | |
return; | |
} | |
var value = me.get_value(); | |
if(me.doctype && me.docname) { | |
if(value!==me.last_value) { | |
me.parse_validate_and_set_in_model(value); | |
} | |
} else { | |
me.set_mandatory(value); | |
} | |
}); | |
this.$input.cache = {}; | |
this.$input.autocomplete({ | |
minLength: 0, | |
autoFocus: true, | |
source: function(request, response) { | |
var doctype = me.get_options(); | |
if(!doctype) return; | |
if (!me.$input.cache[doctype]) { | |
me.$input.cache[doctype] = {}; | |
} | |
if (me.$input.cache[doctype][request.term]!=null) { | |
// immediately show from cache | |
response(me.$input.cache[doctype][request.term]); | |
} | |
var args = { | |
'txt': request.term, | |
'doctype': doctype, | |
}; | |
me.set_custom_query(args); | |
return frappe.call({ | |
type: "GET", | |
method:'frappe.desk.search.search_link', | |
no_spinner: true, | |
args: args, | |
callback: function(r) { | |
if(!me.$input.is(":focus")) { | |
return; | |
} | |
if(!me.df.only_select) { | |
if(frappe.model.can_create(doctype) | |
&& me.df.fieldtype !== "Dynamic Link") { | |
// new item | |
r.results.push({ | |
value: "<span class='text-primary link-option'>" | |
+ "<i class='icon-plus' style='margin-right: 5px;'></i> " | |
+ __("Create a new {0}", [__(me.df.options)]) | |
+ "</span>", | |
action: me.new_doc | |
}); | |
}; | |
// advanced search | |
r.results.push({ | |
value: "<span class='text-primary link-option'>" | |
+ "<i class='icon-search' style='margin-right: 5px;'></i> " | |
+ __("Advanced Search") | |
+ "</span>", | |
action: me.open_advanced_search | |
}); | |
} | |
me.$input.cache[doctype][request.term] = r.results; | |
response(r.results); | |
}, | |
}); | |
}, | |
open: function(event, ui) { | |
me.$wrapper.css({"z-index": 101}); | |
me.autocomplete_open = true; | |
}, | |
close: function(event, ui) { | |
me.$wrapper.css({"z-index": 1}); | |
me.autocomplete_open = false; | |
}, | |
focus: function( event, ui ) { | |
event.preventDefault(); | |
if(ui.item.action) { | |
return false; | |
} | |
}, | |
select: function(event, ui) { | |
me.autocomplete_open = false; | |
// prevent selection on tab | |
var TABKEY = 9; | |
if(event.keyCode === TABKEY) { | |
event.preventDefault(); | |
me.$input.autocomplete("close"); | |
return false; | |
} | |
if(ui.item.action) { | |
ui.item.value = ""; | |
ui.item.action.apply(me); | |
} | |
// if remember_selected hook is set, add this value | |
// to defaults so you do not need to set it again | |
// unless it is changed. | |
if(frappe.boot.remember_selected && frappe.boot.remember_selected.indexOf(me.df.options)!==-1) { | |
frappe.boot.user.defaults[me.df.options] = ui.item.value; | |
} | |
if(me.frm && me.frm.doc) { | |
me.selected = true; | |
me.selected_title = ui.item.title || ui.item.value; | |
me.parse_validate_and_set_in_model(ui.item.value); | |
setTimeout(function() { | |
me.selected = false; | |
}, 100); | |
} else { | |
me.$input.val(ui.item.value).trigger('change'); | |
me.set_mandatory(ui.item.value); | |
} | |
} | |
}) | |
.on("blur", function() { | |
$(this).autocomplete("close"); | |
}) | |
.data('ui-autocomplete')._renderItem = function(ul, d) { | |
var html = "<strong>" + __(d.title || d.value) + "</strong>"; | |
if(d.description && d.value!==d.description) { | |
html += '<br><span class="small">' + __(d.description) + '</span>'; | |
} | |
return $('<li></li>') | |
.data('item.autocomplete', d) | |
.html('<a><p>' + html + '</p></a>') | |
.appendTo(ul); | |
}; | |
// remove accessibility span (for now) | |
this.$wrapper.find(".ui-helper-hidden-accessible").remove(); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
App hosted here https://github.com/mxmo-co/title_links