Skip to content

Instantly share code, notes, and snippets.

@MaxMorais
Last active July 2, 2016 18:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MaxMorais/e9dff8dbc51caac587c84faf2b4832d5 to your computer and use it in GitHub Desktop.
Save MaxMorais/e9dff8dbc51caac587c84faf2b4832d5 to your computer and use it in GitHub Desktop.
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"
}
# -*- 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
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();
}
});
@MaxMorais
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment