diff --git a/frappe/desk/doctype/tag/tag.json b/frappe/desk/doctype/tag/tag.json index 7522d9471a..895516594e 100644 --- a/frappe/desk/doctype/tag/tag.json +++ b/frappe/desk/doctype/tag/tag.json @@ -4,8 +4,7 @@ "doctype": "DocType", "engine": "InnoDB", "field_order": [ - "description", - "count" + "description" ], "fields": [ { @@ -13,15 +12,9 @@ "fieldtype": "Small Text", "in_list_view": 1, "label": "Description" - }, - { - "fieldname": "count", - "fieldtype": "Int", - "label": "Count", - "read_only": 1 } ], - "modified": "2019-09-24 00:53:13.488147", + "modified": "2019-09-25 17:47:41.712237", "modified_by": "Administrator", "module": "Desk", "name": "Tag", diff --git a/frappe/public/js/frappe/ui/toolbar/global_tags.js b/frappe/public/js/frappe/ui/toolbar/global_tags.js index bf4cb2293b..f8fbb39c8b 100644 --- a/frappe/public/js/frappe/ui/toolbar/global_tags.js +++ b/frappe/public/js/frappe/ui/toolbar/global_tags.js @@ -2,109 +2,14 @@ // MIT License. See license.txt frappe.provide("frappe.global_tags"); -frappe.provide("locals.global_tags"); - -frappe.global_tags.GlobalTagsDialog = class GlobalTags { - constructor(opts) { - $.extend(this, opts); - this.show(); - } - - show() { - if (!this.dialog) { - this.make_dialog(); - } - - $(this.dialog.body).html( - `
- ${__("Loading")}... -
`); - - this.dialog.show(); - } - - make_dialog() { - let title = __("Tag {0}", ["#".concat(this.tag)]); - - this.dialog = new frappe.ui.Dialog({ - hide_on_page_refresh: true, - minimizable: true, - title: title - }); - - this.dialog.on_page_show = () => { - this.get_documents_for_tag() - .then(() => this.make_html()); - }; - } - - make_html() { - const results = this.results; - let html = ''; - - const linked_doctypes = Object.keys(results); - - if (linked_doctypes.length === 0) { - html = __("Not Linked to any record"); - } else { - html = linked_doctypes.map(doctype => { - const docs = results[doctype]; - return ` -
- ${this.make_doc_head(doctype)} - ${docs.map(doc => this.make_doc_row(doc.dn, doctype, doc.title)).join('')} -
- `; - }).join(''); - } - - $(this.dialog.body).html(html); - } - - get_documents_for_tag() { - return new Promise((resolve) => { - frappe.call({ - method: "frappe.utils.global_tags.get_documents_for_tag", - args: { - tag: this.tag - }, - callback: (r) => { - this.results = r.message; - resolve(); - } - }); - }); - } - - make_doc_head(heading) { - return ` -
-
${__(heading)}
-
- `; - } - - make_doc_row(docname, doctype, title) { - return `
-
- -
-

${title}

