Merge branch 'develop' into empty-fetch-from-field

This commit is contained in:
Suraj Shetty 2021-12-22 11:06:52 +05:30 committed by GitHub
commit f8cbe9575f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 233 additions and 150 deletions

View file

@ -5,6 +5,7 @@
import typing
import frappe
from frappe import _
from frappe.model import (
display_fieldtypes,
no_value_fields,
@ -215,9 +216,9 @@ class Exporter:
for df in self.fields:
is_parent = not df.is_child_table_field
if is_parent:
label = df.label
label = _(df.label)
else:
label = "{0} ({1})".format(df.label, df.child_table_df.label)
label = "{0} ({1})".format(_(df.label), _(df.child_table_df.label))
if label in header:
# this label is already in the header,
@ -227,6 +228,7 @@ class Exporter:
label = "{0}".format(df.fieldname)
else:
label = "{0}.{1}".format(df.child_table_df.fieldname, df.fieldname)
header.append(label)
self.csv_array.append(header)
@ -253,10 +255,10 @@ class Exporter:
self.build_xlsx_response()
def build_csv_response(self):
build_csv_response(self.get_csv_array_for_export(), self.doctype)
build_csv_response(self.get_csv_array_for_export(), _(self.doctype))
def build_xlsx_response(self):
build_xlsx_response(self.get_csv_array_for_export(), self.doctype)
build_xlsx_response(self.get_csv_array_for_export(), _(self.doctype))
def group_children_data_by_parent(self, children_data: typing.Dict[str, list]):
return groupby_metric(children_data, key='parent')

View file

@ -262,7 +262,7 @@ class Importer:
rows = [header_row]
rows += [row.data for row in self.import_file.data if row.row_number in row_indexes]
build_csv_response(rows, self.doctype)
build_csv_response(rows, _(self.doctype))
def print_import_log(self, import_log):
failed_records = [log for log in import_log if not log.success]
@ -1009,18 +1009,14 @@ def build_fields_dict_for_column_matching(parent_doctype):
out = {}
# doctypes and fieldname if it is a child doctype
doctypes = [[parent_doctype, None]] + [
[df.options, df] for df in parent_meta.get_table_fields()
doctypes = [(parent_doctype, None)] + [
(df.options, df) for df in parent_meta.get_table_fields()
]
for doctype, table_df in doctypes:
translated_table_label = _(table_df.label) if table_df else None
# name field
name_by_label = (
"ID" if doctype == parent_doctype else "ID ({0})".format(table_df.label)
)
name_by_fieldname = (
"name" if doctype == parent_doctype else "{0}.name".format(table_df.fieldname)
)
name_df = frappe._dict(
{
"fieldtype": "Data",
@ -1031,63 +1027,90 @@ def build_fields_dict_for_column_matching(parent_doctype):
}
)
if doctype != parent_doctype:
if doctype == parent_doctype:
name_headers = (
"name", # fieldname
"ID", # label
_("ID"), # translated label
)
else:
name_headers = (
"{0}.name".format(table_df.fieldname), # fieldname
"ID ({0})".format(table_df.label), # label
"{0} ({1})".format(_("ID"), translated_table_label), # translated label
)
name_df.is_child_table_field = True
name_df.child_table_df = table_df
out[name_by_label] = name_df
out[name_by_fieldname] = name_df
for header in name_headers:
out[header] = name_df
# other fields
fields = get_standard_fields(doctype) + frappe.get_meta(doctype).fields
for df in fields:
label = (df.label or "").strip()
fieldtype = df.fieldtype or "Data"
if fieldtype in no_value_fields:
continue
label = (df.label or "").strip()
translated_label = _(label)
parent = df.parent or parent_doctype
if fieldtype not in no_value_fields:
if parent_doctype == doctype:
# for parent doctypes keys will be
# Label
# label
# Label (label)
if not out.get(label):
# if Label is already set, don't set it again
# in case of duplicate column headers
out[label] = df
out[df.fieldname] = df
label_with_fieldname = "{0} ({1})".format(label, df.fieldname)
out[label_with_fieldname] = df
if parent_doctype == doctype:
# for parent doctypes keys will be
# Label, fieldname, Label (fieldname)
for header in (label, translated_label):
# if Label is already set, don't set it again
# in case of duplicate column headers
if header not in out:
out[header] = df
for header in (
df.fieldname,
f"{label} ({df.fieldname})",
f"{translated_label} ({df.fieldname})"
):
out[header] = df
else:
# for child doctypes keys will be
# Label (Table Field Label)
# table_field.fieldname
# create a new df object to avoid mutation problems
if isinstance(df, dict):
new_df = frappe._dict(df.copy())
else:
# in case there are multiple table fields with the same doctype
# for child doctypes keys will be
# Label (Table Field Label)
# table_field.fieldname
table_fields = parent_meta.get(
"fields", {"fieldtype": ["in", table_fieldtypes], "options": parent}
)
for table_field in table_fields:
by_label = "{0} ({1})".format(label, table_field.label)
by_fieldname = "{0}.{1}".format(table_field.fieldname, df.fieldname)
new_df = df.as_dict()
# create a new df object to avoid mutation problems
if isinstance(df, dict):
new_df = frappe._dict(df.copy())
else:
new_df = df.as_dict()
new_df.is_child_table_field = True
new_df.child_table_df = table_df
new_df.is_child_table_field = True
new_df.child_table_df = table_field
out[by_label] = new_df
out[by_fieldname] = new_df
for header in (
# fieldname
"{0}.{1}".format(table_df.fieldname, df.fieldname),
# label
"{0} ({1})".format(label, table_df.label),
# translated label
"{0} ({1})".format(translated_label, translated_table_label),
):
out[header] = new_df
# if autoname is based on field
# add an entry for "ID (Autoname Field)"
autoname_field = get_autoname_field(parent_doctype)
if autoname_field:
out["ID ({})".format(autoname_field.label)] = autoname_field
# ID field should also map to the autoname field
out["ID"] = autoname_field
out["name"] = autoname_field
for header in (
"ID ({})".format(autoname_field.label), # label
"{0} ({1})".format(_("ID"), _(autoname_field.label)), # translated label
# ID field should also map to the autoname field
"ID",
_("ID"),
"name",
):
out[header] = autoname_field
return out

View file

@ -801,14 +801,27 @@ class Database(object):
frappe.local.realtime_log = []
def rollback(self):
"""`ROLLBACK` current transaction."""
self.sql("rollback")
self.begin()
for obj in frappe.local.rollback_observers:
if hasattr(obj, "on_rollback"):
obj.on_rollback()
frappe.local.rollback_observers = []
def savepoint(self, save_point):
"""Savepoints work as a nested transaction.
Changes can be undone to a save point by doing frappe.db.rollback(save_point)
Note: rollback watchers can not work with save points.
so only changes to database are undone when rolling back to a savepoint.
Avoid using savepoints when writing to filesystem."""
self.sql(f"savepoint {save_point}")
def rollback(self, *, save_point=None):
"""`ROLLBACK` current transaction. Optionally rollback to a known save_point."""
if save_point:
self.sql(f"rollback to savepoint {save_point}")
else:
self.sql("rollback")
self.begin()
for obj in frappe.local.rollback_observers:
if hasattr(obj, "on_rollback"):
obj.on_rollback()
frappe.local.rollback_observers = []
def field_exists(self, dt, fn):
"""Return true of field exists."""

View file

@ -392,17 +392,24 @@ frappe.setup.slides_settings = [
fields: [
{
fieldname: "country", label: __("Your Country"), reqd: 1,
fieldtype: "Select"
fieldtype: "Autocomplete",
placeholder: __('Select Country')
},
{ fieldtype: "Section Break" },
{
fieldname: "timezone", label: __("Time Zone"), reqd: 1,
fieldtype: "Select"
fieldname: "timezone",
label: __("Time Zone"),
placeholder: __('Select Time Zone'),
reqd: 1,
fieldtype: "Select",
},
{ fieldtype: "Column Break" },
{
fieldname: "currency", label: __("Currency"), reqd: 1,
fieldtype: "Select"
fieldname: "currency",
label: __("Currency"),
placeholder: __('Select Currency'),
reqd: 1,
fieldtype: "Select",
}
],
@ -512,7 +519,7 @@ frappe.setup.utils = {
frappe.setup.data.email = r.message.email;
callback(slide);
}
})
});
},
setup_language_field: function (slide) {
@ -529,16 +536,19 @@ frappe.setup.utils = {
var country_field = slide.get_field('country');
slide.get_input("country").empty()
.add_options([""].concat(Object.keys(data.country_info).sort()));
slide.get_input("currency").empty()
.add_options(frappe.utils.unique([""].concat(
$.map(data.country_info, opts => opts.currency)
)).sort());
country_field.set_data(Object.keys(data.country_info).sort());
slide.get_input("currency")
.empty()
.add_options(
frappe.utils.unique(
$.map(data.country_info, opts => opts.currency).sort()
)
);
slide.get_input("timezone").empty()
.add_options([""].concat(data.all_timezones));
.add_options(data.all_timezones);
// set values if present
if (frappe.wizard.values.country) {
@ -547,13 +557,9 @@ frappe.setup.utils = {
country_field.set_input(data.default_country);
}
if (frappe.wizard.values.currency) {
slide.get_field("currency").set_input(frappe.wizard.values.currency);
}
slide.get_field("currency").set_input(frappe.wizard.values.currency);
if (frappe.wizard.values.timezone) {
slide.get_field("timezone").set_input(frappe.wizard.values.timezone);
}
slide.get_field("timezone").set_input(frappe.wizard.values.timezone);
},
@ -589,17 +595,15 @@ frappe.setup.utils = {
$timezone.empty();
if (!country) return;
// add country specific timezones first
if (country) {
var timezone_list = data.country_info[country].timezones || [];
$timezone.add_options(timezone_list.sort());
slide.get_field("currency").set_input(data.country_info[country].currency);
slide.get_field("currency").$input.trigger("change");
}
const timezone_list = data.country_info[country].timezones || [];
$timezone.add_options(timezone_list.sort());
slide.get_field("currency").set_input(data.country_info[country].currency);
slide.get_field("currency").$input.trigger("change");
// add all timezones at the end, so that user has the option to change it to any timezone
$timezone.add_options([""].concat(data.all_timezones));
$timezone.add_options(data.all_timezones);
slide.get_field("timezone").set_input($timezone.val());
// temporarily set date format
@ -617,7 +621,7 @@ frappe.setup.utils = {
if (number_format === "#.###") {
number_format = "#.###,##";
} else if (number_format === "#,###") {
number_format = "#,###.##"
number_format = "#,###.##";
}
frappe.boot.sysdefaults.number_format = number_format;

View file

@ -151,7 +151,7 @@ def update_system_settings(args):
system_settings = frappe.get_doc("System Settings", "System Settings")
system_settings.update({
"country": args.get("country"),
"language": get_language_code(args.get("language")),
"language": get_language_code(args.get("language")) or 'en',
"time_zone": args.get("timezone"),
"float_precision": 3,
'date_format': frappe.db.get_value("Country", args.get("country"), "date_format"),

View file

@ -260,7 +260,7 @@ def run_doc_method(method, docs=None, dt=None, dn=None, arg=None, args=None):
# build output as csv
if cint(frappe.form_dict.get('as_csv')):
build_csv_response(response, doc.doctype.replace(' ', ''))
build_csv_response(response, _(doc.doctype).replace(' ', ''))
return
frappe.response['message'] = response

View file

@ -301,8 +301,7 @@ class Document(BaseDocument):
self.flags.ignore_version = frappe.flags.in_test if ignore_version is None else ignore_version
if self.get("__islocal") or not self.get("name"):
self.insert()
return
return self.insert()
self.check_permission("write", "save")

View file

@ -343,11 +343,11 @@ function get_fields_as_options(doctype, column_map) {
return [].concat(
...keys.map(key => {
return column_map[key].map(df => {
let label = df.label;
let label = __(df.label);
let value = df.fieldname;
if (doctype !== key) {
let table_field = frappe.meta.get_docfield(doctype, key);
label = `${df.label} (${table_field.label})`;
label = `${__(df.label)} (${__(table_field.label)})`;
value = `${table_field.fieldname}.${df.fieldname}`;
}
return {

View file

@ -11,9 +11,7 @@ frappe.user_info = function(uid) {
}
if(!(frappe.boot.user_info && frappe.boot.user_info[uid])) {
var user_info = {
fullname: frappe.utils.to_title_case(uid.split("@")[0]) || "Unknown"
};
var user_info = {fullname: uid || "Unknown"};
} else {
var user_info = frappe.boot.user_info[uid];
}
@ -157,4 +155,4 @@ $(document).bind('mousemove', function() {
if(frappe.session_alive_timeout)
clearTimeout(frappe.session_alive_timeout);
frappe.session_alive_timeout = setTimeout('frappe.session_alive=false;', 30000);
});
});

View file

@ -1,13 +1,14 @@
@import "frappe/public/css/bootstrap.css";
@import './common/quill';
@import "./common/quill";
@import "./desk/css_variables";
// .print-format {
// .ql-snow .ql-editor {
// height: auto;
// min-height: 0;
// // max-height: 0;
// }
// }
// !! PDF Barcode hack !!
// Workaround for rendering barcodes prior to https://github.com/frappe/frappe/pull/15307
@media print {
svg[data-barcode-value] > rect {
fill: white !important;
}
svg[data-barcode-value] > g {
fill: black !important;
}
}

View file

@ -0,0 +1,22 @@
<form class="form-signin form-signup hide" role="form">
<div class="page-card-body">
<div class="form-group">
<label class="form-label sr-only" for="signup_fullname">{{ _("Full Name") }}</label>
<input type="text" id="signup_fullname" class="form-control" placeholder="{{ _('Jane Doe') }}"
required autofocus>
</div>
<div class="form-group">
<label class="form-label sr-only" for="signup_email">{{ _("Email") }}</label>
<input type="email" id="signup_email" class="form-control"
placeholder="{{ _('jane@example.com') }}" required>
</div>
</div>
<div class="page-card-actions">
<button class="btn btn-sm btn-primary btn-block btn-signup"
type="submit">{{ _("Sign up") }}</button>
<p class="text-center sign-up-message">
<a href="#login" class="blue">{{ _("Have an account? Login") }}</a>
</p>
</div>
</form>

View file

@ -246,6 +246,28 @@ class TestDB(unittest.TestCase):
clear_custom_fields(test_doctype)
def test_savepoints(self):
frappe.db.rollback()
save_point = "todonope"
created_docs = []
failed_docs = []
for _ in range(5):
frappe.db.savepoint(save_point)
doc_gone = frappe.get_doc(doctype="ToDo", description="nope").save()
failed_docs.append(doc_gone.name)
frappe.db.rollback(save_point=save_point)
doc_kept = frappe.get_doc(doctype="ToDo", description="nope").save()
created_docs.append(doc_kept.name)
frappe.db.commit()
for d in failed_docs:
self.assertFalse(frappe.db.exists("ToDo", d))
for d in created_docs:
self.assertTrue(frappe.db.exists("ToDo", d))
@run_only_if(db_type_is.MARIADB)
class TestDDLCommandsMaria(unittest.TestCase):
test_table_name = "TestNotes"
@ -368,4 +390,4 @@ class TestDDLCommandsPost(unittest.TestCase):
AND indexname = '{index_name}' ;
""",
)
self.assertEquals(len(indexs_in_table), 1)
self.assertEquals(len(indexs_in_table), 1)

View file

@ -81,7 +81,7 @@
<div class="page-card-body">
<form class="form-signin form-login" role="form">
{{ email_login_body() }}
</form>
</form>
<div class="social-logins text-center">
<p class="text-muted login-divider">{{ _("or") }}</p>
<div class="social-login-buttons">
@ -131,28 +131,7 @@
<div class="login-content page-card">
{{ logo_section() }}
{%- if not disable_signup -%}
<form class="form-signin form-signup hide" role="form">
<div class="page-card-body">
<div class="form-group">
<label class="form-label sr-only" for="signup_fullname">Full Name</label>
<input type="text" id="signup_fullname" class="form-control" placeholder="{{ _('Jane Doe') }}"
required autofocus>
</div>
<div class="form-group">
<label class="form-label sr-only" for="signup_email">Email</label>
<input type="email" id="signup_email" class="form-control"
placeholder="{{ _('jane@example.com') }}" required>
</div>
</div>
<div class="page-card-actions">
<button class="btn btn-sm btn-primary btn-block btn-signup"
type="submit">{{ _("Sign up") }}</button>
<p class="text-center sign-up-message">
<a href="#login" class="blue">{{ _("Have an account? Login") }}</a>
</p>
</div>
</form>
{{ signup_form_template }}
{%- else -%}
<div class='page-card-head mb-2'>
<span class='indicator gray'>{{_("Signup Disabled")}}</span>

View file

@ -12,6 +12,7 @@ from frappe.utils.password import get_decrypted_password
from frappe.utils.html_utils import get_icon_html
from frappe.integrations.oauth2_logins import decoder_compat
from frappe.website.utils import get_home_page
from frappe.utils.jinja import guess_is_path
no_cache = True
@ -39,6 +40,17 @@ def get_context(context):
frappe.get_hooks("app_logo_url")[-1])
context["app_name"] = (frappe.db.get_single_value('Website Settings', 'app_name') or
frappe.get_system_settings("app_name") or _("Frappe"))
signup_form_template = frappe.get_hooks("signup_form_template")
if signup_form_template and len(signup_form_template) and signup_form_template[0]:
path = signup_form_template[0]
if not guess_is_path(path):
path = frappe.get_attr(signup_form_template[0])()
else:
path = "frappe/templates/signup.html"
if path:
context["signup_form_template"] = frappe.get_template(path).render()
providers = [i.name for i in frappe.get_all("Social Login Key", filters={"enable_social_login":1}, order_by="name")]
for provider in providers:
client_id, base_url = frappe.get_value("Social Login Key", provider, ["client_id", "base_url"])

View file

@ -12,21 +12,21 @@
<form id="reset-password">
<div class="form-group">
<input id="old_password" type="password"
class="form-control" placeholder="{{ _("Old Password") }}">
class="form-control" placeholder="{{ _('Old Password') }}">
</div>
<div class="form-group">
<input id="new_password" type="password"
class="form-control" placeholder="{{ _("New Password") }}">
class="form-control" placeholder="{{ _('New Password') }}">
<span class="password-strength-indicator indicator"></span>
</div>
<div class="form-group">
<input id="confirm_password" type="password"
class="form-control" placeholder="{{ _("Confirm Password") }}">
class="form-control" placeholder="{{ _('Confirm Password') }}">
<p class="password-mismatch-message text-muted small hidden mt-2"></p>
</div>
<p class='password-strength-message text-muted small hidden'></p>
<button type="submit" id="update"
<button type="submit" id="update"
class="btn btn-primary btn-block btn-update">{{_("Confirm")}}</button>
</form>
{%- if not disable_signup -%}
@ -36,7 +36,6 @@
</div>
{%- endif -%}
</div>
</section>
<style>
</style>
@ -49,7 +48,7 @@ frappe.ready(function() {
}
if(frappe.utils.get_url_arg("password_expired")) {
$(".password-box").html(__('The password of your account has expired.'));
$(".password-box").html("{{ _('The password of your account has expired.') }}");
}
$("#reset-password").on("submit", function() {
@ -69,15 +68,23 @@ frappe.ready(function() {
}
const confirm_password = $('#confirm_password').val()
if (!args.old_password && !args.key) {
frappe.msgprint(__("Old Password Required."));
frappe.msgprint({
title: "{{ _('Message') }}",
message: "{{ _('Old Password Required.') }}",
clear: true
});
}
if (!args.new_password) {
frappe.msgprint(__("New Password Required."));
frappe.msgprint({
title: "{{ _('Message') }}",
message: "{{ _('New Password Required.') }}",
clear: true
});
}
if (args.new_password !== confirm_password) {
$('.password-mismatch-message').text(__("Passwords do not match"))
$('.password-mismatch-message').text("{{ _('Passwords do not match') }}")
.removeClass('hidden text-muted').addClass('text-danger');
return false
return false;
}
frappe.call({
@ -87,10 +94,10 @@ frappe.ready(function() {
args: args,
statusCode: {
401: function() {
$(".page-card-head .reset-password-heading").text(__("Invalid Password"));
$(".page-card-head .reset-password-heading").text("{{ _('Invalid Password') }}");
},
410: function({ responseJSON }) {
const title = __("Invalid Link");
const title = "{{ _('Invalid Link') }}";
const message = responseJSON.message;
$(".page-card-head .reset-password-heading").text(title);
frappe.msgprint({ title: title, message: message, clear: true });
@ -100,10 +107,11 @@ frappe.ready(function() {
strength_indicator.addClass("hidden");
strength_message.addClass("hidden");
$(".page-card-head .reset-password-heading")
.html(__("Status Updated"));
.html("{{ _('Status Updated') }}");
if(r.message) {
frappe.msgprint({
message: __("Password Updated"),
title: "{{ _('Message') }}",
message: "{{ _('Password Updated') }}",
// password is updated successfully
// clear any server message
clear: true
@ -176,7 +184,7 @@ frappe.ready(function() {
var message = [];
feedback.help_msg = "";
if(!feedback.password_policy_validation_passed){
feedback.help_msg = "<br>" + "{{ _("Hint: Include symbols, numbers and capital letters in the password") }}";
feedback.help_msg = "<br>" + "{{ _('Hint: Include symbols, numbers and capital letters in the password') }}";
}
if (feedback) {
if(!feedback.password_policy_validation_passed){