Selaa lähdekoodia

Merge branch 'develop' into refactor-db-exists

version-14
Raffael Meyer 3 vuotta sitten
committed by GitHub
vanhempi
commit
621a473614
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
18 muutettua tiedostoa jossa 213 lisäystä ja 192 poistoa
  1. +9
    -7
      frappe/commands/site.py
  2. +39
    -0
      frappe/core/doctype/communication/communication.py
  3. +100
    -22
      frappe/core/doctype/communication/email.py
  4. +1
    -2
      frappe/core/doctype/user/user.json
  5. +25
    -16
      frappe/desk/form/linked_with.py
  6. +1
    -2
      frappe/email/doctype/newsletter/newsletter.json
  7. +4
    -3
      frappe/email/doctype/notification/notification.py
  8. +1
    -7
      frappe/email/email_body.py
  9. +2
    -2
      frappe/model/rename_doc.py
  10. +9
    -98
      frappe/public/js/frappe/form/linked_with.js
  11. +12
    -4
      frappe/public/js/frappe/list/base_list.js
  12. +2
    -19
      frappe/public/js/frappe/list/list_view.js
  13. +4
    -3
      frappe/public/js/frappe/views/reports/report_view.js
  14. +0
    -1
      frappe/templates/emails/standard.html
  15. +1
    -2
      frappe/website/doctype/blog_post/blog_post.json
  16. +1
    -2
      frappe/website/doctype/web_page/web_page.json
  17. +1
    -2
      frappe/website/doctype/website_settings/website_settings.json
  18. +1
    -0
      requirements.txt

+ 9
- 7
frappe/commands/site.py Näytä tiedosto

@@ -1,7 +1,7 @@
# imports - standard imports # imports - standard imports
import os import os
import sys
import shutil import shutil
import sys


