Merge branch 'develop' into cmd/ctrl-to-see-fieldname
This commit is contained in:
commit
1b8bf0f64c
13 changed files with 124 additions and 47 deletions
38
.github/workflows/release_notes.yml
vendored
Normal file
38
.github/workflows/release_notes.yml
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# This action:
|
||||
#
|
||||
# 1. Generates release notes using github API.
|
||||
# 2. Strips unnecessary info like chore/style etc from notes.
|
||||
# 3. Updates release info.
|
||||
|
||||
# This action needs to be maintained on all branches that do releases.
|
||||
|
||||
name: 'Release Notes'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag_name:
|
||||
description: 'Tag of release like v13.0.0'
|
||||
required: true
|
||||
type: string
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
regen-notes:
|
||||
name: 'Regenerate release notes'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Update notes
|
||||
run: |
|
||||
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' )
|
||||
RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/tags/$RELEASE_TAG | jq -r '.id')
|
||||
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/$RELEASE_ID -f body=$NEW_NOTES
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }}
|
||||
|
|
@ -13,9 +13,9 @@
|
|||
[
|
||||
"@semantic-release/git", {
|
||||
"assets": ["frappe/__init__.py"],
|
||||
"message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}"
|
||||
"message": "chore(release): Bumped to Version ${nextRelease.version}"
|
||||
}
|
||||
],
|
||||
"@semantic-release/github"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -532,7 +532,7 @@ def _mariadb():
|
|||
command = [
|
||||
mysql,
|
||||
"--port",
|
||||
frappe.conf.db_port or MariaDBDatabase.default_port,
|
||||
str(frappe.conf.db_port or MariaDBDatabase.default_port),
|
||||
"-u",
|
||||
frappe.conf.db_name,
|
||||
f"-p{frappe.conf.db_password}",
|
||||
|
|
@ -548,7 +548,13 @@ def _mariadb():
|
|||
|
||||
def _psql():
|
||||
psql = which("psql")
|
||||
subprocess.run([psql, "-d", frappe.conf.db_name])
|
||||
|
||||
host = frappe.conf.db_host or "127.0.0.1"
|
||||
port = frappe.conf.db_port or "5432"
|
||||
env = os.environ.copy()
|
||||
env["PGPASSWORD"] = frappe.conf.db_password
|
||||
conn_string = f"postgresql://{frappe.conf.db_name}@{host}:{port}/{frappe.conf.db_name}"
|
||||
subprocess.run([psql, conn_string], check=True, env=env)
|
||||
|
||||
|
||||
@click.command("jupyter")
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ class Contact(Document):
|
|||
def get_default_contact(doctype, name):
|
||||
"""Returns default contact for the given doctype, name"""
|
||||
out = frappe.db.sql(
|
||||
'''select parent,
|
||||
"""select parent,
|
||||
IFNULL((select is_primary_contact from tabContact c where c.name = dl.parent), 0)
|
||||
as is_primary_contact
|
||||
from
|
||||
|
|
@ -141,7 +141,7 @@ def get_default_contact(doctype, name):
|
|||
where
|
||||
dl.link_doctype=%s and
|
||||
dl.link_name=%s and
|
||||
dl.parenttype = 'Contact' ''',
|
||||
dl.parenttype = 'Contact' """,
|
||||
(doctype, name),
|
||||
as_dict=True,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -59,10 +59,7 @@ class CommunicationEmailMixin:
|
|||
* if email copy is requested by sender, then add sender to CC.
|
||||
* If this doc is created through inbound mail, then add doc owner to cc list
|
||||
* remove all the thread_notify disabled users.
|
||||
* Make sure that all users enabled in the system
|
||||
* Remove admin from email list
|
||||
|
||||
* FixMe: Removed adding TODO owners to cc list. Check if that is needed.
|
||||
* Remove standard users from email list
|
||||
"""
|
||||
if hasattr(self, "_final_cc"):
|
||||
return self._final_cc
|
||||
|
|
@ -80,13 +77,12 @@ class CommunicationEmailMixin:
|
|||
|
||||
cc = set(cc) - set(self.filter_thread_notification_disbled_users(cc))
|
||||
cc = cc - set(self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation))
|
||||
cc = cc - set(self.filter_disabled_users(cc))
|
||||
|
||||
# # Incase of inbound mail, to and cc already received the mail, no need to send again.
|
||||
if is_inbound_mail_communcation:
|
||||
cc = cc - set(self.cc_list() + self.to_list())
|
||||
|
||||
self._final_cc = list(filter(lambda id: id != "Administrator", cc))
|
||||
self._final_cc = [m for m in cc if m not in frappe.STANDARD_USERS]
|
||||
return self._final_cc
|
||||
|
||||
def get_mail_cc_with_displayname(self, is_inbound_mail_communcation=False, include_sender=False):
|
||||
|
|
@ -99,8 +95,7 @@ class CommunicationEmailMixin:
|
|||
"""
|
||||
* Thread_notify check
|
||||
* Email unsubscribe list
|
||||
* User must be enabled in the system
|
||||
* remove_administrator_from_email_list
|
||||
* remove standard users.
|
||||
"""
|
||||
if hasattr(self, "_final_bcc"):
|
||||
return self._final_bcc
|
||||
|
|
@ -110,13 +105,12 @@ class CommunicationEmailMixin:
|
|||
bcc = bcc - {self.sender_mailid}
|
||||
bcc = bcc - set(self.filter_thread_notification_disbled_users(bcc))
|
||||
bcc = bcc - set(self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation))
|
||||
bcc = bcc - set(self.filter_disabled_users(bcc))
|
||||
|
||||
# Incase of inbound mail, to and cc & bcc already received the mail, no need to send again.
|
||||
if is_inbound_mail_communcation:
|
||||
bcc = bcc - set(self.bcc_list() + self.to_list())
|
||||
|
||||
self._final_bcc = list(filter(lambda id: id != "Administrator", bcc))
|
||||
self._final_bcc = [m for m in bcc if m not in frappe.STANDARD_USERS]
|
||||
return self._final_bcc
|
||||
|
||||
def get_mail_bcc_with_displayname(self, is_inbound_mail_communcation=False):
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@ class TestCommunicationEmailMixin(FrappeTestCase):
|
|||
user = self.new_user(email="bcc+2@test.com", enabled=0)
|
||||
comm = self.new_communication(bcc=bcc_list)
|
||||
res = comm.get_mail_bcc_with_displayname()
|
||||
self.assertCountEqual(res, ["bcc+1@test.com"])
|
||||
self.assertCountEqual(res, bcc_list)
|
||||
user.delete()
|
||||
comm.delete()
|
||||
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ class ErrorSnapshot(Document):
|
|||
|
||||
def onload(self):
|
||||
if not self.parent_error_snapshot:
|
||||
self.db_set("seen", True, update_modified=False)
|
||||
self.db_set("seen", 1, update_modified=False)
|
||||
|
||||
for relapsed in frappe.get_all("Error Snapshot", filters={"parent_error_snapshot": self.name}):
|
||||
frappe.db.set_value("Error Snapshot", relapsed.name, "seen", True, update_modified=False)
|
||||
frappe.db.set_value("Error Snapshot", relapsed.name, "seen", 1, update_modified=False)
|
||||
|
||||
frappe.local.flags.commit = True
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ class ErrorSnapshot(Document):
|
|||
self.update({"parent_error_snapshot": parent["name"]})
|
||||
frappe.db.set_value("Error Snapshot", parent["name"], "relapses", parent["relapses"] + 1)
|
||||
if parent["seen"]:
|
||||
frappe.db.set_value("Error Snapshot", parent["name"], "seen", False)
|
||||
frappe.db.set_value("Error Snapshot", parent["name"], "seen", 0)
|
||||
|
||||
@staticmethod
|
||||
def clear_old_logs(days=30):
|
||||
|
|
|
|||
|
|
@ -2,24 +2,6 @@ frappe.ui.form.Control = class BaseControl {
|
|||
constructor(opts) {
|
||||
$.extend(this, opts);
|
||||
this.make();
|
||||
|
||||
// if developer_mode=1, show fieldname as tooltip
|
||||
if (frappe.boot.user && frappe.boot.developer_mode === 1 && this.$wrapper) {
|
||||
this.$wrapper.attr("title", __(this.df.fieldname));
|
||||
} else if (this.$wrapper) {
|
||||
// show fieldname as tooltip when cmd/ctrl key is pressed
|
||||
$(document).on("keydown", (e) => {
|
||||
if (e.metaKey) {
|
||||
this.$wrapper.attr("title", __(this.df.fieldname));
|
||||
}
|
||||
});
|
||||
$(document).on("keyup", (e) => {
|
||||
if (!e.metaKey) {
|
||||
this.$wrapper.removeAttr("title");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (this.render_input) {
|
||||
this.refresh();
|
||||
}
|
||||
|
|
@ -31,6 +13,7 @@ frappe.ui.form.Control = class BaseControl {
|
|||
.attr("data-fieldname", this.df.fieldname);
|
||||
this.wrapper = this.$wrapper.get(0);
|
||||
this.wrapper.fieldobj = this; // reference for event handlers
|
||||
this.$wrapper.append(`<span class="tooltip-content">${__(this.df.fieldname)}</span>`);
|
||||
}
|
||||
|
||||
make_wrapper() {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ frappe.ui.form.Layout = class Layout {
|
|||
}
|
||||
|
||||
this.setup_tab_events();
|
||||
this.setup_tooltip_events();
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
|
@ -529,6 +530,19 @@ frappe.ui.form.Layout = class Layout {
|
|||
});
|
||||
}
|
||||
|
||||
setup_tooltip_events() {
|
||||
$(document).on("keydown", (e) => {
|
||||
if (e.metaKey) {
|
||||
this.wrapper.addClass("show-tooltip");
|
||||
}
|
||||
});
|
||||
$(document).on("keyup", (e) => {
|
||||
if (!e.metaKey) {
|
||||
this.wrapper.removeClass("show-tooltip");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handle_tab(doctype, fieldname, shift) {
|
||||
let grid_row = null,
|
||||
prev = null,
|
||||
|
|
|
|||
|
|
@ -1256,8 +1256,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
|
||||
// shift select checkboxes
|
||||
if (e.shiftKey && this.$checkbox_cursor && !$target.is(this.$checkbox_cursor)) {
|
||||
const name_1 = this.$checkbox_cursor.data().name;
|
||||
const name_2 = $target.data().name;
|
||||
const name_1 = decodeURIComponent(this.$checkbox_cursor.data().name);
|
||||
const name_2 = decodeURIComponent($target.data().name);
|
||||
const index_1 = this.data.findIndex((d) => d.name === name_1);
|
||||
const index_2 = this.data.findIndex((d) => d.name === name_2);
|
||||
let [min_index, max_index] = [index_1, index_2];
|
||||
|
|
@ -1268,7 +1268,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
|
||||
let docnames = this.data.slice(min_index + 1, max_index).map((d) => d.name);
|
||||
const selector = docnames
|
||||
.map((name) => `.list-row-checkbox[data-name="${name}"]`)
|
||||
.map((name) => `.list-row-checkbox[data-name="${encodeURIComponent(name)}"]`)
|
||||
.join(",");
|
||||
this.$result.find(selector).prop("checked", true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,29 @@
|
|||
@import "../common/form.scss";
|
||||
@import '~cropperjs/dist/cropper.min';
|
||||
|
||||
.tooltip-content {
|
||||
position: absolute;
|
||||
bottom: 104%;
|
||||
left: 0;
|
||||
z-index: 9999;
|
||||
padding: 2px 6px;
|
||||
border-radius: var(--border-radius-sm);
|
||||
background: var(--gray-dark);
|
||||
color: var(--text-dark);
|
||||
font-size: var(--text-xs);
|
||||
opacity: 0;
|
||||
cursor: default;
|
||||
transition: opacity 0.3s, transform 3s;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.show-tooltip .frappe-control:hover .tooltip-content {
|
||||
opacity: 1;
|
||||
transform: translate3d(0,0,0);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
|
||||
.std-form-layout > .form-layout > .form-page {
|
||||
border-radius: var(--border-radius-md);
|
||||
box-shadow: var(--card-shadow);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import frappe.recorder
|
|||
from frappe.installer import add_to_installed_apps, remove_app
|
||||
from frappe.query_builder.utils import db_type_is
|
||||
from frappe.tests.test_query_builder import run_only_if
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.tests.utils import FrappeTestCase, timeout
|
||||
from frappe.utils import add_to_date, get_bench_path, get_bench_relative_path, now
|
||||
from frappe.utils.backups import BackupGenerator, fetch_latest_backups
|
||||
from frappe.utils.jinja_globals import bundled_asset
|
||||
|
|
@ -763,3 +763,10 @@ class TestCommandUtils(FrappeTestCase):
|
|||
app_groups = get_app_groups()
|
||||
self.assertIn("frappe", app_groups)
|
||||
self.assertIsInstance(app_groups["frappe"], click.Group)
|
||||
|
||||
|
||||
class TestDBCli(BaseTestCommands):
|
||||
@timeout(10)
|
||||
def test_db_cli(self):
|
||||
self.execute("bench --site {site} db-console", kwargs={"cmd_input": rb"\q"})
|
||||
self.assertEqual(self.returncode, 0)
|
||||
|
|
|
|||
|
|
@ -429,6 +429,18 @@ jobs:
|
|||
name: Server
|
||||
|
||||
services:
|
||||
redis-cache:
|
||||
image: redis:alpine
|
||||
ports:
|
||||
- 13000:6379
|
||||
redis-queue:
|
||||
image: redis:alpine
|
||||
ports:
|
||||
- 11000:6379
|
||||
redis-socketio:
|
||||
image: redis:alpine
|
||||
ports:
|
||||
- 12000:6379
|
||||
mariadb:
|
||||
image: mariadb:10.6
|
||||
env:
|
||||
|
|
@ -439,17 +451,17 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 14
|
||||
node-version: 16
|
||||
check-latest: true
|
||||
|
||||
- name: Cache pip
|
||||
|
|
@ -465,7 +477,7 @@ jobs:
|
|||
id: yarn-cache-dir-path
|
||||
run: 'echo "::set-output name=dir::$(yarn cache dir)"'
|
||||
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{{{ steps.yarn-cache-dir-path.outputs.dir }}}}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue