Browse Source

Merge branch 'develop'

version-14
Nabin Hait 8 years ago
parent
commit
503c9dda0d
17 changed files with 1205 additions and 1361 deletions
  1. +1
    -1
      frappe/__init__.py
  2. +12
    -8
      frappe/custom/doctype/custom_script/custom_script.json
  3. +1
    -1
      frappe/geo/country_info.json
  4. +4
    -3
      frappe/public/build.json
  5. +118
    -32
      frappe/public/css/desk.css
  6. +1
    -0
      frappe/public/css/list.css
  7. +1
    -1
      frappe/public/js/frappe/list/list_item_subject.html
  8. +0
    -3
      frappe/public/js/frappe/list/list_renderer.js
  9. +62
    -413
      frappe/public/js/frappe/ui/toolbar/awesome_bar.js
  10. +6
    -12
      frappe/public/js/frappe/ui/toolbar/search.html
  11. +293
    -772
      frappe/public/js/frappe/ui/toolbar/search.js
  12. +5
    -6
      frappe/public/js/frappe/ui/toolbar/search_header.html
  13. +553
    -0
      frappe/public/js/frappe/ui/toolbar/search_utils.js
  14. +11
    -12
      frappe/public/js/frappe/ui/toolbar/toolbar.js
  15. +110
    -46
      frappe/public/less/desk.less
  16. +1
    -0
      frappe/public/less/list.less
  17. +26
    -51
      frappe/utils/global_search.py

+ 1
- 1
frappe/__init__.py View File

@@ -13,7 +13,7 @@ import os, sys, importlib, inspect, json
from .exceptions import *
from .utils.jinja import get_jenv, get_template, render_template

__version__ = '8.0.5'
__version__ = '8.0.6'
__title__ = "Frappe Framework"

local = Local()


+ 12
- 8
frappe/custom/doctype/custom_script/custom_script.json View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "CustomScript.####",
@@ -24,6 +25,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "DocType",
@@ -55,6 +57,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Script Type",
@@ -84,8 +87,9 @@
"hidden": 0,
"ignore_user_permissions": 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,
"label": "Script",
"length": 0,
@@ -115,6 +119,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sample",
@@ -133,19 +138,19 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-glass",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 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",
"name": "Custom Script",
"owner": "Administrator",
@@ -160,7 +165,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -181,7 +185,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -196,7 +199,8 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_order": "ASC",
"track_changes": 1,
"track_seen": 0
}
}

+ 1
- 1
frappe/geo/country_info.json View File

@@ -846,7 +846,7 @@
"currency_fraction": "Cent",
"currency_fraction_units": 100,
"currency_symbol": "\u20ac",
"number_format": "#,###.##",
"number_format": "# ###,##",
"timezones": [
"Europe/Paris"
]


+ 4
- 3
frappe/public/build.json View File