# imports - third party imports # imports - third party imports
import click import click
@@ -65,11 +65,11 @@ def restore(context, sql_file_path, encryption_key=None, db_root_username=None,
"Restore site database from an sql file" "Restore site database from an sql file"
from frappe.installer import ( from frappe.installer import (
_new_site, _new_site,
extract_sql_from_archive,
extract_files, extract_files,
extract_sql_from_archive,
is_downgrade, is_downgrade,
is_partial, is_partial,
validate_database_sql
validate_database_sql,
) )
from frappe.utils.backups import Backup from frappe.utils.backups import Backup
if not os.path.exists(sql_file_path): if not os.path.exists(sql_file_path):
@@ -207,7 +207,7 @@ def restore(context, sql_file_path, encryption_key=None, db_root_username=None,
@click.option('--encryption-key', help='Backup encryption key') @click.option('--encryption-key', help='Backup encryption key')
@pass_context @pass_context
def partial_restore(context, sql_file_path, verbose, encryption_key=None): def partial_restore(context, sql_file_path, verbose, encryption_key=None):
from frappe.installer import partial_restore, extract_sql_from_archive
from frappe.installer import extract_sql_from_archive, partial_restore
from frappe.utils.backups import Backup from frappe.utils.backups import Backup


if not os.path.exists(sql_file_path): if not os.path.exists(sql_file_path):
@@ -545,7 +545,7 @@ def _use(site, sites_path='.'):


def use(site, sites_path='.'): def use(site, sites_path='.'):
if os.path.exists(os.path.join(sites_path, site)): if os.path.exists(os.path.join(sites_path, site)):
with open(os.path.join(sites_path, "currentsite.txt"), "w") as sitefile:
with open(os.path.join(sites_path, "currentsite.txt"), "w") as sitefile:
sitefile.write(site) sitefile.write(site)
print("Current Site set to {}".format(site)) print("Current Site set to {}".format(site))
else: else:
@@ -751,6 +751,7 @@ def set_admin_password(context, admin_password=None, logout_all_sessions=False):


def set_user_password(site, user, password, logout_all_sessions=False): def set_user_password(site, user, password, logout_all_sessions=False):
import getpass import getpass

from frappe.utils.password import update_password from frappe.utils.password import update_password


try: try:
@@ -881,15 +882,16 @@ def stop_recording(context):
raise SiteNotSpecifiedError raise SiteNotSpecifiedError


@click.command('ngrok') @click.command('ngrok')
@click.option('--bind-tls', is_flag=True, default=False, help='Returns a reference to the https tunnel.')
@pass_context @pass_context
def start_ngrok(context):
def start_ngrok(context, bind_tls):
from pyngrok import ngrok from pyngrok import ngrok


site = get_site(context) site = get_site(context)
frappe.init(site=site) frappe.init(site=site)


port = frappe.conf.http_port or frappe.conf.webserver_port port = frappe.conf.http_port or frappe.conf.webserver_port
tunnel = ngrok.connect(addr=str(port), host_header=site)
tunnel = ngrok.connect(addr=str(port), host_header=site, bind_tls=bind_tls)
print(f'Public URL: {tunnel.public_url}') print(f'Public URL: {tunnel.public_url}')
print('Inspect logs at http://localhost:4040') print('Inspect logs at http://localhost:4040')




+ 39
- 0
frappe/core/doctype/communication/communication.py Näytä tiedosto

@@ -18,6 +18,7 @@ from urllib.parse import unquote
from frappe.utils.user import is_system_user from frappe.utils.user import is_system_user
from frappe.contacts.doctype.contact.contact import get_contact_name from frappe.contacts.doctype.contact.contact import get_contact_name
from frappe.automation.doctype.assignment_rule.assignment_rule import apply as apply_assignment_rule from frappe.automation.doctype.assignment_rule.assignment_rule import apply as apply_assignment_rule
from parse import compile


exclude_from_linked_with = True exclude_from_linked_with = True


@@ -114,6 +115,44 @@ class Communication(Document, CommunicationEmailMixin):
frappe.publish_realtime('new_message', self.as_dict(), frappe.publish_realtime('new_message', self.as_dict(),
user=self.reference_name, after_commit=True) user=self.reference_name, after_commit=True)


def set_signature_in_email_content(self):
"""Set sender's User.email_signature or default outgoing's EmailAccount.signature to the email
"""
if not self.content:
return

quill_parser = compile('<div class="ql-editor read-mode">{}</div>')
email_body = quill_parser.parse(self.content)

if not email_body:
return

email_body = email_body[0]

user_email_signature = frappe.db.get_value(
"User",
self.sender,
"email_signature",
) if self.sender else None

signature = user_email_signature or frappe.db.get_value(
"Email Account",
{"default_outgoing": 1, "add_signature": 1},
"signature",
)

if not signature:
return

_signature = quill_parser.parse(signature)[0] if "ql-editor" in signature else None

if (_signature or signature) not in self.content:
self.content = f'{self.content}</p><br><p class="signature">{signature}'

def before_save(self):
if not self.flags.skip_add_signature:
self.set_signature_in_email_content()

def on_update(self): def on_update(self):
# add to _comment property of the doctype, so it shows up in # add to _comment property of the doctype, so it shows up in
# comments count for the list view # comments count for the list view


+ 100
- 22
frappe/core/doctype/communication/email.py Näytä tiedosto

@@ -22,12 +22,30 @@ OUTGOING_EMAIL_ACCOUNT_MISSING = _("""




@frappe.whitelist() @frappe.whitelist()
def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent",
sender=None, sender_full_name=None, recipients=None, communication_medium="Email", send_email=False,
print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, bcc=None,
flags=None, read_receipt=None, print_letterhead=True, email_template=None, communication_type=None,
ignore_permissions=False) -> Dict[str, str]:
"""Make a new communication.
def make(
doctype=None,
name=None,
content=None,
subject=None,
sent_or_received="Sent",
sender=None,
sender_full_name=None,
recipients=None,
communication_medium="Email",
send_email=False,
print_html=None,
print_format=None,
attachments="[]",
send_me_a_copy=False,
cc=None,
bcc=None,
read_receipt=None,
print_letterhead=True,
email_template=None,
communication_type=None,
**kwargs,
) -> Dict[str, str]:
"""Make a new communication. Checks for email permissions for specified Document.


:param doctype: Reference DocType. :param doctype: Reference DocType.
:param name: Reference Document name. :param name: Reference Document name.
@@ -44,17 +62,71 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
:param send_me_a_copy: Send a copy to the sender (default **False**). :param send_me_a_copy: Send a copy to the sender (default **False**).
:param email_template: Template which is used to compose mail . :param email_template: Template which is used to compose mail .
""" """
is_error_report = (doctype=="User" and name==frappe.session.user and subject=="Error Report")
send_me_a_copy = cint(send_me_a_copy)
if kwargs:
from frappe.utils.commands import warn
warn(
f"Options {kwargs} used in frappe.core.doctype.communication.email.make "
"are deprecated or unsupported",
category=DeprecationWarning
)

if doctype and name and not frappe.has_permission(doctype=doctype, ptype="email", doc=name):
raise frappe.PermissionError(
f"You are not allowed to send emails related to: {doctype} {name}"
)

return _make(
doctype=doctype,
name=name,
content=content,
subject=subject,
sent_or_received=sent_or_received,
sender=sender,
sender_full_name=sender_full_name,
recipients=recipients,
communication_medium=communication_medium,
send_email=send_email,
print_html=print_html,
print_format=print_format,
attachments=attachments,
send_me_a_copy=cint(send_me_a_copy),
cc=cc,
bcc=bcc,
read_receipt=read_receipt,
print_letterhead=print_letterhead,
email_template=email_template,
communication_type=communication_type,
add_signature=False,
)


if not ignore_permissions:
if doctype and name and not is_error_report and not frappe.has_permission(doctype, "email", name) and not (flags or {}).get('ignore_doctype_permissions'):
raise frappe.PermissionError("You are not allowed to send emails related to: {doctype} {name}".format(
doctype=doctype, name=name))


if not sender:
sender = get_formatted_email(frappe.session.user)
def _make(
doctype=None,
name=None,
content=None,
subject=None,
sent_or_received="Sent",
sender=None,
sender_full_name=None,
recipients=None,
communication_medium="Email",
send_email=False,
print_html=None,
print_format=None,
attachments="[]",
send_me_a_copy=False,
cc=None,
bcc=None,
read_receipt=None,
print_letterhead=True,
email_template=None,
communication_type=None,
add_signature=True,
) -> Dict[str, str]:
"""Internal method to make a new communication that ignores Permission checks.
"""


sender = sender or get_formatted_email(frappe.session.user)
recipients = list_to_str(recipients) if isinstance(recipients, list) else recipients recipients = list_to_str(recipients) if isinstance(recipients, list) else recipients
cc = list_to_str(cc) if isinstance(cc, list) else cc cc = list_to_str(cc) if isinstance(cc, list) else cc
bcc = list_to_str(bcc) if isinstance(bcc, list) else bcc bcc = list_to_str(bcc) if isinstance(bcc, list) else bcc
@@ -77,7 +149,9 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
"read_receipt":read_receipt, "read_receipt":read_receipt,
"has_attachment": 1 if attachments else 0, "has_attachment": 1 if attachments else 0,
"communication_type": communication_type, "communication_type": communication_type,
}).insert(ignore_permissions=True)
})
comm.flags.skip_add_signature = not add_signature
comm.insert(ignore_permissions=True)


# if not committed, delayed task doesn't find the communication # if not committed, delayed task doesn't find the communication
if attachments: if attachments:
@@ -87,17 +161,21 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =


if cint(send_email): if cint(send_email):
if not comm.get_outgoing_email_account(): if not comm.get_outgoing_email_account():
frappe.throw(msg=OUTGOING_EMAIL_ACCOUNT_MISSING, exc=frappe.OutgoingEmailError)
frappe.throw(
msg=OUTGOING_EMAIL_ACCOUNT_MISSING, exc=frappe.OutgoingEmailError
)


comm.send_email(print_html=print_html, print_format=print_format,
send_me_a_copy=send_me_a_copy, print_letterhead=print_letterhead)
comm.send_email(
print_html=print_html,
print_format=print_format,
send_me_a_copy=send_me_a_copy,
print_letterhead=print_letterhead,
)


emails_not_sent_to = comm.exclude_emails_list(include_sender=send_me_a_copy) emails_not_sent_to = comm.exclude_emails_list(include_sender=send_me_a_copy)


return {
"name": comm.name,
"emails_not_sent_to": ", ".join(emails_not_sent_to)
}
return {"name": comm.name, "emails_not_sent_to": ", ".join(emails_not_sent_to)}



def validate_email(doc: "Communication") -> None: def validate_email(doc: "Communication") -> None:
"""Validate Email Addresses of Recipients and CC""" """Validate Email Addresses of Recipients and CC"""


+ 1
- 2
frappe/core/doctype/user/user.json Näytä tiedosto

@@ -668,8 +668,7 @@
"link_fieldname": "user" "link_fieldname": "user"
} }
], ],
"max_attachments": 5,
"modified": "2022-01-03 11:53:25.250822",
"modified": "2022-03-09 01:47:56.745069",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Core", "module": "Core",
"name": "User", "name": "User",


+ 25
- 16
frappe/desk/form/linked_with.py Näytä tiedosto

@@ -1,9 +1,10 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE # License: MIT. See LICENSE

import json import json
from collections import defaultdict from collections import defaultdict
import itertools import itertools
from typing import List
from typing import Dict, List, Optional


import frappe import frappe
import frappe.desk.form.load import frappe.desk.form.load
@@ -367,7 +368,7 @@ def get_exempted_doctypes():




@frappe.whitelist() @frappe.whitelist()
def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):
def get_linked_docs(doctype: str, name: str, linkinfo: Optional[Dict] = None) -> Dict[str, List]:
if isinstance(linkinfo, str): if isinstance(linkinfo, str):
# additional fields are added in linkinfo # additional fields are added in linkinfo
linkinfo = json.loads(linkinfo) linkinfo = json.loads(linkinfo)
@@ -377,23 +378,21 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):
if not linkinfo: if not linkinfo:
return results return results


if for_doctype:
links = frappe.get_doc(doctype, name).get_link_filters(for_doctype)

if links:
linkinfo = links

if for_doctype in linkinfo:
# only get linked with for this particular doctype
linkinfo = { for_doctype: linkinfo.get(for_doctype) }
else:
return results

for dt, link in linkinfo.items(): for dt, link in linkinfo.items():
filters = [] filters = []
link["doctype"] = dt link["doctype"] = dt
link_meta_bundle = frappe.desk.form.load.get_meta_bundle(dt)
try:
link_meta_bundle = frappe.desk.form.load.get_meta_bundle(dt)
except Exception as e:
if isinstance(e, frappe.DoesNotExistError):
if frappe.local.message_log:
frappe.local.message_log.pop()
continue
linkmeta = link_meta_bundle[0] linkmeta = link_meta_bundle[0]

if not linkmeta.has_permission():
continue

if not linkmeta.get("issingle"): if not linkmeta.get("issingle"):
fields = [d.fieldname for d in linkmeta.get("fields", { fields = [d.fieldname for d in linkmeta.get("fields", {
"in_list_view": 1, "in_list_view": 1,
@@ -456,6 +455,13 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):


return results return results



@frappe.whitelist()
def get(doctype, docname):
linked_doctypes = get_linked_doctypes(doctype=doctype)
return get_linked_docs(doctype=doctype, name=docname, linkinfo=linked_doctypes)


@frappe.whitelist() @frappe.whitelist()
def get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False): def get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False):
"""add list of doctypes this doctype is 'linked' with. """add list of doctypes this doctype is 'linked' with.
@@ -470,6 +476,7 @@ def get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False):
else: else:
return frappe.cache().hget("linked_doctypes", doctype, lambda: _get_linked_doctypes(doctype)) return frappe.cache().hget("linked_doctypes", doctype, lambda: _get_linked_doctypes(doctype))



def _get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False): def _get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False):
ret = {} ret = {}
# find fields where this doctype is linked # find fields where this doctype is linked
@@ -499,6 +506,7 @@ def _get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False)


return ret return ret



def get_linked_fields(doctype, without_ignore_user_permissions_enabled=False): def get_linked_fields(doctype, without_ignore_user_permissions_enabled=False):


filters = [['fieldtype','=', 'Link'], ['options', '=', doctype]] filters = [['fieldtype','=', 'Link'], ['options', '=', doctype]]
@@ -529,6 +537,7 @@ def get_linked_fields(doctype, without_ignore_user_permissions_enabled=False):


return ret return ret



def get_dynamic_linked_fields(doctype, without_ignore_user_permissions_enabled=False): def get_dynamic_linked_fields(doctype, without_ignore_user_permissions_enabled=False):
ret = {} ret = {}




+ 1
- 2
frappe/email/doctype/newsletter/newsletter.json Näytä tiedosto

@@ -236,8 +236,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_published_field": "published", "is_published_field": "published",
"links": [], "links": [],
"max_attachments": 3,
"modified": "2021-12-06 20:09:37.963141",
"modified": "2022-03-09 01:48:16.741603",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Email", "module": "Email",
"name": "Newsletter", "name": "Newsletter",


+ 4
- 3
frappe/email/doctype/notification/notification.py Näytä tiedosto

@@ -186,7 +186,7 @@ def get_context(context):


def send_an_email(self, doc, context): def send_an_email(self, doc, context):
from email.utils import formataddr from email.utils import formataddr
from frappe.core.doctype.communication.email import make as make_communication
from frappe.core.doctype.communication.email import _make as make_communication
subject = self.subject subject = self.subject
if "{" in subject: if "{" in subject:
subject = frappe.render_template(self.subject, context) subject = frappe.render_template(self.subject, context)
@@ -216,7 +216,8 @@ def get_context(context):
# Add mail notification to communication list # Add mail notification to communication list
# No need to add if it is already a communication. # No need to add if it is already a communication.
if doc.doctype != 'Communication': if doc.doctype != 'Communication':
make_communication(doctype=doc.doctype,
make_communication(
doctype=doc.doctype,
name=doc.name, name=doc.name,
content=message, content=message,
subject=subject, subject=subject,
@@ -228,7 +229,7 @@ def get_context(context):
cc=cc, cc=cc,
bcc=bcc, bcc=bcc,
communication_type='Automated Message', communication_type='Automated Message',
ignore_permissions=True)
)


def send_a_slack_msg(self, doc, context): def send_a_slack_msg(self, doc, context):
send_slack_message( send_slack_message(


+ 1
- 7
frappe/email/email_body.py Näytä tiedosto

@@ -259,17 +259,12 @@ def get_formatted_html(subject, message, footer=None, print_html=None,


email_account = email_account or EmailAccount.find_outgoing(match_by_email=sender) email_account = email_account or EmailAccount.find_outgoing(match_by_email=sender)


signature = None
if "<!-- signature-included -->" not in message:
signature = get_signature(email_account)

rendered_email = frappe.get_template("templates/emails/standard.html").render({ rendered_email = frappe.get_template("templates/emails/standard.html").render({
"brand_logo": get_brand_logo(email_account) if with_container or header else None, "brand_logo": get_brand_logo(email_account) if with_container or header else None,
"with_container": with_container, "with_container": with_container,
"site_url": get_url(), "site_url": get_url(),
"header": get_header(header), "header": get_header(header),
"content": message, "content": message,
"signature": signature,
"footer": get_footer(email_account, footer), "footer": get_footer(email_account, footer),
"title": subject, "title": subject,
"print_html": print_html, "print_html": print_html,
@@ -281,8 +276,7 @@ def get_formatted_html(subject, message, footer=None, print_html=None,
if unsubscribe_link: if unsubscribe_link:
html = html.replace("<!--unsubscribe link here-->", unsubscribe_link.html) html = html.replace("<!--unsubscribe link here-->", unsubscribe_link.html)


html = inline_style_in_html(html)
return html
return inline_style_in_html(html)


@frappe.whitelist() @frappe.whitelist()
def get_email_html(template, args, subject, header=None, with_container=False): def get_email_html(template, args, subject, header=None, with_container=False):


+ 2
- 2
frappe/model/rename_doc.py Näytä tiedosto

@@ -43,8 +43,8 @@ def update_document_title(


title_field = doc.meta.get_title_field() title_field = doc.meta.get_title_field()


title_updated = (title_field != "name") and (updated_title != doc.get(title_field))
name_updated = updated_name != doc.name
title_updated = updated_title and (title_field != "name") and (updated_title != doc.get(title_field))
name_updated = updated_name and (updated_name != doc.name)


if name_updated: if name_updated:
docname = rename_doc(doctype=doctype, old=docname, new=updated_name, merge=merge) docname = rename_doc(doctype=doctype, old=docname, new=updated_name, merge=merge)


+ 9
- 98
frappe/public/js/frappe/form/linked_with.js Näytä tiedosto

@@ -1,9 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See LICENSE




frappe.ui.form.LinkedWith = class LinkedWith { frappe.ui.form.LinkedWith = class LinkedWith {

constructor(opts) { constructor(opts) {
$.extend(this, opts); $.extend(this, opts);
} }
@@ -21,29 +20,23 @@ frappe.ui.form.LinkedWith = class LinkedWith {
} }


make_dialog() { make_dialog() {

this.dialog = new frappe.ui.Dialog({ this.dialog = new frappe.ui.Dialog({
title: __("Linked With") title: __("Linked With")
}); });


this.dialog.on_page_show = () => { this.dialog.on_page_show = () => {
// execute ajax calls sequentially
// 1. get linked doctypes
// 2. load all doctypes
// 3. load linked docs
this.get_linked_doctypes()
.then(() => this.load_doctypes())
.then(() => this.links_not_permitted_or_missing())
.then(() => this.get_linked_docs())
.then(() => this.make_html());
frappe.xcall(
"frappe.desk.form.linked_with.get",
{"doctype": cur_frm.doctype, "docname": cur_frm.docname},
).then(r => {
this.frm.__linked_docs = r;
}).then(() => this.make_html());
}; };
} }


make_html() { make_html() {
const linked_docs = this.frm.__linked_docs;

let html = ''; let html = '';
const linked_docs = this.frm.__linked_docs;
const linked_doctypes = Object.keys(linked_docs); const linked_doctypes = Object.keys(linked_docs);


if (linked_doctypes.length === 0) { if (linked_doctypes.length === 0) {
@@ -63,88 +56,6 @@ frappe.ui.form.LinkedWith = class LinkedWith {
$(this.dialog.body).html(html); $(this.dialog.body).html(html);
} }


load_doctypes() {
const already_loaded = Object.keys(locals.DocType);
let doctypes_to_load = [];

if (this.frm.__linked_doctypes) {
doctypes_to_load =
Object.keys(this.frm.__linked_doctypes)
.filter(doctype => !already_loaded.includes(doctype));
}

// load all doctypes asynchronously using with_doctype
const promises = doctypes_to_load.map(dt => {
return frappe.model.with_doctype(dt, () => {
if(frappe.listview_settings[dt]) {
// add additional fields to __linked_doctypes
this.frm.__linked_doctypes[dt].add_fields =
frappe.listview_settings[dt].add_fields;
}
});
});

return Promise.all(promises);
}

links_not_permitted_or_missing() {
let links = null;

if (this.frm.__linked_doctypes) {
links =
Object.keys(this.frm.__linked_doctypes)
.filter(frappe.model.can_get_report);
}

let flag;
if(!links) {
$(this.dialog.body).html(`${this.frm.__linked_doctypes
? __("Not enough permission to see links")
: __("Not Linked to any record")}`);
flag = true;
}
flag = false;

// reject Promise if not_permitted or missing
return new Promise(
(resolve, reject) => flag ? reject() : resolve()
);
}

get_linked_doctypes() {
return new Promise((resolve) => {
if (this.frm.__linked_doctypes) {
resolve();
}

frappe.call({
method: "frappe.desk.form.linked_with.get_linked_doctypes",
args: {
doctype: this.frm.doctype
},
callback: (r) => {
this.frm.__linked_doctypes = r.message;
resolve();
}
});
});
}

get_linked_docs() {
return frappe.call({
method: "frappe.desk.form.linked_with.get_linked_docs",
args: {
doctype: this.frm.doctype,
name: this.frm.docname,
linkinfo: this.frm.__linked_doctypes,
for_doctype: this.for_doctype
},
callback: (r) => {
this.frm.__linked_docs = r.message || {};
}
});
}

make_doc_head(heading) { make_doc_head(heading) {
return ` return `
<header class="level list-row list-row-head text-muted small"> <header class="level list-row list-row-head text-muted small">


+ 12
- 4
frappe/public/js/frappe/list/base_list.js Näytä tiedosto

@@ -760,6 +760,10 @@ class FilterArea {


const doctype_fields = this.list_view.meta.fields; const doctype_fields = this.list_view.meta.fields;
const title_field = this.list_view.meta.title_field; const title_field = this.list_view.meta.title_field;
const has_existing_filters = (
this.list_view.filters
&& this.list_view.filters.length > 0
);


fields = fields.concat( fields = fields.concat(
doctype_fields doctype_fields
@@ -794,13 +798,17 @@ class FilterArea {
options = options.join("\n"); options = options.join("\n");
} }
} }
let default_value =
fieldtype === "Link"
? frappe.defaults.get_user_default(options)
: null;

let default_value;

if (fieldtype === "Link" && !has_existing_filters) {
default_value = frappe.defaults.get_user_default(options);
}

if (["__default", "__global"].includes(default_value)) { if (["__default", "__global"].includes(default_value)) {
default_value = null; default_value = null;
} }

return { return {
fieldtype: fieldtype, fieldtype: fieldtype,
label: __(df.label), label: __(df.label),


+ 2
- 19
frappe/public/js/frappe/list/list_view.js Näytä tiedosto

@@ -83,32 +83,15 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
this.sort_by = this.view_user_settings.sort_by || "modified"; this.sort_by = this.view_user_settings.sort_by || "modified";
this.sort_order = this.view_user_settings.sort_order || "desc"; this.sort_order = this.view_user_settings.sort_order || "desc";


// set filters from user_settings or list_settings
if (
this.view_user_settings.filters &&
this.view_user_settings.filters.length
) {
// Priority 1: user_settings
const saved_filters = this.view_user_settings.filters;
this.filters = this.validate_filters(saved_filters);
} else {
// Priority 2: filters in listview_settings
this.filters = (this.settings.filters || []).map((f) => {
if (f.length === 3) {
f = [this.doctype, f[0], f[1], f[2]];
}
return f;
});
}

// build menu items // build menu items
this.menu_items = this.menu_items.concat(this.get_menu_items()); this.menu_items = this.menu_items.concat(this.get_menu_items());


// set filters from view_user_settings or list_settings
if ( if (
this.view_user_settings.filters && this.view_user_settings.filters &&
this.view_user_settings.filters.length this.view_user_settings.filters.length
) { ) {
// Priority 1: saved filters
// Priority 1: view_user_settings
const saved_filters = this.view_user_settings.filters; const saved_filters = this.view_user_settings.filters;
this.filters = this.validate_filters(saved_filters); this.filters = this.validate_filters(saved_filters);
} else { } else {


+ 4
- 3
frappe/public/js/frappe/views/reports/report_view.js Näytä tiedosto

@@ -125,11 +125,12 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
} }


after_render() { after_render() {
if (this.report_doc) {
this.set_dirty_state_for_custom_report();
} else {
if (!this.report_doc) {
this.save_report_settings(); this.save_report_settings();
} else if (!$.isEmptyObject(this.report_doc.json)) {
this.set_dirty_state_for_custom_report();
} }

if (!this.group_by) { if (!this.group_by) {
this.init_chart(); this.init_chart();
} }


+ 0
- 1
frappe/templates/emails/standard.html Näytä tiedosto

@@ -37,7 +37,6 @@
<tr> <tr>
<td valign="top"> <td valign="top">
<p>{{ content }}</p> <p>{{ content }}</p>
<p class="signature">{{ signature }}</p>
</td> </td>
</tr> </tr>
</table> </table>


+ 1
- 2
frappe/website/doctype/blog_post/blog_post.json Näytä tiedosto

@@ -213,8 +213,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_published_field": "published", "is_published_field": "published",
"links": [], "links": [],
"max_attachments": 5,
"modified": "2021-11-23 10:42:01.759723",
"modified": "2022-03-09 01:48:25.227295",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Website", "module": "Website",
"name": "Blog Post", "name": "Blog Post",


+ 1
- 2
frappe/website/doctype/web_page/web_page.json Näytä tiedosto

@@ -338,8 +338,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_published_field": "published", "is_published_field": "published",
"links": [], "links": [],
"max_attachments": 20,
"modified": "2022-01-03 13:01:48.182645",
"modified": "2022-03-09 01:45:28.548671",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Website", "module": "Website",
"name": "Web Page", "name": "Web Page",


+ 1
- 2
frappe/website/doctype/website_settings/website_settings.json Näytä tiedosto

@@ -420,8 +420,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"max_attachments": 10,
"modified": "2022-02-24 15:37:22.360138",
"modified": "2022-03-09 01:47:31.094462",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Website", "module": "Website",
"name": "Website Settings", "name": "Website Settings",


+ 1
- 0
requirements.txt Näytä tiedosto

@@ -29,6 +29,7 @@ maxminddb-geolite2==2018.703
num2words~=0.5.10 num2words~=0.5.10
oauthlib~=3.1.0 oauthlib~=3.1.0
openpyxl~=3.0.7 openpyxl~=3.0.7
parse~=1.19.0
passlib~=1.7.4 passlib~=1.7.4
paytmchecksum~=1.7.0 paytmchecksum~=1.7.0
pdfkit~=0.6.1 pdfkit~=0.6.1


Ladataan…
Peruuta
Tallenna