-
-
-
`; - } -}; frappe.global_tags.utils = { get_tags: function(txt) { txt = txt.slice(1); let out = []; - for (let i in locals.global_tags) { - let tag = locals.global_tags[i]; + for (let i in frappe.global_tags.tags) { + let tag = frappe.global_tags.tags[i]; let level = frappe.search.utils.fuzzy_search(txt, tag); if (level) { out.push({ @@ -113,8 +18,9 @@ frappe.global_tags.utils = { value: __("#{0}", [__(tag)]), index: 1 + level, match: tag, - onclick: function() { - new frappe.global_tags.GlobalTagsDialog({"tag": tag}); + onclick() { + // Use Global Search Dialog for tag search too. + frappe.searchdialog.search.init_search("#".concat(tag), "global_tag") } }); } @@ -123,14 +29,130 @@ frappe.global_tags.utils = { return out; }, - set_tags: function() { + set_tags() { frappe.call({ method: "frappe.utils.global_tags.get_tags_list_for_awesomebar", callback: function(r) { if (r && r.message) { - locals.global_tags = $.extend([], r.message); + frappe.global_tags.tags = $.extend([], r.message); } } }); - } -} \ No newline at end of file + }, + + get_tag_results: function(tag) { + var me = this; + function get_results_sets(data) { + var results_sets = [], result, set; + function get_existing_set(doctype) { + return results_sets.find(function(set) { + return set.title === doctype; + }); + } + + function make_description(content, doc_name) { + var parts = content.split(" ||| "); + var result_max_length = 300; + var field_length = 120; + var fields = []; + var result_current_length = 0; + var field_text = ""; + for(var i = 0; i < parts.length; i++) { + var part = parts[i]; + if(part.toLowerCase().indexOf(tag) !== -1) { + // If the field contains the keyword + if(part.indexOf(' &&& ') !== -1) { + var colon_index = part.indexOf(' &&& '); + var field_value = part.slice(colon_index + 5); + } else { + var colon_index = part.indexOf(' : '); + var field_value = part.slice(colon_index + 3); + } + if(field_value.length > field_length) { + // If field value exceeds field_length, find the keyword in it + // and trim field value by half the field_length at both sides + // ellipsify if necessary + var field_data = ""; + var index = field_value.indexOf(tag); + field_data += index < field_length/2 ? field_value.slice(0, index) + : '...' + field_value.slice(index - field_length/2, index); + field_data += field_value.slice(index, index + field_length/2); + field_data += index + field_length/2 < field_value.length ? "..." : ""; + field_value = field_data; + } + var field_name = part.slice(0, colon_index); + + // Find remaining result_length and add field length to result_current_length + var remaining_length = result_max_length - result_current_length; + result_current_length += field_name.length + field_value.length + 2; + if(result_current_length < result_max_length) { + // We have room, push the entire field + field_text = '' + + me.bolden_match_part(field_name, tag) + ': ' + + me.bolden_match_part(field_value, tag); + if(fields.indexOf(field_text) === -1 && doc_name !== field_value) { + fields.push(field_text); + } + } else { + // Not enough room + if(field_name.length < remaining_length){ + // Ellipsify (trim at word end) and push + remaining_length -= field_name.length; + field_text = '' + + me.bolden_match_part(field_name, tag) + ': '; + field_value = field_value.slice(0, remaining_length); + field_value = field_value.slice(0, field_value.lastIndexOf(' ')) + ' ...'; + field_text += me.bolden_match_part(field_value, tag); + fields.push(field_text); + } else { + // No room for even the field name, skip + fields.push('...'); + } + break; + } + } + } + return fields.join(', '); + } + + data.forEach(function(d) { + // more properties + result = { + label: d.name, + value: d.name, + description: make_description(d.content, d.name), + route: ['Form', d.doctype, d.name], + + }; + set = get_existing_set(d.doctype); + if(set) { + set.results.push(result); + } else { + set = { + title: d.doctype, + results: [result], + fetch_type: "Global" + }; + results_sets.push(set); + } + + }); + return results_sets; + } + return new Promise(function(resolve, reject) { + frappe.call({ + method: "frappe.utils.global_tags.get_documents_for_tag", + args: { + tag: tag + }, + callback: function(r) { + if(r.message) { + resolve(get_results_sets(r.message)); + } else { + resolve([]); + } + } + }); + }); + }, +} diff --git a/frappe/public/js/frappe/ui/toolbar/search.js b/frappe/public/js/frappe/ui/toolbar/search.js index e27c383ec4..ac50a35a60 100644 --- a/frappe/public/js/frappe/ui/toolbar/search.js +++ b/frappe/public/js/frappe/ui/toolbar/search.js @@ -124,6 +124,10 @@ frappe.search.SearchDialog = Class.extend({ // Help results // this.$modal_body.on('click', 'a[data-path]', frappe.help.show_results); this.bind_keyboard_events(); + + // Setup Minimizable functionality + this.search_dialog.minimizable = true; + this.search_dialog.$wrapper.find('.btn-modal-minimize').click(() => this.toggle_minimize()); }, bind_keyboard_events: function() { @@ -308,7 +312,7 @@ frappe.search.SearchDialog = Class.extend({ frappe.route_options = result.route_options; } $result.on('click', (e) => { - this.search_dialog.hide(); + this.toggle_minimize(); if(result.onclick) { result.onclick(result.match); } else { @@ -353,6 +357,19 @@ frappe.search.SearchDialog = Class.extend({ this.$modal_body.find('.more-results.last').slideDown(200, function() {}); }, + get_minimize_btn: function() { + return this.search_dialog.$wrapper.find(".modal-header .btn-modal-minimize"); + }, + + toggle_minimize: function() { + let modal = this.search_dialog.$wrapper.closest('.modal').toggleClass('modal-minimize'); + modal.attr('tabindex') ? modal.removeAttr('tabindex') : modal.attr('tabindex', -1); + this.get_minimize_btn().find('i').toggleClass('octicon-chevron-down').toggleClass('octicon-chevron-up'); + this.search_dialog.is_minimized = !this.search_dialog.is_minimized; + this.on_minimize_toggle && this.on_minimize_toggle(this.search_dialog.is_minimized); + this.search_dialog.header.find('.modal-title').toggleClass('cursor-pointer'); + }, + // Search objects searches: { global_search: { @@ -372,6 +389,22 @@ frappe.search.SearchDialog = Class.extend({ }); } }, + global_tag: { + input_placeholder: __("Global Tags"), + empty_state_text: __("Search for Tags"), + no_results_status: (keyword) => __("

No results found for '" + keyword + "' in Global Tags

"), + + get_results: function(keywords, callback) { + var results = frappe.search.utils.get_nav_results(keywords); + frappe.global_tags.utils.get_tag_results(keywords) + .then(function(global_results) { + results = results.concat(global_results); + callback(results, keywords); + }, function (err) { + console.error(err); + }); + } + }, }, }); \ No newline at end of file diff --git a/frappe/public/js/frappe/ui/toolbar/search_header.html b/frappe/public/js/frappe/ui/toolbar/search_header.html index f36e87c7ea..4b8b70ad97 100644 --- a/frappe/public/js/frappe/ui/toolbar/search_header.html +++ b/frappe/public/js/frappe/ui/toolbar/search_header.html @@ -1,6 +1,13 @@
- - -

{%= __("Searching")%} ...

- + + +

{%= __("Searching")%} ...

+ + + + +
\ No newline at end of file diff --git a/frappe/utils/global_tags.py b/frappe/utils/global_tags.py index d13e4e2cc3..3c061d057a 100644 --- a/frappe/utils/global_tags.py +++ b/frappe/utils/global_tags.py @@ -48,7 +48,10 @@ def get_documents_for_tag(tag): :param tag: tag to be searched """ # remove hastag `#` from tag - results = {} + tag = tag[1:] + + results = [] + tag = frappe.db.escape('%{0}%'.format(tag.lower()), False) result = frappe.db.sql(''' @@ -58,10 +61,11 @@ def get_documents_for_tag(tag): '''.format(tag), as_dict=True) for res in result: - if res.dt in results.keys(): - results[res.dt].append(res) - else: - results[res.dt] = [res] + results.append({ + "doctype": res.dt, + "name": res.dn, + "content": res.title + }) return results