@@ -139,6 +139,7 @@
"public/js/frappe/ui/toolbar/search.js",
"public/js/frappe/ui/toolbar/search.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/navbar.html",
"public/js/frappe/ui/toolbar/toolbar.js",
@@ -213,7 +214,7 @@
],
"js/list.min.js": [
"public/js/frappe/ui/listing.html",
"public/js/frappe/ui/base_list.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_subject.html",
"public/js/frappe/list/list_permission_footer.html",
"public/js/frappe/list/list_renderer.js",
"public/js/frappe/views/gantt/gantt_view.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_view_item_row.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_column.html",
"public/js/frappe/views/kanban/kanban_card.html"


+ 118
- 32
frappe/public/css/desk.css View File

@@ -677,13 +677,100 @@ fieldset[disabled] .form-control {
}
.search-dialog .modal-dialog {
width: 768px;
height: 500px;
}
.search-dialog .search-header {
display: flex;
align-items: center;
padding: 5px;
}
.search-dialog .modal-body {
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-left-style: none;
}
@@ -691,9 +778,6 @@ fieldset[disabled] .form-control {
outline: none;
box-shadow: none;
}
.search-dialog .input-group-addon {
background-color: #FFF;
}
.search-dialog .layout-side-section,
.search-dialog .layout-main-section {
height: 500px;
@@ -712,6 +796,7 @@ fieldset[disabled] .form-control {
align-items: center;
justify-content: space-between;
padding-left: 20px;
background-color: #ffffff;
}
.search-dialog .layout-side-section .nav li a i {
visibility: hidden;
@@ -719,23 +804,9 @@ fieldset[disabled] .form-control {
.search-dialog .layout-side-section .nav .active i {
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 {
color: #36414c;
@@ -748,6 +819,9 @@ fieldset[disabled] .form-control {
font-family: 'Octicons';
content: '\f0a4';
}
.search-dialog .module-section .result {
margin-bottom: 5px;
}
.search-dialog .full-list .result {
margin-top: 15px;
}
@@ -772,6 +846,27 @@ fieldset[disabled] .form-control {
.search-dialog .more-results {
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) {
.search-dialog .modal-dialog {
width: auto;
@@ -785,21 +880,12 @@ fieldset[disabled] .form-control {
margin: 0px;
border-top: none;
}
.search-dialog .layout-side-section .sidebar-menu {
margin: 30px 0px;
}
}
@media (min-width: 600px) {
.search-dialog .results-area .back-link {
display: none;
}
}
.result p {
margin-top: 0.2em;
}
.search-result {
margin-bottom: 24px;
}
.note-editor.note-frame .note-editing-area .note-editable {
color: #36414C;
}


+ 1
- 0
frappe/public/css/list.css View File

@@ -420,6 +420,7 @@
.list-item input[type=checkbox] {
margin: 0;
margin-right: 5px;
flex: 0 0 12px;
}
.list-item .liked-by,
.list-item .liked-by-filter-button {


+ 1
- 1
frappe/public/js/frappe/list/list_item_subject.html View File

@@ -11,7 +11,7 @@
</i>
<span class="likes-count">{{ (_liked_by.length > 99 ? "99+" : _liked_by.length) || "" }}</span>
</span>
<a class="grey list-id {{ css_seen }}"
<a class="grey list-id {{ css_seen }} ellipsis"
data-name="{{ _name }}"
href="#Form/{{ _doctype_encoded }}/{{ _name_encoded }}"
title="{{ _full_title }}">{{ strip_html(_title) }}</a>


+ 0
- 3
frappe/public/js/frappe/list/list_renderer.js View File

@@ -447,9 +447,6 @@ frappe.views.ListRenderer = Class.extend({
data._title = strip_html(data[title_field] || data.name);
data._full_title = data._title;

if (data._title.length > 35) {
data._title = data._title.slice(0, 35) + '...';
}

data._workflow = null;
if (this.workflow_state_fieldname) {


+ 62
- 413
frappe/public/js/frappe/ui/toolbar/awesome_bar.js View File

@@ -9,11 +9,6 @@ frappe.search.AwesomeBar = Class.extend({
var $input = $(element);
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.global_results = [];

@@ -23,32 +18,22 @@ frappe.search.AwesomeBar = Class.extend({
autoFirst: true,
list: [],
filter: function (text, term) {
this.get_item(text.value).boo = "foo";
return true;
},
data: function (item, input) {
var label = item.label + "%%%" + item.value + "%%%" +
(item.description || "") + "%%%" + (item.index || "")
+ "%%%" + (item.type || "") + "%%%" + (item.prefix || "");
return {
label: label,
label: (item.index || ""),
value: item.value
};
},
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) {
html += '<br><span class="text-muted">' + __(d.description) + '</span>';
html += '<br><span class="text-muted ellipsis">' + __(d.description) + '</span>';
}
return $('<li></li>')
.data('item.autocomplete', d)
@@ -56,9 +41,7 @@ frappe.search.AwesomeBar = Class.extend({
.get(0);
},
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 last_space = txt.lastIndexOf(' ');
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);
clearTimeout($this.data('timeout'));

$this.data('timeout', setTimeout(function(){
me.options = [];
if(txt && txt.length > 2) {
if(txt && txt.length > 1) {
if(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.build_defaults(txt);
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();

// 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));

});
@@ -153,20 +112,20 @@ frappe.search.AwesomeBar = Class.extend({
frappe.route();
}
}
$input.val("");
});

$input.on("awesomplete-selectcomplete", function(e) {
$input.val("");
});
this.setup_recent();
this.search.setup();
frappe.search.utils.setup_recent();
},

add_help: function() {
this.options.push({
label: __("Help on Search"),
value: "Help on Search",
index: 100,
index: -10,
default: "Help",
onclick: function() {
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) {
var me = this;
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_search_in_current(txt);
this.options = this.options.concat(this.make_search_in_list(txt));
this.make_calculator(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){
@@ -357,11 +211,10 @@ frappe.search.AwesomeBar = Class.extend({
label: __("Search for '" + txt.bold() + "'"),
value: __("Search for '" + txt + "'"),
match: txt,
index: 1,
index: 100,
default: "Search",
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()]),
value: __('Find {0} in {1}', [txt, route[1]]),
route_options: options,
index: 2,
onclick: function() {
cur_list.refresh();
},
index: 90,
default: "Current",
match: txt
});
@@ -401,7 +254,7 @@ frappe.search.AwesomeBar = Class.extend({
label: formatted_value,
value: __('{0} = {1}', [txt, val]),
match: val,
index: 3,
index: 80,
default: "Calculator",
onclick: function() {
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;
},
});

+ 6
- 12
frappe/public/js/frappe/ui/toolbar/search.html View File

@@ -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>

+ 293
- 772
frappe/public/js/frappe/ui/toolbar/search.js
File diff suppressed because it is too large
View File


+ 5
- 6
frappe/public/js/frappe/ui/toolbar/search_header.html View File

@@ -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">&times;</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")%}&nbsp...</p>
<a type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</a>
</div>

+ 553
- 0
frappe/public/js/frappe/ui/toolbar/search_utils.js View File

@@ -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();});
},
}

+ 11
- 12
frappe/public/js/frappe/ui/toolbar/toolbar.js View File

@@ -12,12 +12,11 @@ frappe.ui.toolbar.Toolbar = Class.extend({

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() {
frappe.ui.notifications.update_notifications();
@@ -25,8 +24,6 @@ frappe.ui.toolbar.Toolbar = Class.extend({

$('.dropdown-toggle').dropdown();

this.setup_help();

$(document).trigger('toolbar_setup');

// clear all custom menus on page change
@@ -81,6 +78,12 @@ frappe.ui.toolbar.Toolbar = Class.extend({
},

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 input").focus();
@@ -154,13 +157,9 @@ frappe.ui.toolbar.Toolbar = Class.extend({

var me = this;
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) {
//edit links
href = e.target.href;


+ 110
- 46
frappe/public/less/desk.less View File

@@ -550,14 +550,85 @@ textarea.form-control {
.search-dialog {
.modal-dialog {
width: 768px;
height: 500px;
}

.search-header {
display: flex;
align-items: center;
padding: 5px;
}

.modal-body {
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-left-style:none;
}
@@ -567,10 +638,6 @@ textarea.form-control {
box-shadow: none;
}

.input-group-addon{
background-color: #FFF;
}

.layout-side-section,
.layout-main-section {
height: 500px;
@@ -593,6 +660,7 @@ textarea.form-control {
align-items: center;
justify-content: space-between;
padding-left: 20px;
background-color: #ffffff;

i {
visibility: hidden;
@@ -602,36 +670,14 @@ textarea.form-control {
.active i {
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 {
color: #36414c;
}
@@ -647,6 +693,10 @@ textarea.form-control {
font-family: 'Octicons';
content: '\f0a4';
}

.result {
margin-bottom: 5px;
}
}

.full-list {
@@ -684,6 +734,34 @@ textarea.form-control {
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) {
.modal-dialog {
width: auto;
@@ -699,10 +777,6 @@ textarea.form-control {
margin: 0px;
border-top: none;
}

.layout-side-section .sidebar-menu {
margin: 30px 0px;
}
}

@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 {
color: @text-color;
}


+ 1
- 0
frappe/public/less/list.less View File

@@ -514,6 +514,7 @@
input[type=checkbox] {
margin: 0;
margin-right: 5px;
flex: 0 0 12px;
}

.liked-by, .liked-by-filter-button {


+ 26
- 51
frappe/utils/global_search.py View File

@@ -110,7 +110,7 @@ def delete_for_document(doc):
name = %s''', (doc.doctype, doc.name), as_dict=True)

@frappe.whitelist()
def search(text, start=0, limit=20):
def search(text, start=0, limit=20, doctype=""):
'''Search for given text in __global_search
:param text: phrase to be searched
:param start: start results at, default 0
@@ -118,14 +118,31 @@ def search(text, start=0, limit=20):
:return: Array of result objects'''

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

@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),
text, as_dict=True)
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

Loading…
Cancel
Save