@@ -13,7 +13,7 @@ import os, sys, importlib, inspect, json | |||||
from .exceptions import * | from .exceptions import * | ||||
from .utils.jinja import get_jenv, get_template, render_template | from .utils.jinja import get_jenv, get_template, render_template | ||||
__version__ = '8.0.5' | |||||
__version__ = '8.0.6' | |||||
__title__ = "Frappe Framework" | __title__ = "Frappe Framework" | ||||
local = Local() | local = Local() | ||||
@@ -1,5 +1,6 @@ | |||||
{ | { | ||||
"allow_copy": 0, | "allow_copy": 0, | ||||
"allow_guest_to_view": 0, | |||||
"allow_import": 1, | "allow_import": 1, | ||||
"allow_rename": 0, | "allow_rename": 0, | ||||
"autoname": "CustomScript.####", | "autoname": "CustomScript.####", | ||||
@@ -24,6 +25,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_global_search": 0, | |||||
"in_list_view": 1, | "in_list_view": 1, | ||||
"in_standard_filter": 1, | "in_standard_filter": 1, | ||||
"label": "DocType", | "label": "DocType", | ||||
@@ -55,6 +57,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_global_search": 0, | |||||
"in_list_view": 1, | "in_list_view": 1, | ||||
"in_standard_filter": 1, | "in_standard_filter": 1, | ||||
"label": "Script Type", | "label": "Script Type", | ||||
@@ -84,8 +87,9 @@ | |||||
"hidden": 0, | "hidden": 0, | ||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | |||||
"in_list_view": 1, | |||||
"in_filter": 0, | |||||
"in_global_search": 0, | |||||
"in_list_view": 0, | |||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "Script", | "label": "Script", | ||||
"length": 0, | "length": 0, | ||||
@@ -115,6 +119,7 @@ | |||||
"ignore_user_permissions": 0, | "ignore_user_permissions": 0, | ||||
"ignore_xss_filter": 0, | "ignore_xss_filter": 0, | ||||
"in_filter": 0, | "in_filter": 0, | ||||
"in_global_search": 0, | |||||
"in_list_view": 0, | "in_list_view": 0, | ||||
"in_standard_filter": 0, | "in_standard_filter": 0, | ||||
"label": "Sample", | "label": "Sample", | ||||
@@ -133,19 +138,19 @@ | |||||
"unique": 0 | "unique": 0 | ||||
} | } | ||||
], | ], | ||||
"has_web_view": 0, | |||||
"hide_heading": 0, | "hide_heading": 0, | ||||
"hide_toolbar": 0, | "hide_toolbar": 0, | ||||
"icon": "fa fa-glass", | "icon": "fa fa-glass", | ||||
"idx": 1, | "idx": 1, | ||||
"image_view": 0, | "image_view": 0, | ||||
"in_create": 0, | "in_create": 0, | ||||
"in_dialog": 0, | |||||
"is_submittable": 0, | "is_submittable": 0, | ||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2016-12-29 14:40:24.037012", | |||||
"modified_by": "Administrator", | |||||
"modified": "2017-04-03 18:07:28.138437", | |||||
"modified_by": "faris@erpnext.com", | |||||
"module": "Custom", | "module": "Custom", | ||||
"name": "Custom Script", | "name": "Custom Script", | ||||
"owner": "Administrator", | "owner": "Administrator", | ||||
@@ -160,7 +165,6 @@ | |||||
"export": 0, | "export": 0, | ||||
"if_owner": 0, | "if_owner": 0, | ||||
"import": 0, | "import": 0, | ||||
"is_custom": 0, | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"print": 1, | "print": 1, | ||||
"read": 1, | "read": 1, | ||||
@@ -181,7 +185,6 @@ | |||||
"export": 0, | "export": 0, | ||||
"if_owner": 0, | "if_owner": 0, | ||||
"import": 0, | "import": 0, | ||||
"is_custom": 0, | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"print": 1, | "print": 1, | ||||
"read": 1, | "read": 1, | ||||
@@ -196,7 +199,8 @@ | |||||
"quick_entry": 0, | "quick_entry": 0, | ||||
"read_only": 0, | "read_only": 0, | ||||
"read_only_onload": 0, | "read_only_onload": 0, | ||||
"show_name_in_global_search": 0, | |||||
"sort_order": "ASC", | "sort_order": "ASC", | ||||
"track_changes": 1, | "track_changes": 1, | ||||
"track_seen": 0 | "track_seen": 0 | ||||
} | |||||
} |
@@ -846,7 +846,7 @@ | |||||
"currency_fraction": "Cent", | "currency_fraction": "Cent", | ||||
"currency_fraction_units": 100, | "currency_fraction_units": 100, | ||||
"currency_symbol": "\u20ac", | "currency_symbol": "\u20ac", | ||||
"number_format": "#,###.##", | |||||
"number_format": "# ###,##", | |||||
"timezones": [ | "timezones": [ | ||||
"Europe/Paris" | "Europe/Paris" | ||||
] | ] | ||||
@@ -139,6 +139,7 @@ | |||||
"public/js/frappe/ui/toolbar/search.js", | "public/js/frappe/ui/toolbar/search.js", | ||||
"public/js/frappe/ui/toolbar/search.html", | "public/js/frappe/ui/toolbar/search.html", | ||||
"public/js/frappe/ui/toolbar/search_header.html", | "public/js/frappe/ui/toolbar/search_header.html", | ||||
"public/js/frappe/ui/toolbar/search_utils.js", | |||||
"public/js/frappe/ui/toolbar/about.js", | "public/js/frappe/ui/toolbar/about.js", | ||||
"public/js/frappe/ui/toolbar/navbar.html", | "public/js/frappe/ui/toolbar/navbar.html", | ||||
"public/js/frappe/ui/toolbar/toolbar.js", | "public/js/frappe/ui/toolbar/toolbar.js", | ||||
@@ -213,7 +214,7 @@ | |||||
], | ], | ||||
"js/list.min.js": [ | "js/list.min.js": [ | ||||
"public/js/frappe/ui/listing.html", | "public/js/frappe/ui/listing.html", | ||||
"public/js/frappe/ui/base_list.js", | "public/js/frappe/ui/base_list.js", | ||||
"public/js/frappe/model/indicator.js", | "public/js/frappe/model/indicator.js", | ||||
@@ -238,7 +239,7 @@ | |||||
"public/js/frappe/list/list_item_row_head.html", | "public/js/frappe/list/list_item_row_head.html", | ||||
"public/js/frappe/list/list_item_subject.html", | "public/js/frappe/list/list_item_subject.html", | ||||
"public/js/frappe/list/list_permission_footer.html", | "public/js/frappe/list/list_permission_footer.html", | ||||
"public/js/frappe/list/list_renderer.js", | "public/js/frappe/list/list_renderer.js", | ||||
"public/js/frappe/views/gantt/gantt_view.js", | "public/js/frappe/views/gantt/gantt_view.js", | ||||
"public/js/frappe/views/calendar/calendar.js", | "public/js/frappe/views/calendar/calendar.js", | ||||
@@ -256,7 +257,7 @@ | |||||
"public/js/frappe/views/inbox/inbox_no_result.html", | "public/js/frappe/views/inbox/inbox_no_result.html", | ||||
"public/js/frappe/views/inbox/inbox_view_item_row.html", | "public/js/frappe/views/inbox/inbox_view_item_row.html", | ||||
"public/js/frappe/views/inbox/inbox_view_item_main_head.html", | "public/js/frappe/views/inbox/inbox_view_item_main_head.html", | ||||
"public/js/frappe/views/kanban/kanban_board.html", | "public/js/frappe/views/kanban/kanban_board.html", | ||||
"public/js/frappe/views/kanban/kanban_column.html", | "public/js/frappe/views/kanban/kanban_column.html", | ||||
"public/js/frappe/views/kanban/kanban_card.html" | "public/js/frappe/views/kanban/kanban_card.html" | ||||
@@ -677,13 +677,100 @@ fieldset[disabled] .form-control { | |||||
} | } | ||||
.search-dialog .modal-dialog { | .search-dialog .modal-dialog { | ||||
width: 768px; | width: 768px; | ||||
height: 500px; | |||||
} | |||||
.search-dialog .search-header { | |||||
display: flex; | |||||
align-items: center; | |||||
padding: 5px; | |||||
} | } | ||||
.search-dialog .modal-body { | .search-dialog .modal-body { | ||||
padding: 0px 15px; | padding: 0px 15px; | ||||
} | } | ||||
.search-dialog input.form-control, | |||||
.search-dialog .input-group-addon { | |||||
.search-dialog .empty-state { | |||||
color: #d4d9dd; | |||||
height: 500px; | |||||
display: flex; | |||||
justify-content: center; | |||||
align-items: center; | |||||
text-align: center; | |||||
} | |||||
.search-dialog .empty-state .status-icon { | |||||
font-size: 40px; | |||||
position: relative; | |||||
margin-bottom: 10px; | |||||
} | |||||
.search-dialog .empty-state p { | |||||
font-size: 15px; | |||||
display: block; | |||||
} | |||||
.search-dialog .empty-state .cover { | |||||
color: white; | |||||
font-size: 6px; | |||||
position: absolute; | |||||
} | |||||
@keyframes twinkle { | |||||
0% { | |||||
opacity: 1; | |||||
} | |||||
50% { | |||||
opacity: 0; | |||||
} | |||||
100% { | |||||
opacity: 1; | |||||
} | |||||
} | |||||
@-o-keyframes twinkle { | |||||
0% { | |||||
opacity: 1; | |||||
} | |||||
50% { | |||||
opacity: 0; | |||||
} | |||||
100% { | |||||
opacity: 1; | |||||
} | |||||
} | |||||
@-moz-keyframes twinkle { | |||||
0% { | |||||
opacity: 1; | |||||
} | |||||
50% { | |||||
opacity: 0; | |||||
} | |||||
100% { | |||||
opacity: 1; | |||||
} | |||||
} | |||||
@-webkit-keyframes twinkle { | |||||
0% { | |||||
opacity: 1; | |||||
} | |||||
50% { | |||||
opacity: 0; | |||||
} | |||||
100% { | |||||
opacity: 1; | |||||
} | |||||
} | |||||
.search-dialog .twinkle-one { | |||||
-webkit-animation: twinkle 1.5s ease infinite; | |||||
-moz-animation: twinkle 1.5s ease infinite; | |||||
-o-animation: twinkle 1.5s ease infinite; | |||||
animation: twinkle 1.5s ease infinite; | |||||
} | |||||
.search-dialog .twinkle-two { | |||||
-webkit-animation: twinkle 1.5s ease infinite 0.5s; | |||||
-moz-animation: twinkle 1.5s ease infinite 0.5s; | |||||
-o-animation: twinkle 1.5s ease infinite 0.5s; | |||||
animation: twinkle 1.5s ease infinite 0.5s; | |||||
} | |||||
.search-dialog .twinkle-three { | |||||
-webkit-animation: twinkle 1.5s ease infinite 1s; | |||||
-moz-animation: twinkle 1.5s ease infinite 1s; | |||||
-o-animation: twinkle 1.5s ease infinite 1s; | |||||
animation: twinkle 1.5s ease infinite 1s; | |||||
} | |||||
.search-dialog input.form-control { | |||||
border: none; | border: none; | ||||
border-left-style: none; | border-left-style: none; | ||||
} | } | ||||
@@ -691,9 +778,6 @@ fieldset[disabled] .form-control { | |||||
outline: none; | outline: none; | ||||
box-shadow: none; | box-shadow: none; | ||||
} | } | ||||
.search-dialog .input-group-addon { | |||||
background-color: #FFF; | |||||
} | |||||
.search-dialog .layout-side-section, | .search-dialog .layout-side-section, | ||||
.search-dialog .layout-main-section { | .search-dialog .layout-main-section { | ||||
height: 500px; | height: 500px; | ||||
@@ -712,6 +796,7 @@ fieldset[disabled] .form-control { | |||||
align-items: center; | align-items: center; | ||||
justify-content: space-between; | justify-content: space-between; | ||||
padding-left: 20px; | padding-left: 20px; | ||||
background-color: #ffffff; | |||||
} | } | ||||
.search-dialog .layout-side-section .nav li a i { | .search-dialog .layout-side-section .nav li a i { | ||||
visibility: hidden; | visibility: hidden; | ||||
@@ -719,23 +804,9 @@ fieldset[disabled] .form-control { | |||||
.search-dialog .layout-side-section .nav .active i { | .search-dialog .layout-side-section .nav .active i { | ||||
visibility: visible; | visibility: visible; | ||||
} | } | ||||
.search-dialog .results-area .search-intro-placeholder { | |||||
color: #d4d9dd; | |||||
height: inherit; | |||||
display: flex; | |||||
justify-content: center; | |||||
align-items: center; | |||||
} | |||||
.search-dialog .results-area .search-intro-placeholder span { | |||||
text-align: center; | |||||
} | |||||
.search-dialog .results-area .search-intro-placeholder span i { | |||||
font-size: 64px; | |||||
display: block; | |||||
} | |||||
.search-dialog .results-area .search-intro-placeholder span p { | |||||
font-size: 15px; | |||||
display: block; | |||||
.search-dialog .layout-side-section .nav .select a, | |||||
.search-dialog .layout-side-section .nav a:hover { | |||||
background-color: #f7fafc; | |||||
} | } | ||||
.search-dialog .results-area .single-link a { | .search-dialog .results-area .single-link a { | ||||
color: #36414c; | color: #36414c; | ||||
@@ -748,6 +819,9 @@ fieldset[disabled] .form-control { | |||||
font-family: 'Octicons'; | font-family: 'Octicons'; | ||||
content: '\f0a4'; | content: '\f0a4'; | ||||
} | } | ||||
.search-dialog .module-section .result { | |||||
margin-bottom: 5px; | |||||
} | |||||
.search-dialog .full-list .result { | .search-dialog .full-list .result { | ||||
margin-top: 15px; | margin-top: 15px; | ||||
} | } | ||||
@@ -772,6 +846,27 @@ fieldset[disabled] .form-control { | |||||
.search-dialog .more-results { | .search-dialog .more-results { | ||||
display: none; | display: none; | ||||
} | } | ||||
.search-dialog .result p { | |||||
margin-top: 5px; | |||||
margin-bottom: 5px; | |||||
} | |||||
.search-dialog .result .result-image { | |||||
display: inline-block; | |||||
margin-right: 10px; | |||||
height: 60px; | |||||
width: 60px; | |||||
background-color: #fafbfc; | |||||
} | |||||
.search-dialog .result .result-image .flex-text { | |||||
display: flex; | |||||
height: 60px; | |||||
align-items: center; | |||||
justify-content: center; | |||||
} | |||||
.search-dialog .result .result-image span { | |||||
font-size: 30px; | |||||
color: #d1d8dd; | |||||
} | |||||
@media (max-width: 767px) { | @media (max-width: 767px) { | ||||
.search-dialog .modal-dialog { | .search-dialog .modal-dialog { | ||||
width: auto; | width: auto; | ||||
@@ -785,21 +880,12 @@ fieldset[disabled] .form-control { | |||||
margin: 0px; | margin: 0px; | ||||
border-top: none; | border-top: none; | ||||
} | } | ||||
.search-dialog .layout-side-section .sidebar-menu { | |||||
margin: 30px 0px; | |||||
} | |||||
} | } | ||||
@media (min-width: 600px) { | @media (min-width: 600px) { | ||||
.search-dialog .results-area .back-link { | .search-dialog .results-area .back-link { | ||||
display: none; | display: none; | ||||
} | } | ||||
} | } | ||||
.result p { | |||||
margin-top: 0.2em; | |||||
} | |||||
.search-result { | |||||
margin-bottom: 24px; | |||||
} | |||||
.note-editor.note-frame .note-editing-area .note-editable { | .note-editor.note-frame .note-editing-area .note-editable { | ||||
color: #36414C; | color: #36414C; | ||||
} | } | ||||
@@ -420,6 +420,7 @@ | |||||
.list-item input[type=checkbox] { | .list-item input[type=checkbox] { | ||||
margin: 0; | margin: 0; | ||||
margin-right: 5px; | margin-right: 5px; | ||||
flex: 0 0 12px; | |||||
} | } | ||||
.list-item .liked-by, | .list-item .liked-by, | ||||
.list-item .liked-by-filter-button { | .list-item .liked-by-filter-button { | ||||
@@ -11,7 +11,7 @@ | |||||
</i> | </i> | ||||
<span class="likes-count">{{ (_liked_by.length > 99 ? "99+" : _liked_by.length) || "" }}</span> | <span class="likes-count">{{ (_liked_by.length > 99 ? "99+" : _liked_by.length) || "" }}</span> | ||||
</span> | </span> | ||||
<a class="grey list-id {{ css_seen }}" | |||||
<a class="grey list-id {{ css_seen }} ellipsis" | |||||
data-name="{{ _name }}" | data-name="{{ _name }}" | ||||
href="#Form/{{ _doctype_encoded }}/{{ _name_encoded }}" | href="#Form/{{ _doctype_encoded }}/{{ _name_encoded }}" | ||||
title="{{ _full_title }}">{{ strip_html(_title) }}</a> | title="{{ _full_title }}">{{ strip_html(_title) }}</a> | ||||
@@ -447,9 +447,6 @@ frappe.views.ListRenderer = Class.extend({ | |||||
data._title = strip_html(data[title_field] || data.name); | data._title = strip_html(data[title_field] || data.name); | ||||
data._full_title = data._title; | data._full_title = data._title; | ||||
if (data._title.length > 35) { | |||||
data._title = data._title.slice(0, 35) + '...'; | |||||
} | |||||
data._workflow = null; | data._workflow = null; | ||||
if (this.workflow_state_fieldname) { | if (this.workflow_state_fieldname) { | ||||
@@ -9,11 +9,6 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
var $input = $(element); | var $input = $(element); | ||||
var input = $input.get(0); | var input = $input.get(0); | ||||
this.search = new frappe.search.UnifiedSearch(); | |||||
this.global = new frappe.search.GlobalSearch(); | |||||
this.nav = new frappe.search.NavSearch(); | |||||
this.help = new frappe.search.HelpSearch(); | |||||
this.options = []; | this.options = []; | ||||
this.global_results = []; | this.global_results = []; | ||||
@@ -23,32 +18,22 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
autoFirst: true, | autoFirst: true, | ||||
list: [], | list: [], | ||||
filter: function (text, term) { | filter: function (text, term) { | ||||
this.get_item(text.value).boo = "foo"; | |||||
return true; | return true; | ||||
}, | }, | ||||
data: function (item, input) { | data: function (item, input) { | ||||
var label = item.label + "%%%" + item.value + "%%%" + | |||||
(item.description || "") + "%%%" + (item.index || "") | |||||
+ "%%%" + (item.type || "") + "%%%" + (item.prefix || ""); | |||||
return { | return { | ||||
label: label, | |||||
label: (item.index || ""), | |||||
value: item.value | value: item.value | ||||
}; | }; | ||||
}, | }, | ||||
item: function(item, term) { | item: function(item, term) { | ||||
var d = item; | |||||
var parts = item.split("%%%"), | |||||
d = { label: parts[0], value: parts[1], description: parts[2], | |||||
type: parts[4], prefix: parts[5]}; | |||||
if(d.prefix) { | |||||
var html = "<span>" + __((d.prefix + ' ' + d.label)) + "</span>"; | |||||
} else if(d.type) { | |||||
var html = "<span>" + __((d.label + ' ' + d.type)) + "</span>"; | |||||
} else { | |||||
var html = "<span>" + __(d.label || d.value) + "</span>"; | |||||
} | |||||
var d = this.get_item(item.value); | |||||
var name = d.prefix ? __(d.prefix + ' ' + (d.label || d.value)) : | |||||
__(d.label || d.value); | |||||
var html = '<span>' + name + '</span>'; | |||||
if(d.description && d.value!==d.description) { | if(d.description && d.value!==d.description) { | ||||
html += '<br><span class="text-muted">' + __(d.description) + '</span>'; | |||||
html += '<br><span class="text-muted ellipsis">' + __(d.description) + '</span>'; | |||||
} | } | ||||
return $('<li></li>') | return $('<li></li>') | ||||
.data('item.autocomplete', d) | .data('item.autocomplete', d) | ||||
@@ -56,9 +41,7 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
.get(0); | .get(0); | ||||
}, | }, | ||||
sort: function(a, b) { | sort: function(a, b) { | ||||
var a_index = a.split("%%%")[3]; | |||||
var b_index = b.split("%%%")[3]; | |||||
return (a_index - b_index); | |||||
return (b.label - a.label); | |||||
} | } | ||||
}); | }); | ||||
@@ -67,53 +50,29 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
var txt = value.trim().replace(/\s\s+/g, ' '); | var txt = value.trim().replace(/\s\s+/g, ' '); | ||||
var last_space = txt.lastIndexOf(' '); | var last_space = txt.lastIndexOf(' '); | ||||
me.global_results = []; | me.global_results = []; | ||||
if(txt && txt.length > 2) { | |||||
me.global.get_awesome_bar_options(txt.toLowerCase(), me); | |||||
} | |||||
// if(txt && txt.length > 1) { | |||||
// me.global.get_awesome_bar_options(txt.toLowerCase(), me); | |||||
// } | |||||
var $this = $(this); | var $this = $(this); | ||||
clearTimeout($this.data('timeout')); | clearTimeout($this.data('timeout')); | ||||
$this.data('timeout', setTimeout(function(){ | $this.data('timeout', setTimeout(function(){ | ||||
me.options = []; | me.options = []; | ||||
if(txt && txt.length > 2) { | |||||
if(txt && txt.length > 1) { | |||||
if(last_space !== -1) { | if(last_space !== -1) { | ||||
me.set_specifics(txt.slice(0,last_space), txt.slice(last_space+1)); | me.set_specifics(txt.slice(0,last_space), txt.slice(last_space+1)); | ||||
} | } | ||||
me.add_defaults(txt); | |||||
me.options = me.options.concat(me.build_options(txt)); | me.options = me.options.concat(me.build_options(txt)); | ||||
me.build_defaults(txt); | |||||
me.options = me.options.concat(me.global_results); | me.options = me.options.concat(me.global_results); | ||||
} else { | |||||
me.options = me.options.concat( | |||||
me.deduplicate(frappe.search.utils.get_recent_pages(txt || ""))); | |||||
} | } | ||||
me.make_calculator(txt); | |||||
me.add_recent(txt || ""); | |||||
me.add_help(); | me.add_help(); | ||||
// de-duplicate | |||||
var out = [], routes = []; | |||||
me.options.forEach(function(option) { | |||||
if(option.route) { | |||||
if(option.route[0] === "List" && option.route[2]) { | |||||
option.route.splice(2); | |||||
} | |||||
var str_route = (typeof option.route==='string') ? | |||||
option.route : option.route.join('/'); | |||||
if(routes.indexOf(str_route)===-1) { | |||||
out.push(option); | |||||
routes.push(str_route); | |||||
} else { | |||||
var old = routes.indexOf(str_route); | |||||
if(out[old].index > option.index) { | |||||
out[old] = option; | |||||
} | |||||
} | |||||
} else { | |||||
out.push(option); | |||||
} | |||||
}); | |||||
awesomplete.list = out; | |||||
awesomplete.list = me.options; | |||||
}, 100)); | }, 100)); | ||||
}); | }); | ||||
@@ -153,20 +112,20 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
frappe.route(); | frappe.route(); | ||||
} | } | ||||
} | } | ||||
$input.val(""); | |||||
}); | }); | ||||
$input.on("awesomplete-selectcomplete", function(e) { | $input.on("awesomplete-selectcomplete", function(e) { | ||||
$input.val(""); | $input.val(""); | ||||
}); | }); | ||||
this.setup_recent(); | |||||
this.search.setup(); | |||||
frappe.search.utils.setup_recent(); | |||||
}, | }, | ||||
add_help: function() { | add_help: function() { | ||||
this.options.push({ | this.options.push({ | ||||
label: __("Help on Search"), | label: __("Help on Search"), | ||||
value: "Help on Search", | value: "Help on Search", | ||||
index: 100, | |||||
index: -10, | |||||
default: "Help", | default: "Help", | ||||
onclick: function() { | onclick: function() { | ||||
var txt = '<table class="table table-bordered">\ | var txt = '<table class="table table-bordered">\ | ||||
@@ -186,142 +145,6 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
}); | }); | ||||
}, | }, | ||||
add_recent: function(txt) { | |||||
var me = this; | |||||
values = []; | |||||
$.each(me.recent, function(i, doctype) { | |||||
values.push([doctype[1], ['Form', doctype[0], doctype[1]]]); | |||||
}); | |||||
values = values.reverse(); | |||||
$.each(frappe.route_history, function(i, route) { | |||||
if(route[0]==='Form') { | |||||
values.push([route[2], route]); | |||||
} | |||||
else if(in_list(['List', 'Report', 'Tree', 'modules', 'query-report'], route[0])) { | |||||
if(route[1]) { | |||||
values.push([route[1], route]); | |||||
} | |||||
} | |||||
else if(route[0]) { | |||||
values.push([frappe.route_titles[route[0]] || route[0], route]); | |||||
} | |||||
}); | |||||
this.find(values, txt, function(match) { | |||||
out = { | |||||
route: match[1] | |||||
} | |||||
if(match[1][0]==='Form') { | |||||
if(match[1][1] !== match[1][2]) { | |||||
out.label = __(match[1][1]) + " " + match[1][2].bold(); | |||||
out.value = __(match[1][1]) + " " + match[1][2]; | |||||
} else { | |||||
out.label = __(match[1][1]).bold(); | |||||
out.value = __(match[1][1]); | |||||
} | |||||
} else if(in_list(['List', 'Report', 'Tree', 'modules', 'query-report'], match[1][0])) { | |||||
var type = match[1][0], label = type; | |||||
if(type==='modules') label = 'Module'; | |||||
else if(type==='query-report') label = 'Report'; | |||||
out.label = __(match[1][1]).bold() + " " + __(label); | |||||
out.value = __(match[1][1]) + " " + __(label); | |||||
} else { | |||||
out.label = match[0].bold(); | |||||
out.value = match[0]; | |||||
} | |||||
out.index = 80; | |||||
out.default = "Recent"; | |||||
return out; | |||||
}, true); | |||||
}, | |||||
find: function(list, txt, process, prepend) { | |||||
var me = this; | |||||
$.each(list, function(i, item) { | |||||
if($.isArray(item)) { | |||||
_item = item[0]; | |||||
} else { | |||||
_item = item; | |||||
} | |||||
_item = __(_item || '').toLowerCase().replace(/-/g, " "); | |||||
if(txt===_item || _item.indexOf(txt) !== -1) { | |||||
var option = process(item); | |||||
if(option) { | |||||
if($.isPlainObject(option)) { | |||||
option = [option]; | |||||
} | |||||
option.forEach(function(o) { o.match = item; }); | |||||
if(prepend) { | |||||
me.options = option.concat(me.options); | |||||
} else { | |||||
me.options = me.options.concat(option); | |||||
} | |||||
} | |||||
} | |||||
}); | |||||
}, | |||||
setup_recent: function() { | |||||
this.recent = JSON.parse(frappe.boot.user.recent || "[]") || []; | |||||
}, | |||||
fuzzy_search: function(_txt, _item) { | |||||
parsed_item = __(_item || '').replace(/-|_/g, " "); | |||||
item = parsed_item.toLowerCase(); | |||||
txt = _txt.toLowerCase(); | |||||
var ilen = item.length; | |||||
var tlen = txt.length; | |||||
var match_level = tlen/ilen; | |||||
var rendered_label = ""; | |||||
var i, j, skips = 0, mismatches = 0; | |||||
if(tlen > ilen) { | |||||
return []; | |||||
} | |||||
if(parsed_item.indexOf(_txt) !== -1 && txt !== _txt) { | |||||
var regEx = new RegExp("("+ txt +")", "ig"); | |||||
rendered_label = parsed_item.replace(regEx, '<b>$1</b>'); | |||||
return [parsed_item, (ilen + 10), rendered_label]; | |||||
} | |||||
if(item.indexOf(txt) !== -1) { | |||||
var regEx = new RegExp("("+ txt +")", "ig"); | |||||
rendered_label = parsed_item.replace(regEx, '<b>$1</b>'); | |||||
return [parsed_item, 20 + (ilen + 10), rendered_label]; | |||||
} | |||||
outer: for (i = 0, j = 0; i < tlen; i++) { | |||||
var t_ch = txt.charCodeAt(i); | |||||
if(mismatches !== 0) skips++; | |||||
if(skips > 3) return []; | |||||
mismatches = 0; | |||||
while (j < ilen) { | |||||
var i_ch = item.charCodeAt(j); | |||||
if (i_ch === t_ch) { | |||||
var item_char = parsed_item.charAt(j); | |||||
if(item_char === item_char.toLowerCase()){ | |||||
rendered_label += '<b>' + txt.charAt(i) + '</b>'; | |||||
} else { | |||||
rendered_label += '<b>' + txt.charAt(i).toUpperCase() + '</b>'; | |||||
} | |||||
j++; | |||||
continue outer; | |||||
} | |||||
mismatches++; | |||||
if(mismatches > 2) return []; | |||||
rendered_label += parsed_item.charAt(j); | |||||
j++; | |||||
} | |||||
return []; | |||||
} | |||||
rendered_label += parsed_item.slice(j); | |||||
return [parsed_item, 40 + (ilen + 10), rendered_label]; | |||||
}, | |||||
set_specifics: function(txt, end_txt) { | set_specifics: function(txt, end_txt) { | ||||
var me = this; | var me = this; | ||||
var results = this.build_options(txt); | var results = this.build_options(txt); | ||||
@@ -332,19 +155,50 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
}); | }); | ||||
}, | }, | ||||
build_defaults: function(txt) { | |||||
add_defaults: function(txt) { | |||||
this.make_global_search(txt); | this.make_global_search(txt); | ||||
this.make_search_in_current(txt); | this.make_search_in_current(txt); | ||||
this.options = this.options.concat(this.make_search_in_list(txt)); | |||||
this.make_calculator(txt); | |||||
}, | }, | ||||
build_options: function(txt) { | build_options: function(txt) { | ||||
return this.make_new_doc(txt).concat( | |||||
this.get_doctypes(txt), | |||||
this.get_reports(txt), | |||||
this.get_pages(txt), | |||||
this.get_modules(txt) | |||||
var options = frappe.search.utils.get_creatables(txt).concat( | |||||
frappe.search.utils.get_search_in_list(txt), | |||||
frappe.search.utils.get_doctypes(txt), | |||||
frappe.search.utils.get_reports(txt), | |||||
frappe.search.utils.get_pages(txt), | |||||
frappe.search.utils.get_modules(txt), | |||||
frappe.search.utils.get_recent_pages(txt || "") | |||||
); | ); | ||||
var out = this.deduplicate(options); | |||||
return out.sort(function(a, b) { | |||||
return b.index - a.index; | |||||
}); | |||||
}, | |||||
deduplicate: function(options) { | |||||
var out = [], routes = []; | |||||
options.forEach(function(option) { | |||||
if(option.route) { | |||||
if(option.route[0] === "List" && option.route[2]) { | |||||
option.route.splice(2); | |||||
} | |||||
var str_route = (typeof option.route==='string') ? | |||||
option.route : option.route.join('/'); | |||||
if(routes.indexOf(str_route)===-1) { | |||||
out.push(option); | |||||
routes.push(str_route); | |||||
} else { | |||||
var old = routes.indexOf(str_route); | |||||
if(out[old].index > option.index) { | |||||
out[old] = option; | |||||
} | |||||
} | |||||
} else { | |||||
out.push(option); | |||||
} | |||||
}); | |||||
return out; | |||||
}, | }, | ||||
set_global_results: function(global_results, txt){ | set_global_results: function(global_results, txt){ | ||||
@@ -357,11 +211,10 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
label: __("Search for '" + txt.bold() + "'"), | label: __("Search for '" + txt.bold() + "'"), | ||||
value: __("Search for '" + txt + "'"), | value: __("Search for '" + txt + "'"), | ||||
match: txt, | match: txt, | ||||
index: 1, | |||||
index: 100, | |||||
default: "Search", | default: "Search", | ||||
onclick: function() { | onclick: function() { | ||||
me.search.search_dialog.show(); | |||||
me.search.setup_search(txt, [me.nav, me.global, me.help]); | |||||
frappe.searchdialog.search.init_search(txt, "global_search"); | |||||
} | } | ||||
}); | }); | ||||
}, | }, | ||||
@@ -378,10 +231,10 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
label: __('Find {0} in {1}', [txt.bold(), route[1].bold()]), | label: __('Find {0} in {1}', [txt.bold(), route[1].bold()]), | ||||
value: __('Find {0} in {1}', [txt, route[1]]), | value: __('Find {0} in {1}', [txt, route[1]]), | ||||
route_options: options, | route_options: options, | ||||
index: 2, | |||||
onclick: function() { | onclick: function() { | ||||
cur_list.refresh(); | cur_list.refresh(); | ||||
}, | }, | ||||
index: 90, | |||||
default: "Current", | default: "Current", | ||||
match: txt | match: txt | ||||
}); | }); | ||||
@@ -401,7 +254,7 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
label: formatted_value, | label: formatted_value, | ||||
value: __('{0} = {1}', [txt, val]), | value: __('{0} = {1}', [txt, val]), | ||||
match: val, | match: val, | ||||
index: 3, | |||||
index: 80, | |||||
default: "Calculator", | default: "Calculator", | ||||
onclick: function() { | onclick: function() { | ||||
msgprint(formatted_value, "Result"); | msgprint(formatted_value, "Result"); | ||||
@@ -412,208 +265,4 @@ frappe.search.AwesomeBar = Class.extend({ | |||||
} | } | ||||
} | } | ||||
}, | }, | ||||
make_search_in_list: function(txt) { | |||||
var me = this; | |||||
var out = []; | |||||
if(in_list(txt.split(" "), "in") && (txt.slice(-2) !== "in")) { | |||||
parts = txt.split(" in "); | |||||
frappe.boot.user.can_read.forEach(function (item) { | |||||
var target = me.fuzzy_search(parts[1], item)[0]; | |||||
if(target) { | |||||
out.push({ | |||||
label: __('Find {0} in {1}', [__(parts[0]).bold(), __(target).bold()]), | |||||
value: __('Find {0} in {1}', [__(parts[0]), __(target)]), | |||||
route_options: {"name": ["like", "%" + parts[0] + "%"]}, | |||||
index: 4, | |||||
default: "In List", | |||||
route: ["List", item] | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
return out; | |||||
}, | |||||
make_new_doc: function(txt) { | |||||
var me = this; | |||||
var out = []; | |||||
if(txt.split(" ")[0]==="new") { | |||||
frappe.boot.user.can_create.forEach(function (item) { | |||||
var result = me.fuzzy_search(txt.substr(4), item); | |||||
var target = result[0]; | |||||
var index = result[1]; | |||||
var rendered_label = result[2]; | |||||
if(target) { | |||||
out.push({ | |||||
label: rendered_label, | |||||
value: __("New {0}", [target]), | |||||
index: index, | |||||
type: "New", | |||||
prefix: "New", | |||||
match: item, | |||||
onclick: function() { frappe.new_doc(item, true); } | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
return out; | |||||
}, | |||||
get_doctypes: function(txt) { | |||||
var me = this; | |||||
var out = []; | |||||
var result, target, index, rendered_label; | |||||
var option = function(type, route, order) { | |||||
return { | |||||
label: rendered_label, | |||||
value: __(target + " " + type), | |||||
route: route, | |||||
index: index + order, | |||||
match: target, | |||||
type: type | |||||
} | |||||
}; | |||||
frappe.boot.user.can_read.forEach(function (item) { | |||||
result = me.fuzzy_search(txt, item); | |||||
target = result[0]; | |||||
index = result[1]; | |||||
rendered_label = result[2]; | |||||
if(target) { | |||||
// include 'making new' option | |||||
if(in_list(frappe.boot.user.can_create, item)) { | |||||
var match = item; | |||||
out.push({ | |||||
label: rendered_label, | |||||
value: __("New {0}", [target]), | |||||
index: index + 0.4, | |||||
type: "New", | |||||
prefix: "New", | |||||
match: item, | |||||
onclick: function() { frappe.new_doc(match, true); } | |||||
}); | |||||
} | |||||
if(in_list(frappe.boot.single_types, target)) { | |||||
out.push(option("", ["Form", target, target], 0)); | |||||
} else if(in_list(frappe.boot.treeviews, target)) { | |||||
out.push(option("Tree", ["Tree", target], 0)); | |||||
} else { | |||||
out.push(option("List", ["List", target], 0)); | |||||
if(frappe.model.can_get_report(target)) { | |||||
out.push(option("Report", ["Report", target], 0.1)); | |||||
} | |||||
if(frappe.boot.calendars.indexOf(target) !== -1) { | |||||
out.push(option("Calendar", ["List", target, "Calendar"], 0.2)); | |||||
out.push(option("Gantt", ["List", target, "Gantt"], 0.3)); | |||||
} | |||||
} | |||||
} | |||||
}); | |||||
return out; | |||||
}, | |||||
get_reports: function(txt) { | |||||
var me = this; | |||||
var out = []; | |||||
Object.keys(frappe.boot.user.all_reports).forEach(function(item) { | |||||
var result = me.fuzzy_search(txt, item); | |||||
var target = result[0]; | |||||
var index = result[1]; | |||||
var rendered_label = result[2]; | |||||
if(target) { | |||||
var report = frappe.boot.user.all_reports[item]; | |||||
var route = []; | |||||
if(report.report_type == "Report Builder") | |||||
route = ["Report", report.ref_doctype, item]; | |||||
else | |||||
route = ["query-report", item]; | |||||
out.push({ | |||||
label: rendered_label, | |||||
value: __("Report {0}" , [__(target)]), | |||||
match: txt, | |||||
index: index, | |||||
type: "Report", | |||||
prefix: "Report", | |||||
route: route | |||||
}); | |||||
} | |||||
}); | |||||
return out; | |||||
}, | |||||
get_pages: function(txt) { | |||||
var me = this; | |||||
var out = []; | |||||
this.pages = {}; | |||||
$.each(frappe.boot.page_info, function(name, p) { | |||||
me.pages[p.title] = p; | |||||
p.name = name; | |||||
}); | |||||
Object.keys(this.pages).forEach(function(item) { | |||||
var result = me.fuzzy_search(txt, item); | |||||
var target = result[0]; | |||||
var index = result[1]; | |||||
var rendered_label = result[2]; | |||||
if(target) { | |||||
var page = me.pages[item]; | |||||
out.push({ | |||||
label: rendered_label, | |||||
value: __("Open {0}", [__(target)]), | |||||
match: txt, | |||||
index: index, | |||||
type: "Page", | |||||
prefix: "Open", | |||||
route: [page.route || page.name] | |||||
}); | |||||
} | |||||
}); | |||||
// calendar | |||||
var target = 'Calendar'; | |||||
if(__('calendar').indexOf(txt.toLowerCase()) === 0) { | |||||
out.push({ | |||||
label: __(target), | |||||
value: __("Open {0}", [__(target)]), | |||||
route: [target, 'Event'], | |||||
index: 5, | |||||
type: "Calendar", | |||||
prefix: "Open", | |||||
match: target | |||||
}); | |||||
} | |||||
return out; | |||||
}, | |||||
get_modules: function(txt) { | |||||
var me = this; | |||||
var out = []; | |||||
Object.keys(frappe.modules).forEach(function(item) { | |||||
var result = me.fuzzy_search(txt, item); | |||||
var target = result[0]; | |||||
var index = result[1]; | |||||
var rendered_label = result[2]; | |||||
if(target) { | |||||
var module = frappe.modules[item]; | |||||
if(module._doctype) return; | |||||
ret = { | |||||
label: rendered_label, | |||||
value: __("Open {0}", [__(target)]), | |||||
match: txt, | |||||
index: index, | |||||
type: "Module", | |||||
prefix: "Open" | |||||
} | |||||
if(module.link) { | |||||
ret.route = [module.link]; | |||||
} else { | |||||
ret.route = ["Module", item]; | |||||
} | |||||
out.push(ret); | |||||
} | |||||
}); | |||||
return out; | |||||
}, | |||||
}); | }); |
@@ -1,12 +1,6 @@ | |||||
<div class="row"> | |||||
<div class="col-md-2 col-sm-2 hidden-xs layout-side-section search-sidebar"> | |||||
</div> | |||||
<div class="col-md-10 col-sm-10 layout-main-section results-area"> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="row search-results"> | |||||
<div class="col-md-2 col-sm-2 hidden-xs layout-side-section"> | |||||
<ul class="module-sidebar-nav overlay-sidebar nav nav-pills nav-stacked search-sidebar"></ul> | |||||
</div> | |||||
<div class="col-md-10 col-sm-10 layout-main-section results-area"></div> | |||||
</div> |
@@ -1,7 +1,6 @@ | |||||
<div class="input-group has-feedback search-header" style="margin:5px -5px;"> | |||||
<span class="input-group-addon"> | |||||
<i class="octicon octicon-search"></i> | |||||
</span> | |||||
<input type="text" class="form-control search-input" placeholder="Search for ..."> | |||||
<span class="input-group-addon"><a type="button" class="close" data-dismiss="modal" aria-hidden="true">×</a></span> | |||||
<div class="search-header"> | |||||
<i class="octicon octicon-search"></i> | |||||
<input type="text" class="form-control search-input" style="padding: 15px"> | |||||
<p class="loading-state hide" style="margin: 0px 20px; color:#d4d9dd">{%= __("Searching")%} ...</p> | |||||
<a type="button" class="close" data-dismiss="modal" aria-hidden="true">×</a> | |||||
</div> | </div> |
@@ -0,0 +1,553 @@ | |||||
frappe.provide('frappe.search'); | |||||
frappe.search.utils = { | |||||
setup_recent: function() { | |||||
this.recent = JSON.parse(frappe.boot.user.recent || "[]") || []; | |||||
}, | |||||
get_recent_pages: function(keywords) { | |||||
var me = this; | |||||
values = [], options = []; | |||||
function find(list, keywords, process) { | |||||
list.forEach(function(item, i) { | |||||
_item = ($.isArray(item)) ? item[0] : item; | |||||
_item = __(_item || '').toLowerCase().replace(/-/g, " "); | |||||
if(keywords===_item || _item.indexOf(keywords) !== -1) { | |||||
var option = process(item); | |||||
if(option) { | |||||
if($.isPlainObject(option)) { | |||||
option = [option]; | |||||
} | |||||
option.forEach(function(o) { o.match = item; }); | |||||
options = option.concat(options); | |||||
} | |||||
} | |||||
}); | |||||
} | |||||
me.recent.forEach(function(doctype, i) { | |||||
values.push([doctype[1], ['Form', doctype[0], doctype[1]]]); | |||||
}); | |||||
values = values.reverse(); | |||||
frappe.route_history.forEach(function(route, i) { | |||||
if(route[0]==='Form') { | |||||
values.push([route[2], route]); | |||||
} | |||||
else if(in_list(['List', 'Report', 'Tree', 'modules', 'query-report'], route[0])) { | |||||
if(route[1]) { | |||||
values.push([route[1], route]); | |||||
} | |||||
} | |||||
else if(route[0]) { | |||||
values.push([frappe.route_titles[route[0]] || route[0], route]); | |||||
} | |||||
}); | |||||
find(values, keywords, function(match) { | |||||
out = { | |||||
route: match[1] | |||||
} | |||||
if(match[1][0]==='Form') { | |||||
if(match[1][1] !== match[1][2]) { | |||||
out.label = __(match[1][1]) + " " + match[1][2].bold(); | |||||
out.value = __(match[1][1]) + " " + match[1][2]; | |||||
} else { | |||||
out.label = __(match[1][1]).bold(); | |||||
out.value = __(match[1][1]); | |||||
} | |||||
} else if(in_list(['List', 'Report', 'Tree', 'modules', 'query-report'], match[1][0])) { | |||||
var type = match[1][0], label = type; | |||||
if(type==='modules') label = 'Module'; | |||||
else if(type==='query-report') label = 'Report'; | |||||
out.label = __(match[1][1]).bold() + " " + __(label); | |||||
out.value = __(match[1][1]) + " " + __(label); | |||||
} else { | |||||
out.label = match[0].bold(); | |||||
out.value = match[0]; | |||||
} | |||||
out.index = 80; | |||||
return out; | |||||
}); | |||||
return options; | |||||
}, | |||||
get_search_in_list: function(keywords) { | |||||
var me = this; | |||||
var out = []; | |||||
if(in_list(keywords.split(" "), "in") && (keywords.slice(-2) !== "in")) { | |||||
parts = keywords.split(" in "); | |||||
frappe.boot.user.can_read.forEach(function (item) { | |||||
if(frappe.boot.user.can_search.includes(item)) { | |||||
var level = me.fuzzy_search(parts[1], item); | |||||
if(level) { | |||||
out.push({ | |||||
type: "In List", | |||||
prefix: "Find '" + __(parts[0]).bold() + "' in ", | |||||
label: __(me.bolden_match_part(item, parts[1])), | |||||
value: __('Find {0} in {1}', [__(parts[0]), __(item)]), | |||||
route_options: {"name": ["like", "%" + parts[0] + "%"]}, | |||||
index: 1 + level, | |||||
route: ["List", item] | |||||
}); | |||||
} | |||||
} | |||||
}); | |||||
} | |||||
return out; | |||||
}, | |||||
get_creatables: function(keywords) { | |||||
var me = this; | |||||
var out = []; | |||||
if(keywords.split(" ")[0]==="new") { | |||||
frappe.boot.user.can_create.forEach(function (item) { | |||||
var level = me.fuzzy_search(keywords.substr(4), item); | |||||
if(level) { | |||||
out.push({ | |||||
type: "New", | |||||
prefix: "New ", | |||||
label: __(me.bolden_match_part(item, keywords.substr(4))), | |||||
value: __("New {0}", [item]), | |||||
index: 1 + level, | |||||
match: item, | |||||
onclick: function() { frappe.new_doc(item, true); } | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
return out; | |||||
}, | |||||
get_doctypes: function(keywords) { | |||||
var me = this; | |||||
var out = []; | |||||
var level, target; | |||||
var option = function(type, route, order) { | |||||
return { | |||||
type: type, | |||||
label: __("{0}" + " " + type, [__(me.bolden_match_part(target, keywords))]), | |||||
value: __(target + " " + type), | |||||
index: level + order, | |||||
match: target, | |||||
route: route, | |||||
} | |||||
}; | |||||
frappe.boot.user.can_read.forEach(function(item) { | |||||
if(frappe.boot.user.can_search.includes(item)) { | |||||
level = me.fuzzy_search(keywords, item); | |||||
if(level) { | |||||
target = item; | |||||
// include 'making new' option | |||||
if(in_list(frappe.boot.user.can_create, item)) { | |||||
var match = item; | |||||
out.push({ | |||||
type: "New", | |||||
label: __("New {0}", [__(me.bolden_match_part(item, keywords))]), | |||||
value: __("New {0}", [__(item)]), | |||||
index: level + 0.01, | |||||
match: item, | |||||
onclick: function() { frappe.new_doc(match, true); } | |||||
}); | |||||
} | |||||
if(in_list(frappe.boot.single_types, item)) { | |||||
out.push(option("", ["Form", item, item], 0.05)); | |||||
} else if(in_list(frappe.boot.treeviews, item)) { | |||||
out.push(option("Tree", ["Tree", item], 0.05)); | |||||
} else { | |||||
out.push(option("List", ["List", item], 0.05)); | |||||
if(frappe.model.can_get_report(item)) { | |||||
out.push(option("Report", ["Report", item], 0.04)); | |||||
} | |||||
if(frappe.boot.calendars.indexOf(item) !== -1) { | |||||
out.push(option("Calendar", ["List", item, "Calendar"], 0.03)); | |||||
out.push(option("Gantt", ["List", item, "Gantt"], 0.02)); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
}); | |||||
return out; | |||||
}, | |||||
get_reports: function(keywords) { | |||||
var me = this; | |||||
var out = []; | |||||
Object.keys(frappe.boot.user.all_reports).forEach(function(item) { | |||||
var level = me.fuzzy_search(keywords, item); | |||||
if(level > 0) { | |||||
var report = frappe.boot.user.all_reports[item]; | |||||
if(report.report_type == "Report Builder") | |||||
route = ["Report", report.ref_doctype, item]; | |||||
else | |||||
route = ["query-report", item]; | |||||
out.push({ | |||||
type: "Report", | |||||
prefix: "Report ", | |||||
label: __(me.bolden_match_part(item, keywords)), | |||||
value: __("Report {0}" , [item]), | |||||
index: level, | |||||
route: route | |||||
}); | |||||
} | |||||
}); | |||||
return out; | |||||
}, | |||||
get_pages: function(keywords) { | |||||
var me = this; | |||||
var out = []; | |||||
this.pages = {}; | |||||
$.each(frappe.boot.page_info, function(name, p) { | |||||
me.pages[p.title] = p; | |||||
p.name = name; | |||||
}); | |||||
Object.keys(this.pages).forEach(function(item) { | |||||
var level = me.fuzzy_search(keywords, item); | |||||
if(level) { | |||||
var page = me.pages[item]; | |||||
out.push({ | |||||
type: "Page", | |||||
prefix: "Open ", | |||||
label: __(me.bolden_match_part(item, keywords)), | |||||
value: __("Open {0}", [__(item)]), | |||||
match: item, | |||||
index: level, | |||||
route: [page.route || page.name] | |||||
}); | |||||
} | |||||
}); | |||||
var target = 'Calendar'; | |||||
if(__('calendar').indexOf(keywords.toLowerCase()) === 0) { | |||||
out.push({ | |||||
type: "Calendar", | |||||
prefix: "Open ", | |||||
label: __('Calendar'), | |||||
value: __("Open {0}", [__(target)]), | |||||
index: me.fuzzy_search(keywords, 'Calendar'), | |||||
match: target, | |||||
route: [target, 'Event'], | |||||
}); | |||||
} | |||||
return out; | |||||
}, | |||||
get_modules: function(keywords) { | |||||
var me = this; | |||||
var out = []; | |||||
Object.keys(frappe.modules).forEach(function(item) { | |||||
var level = me.fuzzy_search(keywords, item); | |||||
if(level > 0) { | |||||
var module = frappe.modules[item]; | |||||
if(module._doctype) return; | |||||
ret = { | |||||
type: "Module", | |||||
prefix: "Open ", | |||||
label: __(me.bolden_match_part(item, keywords)), | |||||
value: __("Open {0}", [__(item)]), | |||||
index: level, | |||||
} | |||||
if(module.link) { | |||||
ret.route = [module.link]; | |||||
} else { | |||||
ret.route = ["Module", item]; | |||||
} | |||||
out.push(ret); | |||||
} | |||||
}); | |||||
return out; | |||||
}, | |||||
get_global_results: function (keywords, start, limit, doctype = "") { | |||||
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) { | |||||
parts = content.split("|||"); | |||||
content_length = 300; | |||||
fields = []; | |||||
current_length = 0; | |||||
var field_text = ""; | |||||
for(var i = 0; i < parts.length; i++) { | |||||
part = parts[i]; | |||||
if(part.toLowerCase().indexOf(keywords) !== -1) { | |||||
if(part.indexOf('&&&') !== -1) { | |||||
var colon_index = part.indexOf('&&&'); | |||||
var field_value = part.slice(colon_index + 3); | |||||
} else { | |||||
var colon_index = part.indexOf(':'); | |||||
var field_value = part.slice(colon_index + 1); | |||||
} | |||||
var field_name = part.slice(0, colon_index); | |||||
var remaining_length = content_length - current_length; | |||||
current_length += field_name.length + field_value.length + 2; | |||||
if(current_length < content_length) { | |||||
field_text = '<span class="field-name text-muted">' + | |||||
me.bolden_match_part(field_name, keywords) + ':' + '</span>' + | |||||
me.bolden_match_part(field_value, keywords); | |||||
if(fields.indexOf(field_text) === -1 && doc_name !== field_value) { | |||||
fields.push(field_text); | |||||
} | |||||
} else { | |||||
if(field_name.length < remaining_length){ | |||||
remaining_length -= field_name.length; | |||||
field_text = '<span class="field-name text-muted">' + | |||||
me.bolden_match_part(field_name, keywords) + ':' + '</span>'; | |||||
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, keywords); | |||||
fields.push(field_text); | |||||
} else { | |||||
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], | |||||
} | |||||
if(d.image || d.image === null){ | |||||
result.image = d.image; | |||||
} | |||||
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_search.search", | |||||
args: { | |||||
text: keywords, | |||||
start: start, | |||||
limit: limit, | |||||
doctype: doctype | |||||
}, | |||||
callback: function(r) { | |||||
if(r.message) { | |||||
resolve(get_results_sets(r.message)); | |||||
} else { | |||||
resolve([]); | |||||
} | |||||
} | |||||
}); | |||||
}); | |||||
}, | |||||
get_help_results: function(keywords) { | |||||
function get_results_set(data) { | |||||
var result; | |||||
var set = { | |||||
title: "Help", | |||||
fetch_type: "Help", | |||||
results: [] | |||||
} | |||||
data.forEach(function(d) { | |||||
// more properties | |||||
result = { | |||||
label: d[0], | |||||
value: d[0], | |||||
description: d[1], | |||||
data_path: d[2], | |||||
onclick: function() { | |||||
} | |||||
} | |||||
set.results.push(result); | |||||
}); | |||||
return [set]; | |||||
} | |||||
return new Promise(function(resolve, reject) { | |||||
frappe.call({ | |||||
method: "frappe.utils.help.get_help", | |||||
args: { | |||||
text: keywords | |||||
}, | |||||
callback: function(r) { | |||||
if(r.message) { | |||||
resolve(get_results_set(r.message)); | |||||
} else { | |||||
resolve([]); | |||||
} | |||||
} | |||||
}); | |||||
}); | |||||
}, | |||||
get_nav_results: function(keywords) { | |||||
function sort_uniques(array) { | |||||
var routes = [], out = []; | |||||
array.forEach(function(d) { | |||||
if(d.route) { | |||||
if(d.route[0] === "List" && d.route[2]) { | |||||
d.route.splice(2); | |||||
} | |||||
var str_route = d.route.join('/'); | |||||
if(routes.indexOf(str_route) === -1) { | |||||
routes.push(str_route); | |||||
out.push(d); | |||||
} else { | |||||
var old = routes.indexOf(str_route); | |||||
if(out[old].index > d.index) { | |||||
out[old] = d; | |||||
} | |||||
} | |||||
} else { | |||||
out.push(d); | |||||
} | |||||
}); | |||||
return out.sort(function(a, b) { | |||||
return b.index - a.index; | |||||
}); | |||||
} | |||||
var lists = [], setup = []; | |||||
var all_doctypes = sort_uniques(this.get_doctypes(keywords)); | |||||
all_doctypes.forEach(function(d) { | |||||
if(d.type === "") { | |||||
setup.push(d); | |||||
} else { | |||||
lists.push(d); | |||||
} | |||||
}); | |||||
var in_keyword = keywords.split(" in ")[0]; | |||||
return [ | |||||
{title: "Recents", fetch_type: "Nav", results: sort_uniques(this.get_recent_pages(keywords))}, | |||||
{title: "Create a new ...", fetch_type: "Nav", results: sort_uniques(this.get_creatables(keywords))}, | |||||
{title: "Find '" + in_keyword + "' in ... ", fetch_type: "Nav", results: sort_uniques(this.get_search_in_list(keywords))}, | |||||
{title: "Lists", fetch_type: "Nav", results: lists}, | |||||
{title: "Reports", fetch_type: "Nav", results: sort_uniques(this.get_reports(keywords))}, | |||||
{title: "Administration", fetch_type: "Nav", results: sort_uniques(this.get_pages(keywords))}, | |||||
{title: "Modules", fetch_type: "Nav", results: sort_uniques(this.get_modules(keywords))}, | |||||
{title: "Setup", fetch_type: "Nav", results: setup}, | |||||
] | |||||
}, | |||||
fuzzy_search: function(keywords, _item) { | |||||
// Returns 10 for case-perfect contain, 0 for not found | |||||
// 9 for perfect contain, | |||||
// 0 - 6 for fuzzy contain | |||||
// **Specific use-case step** | |||||
item = __(_item || '').replace(/-/g, " "); | |||||
var ilen = item.length; | |||||
var klen = keywords.length; | |||||
var length_ratio = klen/ilen; | |||||
var max_skips = 3, max_mismatch_len = 2; | |||||
if (klen > ilen) { return 0; } | |||||
if(keywords === item || item.toLowerCase().indexOf(keywords) === 0) { | |||||
return 10 + length_ratio; | |||||
} | |||||
if (item.indexOf(keywords) !== -1 && keywords !== keywords.toLowerCase()) { | |||||
return 9 + length_ratio; | |||||
} | |||||
item = item.toLowerCase(); | |||||
keywords = keywords.toLowerCase(); | |||||
if (item.indexOf(keywords) !== -1) { | |||||
return 8 + length_ratio; | |||||
} | |||||
var skips = 0, mismatches = 0; | |||||
outer: for (var i = 0, j = 0; i < klen; i++) { | |||||
if(mismatches !== 0) skips++; | |||||
if(skips > max_skips) return 0; | |||||
var k_ch = keywords.charCodeAt(i); | |||||
mismatches = 0; | |||||
while (j < ilen) { | |||||
if (item.charCodeAt(j++) === k_ch) { | |||||
continue outer; | |||||
} | |||||
if(++mismatches > max_mismatch_len) return 0 ; | |||||
} | |||||
return 0; | |||||
} | |||||
// Since indexOf didn't pass, there will be atleast 1 skip | |||||
// hence no divide by zero, but just to be safe | |||||
if((skips + mismatches) > 0) { | |||||
return (5 + length_ratio)/(skips + mismatches); | |||||
} else { | |||||
return 0; | |||||
} | |||||
}, | |||||
bolden_match_part: function(str, subseq) { | |||||
var rendered = ""; | |||||
if(this.fuzzy_search(subseq, str) === 0) { | |||||
return str; | |||||
} else if(this.fuzzy_search(subseq, str) > 6) { | |||||
var regEx = new RegExp("("+ subseq +")", "ig"); | |||||
return str.replace(regEx, '<b>$1</b>'); | |||||
} else { | |||||
var str_orig = str; | |||||
var str = str.toLowerCase(); | |||||
var str_len = str.length; | |||||
var subseq = subseq.toLowerCase(); | |||||
outer: for(var i = 0, j = 0; i < subseq.length; i++) { | |||||
var sub_ch = subseq.charCodeAt(i); | |||||
while(j < str_len) { | |||||
if(str.charCodeAt(j) === sub_ch) { | |||||
var str_char = str_orig.charAt(j); | |||||
if(str_char === str_char.toLowerCase()) { | |||||
rendered += '<b>' + subseq.charAt(i) + '</b>'; | |||||
} else { | |||||
rendered += '<b>' + subseq.charAt(i).toUpperCase() + '</b>'; | |||||
} | |||||
j++; | |||||
continue outer; | |||||
} | |||||
rendered += str_orig.charAt(j); | |||||
j++; | |||||
} | |||||
return str_orig; | |||||
} | |||||
rendered += str_orig.slice(j); | |||||
return rendered; | |||||
} | |||||
}, | |||||
unscrub_and_titlecase: function(str) { | |||||
return __(str || '').replace(/-|_/g, " ").replace(/\w*/g, | |||||
function(keywords){return keywords.charAt(0).toUpperCase() + keywords.substr(1).toLowerCase();}); | |||||
}, | |||||
} |
@@ -12,12 +12,11 @@ frappe.ui.toolbar.Toolbar = Class.extend({ | |||||
this.setup_sidebar(); | this.setup_sidebar(); | ||||
this.awesome_bar = new frappe.search.AwesomeBar(); | |||||
this.awesome_bar.setup("#navbar-search"); | |||||
this.awesome_bar.setup("#modal-search"); | |||||
var awesome_bar = new frappe.search.AwesomeBar(); | |||||
awesome_bar.setup("#navbar-search"); | |||||
awesome_bar.setup("#modal-search"); | |||||
this.search = this.awesome_bar.search; | |||||
this.help = this.awesome_bar.help; | |||||
this.setup_help(); | |||||
$(document).on("notification-update", function() { | $(document).on("notification-update", function() { | ||||
frappe.ui.notifications.update_notifications(); | frappe.ui.notifications.update_notifications(); | ||||
@@ -25,8 +24,6 @@ frappe.ui.toolbar.Toolbar = Class.extend({ | |||||
$('.dropdown-toggle').dropdown(); | $('.dropdown-toggle').dropdown(); | ||||
this.setup_help(); | |||||
$(document).trigger('toolbar_setup'); | $(document).trigger('toolbar_setup'); | ||||
// clear all custom menus on page change | // clear all custom menus on page change | ||||
@@ -81,6 +78,12 @@ frappe.ui.toolbar.Toolbar = Class.extend({ | |||||
}, | }, | ||||
setup_help: function () { | setup_help: function () { | ||||
frappe.provide('frappe.help'); | |||||
frappe.help.show_results = show_results; | |||||
this.search = new frappe.search.SearchDialog(); | |||||
frappe.provide('frappe.searchdialog'); | |||||
frappe.searchdialog.search = this.search; | |||||
$(".dropdown-help .dropdown-toggle").on("click", function () { | $(".dropdown-help .dropdown-toggle").on("click", function () { | ||||
$(".dropdown-help input").focus(); | $(".dropdown-help input").focus(); | ||||
@@ -154,13 +157,9 @@ frappe.ui.toolbar.Toolbar = Class.extend({ | |||||
var me = this; | var me = this; | ||||
function show_help_results(keywords) { | function show_help_results(keywords) { | ||||
me.search.search_dialog.show(); | |||||
me.search.setup_search(keywords, [me.help]); | |||||
me.search.init_search(keywords, "help"); | |||||
} | } | ||||
frappe.provide('frappe.help'); | |||||
frappe.help.show_results = show_results; | |||||
function show_results(e) { | function show_results(e) { | ||||
//edit links | //edit links | ||||
href = e.target.href; | href = e.target.href; | ||||
@@ -550,14 +550,85 @@ textarea.form-control { | |||||
.search-dialog { | .search-dialog { | ||||
.modal-dialog { | .modal-dialog { | ||||
width: 768px; | width: 768px; | ||||
height: 500px; | |||||
} | |||||
.search-header { | |||||
display: flex; | |||||
align-items: center; | |||||
padding: 5px; | |||||
} | } | ||||
.modal-body { | .modal-body { | ||||
padding: 0px 15px; | padding: 0px 15px; | ||||
} | |||||
.empty-state { | |||||
color: #d4d9dd; | |||||
height: 500px; | |||||
display: flex; | |||||
justify-content: center; | |||||
align-items: center; | |||||
text-align: center; | |||||
.status-icon { | |||||
font-size: 40px; | |||||
position: relative; | |||||
margin-bottom: 10px; | |||||
} | |||||
p { | |||||
font-size: 15px; | |||||
display: block; | |||||
} | |||||
.cover { | |||||
color: white; | |||||
font-size: 6px; | |||||
position: absolute; | |||||
} | |||||
} | |||||
@keyframes twinkle { | |||||
0% { opacity:1; } | |||||
50% { opacity:0; } | |||||
100% { opacity:1; } | |||||
} | |||||
@-o-keyframes twinkle { | |||||
0% { opacity:1; } | |||||
50% { opacity:0; } | |||||
100% { opacity:1; } | |||||
} | |||||
@-moz-keyframes twinkle { | |||||
0% { opacity:1; } | |||||
50% { opacity:0; } | |||||
100% { opacity:1; } | |||||
} | |||||
@-webkit-keyframes twinkle { | |||||
0% { opacity:1; } | |||||
50% { opacity:0; } | |||||
100% { opacity:1; } | |||||
} | |||||
.twinkle-one { | |||||
-webkit-animation: twinkle 1.5s ease infinite; | |||||
-moz-animation: twinkle 1.5s ease infinite; | |||||
-o-animation: twinkle 1.5s ease infinite; | |||||
animation: twinkle 1.5s ease infinite; | |||||
} | |||||
.twinkle-two { | |||||
-webkit-animation: twinkle 1.5s ease infinite 0.5s; | |||||
-moz-animation: twinkle 1.5s ease infinite 0.5s; | |||||
-o-animation: twinkle 1.5s ease infinite 0.5s; | |||||
animation: twinkle 1.5s ease infinite 0.5s; | |||||
} | |||||
.twinkle-three { | |||||
-webkit-animation: twinkle 1.5s ease infinite 1s; | |||||
-moz-animation: twinkle 1.5s ease infinite 1s; | |||||
-o-animation: twinkle 1.5s ease infinite 1s; | |||||
animation: twinkle 1.5s ease infinite 1s; | |||||
} | } | ||||
input.form-control, .input-group-addon { | |||||
input.form-control { | |||||
border: none; | border: none; | ||||
border-left-style:none; | border-left-style:none; | ||||
} | } | ||||
@@ -567,10 +638,6 @@ textarea.form-control { | |||||
box-shadow: none; | box-shadow: none; | ||||
} | } | ||||
.input-group-addon{ | |||||
background-color: #FFF; | |||||
} | |||||
.layout-side-section, | .layout-side-section, | ||||
.layout-main-section { | .layout-main-section { | ||||
height: 500px; | height: 500px; | ||||
@@ -593,6 +660,7 @@ textarea.form-control { | |||||
align-items: center; | align-items: center; | ||||
justify-content: space-between; | justify-content: space-between; | ||||
padding-left: 20px; | padding-left: 20px; | ||||
background-color: #ffffff; | |||||
i { | i { | ||||
visibility: hidden; | visibility: hidden; | ||||
@@ -602,36 +670,14 @@ textarea.form-control { | |||||
.active i { | .active i { | ||||
visibility: visible; | visibility: visible; | ||||
} | } | ||||
} | |||||
} | |||||
.results-area { | |||||
.search-intro-placeholder { | |||||
color: #d4d9dd; | |||||
height: inherit; | |||||
display: flex; | |||||
justify-content: center; | |||||
align-items: center; | |||||
span { | |||||
text-align: center; | |||||
i { | |||||
font-size: 64px; | |||||
display: block; | |||||
} | |||||
p { | |||||
font-size: 15px; | |||||
display: block; | |||||
} | |||||
.select a, a:hover { | |||||
background-color: #f7fafc; | |||||
} | } | ||||
} | } | ||||
} | |||||
// a { | |||||
// color: #5E64FF; | |||||
// } | |||||
.results-area { | |||||
.single-link a { | .single-link a { | ||||
color: #36414c; | color: #36414c; | ||||
} | } | ||||
@@ -647,6 +693,10 @@ textarea.form-control { | |||||
font-family: 'Octicons'; | font-family: 'Octicons'; | ||||
content: '\f0a4'; | content: '\f0a4'; | ||||
} | } | ||||
.result { | |||||
margin-bottom: 5px; | |||||
} | |||||
} | } | ||||
.full-list { | .full-list { | ||||
@@ -684,6 +734,34 @@ textarea.form-control { | |||||
display: none; | display: none; | ||||
} | } | ||||
.result { | |||||
p { | |||||
margin-top: 5px; | |||||
margin-bottom: 5px; | |||||
} | |||||
.result-image { | |||||
display: inline-block; | |||||
margin-right: 10px; | |||||
height: 60px; | |||||
width: 60px; | |||||
background-color: #fafbfc; | |||||
.flex-text { | |||||
display: flex; | |||||
height: 60px; | |||||
align-items: center; | |||||
justify-content: center; | |||||
} | |||||
span { | |||||
font-size: 30px; | |||||
color: @text-extra-muted; | |||||
} | |||||
} | |||||
} | |||||
@media (max-width: @screen-xs) { | @media (max-width: @screen-xs) { | ||||
.modal-dialog { | .modal-dialog { | ||||
width: auto; | width: auto; | ||||
@@ -699,10 +777,6 @@ textarea.form-control { | |||||
margin: 0px; | margin: 0px; | ||||
border-top: none; | border-top: none; | ||||
} | } | ||||
.layout-side-section .sidebar-menu { | |||||
margin: 30px 0px; | |||||
} | |||||
} | } | ||||
@media (min-width: 600px) { | @media (min-width: 600px) { | ||||
@@ -712,16 +786,6 @@ textarea.form-control { | |||||
} | } | ||||
} | } | ||||
.result { | |||||
p { | |||||
margin-top: 0.2em; | |||||
} | |||||
} | |||||
.search-result { | |||||
margin-bottom: 24px; | |||||
} | |||||
.note-editor.note-frame .note-editing-area .note-editable { | .note-editor.note-frame .note-editing-area .note-editable { | ||||
color: @text-color; | color: @text-color; | ||||
} | } | ||||
@@ -514,6 +514,7 @@ | |||||
input[type=checkbox] { | input[type=checkbox] { | ||||
margin: 0; | margin: 0; | ||||
margin-right: 5px; | margin-right: 5px; | ||||
flex: 0 0 12px; | |||||
} | } | ||||
.liked-by, .liked-by-filter-button { | .liked-by, .liked-by-filter-button { | ||||
@@ -110,7 +110,7 @@ def delete_for_document(doc): | |||||
name = %s''', (doc.doctype, doc.name), as_dict=True) | name = %s''', (doc.doctype, doc.name), as_dict=True) | ||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def search(text, start=0, limit=20): | |||||
def search(text, start=0, limit=20, doctype=""): | |||||
'''Search for given text in __global_search | '''Search for given text in __global_search | ||||
:param text: phrase to be searched | :param text: phrase to be searched | ||||
:param start: start results at, default 0 | :param start: start results at, default 0 | ||||
@@ -118,14 +118,31 @@ def search(text, start=0, limit=20): | |||||
:return: Array of result objects''' | :return: Array of result objects''' | ||||
text = "+" + text + "*" | text = "+" + text + "*" | ||||
results = frappe.db.sql(''' | |||||
select | |||||
doctype, name, content | |||||
from | |||||
__global_search | |||||
where | |||||
match(content) against (%s IN BOOLEAN MODE) | |||||
limit {start}, {limit}'''.format(start=start, limit=limit), text, as_dict=True) | |||||
if not doctype: | |||||
results = frappe.db.sql(''' | |||||
select | |||||
doctype, name, content | |||||
from | |||||
__global_search | |||||
where | |||||
match(content) against (%s IN BOOLEAN MODE) | |||||
limit {start}, {limit}'''.format(start=start, limit=limit), text+"*", as_dict=True) | |||||
else: | |||||
results = frappe.db.sql(''' | |||||
select | |||||
doctype, name, content | |||||
from | |||||
__global_search | |||||
where | |||||
doctype = %s AND | |||||
match(content) against (%s IN BOOLEAN MODE) | |||||
limit {start}, {limit}'''.format(start=start, limit=limit), (doctype, text), as_dict=True) | |||||
for r in results: | |||||
if frappe.get_meta(r.doctype).image_field: | |||||
doc = frappe.get_doc(r.doctype, r.name) | |||||
r.image = doc.get(doc.meta.image_field) | |||||
return results | return results | ||||
@frappe.whitelist(allow_guest=True) | @frappe.whitelist(allow_guest=True) | ||||
@@ -148,45 +165,3 @@ def web_search(text, start=0, limit=20): | |||||
limit {start}, {limit}'''.format(start=start, limit=limit), | limit {start}, {limit}'''.format(start=start, limit=limit), | ||||
text, as_dict=True) | text, as_dict=True) | ||||
return results | return results | ||||
@frappe.whitelist() | |||||
def get_search_doctypes(text): | |||||
'''Search for all t | |||||
:param text: phrase to be searched | |||||
:return: Array of result objects''' | |||||
text = "+" + text + "*" | |||||
results = frappe.db.sql(''' | |||||
select | |||||
doctype | |||||
from | |||||
__global_search | |||||
where | |||||
match(content) against (%s IN BOOLEAN MODE) | |||||
group by | |||||
doctype | |||||
order by | |||||
count(doctype) desc limit 0, 80''', text, as_dict=True) | |||||
return results | |||||
@frappe.whitelist() | |||||
def search_in_doctype(doctype, text, start, limit): | |||||
'''Search for given text in given doctype in __global_search | |||||
:param doctype: doctype to be searched in | |||||
:param text: phrase to be searched | |||||
:param start: start results at, default 0 | |||||
:param limit: number of results to return, default 20 | |||||
:return: Array of result objects''' | |||||
text = "+" + text + "*" | |||||
results = frappe.db.sql(''' | |||||
select | |||||
doctype, name, content | |||||
from | |||||
__global_search | |||||
where | |||||
doctype = %s AND | |||||
match(content) against (%s IN BOOLEAN MODE) | |||||
limit {start}, {limit}'''.format(start=start, limit=limit), (doctype, text), as_dict=True) | |||||
return results |