Merge branch 'develop'
This commit is contained in:
commit
53cfd2bba2
28 changed files with 163 additions and 94 deletions
|
|
@ -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.26'
|
||||
__version__ = '8.0.27'
|
||||
__title__ = "Frappe Framework"
|
||||
|
||||
local = Local()
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ from werkzeug.local import LocalManager
|
|||
from werkzeug.exceptions import HTTPException, NotFound
|
||||
from werkzeug.contrib.profiler import ProfilerMiddleware
|
||||
from werkzeug.wsgi import SharedDataMiddleware
|
||||
from werkzeug.serving import run_with_reloader
|
||||
|
||||
import frappe
|
||||
import frappe.handler
|
||||
|
|
@ -20,7 +19,7 @@ import frappe.api
|
|||
import frappe.async
|
||||
import frappe.utils.response
|
||||
import frappe.website.render
|
||||
from frappe.utils import get_site_name, get_site_path
|
||||
from frappe.utils import get_site_name
|
||||
from frappe.middlewares import StaticDataMiddleware
|
||||
from frappe.utils.error import make_error_snapshot
|
||||
from frappe.core.doctype.communication.comment import update_comments_in_parent_after_request
|
||||
|
|
@ -222,5 +221,9 @@ def serve(port=8000, profile=False, site=None, sites_path='.'):
|
|||
'SERVER_NAME': 'localhost:8000'
|
||||
}
|
||||
|
||||
run_simple('0.0.0.0', int(port), application, use_reloader=True,
|
||||
use_debugger=True, use_evalex=True, threaded=True)
|
||||
in_test_env = os.environ.get('CI')
|
||||
run_simple('0.0.0.0', int(port), application,
|
||||
use_reloader=not in_test_env,
|
||||
use_debugger=not in_test_env,
|
||||
use_evalex=not in_test_env,
|
||||
threaded=True)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals, absolute_import
|
|||
import click
|
||||
import hashlib, os, sys
|
||||
import frappe
|
||||
from _mysql_exceptions import ProgrammingError
|
||||
from frappe.commands import pass_context, get_site
|
||||
from frappe.commands.scheduler import _is_scheduler_enabled
|
||||
from frappe.limits import update_limits, get_limits
|
||||
|
|
@ -323,7 +324,8 @@ def uninstall(context, app, dry_run=False, yes=False):
|
|||
@click.option('--root-login', default='root')
|
||||
@click.option('--root-password')
|
||||
@click.option('--archived-sites-path')
|
||||
def drop_site(site, root_login='root', root_password=None, archived_sites_path=None):
|
||||
@click.option('--force', help='Force drop-site even if an error is encountered', is_flag=True, default=False)
|
||||
def drop_site(site, root_login='root', root_password=None, archived_sites_path=None, force=False):
|
||||
"Remove site from database and filesystem"
|
||||
from frappe.installer import get_root_connection
|
||||
from frappe.model.db_schema import DbManager
|
||||
|
|
@ -331,7 +333,22 @@ def drop_site(site, root_login='root', root_password=None, archived_sites_path=N
|
|||
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
scheduled_backup(ignore_files=False, force=True)
|
||||
|
||||
try:
|
||||
scheduled_backup(ignore_files=False, force=True)
|
||||
except ProgrammingError as err:
|
||||
if err[0] == 1146:
|
||||
if force:
|
||||
pass
|
||||
else:
|
||||
click.echo("="*80)
|
||||
click.echo("Error: The operation has stopped because backup of {s}'s database failed.".format(s=site))
|
||||
click.echo("Reason: {reason}{sep}".format(reason=err[1], sep="\n"))
|
||||
click.echo("Fix the issue and try again.")
|
||||
click.echo(
|
||||
"Hint: Use 'bench drop-site {s} --force' to force the removal of {s}".format(sep="\n", tab="\t", s=site)
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
db_name = frappe.local.conf.db_name
|
||||
frappe.local.db = get_root_connection(root_login, root_password)
|
||||
|
|
|
|||
|
|
@ -28,9 +28,10 @@
|
|||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<span class="indicator green" style="margin-right: 20px;">Started</span>
|
||||
<span class="indicator blue" style="margin-right: 20px;">Started</span>
|
||||
<span class="indicator orange" style="margin-right: 20px;">Queued</span>
|
||||
<span class="indicator red">Failed</span>
|
||||
<span class="indicator red" style="margin-right: 20px;">Failed</span>
|
||||
<span class="indicator green">Finished</span>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="text-muted">No pending or current jobs for this site</p>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ from frappe.utils import format_datetime, cint
|
|||
colors = {
|
||||
'queued': 'orange',
|
||||
'failed': 'red',
|
||||
'started': 'green'
|
||||
'started': 'blue',
|
||||
'finished': 'green'
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
|
|||
|
|
@ -66,10 +66,7 @@ def add(args=None):
|
|||
notify_assignment(d.assigned_by, d.owner, d.reference_type, d.reference_name, action='ASSIGN',\
|
||||
description=args.get("description"), notify=args.get('notify'))
|
||||
|
||||
if not args.get("bulk_assign"):
|
||||
return get(args)
|
||||
else:
|
||||
return {}
|
||||
return get(args)
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_multiple(args=None):
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class AutoEmailReport(Document):
|
|||
count = frappe.db.sql('select count(*) from `tabAuto Email Report` where user=%s and enabled=1', self.user)[0][0]
|
||||
if count > max_reports_per_user + (-1 if self.flags.in_insert else 0):
|
||||
frappe.throw(_('Only {0} emailed reports are allowed per user').format(max_reports_per_user))
|
||||
|
||||
|
||||
def validate_report_format(self):
|
||||
""" check if user has select correct report format """
|
||||
valid_report_formats = ["HTML", "XLS", "CSV"]
|
||||
|
|
@ -59,6 +59,11 @@ class AutoEmailReport(Document):
|
|||
columns, data = report.get_data(limit=self.no_of_rows or 100, user = self.user,
|
||||
filters = self.filters, as_dict=True)
|
||||
|
||||
# add serial numbers
|
||||
columns.insert(0, frappe._dict(fieldname='idx', label='', width='30px'))
|
||||
for i in range(len(data)):
|
||||
data[i]['idx'] = i+1
|
||||
|
||||
if len(data)==0 and self.send_if_data:
|
||||
return None
|
||||
|
||||
|
|
@ -77,7 +82,7 @@ class AutoEmailReport(Document):
|
|||
def get_html_table(self, columns, data):
|
||||
return frappe.render_template('frappe/templates/includes/print_table.html', {
|
||||
'columns': columns,
|
||||
'data': data[1:]
|
||||
'data': data
|
||||
})
|
||||
|
||||
def get_csv(self, columns, data):
|
||||
|
|
@ -96,7 +101,7 @@ class AutoEmailReport(Document):
|
|||
def send(self):
|
||||
if self.filter_meta and not self.filters:
|
||||
frappe.throw(_("Please set filters value in Report Filter table."))
|
||||
|
||||
|
||||
data = self.get_report_content()
|
||||
if not data:
|
||||
return
|
||||
|
|
@ -135,7 +140,7 @@ class AutoEmailReport(Document):
|
|||
def get_report_footer(self):
|
||||
return """<hr style="margin: 30px 0px 15px 0px;">
|
||||
<p style="font-size: 9px;">
|
||||
View report in your browser:
|
||||
View report in your browser:
|
||||
<a href= {{report_url}} target="_blank">{{report_name}}</a><br><br>
|
||||
Edit Auto Email Report Settings: {{edit_report_settings}}
|
||||
</p>"""
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class EmailAccount(Document):
|
|||
|
||||
if (not self.awaiting_password and not frappe.local.flags.in_install
|
||||
and not frappe.local.flags.in_patch):
|
||||
if self.password:
|
||||
if self.password or self.smtp_server in ('127.0.0.1', 'localhost'):
|
||||
if self.enable_incoming:
|
||||
self.get_incoming_server()
|
||||
self.no_failed = 0
|
||||
|
|
@ -331,7 +331,7 @@ class EmailAccount(Document):
|
|||
raise SentEmailInInbox
|
||||
|
||||
if email.message_id:
|
||||
names = frappe.db.sql("""select distinct name from tabCommunication
|
||||
names = frappe.db.sql("""select distinct name from tabCommunication
|
||||
where message_id='{message_id}'
|
||||
order by creation desc limit 1""".format(
|
||||
message_id=email.message_id
|
||||
|
|
@ -483,7 +483,7 @@ class EmailAccount(Document):
|
|||
parent = frappe.new_doc(self.append_to)
|
||||
|
||||
if self.subject_field:
|
||||
parent.set(self.subject_field, frappe.as_unicode(email.subject))
|
||||
parent.set(self.subject_field, frappe.as_unicode(email.subject)[:140])
|
||||
|
||||
if self.sender_field:
|
||||
parent.set(self.sender_field, frappe.as_unicode(email.from_email))
|
||||
|
|
@ -591,7 +591,7 @@ class EmailAccount(Document):
|
|||
if not self.use_imap:
|
||||
return
|
||||
|
||||
flags = frappe.db.sql("""select name, communication, uid, action from
|
||||
flags = frappe.db.sql("""select name, communication, uid, action from
|
||||
`tabEmail Flag Queue` where is_completed=0 and email_account='{email_account}'
|
||||
""".format(email_account=self.name), as_dict=True)
|
||||
|
||||
|
|
@ -614,7 +614,7 @@ class EmailAccount(Document):
|
|||
self.set_communication_seen_status(docnames, seen=0)
|
||||
|
||||
docnames = ",".join([ "'%s'"%flag.get("name") for flag in flags ])
|
||||
frappe.db.sql(""" update `tabEmail Flag Queue` set is_completed=1
|
||||
frappe.db.sql(""" update `tabEmail Flag Queue` set is_completed=1
|
||||
where name in ({docnames})""".format(docnames=docnames))
|
||||
|
||||
def set_communication_seen_status(self, docnames, seen=0):
|
||||
|
|
@ -622,7 +622,7 @@ class EmailAccount(Document):
|
|||
if not docnames:
|
||||
return
|
||||
|
||||
frappe.db.sql(""" update `tabCommunication` set seen={seen}
|
||||
frappe.db.sql(""" update `tabCommunication` set seen={seen}
|
||||
where name in ({docnames})""".format(docnames=docnames, seen=seen))
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
@ -716,4 +716,4 @@ def get_max_email_uid(email_account):
|
|||
return 1
|
||||
else:
|
||||
max_uid = int(result[0].get("uid", 0)) + 1
|
||||
return max_uid
|
||||
return max_uid
|
||||
|
|
|
|||
|
|
@ -112,6 +112,10 @@ class EmailServer:
|
|||
self.uid_reindexed = False
|
||||
|
||||
uid_list = email_list = self.get_new_mails()
|
||||
|
||||
if not email_list:
|
||||
return
|
||||
|
||||
num = num_copy = len(email_list)
|
||||
|
||||
# WARNING: Hard coded max no. of messages to be popped
|
||||
|
|
@ -166,11 +170,13 @@ class EmailServer:
|
|||
def get_new_mails(self):
|
||||
"""Return list of new mails"""
|
||||
if cint(self.settings.use_imap):
|
||||
email_list = []
|
||||
self.check_imap_uidvalidity()
|
||||
|
||||
self.imap.select("Inbox", readonly=True)
|
||||
response, message = self.imap.uid('search', None, self.settings.email_sync_rule)
|
||||
email_list = message[0].split()
|
||||
if message[0]:
|
||||
email_list = message[0].split()
|
||||
else:
|
||||
email_list = self.pop.list()[1]
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,10 @@ def get_outgoing_email_account(raise_exception_not_set=True, append_to=None):
|
|||
|
||||
if email_account:
|
||||
if email_account.enable_outgoing and not getattr(email_account, 'from_site_config', False):
|
||||
email_account.password = email_account.get_password()
|
||||
raise_exception = True
|
||||
if email_account.smtp_server in ['localhost','127.0.0.1']:
|
||||
raise_exception = False
|
||||
email_account.password = email_account.get_password(raise_exception=raise_exception)
|
||||
email_account.default_sender = email.utils.formataddr((email_account.name, email_account.get("email_id")))
|
||||
|
||||
frappe.local.outgoing_email_account[append_to or "default"] = email_account
|
||||
|
|
|
|||
|
|
@ -10,13 +10,23 @@ import frappe.sessions
|
|||
import frappe.utils.file_manager
|
||||
import frappe.desk.form.run_method
|
||||
from frappe.utils.response import build_response
|
||||
from werkzeug.wrappers import Response
|
||||
|
||||
def handle():
|
||||
"""handle request"""
|
||||
cmd = frappe.local.form_dict.cmd
|
||||
data = None
|
||||
|
||||
if cmd!='login':
|
||||
execute_cmd(cmd)
|
||||
data = execute_cmd(cmd)
|
||||
|
||||
if data:
|
||||
if isinstance(data, Response):
|
||||
# method returns a response object, pass it on
|
||||
return data
|
||||
|
||||
# add the response to `message` label
|
||||
frappe.response['message'] = data
|
||||
|
||||
return build_response("json")
|
||||
|
||||
|
|
@ -39,11 +49,8 @@ def execute_cmd(cmd, from_async=False):
|
|||
|
||||
is_whitelisted(method)
|
||||
|
||||
ret = frappe.call(method, **frappe.form_dict)
|
||||
return frappe.call(method, **frappe.form_dict)
|
||||
|
||||
# returns with a message
|
||||
if ret:
|
||||
frappe.response['message'] = ret
|
||||
|
||||
def is_whitelisted(method):
|
||||
# check if whitelisted
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class DatabaseQuery(object):
|
|||
self.user = user or frappe.session.user
|
||||
self.update = update
|
||||
self.user_settings_fields = copy.deepcopy(self.fields)
|
||||
# self.debug = True
|
||||
#self.debug = True
|
||||
|
||||
if user_settings:
|
||||
self.user_settings = json.loads(user_settings)
|
||||
|
|
@ -300,8 +300,8 @@ class DatabaseQuery(object):
|
|||
if f.operator.lower() == 'between' and \
|
||||
(f.fieldname in ('creation', 'modified') or (df and (df.fieldtype=="Date" or df.fieldtype=="Datetime"))):
|
||||
value = "'%s' AND '%s'" % (
|
||||
get_datetime(f.value[0]).strftime("%Y-%m-%d %H:%M:%S.%f"),
|
||||
add_to_date(get_datetime(f.value[1]),days=1).strftime("%Y-%m-%d %H:%M:%S.%f"))
|
||||
add_to_date(get_datetime(f.value[0]),days=-1).strftime("%Y-%m-%d %H:%M:%S.%f"),
|
||||
get_datetime(f.value[1]).strftime("%Y-%m-%d %H:%M:%S.%f"))
|
||||
fallback = "'0000-00-00 00:00:00'"
|
||||
|
||||
elif df and df.fieldtype=="Date":
|
||||
|
|
|
|||
|
|
@ -3,9 +3,24 @@
|
|||
|
||||
import frappe
|
||||
|
||||
# select doctypes that are accessed by the user (not read_only) first, so that the
|
||||
# the validation message shows the user-facing doctype first.
|
||||
# For example Journal Entry should be validated before GL Entry (which is an internal doctype)
|
||||
|
||||
dynamic_link_queries = [
|
||||
"""select parent, fieldname, options from tabDocField where fieldtype='Dynamic Link'""",
|
||||
"""select dt as parent, fieldname, options from `tabCustom Field` where fieldtype='Dynamic Link'""",
|
||||
"""select parent,
|
||||
(select read_only from `tabDocType` where name=tabDocField.parent) as read_only,
|
||||
fieldname, options
|
||||
from tabDocField
|
||||
where fieldtype='Dynamic Link'
|
||||
order by read_only""",
|
||||
|
||||
"""select dt as parent,
|
||||
(select read_only from `tabDocType` where name=`tabCustom Field`.dt) as read_only,
|
||||
fieldname, options
|
||||
from `tabCustom Field`
|
||||
where fieldtype='Dynamic Link'
|
||||
order by read_only""",
|
||||
]
|
||||
|
||||
def get_dynamic_link_map(for_delete=False):
|
||||
|
|
@ -29,7 +44,6 @@ def get_dynamic_link_map(for_delete=False):
|
|||
dynamic_link_map.setdefault(doctype, []).append(df)
|
||||
|
||||
frappe.local.dynamic_link_map = dynamic_link_map
|
||||
|
||||
return frappe.local.dynamic_link_map
|
||||
|
||||
def get_dynamic_links():
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import frappe
|
||||
|
||||
def execute():
|
||||
from frappe.website.router import get_doctypes_with_web_view
|
||||
from frappe.utils.global_search import rebuild_for_doctype
|
||||
|
||||
for doctype in get_doctypes_with_web_view():
|
||||
rebuild_for_doctype(doctype)
|
||||
try:
|
||||
rebuild_for_doctype(doctype)
|
||||
except frappe.DoesNotExistError:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -497,6 +497,10 @@ h6.uppercase,
|
|||
}
|
||||
.frappe-control pre {
|
||||
white-space: pre-wrap;
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
.hide-control {
|
||||
display: none !important;
|
||||
|
|
|
|||
|
|
@ -328,6 +328,7 @@ frappe.Application = Class.extend({
|
|||
if(!frappe.app.session_expired_dialog) {
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
title: __('Session Expired'),
|
||||
keep_open: true,
|
||||
fields: [
|
||||
{ fieldtype:'Password', fieldname:'password',
|
||||
label: __('Please Enter Your Password to Continue') },
|
||||
|
|
@ -369,7 +370,7 @@ frappe.Application = Class.extend({
|
|||
// add backdrop
|
||||
$('.modal-backdrop').css({
|
||||
'opacity': 1,
|
||||
'background-color': '#EBEFF2'
|
||||
'background-color': '#4B4C9D'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1343,10 +1343,7 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
|
|||
};
|
||||
},
|
||||
filter: function(item, input) {
|
||||
var d = this.get_item(item.value);
|
||||
return Awesomplete.FILTER_CONTAINS(d.value, '__link_option') ||
|
||||
Awesomplete.FILTER_CONTAINS(d.value, input) ||
|
||||
Awesomplete.FILTER_CONTAINS(d.description, input);
|
||||
return true;
|
||||
},
|
||||
item: function (item, input) {
|
||||
d = this.get_item(item.value);
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ frappe.ui.form.Dashboard = Class.extend({
|
|||
if(!this.heatmap) {
|
||||
this.heatmap = new CalHeatMap();
|
||||
this.heatmap.init({
|
||||
itemSelector: "#heatmap-" + this.frm.doctype,
|
||||
itemSelector: "#heatmap-" + frappe.model.scrub(this.frm.doctype),
|
||||
domain: "month",
|
||||
subDomain: "day",
|
||||
start: moment().subtract(1, 'year').add(1, 'month').toDate(),
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ frappe.ui.form.AssignTo = Class.extend({
|
|||
add: function() {
|
||||
var me = this;
|
||||
|
||||
if(this.frm.doc.__unsaved == 1) {
|
||||
if(this.frm.is_new()) {
|
||||
frappe.throw(__("Please save the document before assignment"));
|
||||
return;
|
||||
}
|
||||
|
|
@ -93,7 +93,6 @@ frappe.ui.form.AssignTo = Class.extend({
|
|||
docname: me.frm.docname,
|
||||
callback: function(r) {
|
||||
me.render(r.message);
|
||||
me.frm.reload_doc();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -108,7 +107,7 @@ frappe.ui.form.AssignTo = Class.extend({
|
|||
remove: function(owner) {
|
||||
var me = this;
|
||||
|
||||
if(this.frm.doc.__unsaved == 1) {
|
||||
if(this.frm.is_new()) {
|
||||
frappe.throw(__("Please save the document before removing assignment"));
|
||||
return;
|
||||
}
|
||||
|
|
@ -122,7 +121,6 @@ frappe.ui.form.AssignTo = Class.extend({
|
|||
},
|
||||
callback:function(r,rt) {
|
||||
me.render(r.message);
|
||||
me.frm.reload_doc();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<div class="progress-area hidden form-dashboard-section">
|
||||
</div>
|
||||
<div class="form-heatmap hidden form-dashboard-section">
|
||||
<div id="heatmap-{{ frm.doctype }}"></div>
|
||||
<div id="heatmap-{{ frappe.model.scrub(frm.doctype) }}"></div>
|
||||
<div class="text-muted small heatmap-message hidden"></div>
|
||||
</div>
|
||||
<div class="form-chart form-dashboard-section hidden"></div>
|
||||
|
|
|
|||
|
|
@ -153,8 +153,9 @@ $(window).on('hashchange', function() {
|
|||
return;
|
||||
|
||||
// hide open dialog
|
||||
if(cur_dialog && cur_dialog.hide_on_page_refresh)
|
||||
if(cur_dialog && cur_dialog.hide_on_page_refresh) {
|
||||
cur_dialog.hide();
|
||||
}
|
||||
|
||||
frappe.route();
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,12 @@ frappe.socket = {
|
|||
if (frappe.flags.doc_subscribe) {
|
||||
return;
|
||||
}
|
||||
|
||||
frappe.flags.doc_subscribe = true;
|
||||
|
||||
// throttle to 1 per sec
|
||||
setTimeout(function() { frappe.flags.doc_subscribe = false }, 1000);
|
||||
|
||||
if (frm.is_new()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -72,11 +78,6 @@ frappe.socket = {
|
|||
}
|
||||
}
|
||||
|
||||
frappe.flags.doc_subscribe = true;
|
||||
|
||||
// throttle to 1 per sec
|
||||
setTimeout(function() { frappe.flags.doc_subscribe = false }, 1000);
|
||||
|
||||
frappe.socket.doc_subscribe(frm.doctype, frm.docname);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div class="filter-dash-item">
|
||||
<div class="filter-header">
|
||||
<h6 class="h6 filter-label" data-name="{{ label }}">{{ label }}</h6>
|
||||
<h6 class="h6 filter-label" data-name="{{ field }}">{{ label }}</h6>
|
||||
{% if (type!=="Date" && type!=="Datetime" && type!=="DateRange") { %}
|
||||
<div class="dropdown search-dropdown hide pull-right">
|
||||
<i class="dropdown-toggle octicon octicon-search text-muted"
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<div class="dropdown sort-dropdown pull-right">
|
||||
<i class="pull-right dropdown-toggle
|
||||
filter-sort-active octicon octicon-gear text-muted"
|
||||
data-name="{{ label }}" data-sort-by="number" data-order="desc"
|
||||
data-name="{{ field }}" data-sort-by="number" data-order="desc"
|
||||
data-toggle="dropdown"/>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="filter-sort-item" data-sort-by="alphabet" data-order="asc">
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
<div class="filter-input filter-input-date" data-name="{{ field }}">
|
||||
</div>
|
||||
{% } else { %}
|
||||
<ul class="list-unstyled sidebar-menu filter-stat" data-name="{{ label }}">
|
||||
<ul class="list-unstyled sidebar-menu filter-stat" data-name="{{ field }}">
|
||||
|
||||
</ul>
|
||||
{% } %}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ frappe.ui.FilterList = Class.extend({
|
|||
return
|
||||
}
|
||||
|
||||
var active = this.wrapper.find(".filter-sort-active[data-name='"+__(field.label)+"']");
|
||||
var active = this.wrapper.find(".filter-sort-active[data-name='"+__(field.name)+"']");
|
||||
|
||||
// sort filters
|
||||
if(active.attr('data-sort-by')==='alphabet') {
|
||||
|
|
@ -168,7 +168,7 @@ frappe.ui.FilterList = Class.extend({
|
|||
label: __(field.label),
|
||||
labels:labels
|
||||
};
|
||||
var dashboard_filter = this.wrapper.find(".filter-stat[data-name='" + __(field.label) + "']")
|
||||
var dashboard_filter = this.wrapper.find(".filter-stat[data-name='" + __(field.name) + "']")
|
||||
dashboard_filter.html(frappe.render_template("filter_dashboard_value", context))
|
||||
.on("click", ".filter-stat-link", function() {
|
||||
var fieldname = $(this).attr('data-field');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div class="search-header">
|
||||
<i class="octicon octicon-search"></i>
|
||||
<input type="text" class="form-control search-input" style="padding: 15px">
|
||||
<input type="text" class="form-control search-input" style="padding-left: 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>
|
||||
|
|
@ -625,6 +625,10 @@ h6.uppercase, .h6.uppercase {
|
|||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ def rebuild_for_doctype(doctype):
|
|||
filters.enabled = 1
|
||||
if meta.has_field("disabled"):
|
||||
filters.disabled = 0
|
||||
|
||||
|
||||
return filters
|
||||
|
||||
meta = frappe.get_meta(doctype)
|
||||
|
|
@ -69,36 +69,36 @@ def rebuild_for_doctype(doctype):
|
|||
})
|
||||
for p in parent_doctypes:
|
||||
rebuild_for_doctype(p.parent)
|
||||
|
||||
|
||||
return
|
||||
|
||||
|
||||
# Delete records
|
||||
delete_global_search_records_for_doctype(doctype)
|
||||
|
||||
parent_search_fields = meta.get_global_search_fields()
|
||||
fieldnames = get_selected_fields(meta, parent_search_fields)
|
||||
|
||||
|
||||
# Get all records from parent doctype table
|
||||
all_records = frappe.get_all(doctype, fields=fieldnames, filters=_get_filters())
|
||||
|
||||
|
||||
# Children data
|
||||
all_children, child_search_fields = get_children_data(doctype, meta)
|
||||
all_children, child_search_fields = get_children_data(doctype, meta)
|
||||
all_contents = []
|
||||
|
||||
|
||||
for doc in all_records:
|
||||
content = []
|
||||
for field in parent_search_fields:
|
||||
value = doc.get(field.fieldname)
|
||||
if value:
|
||||
content.append(get_formatted_value(value, field))
|
||||
|
||||
|
||||
# get children data
|
||||
for child_doctype, records in all_children.get(doc.name, {}).items():
|
||||
for field in child_search_fields.get(child_doctype):
|
||||
for r in records:
|
||||
if r.get(field.fieldname):
|
||||
content.append(get_formatted_value(r.get(field.fieldname), field))
|
||||
|
||||
|
||||
if content:
|
||||
# if doctype published in website, push title, route etc.
|
||||
published = 0
|
||||
|
|
@ -108,9 +108,9 @@ def rebuild_for_doctype(doctype):
|
|||
published = 1 if d.is_website_published() else 0
|
||||
title = d.get_title()
|
||||
route = d.get("route")
|
||||
|
||||
|
||||
all_contents.append({
|
||||
"doctype": doctype,
|
||||
"doctype": frappe.db.escape(doctype),
|
||||
"name": frappe.db.escape(doc.name),
|
||||
"content": frappe.db.escape(' ||| '.join(content or '')),
|
||||
"published": published,
|
||||
|
|
@ -119,30 +119,30 @@ def rebuild_for_doctype(doctype):
|
|||
})
|
||||
if all_contents:
|
||||
insert_values_for_multiple_docs(all_contents)
|
||||
|
||||
|
||||
def delete_global_search_records_for_doctype(doctype):
|
||||
frappe.db.sql('''
|
||||
delete
|
||||
from __global_search
|
||||
where
|
||||
doctype = %s''', doctype, as_dict=True)
|
||||
|
||||
|
||||
def get_selected_fields(meta, global_search_fields):
|
||||
fieldnames = [df.fieldname for df in global_search_fields]
|
||||
if meta.istable==1:
|
||||
fieldnames.append("parent")
|
||||
elif "name" not in fieldnames:
|
||||
fieldnames.append("name")
|
||||
|
||||
|
||||
if meta.has_field("is_website_published"):
|
||||
fieldnames.append("is_website_published")
|
||||
|
||||
|
||||
return fieldnames
|
||||
|
||||
|
||||
def get_children_data(doctype, meta):
|
||||
"""
|
||||
Get all records from all the child tables of a doctype
|
||||
|
||||
|
||||
all_children = {
|
||||
"parent1": {
|
||||
"child_doctype1": [
|
||||
|
|
@ -153,41 +153,41 @@ def get_children_data(doctype, meta):
|
|||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
all_children = frappe._dict()
|
||||
child_search_fields = frappe._dict()
|
||||
|
||||
|
||||
for child in meta.get_table_fields():
|
||||
child_meta = frappe.get_meta(child.options)
|
||||
search_fields = child_meta.get_global_search_fields()
|
||||
if search_fields:
|
||||
child_search_fields.setdefault(child.options, search_fields)
|
||||
child_fieldnames = get_selected_fields(child_meta, search_fields)
|
||||
child_records = frappe.get_all(child.options, fields=child_fieldnames, filters={
|
||||
child_records = frappe.get_all(child.options, fields=child_fieldnames, filters={
|
||||
"docstatus": ["!=", 1],
|
||||
"parenttype": doctype
|
||||
})
|
||||
|
||||
|
||||
for record in child_records:
|
||||
all_children.setdefault(record.parent, frappe._dict())\
|
||||
.setdefault(child.options, []).append(record)
|
||||
|
||||
|
||||
return all_children, child_search_fields
|
||||
|
||||
|
||||
def insert_values_for_multiple_docs(all_contents):
|
||||
values = []
|
||||
for content in all_contents:
|
||||
values.append("( '{doctype}', '{name}', '{content}', '{published}', '{title}', '{route}')"
|
||||
.format(**content))
|
||||
|
||||
|
||||
frappe.db.sql('''
|
||||
insert into __global_search
|
||||
(doctype, name, content, published, title, route)
|
||||
values
|
||||
{0}
|
||||
'''.format(", ".join(values)))
|
||||
|
||||
|
||||
|
||||
def update_global_search(doc):
|
||||
'''Add values marked with `in_global_search` to
|
||||
|
|
@ -205,7 +205,7 @@ def update_global_search(doc):
|
|||
for field in doc.meta.get_global_search_fields():
|
||||
if doc.get(field.fieldname) and field.fieldtype != "Table":
|
||||
content.append(get_formatted_value(doc.get(field.fieldname), field))
|
||||
|
||||
|
||||
# Get children
|
||||
for child in doc.meta.get_table_fields():
|
||||
for d in doc.get(child.fieldname):
|
||||
|
|
|
|||
|
|
@ -123,15 +123,19 @@ def get_list_context(context, doctype):
|
|||
from frappe.website.doctype.web_form.web_form import get_web_form_list
|
||||
|
||||
list_context = context or frappe._dict()
|
||||
module = load_doctype_module(doctype)
|
||||
if hasattr(module, "get_list_context"):
|
||||
out = frappe._dict(module.get_list_context(list_context) or {})
|
||||
if out:
|
||||
list_context = out
|
||||
meta = frappe.get_meta(doctype)
|
||||
|
||||
if not meta.custom:
|
||||
# custom doctypes don't have modules
|
||||
module = load_doctype_module(doctype)
|
||||
if hasattr(module, "get_list_context"):
|
||||
out = frappe._dict(module.get_list_context(list_context) or {})
|
||||
if out:
|
||||
list_context = out
|
||||
|
||||
# get path from '/templates/' folder of the doctype
|
||||
if not list_context.row_template:
|
||||
list_context.row_template = frappe.get_meta(doctype).get_row_template()
|
||||
list_context.row_template = meta.get_row_template()
|
||||
|
||||
# is web form, show the default web form filters
|
||||
# which is only the owner
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue