diff --git a/.github/workflows/patch-mariadb-tests.yml b/.github/workflows/patch-mariadb-tests.yml
new file mode 100644
index 0000000000..e8627a01fb
--- /dev/null
+++ b/.github/workflows/patch-mariadb-tests.yml
@@ -0,0 +1,83 @@
+name: Patch
+
+on: [pull_request, workflow_dispatch]
+
+jobs:
+ test:
+ runs-on: ubuntu-18.04
+
+ name: Patch Test
+
+ services:
+ mysql:
+ image: mariadb:10.3
+ env:
+ MYSQL_ALLOW_EMPTY_PASSWORD: YES
+ ports:
+ - 3306:3306
+ options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
+
+ steps:
+ - name: Clone
+ uses: actions/checkout@v2
+
+ - name: Setup Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.7
+
+ - name: Add to Hosts
+ run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
+
+ - name: Cache pip
+ uses: actions/cache@v2
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
+ restore-keys: |
+ ${{ runner.os }}-pip-
+ ${{ runner.os }}-
+
+ - name: Cache node modules
+ uses: actions/cache@v2
+ env:
+ cache-name: cache-node-modules
+ with:
+ path: ~/.npm
+ key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-build-${{ env.cache-name }}-
+ ${{ runner.os }}-build-
+ ${{ runner.os }}-
+
+ - name: Get yarn cache directory path
+ id: yarn-cache-dir-path
+ run: echo "::set-output name=dir::$(yarn cache dir)"
+
+ - uses: actions/cache@v2
+ id: yarn-cache
+ with:
+ path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
+ key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-yarn-
+
+ - name: Install Dependencies
+ run: bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
+ env:
+ BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
+ AFTER: ${{ env.GITHUB_EVENT_PATH.after }}
+ TYPE: server
+
+ - name: Install
+ run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
+ env:
+ DB: mariadb
+ TYPE: server
+
+ - name: Run Patch Tests
+ run: |
+ cd ~/frappe-bench/
+ wget https://frappeframework.com/files/v10-frappe.sql.gz
+ bench --site test_site --force restore ~/frappe-bench/v10-frappe.sql.gz
+ bench --site test_site migrate
diff --git a/.github/workflows/server-mariadb-tests.yml b/.github/workflows/server-mariadb-tests.yml
index 1c7655528c..2476102e3d 100644
--- a/.github/workflows/server-mariadb-tests.yml
+++ b/.github/workflows/server-mariadb-tests.yml
@@ -91,7 +91,6 @@ jobs:
DB: mariadb
TYPE: server
-
- name: Run Tests
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator --with-coverage
env:
diff --git a/cypress/integration/form.js b/cypress/integration/form.js
index 20ed7a61cd..909955c1df 100644
--- a/cypress/integration/form.js
+++ b/cypress/integration/form.js
@@ -18,6 +18,7 @@ context('Form', () => {
cy.get('.primary-action').click();
cy.wait('@form_save').its('response.statusCode').should('eq', 200);
cy.visit('/app/todo');
+ cy.wait(300);
cy.get('.title-text').should('be.visible').and('contain', 'To Do');
cy.get('.list-row').should('contain', 'this is a test todo');
});
diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js
index 5154adb634..efa1959969 100644
--- a/esbuild/esbuild.js
+++ b/esbuild/esbuild.js
@@ -258,12 +258,17 @@ function get_watch_config() {
async function clean_dist_folders(apps) {
for (let app of apps) {
let public_path = get_public_path(app);
- await fs.promises.rmdir(path.resolve(public_path, "dist", "js"), {
- recursive: true
- });
- await fs.promises.rmdir(path.resolve(public_path, "dist", "css"), {
- recursive: true
- });
+ let paths = [
+ path.resolve(public_path, "dist", "js"),
+ path.resolve(public_path, "dist", "css")
+ ];
+ for (let target of paths) {
+ if (fs.existsSync(target)) {
+ // rmdir is deprecated in node 16, this will work in both node 14 and 16
+ let rmdir = fs.promises.rm || fs.promises.rmdir;
+ await rmdir(target, { recursive: true });
+ }
+ }
}
}
diff --git a/frappe/__init__.py b/frappe/__init__.py
index bb4e409d61..01c7879a06 100644
--- a/frappe/__init__.py
+++ b/frappe/__init__.py
@@ -1110,9 +1110,7 @@ def setup_module_map():
if not (local.app_modules and local.module_app):
local.module_app, local.app_modules = {}, {}
- for app in get_all_apps(True):
- if app == "webnotes":
- app = "frappe"
+ for app in get_all_apps(with_internal_apps=True):
local.app_modules.setdefault(app, [])
for module in get_module_list(app):
module = scrub(module)
@@ -1493,7 +1491,7 @@ def get_print(doctype=None, name=None, print_format=None, style=None,
:param style: Print Format style.
:param as_pdf: Return as PDF. Default False.
:param password: Password to encrypt the pdf with. Default None"""
- from frappe.website.render import build_page
+ from frappe.website.serve import get_response_content
from frappe.utils.pdf import get_pdf
local.form_dict.doctype = doctype
@@ -1508,7 +1506,7 @@ def get_print(doctype=None, name=None, print_format=None, style=None,
options = {'password': password}
if not html:
- html = build_page("printview")
+ html = get_response_content("printview")
if as_pdf:
return get_pdf(html, output = output, options = options)
diff --git a/frappe/app.py b/frappe/app.py
index 6f5023be93..920628dda4 100644
--- a/frappe/app.py
+++ b/frappe/app.py
@@ -16,9 +16,9 @@ import frappe.handler
import frappe.auth
import frappe.api
import frappe.utils.response
-import frappe.website.render
from frappe.utils import get_site_name, sanitize_html
from frappe.middlewares import StaticDataMiddleware
+from frappe.website.serve import get_response
from frappe.utils.error import make_error_snapshot
from frappe.core.doctype.comment.comment import update_comments_in_parent_after_request
from frappe import _
@@ -72,7 +72,7 @@ def application(request):
response = frappe.utils.response.download_private_file(request.path)
elif request.method in ('GET', 'HEAD', 'POST'):
- response = frappe.website.render.render()
+ response = get_response()
else:
raise NotFound
@@ -266,8 +266,7 @@ def handle_exception(e):
make_error_snapshot(e)
if return_as_message:
- response = frappe.website.render.render("message",
- http_status_code=http_status_code)
+ response = get_response("message", http_status_code=http_status_code)
return response
diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py
index 998e73a42c..d2afda1553 100644
--- a/frappe/automation/doctype/auto_repeat/auto_repeat.py
+++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py
@@ -333,7 +333,7 @@ class AutoRepeat(Document):
if self.reference_doctype and self.reference_document:
res = get_contacts_linking_to(self.reference_doctype, self.reference_document, fields=['email_id'])
res += get_contacts_linked_from(self.reference_doctype, self.reference_document, fields=['email_id'])
- email_ids = list(set([d.email_id for d in res]))
+ email_ids = {d.email_id for d in res}
if not email_ids:
frappe.msgprint(_('No contacts linked to document'), alert=True)
else:
diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py
index 52fba4568d..9f09f26be8 100644
--- a/frappe/cache_manager.py
+++ b/frappe/cache_manager.py
@@ -53,7 +53,7 @@ def clear_domain_cache(user=None):
cache.delete_value(domain_cache_keys)
def clear_global_cache():
- from frappe.website.render import clear_cache as clear_website_cache
+ from frappe.website.utils import clear_website_cache
clear_doctype_cache()
clear_website_cache()
diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py
index bcb1749644..c16de497ec 100644
--- a/frappe/commands/utils.py
+++ b/frappe/commands/utils.py
@@ -69,14 +69,14 @@ def watch(apps=None):
def clear_cache(context):
"Clear cache, doctype cache and defaults"
import frappe.sessions
- import frappe.website.render
+ from frappe.website.utils import clear_website_cache
from frappe.desk.notifications import clear_notifications
for site in context.sites:
try:
frappe.connect(site)
frappe.clear_cache()
clear_notifications()
- frappe.website.render.clear_cache()
+ clear_website_cache()
finally:
frappe.destroy()
if not context.sites:
@@ -86,12 +86,12 @@ def clear_cache(context):
@pass_context
def clear_website_cache(context):
"Clear website cache"
- import frappe.website.render
+ from frappe.website.utils import clear_website_cache
for site in context.sites:
try:
frappe.init(site=site)
frappe.connect()
- frappe.website.render.clear_cache()
+ clear_website_cache()
finally:
frappe.destroy()
if not context.sites:
@@ -572,22 +572,29 @@ def run_tests(context, app=None, module=None, doctype=None, test=(), profile=Fal
# Generate coverage report only for app that is being tested
source_path = os.path.join(get_bench_path(), 'apps', app or 'frappe')
- omit=[
- '*.html',
+ incl = [
+ '*.py',
+ ]
+ omit = [
'*.js',
'*.xml',
+ '*.pyc',
'*.css',
'*.less',
'*.scss',
'*.vue',
+ '*.html',
+ '*/test_*',
+ '*/node_modules/*',
'*/doctype/*/*_dashboard.py',
- '*/patches/*'
+ '*/patches/*',
]
if not app or app == 'frappe':
+ omit.append('*/tests/*')
omit.append('*/commands/*')
- cov = Coverage(source=[source_path], omit=omit)
+ cov = Coverage(source=[source_path], omit=omit, include=incl)
cov.start()
ret = frappe.test_runner.main(app, module, doctype, context.verbose, tests=tests,
diff --git a/frappe/contacts/address_and_contact.py b/frappe/contacts/address_and_contact.py
index f21819ad98..77305168c1 100644
--- a/frappe/contacts/address_and_contact.py
+++ b/frappe/contacts/address_and_contact.py
@@ -153,7 +153,7 @@ def filter_dynamic_link_doctypes(doctype, txt, searchfield, start, page_len, fil
doctypes = frappe.db.get_all("DocField", filters=filters, fields=["parent"],
distinct=True, as_list=True)
- doctypes = tuple([d for d in doctypes if re.search(txt+".*", _(d[0]), re.IGNORECASE)])
+ doctypes = tuple(d for d in doctypes if re.search(txt+".*", _(d[0]), re.IGNORECASE))
filters.update({
"dt": ("not in", [d[0] for d in doctypes])
diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py
index bfcf91427d..755bc63064 100644
--- a/frappe/contacts/doctype/address/address.py
+++ b/frappe/contacts/doctype/address/address.py
@@ -257,7 +257,7 @@ def address_query(doctype, txt, searchfield, start, page_len, filters):
def get_condensed_address(doc):
fields = ["address_title", "address_line1", "address_line2", "city", "county", "state", "country"]
- return ", ".join([doc.get(d) for d in fields if doc.get(d)])
+ return ", ".join(doc.get(d) for d in fields if doc.get(d))
def update_preferred_address(address, field):
frappe.db.set_value('Address', address, field, 0)
diff --git a/frappe/core/doctype/comment/comment.py b/frappe/core/doctype/comment/comment.py
index e29bae25a2..2706ab1c30 100644
--- a/frappe/core/doctype/comment/comment.py
+++ b/frappe/core/doctype/comment/comment.py
@@ -9,7 +9,7 @@ from frappe.core.doctype.user.user import extract_mentions
from frappe.desk.doctype.notification_log.notification_log import enqueue_create_notification,\
get_title, get_title_html
from frappe.utils import get_fullname
-from frappe.website.render import clear_cache
+from frappe.website.utils import clear_cache
from frappe.database.schema import add_column
from frappe.exceptions import ImplicitCommitError
diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py
index 0caa565e2c..17b1290776 100644
--- a/frappe/core/doctype/communication/communication.py
+++ b/frappe/core/doctype/communication/communication.py
@@ -6,10 +6,11 @@ import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import validate_email_address, strip_html, cstr, time_diff_in_seconds
-from frappe.core.doctype.communication.email import validate_email, notify, _notify
+from frappe.core.doctype.communication.email import validate_email
+from frappe.core.doctype.communication.mixins import CommunicationEmailMixin
from frappe.core.utils import get_parent_doc
from frappe.utils.bot import BotReply
-from frappe.utils import parse_addr
+from frappe.utils import parse_addr, split_emails
from frappe.core.doctype.comment.comment import update_comment_in_doc
from email.utils import parseaddr
from urllib.parse import unquote
@@ -19,7 +20,7 @@ from frappe.automation.doctype.assignment_rule.assignment_rule import apply as a
exclude_from_linked_with = True
-class Communication(Document):
+class Communication(Document, CommunicationEmailMixin):
"""Communication represents an external communication like Email.
"""
no_feed_on_delete = True
@@ -125,6 +126,45 @@ class Communication(Document):
if self.communication_type == "Communication":
self.notify_change('delete')
+ @property
+ def sender_mailid(self):
+ return parse_addr(self.sender)[1] if self.sender else ""
+
+ @staticmethod
+ def _get_emails_list(emails=None, exclude_displayname = False):
+ """Returns list of emails from given email string.
+
+ * Removes duplicate mailids
+ * Removes display name from email address if exclude_displayname is True
+ """
+ emails = split_emails(emails) if isinstance(emails, str) else (emails or [])
+ if exclude_displayname:
+ return [email.lower() for email in set([parse_addr(email)[1] for email in emails]) if email]
+ return [email.lower() for email in set(emails) if email]
+
+ def to_list(self, exclude_displayname = True):
+ """Returns to list.
+ """
+ return self._get_emails_list(self.recipients, exclude_displayname=exclude_displayname)
+
+ def cc_list(self, exclude_displayname = True):
+ """Returns cc list.
+ """
+ return self._get_emails_list(self.cc, exclude_displayname=exclude_displayname)
+
+ def bcc_list(self, exclude_displayname = True):
+ """Returns bcc list.
+ """
+ return self._get_emails_list(self.bcc, exclude_displayname=exclude_displayname)
+
+ def get_attachments(self):
+ attachments = frappe.get_all(
+ "File",
+ fields=["name", "file_name", "file_url", "is_private"],
+ filters = {"attached_to_name": self.name, "attached_to_doctype": self.DOCTYPE}
+ )
+ return attachments
+
def notify_change(self, action):
frappe.publish_realtime('update_docinfo_for_{}_{}'.format(self.reference_doctype, self.reference_name), {
'doc': self.as_dict(),
@@ -198,36 +238,6 @@ class Communication(Document):
if not self.sender_full_name:
self.sender_full_name = sender_email
- def send(self, print_html=None, print_format=None, attachments=None,
- send_me_a_copy=False, recipients=None):
- """Send communication via Email.
-
- :param print_html: Send given value as HTML attachment.
- :param print_format: Attach print format of parent document."""
-
- self.send_me_a_copy = send_me_a_copy
- self.notify(print_html, print_format, attachments, recipients)
-
- def notify(self, print_html=None, print_format=None, attachments=None,
- recipients=None, cc=None, bcc=None,fetched_from_email_account=False):
- """Calls a delayed task 'sendmail' that enqueus email in Email Queue queue
-
- :param print_html: Send given value as HTML attachment
- :param print_format: Attach print format of parent document
- :param attachments: A list of filenames that should be attached when sending this email
- :param recipients: Email recipients
- :param cc: Send email as CC to
- :param fetched_from_email_account: True when pulling email, the notification shouldn't go to the main recipient
-
- """
- notify(self, print_html, print_format, attachments, recipients, cc, bcc,
- fetched_from_email_account)
-
- def _notify(self, print_html=None, print_format=None, attachments=None,
- recipients=None, cc=None, bcc=None):
-
- _notify(self, print_html, print_format, attachments, recipients, cc, bcc)
-
def bot_reply(self):
if self.comment_type == 'Bot' and self.communication_type == 'Chat':
reply = BotReply().get_reply(self.content)
@@ -504,3 +514,4 @@ def set_avg_response_time(parent, communication):
if response_times:
avg_response_time = sum(response_times) / len(response_times)
parent.db_set("avg_response_time", avg_response_time)
+
diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py
index c28956b41f..d35c118550 100755
--- a/frappe/core/doctype/communication/email.py
+++ b/frappe/core/doctype/communication/email.py
@@ -13,6 +13,11 @@ import time
from frappe import _
from frappe.utils.background_jobs import enqueue
+OUTGOING_EMAIL_ACCOUNT_MISSING = _("""
+ Unable to send mail because of a missing email account.
+ Please setup default Email Account from Setup > Email > Email Account
+""")
+
@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,
@@ -36,7 +41,6 @@ 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 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)
@@ -84,12 +88,16 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
frappe.db.commit()
if cint(send_email):
- frappe.flags.print_letterhead = cint(print_letterhead)
- comm.send(print_html, print_format, attachments, send_me_a_copy=send_me_a_copy)
+ if not comm.get_outgoing_email_account():
+ 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)
+
+ emails_not_sent_to = comm.exclude_emails_list(include_sender=send_me_a_copy)
return {
"name": comm.name,
- "emails_not_sent_to": ", ".join(comm.emails_not_sent_to) if hasattr(comm, "emails_not_sent_to") else None
+ "emails_not_sent_to": ", ".join(emails_not_sent_to or [])
}
def validate_email(doc):
@@ -110,164 +118,6 @@ def validate_email(doc):
# validate sender
-def notify(doc, print_html=None, print_format=None, attachments=None,
- recipients=None, cc=None, bcc=None, fetched_from_email_account=False):
- """Calls a delayed task 'sendmail' that enqueus email in Email Queue queue
-
- :param print_html: Send given value as HTML attachment
- :param print_format: Attach print format of parent document
- :param attachments: A list of filenames that should be attached when sending this email
- :param recipients: Email recipients
- :param cc: Send email as CC to
- :param bcc: Send email as BCC to
- :param fetched_from_email_account: True when pulling email, the notification shouldn't go to the main recipient
-
- """
- recipients, cc, bcc = get_recipients_cc_and_bcc(doc, recipients, cc, bcc,
- fetched_from_email_account=fetched_from_email_account)
-
- if not recipients and not cc:
- return
-
- doc.emails_not_sent_to = set(doc.all_email_addresses) - set(doc.sent_email_addresses)
-
- if frappe.flags.in_test:
- # for test cases, run synchronously
- doc._notify(print_html=print_html, print_format=print_format, attachments=attachments,
- recipients=recipients, cc=cc, bcc=None)
- else:
- enqueue(sendmail, queue="default", timeout=300, event="sendmail",
- communication_name=doc.name,
- print_html=print_html, print_format=print_format, attachments=attachments,
- recipients=recipients, cc=cc, bcc=bcc, lang=frappe.local.lang,
- session=frappe.local.session, print_letterhead=frappe.flags.print_letterhead)
-
-def _notify(doc, print_html=None, print_format=None, attachments=None,
- recipients=None, cc=None, bcc=None):
-
- prepare_to_notify(doc, print_html, print_format, attachments)
-
- if doc.outgoing_email_account.send_unsubscribe_message:
- unsubscribe_message = _("Leave this conversation")
- else:
- unsubscribe_message = ""
-
- frappe.sendmail(
- recipients=(recipients or []),
- cc=(cc or []),
- bcc=(bcc or []),
- expose_recipients="header",
- sender=doc.sender,
- reply_to=doc.incoming_email_account,
- subject=doc.subject,
- content=doc.content,
- reference_doctype=doc.reference_doctype,
- reference_name=doc.reference_name,
- attachments=doc.attachments,
- message_id=doc.message_id,
- unsubscribe_message=unsubscribe_message,
- delayed=True,
- communication=doc.name,
- read_receipt=doc.read_receipt,
- is_notification=True if doc.sent_or_received =="Received" else False,
- print_letterhead=frappe.flags.print_letterhead
- )
-
-def get_recipients_cc_and_bcc(doc, recipients, cc, bcc, fetched_from_email_account=False):
- doc.all_email_addresses = []
- doc.sent_email_addresses = []
- doc.previous_email_sender = None
-
- if not recipients:
- recipients = get_recipients(doc, fetched_from_email_account=fetched_from_email_account)
-
- if not cc:
- cc = get_cc(doc, recipients, fetched_from_email_account=fetched_from_email_account)
-
- if not bcc:
- bcc = get_bcc(doc, recipients, fetched_from_email_account=fetched_from_email_account)
-
- if fetched_from_email_account:
- # email was already sent to the original recipient by the sender's email service
- original_recipients, recipients = recipients, []
-
- # send email to the sender of the previous email in the thread which this email is a reply to
- #provides erratic results and can send external
- #if doc.previous_email_sender:
- # recipients.append(doc.previous_email_sender)
-
- # cc that was received in the email
- original_cc = split_emails(doc.cc)
-
- # don't cc to people who already received the mail from sender's email service
- cc = list(set(cc) - set(original_cc) - set(original_recipients))
- remove_administrator_from_email_list(cc)
-
- original_bcc = split_emails(doc.bcc)
- bcc = list(set(bcc) - set(original_bcc) - set(original_recipients))
- remove_administrator_from_email_list(bcc)
-
- remove_administrator_from_email_list(recipients)
-
- return recipients, cc, bcc
-
-def remove_administrator_from_email_list(email_list):
- administrator_email = list(filter(lambda emails: "Administrator" in emails, email_list))
- if administrator_email:
- email_list.remove(administrator_email[0])
-
-def prepare_to_notify(doc, print_html=None, print_format=None, attachments=None):
- """Prepare to make multipart MIME Email
-
- :param print_html: Send given value as HTML attachment.
- :param print_format: Attach print format of parent document."""
-
- view_link = frappe.utils.cint(frappe.db.get_value("System Settings", "System Settings", "attach_view_link"))
-
- if print_format and view_link:
- doc.content += get_attach_link(doc, print_format)
-
- set_incoming_outgoing_accounts(doc)
-
- if not doc.sender:
- doc.sender = doc.outgoing_email_account.email_id
-
- if not doc.sender_full_name:
- doc.sender_full_name = doc.outgoing_email_account.name or _("Notification")
-
- if doc.sender:
- # combine for sending to get the format 'Jane {0} ({1})
" * 2 + _("Only Options allowed for Data field are:") + "
"
- df_options_str = "
"
+ df_options_str = "
"
frappe.msgprint(text_str + df_options_str, title="Invalid Data Field", raise_exception=True)
diff --git a/frappe/core/doctype/domain/domain.py b/frappe/core/doctype/domain/domain.py
index 681824bb02..bbd20f3b70 100644
--- a/frappe/core/doctype/domain/domain.py
+++ b/frappe/core/doctype/domain/domain.py
@@ -110,7 +110,7 @@ class Domain(Document):
# enable
frappe.db.sql('''update `tabPortal Menu Item` set enabled=1
- where route in ({0})'''.format(', '.join(['"{0}"'.format(d) for d in self.data.allow_sidebar_items])))
+ where route in ({0})'''.format(', '.join('"{0}"'.format(d) for d in self.data.allow_sidebar_items)))
if self.data.remove_sidebar_items:
# disable all
@@ -118,4 +118,4 @@ class Domain(Document):
# enable
frappe.db.sql('''update `tabPortal Menu Item` set enabled=0
- where route in ({0})'''.format(', '.join(['"{0}"'.format(d) for d in self.data.remove_sidebar_items])))
+ where route in ({0})'''.format(', '.join('"{0}"'.format(d) for d in self.data.remove_sidebar_items)))
diff --git a/frappe/patches/v4_0/__init__.py b/frappe/core/doctype/feedback/__init__.py
similarity index 100%
rename from frappe/patches/v4_0/__init__.py
rename to frappe/core/doctype/feedback/__init__.py
diff --git a/frappe/core/doctype/feedback/feedback.js b/frappe/core/doctype/feedback/feedback.js
new file mode 100644
index 0000000000..131f0e19d8
--- /dev/null
+++ b/frappe/core/doctype/feedback/feedback.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Feedback', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/frappe/core/doctype/feedback/feedback.json b/frappe/core/doctype/feedback/feedback.json
new file mode 100644
index 0000000000..cf8a180e27
--- /dev/null
+++ b/frappe/core/doctype/feedback/feedback.json
@@ -0,0 +1,86 @@
+{
+ "actions": [],
+ "creation": "2021-06-03 19:02:55.328423",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "reference_doctype",
+ "reference_name",
+ "column_break_3",
+ "email",
+ "rating",
+ "section_break_6",
+ "feedback"
+ ],
+ "fields": [
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "email",
+ "fieldtype": "Data",
+ "label": "Email",
+ "reqd": 1
+ },
+ {
+ "fieldname": "rating",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Rating",
+ "precision": "1",
+ "reqd": 1
+ },
+ {
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "feedback",
+ "fieldtype": "Small Text",
+ "label": "Feedback",
+ "reqd": 1
+ },
+ {
+ "fieldname": "reference_doctype",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Reference Document Type",
+ "options": "\nBlog Post"
+ },
+ {
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "in_list_view": 1,
+ "label": "Reference Name",
+ "options": "reference_doctype",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-06-14 15:11:26.005805",
+ "modified_by": "Administrator",
+ "module": "Core",
+ "name": "Feedback",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "reference_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/frappe/core/doctype/feedback/feedback.py b/frappe/core/doctype/feedback/feedback.py
new file mode 100644
index 0000000000..655bed6eb1
--- /dev/null
+++ b/frappe/core/doctype/feedback/feedback.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+class Feedback(Document):
+ pass
diff --git a/frappe/core/doctype/feedback/test_feedback.py b/frappe/core/doctype/feedback/test_feedback.py
new file mode 100644
index 0000000000..702f9d8ac1
--- /dev/null
+++ b/frappe/core/doctype/feedback/test_feedback.py
@@ -0,0 +1,27 @@
+# Copyright (c) 2021, Frappe Technologies and Contributors
+# See license.txt
+
+import frappe
+import unittest
+
+class TestFeedback(unittest.TestCase):
+ def test_feedback_creation_updation(self):
+ from frappe.website.doctype.blog_post.test_blog_post import make_test_blog
+ test_blog = make_test_blog()
+
+ frappe.db.sql("delete from `tabFeedback` where reference_doctype = 'Blog Post'")
+
+ from frappe.templates.includes.feedback.feedback import add_feedback, update_feedback
+ feedback = add_feedback('Blog Post', test_blog.name, 5, 'New feedback','test@test.com')
+
+ self.assertEqual(feedback.feedback, 'New feedback')
+ self.assertEqual(feedback.rating, 5)
+
+ updated_feedback = update_feedback('Blog Post', test_blog.name, 6, 'Updated feedback', 'test@test.com')
+
+ self.assertEqual(updated_feedback.feedback, 'Updated feedback')
+ self.assertEqual(updated_feedback.rating, 6)
+
+ frappe.db.sql("delete from `tabFeedback` where reference_doctype = 'Blog Post'")
+
+ test_blog.delete()
\ No newline at end of file
diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py
index 3fa31cbf80..5b605504e8 100644
--- a/frappe/core/doctype/user/user.py
+++ b/frappe/core/doctype/user/user.py
@@ -13,7 +13,7 @@ from frappe.utils.password import update_password as _update_password, check_pas
from frappe.desk.notifications import clear_notifications
from frappe.desk.doctype.notification_settings.notification_settings import create_notification_settings, toggle_notifications
from frappe.utils.user import get_system_managers
-from frappe.website.utils import is_signup_enabled
+from frappe.website.utils import is_signup_disabled
from frappe.rate_limiter import rate_limit
from frappe.utils.background_jobs import enqueue
from frappe.core.doctype.user_type.user_type import user_linked_with_permission_on_doctype
@@ -839,7 +839,7 @@ def verify_password(password):
@frappe.whitelist(allow_guest=True)
def sign_up(email, full_name, redirect_to):
- if not is_signup_enabled():
+ if is_signup_disabled():
frappe.throw(_('Sign Up is disabled'), title='Not Allowed')
user = frappe.db.get("User", {"email": email})
@@ -931,7 +931,7 @@ def user_query(doctype, txt, searchfield, start, page_len, filters):
LIMIT %(page_len)s OFFSET %(start)s
""".format(
user_type_condition = user_type_condition,
- standard_users=", ".join([frappe.db.escape(u) for u in STANDARD_USERS]),
+ standard_users=", ".join(frappe.db.escape(u) for u in STANDARD_USERS),
key=searchfield,
fcond=get_filters_cond(doctype, filters, conditions),
mcond=get_match_cond(doctype)
diff --git a/frappe/core/doctype/user_permission/user_permission.py b/frappe/core/doctype/user_permission/user_permission.py
index 42ca4d7a14..4aa5797c7f 100644
--- a/frappe/core/doctype/user_permission/user_permission.py
+++ b/frappe/core/doctype/user_permission/user_permission.py
@@ -16,11 +16,11 @@ class UserPermission(Document):
self.validate_default_permission()
def on_update(self):
- frappe.cache().delete_value('user_permissions')
+ frappe.cache().hdel('user_permissions', self.user)
frappe.publish_realtime('update_user_permissions')
def on_trash(self): # pylint: disable=no-self-use
- frappe.cache().delete_value('user_permissions')
+ frappe.cache().hdel('user_permissions', self.user)
frappe.publish_realtime('update_user_permissions')
def validate_user_permission(self):
diff --git a/frappe/core/doctype/user_type/user_type.py b/frappe/core/doctype/user_type/user_type.py
index e7d06c45f2..82ffb090f1 100644
--- a/frappe/core/doctype/user_type/user_type.py
+++ b/frappe/core/doctype/user_type/user_type.py
@@ -112,7 +112,7 @@ class UserType(Document):
self.select_doctypes = []
select_doctypes = []
- user_doctypes = tuple([row.document_type for row in self.user_doctypes])
+ user_doctypes = [row.document_type for row in self.user_doctypes]
for doctype in user_doctypes:
doc = frappe.get_meta(doctype)
@@ -265,4 +265,4 @@ def apply_permissions_for_non_standard_user_type(doc, method=None):
user_doc.update_children()
add_user_permission(doc.doctype, doc.name, doc.get(data[1]))
else:
- frappe.db.set_value('User Permission', perm_data[0], 'user', doc.get(data[1]))
\ No newline at end of file
+ frappe.db.set_value('User Permission', perm_data[0], 'user', doc.get(data[1]))
diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py
index 8bcc6cf059..1b8977acc4 100644
--- a/frappe/custom/doctype/customize_form/customize_form.py
+++ b/frappe/custom/doctype/customize_form/customize_form.py
@@ -355,9 +355,9 @@ class CustomizeForm(Document):
def delete_custom_fields(self):
meta = frappe.get_meta(self.doc_type)
- fields_to_remove = (set([df.fieldname for df in meta.get("fields")])
- - set(df.fieldname for df in self.get("fields")))
-
+ fields_to_remove = (
+ {df.fieldname for df in meta.get("fields")} - {df.fieldname for df in self.get("fields")}
+ )
for fieldname in fields_to_remove:
df = meta.get("fields", {"fieldname": fieldname})[0]
if df.get("is_custom_field"):
diff --git a/frappe/database/database.py b/frappe/database/database.py
index 7e8d2da43b..81e24cc7ad 100644
--- a/frappe/database/database.py
+++ b/frappe/database/database.py
@@ -335,7 +335,7 @@ class Database(object):
values[key] = value[1]
if isinstance(value[1], (tuple, list)):
# value is a list in tuple ("in", ("A", "B"))
- _rhs = " ({0})".format(", ".join([self.escape(v) for v in value[1]]))
+ _rhs = " ({0})".format(", ".join(self.escape(v) for v in value[1]))
del values[key]
if _operator not in ["=", "!=", ">", ">=", "<", "<=", "like", "in", "not in", "not like"]:
@@ -1010,7 +1010,7 @@ class Database(object):
:params values: list of list of values
"""
insert_list = []
- fields = ", ".join(["`"+field+"`" for field in fields])
+ fields = ", ".join("`"+field+"`" for field in fields)
for idx, value in enumerate(values):
insert_list.append(tuple(value))
diff --git a/frappe/patches/v4_1/__init__.py b/frappe/desk/doctype/form_tour/__init__.py
similarity index 100%
rename from frappe/patches/v4_1/__init__.py
rename to frappe/desk/doctype/form_tour/__init__.py
diff --git a/frappe/desk/doctype/form_tour/form_tour.js b/frappe/desk/doctype/form_tour/form_tour.js
new file mode 100644
index 0000000000..94c6806b50
--- /dev/null
+++ b/frappe/desk/doctype/form_tour/form_tour.js
@@ -0,0 +1,24 @@
+// Copyright (c) 2021, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Form Tour', {
+ setup: function(frm) {
+ frm.set_query("reference_doctype", function() {
+ return {
+ filters: {
+ istable: 0
+ }
+ };
+ });
+
+ frm.set_query("field", "steps", function() {
+ return {
+ query: "frappe.desk.doctype.form_tour.form_tour.get_docfield_list",
+ filters: {
+ doctype: frm.doc.reference_doctype,
+ hidden: 0
+ }
+ };
+ });
+ }
+});
diff --git a/frappe/desk/doctype/form_tour/form_tour.json b/frappe/desk/doctype/form_tour/form_tour.json
new file mode 100644
index 0000000000..8e09a5d63a
--- /dev/null
+++ b/frappe/desk/doctype/form_tour/form_tour.json
@@ -0,0 +1,75 @@
+{
+ "actions": [],
+ "autoname": "field:title",
+ "creation": "2021-05-21 23:02:52.242721",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "title",
+ "reference_doctype",
+ "completed",
+ "section_break_3",
+ "steps"
+ ],
+ "fields": [
+ {
+ "fieldname": "reference_doctype",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Reference Document",
+ "options": "DocType",
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "fieldname": "steps",
+ "fieldtype": "Table",
+ "label": "Steps",
+ "options": "Form Tour Step",
+ "reqd": 1
+ },
+ {
+ "default": "0",
+ "depends_on": "eval: doc.__islocal != 1",
+ "fieldname": "completed",
+ "fieldtype": "Check",
+ "label": "Mark as Completed"
+ },
+ {
+ "fieldname": "section_break_3",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "label": "Title",
+ "reqd": 1,
+ "unique": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-05-26 19:36:59.093753",
+ "modified_by": "Administrator",
+ "module": "Desk",
+ "name": "Form Tour",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/frappe/desk/doctype/form_tour/form_tour.py b/frappe/desk/doctype/form_tour/form_tour.py
new file mode 100644
index 0000000000..dd762395c4
--- /dev/null
+++ b/frappe/desk/doctype/form_tour/form_tour.py
@@ -0,0 +1,32 @@
+# Copyright (c) 2021, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe.model.document import Document
+
+class FormTour(Document):
+ pass
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_docfield_list(doctype, txt, searchfield, start, page_len, filters):
+ or_filters = [
+ ['fieldname', 'like', '%' + txt + '%'],
+ ['label', 'like', '%' + txt + '%'],
+ ['fieldtype', 'like', '%' + txt + '%']
+ ]
+
+ parent_doctype = filters.pop('doctype')
+ excluded_fieldtypes = ['Column Break']
+ excluded_fieldtypes += filters.get('excluded_fieldtypes', [])
+
+ docfields = frappe.get_all(
+ doctype,
+ fields=["name as value", "label", "fieldtype"],
+ filters={'parent': parent_doctype, 'fieldtype': ['not in', excluded_fieldtypes]},
+ or_filters=or_filters,
+ limit_start=start,
+ limit_page_length=page_len,
+ as_list=1,
+ )
+ return docfields
diff --git a/frappe/desk/doctype/form_tour/test_form_tour.py b/frappe/desk/doctype/form_tour/test_form_tour.py
new file mode 100644
index 0000000000..a4a796ce41
--- /dev/null
+++ b/frappe/desk/doctype/form_tour/test_form_tour.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+class TestFormTour(unittest.TestCase):
+ pass
diff --git a/frappe/patches/v4_2/__init__.py b/frappe/desk/doctype/form_tour_step/__init__.py
similarity index 100%
rename from frappe/patches/v4_2/__init__.py
rename to frappe/desk/doctype/form_tour_step/__init__.py
diff --git a/frappe/desk/doctype/form_tour_step/form_tour_step.json b/frappe/desk/doctype/form_tour_step/form_tour_step.json
new file mode 100644
index 0000000000..a772a2498a
--- /dev/null
+++ b/frappe/desk/doctype/form_tour_step/form_tour_step.json
@@ -0,0 +1,85 @@
+{
+ "actions": [],
+ "creation": "2021-05-21 23:05:45.342114",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "field",
+ "title",
+ "description",
+ "column_break_2",
+ "position",
+ "fieldname",
+ "label",
+ "condition"
+ ],
+ "fields": [
+ {
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Title",
+ "reqd": 1
+ },
+ {
+ "columns": 4,
+ "fieldname": "description",
+ "fieldtype": "HTML Editor",
+ "in_list_view": 1,
+ "label": "Description",
+ "reqd": 1
+ },
+ {
+ "fieldname": "field",
+ "fieldtype": "Link",
+ "label": "Field",
+ "options": "DocField",
+ "reqd": 1
+ },
+ {
+ "fetch_from": "field.fieldname",
+ "fieldname": "fieldname",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Fieldname",
+ "read_only": 1
+ },
+ {
+ "fetch_from": "field.label",
+ "fieldname": "label",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Label",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "Bottom",
+ "fieldname": "position",
+ "fieldtype": "Select",
+ "label": "Position",
+ "options": "Left\nLeft Center\nLeft Bottom\nTop\nTop Center\nTop Right\nRight\nRight Center\nRight Bottom\nBottom\nBottom Center\nBottom Right\nMid Center"
+ },
+ {
+ "fieldname": "next_step_condition",
+ "fieldtype": "Code",
+ "label": "Next Step Condition",
+ "options": "JS"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-26 19:44:48.737453",
+ "modified_by": "Administrator",
+ "module": "Desk",
+ "name": "Form Tour Step",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
diff --git a/frappe/desk/doctype/form_tour_step/form_tour_step.py b/frappe/desk/doctype/form_tour_step/form_tour_step.py
new file mode 100644
index 0000000000..0df5665c63
--- /dev/null
+++ b/frappe/desk/doctype/form_tour_step/form_tour_step.py
@@ -0,0 +1,8 @@
+# Copyright (c) 2021, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+class FormTourStep(Document):
+ pass
diff --git a/frappe/desk/doctype/global_search_settings/global_search_settings.py b/frappe/desk/doctype/global_search_settings/global_search_settings.py
index 28a1ed8239..9112349c1b 100644
--- a/frappe/desk/doctype/global_search_settings/global_search_settings.py
+++ b/frappe/desk/doctype/global_search_settings/global_search_settings.py
@@ -21,7 +21,7 @@ class GlobalSearchSettings(Document):
dts.append(dt.document_type)
if core_dts:
- core_dts = (", ".join([frappe.bold(dt) for dt in core_dts]))
+ core_dts = ", ".join(frappe.bold(dt) for dt in core_dts)
frappe.throw(_("Core Modules {0} cannot be searched in Global Search.").format(core_dts))
if repeated_dts:
@@ -60,7 +60,7 @@ def update_global_search_doctypes():
if search_doctypes.get(domain):
global_search_doctypes.extend(search_doctypes.get(domain))
- doctype_list = set([dt.name for dt in frappe.get_all("DocType")])
+ doctype_list = {dt.name for dt in frappe.get_all("DocType")}
allowed_in_global_search = []
for dt in global_search_doctypes:
diff --git a/frappe/desk/doctype/tag/tag.py b/frappe/desk/doctype/tag/tag.py
index 3c67bb4668..4ea5c9cd7e 100644
--- a/frappe/desk/doctype/tag/tag.py
+++ b/frappe/desk/doctype/tag/tag.py
@@ -131,7 +131,7 @@ def update_tags(doc, tags):
:param doc: Document to be added to global tags
"""
- new_tags = list(set([tag.strip() for tag in tags.split(",") if tag]))
+ new_tags = {tag.strip() for tag in tags.split(",") if tag}
for tag in new_tags:
if not frappe.db.exists("Tag Link", {"parenttype": doc.doctype, "parent": doc.name, "tag": tag}):
@@ -186,4 +186,4 @@ def get_documents_for_tag(tag):
@frappe.whitelist()
def get_tags_list_for_awesomebar():
- return [t.name for t in frappe.get_list("Tag")]
\ No newline at end of file
+ return [t.name for t in frappe.get_list("Tag")]
diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py
index 0329e0f7d2..0b5babc8d9 100644
--- a/frappe/desk/doctype/workspace/workspace.py
+++ b/frappe/desk/doctype/workspace/workspace.py
@@ -55,8 +55,7 @@ class Workspace(Document):
for link in self.links:
link = link.as_dict()
if link.type == "Card Break":
-
- if card_links:
+ if card_links and (not current_card.only_for or current_card.only_for == frappe.get_system_settings('country')):
current_card['links'] = card_links
cards.append(current_card)
diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py
index c77ba00021..ecd59f42bb 100755
--- a/frappe/email/doctype/email_account/email_account.py
+++ b/frappe/email/doctype/email_account/email_account.py
@@ -1,32 +1,25 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-import frappe
+import email.utils
+import functools
import imaplib
-import re
-import json
import socket
import time
-import functools
-
-import email.utils
-
-from frappe import _, are_emails_muted
-from frappe.model.document import Document
-from frappe.utils import (validate_email_address, cint, cstr, get_datetime,
- DATE_FORMAT, strip, comma_or, sanitize_html, add_days, parse_addr)
-from frappe.utils.user import is_system_user
-from frappe.utils.jinja import render_template
-from frappe.email.smtp import SMTPServer
-from frappe.email.receive import EmailServer, InboundMail, SentEmailInInboxError
-from poplib import error_proto
-from dateutil.relativedelta import relativedelta
from datetime import datetime, timedelta
+from poplib import error_proto
+
+import frappe
+from frappe import _, are_emails_muted, safe_encode
from frappe.desk.form import assign_to
-from frappe.utils.user import get_system_managers
-from frappe.utils.background_jobs import enqueue, get_jobs
-from frappe.utils.html_utils import clean_email_html
-from frappe.utils.error import raise_error_on_no_output
+from frappe.email.receive import EmailServer, InboundMail, SentEmailInInboxError
+from frappe.email.smtp import SMTPServer
from frappe.email.utils import get_port
+from frappe.model.document import Document
+from frappe.utils import cint, comma_or, cstr, parse_addr, validate_email_address
+from frappe.utils.background_jobs import enqueue, get_jobs
+from frappe.utils.error import raise_error_on_no_output
+from frappe.utils.jinja import render_template
+from frappe.utils.user import get_system_managers
OUTGOING_EMAIL_ACCOUNT_MISSING = _("Please setup default Email Account from Setup > Email > Email Account")
@@ -441,10 +434,7 @@ class EmailAccount(Document):
if self.enable_auto_reply:
self.send_auto_reply(communication, mail)
- attachments = []
- if hasattr(communication, '_attachments'):
- attachments = [d.file_name for d in communication._attachments]
- communication.notify(attachments=attachments, fetched_from_email_account=True)
+ communication.send_email(is_inbound_mail_communcation=True)
except SentEmailInInboxError:
frappe.db.rollback()
except Exception:
@@ -453,6 +443,8 @@ class EmailAccount(Document):
if self.use_imap:
self.handle_bad_emails(mail.uid, mail.raw_message, frappe.get_traceback())
exceptions.append(frappe.get_traceback())
+ else:
+ frappe.db.commit()
#notify if user is linked to account
if len(inbound_mails)>0 and not frappe.local.flags.in_test:
@@ -478,14 +470,13 @@ class EmailAccount(Document):
email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule)
messages = email_server.get_messages() or {}
except Exception:
- raise
frappe.log_error(title=_("Error while connecting to email account {0}").format(self.name))
return []
mails = []
for index, message in enumerate(messages.get("latest_messages", [])):
- uid = messages['uid_list'][index]
- seen_status = 1 if messages['seen_status'][uid]=='SEEN' else 0
+ uid = messages['uid_list'][index] if messages.get('uid_list') else None
+ seen_status = 1 if messages.get('seen_status', {}).get(uid)=='SEEN' else 0
mails.append(InboundMail(message, self, uid, seen_status))
return mails
@@ -579,8 +570,8 @@ class EmailAccount(Document):
email_server.update_flag(uid_list=uid_list)
# mark communication as read
- docnames = ",".join([ "'%s'"%flag.get("communication") for flag in flags \
- if flag.get("action") == "Read" ])
+ docnames = ",".join("'%s'"%flag.get("communication") for flag in flags \
+ if flag.get("action") == "Read")
self.set_communication_seen_status(docnames, seen=1)
# mark communication as unread
@@ -610,7 +601,6 @@ class EmailAccount(Document):
def append_email_to_sent_folder(self, message):
-
email_server = None
try:
email_server = self.get_incoming_server(in_receive=True)
@@ -624,7 +614,8 @@ class EmailAccount(Document):
if email_server.imap:
try:
- email_server.imap.append("Sent", "\\Seen", imaplib.Time2Internaldate(time.time()), message.encode())
+ message = safe_encode(message)
+ email_server.imap.append("Sent", "\\Seen", imaplib.Time2Internaldate(time.time()), message)
except Exception:
frappe.log_error()
diff --git a/frappe/email/doctype/email_group/email_group.json b/frappe/email/doctype/email_group/email_group.json
index c49de841e6..cb74249143 100644
--- a/frappe/email/doctype/email_group/email_group.json
+++ b/frappe/email/doctype/email_group/email_group.json
@@ -1,6 +1,7 @@
{
"actions": [],
"allow_import": 1,
+ "allow_rename": 1,
"autoname": "field:title",
"creation": "2015-03-18 06:08:32.729800",
"doctype": "DocType",
@@ -50,7 +51,7 @@
"link_fieldname": "email_group"
}
],
- "modified": "2020-09-24 16:41:55.286377",
+ "modified": "2021-06-15 11:25:13.556201",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Group",
diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py
index dad473b8aa..e1e332f978 100644
--- a/frappe/email/doctype/email_queue/email_queue.py
+++ b/frappe/email/doctype/email_queue/email_queue.py
@@ -179,7 +179,14 @@ class SendMailContext:
else:
email_status = self.is_mail_sent_to_all() and 'Sent'
email_status = email_status or (self.sent_to and 'Partially Sent') or 'Not Sent'
- self.queue_doc.update_status(status = email_status, commit = True)
+
+ update_fields = {'status': email_status}
+ if self.email_account_doc.is_exists_in_db():
+ update_fields['email_account'] = self.email_account_doc.name
+ else:
+ update_fields['email_account'] = None
+
+ self.queue_doc.update_status(**update_fields, commit = True)
def log_exception(self, exc_type, exc_val, exc_tb):
if exc_type:
diff --git a/frappe/email/doctype/newsletter/test_newsletter.py b/frappe/email/doctype/newsletter/test_newsletter.py
index cfd0df53a9..3abd339ed9 100644
--- a/frappe/email/doctype/newsletter/test_newsletter.py
+++ b/frappe/email/doctype/newsletter/test_newsletter.py
@@ -42,7 +42,7 @@ class TestNewsletter(unittest.TestCase):
email_queue_list = [frappe.get_doc("Email Queue", e.name) for e in frappe.get_all("Email Queue")]
self.assertEqual(len(email_queue_list), 4)
- recipients = set([e.recipients[0].recipient for e in email_queue_list])
+ recipients = {e.recipients[0].recipient for e in email_queue_list}
self.assertTrue(set(emails).issubset(recipients))
def test_unsubscribe(self):
diff --git a/frappe/email/inbox.py b/frappe/email/inbox.py
index 5f8f516772..c6020e14e4 100644
--- a/frappe/email/inbox.py
+++ b/frappe/email/inbox.py
@@ -18,7 +18,7 @@ def get_email_accounts(user=None):
"all_accounts": ""
}
- all_accounts = ",".join([ account.get("email_account") for account in accounts ])
+ all_accounts = ",".join(account.get("email_account") for account in accounts)
if len(accounts) > 1:
email_accounts.append({
"email_account": all_accounts,
diff --git a/frappe/email/queue.py b/frappe/email/queue.py
index ca96981aa8..885a306cfb 100755
--- a/frappe/email/queue.py
+++ b/frappe/email/queue.py
@@ -6,15 +6,61 @@ from frappe import msgprint, _
from frappe.utils.verified_command import get_signed_params, verify_request
from frappe.utils import get_url, now_datetime, cint
-def get_emails_sent_this_month():
- return frappe.db.sql("""
- SELECT COUNT(*) FROM `tabEmail Queue`
- WHERE `status`='Sent' AND EXTRACT(YEAR_MONTH FROM `creation`) = EXTRACT(YEAR_MONTH FROM NOW())
- """)[0][0]
+def get_emails_sent_this_month(email_account=None):
+ """Get count of emails sent from a specific email account.
-def get_emails_sent_today():
- return frappe.db.sql("""SELECT COUNT(`name`) FROM `tabEmail Queue` WHERE
- `status` in ('Sent', 'Not Sent', 'Sending') AND `creation` > (NOW() - INTERVAL '24' HOUR)""")[0][0]
+ :param email_account: name of the email account used to send mail
+
+ if email_account=None, email account filter is not applied while counting
+ """
+ q = """
+ SELECT
+ COUNT(*)
+ FROM
+ `tabEmail Queue`
+ WHERE
+ `status`='Sent'
+ AND
+ EXTRACT(YEAR_MONTH FROM `creation`) = EXTRACT(YEAR_MONTH FROM NOW())
+ """
+
+ q_args = {}
+ if email_account is not None:
+ if email_account:
+ q += " AND email_account = %(email_account)s"
+ q_args['email_account'] = email_account
+ else:
+ q += " AND (email_account is null OR email_account='')"
+
+ return frappe.db.sql(q, q_args)[0][0]
+
+def get_emails_sent_today(email_account=None):
+ """Get count of emails sent from a specific email account.
+
+ :param email_account: name of the email account used to send mail
+
+ if email_account=None, email account filter is not applied while counting
+ """
+ q = """
+ SELECT
+ COUNT(`name`)
+ FROM
+ `tabEmail Queue`
+ WHERE
+ `status` in ('Sent', 'Not Sent', 'Sending')
+ AND
+ `creation` > (NOW() - INTERVAL '24' HOUR)
+ """
+
+ q_args = {}
+ if email_account is not None:
+ if email_account:
+ q += " AND email_account = %(email_account)s"
+ q_args['email_account'] = email_account
+ else:
+ q += " AND (email_account is null OR email_account='')"
+
+ return frappe.db.sql(q, q_args)[0][0]
def get_unsubscribe_message(unsubscribe_message, expose_recipients):
if unsubscribe_message:
diff --git a/frappe/email/receive.py b/frappe/email/receive.py
index 7da4840df1..2e42008951 100644
--- a/frappe/email/receive.py
+++ b/frappe/email/receive.py
@@ -738,9 +738,6 @@ class InboundMail(Email):
if not reference_document and self.email_account.append_to:
reference_document = self.match_record_by_subject_and_sender(self.email_account.append_to)
- # if not reference_document:
- # reference_document = Create_reference_document(self.email_account.append_to)
-
self._reference_document = reference_document or ''
return self._reference_document
@@ -805,7 +802,7 @@ class InboundMail(Email):
except frappe.DuplicateEntryError:
# try and find matching parent
parent_name = frappe.db.get_value(self.email_account.append_to,
- {email_fileds.sender_field: email.from_email}
+ {email_fileds.sender_field: self.from_email}
)
if parent_name:
parent.name = parent_name
diff --git a/frappe/installer.py b/frappe/installer.py
index d7d885d60e..d4d8117fcb 100755
--- a/frappe/installer.py
+++ b/frappe/installer.py
@@ -282,10 +282,10 @@ def remove_app(app_name, dry_run=False, yes=False, no_backup=False, force=False)
def post_install(rebuild_website=False):
- from frappe.website import render
+ from frappe.website.utils import clear_website_cache
if rebuild_website:
- render.clear_cache()
+ clear_website_cache()
init_singles()
frappe.db.commit()
@@ -537,7 +537,7 @@ def is_downgrade(sql_file_path, verbose=False):
def is_partial(sql_file_path):
with open(sql_file_path) as f:
- header = " ".join([f.readline() for _ in range(5)])
+ header = " ".join(f.readline() for _ in range(5))
if "Partial Backup" in header:
return True
return False
diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py
index 122096cf6f..acc8b96679 100644
--- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py
+++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py
@@ -79,7 +79,7 @@ class LDAPSettings(Document):
def sync_roles(self, user, additional_groups=None):
- current_roles = set([d.role for d in user.get("roles")])
+ current_roles = set(d.role for d in user.get("roles"))
needed_roles = set()
needed_roles.add(self.default_role)
diff --git a/frappe/migrate.py b/frappe/migrate.py
index d19e255639..061e4c98d7 100644
--- a/frappe/migrate.py
+++ b/frappe/migrate.py
@@ -13,7 +13,7 @@ from frappe.utils.connections import check_connection
from frappe.utils.dashboard import sync_dashboards
from frappe.cache_manager import clear_global_cache
from frappe.desk.notifications import clear_notifications
-from frappe.website import render
+from frappe.website.utils import clear_website_cache
from frappe.core.doctype.language.language import sync_languages
from frappe.modules.utils import sync_customizations
from frappe.core.doctype.scheduled_job_type.scheduled_job_type import sync_jobs
@@ -76,7 +76,7 @@ Otherwise, check the server logs and ensure that all the required services are r
frappe.get_doc('Portal Settings', 'Portal Settings').sync_menu()
# syncs statics
- render.clear_cache()
+ clear_website_cache()
# updating installed applications data
frappe.get_single('Installed Applications').update_versions()
diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py
index dd93fbcc18..75122f5aba 100644
--- a/frappe/model/__init__.py
+++ b/frappe/model/__init__.py
@@ -165,7 +165,7 @@ def delete_fields(args_dict, delete=0):
frappe.db.sql("""
DELETE FROM `tabSingles`
WHERE doctype='%s' AND field IN (%s)
- """ % (dt, ", ".join(["'{}'".format(f) for f in fields])))
+ """ % (dt, ", ".join("'{}'".format(f) for f in fields)))
else:
existing_fields = frappe.db.multisql({
"mariadb": "DESC `tab%s`" % dt,
@@ -188,7 +188,7 @@ def delete_fields(args_dict, delete=0):
frappe.db.commit()
query = "ALTER TABLE `tab%s` " % dt + \
- ", ".join(["DROP COLUMN `%s`" % f for f in fields_need_to_delete])
+ ", ".join("DROP COLUMN `%s`" % f for f in fields_need_to_delete)
frappe.db.sql(query)
if frappe.db.db_type == 'postgres':
diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py
index 2f5154cfd9..af696e116d 100644
--- a/frappe/model/base_document.py
+++ b/frappe/model/base_document.py
@@ -354,7 +354,7 @@ class BaseDocument(object):
frappe.db.sql("""INSERT INTO `tab{doctype}` ({columns})
VALUES ({values})""".format(
doctype = self.doctype,
- columns = ", ".join(["`"+c+"`" for c in columns]),
+ columns = ", ".join("`"+c+"`" for c in columns),
values = ", ".join(["%s"] * len(columns))
), list(d.values()))
except Exception as e:
@@ -397,7 +397,7 @@ class BaseDocument(object):
frappe.db.sql("""UPDATE `tab{doctype}`
SET {values} WHERE `name`=%s""".format(
doctype = self.doctype,
- values = ", ".join(["`"+c+"`=%s" for c in columns])
+ values = ", ".join("`"+c+"`=%s" for c in columns)
), list(d.values()) + [name])
except Exception as e:
if frappe.db.is_unique_key_violation(e):
diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py
index 7f22282acf..7ed681644f 100644
--- a/frappe/model/db_query.py
+++ b/frappe/model/db_query.py
@@ -43,8 +43,14 @@ class DatabaseQuery(object):
# filters and fields swappable
# its hard to remember what comes first
- if (isinstance(fields, dict)
- or (isinstance(fields, list) and fields and isinstance(fields[0], list))):
+ if (
+ isinstance(fields, dict)
+ or (
+ fields
+ and isinstance(fields, list)
+ and isinstance(fields[0], list)
+ )
+ ):
# if fields is given as dict/list of list, its probably filters
filters, fields = fields, filters
@@ -56,10 +62,7 @@ class DatabaseQuery(object):
if fields:
self.fields = fields
else:
- if pluck:
- self.fields = ["`tab{0}`.`{1}`".format(self.doctype, pluck)]
- else:
- self.fields = ["`tab{0}`.`name`".format(self.doctype)]
+ self.fields = [f"`tab{self.doctype}`.`{pluck or 'name'}`"]
if start: limit_start = start
if page_length: limit_page_length = page_length
@@ -70,7 +73,7 @@ class DatabaseQuery(object):
self.docstatus = docstatus or []
self.group_by = group_by
self.order_by = order_by
- self.limit_start = 0 if (limit_start is False) else cint(limit_start)
+ self.limit_start = cint(limit_start)
self.limit_page_length = cint(limit_page_length) if limit_page_length else None
self.with_childnames = with_childnames
self.debug = debug
@@ -157,11 +160,10 @@ class DatabaseQuery(object):
# left join parent, child tables
for child in self.tables[1:]:
- args.tables += " {join} {child} on ({child}.parent = {main}.name)".format(join=self.join,
- child=child, main=self.tables[0])
+ args.tables += f" {self.join} {child} on ({child}.parent = {self.tables[0]}.name)"
if self.grouped_or_conditions:
- self.conditions.append("({0})".format(" or ".join(self.grouped_or_conditions)))
+ self.conditions.append(f"({' or '.join(self.grouped_or_conditions)})")
args.conditions = ' and '.join(self.conditions)
@@ -186,9 +188,9 @@ class DatabaseQuery(object):
fields.append(field)
elif "as" in field.lower().split(" "):
col, _, new = field.split()
- fields.append("`{0}` as {1}".format(col, new))
+ fields.append(f"`{col}` as {new}")
else:
- fields.append("`{0}`".format(field))
+ fields.append(f"`{field}`")
args.fields = ", ".join(fields)
@@ -260,10 +262,10 @@ class DatabaseQuery(object):
if any(keyword in field.lower().split() for keyword in blacklisted_keywords):
_raise_exception()
- if any("({0}".format(keyword) in field.lower() for keyword in blacklisted_keywords):
+ if any(f"({keyword}" in field.lower() for keyword in blacklisted_keywords):
_raise_exception()
- if any("{0}(".format(keyword) in field.lower() for keyword in blacklisted_functions):
+ if any(f"{keyword}(" in field.lower() for keyword in blacklisted_functions):
_raise_exception()
if '@' in field.lower():
@@ -287,22 +289,30 @@ class DatabaseQuery(object):
def extract_tables(self):
"""extract tables from fields"""
- self.tables = ['`tab' + self.doctype + '`']
-
+ self.tables = [f"`tab{self.doctype}`"]
+ sql_functions = [
+ "dayofyear(",
+ "extract(",
+ "locate(",
+ "strpos(",
+ "count(",
+ "sum(",
+ "avg(",
+ ]
# add tables from fields
if self.fields:
- for f in self.fields:
- if ( not ("tab" in f and "." in f) ) or ("locate(" in f) or ("strpos(" in f) or \
- ("count(" in f) or ("avg(" in f) or ("sum(" in f) or ("extract(" in f) or ("dayofyear(" in f):
+ for field in self.fields:
+ if not ("tab" in field and "." in field) or any(x for x in sql_functions if x in field):
continue
- table_name = f.split('.')[0]
+ table_name = field.split('.')[0]
+
if table_name.lower().startswith('group_concat('):
table_name = table_name[13:]
if table_name.lower().startswith('ifnull('):
table_name = table_name[7:]
if not table_name[0]=='`':
- table_name = '`' + table_name + '`'
+ table_name = f"`{table_name}`"
if not table_name in self.tables:
self.append_table(table_name)
@@ -311,8 +321,7 @@ class DatabaseQuery(object):
doctype = table_name[4:-1]
ptype = 'select' if frappe.only_has_select_perm(doctype) else 'read'
- if (not self.flags.ignore_permissions) and\
- (not frappe.has_permission(doctype, ptype=ptype)):
+ if not self.flags.ignore_permissions and not frappe.has_permission(doctype, ptype=ptype):
frappe.flags.error_message = _('Insufficient Permission for {0}').format(frappe.bold(doctype))
raise frappe.PermissionError(doctype)
@@ -326,7 +335,7 @@ class DatabaseQuery(object):
if len(self.tables) > 1:
for idx, field in enumerate(self.fields):
if '.' not in field and not _in_standard_sql_methods(field):
- self.fields[idx] = '{0}.{1}'.format(self.tables[0], field)
+ self.fields[idx] = f"{self.tables[0]}.{field}"
def get_table_columns(self):
try:
@@ -375,7 +384,7 @@ class DatabaseQuery(object):
if not self.flags.ignore_permissions:
match_conditions = self.build_match_conditions()
if match_conditions:
- self.conditions.append("(" + match_conditions + ")")
+ self.conditions.append(f"({match_conditions})")
def build_filter_conditions(self, filters, conditions, ignore_permissions=None):
"""build conditions from user filters"""
@@ -407,8 +416,7 @@ class DatabaseQuery(object):
if 'ifnull(' in f.fieldname:
column_name = f.fieldname
else:
- column_name = '{tname}.{fname}'.format(tname=tname,
- fname=f.fieldname)
+ column_name = f"{tname}.{f.fieldname}"
can_be_null = True
@@ -450,7 +458,7 @@ class DatabaseQuery(object):
fallback = "''"
value = [frappe.db.escape((v.name or '').strip(), percent=False) for v in result]
if len(value):
- value = "({0})".format(", ".join(value))
+ value = f"({', '.join(value)})"
else:
value = "('')"
# changing operator to IN as the above code fetches all the parent / child values and convert into tuple
@@ -466,7 +474,7 @@ class DatabaseQuery(object):
fallback = "''"
value = [frappe.db.escape((v or '').strip(), percent=False) for v in values]
if len(value):
- value = "({0})".format(", ".join(value))
+ value = f"({', '.join(value)})"
else:
value = "('')"
else:
@@ -503,7 +511,7 @@ class DatabaseQuery(object):
can_be_null = True
if 'ifnull' not in column_name:
- column_name = 'ifnull({}, {})'.format(column_name, fallback)
+ column_name = f'ifnull({column_name}, {fallback})'
elif df and df.fieldtype=="Date":
value = frappe.db.format_date(f.value)
@@ -540,21 +548,19 @@ class DatabaseQuery(object):
# escape value
if isinstance(value, str) and not f.operator.lower() == 'between':
- value = "{0}".format(frappe.db.escape(value, percent=False))
+ value = f"{frappe.db.escape(value, percent=False)}"
- if (self.ignore_ifnull
+ if (
+ self.ignore_ifnull
or not can_be_null
or (f.value and f.operator.lower() in ('=', 'like'))
- or 'ifnull(' in column_name.lower()):
+ or 'ifnull(' in column_name.lower()
+ ):
if f.operator.lower() == 'like' and frappe.conf.get('db_type') == 'postgres':
f.operator = 'ilike'
- condition = '{column_name} {operator} {value}'.format(
- column_name=column_name, operator=f.operator,
- value=value)
+ condition = f'{column_name} {f.operator} {value}'
else:
- condition = 'ifnull({column_name}, {fallback}) {operator} {value}'.format(
- column_name=column_name, fallback=fallback, operator=f.operator,
- value=value)
+ condition = f'ifnull({column_name}, {fallback}) {f.operator} {value}'
return condition
@@ -572,10 +578,12 @@ class DatabaseQuery(object):
role_permissions = frappe.permissions.get_role_permissions(meta, user=self.user)
self.shared = frappe.share.get_shared(self.doctype, self.user)
- if (not meta.istable and
+ if (
+ not meta.istable and
not (role_permissions.get("select") or role_permissions.get("read")) and
not self.flags.ignore_permissions and
- not has_any_user_permission_for_doctype(self.doctype, self.user, self.reference_doctype)):
+ not has_any_user_permission_for_doctype(self.doctype, self.user, self.reference_doctype)
+ ):
only_if_shared = True
if not self.shared:
frappe.throw(_("No permission to read {0}").format(self.doctype), frappe.PermissionError)
@@ -585,8 +593,10 @@ class DatabaseQuery(object):
else:
#if has if_owner permission skip user perm check
if role_permissions.get("has_if_owner_enabled") and role_permissions.get("if_owner", {}):
- self.match_conditions.append("`tab{0}`.`owner` = {1}".format(self.doctype,
- frappe.db.escape(self.user, percent=False)))
+ self.match_conditions.append(
+ f"`tab{self.doctype}`.`owner` = {frappe.db.escape(self.user, percent=False)}"
+ )
+
# add user permission only if role has read perm
elif role_permissions.get("read") or role_permissions.get("select"):
# get user permissions
@@ -605,8 +615,7 @@ class DatabaseQuery(object):
# share is an OR condition, if there is a role permission
if not only_if_shared and self.shared and conditions:
- conditions = "({conditions}) or ({shared_condition})".format(
- conditions=conditions, shared_condition=self.get_share_condition())
+ conditions = f"({conditions}) or ({self.get_share_condition()})"
return conditions
@@ -614,8 +623,7 @@ class DatabaseQuery(object):
return self.match_filters
def get_share_condition(self):
- return """`tab{0}`.name in ({1})""".format(self.doctype, ", ".join(["%s"] * len(self.shared))) % \
- tuple([frappe.db.escape(s, percent=False) for s in self.shared])
+ return f"`tab{self.doctype}`.name in ({', '.join(frappe.db.escape(s, percent=False) for s in self.shared)})"
def add_user_permissions(self, user_permissions):
meta = frappe.get_meta(self.doctype)
@@ -640,9 +648,7 @@ class DatabaseQuery(object):
if frappe.get_system_settings("apply_strict_user_permissions"):
condition = ""
else:
- empty_value_condition = "ifnull(`tab{doctype}`.`{fieldname}`, '')=''".format(
- doctype=self.doctype, fieldname=df.get('fieldname')
- )
+ empty_value_condition = f"ifnull(`tab{self.doctype}`.`{df.get('fieldname')}`, '')=''"
condition = empty_value_condition + " or "
for permission in user_permission_values:
@@ -650,9 +656,7 @@ class DatabaseQuery(object):
docs.append(permission.get('doc'))
# append docs based on user permission applicable on reference doctype
-
# this is useful when getting list of docs from a link field
-
# in this case parent doctype of the link
# will be the reference doctype
@@ -664,14 +668,9 @@ class DatabaseQuery(object):
docs.append(permission.get('doc'))
if docs:
- condition += "`tab{doctype}`.`{fieldname}` in ({values})".format(
- doctype=self.doctype,
- fieldname=df.get('fieldname'),
- values=", ".join(
- [(frappe.db.escape(doc, percent=False)) for doc in docs])
- )
-
- match_conditions.append("({condition})".format(condition=condition))
+ values = ", ".join(frappe.db.escape(doc, percent=False) for doc in docs)
+ condition += f"`tab{self.doctype}`.`{df.get('fieldname')}` in ({values})"
+ match_conditions.append(f"({condition})")
match_filters[df.get('options')] = docs
if match_conditions:
@@ -721,17 +720,17 @@ class DatabaseQuery(object):
# `idx desc, modified desc`
# will covert to
# `tabItem`.`idx` desc, `tabItem`.`modified` desc
- args.order_by = ', '.join(['`tab{0}`.`{1}` {2}'.format(self.doctype,
- f.split()[0].strip(), f.split()[1].strip()) for f in meta.sort_field.split(',')])
+ args.order_by = ', '.join(
+ f"`tab{self.doctype}`.`{f.split()[0].strip()}` {f.split()[1].strip()}" for f in meta.sort_field.split(',')
+ )
else:
sort_field = meta.sort_field or 'modified'
sort_order = (meta.sort_field and meta.sort_order) or 'desc'
-
- args.order_by = "`tab{0}`.`{1}` {2}".format(self.doctype, sort_field or "modified", sort_order or "desc")
+ args.order_by = f"`tab{self.doctype}`.`{sort_field or 'modified'}` {sort_order or 'desc'}"
# draft docs always on top
- if meta.is_submittable:
- args.order_by = "`tab{0}`.docstatus asc, {1}".format(self.doctype, args.order_by)
+ if hasattr(meta, 'is_submittable') and meta.is_submittable:
+ args.order_by = f"`tab{self.doctype}`.docstatus asc, {args.order_by}"
def validate_order_by_and_group_by(self, parameters):
"""Check order by, group by so that atleast one column is selected and does not have subquery"""
@@ -802,17 +801,16 @@ def get_order_by(doctype, meta):
# `idx desc, modified desc`
# will covert to
# `tabItem`.`idx` desc, `tabItem`.`modified` desc
- order_by = ', '.join(['`tab{0}`.`{1}` {2}'.format(doctype,
- f.split()[0].strip(), f.split()[1].strip()) for f in meta.sort_field.split(',')])
+ order_by = ', '.join(f"`tab{doctype}`.`{f.split()[0].strip()}` {f.split()[1].strip()}" for f in meta.sort_field.split(','))
+
else:
sort_field = meta.sort_field or 'modified'
sort_order = (meta.sort_field and meta.sort_order) or 'desc'
-
- order_by = "`tab{0}`.`{1}` {2}".format(doctype, sort_field or "modified", sort_order or "desc")
+ order_by = f"`tab{doctype}`.`{sort_field or 'modified'}` {sort_order or 'desc'}"
# draft docs always on top
if meta.is_submittable:
- order_by = "`tab{0}`.docstatus asc, {1}".format(doctype, order_by)
+ order_by = f"`tab{doctype}`.docstatus asc, {order_by}"
return order_by
diff --git a/frappe/model/document.py b/frappe/model/document.py
index 8f57aae475..61160e1f01 100644
--- a/frappe/model/document.py
+++ b/frappe/model/document.py
@@ -14,8 +14,8 @@ from frappe.model.workflow import set_workflow_state_on_action
from frappe.utils.global_search import update_global_search
from frappe.integrations.doctype.webhook import run_webhooks
from frappe.desk.form.document_follow import follow_document
-from frappe.desk.utils import slug
from frappe.core.doctype.server_script.server_script_utils import run_server_script_for_doc_event
+from frappe.utils.data import get_absolute_url
# once_only validation
# methods
@@ -1200,8 +1200,8 @@ class Document(BaseDocument):
doc.set(fieldname, flt(doc.get(fieldname), self.precision(fieldname, doc.parentfield)))
def get_url(self):
- """Returns Desk URL for this document. `/app/{doctype}/{name}`"""
- return f"/app/{slug(self.doctype)}/{self.name}"
+ """Returns Desk URL for this document."""
+ return get_absolute_url(self.doctype, self.name)
def add_comment(self, comment_type='Comment', text=None, comment_email=None, link_doctype=None, link_name=None, comment_by=None):
"""Add a comment to this document.
diff --git a/frappe/model/meta.py b/frappe/model/meta.py
index b67c41c990..b212324208 100644
--- a/frappe/model/meta.py
+++ b/frappe/model/meta.py
@@ -664,7 +664,7 @@ def trim_tables(doctype=None):
and not f.startswith("_")]
if columns_to_remove:
print(doctype, "columns removed:", columns_to_remove)
- columns_to_remove = ", ".join(["drop `{0}`".format(c) for c in columns_to_remove])
+ columns_to_remove = ", ".join("drop `{0}`".format(c) for c in columns_to_remove)
query = """alter table `tab{doctype}` {columns}""".format(
doctype=doctype, columns=columns_to_remove)
frappe.db.sql_ddl(query)
diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py
index fc5b3ca9fe..9b8ac2574d 100644
--- a/frappe/model/rename_doc.py
+++ b/frappe/model/rename_doc.py
@@ -141,7 +141,7 @@ def update_user_settings(old, new, link_fields):
if not link_fields: return
# find the user settings for the linked doctypes
- linked_doctypes = set([d.parent for d in link_fields if not d.issingle])
+ linked_doctypes = {d.parent for d in link_fields if not d.issingle}
user_settings_details = frappe.db.sql('''SELECT `user`, `doctype`, `data`
FROM `__UserSettings`
WHERE `data` like %s
diff --git a/frappe/parallel_test_runner.py b/frappe/parallel_test_runner.py
index 1dbb24f191..2f83b88572 100644
--- a/frappe/parallel_test_runner.py
+++ b/frappe/parallel_test_runner.py
@@ -114,13 +114,30 @@ class ParallelTestRunner():
# Generate coverage report only for app that is being tested
source_path = os.path.join(get_bench_path(), 'apps', self.app)
- omit=['*.html', '*.js', '*.xml', '*.css', '*.less', '*.scss',
- '*.vue', '*/doctype/*/*_dashboard.py', '*/patches/*']
+ incl = [
+ '*.py',
+ ]
+ omit = [
+ '*.js',
+ '*.xml',
+ '*.pyc',
+ '*.css',
+ '*.less',
+ '*.scss',
+ '*.vue',
+ '*.pyc',
+ '*.html',
+ '*/test_*',
+ '*/node_modules/*',
+ '*/doctype/*/*_dashboard.py',
+ '*/patches/*',
+ ]
if self.app == 'frappe':
+ omit.append('*/tests/*')
omit.append('*/commands/*')
- self.coverage = Coverage(source=[source_path], omit=omit)
+ self.coverage = Coverage(source=[source_path], omit=omit, include=incl)
self.coverage.start()
def save_coverage(self):
diff --git a/frappe/patches.txt b/frappe/patches.txt
index e70be0a37b..7605d8ea2b 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -1,11 +1,5 @@
frappe.patches.v12_0.remove_deprecated_fields_from_doctype #3
-execute:frappe.db.sql("""update `tabPatch Log` set patch=replace(patch, '.4_0.', '.v4_0.')""") #2014-05-12
-frappe.patches.v5_0.convert_to_barracuda_and_utf8mb4
execute:frappe.utils.global_search.setup_global_search_table()
-frappe.patches.v8_0.update_global_search_table
-frappe.patches.v7_0.update_auth
-frappe.patches.v8_0.drop_in_dialog #2017-09-22
-frappe.patches.v7_2.remove_in_filter
execute:frappe.reload_doc('core', 'doctype', 'doctype_action', force=True) #2019-09-23
execute:frappe.reload_doc('core', 'doctype', 'doctype_link', force=True) #2020-10-17
execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2017-09-22
@@ -14,7 +8,6 @@ frappe.patches.v11_0.drop_column_apply_user_permissions
execute:frappe.reload_doc('core', 'doctype', 'custom_docperm')
execute:frappe.reload_doc('core', 'doctype', 'docperm') #2018-05-29
execute:frappe.reload_doc('core', 'doctype', 'comment')
-frappe.patches.v8_0.drop_is_custom_from_docperm
execute:frappe.reload_doc('core', 'doctype', 'document_naming_rule', force=True)
execute:frappe.reload_doc('core', 'doctype', 'module_def') #2020-08-28
execute:frappe.reload_doc('core', 'doctype', 'version') #2017-04-01
@@ -25,190 +18,40 @@ execute:frappe.reload_doc('core', 'doctype', 'communication') #2019-10-02
execute:frappe.reload_doc('core', 'doctype', 'server_script')
frappe.patches.v11_0.replicate_old_user_permissions
frappe.patches.v11_0.reload_and_rename_view_log #2019-01-03
-frappe.patches.v7_1.rename_scheduler_log_to_error_log
-frappe.patches.v6_1.rename_file_data
-frappe.patches.v7_0.re_route #2016-06-27
-frappe.patches.v8_0.update_records_in_global_search #11-05-2017
-frappe.patches.v8_0.update_published_in_global_search
frappe.patches.v11_0.copy_fetch_data_from_options
frappe.patches.v11_0.change_email_signature_fieldtype
execute:frappe.reload_doc('core', 'doctype', 'activity_log')
execute:frappe.reload_doc('core', 'doctype', 'deleted_document')
execute:frappe.reload_doc('core', 'doctype', 'domain_settings')
frappe.patches.v13_0.rename_custom_client_script
-frappe.patches.v8_0.rename_page_role_to_has_role #2017-03-16
-frappe.patches.v7_2.setup_custom_perms #2017-01-19
-frappe.patches.v8_0.set_user_permission_for_page_and_report #2017-03-20
execute:frappe.reload_doc('core', 'doctype', 'role') #2017-05-23
execute:frappe.reload_doc('core', 'doctype', 'user') #2017-10-27
-execute:frappe.reload_doc('custom', 'doctype', 'custom_field') #2015-10-19
-execute:frappe.reload_doc('core', 'doctype', 'page') #2013-13-26
execute:frappe.reload_doc('core', 'doctype', 'report_column')
execute:frappe.reload_doc('core', 'doctype', 'report_filter')
execute:frappe.reload_doc('core', 'doctype', 'report') #2020-08-25
-execute:frappe.reload_doc('core', 'doctype', 'translation') #2016-03-03
-execute:frappe.reload_doc('email', 'doctype', 'email_alert') #2014-07-15
-execute:frappe.reload_doc('desk', 'doctype', 'todo') #2014-12-31-1
-execute:frappe.reload_doc('custom', 'doctype', 'property_setter') #2014-12-31-1
-execute:frappe.reload_doc('core', 'doctype', 'patch_log') #2016-10-31
-execute:frappe.reload_doctype("File") # 2015-10-19
execute:frappe.reload_doc('core', 'doctype', 'error_snapshot')
-execute:frappe.clear_cache()
-frappe.patches.v7_1.rename_scheduler_log_to_error_log
-frappe.patches.v7_1.sync_language_doctype
-frappe.patches.v7_0.rename_bulk_email_to_email_queue
-frappe.patches.v7_1.rename_chinese_language_codes
-
-execute:frappe.db.sql("alter table `tabSessions` modify `user` varchar(255), engine=InnoDB")
-execute:frappe.db.sql("delete from `tabDocField` where parent='0'")
-frappe.patches.v4_0.change_varchar_length
-frappe.patches.v6_4.reduce_varchar_length
-frappe.patches.v5_2.change_checks_to_not_null
-frappe.patches.v6_9.int_float_not_null #2015-11-25
-frappe.patches.v5_0.v4_to_v5
-
-frappe.patches.v5_0.remove_shopping_cart_app
-frappe.patches.v4_0.webnotes_to_frappe
-execute:frappe.permissions.reset_perms("Module Def")
-execute:import frappe.installer;frappe.installer.make_site_dirs() #2014-02-19
-frappe.patches.v4_0.rename_profile_to_user
-frappe.patches.v4_0.deprecate_control_panel
-frappe.patches.v4_0.remove_old_parent
-frappe.patches.v4_0.rename_sitemap_to_route
-frappe.patches.v4_0.website_sitemap_hierarchy
-frappe.patches.v4_0.remove_index_sitemap
-frappe.patches.v4_0.set_website_route_idx
-frappe.patches.v4_0.add_delete_permission
-frappe.patches.v4_0.set_todo_checked_as_closed
-frappe.patches.v4_0.private_backups
-frappe.patches.v4_0.set_module_in_report
-frappe.patches.v4_0.update_datetime
-frappe.patches.v4_0.file_manager_hooks
execute:frappe.get_doc("User", "Guest").save()
-frappe.patches.v4_0.update_custom_field_insert_after
-frappe.patches.v4_0.deprecate_link_selects
-frappe.patches.v4_0.set_user_gravatar
-frappe.patches.v4_0.set_user_permissions
-frappe.patches.v4_0.create_custom_field_for_owner_match
-frappe.patches.v4_0.enable_scheduler_in_system_settings
-execute:frappe.db.sql("update tabReport set apply_user_permissions=1") #2014-06-03
-frappe.patches.v4_0.replace_deprecated_timezones
-execute:import frappe.website.render; frappe.website.render.clear_cache("login"); #2014-06-10
-frappe.patches.v4_0.fix_attach_field_file_url
-execute:frappe.permissions.reset_perms("User") #2015-03-24
-execute:frappe.db.sql("""delete from `tabUserRole` where ifnull(parentfield, '')='' or ifnull(`role`, '')=''""") #2014-08-18
-frappe.patches.v4_0.remove_user_owner_custom_field
-execute:frappe.delete_doc("DocType", "Website Template")
-execute:frappe.db.sql("""update `tabProperty Setter` set property_type='Text' where property in ('options', 'default')""") #2014-06-20
-frappe.patches.v4_1.enable_outgoing_email_settings
-execute:frappe.db.sql("""update `tabSingles` set `value`=`doctype` where `field`='name'""") #2014-07-04
-frappe.patches.v4_1.enable_print_as_pdf #2014-06-17
-execute:frappe.db.sql("""update `tabDocPerm` set email=1 where parent='User' and permlevel=0 and `role`='All' and `read`=1 and apply_user_permissions=1""") #2014-07-15
-execute:frappe.db.sql("""update `tabPrint Format` set print_format_type='Client' where ifnull(print_format_type, '')=''""") #2014-07-28
-frappe.patches.v4_1.file_manager_fix
-frappe.patches.v4_2.print_with_letterhead
execute:frappe.delete_doc("DocType", "Control Panel", force=1)
-execute:frappe.reload_doc('website', 'doctype', 'web_form') #2014-09-04
-execute:frappe.reload_doc('website', 'doctype', 'web_form_field') #2014-09-04
-frappe.patches.v4_2.refactor_website_routing
-frappe.patches.v4_2.set_assign_in_doc
-frappe.patches.v4_3.remove_allow_on_submit_customization
-frappe.patches.v5_0.rename_table_fieldnames
-frappe.patches.v5_0.communication_parent
-frappe.patches.v5_0.clear_website_group_and_notifications
-frappe.patches.v5_0.update_shared
-execute:frappe.reload_doc("core", "doctype", "docshare") #2015-07-21
-frappe.patches.v6_19.comment_feed_communication
-frappe.patches.v6_16.star_to_like
-frappe.patches.v5_0.bookmarks_to_stars
-frappe.patches.v5_0.style_settings_to_website_theme
-frappe.patches.v5_0.rename_ref_type_fieldnames
-frappe.patches.v5_0.fix_email_alert
-frappe.patches.v5_0.fix_null_date_datetime
-frappe.patches.v5_0.force_sync_website
execute:frappe.delete_doc("DocType", "Tag")
execute:frappe.db.sql("delete from `tabProperty Setter` where `property` in ('idx', '_idx')")
-frappe.patches.v5_0.move_scheduler_last_event_to_system_settings
execute:frappe.db.sql("update tabUser set new_password='' where ifnull(new_password, '')!=''")
-frappe.patches.v5_0.fix_text_editor_file_urls
-frappe.patches.v5_0.modify_session
-frappe.patches.v5_0.expire_old_scheduler_logs
execute:frappe.permissions.reset_perms("DocType")
execute:frappe.db.sql("delete from `tabProperty Setter` where `property` = 'idx'")
-frappe.patches.v6_0.communication_status_and_permission
-frappe.patches.v6_0.make_task_log_folder
-frappe.patches.v6_0.document_type_rename
-frappe.patches.v6_0.fix_ghana_currency
-frappe.patches.v6_2.ignore_user_permissions_if_missing
execute:frappe.db.sql("delete from tabSessions where user is null")
-frappe.patches.v6_2.rename_backup_manager
execute:frappe.delete_doc("DocType", "Backup Manager")
-execute:frappe.db.sql("""update `tabCommunication` set parenttype=null, parent=null, parentfield=null""") #2015-10-22
execute:frappe.permissions.reset_perms("Web Page")
-frappe.patches.v6_6.user_last_active
-frappe.patches.v6_6.fix_file_url
-frappe.patches.v6_11.rename_field_in_email_account
-frappe.patches.v7_0.create_private_file_folder
-frappe.patches.v6_15.remove_property_setter_for_previous_field #2015-12-29
-frappe.patches.v6_15.set_username
execute:frappe.permissions.reset_perms("Error Snapshot")
-frappe.patches.v6_16.feed_doc_owner
-frappe.patches.v6_21.print_settings_repeat_header_footer
-frappe.patches.v6_24.set_language_as_code
-frappe.patches.v6_20x.update_insert_after
-frappe.patches.v6_20x.set_allow_draft_for_print
-frappe.patches.v6_20x.remove_roles_from_website_user
-frappe.patches.v7_0.set_user_fullname
-frappe.patches.v7_0.add_communication_in_doc
-frappe.patches.v7_0.update_send_after_in_bulk_email
-execute:frappe.db.sql('''delete from `tabSingles` where doctype="Email Settings"''') # 2016-06-13
execute:frappe.db.sql("delete from `tabWeb Page` where ifnull(template_path, '')!=''")
-frappe.patches.v7_0.rename_newsletter_list_to_email_group
-frappe.patches.v7_0.set_email_group
-frappe.patches.v7_1.setup_integration_services #2016-10-27
-frappe.patches.v7_1.rename_chinese_language_codes
execute:frappe.core.doctype.language.language.update_language_names() # 2017-04-12
execute:frappe.db.set_value("Print Settings", "Print Settings", "add_draft_heading", 1)
-frappe.patches.v7_0.cleanup_list_settings
execute:frappe.db.set_default('language', '')
-frappe.patches.v7_1.refactor_integration_broker
-frappe.patches.v7_1.set_backup_limit
-frappe.patches.v7_2.set_doctype_engine
-frappe.patches.v7_2.merge_knowledge_base
-frappe.patches.v7_0.update_report_builder_json
-frappe.patches.v7_2.set_in_standard_filter_property #1
-frappe.patches.v8_0.drop_unwanted_indexes
execute:frappe.db.sql("update tabCommunication set communication_date = creation where time(communication_date) = 0")
-frappe.patches.v7_2.fix_email_queue_recipient
-frappe.patches.v7_2.update_feedback_request # 2017-02-27
execute:frappe.rename_doc('Country', 'Macedonia, Republic of', 'Macedonia', ignore_if_exists=True)
execute:frappe.rename_doc('Country', 'Iran, Islamic Republic of', 'Iran', ignore_if_exists=True)
execute:frappe.rename_doc('Country', 'Tanzania, United Republic of', 'Tanzania', ignore_if_exists=True)
execute:frappe.rename_doc('Country', 'Syrian Arab Republic', 'Syria', ignore_if_exists=True)
-frappe.patches.v8_0.rename_listsettings_to_usersettings
-frappe.patches.v7_2.update_communications
-frappe.patches.v8_0.deprecate_integration_broker
-frappe.patches.v8_0.update_gender_and_salutation
-frappe.patches.v8_0.setup_email_inbox #2017-03-29
-frappe.patches.v8_0.newsletter_childtable_migrate
-frappe.patches.v8_0.set_doctype_values_in_custom_role
-frappe.patches.v8_0.install_new_build_system_requirements
-frappe.patches.v8_0.set_currency_field_precision # 2017-05-09
execute:frappe.reload_doc('desk', 'doctype', 'notification_log')
-frappe.patches.v8_0.rename_print_to_printing
-frappe.patches.v7_1.disabled_print_settings_for_custom_print_format
execute:frappe.db.sql('update tabReport set module="Desk" where name="ToDo"')
-frappe.patches.v8_1.enable_allow_error_traceback_in_system_settings
-frappe.patches.v8_1.update_format_options_in_auto_email_report
-frappe.patches.v8_1.delete_custom_docperm_if_doctype_not_exists
-frappe.patches.v8_5.delete_email_group_member_with_invalid_emails
-frappe.patches.v8_x.update_user_permission
-frappe.patches.v8_5.patch_event_colors
-frappe.patches.v8_10.delete_static_web_page_from_global_search
-frappe.patches.v9_1.add_sms_sender_name_as_parameters
-frappe.patches.v9_1.resave_domain_settings
-frappe.patches.v9_1.revert_domain_settings
-frappe.patches.v9_1.move_feed_to_activity_log
execute:frappe.delete_doc('Page', 'data-import-tool', ignore_missing=True)
frappe.patches.v10_0.reload_countries_and_currencies # 2021-02-03
frappe.patches.v10_0.refactor_social_login_keys
diff --git a/frappe/patches/v11_0/create_contact_for_user.py b/frappe/patches/v11_0/create_contact_for_user.py
index 21e681a83e..5a483b630e 100644
--- a/frappe/patches/v11_0/create_contact_for_user.py
+++ b/frappe/patches/v11_0/create_contact_for_user.py
@@ -8,7 +8,6 @@ def execute():
frappe.reload_doc('integrations', 'doctype', 'google_contacts')
frappe.reload_doc('contacts', 'doctype', 'contact')
frappe.reload_doc('core', 'doctype', 'dynamic_link')
- frappe.reload_doc('communication', 'doctype', 'call_log')
contact_meta = frappe.get_meta("Contact")
if contact_meta.has_field("phone_nos") and contact_meta.has_field("email_ids"):
diff --git a/frappe/patches/v11_0/rename_google_maps_doctype.py b/frappe/patches/v11_0/rename_google_maps_doctype.py
index 4e8aee6280..8091154b9c 100644
--- a/frappe/patches/v11_0/rename_google_maps_doctype.py
+++ b/frappe/patches/v11_0/rename_google_maps_doctype.py
@@ -5,4 +5,3 @@ from frappe.model.rename_doc import rename_doc
def execute():
if frappe.db.exists("DocType","Google Maps") and not frappe.db.exists("DocType","Google Maps Settings"):
rename_doc('DocType', 'Google Maps', 'Google Maps Settings')
- frappe.reload_doc('integrations', 'doctype', 'google_maps_settings')
\ No newline at end of file
diff --git a/frappe/patches/v4_0/add_delete_permission.py b/frappe/patches/v4_0/add_delete_permission.py
deleted file mode 100644
index 9e375a431d..0000000000
--- a/frappe/patches/v4_0/add_delete_permission.py
+++ /dev/null
@@ -1,13 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doc("core", "doctype", "docperm")
-
- # delete same as cancel (map old permissions)
- frappe.db.sql("""update tabDocPerm set `delete`=ifnull(`cancel`,0)""")
-
- # can't cancel if can't submit
- frappe.db.sql("""update tabDocPerm set `cancel`=0 where ifnull(`submit`,0)=0""")
-
- frappe.clear_cache()
\ No newline at end of file
diff --git a/frappe/patches/v4_0/change_varchar_length.py b/frappe/patches/v4_0/change_varchar_length.py
deleted file mode 100644
index 914034ccba..0000000000
--- a/frappe/patches/v4_0/change_varchar_length.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- frappe.db.sql('update tabDocField set search_index=0 where fieldtype="Small Text"')
- frappe.db.sql('update tabDocField set in_list_view=0 where fieldtype="Image"')
-
- for dt in frappe.db.sql_list("""select name from `tabDocType` where issingle=0"""):
- desc = dict((d["Field"], d) for d in frappe.db.sql("desc `tab{}`".format(dt), as_dict=True))
- alter_table = []
-
- if desc["name"]["Type"] != "varchar(255)":
- alter_table.append("change `name` `name` varchar(255) not null")
-
- for fieldname in ("modified_by", "owner", "parent", "parentfield", "parenttype"):
- if desc[fieldname]["Type"] != "varchar(255)":
- alter_table.append("change `{fieldname}` `{fieldname}` varchar(255)".format(fieldname=fieldname))
-
- if alter_table:
- alter_table_query = "alter table `tab{doctype}` {alter_table}".format(doctype=dt, alter_table=",\n".join(alter_table))
- # print alter_table_query
- frappe.db.sql_ddl(alter_table_query)
-
diff --git a/frappe/patches/v4_0/create_custom_field_for_owner_match.py b/frappe/patches/v4_0/create_custom_field_for_owner_match.py
deleted file mode 100644
index 438e280669..0000000000
--- a/frappe/patches/v4_0/create_custom_field_for_owner_match.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_field
-
-def execute():
- if "match" in frappe.db.get_table_columns("DocPerm"):
- create_custom_field_for_owner_match()
-
-def create_custom_field_for_owner_match():
- docperm_meta = frappe.get_meta('DocPerm')
- if docperm_meta.get_field('apply_user_permissions'):
- frappe.db.sql("""update `tabDocPerm` set apply_user_permissions=1 where `match`='owner'""")
-
- for dt in frappe.db.sql_list("""select distinct parent from `tabDocPerm`
- where `match`='owner' and permlevel=0 and parent != 'User'"""):
-
- # a link field pointing to User already exists
- if (frappe.db.get_value("DocField", {"parent": dt, "fieldtype": "Link", "options": "User", "default": "__user"})
- or frappe.db.get_value("Custom Field", {"dt": dt, "fieldtype": "Link", "options": "User", "default": "__user"})):
- print("User link field already exists for", dt)
- continue
-
- fieldname = "{}_owner".format(frappe.scrub(dt))
-
- create_custom_field(dt, frappe._dict({
- "permlevel": 0,
- "label": "{} Owner".format(dt),
- "fieldname": fieldname,
- "fieldtype": "Link",
- "options": "User",
- "default": "__user"
- }))
-
- frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=owner""".format(doctype=dt,
- fieldname=fieldname))
-
- # commit is required so that we don't lose these changes because of an error in next loop's ddl
- frappe.db.commit()
diff --git a/frappe/patches/v4_0/deprecate_control_panel.py b/frappe/patches/v4_0/deprecate_control_panel.py
deleted file mode 100644
index 29ec8d35f6..0000000000
--- a/frappe/patches/v4_0/deprecate_control_panel.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- frappe.db.sql("update `tabDefaultValue` set parenttype='__default' where parenttype='Control Panel'")
- frappe.db.sql("update `tabDefaultValue` set parent='__default' where parent='Control Panel'")
- frappe.clear_cache()
diff --git a/frappe/patches/v4_0/deprecate_link_selects.py b/frappe/patches/v4_0/deprecate_link_selects.py
deleted file mode 100644
index 837144a6ba..0000000000
--- a/frappe/patches/v4_0/deprecate_link_selects.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- for name in frappe.db.sql_list("""select name from `tabCustom Field`
- where fieldtype="Select" and options like "link:%" """):
- custom_field = frappe.get_doc("Custom Field", name)
- custom_field.fieldtype = "Link"
- custom_field.options = custom_field.options[5:]
- custom_field.save()
diff --git a/frappe/patches/v4_0/enable_scheduler_in_system_settings.py b/frappe/patches/v4_0/enable_scheduler_in_system_settings.py
deleted file mode 100644
index 68c74edb4f..0000000000
--- a/frappe/patches/v4_0/enable_scheduler_in_system_settings.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-from frappe.utils.scheduler import disable_scheduler, enable_scheduler
-from frappe.utils import cint
-
-def execute():
- frappe.reload_doc("core", "doctype", "system_settings")
- if cint(frappe.db.get_global("disable_scheduler")):
- disable_scheduler()
- else:
- enable_scheduler()
diff --git a/frappe/patches/v4_0/file_manager_hooks.py b/frappe/patches/v4_0/file_manager_hooks.py
deleted file mode 100644
index ccf95d1d31..0000000000
--- a/frappe/patches/v4_0/file_manager_hooks.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-import frappe
-import os
-from frappe.core.doctype.file.file import get_content_hash
-
-
-def execute():
- frappe.reload_doc('core', 'doctype', 'file_data')
- for name, file_name, file_url in frappe.db.sql(
- """select name, file_name, file_url from `tabFile`
- where file_name is not null"""):
- b = frappe.get_doc('File', name)
- old_file_name = b.file_name
- b.file_name = os.path.basename(old_file_name)
- if old_file_name.startswith('files/') or old_file_name.startswith('/files/'):
- b.file_url = os.path.normpath('/' + old_file_name)
- else:
- b.file_url = os.path.normpath('/files/' + old_file_name)
- try:
- _file = frappe.get_doc("File", {"file_name": name})
- content = _file.get_content()
- b.content_hash = get_content_hash(content)
- except IOError:
- print('Warning: Error processing ', name)
- _file_name = old_file_name
- b.content_hash = None
-
- try:
- b.save()
- except frappe.DuplicateEntryError:
- frappe.delete_doc(b.doctype, b.name)
-
diff --git a/frappe/patches/v4_0/fix_attach_field_file_url.py b/frappe/patches/v4_0/fix_attach_field_file_url.py
deleted file mode 100644
index 61c35b120d..0000000000
--- a/frappe/patches/v4_0/fix_attach_field_file_url.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- attach_fields = (frappe.db.sql("""select parent, fieldname from `tabDocField` where fieldtype in ('Attach', 'Attach Image')""") +
- frappe.db.sql("""select dt, fieldname from `tabCustom Field` where fieldtype in ('Attach', 'Attach Image')"""))
-
- for doctype, fieldname in attach_fields:
- frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=concat("/", `{fieldname}`)
- where `{fieldname}` like 'files/%'""".format(doctype=doctype, fieldname=fieldname))
diff --git a/frappe/patches/v4_0/private_backups.py b/frappe/patches/v4_0/private_backups.py
deleted file mode 100644
index 7920564677..0000000000
--- a/frappe/patches/v4_0/private_backups.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-from frappe.installer import make_site_dirs
-
-def execute():
- make_site_dirs()
- if frappe.local.conf.backup_path and frappe.local.conf.backup_path.startswith("public"):
- raise Exception("Backups path in conf set to public directory")
diff --git a/frappe/patches/v4_0/remove_index_sitemap.py b/frappe/patches/v4_0/remove_index_sitemap.py
deleted file mode 100644
index 8f48729276..0000000000
--- a/frappe/patches/v4_0/remove_index_sitemap.py
+++ /dev/null
@@ -1,5 +0,0 @@
-
-import frappe
-
-def execute():
- pass
diff --git a/frappe/patches/v4_0/remove_old_parent.py b/frappe/patches/v4_0/remove_old_parent.py
deleted file mode 100644
index f2d125953a..0000000000
--- a/frappe/patches/v4_0/remove_old_parent.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- for doctype in frappe.db.sql_list("""select name from `tabDocType` where istable=1"""):
- frappe.db.sql("""delete from `tab{0}` where parent like "old_par%:%" """.format(doctype))
- frappe.db.sql("""delete from `tabDocField` where parent="0" """)
diff --git a/frappe/patches/v4_0/remove_user_owner_custom_field.py b/frappe/patches/v4_0/remove_user_owner_custom_field.py
deleted file mode 100644
index 4620f055d9..0000000000
--- a/frappe/patches/v4_0/remove_user_owner_custom_field.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- user_owner = frappe.db.get_value("Custom Field", {"fieldname": "user_owner"})
- if user_owner:
- frappe.delete_doc("Custom Field", user_owner)
diff --git a/frappe/patches/v4_0/rename_profile_to_user.py b/frappe/patches/v4_0/rename_profile_to_user.py
deleted file mode 100644
index 3e6f269329..0000000000
--- a/frappe/patches/v4_0/rename_profile_to_user.py
+++ /dev/null
@@ -1,15 +0,0 @@
-
-import frappe
-
-from frappe.model.utils.rename_field import rename_field
-from frappe.model.meta import get_table_columns
-
-def execute():
- tables = frappe.db.sql_list("show tables")
- if "tabUser" not in tables:
- frappe.rename_doc("DocType", "Profile", "User", force=True)
-
- frappe.reload_doc("website", "doctype", "blogger")
-
- if "profile" in get_table_columns("Blogger"):
- rename_field("Blogger", "profile", "user")
diff --git a/frappe/patches/v4_0/rename_sitemap_to_route.py b/frappe/patches/v4_0/rename_sitemap_to_route.py
deleted file mode 100644
index b4606672bc..0000000000
--- a/frappe/patches/v4_0/rename_sitemap_to_route.py
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import frappe
-
-from frappe.model.utils.rename_field import rename_field
-
-def execute():
- tables = frappe.db.sql_list("show tables")
- for doctype in ("Website Sitemap", "Website Sitemap Config"):
- if "tab{}".format(doctype) in tables:
- frappe.delete_doc("DocType", doctype, force=1)
- frappe.db.sql("drop table `tab{}`".format(doctype))
-
- for d in ("Blog Category", "Blog Post", "Web Page"):
- frappe.reload_doc("website", "doctype", frappe.scrub(d))
- rename_field_if_exists(d, "parent_website_sitemap", "parent_website_route")
-
- for d in ("blog_category", "blog_post", "web_page", "post", "user_vote"):
- frappe.reload_doc("website", "doctype", d)
-
-def rename_field_if_exists(doctype, old_fieldname, new_fieldname):
- try:
- rename_field(doctype, old_fieldname, new_fieldname)
- except frappe.db.ProgrammingError as e:
- if not frappe.db.is_column_missing(e):
- raise
diff --git a/frappe/patches/v4_0/replace_deprecated_timezones.py b/frappe/patches/v4_0/replace_deprecated_timezones.py
deleted file mode 100644
index a3d8ecbf2b..0000000000
--- a/frappe/patches/v4_0/replace_deprecated_timezones.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-from frappe.utils.momentjs import data as momentjs_data
-
-def execute():
- frappe.reload_doc("core", "doctype", "user")
-
- ss = frappe.get_doc("System Settings", "System Settings")
- if ss.time_zone in momentjs_data.get("links"):
- ss.time_zone = momentjs_data["links"][ss.time_zone]
- ss.flags.ignore_mandatory = True
- ss.save()
-
- for user, time_zone in frappe.db.sql("select name, time_zone from `tabUser` where ifnull(time_zone, '')!=''"):
- if time_zone in momentjs_data.get("links"):
- user = frappe.get_doc("User", user)
- user.time_zone = momentjs_data["links"][user.time_zone]
- user.save()
diff --git a/frappe/patches/v4_0/set_module_in_report.py b/frappe/patches/v4_0/set_module_in_report.py
deleted file mode 100644
index 6c670f4c8e..0000000000
--- a/frappe/patches/v4_0/set_module_in_report.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- frappe.reload_doc("core", "doctype", "report")
- frappe.db.sql("""update `tabReport` r set r.module=(select d.module from `tabDocType` d
- where d.name=r.ref_doctype) where ifnull(r.module, '')=''""")
\ No newline at end of file
diff --git a/frappe/patches/v4_0/set_todo_checked_as_closed.py b/frappe/patches/v4_0/set_todo_checked_as_closed.py
deleted file mode 100644
index 5f02e1447b..0000000000
--- a/frappe/patches/v4_0/set_todo_checked_as_closed.py
+++ /dev/null
@@ -1,9 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doc("core", "doctype", "todo")
- try:
- frappe.db.sql("""update tabToDo set status = if(ifnull(checked,0)=0, 'Open', 'Closed')""")
- except:
- pass
diff --git a/frappe/patches/v4_0/set_user_gravatar.py b/frappe/patches/v4_0/set_user_gravatar.py
deleted file mode 100644
index 348991c320..0000000000
--- a/frappe/patches/v4_0/set_user_gravatar.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- for name in frappe.db.sql_list("select name from `tabUser` where ifnull(user_image, '')=''"):
- user = frappe.get_doc("User", name)
- user.update_gravatar()
- user.db_set("user_image", user.user_image)
diff --git a/frappe/patches/v4_0/set_user_permissions.py b/frappe/patches/v4_0/set_user_permissions.py
deleted file mode 100644
index ef6f3a27e5..0000000000
--- a/frappe/patches/v4_0/set_user_permissions.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-import frappe.permissions
-
-def execute():
- frappe.reload_doc("core", "doctype", "docperm")
- table_columns = frappe.db.get_table_columns("DocPerm")
-
- if "restricted" in table_columns:
- frappe.db.sql("""update `tabDocPerm` set apply_user_permissions=1 where apply_user_permissions=0
- and restricted=1""")
-
- if "match" in table_columns:
- frappe.db.sql("""update `tabDocPerm` set apply_user_permissions=1
- where apply_user_permissions=0 and ifnull(`match`, '')!=''""")
-
- # change Restriction to User Permission in tabDefaultValue
- frappe.db.sql("""update `tabDefaultValue` set parenttype='User Permission' where parenttype='Restriction'""")
-
- frappe.clear_cache()
-
diff --git a/frappe/patches/v4_0/set_website_route_idx.py b/frappe/patches/v4_0/set_website_route_idx.py
deleted file mode 100644
index c3dba712d8..0000000000
--- a/frappe/patches/v4_0/set_website_route_idx.py
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import frappe
-
-def execute():
- pass
- # from frappe.website.doctype.website_template.website_template import \
- # get_pages_and_generators, get_template_controller
- #
- # frappe.reload_doc("website", "doctype", "website_template")
- # frappe.reload_doc("website", "doctype", "website_route")
- #
- # for app in frappe.get_installed_apps():
- # pages, generators = get_pages_and_generators(app)
- # for g in generators:
- # doctype = frappe.get_attr(get_template_controller(app, g["path"], g["fname"]) + ".doctype")
- # module = frappe.db.get_value("DocType", doctype, "module")
- # frappe.reload_doc(frappe.scrub(module), "doctype", frappe.scrub(doctype))
- #
- # frappe.db.sql("""update `tabBlog Category` set `title`=`name` where ifnull(`title`, '')=''""")
- # frappe.db.sql("""update `tabWebsite Route` set idx=null""")
- # for doctype in ["Blog Category", "Blog Post", "Web Page", "Website Group"]:
- # frappe.db.sql("""update `tab{}` set idx=null""".format(doctype))
- #
- # from frappe.website.doctype.website_template.website_template import rebuild_website_template
- # rebuild_website_template()
diff --git a/frappe/patches/v4_0/update_custom_field_insert_after.py b/frappe/patches/v4_0/update_custom_field_insert_after.py
deleted file mode 100644
index 4cb50956d6..0000000000
--- a/frappe/patches/v4_0/update_custom_field_insert_after.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- for d in frappe.db.sql("""select name, dt, insert_after from `tabCustom Field`
- where docstatus < 2""", as_dict=1):
- dt_meta = frappe.get_meta(d.dt)
- if not dt_meta.get_field(d.insert_after):
- cf = frappe.get_doc("Custom Field", d.name)
- df = dt_meta.get("fields", {"label": d.insert_after})
- if df:
- cf.insert_after = df[0].fieldname
- else:
- cf.insert_after = None
- cf.save()
diff --git a/frappe/patches/v4_0/update_datetime.py b/frappe/patches/v4_0/update_datetime.py
deleted file mode 100644
index 4034d8f665..0000000000
--- a/frappe/patches/v4_0/update_datetime.py
+++ /dev/null
@@ -1,12 +0,0 @@
-
-import frappe
-
-def execute():
- for table in frappe.db.sql_list("show tables"):
- for field in frappe.db.sql("desc `%s`" % table):
- if field[1]=="datetime":
- frappe.db.sql("alter table `%s` change `%s` `%s` datetime(6)" % \
- (table, field[0], field[0]))
- elif field[1]=="time":
- frappe.db.sql("alter table `%s` change `%s` `%s` time(6)" % \
- (table, field[0], field[0]))
diff --git a/frappe/patches/v4_0/webnotes_to_frappe.py b/frappe/patches/v4_0/webnotes_to_frappe.py
deleted file mode 100644
index c29f6f603e..0000000000
--- a/frappe/patches/v4_0/webnotes_to_frappe.py
+++ /dev/null
@@ -1,12 +0,0 @@
-
-import frappe, json
-
-def execute():
- frappe.clear_cache()
- installed = frappe.get_installed_apps()
- if "webnotes" in installed:
- installed.remove("webnotes")
- if "frappe" not in installed:
- installed = ["frappe"] + installed
- frappe.db.set_global("installed_apps", json.dumps(installed))
- frappe.clear_cache()
diff --git a/frappe/patches/v4_0/website_sitemap_hierarchy.py b/frappe/patches/v4_0/website_sitemap_hierarchy.py
deleted file mode 100644
index 6404986245..0000000000
--- a/frappe/patches/v4_0/website_sitemap_hierarchy.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- # frappe.db.sql("""update `tabWebsite Route` ws set ref_doctype=(select wsc.ref_doctype
- # from `tabWebsite Template` wsc where wsc.name=ws.website_template)
- # where ifnull(page_or_generator, '')!='Page'""")
-
- frappe.reload_doc("website", "doctype", "website_settings")
-
- # original_home_page = frappe.db.get_value("Website Settings", "Website Settings", "home_page")
- #
- # home_page = frappe.db.sql("""select name from `tabWebsite Route`
- # where (name=%s or docname=%s) and name!='index'""", (original_home_page, original_home_page))
- # home_page = home_page[0][0] if home_page else original_home_page
- #
- # frappe.db.set_value("Website Settings", "Website Settings", "home_page", home_page)
diff --git a/frappe/patches/v4_1/enable_outgoing_email_settings.py b/frappe/patches/v4_1/enable_outgoing_email_settings.py
deleted file mode 100644
index ffa891ae7c..0000000000
--- a/frappe/patches/v4_1/enable_outgoing_email_settings.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- frappe.reload_doc("core", "doctype", "outgoing_email_settings")
- if (frappe.db.get_value("Outgoing Email Settings", "Outgoing Email Settings", "mail_server") or "").strip():
- frappe.db.set_value("Outgoing Email Settings", "Outgoing Email Settings", "enabled", 1)
diff --git a/frappe/patches/v4_1/enable_print_as_pdf.py b/frappe/patches/v4_1/enable_print_as_pdf.py
deleted file mode 100644
index e5a8f830f6..0000000000
--- a/frappe/patches/v4_1/enable_print_as_pdf.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- frappe.reload_doc("core", "doctype", "print_settings")
- print_settings = frappe.get_doc("Print Settings")
- print_settings.print_style = "Modern"
-
- try:
- import pdfkit
- except ImportError:
- pass
- else:
- # if someone has already configured in Outgoing Email Settings
- outgoing_email_settings = frappe.db.get_singles_dict("Outgoing Email Settings")
- if "send_print_as_pdf" in outgoing_email_settings:
- print_settings.send_print_as_pdf = outgoing_email_settings.send_print_as_pdf
- print_settings.pdf_page_size = outgoing_email_settings.pdf_page_size
-
- else:
- print_settings.send_print_as_pdf = 1
-
- print_settings.save()
diff --git a/frappe/patches/v4_1/file_manager_fix.py b/frappe/patches/v4_1/file_manager_fix.py
deleted file mode 100644
index 18f44203f2..0000000000
--- a/frappe/patches/v4_1/file_manager_fix.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-import frappe
-import os
-from frappe.core.doctype.file.file import get_content_hash, get_file_name
-from frappe.utils import get_files_path, get_site_path
-
-# The files missed by the previous patch might have been replaced with new files
-# with the same filename
-#
-# This patch does the following,
-# * Detect which files were replaced and rename them with name{hash:5}.extn and
-# update filedata record for the new file
-#
-# * make missing_files.txt in site dir with files that should be recovered from
-# a backup from a time before version 3 migration
-#
-# * Patch remaining unpatched File records.
-
-
-def execute():
- frappe.db.auto_commit_on_many_writes = True
- rename_replacing_files()
- for name, file_name, file_url in frappe.db.sql(
- """select name, file_name, file_url from `tabFile`
- where ifnull(file_name, '')!='' and ifnull(content_hash, '')=''"""):
- b = frappe.get_doc('File', name)
- old_file_name = b.file_name
- b.file_name = os.path.basename(old_file_name)
- if old_file_name.startswith('files/') or old_file_name.startswith('/files/'):
- b.file_url = os.path.normpath('/' + old_file_name)
- else:
- b.file_url = os.path.normpath('/files/' + old_file_name)
- try:
- _file = frappe.get_doc("File", {"file_name": name})
- content = _file.get_content()
- b.content_hash = get_content_hash(content)
- except IOError:
- print('Warning: Error processing ', name)
- b.content_hash = None
- b.flags.ignore_duplicate_entry_error = True
- b.save()
- frappe.db.auto_commit_on_many_writes = False
-
-def get_replaced_files():
- ret = []
- new_files = dict(frappe.db.sql("select name, file_name from `tabFile` where file_name not like 'files/%'"))
- old_files = dict(frappe.db.sql("select name, file_name from `tabFile` where ifnull(content_hash, '')=''"))
- invfiles = invert_dict(new_files)
-
- for nname, nfilename in new_files.items():
- if 'files/' + nfilename in old_files.values():
- ret.append((nfilename, invfiles[nfilename]))
- return ret
-
-def rename_replacing_files():
- replaced_files = get_replaced_files()
- if len(replaced_files):
- missing_files = [v[0] for v in replaced_files]
- with open(get_site_path('missing_files.txt'), 'w') as f:
- f.write(('\n'.join(missing_files) + '\n').encode('utf-8'))
-
- for file_name, file_datas in replaced_files:
- print ('processing ' + file_name)
- content_hash = frappe.db.get_value('File', file_datas[0], 'content_hash')
- if not content_hash:
- continue
- new_file_name = get_file_name(file_name, content_hash)
- if os.path.exists(get_files_path(new_file_name)):
- continue
- print('skipping ' + file_name)
- try:
- os.rename(get_files_path(file_name), get_files_path(new_file_name))
- except OSError:
- print('Error renaming ', file_name)
- for name in file_datas:
- f = frappe.get_doc('File', name)
- f.file_name = new_file_name
- f.file_url = '/files/' + new_file_name
- f.save()
-
-def invert_dict(ddict):
- ret = {}
- for k,v in ddict.items():
- if not ret.get(v):
- ret[v] = [k]
- else:
- ret[v].append(k)
- return ret
-
-def get_file_name(fname, hash):
- if '.' in fname:
- partial, extn = fname.rsplit('.', 1)
- else:
- partial = fname
- extn = ''
- return '{partial}{suffix}.{extn}'.format(partial=partial, extn=extn, suffix=hash[:5])
diff --git a/frappe/patches/v4_2/print_with_letterhead.py b/frappe/patches/v4_2/print_with_letterhead.py
deleted file mode 100644
index 111f6c762e..0000000000
--- a/frappe/patches/v4_2/print_with_letterhead.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- frappe.reload_doc("core", "doctype", "print_settings")
- print_settings = frappe.get_doc("Print Settings")
- print_settings.with_letterhead = 1
- print_settings.save()
diff --git a/frappe/patches/v4_2/refactor_website_routing.py b/frappe/patches/v4_2/refactor_website_routing.py
deleted file mode 100644
index 77eea3d429..0000000000
--- a/frappe/patches/v4_2/refactor_website_routing.py
+++ /dev/null
@@ -1,8 +0,0 @@
-
-import frappe
-
-def execute():
- # clear all static web pages
- frappe.delete_doc("DocType", "Website Route", force=1)
- frappe.delete_doc("Page", "sitemap-browser", force=1)
- frappe.db.sql("drop table if exists `tabWebsite Route`")
diff --git a/frappe/patches/v4_2/set_assign_in_doc.py b/frappe/patches/v4_2/set_assign_in_doc.py
deleted file mode 100644
index 8fbd37c5c5..0000000000
--- a/frappe/patches/v4_2/set_assign_in_doc.py
+++ /dev/null
@@ -1,11 +0,0 @@
-
-import frappe
-
-def execute():
- for name in frappe.db.sql_list("""select name from `tabToDo`
- where ifnull(reference_type, '')!='' and ifnull(reference_name, '')!=''"""):
- try:
- frappe.get_doc("ToDo", name).on_update()
- except Exception as e:
- if not frappe.db.is_table_missing(e):
- raise
diff --git a/frappe/patches/v4_3/remove_allow_on_submit_customization.py b/frappe/patches/v4_3/remove_allow_on_submit_customization.py
deleted file mode 100644
index a762fd10ab..0000000000
--- a/frappe/patches/v4_3/remove_allow_on_submit_customization.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- for d in frappe.get_all("Property Setter", fields=["name", "doc_type"],
- filters={"doctype_or_field": "DocField", "property": "allow_on_submit", "value": "1"}):
- frappe.delete_doc("Property Setter", d.name)
- frappe.clear_cache(doctype=d.doc_type)
diff --git a/frappe/patches/v5_0/bookmarks_to_stars.py b/frappe/patches/v5_0/bookmarks_to_stars.py
deleted file mode 100644
index 0d2c13525e..0000000000
--- a/frappe/patches/v5_0/bookmarks_to_stars.py
+++ /dev/null
@@ -1,32 +0,0 @@
-
-import json
-import frappe
-import frappe.defaults
-from frappe.desk.like import _toggle_like
-
-def execute():
- for user in frappe.get_all("User"):
- username = user["name"]
- bookmarks = frappe.db.get_default("_bookmarks", username)
-
- if not bookmarks:
- continue
-
- if isinstance(bookmarks, str):
- bookmarks = json.loads(bookmarks)
-
- for opts in bookmarks:
- route = (opts.get("route") or "").strip("#/ ")
-
- if route and route.startswith("Form"):
- try:
- view, doctype, docname = opts["route"].split("/")
- except ValueError:
- continue
-
- if frappe.db.exists(doctype, docname):
- if (doctype=="DocType"
- or int(frappe.db.get_value("DocType", doctype, "issingle") or 0)
- or not frappe.db.table_exists(doctype)):
- continue
- _toggle_like(doctype, docname, add="Yes", user=username)
diff --git a/frappe/patches/v5_0/clear_website_group_and_notifications.py b/frappe/patches/v5_0/clear_website_group_and_notifications.py
deleted file mode 100644
index 3d3d0c0d16..0000000000
--- a/frappe/patches/v5_0/clear_website_group_and_notifications.py
+++ /dev/null
@@ -1,9 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.delete_doc("DocType", "Post")
- frappe.delete_doc("DocType", "Website Group")
- frappe.delete_doc("DocType", "Website Route Permission")
- frappe.delete_doc("DocType", "User Vote")
- frappe.delete_doc("DocType", "Notification Count")
diff --git a/frappe/patches/v5_0/communication_parent.py b/frappe/patches/v5_0/communication_parent.py
deleted file mode 100644
index 3c73d91972..0000000000
--- a/frappe/patches/v5_0/communication_parent.py
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doc("core", "doctype", "communication")
- frappe.db.sql("""update tabCommunication set reference_doctype = parenttype, reference_name = parent""")
diff --git a/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py b/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py
deleted file mode 100644
index 6fa6434f98..0000000000
--- a/frappe/patches/v5_0/convert_to_barracuda_and_utf8mb4.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-import frappe
-from frappe.database.mariadb.setup_db import check_database_settings
-from frappe.model.meta import trim_tables
-
-def execute():
- check_database_settings()
-
- for table in frappe.db.get_tables():
- frappe.db.sql_ddl("""alter table `{0}` ENGINE=InnoDB ROW_FORMAT=COMPRESSED""".format(table))
- try:
- frappe.db.sql_ddl("""alter table `{0}` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci""".format(table))
- except:
- # if row size gets too large, let it be old charset!
- pass
-
diff --git a/frappe/patches/v5_0/expire_old_scheduler_logs.py b/frappe/patches/v5_0/expire_old_scheduler_logs.py
deleted file mode 100644
index 0262acd346..0000000000
--- a/frappe/patches/v5_0/expire_old_scheduler_logs.py
+++ /dev/null
@@ -1,8 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doctype("Error Log")
-
- from frappe.core.doctype.error_log.error_log import set_old_logs_as_seen
- set_old_logs_as_seen()
diff --git a/frappe/patches/v5_0/fix_email_alert.py b/frappe/patches/v5_0/fix_email_alert.py
deleted file mode 100644
index e7366e8b66..0000000000
--- a/frappe/patches/v5_0/fix_email_alert.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import frappe
-
-def execute():
- frappe.reload_doctype("Notification")
- for e in frappe.get_all("Notification"):
- notification = frappe.get_doc("Notification", e.name)
- if notification.event == "Date Change":
- if notification.days_in_advance < 0:
- notification.event = "Days After"
- notification.days_in_advance = -email_alert.days_in_advance
- else:
- notification.event = "Days Before"
-
- notification.save()
diff --git a/frappe/patches/v5_0/fix_null_date_datetime.py b/frappe/patches/v5_0/fix_null_date_datetime.py
deleted file mode 100644
index 078cba079a..0000000000
--- a/frappe/patches/v5_0/fix_null_date_datetime.py
+++ /dev/null
@@ -1,20 +0,0 @@
-
-import frappe
-
-def execute():
- for table in frappe.db.get_tables():
- changed = False
- desc = frappe.db.sql("desc `{table}`".format(table=table), as_dict=True)
- for field in desc:
- if field["Type"] == "date":
- frappe.db.sql("""update `{table}` set `{fieldname}`=null where `{fieldname}`='0000-00-00'""".format(
- table=table, fieldname=field["Field"]))
- changed = True
-
- elif field["Type"] == "datetime(6)":
- frappe.db.sql("""update `{table}` set `{fieldname}`=null where `{fieldname}`='0000-00-00 00:00:00.000000'""".format(
- table=table, fieldname=field["Field"]))
- changed = True
-
- if changed:
- frappe.db.commit()
diff --git a/frappe/patches/v5_0/fix_text_editor_file_urls.py b/frappe/patches/v5_0/fix_text_editor_file_urls.py
deleted file mode 100644
index 43f0c9d8a5..0000000000
--- a/frappe/patches/v5_0/fix_text_editor_file_urls.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import frappe
-import re
-
-def execute():
- """Fix relative urls for image src="files/" to src="/files/" in DocTypes with text editor fields"""
- doctypes_with_text_fields = frappe.get_all("DocField", fields=["parent", "fieldname"],
- filters={"fieldtype": "Text Editor"})
-
- done = []
- for opts in doctypes_with_text_fields:
- if opts in done:
- continue
-
- try:
- result = frappe.get_all(opts.parent, fields=["name", opts.fieldname])
- except frappe.db.SQLError:
- # bypass single tables
- continue
-
- for data in result:
- old_value = data[opts.fieldname]
- if not old_value:
- continue
-
- html = scrub_relative_urls(old_value)
- if html != old_value:
- # print_diff(html, old_value)
- frappe.db.set_value(opts.parent, data.name, opts.fieldname, html, update_modified=False)
-
- done.append(opts)
-
-def scrub_relative_urls(html):
- """prepend a slash before a relative url"""
- try:
- return re.sub(r'src[\s]*=[\s]*[\'"]files/([^\'"]*)[\'"]', r'src="/files/\g<1>"', html)
- except:
- print("Error", html)
- raise
-
-def print_diff(html, old_value):
- import difflib
- diff = difflib.unified_diff(old_value.splitlines(1), html.splitlines(1), lineterm='')
- print('\n'.join(list(diff)))
diff --git a/frappe/patches/v5_0/force_sync_website.py b/frappe/patches/v5_0/force_sync_website.py
deleted file mode 100644
index 8f48729276..0000000000
--- a/frappe/patches/v5_0/force_sync_website.py
+++ /dev/null
@@ -1,5 +0,0 @@
-
-import frappe
-
-def execute():
- pass
diff --git a/frappe/patches/v5_0/modify_session.py b/frappe/patches/v5_0/modify_session.py
deleted file mode 100644
index 1c2ff0d6e6..0000000000
--- a/frappe/patches/v5_0/modify_session.py
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import frappe
-
-def execute():
- if "device" not in frappe.db.get_table_columns("Sessions"):
- frappe.db.sql("alter table tabSessions add column `device` varchar(255) default 'desktop'")
diff --git a/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py b/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py
deleted file mode 100644
index bdc52e6152..0000000000
--- a/frappe/patches/v5_0/move_scheduler_last_event_to_system_settings.py
+++ /dev/null
@@ -1,8 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doctype('System Settings')
- last = frappe.db.get_global('scheduler_last_event')
- frappe.db.set_value('System Settings', 'System Settings', 'scheduler_last_event', last)
-
diff --git a/frappe/patches/v5_0/remove_shopping_cart_app.py b/frappe/patches/v5_0/remove_shopping_cart_app.py
deleted file mode 100644
index ed9414159e..0000000000
--- a/frappe/patches/v5_0/remove_shopping_cart_app.py
+++ /dev/null
@@ -1,5 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-def execute():
- from frappe.installer import remove_from_installed_apps
- remove_from_installed_apps("shopping_cart")
diff --git a/frappe/patches/v5_0/rename_ref_type_fieldnames.py b/frappe/patches/v5_0/rename_ref_type_fieldnames.py
deleted file mode 100644
index 01e36af8a9..0000000000
--- a/frappe/patches/v5_0/rename_ref_type_fieldnames.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-import frappe
-
-def execute():
- try:
- frappe.db.sql("alter table `tabEmail Queue` change `ref_docname` `reference_name` varchar(255)")
- except Exception as e:
- if not frappe.db.is_table_or_column_missing(e):
- raise
-
- try:
- frappe.db.sql("alter table `tabEmail Queue` change `ref_doctype` `reference_doctype` varchar(255)")
- except Exception as e:
- if not frappe.db.is_table_or_column_missing(e):
- raise
- frappe.reload_doctype("Email Queue")
diff --git a/frappe/patches/v5_0/rename_table_fieldnames.py b/frappe/patches/v5_0/rename_table_fieldnames.py
deleted file mode 100644
index 79703bbba2..0000000000
--- a/frappe/patches/v5_0/rename_table_fieldnames.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-from frappe.model.utils.rename_field import rename_field
-from frappe.modules import scrub, get_doctype_module
-
-rename_map = {
- "Customize Form": [
- ["customize_form_fields", "fields"]
- ],
- "Email Alert": [
- ["email_alert_recipients", "recipients"]
- ],
- "Workflow": [
- ["workflow_document_states", "states"],
- ["workflow_transitions", "transitions"]
- ]
-}
-
-def execute():
- frappe.reload_doc("custom", "doctype", "customize_form")
- frappe.reload_doc("email", "doctype", "notification")
- frappe.reload_doc("desk", "doctype", "event")
- frappe.reload_doc("workflow", "doctype", "workflow")
-
- for dt, field_list in rename_map.items():
- for field in field_list:
- rename_field(dt, field[0], field[1])
diff --git a/frappe/patches/v5_0/style_settings_to_website_theme.py b/frappe/patches/v5_0/style_settings_to_website_theme.py
deleted file mode 100644
index 73ee28c1fc..0000000000
--- a/frappe/patches/v5_0/style_settings_to_website_theme.py
+++ /dev/null
@@ -1,59 +0,0 @@
-
-import frappe
-from frappe import _
-from frappe.utils import cint
-
-def execute():
- frappe.reload_doc("website", "doctype", "website_theme")
- frappe.reload_doc("website", "website_theme", "standard")
- frappe.reload_doctype("Website Settings")
- migrate_style_settings()
- frappe.delete_doc("website", "doctype", "style_settings")
-
-def migrate_style_settings():
- style_settings = frappe.db.get_singles_dict("Style Settings")
- standard_website_theme = frappe.get_doc("Website Theme", "Standard")
-
- website_theme = frappe.copy_doc(standard_website_theme)
- website_theme.custom = 1
- website_theme.theme = _("Custom")
-
- if style_settings:
- map_color_fields(style_settings, website_theme)
- map_other_fields(style_settings, website_theme)
-
- website_theme.no_sidebar = cint(frappe.db.get_single_value("Website Settings", "no_sidebar"))
-
- website_theme.save()
- website_theme.set_as_default()
-
-def map_color_fields(style_settings, website_theme):
- color_fields_map = {
- "page_text": "text_color",
- "page_links": "link_color",
- "top_bar_background": "top_bar_color",
- "top_bar_foreground": "top_bar_text_color",
- "footer_background": "footer_color",
- "footer_color": "footer_text_color",
- }
-
- for from_fieldname, to_fieldname in color_fields_map.items():
- from_value = style_settings.get(from_fieldname)
-
- if from_value:
- website_theme.set(to_fieldname, "#{0}".format(from_value))
-
-def map_other_fields(style_settings, website_theme):
- other_fields_map = {
- "heading_text_as": "heading_style",
- "google_web_font_for_heading": "heading_webfont",
- "google_web_font_for_text": "text_webfont",
- "add_css": "css"
- }
-
- for from_fieldname, to_fieldname in other_fields_map.items():
- website_theme.set(to_fieldname, style_settings.get(from_fieldname))
-
- for fieldname in ("apply_style", "background_image", "background_color",
- "font_size"):
- website_theme.set(fieldname, style_settings.get(fieldname))
diff --git a/frappe/patches/v5_0/update_shared.py b/frappe/patches/v5_0/update_shared.py
deleted file mode 100644
index e549d7271d..0000000000
--- a/frappe/patches/v5_0/update_shared.py
+++ /dev/null
@@ -1,37 +0,0 @@
-
-import frappe
-import frappe.share
-
-def execute():
- frappe.reload_doc("core", "doctype", "docperm")
- frappe.reload_doc("core", "doctype", "docshare")
- frappe.reload_doc('email', 'doctype', 'email_account')
-
- # default share to all writes
- frappe.db.sql("""update tabDocPerm set `share`=1 where ifnull(`write`,0)=1 and ifnull(`permlevel`,0)=0""")
-
- # every user must have access to his / her own detail
- users = frappe.get_all("User", filters={"user_type": "System User"})
- usernames = [user.name for user in users]
- for user in usernames:
- frappe.share.add("User", user, user, write=1, share=1)
-
- # move event user to shared
- if frappe.db.exists("DocType", "Event User"):
- for event in frappe.get_all("Event User", fields=["parent", "person"]):
- if event.person in usernames:
- if not frappe.db.exists("Event", event.parent):
- frappe.db.sql("delete from `tabEvent User` where parent = %s",event.parent)
- else:
- frappe.share.add("Event", event.parent, event.person, write=1)
-
- frappe.delete_doc("DocType", "Event User")
-
- # move note user to shared
- if frappe.db.exists("DocType", "Note User"):
- for note in frappe.get_all("Note User", fields=["parent", "user", "permission"]):
- perm = {"read": 1} if note.permission=="Read" else {"write": 1}
- if note.user in usernames:
- frappe.share.add("Note", note.parent, note.user, **perm)
-
- frappe.delete_doc("DocType", "Note User")
diff --git a/frappe/patches/v5_0/v4_to_v5.py b/frappe/patches/v5_0/v4_to_v5.py
deleted file mode 100644
index 479acc6d63..0000000000
--- a/frappe/patches/v5_0/v4_to_v5.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe
-
-def execute():
- changed = (
- ("desk", ("feed", "event", "todo", "note")),
- ("custom", ("custom_field", "custom_script", "customize_form",
- "customize_form_field", "property_setter")),
- ("email", ("email_queue", "notification", "notification_recipient", "standard_reply")),
- ("geo", ("country", "currency")),
- ("print", ("letter_head", "print_format", "print_settings"))
- )
- for module in changed:
- for doctype in module[1]:
- frappe.reload_doc(module[0], "doctype", doctype)
diff --git a/frappe/patches/v5_2/change_checks_to_not_null.py b/frappe/patches/v5_2/change_checks_to_not_null.py
deleted file mode 100644
index 32be3aa752..0000000000
--- a/frappe/patches/v5_2/change_checks_to_not_null.py
+++ /dev/null
@@ -1,34 +0,0 @@
-
-import frappe
-from frappe.utils import cint
-from frappe.model import default_fields
-
-def execute():
- for table in frappe.db.get_tables():
- doctype = table[3:]
- if frappe.db.exists("DocType", doctype):
- fieldnames = [df["fieldname"] for df in
- frappe.get_all("DocField", fields=["fieldname"], filters={"parent": doctype})]
- custom_fieldnames = [df["fieldname"] for df in
- frappe.get_all("Custom Field", fields=["fieldname"], filters={"dt": doctype})]
-
- else:
- fieldnames = custom_fieldnames = []
-
- for column in frappe.db.sql("""desc `{0}`""".format(table), as_dict=True):
- if column["Type"]=="int(1)":
- fieldname = column["Field"]
-
- # only change for defined fields, ignore old fields that don't exist in meta
- if not (fieldname in default_fields or fieldname in fieldnames or fieldname in custom_fieldnames):
- continue
-
- # set 0
- frappe.db.sql("""update `{table}` set `{column}`=0 where `{column}` is null"""\
- .format(table=table, column=fieldname))
- frappe.db.commit()
-
- # change definition
- frappe.db.sql_ddl("""alter table `{table}`
- modify `{column}` int(1) not null default {default}"""\
- .format(table=table, column=fieldname, default=cint(column["Default"])))
diff --git a/frappe/patches/v5_3/__init__.py b/frappe/patches/v5_3/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v5_3/rename_chinese_languages.py b/frappe/patches/v5_3/rename_chinese_languages.py
deleted file mode 100644
index f720fb7538..0000000000
--- a/frappe/patches/v5_3/rename_chinese_languages.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- coding: utf-8 -*-
-import frappe
-from frappe.translate import rename_language
-
-def execute():
- language_map = {
- "中国(简体)": "簡體中文",
- "中國(繁體)": "正體中文"
- }
-
- for old_name, new_name in language_map.items():
- rename_language(old_name, new_name)
diff --git a/frappe/patches/v6_0/__init__.py b/frappe/patches/v6_0/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_0/communication_status_and_permission.py b/frappe/patches/v6_0/communication_status_and_permission.py
deleted file mode 100644
index 435dcc21a5..0000000000
--- a/frappe/patches/v6_0/communication_status_and_permission.py
+++ /dev/null
@@ -1,19 +0,0 @@
-
-import frappe
-from frappe.permissions import reset_perms
-
-def execute():
- frappe.reload_doctype("Communication")
-
- # set status = "Linked"
- frappe.db.sql("""update `tabCommunication` set status='Linked'
- where ifnull(reference_doctype, '')!='' and ifnull(reference_name, '')!=''""")
-
- frappe.db.sql("""update `tabCommunication` set status='Closed'
- where status='Archived'""")
-
- # reset permissions if owner of all DocPerms is Administrator
- if not frappe.db.sql("""select name from `tabDocPerm`
- where parent='Communication' and ifnull(owner, '')!='Administrator'"""):
-
- reset_perms("Communication")
diff --git a/frappe/patches/v6_0/document_type_rename.py b/frappe/patches/v6_0/document_type_rename.py
deleted file mode 100644
index 53eec5d85c..0000000000
--- a/frappe/patches/v6_0/document_type_rename.py
+++ /dev/null
@@ -1,8 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.db.sql("""update tabDocType set document_type='Document'
- where document_type='Transaction'""")
- frappe.db.sql("""update tabDocType set document_type='Setup'
- where document_type='Master'""")
diff --git a/frappe/patches/v6_0/fix_ghana_currency.py b/frappe/patches/v6_0/fix_ghana_currency.py
deleted file mode 100644
index 50feb3ca3f..0000000000
--- a/frappe/patches/v6_0/fix_ghana_currency.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def execute():
- from frappe.geo.country_info import get_all
- import frappe.utils.install
-
- countries = get_all()
- frappe.utils.install.add_country_and_currency("Ghana", frappe._dict(countries["Ghana"]))
diff --git a/frappe/patches/v6_0/make_task_log_folder.py b/frappe/patches/v6_0/make_task_log_folder.py
deleted file mode 100644
index b5ed547d71..0000000000
--- a/frappe/patches/v6_0/make_task_log_folder.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-import frappe.utils, os
-
-def execute():
- path = frappe.utils.get_site_path('task-logs')
- if not os.path.exists(path):
- os.makedirs(path)
diff --git a/frappe/patches/v6_1/__init__.py b/frappe/patches/v6_1/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_1/rename_file_data.py b/frappe/patches/v6_1/rename_file_data.py
deleted file mode 100644
index 3c62217e8d..0000000000
--- a/frappe/patches/v6_1/rename_file_data.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import frappe
-
-def execute():
- from frappe.core.doctype.file.file import make_home_folder
-
- if not frappe.db.exists("DocType", "File"):
- frappe.rename_doc("DocType", "File Data", "File")
- frappe.reload_doctype("File")
-
- if not frappe.db.exists("File", {"is_home_folder": 1}):
- make_home_folder()
-
- # make missing folders and set parent folder
- for file in frappe.get_all("File", filters={"is_folder": 0}):
- file = frappe.get_doc("File", file.name)
- file.flags.ignore_folder_validate = True
- file.flags.ignore_file_validate = True
- file.flags.ignore_duplicate_entry_error = True
- file.flags.ignore_links = True
- file.set_folder_name()
- try:
- file.save()
- except:
- print(frappe.get_traceback())
- raise
-
- from frappe.utils.nestedset import rebuild_tree
- rebuild_tree("File", "folder")
-
- # reset file size
- for folder in frappe.db.sql("""select name from tabFile f1 where is_folder = 1 and
- (select count(*) from tabFile f2 where f2.folder = f1.name and f2.is_folder = 1) = 0"""):
- folder = frappe.get_doc("File", folder[0])
- folder.save()
-
-
-
diff --git a/frappe/patches/v6_11/__init__.py b/frappe/patches/v6_11/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_11/rename_field_in_email_account.py b/frappe/patches/v6_11/rename_field_in_email_account.py
deleted file mode 100644
index 8e600cc2b9..0000000000
--- a/frappe/patches/v6_11/rename_field_in_email_account.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doc("email", "doctype", "email_account")
- if frappe.db.has_column('Email Account', 'pop3_server'):
- frappe.db.sql("update `tabEmail Account` set email_server = pop3_server")
diff --git a/frappe/patches/v6_15/__init__.py b/frappe/patches/v6_15/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_15/remove_property_setter_for_previous_field.py b/frappe/patches/v6_15/remove_property_setter_for_previous_field.py
deleted file mode 100644
index 9f0cd69489..0000000000
--- a/frappe/patches/v6_15/remove_property_setter_for_previous_field.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-
-import frappe, json
-from frappe.utils import cstr
-
-def execute():
- # deprecated on 2016-03-09
- # using insert_after instead
- return
-
- frappe.db.sql("""delete from `tabProperty Setter` where property='previous_field'""")
-
- all_custom_fields = frappe._dict()
- for d in frappe.db.sql("""select name, dt, fieldname, insert_after from `tabCustom Field`
- where insert_after is not null and insert_after != ''""", as_dict=1):
- all_custom_fields.setdefault(d.dt, frappe._dict()).setdefault(d.fieldname, d.insert_after)
-
- for dt, custom_fields in all_custom_fields.items():
- _idx = []
- existing_ps = frappe.db.get_value("Property Setter",
- {"doc_type": dt, "property": "_idx"}, ["name", "value", "creation"], as_dict=1)
-
- # if no existsing property setter, build based on meta
- if not existing_ps:
- _idx = get_sorted_fields(dt, custom_fields)
- else:
- _idx = json.loads(existing_ps.value)
-
- idx_needs_to_be_fixed = False
- for fieldname, insert_after in custom_fields.items():
- # Delete existing property setter if field is not there
- if fieldname not in _idx:
- idx_needs_to_be_fixed = True
- break
- else:
- previous_field = _idx[_idx.index(fieldname) - 1]
-
- if previous_field != insert_after and cstr(existing_ps.creation) >= "2015-12-28":
- idx_needs_to_be_fixed = True
- break
-
- if idx_needs_to_be_fixed:
- frappe.delete_doc("Property Setter", existing_ps.name)
- _idx = get_sorted_fields(dt, custom_fields)
-
- if _idx:
- frappe.make_property_setter({
- "doctype":dt,
- "doctype_or_field": "DocType",
- "property": "_idx",
- "value": json.dumps(_idx),
- "property_type": "Text"
- }, validate_fields_for_doctype=False)
-
-
-def get_sorted_fields(doctype, custom_fields):
- """sort on basis of insert_after"""
- fields_dict = frappe.get_meta(doctype).get("fields")
-
- standard_fields_count = frappe.db.sql("""select count(name) from `tabDocField`
- where parent=%s""", doctype)[0][0]
-
- newlist = []
- pending = [d.fieldname for d in fields_dict]
-
- maxloops = len(custom_fields) + 20
- while (pending and maxloops>0):
- maxloops -= 1
- for fieldname in pending[:]:
- if fieldname in custom_fields and len(newlist) >= standard_fields_count:
- # field already added
- for n in newlist:
- if n==custom_fields.get(fieldname):
- newlist.insert(newlist.index(n)+1, fieldname)
- pending.remove(fieldname)
- break
- else:
- newlist.append(fieldname)
- pending.remove(fieldname)
-
- # recurring at end
- if pending:
- newlist += pending
-
- return newlist
diff --git a/frappe/patches/v6_15/set_username.py b/frappe/patches/v6_15/set_username.py
deleted file mode 100644
index ebf01763d0..0000000000
--- a/frappe/patches/v6_15/set_username.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doctype("User")
-
- # give preference to System Users
- users = frappe.db.sql_list("""select name from `tabUser` order by if(user_type='System User', 0, 1)""")
- for name in users:
- user = frappe.get_doc("User", name)
- if user.username or not user.first_name:
- continue
-
- username = user.suggest_username()
- if username:
- user.db_set("username", username, update_modified=False)
diff --git a/frappe/patches/v6_16/__init__.py b/frappe/patches/v6_16/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_16/feed_doc_owner.py b/frappe/patches/v6_16/feed_doc_owner.py
deleted file mode 100644
index b7e738b6d9..0000000000
--- a/frappe/patches/v6_16/feed_doc_owner.py
+++ /dev/null
@@ -1,31 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doctype("Communication")
-
- for doctype, name in frappe.db.sql("""select distinct reference_doctype, reference_name
- from `tabCommunication`
- where
- (reference_doctype is not null and reference_doctype != '')
- and (reference_name is not null and reference_name != '')
- and (reference_owner is null or reference_owner = '')
- for update"""):
-
- owner = frappe.db.get_value(doctype, name, "owner")
-
- if not owner:
- continue
-
- frappe.db.sql("""update `tabCommunication`
- set reference_owner=%(owner)s
- where
- reference_doctype=%(doctype)s
- and reference_name=%(name)s
- and (reference_owner is null or reference_owner = '')""".format(doctype=doctype), {
- "doctype": doctype,
- "name": name,
- "owner": owner
- })
-
- frappe.db.commit()
diff --git a/frappe/patches/v6_16/star_to_like.py b/frappe/patches/v6_16/star_to_like.py
deleted file mode 100644
index f3fc6310d9..0000000000
--- a/frappe/patches/v6_16/star_to_like.py
+++ /dev/null
@@ -1,15 +0,0 @@
-
-import frappe
-from frappe.database.schema import add_column
-
-def execute():
- frappe.db.sql("""update `tabSingles` set field='_liked_by' where field='_starred_by'""")
- frappe.db.commit()
-
- for table in frappe.db.get_tables():
- columns = [r[0] for r in frappe.db.sql("DESC `{0}`".format(table))]
- if "_starred_by" in columns and '_liked_by' not in columns:
- frappe.db.sql_ddl("""alter table `{0}` change `_starred_by` `_liked_by` Text """.format(table))
-
- if not frappe.db.has_column("Communication", "_liked_by"):
- add_column("Communication", "_liked_by", "Text")
diff --git a/frappe/patches/v6_19/__init__.py b/frappe/patches/v6_19/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_19/comment_feed_communication.py b/frappe/patches/v6_19/comment_feed_communication.py
deleted file mode 100644
index 64c5ad9c4c..0000000000
--- a/frappe/patches/v6_19/comment_feed_communication.py
+++ /dev/null
@@ -1,307 +0,0 @@
-
-import frappe
-from frappe import _
-from frappe.model.rename_doc import get_link_fields
-from frappe.model.dynamic_links import dynamic_link_queries
-from frappe.permissions import reset_perms
-
-def execute():
- # comments stay comments in v12
- return
-
- frappe.reload_doctype("DocType")
- frappe.reload_doctype("Communication")
- reset_perms("Communication")
-
- migrate_comments()
- frappe.delete_doc("DocType", "Comment")
- # frappe.db.sql_ddl("drop table `tabComment`")
-
- migrate_feed()
- frappe.delete_doc("DocType", "Feed")
- # frappe.db.sql_ddl("drop table `tabFeed`")
-
- update_timeline_doc_for("Blogger")
-
-def migrate_comments():
- from_fields = ""
- to_fields = ""
-
- if "reference_doctype" in frappe.db.get_table_columns("Comment"):
- from_fields = "reference_doctype as link_doctype, reference_name as link_name,"
- to_fields = "link_doctype, link_name,"
-
- # comments
- frappe.db.sql("""insert ignore into `tabCommunication` (
- subject,
- content,
- sender,
- sender_full_name,
- comment_type,
- communication_date,
- reference_doctype,
- reference_name,
- {to_fields}
-
- name,
- user,
- owner,
- creation,
- modified_by,
- modified,
- status,
- sent_or_received,
- communication_type,
- seen
- )
- select
- substring(comment, 1, 100) as subject,
- comment as content,
- comment_by as sender,
- comment_by_fullname as sender_full_name,
- comment_type,
- ifnull(timestamp(comment_date, comment_time), creation) as communication_date,
- comment_doctype as reference_doctype,
- comment_docname as reference_name,
- {from_fields}
-
- name,
- owner as user,
- owner,
- creation,
- modified_by,
- modified,
- 'Linked' as status,
- 'Sent' as sent_or_received,
- 'Comment' as communication_type,
- 1 as seen
- from `tabComment` where comment_doctype is not null and comment_doctype not in ('Message', 'My Company')"""
- .format(to_fields=to_fields, from_fields=from_fields))
-
- # chat and assignment notifications
- frappe.db.sql("""insert ignore into `tabCommunication` (
- subject,
- content,
- sender,
- sender_full_name,
- comment_type,
- communication_date,
- reference_doctype,
- reference_name,
- {to_fields}
-
- name,
- user,
- owner,
- creation,
- modified_by,
- modified,
- status,
- sent_or_received,
- communication_type,
- seen
- )
- select
- case
- when parenttype='Assignment' then %(assignment)s
- else substring(comment, 1, 100)
- end
- as subject,
- comment as content,
- comment_by as sender,
- comment_by_fullname as sender_full_name,
- comment_type,
- ifnull(timestamp(comment_date, comment_time), creation) as communication_date,
- 'User' as reference_doctype,
- comment_docname as reference_name,
- {from_fields}
-
- name,
- owner as user,
- owner,
- creation,
- modified_by,
- modified,
- 'Linked' as status,
- 'Sent' as sent_or_received,
- case
- when parenttype='Assignment' then 'Notification'
- else 'Chat'
- end
- as communication_type,
- 1 as seen
- from `tabComment` where comment_doctype in ('Message', 'My Company')"""
- .format(to_fields=to_fields, from_fields=from_fields), {"assignment": _("Assignment")})
-
-def migrate_feed():
- # migrate delete feed
- for doctype in frappe.db.sql("""select distinct doc_type from `tabFeed` where subject=%(deleted)s""", {"deleted": _("Deleted")}):
- frappe.db.sql("""insert ignore into `tabCommunication` (
- subject,
- sender,
- sender_full_name,
- comment_type,
- communication_date,
- reference_doctype,
-
- name,
- user,
- owner,
- creation,
- modified_by,
- modified,
- status,
- sent_or_received,
- communication_type,
- seen
- )
- select
- concat_ws(" ", %(_doctype)s, doc_name) as subject,
- owner as sender,
- full_name as sender_full_name,
- 'Deleted' as comment_type,
- creation as communication_date,
- doc_type as reference_doctype,
-
- name,
- owner as user,
- owner,
- creation,
- modified_by,
- modified,
- 'Linked' as status,
- 'Sent' as sent_or_received,
- 'Comment' as communication_type,
- 1 as seen
- from `tabFeed` where subject=%(deleted)s and doc_type=%(doctype)s""", {
- "deleted": _("Deleted"),
- "doctype": doctype,
- "_doctype": _(doctype)
- })
-
- # migrate feed type login or empty
- frappe.db.sql("""insert ignore into `tabCommunication` (
- subject,
- sender,
- sender_full_name,
- comment_type,
- communication_date,
- reference_doctype,
- reference_name,
-
- name,
- user,
- owner,
- creation,
- modified_by,
- modified,
- status,
- sent_or_received,
- communication_type,
- seen
- )
- select
- subject,
- owner as sender,
- full_name as sender_full_name,
- case
- when feed_type='Login' then 'Info'
- else 'Updated'
- end as comment_type,
- creation as communication_date,
- doc_type as reference_doctype,
- doc_name as reference_name,
-
- name,
- owner as user,
- owner,
- creation,
- modified_by,
- modified,
- 'Linked' as status,
- 'Sent' as sent_or_received,
- 'Comment' as communication_type,
- 1 as seen
- from `tabFeed` where (feed_type in ('Login', '') or feed_type is null)""")
-
-def update_timeline_doc_for(timeline_doctype):
- """NOTE: This method may be used by other apps for patching. It also has COMMIT after each update."""
-
- # find linked doctypes
- # link fields
- update_for_linked_docs(timeline_doctype)
-
- # dynamic link fields
- update_for_dynamically_linked_docs(timeline_doctype)
-
-def update_for_linked_docs(timeline_doctype):
- for df in get_link_fields(timeline_doctype):
- if df.issingle:
- continue
-
- reference_doctype = df.parent
-
- if not is_valid_timeline_doctype(reference_doctype, timeline_doctype):
- continue
-
- for doc in frappe.get_all(reference_doctype, fields=["name", df.fieldname]):
- timeline_name = doc.get(df.fieldname)
- update_communication(timeline_doctype, timeline_name, reference_doctype, doc.name)
-
-def update_for_dynamically_linked_docs(timeline_doctype):
- dynamic_link_fields = []
- for query in dynamic_link_queries:
- for df in frappe.db.sql(query, as_dict=True):
- dynamic_link_fields.append(df)
-
- for df in dynamic_link_fields:
- reference_doctype = df.parent
-
- if not is_valid_timeline_doctype(reference_doctype, timeline_doctype):
- continue
-
- try:
- docs = frappe.get_all(reference_doctype, fields=["name", df.fieldname],
- filters={ df.options: timeline_doctype })
- except frappe.db.SQLError as e:
- if frappe.db.is_table_missing(e):
- # single
- continue
- else:
- raise
-
- for doc in docs:
- timeline_name = doc.get(df.fieldname)
- update_communication(timeline_doctype, timeline_name, reference_doctype, doc.name)
-
-def update_communication(timeline_doctype, timeline_name, reference_doctype, reference_name):
- if not timeline_name:
- return
-
- frappe.db.sql("""update `tabCommunication` set timeline_doctype=%(timeline_doctype)s, timeline_name=%(timeline_name)s
- where (reference_doctype=%(reference_doctype)s and reference_name=%(reference_name)s)
- and (timeline_doctype is null or timeline_doctype='')
- and (timeline_name is null or timeline_name='')""", {
- "timeline_doctype": timeline_doctype,
- "timeline_name": timeline_name,
- "reference_doctype": reference_doctype,
- "reference_name": reference_name
- })
-
- frappe.db.commit()
-
-def is_valid_timeline_doctype(reference_doctype, timeline_doctype):
- # for reloading timeline_field
- frappe.reload_doctype(reference_doctype)
-
- # make sure the timeline field's doctype is same as timeline doctype
- meta = frappe.get_meta(reference_doctype)
- if not meta.timeline_field:
- return False
-
- doctype = meta.get_link_doctype(meta.timeline_field)
- if doctype != timeline_doctype:
- return False
-
-
- return True
diff --git a/frappe/patches/v6_2/__init__.py b/frappe/patches/v6_2/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_2/ignore_user_permissions_if_missing.py b/frappe/patches/v6_2/ignore_user_permissions_if_missing.py
deleted file mode 100644
index e216dc36b6..0000000000
--- a/frappe/patches/v6_2/ignore_user_permissions_if_missing.py
+++ /dev/null
@@ -1,8 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doctype("System Settings")
- system_settings = frappe.get_doc("System Settings")
- system_settings.flags.ignore_mandatory = 1
- system_settings.save()
diff --git a/frappe/patches/v6_2/rename_backup_manager.py b/frappe/patches/v6_2/rename_backup_manager.py
deleted file mode 100644
index df2fa72c05..0000000000
--- a/frappe/patches/v6_2/rename_backup_manager.py
+++ /dev/null
@@ -1,20 +0,0 @@
-
-import frappe
-
-def execute():
- unset = False
- frappe.reload_doc("integrations", "doctype", "dropbox_backup")
-
- dropbox_backup = frappe.get_doc("Dropbox Backup", "Dropbox Backup")
- for df in dropbox_backup.meta.fields:
- value = frappe.db.get_single_value("Backup Manager", df.fieldname)
- if value:
- if df.fieldname=="upload_backups_to_dropbox" and value=="Never":
- value = "Daily"
- unset = True
- dropbox_backup.set(df.fieldname, value)
-
- if unset:
- dropbox_backup.set("send_backups_to_dropbox", 0)
-
- dropbox_backup.save()
diff --git a/frappe/patches/v6_20x/__init__.py b/frappe/patches/v6_20x/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_20x/remove_roles_from_website_user.py b/frappe/patches/v6_20x/remove_roles_from_website_user.py
deleted file mode 100644
index 19009ff455..0000000000
--- a/frappe/patches/v6_20x/remove_roles_from_website_user.py
+++ /dev/null
@@ -1,11 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doc("core", "doctype", "user_email")
- frappe.reload_doc("core", "doctype", "user")
- for user_name in frappe.get_all('User', filters={'user_type': 'Website User'}):
- user = frappe.get_doc('User', user_name)
- if user.roles:
- user.roles = []
- user.save()
diff --git a/frappe/patches/v6_20x/set_allow_draft_for_print.py b/frappe/patches/v6_20x/set_allow_draft_for_print.py
deleted file mode 100644
index 0b604567ec..0000000000
--- a/frappe/patches/v6_20x/set_allow_draft_for_print.py
+++ /dev/null
@@ -1,5 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.db.set_value("Print Settings", "Print Settings", "allow_print_for_draft", 1)
\ No newline at end of file
diff --git a/frappe/patches/v6_20x/update_insert_after.py b/frappe/patches/v6_20x/update_insert_after.py
deleted file mode 100644
index 37820b2437..0000000000
--- a/frappe/patches/v6_20x/update_insert_after.py
+++ /dev/null
@@ -1,27 +0,0 @@
-
-import frappe, json
-
-def execute():
- for ps in frappe.get_all('Property Setter', filters={'property': '_idx'},
- fields = ['doc_type', 'value']):
- custom_fields = frappe.get_all('Custom Field',
- filters = {'dt': ps.doc_type}, fields=['name', 'fieldname'])
-
- if custom_fields:
- _idx = json.loads(ps.value)
-
- for custom_field in custom_fields:
- if custom_field.fieldname in _idx:
- custom_field_idx = _idx.index(custom_field.fieldname)
- if custom_field_idx == 0:
- prev_fieldname = ""
-
- else:
- prev_fieldname = _idx[custom_field_idx - 1]
-
- else:
- prev_fieldname = _idx[-1]
- custom_field_idx = len(_idx)
-
- frappe.db.set_value('Custom Field', custom_field.name, 'insert_after', prev_fieldname)
- frappe.db.set_value('Custom Field', custom_field.name, 'idx', custom_field_idx)
diff --git a/frappe/patches/v6_21/__init__.py b/frappe/patches/v6_21/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_21/print_settings_repeat_header_footer.py b/frappe/patches/v6_21/print_settings_repeat_header_footer.py
deleted file mode 100644
index 0919c35903..0000000000
--- a/frappe/patches/v6_21/print_settings_repeat_header_footer.py
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doctype('Print Settings')
- frappe.db.set_value('Print Settings', 'Print Settings', 'repeat_header_footer', 1)
diff --git a/frappe/patches/v6_24/__init__.py b/frappe/patches/v6_24/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_24/set_language_as_code.py b/frappe/patches/v6_24/set_language_as_code.py
deleted file mode 100644
index 6f862ede2e..0000000000
--- a/frappe/patches/v6_24/set_language_as_code.py
+++ /dev/null
@@ -1,8 +0,0 @@
-
-import frappe
-
-from frappe.translate import get_lang_dict
-
-# migrate language from name to code
-def execute():
- return
diff --git a/frappe/patches/v6_4/__init__.py b/frappe/patches/v6_4/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_4/reduce_varchar_length.py b/frappe/patches/v6_4/reduce_varchar_length.py
deleted file mode 100644
index 7edde55778..0000000000
--- a/frappe/patches/v6_4/reduce_varchar_length.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import frappe
-
-def execute():
- for doctype in frappe.get_all("DocType", filters={"issingle": 0}):
- doctype = doctype.name
- if not frappe.db.table_exists(doctype):
- continue
-
- for column in frappe.db.sql("desc `tab{doctype}`".format(doctype=doctype), as_dict=True):
- fieldname = column["Field"]
- column_type = column["Type"]
-
- if not column_type.startswith("varchar"):
- continue
-
- max_length = frappe.db.sql("""select max(char_length(`{fieldname}`)) from `tab{doctype}`"""\
- .format(fieldname=fieldname, doctype=doctype))
-
- max_length = max_length[0][0] if max_length else None
-
- if max_length and 140 < max_length <= 255:
- print(
- "setting length of '{fieldname}' in '{doctype}' as {length}".format(
- fieldname=fieldname, doctype=doctype, length=max_length)
- )
-
- # create property setter for length
- frappe.make_property_setter({
- "doctype": doctype,
- "fieldname": fieldname,
- "property": "length",
- "value": max_length,
- "property_type": "Int"
- })
-
- frappe.clear_cache(doctype=doctype)
diff --git a/frappe/patches/v6_4/rename_bengali_language.py b/frappe/patches/v6_4/rename_bengali_language.py
deleted file mode 100644
index f872dea1b9..0000000000
--- a/frappe/patches/v6_4/rename_bengali_language.py
+++ /dev/null
@@ -1,6 +0,0 @@
-# -*- coding: utf-8 -*-
-import frappe
-from frappe.translate import rename_language
-
-def execute():
- rename_language("বাঙালি", "বাংলা")
\ No newline at end of file
diff --git a/frappe/patches/v6_6/__init__.py b/frappe/patches/v6_6/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_6/fix_file_url.py b/frappe/patches/v6_6/fix_file_url.py
deleted file mode 100644
index 48e292f4d4..0000000000
--- a/frappe/patches/v6_6/fix_file_url.py
+++ /dev/null
@@ -1,36 +0,0 @@
-
-import frappe
-from frappe.model.meta import is_single
-
-def execute():
- """Fix old style file urls that start with files/"""
- fix_file_urls()
- fix_attach_field_urls()
-
-def fix_file_urls():
- for file in frappe.db.sql_list("""select name from `tabFile` where file_url like 'files/%'"""):
- file = frappe.get_doc("File", file)
- file.db_set("file_url", "/" + file.file_url, update_modified=False)
- try:
- file.validate_file()
- file.db_set("file_name", file.file_name, update_modified=False)
- if not file.content_hash:
- file.generate_content_hash()
- file.db_set("content_hash", file.content_hash, update_modified=False)
-
- except IOError:
- pass
-
-def fix_attach_field_urls():
- # taken from an old patch
- attach_fields = (frappe.db.sql("""select parent, fieldname from `tabDocField` where fieldtype in ('Attach', 'Attach Image')""") +
- frappe.db.sql("""select dt, fieldname from `tabCustom Field` where fieldtype in ('Attach', 'Attach Image')"""))
-
- for doctype, fieldname in attach_fields:
- if is_single(doctype):
- frappe.db.sql("""update `tabSingles` set value=concat("/", `value`)
- where doctype=%(doctype)s and field=%(fieldname)s
- and value like 'files/%%'""", {"doctype": doctype, "fieldname": fieldname})
- else:
- frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=concat("/", `{fieldname}`)
- where `{fieldname}` like 'files/%'""".format(doctype=doctype, fieldname=fieldname))
diff --git a/frappe/patches/v6_6/rename_slovak_language.py b/frappe/patches/v6_6/rename_slovak_language.py
deleted file mode 100644
index 198949e79c..0000000000
--- a/frappe/patches/v6_6/rename_slovak_language.py
+++ /dev/null
@@ -1,6 +0,0 @@
-# -*- coding: utf-8 -*-
-import frappe
-from frappe.translate import rename_language
-
-def execute():
- rename_language("slovenčina", "slovenčina (Slovak)")
diff --git a/frappe/patches/v6_6/user_last_active.py b/frappe/patches/v6_6/user_last_active.py
deleted file mode 100644
index b9f63fa45e..0000000000
--- a/frappe/patches/v6_6/user_last_active.py
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doctype("User")
- frappe.db.sql("update `tabUser` set last_active=last_login")
diff --git a/frappe/patches/v6_9/__init__.py b/frappe/patches/v6_9/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v6_9/int_float_not_null.py b/frappe/patches/v6_9/int_float_not_null.py
deleted file mode 100644
index c414d6b583..0000000000
--- a/frappe/patches/v6_9/int_float_not_null.py
+++ /dev/null
@@ -1,30 +0,0 @@
-
-import frappe
-from frappe.utils import cint, flt
-
-def execute():
- for doctype in frappe.get_all("DocType", filters={"issingle": 0}):
- doctype = doctype.name
- meta = frappe.get_meta(doctype)
-
- for column in frappe.db.sql("desc `tab{doctype}`".format(doctype=doctype), as_dict=True):
- fieldname = column["Field"]
- column_type = column["Type"]
-
- if not (column_type.startswith("int") or column_type.startswith("decimal")):
- continue
-
- frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=0 where `{fieldname}` is null"""\
- .format(doctype=doctype, fieldname=fieldname))
-
- # alter table
- if column["Null"]=='YES':
- if not meta.get_field(fieldname):
- continue
-
- default = cint(column["Default"]) if column_type.startswith("int") else flt(column["Default"])
- frappe.db.sql_ddl("""alter table `tab{doctype}`
- change `{fieldname}` `{fieldname}` {column_type} not null default '{default}'""".format(
- doctype=doctype, fieldname=fieldname, column_type=column_type, default=default))
-
-
diff --git a/frappe/patches/v6_9/rename_burmese_language.py b/frappe/patches/v6_9/rename_burmese_language.py
deleted file mode 100644
index 5e1333077e..0000000000
--- a/frappe/patches/v6_9/rename_burmese_language.py
+++ /dev/null
@@ -1,6 +0,0 @@
-# -*- coding: utf-8 -*-
-import frappe
-from frappe.translate import rename_language
-
-def execute():
- rename_language("Melayu", "မြန်မာ")
diff --git a/frappe/patches/v7_0/__init__.py b/frappe/patches/v7_0/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v7_0/add_communication_in_doc.py b/frappe/patches/v7_0/add_communication_in_doc.py
deleted file mode 100644
index 8be229fe3a..0000000000
--- a/frappe/patches/v7_0/add_communication_in_doc.py
+++ /dev/null
@@ -1,14 +0,0 @@
-
-import frappe
-
-from frappe.core.doctype.comment.comment import update_comment_in_doc
-
-def execute():
- for d in frappe.db.get_all("Communication",
- fields = ['name', 'reference_doctype', 'reference_name', 'SUBSTRING(content,1,102)', 'communication_type'],
- filters = {"reference_name":None,"reference_doctype":None,'communication_type': 'Communication'}):
-
- try:
- update_comment_in_doc(d)
- except frappe.ImplicitCommitError:
- pass
diff --git a/frappe/patches/v7_0/cleanup_list_settings.py b/frappe/patches/v7_0/cleanup_list_settings.py
deleted file mode 100644
index 9fe2e71ed1..0000000000
--- a/frappe/patches/v7_0/cleanup_list_settings.py
+++ /dev/null
@@ -1,20 +0,0 @@
-
-import frappe, json
-
-def execute():
- if frappe.db.table_exists("__ListSettings"):
- list_settings = frappe.db.sql("select user, doctype, data from __ListSettings", as_dict=1)
- for ls in list_settings:
- if ls and ls.data:
- data = json.loads(ls.data)
- if "fields" not in data:
- continue
- fields = data["fields"]
- for field in fields:
- if "name as" in field:
- fields.remove(field)
- data["fields"] = fields
-
- frappe.db.sql("update __ListSettings set data = %s where user=%s and doctype=%s",
- (json.dumps(data), ls.user, ls.doctype))
-
diff --git a/frappe/patches/v7_0/create_private_file_folder.py b/frappe/patches/v7_0/create_private_file_folder.py
deleted file mode 100644
index e89beb5d0f..0000000000
--- a/frappe/patches/v7_0/create_private_file_folder.py
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import frappe, os
-
-def execute():
- if not os.path.exists(os.path.join(frappe.local.site_path, 'private', 'files')):
- frappe.create_folder(os.path.join(frappe.local.site_path, 'private', 'files'))
\ No newline at end of file
diff --git a/frappe/patches/v7_0/re_route.py b/frappe/patches/v7_0/re_route.py
deleted file mode 100644
index 8a4daaea86..0000000000
--- a/frappe/patches/v7_0/re_route.py
+++ /dev/null
@@ -1,23 +0,0 @@
-
-import frappe
-from frappe.model.base_document import get_controller
-
-def execute():
- update_routes(['Blog Post', 'Blog Category', 'Web Page'])
-
-def update_routes(doctypes):
- """Patch old routing system"""
- for d in doctypes:
- frappe.reload_doctype(d)
- c = get_controller(d)
-
- condition = ''
- if c.website.condition_field:
- condition = 'where {0}=1'.format(c.website.condition_field)
-
- try:
- frappe.db.sql("""update ignore `tab{0}` set route = concat(ifnull(parent_website_route, ""),
- if(ifnull(parent_website_route, "")="", "", "/"), page_name) {1}""".format(d, condition))
-
- except Exception as e:
- if not frappe.db.is_missing_column(e): raise
diff --git a/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py b/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py
deleted file mode 100644
index 42f2dfe4c2..0000000000
--- a/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py
+++ /dev/null
@@ -1,5 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.rename_doc('DocType', 'Bulk Email', 'Email Queue')
\ No newline at end of file
diff --git a/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py b/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py
deleted file mode 100644
index 5e40d9df35..0000000000
--- a/frappe/patches/v7_0/rename_newsletter_list_to_email_group.py
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.rename_doc('DocType', 'Newsletter List', 'Email Group')
- frappe.rename_doc('DocType', 'Newsletter List Subscriber', 'Email Group Member')
\ No newline at end of file
diff --git a/frappe/patches/v7_0/set_email_group.py b/frappe/patches/v7_0/set_email_group.py
deleted file mode 100644
index 251e9a27b6..0000000000
--- a/frappe/patches/v7_0/set_email_group.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-def execute():
- frappe.reload_doc("email", "doctype", "email_group_member")
- if "newsletter_list" in frappe.db.get_table_columns("Email Group Member"):
- frappe.db.sql("""update `tabEmail Group Member` set email_group = newsletter_list
- where email_group is null or email_group = ''""")
\ No newline at end of file
diff --git a/frappe/patches/v7_0/set_user_fullname.py b/frappe/patches/v7_0/set_user_fullname.py
deleted file mode 100644
index e69c180c27..0000000000
--- a/frappe/patches/v7_0/set_user_fullname.py
+++ /dev/null
@@ -1,10 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doc("Core", "DocType", "User")
-
- for user in frappe.db.get_all('User'):
- user = frappe.get_doc('User', user.name)
- user.set_full_name()
- user.db_set('full_name', user.full_name, update_modified = False)
\ No newline at end of file
diff --git a/frappe/patches/v7_0/update_auth.py b/frappe/patches/v7_0/update_auth.py
deleted file mode 100644
index 098081563f..0000000000
--- a/frappe/patches/v7_0/update_auth.py
+++ /dev/null
@@ -1,42 +0,0 @@
-
-import frappe
-from frappe.utils.password import create_auth_table, set_encrypted_password
-
-def execute():
- if '__OldAuth' not in frappe.db.get_tables():
- frappe.db.sql_ddl('''alter table `__Auth` rename `__OldAuth`''')
-
- create_auth_table()
-
- # user passwords
- frappe.db.sql('''insert ignore into `__Auth` (doctype, name, fieldname, `password`)
- (select 'User', `name`, 'password', `password` from `__OldAuth`)''')
-
- frappe.db.commit()
-
- # other password fields
- for doctype in frappe.db.sql_list('''select distinct parent from `tabDocField`
- where fieldtype="Password" and parent != "User"'''):
-
- frappe.reload_doctype(doctype)
- meta = frappe.get_meta(doctype)
-
- for df in meta.get('fields', {'fieldtype': 'Password'}):
- if meta.issingle:
- password = frappe.db.get_value(doctype, doctype, df.fieldname)
- if password:
- set_encrypted_password(doctype, doctype, password, fieldname=df.fieldname)
- frappe.db.set_value(doctype, doctype, df.fieldname, '*'*len(password))
-
- else:
- for d in frappe.db.sql('''select name, `{fieldname}` from `tab{doctype}`
- where `{fieldname}` is not null'''.format(fieldname=df.fieldname, doctype=doctype), as_dict=True):
-
- set_encrypted_password(doctype, d.name, d.get(df.fieldname), fieldname=df.fieldname)
-
- frappe.db.sql('''update `tab{doctype}` set `{fieldname}`=repeat("*", char_length(`{fieldname}`))'''
- .format(doctype=doctype, fieldname=df.fieldname))
-
- frappe.db.commit()
-
- frappe.db.sql_ddl('''drop table `__OldAuth`''')
diff --git a/frappe/patches/v7_0/update_report_builder_json.py b/frappe/patches/v7_0/update_report_builder_json.py
deleted file mode 100644
index 01a6126de7..0000000000
--- a/frappe/patches/v7_0/update_report_builder_json.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-def execute():
- for report in frappe.db.sql_list(""" select name from `tabReport` where report_type = 'Report Builder'
- and is_standard = 'No' and `json` != '' and `json` is not null """):
- doc = frappe.get_doc("Report", report)
- doc.update_report_json()
- doc.db_set("json", doc.json, update_modified=False)
\ No newline at end of file
diff --git a/frappe/patches/v7_0/update_send_after_in_bulk_email.py b/frappe/patches/v7_0/update_send_after_in_bulk_email.py
deleted file mode 100644
index b9da83eaab..0000000000
--- a/frappe/patches/v7_0/update_send_after_in_bulk_email.py
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import frappe
-from frappe.utils import now_datetime
-
-def execute():
- frappe.db.sql('update `tabEmail Queue` set send_after=%s where send_after is null', now_datetime())
\ No newline at end of file
diff --git a/frappe/patches/v7_1/__init__.py b/frappe/patches/v7_1/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py b/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py
deleted file mode 100644
index 6ab9340845..0000000000
--- a/frappe/patches/v7_1/disabled_print_settings_for_custom_print_format.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-def execute():
- frappe.reload_doctype('Print Format')
- frappe.db.sql("""
- update
- `tabPrint Format`
- set
- align_labels_right = 0, line_breaks = 0, show_section_headings = 0
- where
- custom_format = 1
- """)
diff --git a/frappe/patches/v7_1/refactor_integration_broker.py b/frappe/patches/v7_1/refactor_integration_broker.py
deleted file mode 100644
index 05ccae5d46..0000000000
--- a/frappe/patches/v7_1/refactor_integration_broker.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-import json
-
-def execute():
- for doctype_name in ["Razorpay Log", "Razorpay Payment", "Razorpay Settings"]:
- delete_doc("DocType", doctype_name)
-
- reload_doctypes()
- setup_services()
-
-def delete_doc(doctype, doctype_name):
- frappe.delete_doc(doctype, doctype_name)
-
-def reload_doctypes():
- for doctype in ("razorpay_settings", "paypal_settings", "dropbox_settings", "ldap_settings"):
- frappe.reload_doc("integrations", "doctype", doctype)
-
-def setup_services():
- for service in [{"old_name": "Razorpay", "new_name": "Razorpay"},
- {"old_name": "PayPal", "new_name": "PayPal"},
- {"old_name": "Dropbox Integration", "new_name": "Dropbox"},
- {"old_name": "LDAP Auth", "new_name": "LDAP"}]:
-
- try:
- service_doc = frappe.get_doc("Integration Service", service["old_name"])
- settings = json.loads(service_doc.custom_settings_json)
-
- service_settings = frappe.new_doc("{0} Settings".format(service["new_name"]))
- service_settings.update(settings)
-
- service_settings.flags.ignore_mandatory = True
- service_settings.save(ignore_permissions=True)
-
- if service["old_name"] in ["Dropbox Integration", "LDAP Auth"]:
- delete_doc("Integration Service", service["old_name"])
-
- new_service_doc = frappe.get_doc({
- "doctype": "Integration Service",
- "service": service["new_name"],
- "enabled": 1
- })
-
- new_service_doc.flags.ignore_mandatory = True
- new_service_doc.save(ignore_permissions=True)
-
- except Exception:
- pass
diff --git a/frappe/patches/v7_1/rename_chinese_language_codes.py b/frappe/patches/v7_1/rename_chinese_language_codes.py
deleted file mode 100644
index 91ed73ccae..0000000000
--- a/frappe/patches/v7_1/rename_chinese_language_codes.py
+++ /dev/null
@@ -1,11 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.rename_doc('Language', 'zh-cn', 'zh', force=True,
- merge=True if frappe.db.exists('Language', 'zh') else False)
- if frappe.db.get_value('Language', 'zh-tw') == 'zh-tw':
- frappe.rename_doc('Language', 'zh-tw', 'zh-TW', force=True)
-
- frappe.db.set_value('Language', 'zh', 'language_code', 'zh')
- frappe.db.set_value('Language', 'zh-TW', 'language_code', 'zh-TW')
\ No newline at end of file
diff --git a/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py b/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py
deleted file mode 100644
index c0c9e03565..0000000000
--- a/frappe/patches/v7_1/rename_scheduler_log_to_error_log.py
+++ /dev/null
@@ -1,11 +0,0 @@
-
-import frappe
-
-def execute():
- if not 'tabError Log' in frappe.db.get_tables():
- frappe.rename_doc('DocType', 'Scheduler Log', 'Error Log')
- frappe.db.sql("""delete from `tabError Log` where datediff(curdate(), creation) > 30""")
- frappe.db.commit()
- frappe.db.sql('alter table `tabError Log` change column name name varchar(140)')
- frappe.db.sql('alter table `tabError Log` change column parent parent varchar(140)')
- frappe.db.sql('alter table `tabError Log` engine=MyISAM')
diff --git a/frappe/patches/v7_1/set_backup_limit.py b/frappe/patches/v7_1/set_backup_limit.py
deleted file mode 100644
index ce502393b2..0000000000
--- a/frappe/patches/v7_1/set_backup_limit.py
+++ /dev/null
@@ -1,10 +0,0 @@
-
-from frappe.utils import cint
-import frappe
-
-def execute():
- frappe.reload_doctype('System Settings')
- backup_limit = frappe.db.get_single_value('System Settings', 'backup_limit')
-
- if cint(backup_limit) == 0:
- frappe.db.set_value('System Settings', 'System Settings', 'backup_limit', 3)
diff --git a/frappe/patches/v7_1/setup_integration_services.py b/frappe/patches/v7_1/setup_integration_services.py
deleted file mode 100644
index 9f4c8a3915..0000000000
--- a/frappe/patches/v7_1/setup_integration_services.py
+++ /dev/null
@@ -1,118 +0,0 @@
-
-import frappe
-from frappe.exceptions import DataError
-from frappe.utils.password import get_decrypted_password
-from frappe.utils import cstr
-import os
-
-app_list = [
- {"app_name": "razorpay_integration", "service_name": "Razorpay", "doctype": "Razorpay Settings", "remove": True},
- {"app_name": "paypal_integration", "service_name": "PayPal", "doctype": "PayPal Settings", "remove": True},
- {"app_name": "frappe", "service_name": "Dropbox", "doctype": "Dropbox Backup", "remove": False}
-]
-
-def execute():
- installed_apps = frappe.get_installed_apps()
-
- for app_details in app_list:
- if app_details["app_name"] in installed_apps:
- settings = get_app_settings(app_details)
- if app_details["remove"]:
- uninstall_app(app_details["app_name"])
-
- try:
- setup_integration_service(app_details, settings)
- except DataError:
- pass
-
- frappe.delete_doc("DocType", "Dropbox Backup")
-
-def setup_integration_service(app_details, settings=None):
- if not settings:
- return
-
- setup_service_settings(app_details["service_name"], settings)
-
- doc_path = frappe.get_app_path("frappe", "integration_broker", "doctype",
- "integration_service", "integration_service.json")
-
- if not os.path.exists(doc_path):
- return
-
- frappe.reload_doc("integration_broker", "doctype", "integration_service")
-
- if frappe.db.exists("Integration Service", app_details["service_name"]):
- integration_service = frappe.get_doc("Integration Service", app_details["service_name"])
- else:
- integration_service = frappe.new_doc("Integration Service")
- integration_service.service = app_details["service_name"]
-
- integration_service.enabled = 1
- integration_service.flags.ignore_mandatory = True
- integration_service.save(ignore_permissions=True)
-
-def get_app_settings(app_details):
- parameters = {}
- doctype = docname = app_details["doctype"]
-
- app_settings = get_parameters(app_details)
- if app_settings:
- settings = app_settings["settings"]
- frappe.reload_doc("integrations", "doctype", "{0}_settings".format(app_details["service_name"].lower()))
- controller = frappe.get_meta("{0} Settings".format(app_details["service_name"]))
-
- for d in controller.fields:
- if settings.get(d.fieldname):
- if ''.join(set(cstr(settings.get(d.fieldname)))) == '*':
- setattr(settings, d.fieldname, get_decrypted_password(doctype, docname, d.fieldname, raise_exception=True))
-
- parameters.update({d.fieldname : settings.get(d.fieldname)})
-
- return parameters
-
-def uninstall_app(app_name):
- from frappe.installer import remove_from_installed_apps
- remove_from_installed_apps(app_name)
-
-def get_parameters(app_details):
- if app_details["service_name"] == "Razorpay":
- return {"settings": frappe.get_doc(app_details["doctype"])}
-
- elif app_details["service_name"] == "PayPal":
- if frappe.conf.paypal_username and frappe.conf.paypal_password and frappe.conf.paypal_signature:
- return {
- "settings": {
- "api_username": frappe.conf.paypal_username,
- "api_password": frappe.conf.paypal_password,
- "signature": frappe.conf.paypal_signature
- }
- }
- else:
- return {"settings": frappe.get_doc(app_details["doctype"])}
-
- elif app_details["service_name"] == "Dropbox":
- doc = frappe.db.get_value(app_details["doctype"], None,
- ["dropbox_access_key", "dropbox_access_secret", "upload_backups_to_dropbox"], as_dict=1)
-
- if not doc:
- return
-
- if not (frappe.conf.dropbox_access_key and frappe.conf.dropbox_secret_key):
- return
-
- return {
- "settings": {
- "app_access_key": frappe.conf.dropbox_access_key,
- "app_secret_key": frappe.conf.dropbox_secret_key,
- "dropbox_access_key": doc.dropbox_access_key,
- "dropbox_access_secret": doc.dropbox_access_secret,
- "backup_frequency": doc.upload_backups_to_dropbox,
- "enabled": doc.send_backups_to_dropbox
- }
- }
-
-def setup_service_settings(service_name, settings):
- service_doc = frappe.get_doc("{0} Settings".format(service_name))
- service_doc.update(settings)
- service_doc.flags.ignore_mandatory = True
- service_doc.save(ignore_permissions=True)
\ No newline at end of file
diff --git a/frappe/patches/v7_1/sync_language_doctype.py b/frappe/patches/v7_1/sync_language_doctype.py
deleted file mode 100644
index a5e9ad1cb1..0000000000
--- a/frappe/patches/v7_1/sync_language_doctype.py
+++ /dev/null
@@ -1,22 +0,0 @@
-
-import frappe
-from frappe.translate import get_lang_dict
-
-def execute():
- frappe.reload_doc('core', 'doctype', 'language')
-
- from frappe.core.doctype.language.language import sync_languages
- sync_languages()
-
- # move language from old style to new style for old accounts
- # i.e. from "english" to "en"
-
- lang_dict = get_lang_dict()
- language = frappe.db.get_value('System Settings', None, 'language')
- if language:
- frappe.db.set_value('System Settings', None, 'language', lang_dict.get('language') or 'en')
-
- for user in frappe.get_all('User', fields=['name', 'language']):
- if user.language:
- frappe.db.set_value('User', user.name, 'language',
- lang_dict.get('language') or 'en', update_modified=False)
diff --git a/frappe/patches/v7_2/__init__.py b/frappe/patches/v7_2/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v7_2/fix_email_queue_recipient.py b/frappe/patches/v7_2/fix_email_queue_recipient.py
deleted file mode 100644
index 021397031b..0000000000
--- a/frappe/patches/v7_2/fix_email_queue_recipient.py
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doc('email', 'doctype', 'email_queue_recipient')
- frappe.db.sql('update `tabEmail Queue Recipient` set parenttype="recipients"')
\ No newline at end of file
diff --git a/frappe/patches/v7_2/merge_knowledge_base.py b/frappe/patches/v7_2/merge_knowledge_base.py
deleted file mode 100644
index 04e6c16213..0000000000
--- a/frappe/patches/v7_2/merge_knowledge_base.py
+++ /dev/null
@@ -1,24 +0,0 @@
-
-import frappe
-
-from frappe.patches.v7_0.re_route import update_routes
-from frappe.installer import remove_from_installed_apps
-
-def execute():
- if 'knowledge_base' in frappe.get_installed_apps():
- frappe.reload_doc('website', 'doctype', 'help_category')
- frappe.reload_doc('website', 'doctype', 'help_article')
- update_routes(['Help Category', 'Help Article'])
- remove_from_installed_apps('knowledge_base')
-
- # remove module def
- if frappe.db.exists('Module Def', 'Knowledge Base'):
- frappe.delete_doc('Module Def', 'Knowledge Base')
-
- # set missing routes
- for doctype in ('Help Category', 'Help Article'):
- for d in frappe.get_all(doctype, fields=['name', 'route']):
- if not d.route:
- doc = frappe.get_doc(doctype, d.name)
- doc.set_route()
- doc.db_update()
\ No newline at end of file
diff --git a/frappe/patches/v7_2/remove_in_filter.py b/frappe/patches/v7_2/remove_in_filter.py
deleted file mode 100644
index 306879f996..0000000000
--- a/frappe/patches/v7_2/remove_in_filter.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-import frappe
-
-def execute():
- if frappe.db.has_column('DocField', 'in_filter'):
- frappe.db.sql('alter table tabDocField drop column in_filter')
- frappe.clear_cache(doctype="DocField")
\ No newline at end of file
diff --git a/frappe/patches/v7_2/set_doctype_engine.py b/frappe/patches/v7_2/set_doctype_engine.py
deleted file mode 100644
index e0df9cff87..0000000000
--- a/frappe/patches/v7_2/set_doctype_engine.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-import frappe
-
-def execute():
- for t in frappe.db.sql('show table status'):
- if t[0].startswith('tab'):
- frappe.db.sql('update tabDocType set engine=%s where name=%s', (t[1], t[0][3:]))
\ No newline at end of file
diff --git a/frappe/patches/v7_2/set_in_standard_filter_property.py b/frappe/patches/v7_2/set_in_standard_filter_property.py
deleted file mode 100644
index 568f43d2aa..0000000000
--- a/frappe/patches/v7_2/set_in_standard_filter_property.py
+++ /dev/null
@@ -1,20 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doc('custom', 'doctype', 'custom_field', force=True)
-
- try:
- frappe.db.sql('update `tabCustom Field` set in_standard_filter = in_filter_dash')
- except Exception as e:
- if not frappe.db.is_missing_column(e): raise e
-
- for doctype in frappe.get_all("DocType", {"istable": 0, "issingle": 0, "custom": 0}):
- try:
- frappe.reload_doctype(doctype.name, force=True)
- except KeyError:
- pass
- except frappe.db.DataError:
- pass
- except Exception:
- pass
diff --git a/frappe/patches/v7_2/setup_custom_perms.py b/frappe/patches/v7_2/setup_custom_perms.py
deleted file mode 100644
index 1f46072782..0000000000
--- a/frappe/patches/v7_2/setup_custom_perms.py
+++ /dev/null
@@ -1,13 +0,0 @@
-
-import frappe
-from frappe.permissions import setup_custom_perms
-from frappe.core.page.permission_manager.permission_manager import get_standard_permissions
-from frappe.utils.reset_doc import setup_perms_for
-
-'''
-Copy DocPerm to Custom DocPerm where permissions are set differently
-'''
-
-def execute():
- for d in frappe.db.get_all('DocType', dict(istable=0, issingle=0, custom=0)):
- setup_perms_for(d.name)
diff --git a/frappe/patches/v7_2/setup_ldap_config.py b/frappe/patches/v7_2/setup_ldap_config.py
deleted file mode 100644
index c9ad3e6714..0000000000
--- a/frappe/patches/v7_2/setup_ldap_config.py
+++ /dev/null
@@ -1,22 +0,0 @@
-
-import frappe
-from frappe.utils import cint
-
-def execute():
- frappe.reload_doc("integrations", "doctype", "ldap_settings")
-
- if not frappe.db.exists("DocType", "Integration Service"):
- return
-
- if not frappe.db.exists("Integration Service", "LDAP"):
- return
-
- if not cint(frappe.db.get_value("Integration Service", "LDAP", 'enabled')):
- return
-
- import ldap
- try:
- ldap_settings = frappe.get_doc("LDAP Settings")
- ldap_settings.save(ignore_permissions=True)
- except ldap.LDAPError:
- pass
diff --git a/frappe/patches/v7_2/update_communications.py b/frappe/patches/v7_2/update_communications.py
deleted file mode 100644
index 114e531324..0000000000
--- a/frappe/patches/v7_2/update_communications.py
+++ /dev/null
@@ -1,10 +0,0 @@
-
-import frappe
-
-def execute():
- """
- in communication move feedback details to content
- remove Guest None from sender full name
- setup feedback request trigger's is_manual field
- """
- return
diff --git a/frappe/patches/v7_2/update_feedback_request.py b/frappe/patches/v7_2/update_feedback_request.py
deleted file mode 100644
index 9bc656bf67..0000000000
--- a/frappe/patches/v7_2/update_feedback_request.py
+++ /dev/null
@@ -1,10 +0,0 @@
-
-import frappe
-
-def execute():
- """
- rename feedback request documents,
- update the feedback request and save the rating and communication
- reference in Feedback Request document
- """
- return
diff --git a/frappe/patches/v8_0/__init__.py b/frappe/patches/v8_0/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v8_0/deprecate_integration_broker.py b/frappe/patches/v8_0/deprecate_integration_broker.py
deleted file mode 100644
index 9aeee17837..0000000000
--- a/frappe/patches/v8_0/deprecate_integration_broker.py
+++ /dev/null
@@ -1,51 +0,0 @@
-
-import frappe
-from frappe.integrations.utils import create_payment_gateway
-
-def execute():
- setup_enabled_integrations()
-
- for doctype in ["integration_request", "oauth_authorization_code", "oauth_bearer_token", "oauth_client"]:
- frappe.reload_doc('integrations', 'doctype', doctype)
-
- frappe.reload_doc("core", "doctype", "payment_gateway")
- update_doctype_module()
- create_payment_gateway_master_records()
-
- for doctype in ["Integration Service", "Integration Service Parameter"]:
- frappe.delete_doc("DocType", doctype)
-
- if not frappe.db.get_value("DocType", {"module": "Integration Broker"}, "name"):
- frappe.delete_doc("Module Def", "Integration Broker")
-
-def setup_enabled_integrations():
- if not frappe.db.exists("DocType", "Integration Service"):
- return
-
- for service in frappe.get_all("Integration Service",
- filters={"enabled": 1, "service": ('in', ("Dropbox", "LDAP"))}, fields=["name"]):
-
- doctype = "{0} Settings".format(service.name)
- frappe.db.set_value(doctype, doctype, 'enabled', 1)
-
-def update_doctype_module():
- frappe.db.sql("""update tabDocType set module='Integrations'
- where name in ('Integration Request', 'Oauth Authorization Code',
- 'Oauth Bearer Token', 'Oauth Client') """)
-
- frappe.db.sql(""" update tabDocType set module='Core' where name = 'Payment Gateway'""")
-
-def create_payment_gateway_master_records():
- for payment_gateway in ["Razorpay", "PayPal"]:
- doctype = "{0} Settings".format(payment_gateway)
- doc = frappe.get_doc(doctype)
- doc_meta = frappe.get_meta(doctype)
- all_mandatory_fields_has_value = True
-
- for d in doc_meta.fields:
- if d.reqd and not doc.get(d.fieldname):
- all_mandatory_fields_has_value = False
- break
-
- if all_mandatory_fields_has_value:
- create_payment_gateway(payment_gateway)
diff --git a/frappe/patches/v8_0/drop_in_dialog.py b/frappe/patches/v8_0/drop_in_dialog.py
deleted file mode 100644
index 5022333d22..0000000000
--- a/frappe/patches/v8_0/drop_in_dialog.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-import frappe
-
-def execute():
- if frappe.db.has_column('DocType', 'in_dialog'):
- frappe.db.sql('alter table tabDocType drop column in_dialog')
- frappe.clear_cache(doctype="DocType")
\ No newline at end of file
diff --git a/frappe/patches/v8_0/drop_is_custom_from_docperm.py b/frappe/patches/v8_0/drop_is_custom_from_docperm.py
deleted file mode 100644
index 0f17bbef5c..0000000000
--- a/frappe/patches/v8_0/drop_is_custom_from_docperm.py
+++ /dev/null
@@ -1,8 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doctype('DocPerm')
- if frappe.db.has_column('DocPerm', 'is_custom'):
- frappe.db.commit()
- frappe.db.sql('alter table `tabDocPerm` drop column is_custom')
\ No newline at end of file
diff --git a/frappe/patches/v8_0/drop_unwanted_indexes.py b/frappe/patches/v8_0/drop_unwanted_indexes.py
deleted file mode 100644
index 655bce1a4b..0000000000
--- a/frappe/patches/v8_0/drop_unwanted_indexes.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-# -*- coding: utf-8 -*-
-
-import frappe
-
-def execute():
- # communication
- unwanted_indexes = ["communication_date_index", "message_id_index", "modified_index",
- "creation_index", "reference_owner", "communication_date"]
-
- for k in unwanted_indexes:
- try:
- frappe.db.sql("drop index {0} on `tabCommunication`".format(k))
- except:
- pass
\ No newline at end of file
diff --git a/frappe/patches/v8_0/install_new_build_system_requirements.py b/frappe/patches/v8_0/install_new_build_system_requirements.py
deleted file mode 100644
index 75ccfa87cd..0000000000
--- a/frappe/patches/v8_0/install_new_build_system_requirements.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from subprocess import Popen, call, PIPE
-
-def execute():
- # update nodejs version if brew exists
- p = Popen(['which', 'brew'], stdout=PIPE, stderr=PIPE)
- output, err = p.communicate()
- if output:
- call(['brew', 'upgrade', 'node'])
- else:
- print('Please update your NodeJS version')
-
- call([
- 'npm', 'install',
- 'babel-core',
- 'less',
- 'chokidar',
- 'babel-preset-es2015',
- 'babel-preset-es2016',
- 'babel-preset-es2017',
- 'babel-preset-babili'
- ])
\ No newline at end of file
diff --git a/frappe/patches/v8_0/newsletter_childtable_migrate.py b/frappe/patches/v8_0/newsletter_childtable_migrate.py
deleted file mode 100644
index 67ff5e586f..0000000000
--- a/frappe/patches/v8_0/newsletter_childtable_migrate.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-def execute():
- frappe.reload_doc('email', 'doctype', 'newsletter_email_group')
- frappe.reload_doctype('Newsletter')
-
- if "email_group" not in frappe.db.get_table_columns("Newsletter"):
- return
-
- newsletters = frappe.get_all("Newsletter", fields=["name", "email_group"])
- for newsletter in newsletters:
- if newsletter.email_group:
- newsletter_doc = frappe.get_doc("Newsletter", newsletter.name)
- if not newsletter_doc.get("email_group"):
- newsletter_doc.append("email_group", {
- "email_group": newsletter.email_group,
- })
- newsletter_doc.flags.ignore_validate = True
- newsletter_doc.flags.ignore_mandatory = True
- newsletter_doc.save()
diff --git a/frappe/patches/v8_0/rename_listsettings_to_usersettings.py b/frappe/patches/v8_0/rename_listsettings_to_usersettings.py
deleted file mode 100644
index 9545953e34..0000000000
--- a/frappe/patches/v8_0/rename_listsettings_to_usersettings.py
+++ /dev/null
@@ -1,45 +0,0 @@
-
-from frappe.model.utils.user_settings import update_user_settings
-import frappe, json
-
-
-def execute():
- if frappe.db.table_exists("__ListSettings"):
- for us in frappe.db.sql('''select user, doctype, data from __ListSettings''', as_dict=True):
- try:
- data = json.loads(us.data)
- except:
- continue
-
- if 'List' in data:
- continue
-
- if 'limit' in data:
- data['page_length'] = data['limit']
- del data['limit']
-
- new_data = dict(List=data)
- new_data = json.dumps(new_data)
-
- frappe.db.sql('''update __ListSettings
- set data=%(new_data)s
- where user=%(user)s
- and doctype=%(doctype)s''',
- {'new_data': new_data, 'user': us.user, 'doctype': us.doctype})
-
- frappe.db.sql("RENAME TABLE __ListSettings to __UserSettings")
- else:
- if not frappe.db.table_exists("__UserSettings"):
- frappe.db.create_user_settings_table()
-
- for user in frappe.db.get_all('User', {'user_type': 'System User'}):
- defaults = frappe.defaults.get_defaults_for(user.name)
- for key, value in defaults.items():
- if key.startswith('_list_settings:'):
- doctype = key.replace('_list_settings:', '')
- columns = ['`tab{1}`.`{0}`'.format(*c) for c in json.loads(value)]
- for col in columns:
- if "name as" in col:
- columns.remove(col)
-
- update_user_settings(doctype, {'fields': columns})
\ No newline at end of file
diff --git a/frappe/patches/v8_0/rename_page_role_to_has_role.py b/frappe/patches/v8_0/rename_page_role_to_has_role.py
deleted file mode 100644
index 49006ea419..0000000000
--- a/frappe/patches/v8_0/rename_page_role_to_has_role.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-def execute():
- if not frappe.db.exists('DocType', 'Has Role'):
- frappe.rename_doc('DocType', 'Page Role', 'Has Role')
- reload_doc()
- set_ref_doctype_roles_to_report()
- copy_user_roles_to_has_roles()
- remove_doctypes()
-
-def reload_doc():
- frappe.reload_doc("core", 'doctype', "page")
- frappe.reload_doc("core", 'doctype', "report")
- frappe.reload_doc("core", 'doctype', "user")
- frappe.reload_doc("core", 'doctype', "has_role")
-
-def set_ref_doctype_roles_to_report():
- for data in frappe.get_all('Report', fields=["name"]):
- doc = frappe.get_doc('Report', data.name)
- if frappe.db.exists("DocType", doc.ref_doctype):
- try:
- doc.set_doctype_roles()
- for row in doc.roles:
- row.db_update()
- except:
- pass
-
-def copy_user_roles_to_has_roles():
- if frappe.db.exists('DocType', 'UserRole'):
- for data in frappe.get_all('User', fields = ["name"]):
- doc = frappe.get_doc('User', data.name)
- doc.set('roles',[])
- for args in frappe.get_all('UserRole', fields = ["role"],
- filters = {'parent': data.name, 'parenttype': 'User'}):
- doc.append('roles', {
- 'role': args.role
- })
- for role in doc.roles:
- role.db_update()
-
-def remove_doctypes():
- for doctype in ['UserRole', 'Event Role']:
- if frappe.db.exists('DocType', doctype):
- frappe.delete_doc('DocType', doctype)
\ No newline at end of file
diff --git a/frappe/patches/v8_0/rename_print_to_printing.py b/frappe/patches/v8_0/rename_print_to_printing.py
deleted file mode 100644
index 56889d630e..0000000000
--- a/frappe/patches/v8_0/rename_print_to_printing.py
+++ /dev/null
@@ -1,13 +0,0 @@
-
-import frappe
-
-def execute():
- if frappe.db.exists('Module Def', 'Print'):
- frappe.reload_doc('printing', 'doctype', 'print_format')
- frappe.reload_doc('printing', 'doctype', 'print_settings')
- frappe.reload_doc('printing', 'doctype', 'print_heading')
- frappe.reload_doc('printing', 'doctype', 'letter_head')
- frappe.reload_doc('printing', 'page', 'print_format_builder')
- frappe.db.sql("""update `tabPrint Format` set module='Printing' where module='Print'""")
-
- frappe.delete_doc('Module Def', 'Print')
\ No newline at end of file
diff --git a/frappe/patches/v8_0/set_allow_traceback.py b/frappe/patches/v8_0/set_allow_traceback.py
deleted file mode 100644
index bb72e7dde6..0000000000
--- a/frappe/patches/v8_0/set_allow_traceback.py
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doc('core', 'doctype', 'system_settings')
- frappe.db.sql("update `tabSystem Settings` set allow_error_traceback=1")
diff --git a/frappe/patches/v8_0/set_currency_field_precision.py b/frappe/patches/v8_0/set_currency_field_precision.py
deleted file mode 100644
index 57b12ffdee..0000000000
--- a/frappe/patches/v8_0/set_currency_field_precision.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-from frappe.utils import get_number_format_info
-
-def execute():
- frappe.reload_doc('core', 'doctype', 'system_settings', force=True)
- if not frappe.db.get_value("System Settings", None, "currency_precision"):
- default_currency = frappe.db.get_default("currency")
- number_format = frappe.db.get_value("Currency", default_currency, "number_format", cache=True) \
- or frappe.db.get_default("number_format")
- if number_format:
- precision = get_number_format_info(number_format)[2]
- else:
- precision = 2
-
- ss = frappe.get_doc("System Settings")
- ss.currency_precision = precision
- ss.flags.ignore_mandatory = True
- ss.save()
diff --git a/frappe/patches/v8_0/set_doctype_values_in_custom_role.py b/frappe/patches/v8_0/set_doctype_values_in_custom_role.py
deleted file mode 100644
index 50e7eb83e1..0000000000
--- a/frappe/patches/v8_0/set_doctype_values_in_custom_role.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-def execute():
- frappe.reload_doctype('Custom Role')
-
- # set ref doctype in custom role for reports
- frappe.db.sql(""" update `tabCustom Role` set
- `tabCustom Role`.ref_doctype = (select ref_doctype from `tabReport` where name = `tabCustom Role`.report)
- where `tabCustom Role`.report is not null""")
diff --git a/frappe/patches/v8_0/set_user_permission_for_page_and_report.py b/frappe/patches/v8_0/set_user_permission_for_page_and_report.py
deleted file mode 100644
index 55789a8301..0000000000
--- a/frappe/patches/v8_0/set_user_permission_for_page_and_report.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-def execute():
- if not frappe.db.exists('DocType', 'Custom Role'):
- frappe.reload_doc("core", 'doctype', "custom_role")
- set_user_permission_for_page_and_report()
-
- update_ref_doctype_in_custom_role()
-
-def update_ref_doctype_in_custom_role():
- frappe.reload_doc("core", 'doctype', "custom_role")
- frappe.db.sql("""update `tabCustom Role`
- set
- ref_doctype = (select ref_doctype from tabReport where name = `tabCustom Role`.report)
- where report is not null""")
-
-def set_user_permission_for_page_and_report():
- make_custom_roles_for_page_and_report()
-
-def make_custom_roles_for_page_and_report():
- for doctype in ['Page', 'Report']:
- for data in get_data(doctype):
- doc = frappe.get_doc(doctype, data.name)
- roles = get_roles(doctype, data, doc)
- make_custom_roles(doctype, doc.name, roles)
-
-def get_data(doctype):
- fields = ["name"] if doctype == 'Page' else ["name", "ref_doctype"]
- return frappe.get_all(doctype, fields = fields)
-
-def get_roles(doctype, data, doc):
- roles = []
- if doctype == 'Page':
- for d in doc.roles:
- if frappe.db.exists('Role', d.role):
- roles.append({'role': d.role})
- else:
- out = frappe.get_all('Custom DocPerm', fields='distinct role', filters=dict(parent = data.ref_doctype))
- for d in out:
- roles.append({'role': d.role})
- return roles
-
-def make_custom_roles(doctype, name, roles):
- field = doctype.lower()
-
- if roles:
- custom_permission = frappe.get_doc({
- 'doctype': 'Custom Role',
- field : name,
- 'roles' : roles
- }).insert()
diff --git a/frappe/patches/v8_0/setup_email_inbox.py b/frappe/patches/v8_0/setup_email_inbox.py
deleted file mode 100644
index ad99068eb9..0000000000
--- a/frappe/patches/v8_0/setup_email_inbox.py
+++ /dev/null
@@ -1,26 +0,0 @@
-
-import frappe, json
-from frappe.core.doctype.user.user import ask_pass_update, setup_user_email_inbox
-
-def execute():
- """
- depricate email inbox page if exists
- remove desktop icon for email inbox page if exists
- patch to remove Custom DocPerm for communication
- add user inbox child table entry for existing email account in not exists
- """
-
- if frappe.db.exists("Page", "email_inbox"):
- frappe.delete_doc("Page", "email_inbox")
-
- frappe.db.sql("""update `tabCustom DocPerm` set `write`=0, email=1 where parent='Communication'""")
-
- frappe.reload_doc("core", "doctype", "user_email")
- frappe.reload_doc("email", "doctype", "email_account")
-
- email_accounts = frappe.get_all("Email Account", filters={"enable_incoming": 1},
- fields=["name", "email_id", "awaiting_password", "enable_outgoing"])
-
- for email_account in email_accounts:
- setup_user_email_inbox(email_account.get("name"), email_account.get("awaiting_password"),
- email_account.get("email_id"), email_account.get("enabled_outgoing"))
diff --git a/frappe/patches/v8_0/update_gender_and_salutation.py b/frappe/patches/v8_0/update_gender_and_salutation.py
deleted file mode 100644
index 913e0f714b..0000000000
--- a/frappe/patches/v8_0/update_gender_and_salutation.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-
-import frappe
-from frappe.desk.page.setup_wizard.install_fixtures import update_genders, update_salutations
-
-def execute():
- frappe.db.set_value("DocType", "Contact", "module", "Contacts")
- frappe.db.set_value("DocType", "Address", "module", "Contacts")
- frappe.db.set_value("DocType", "Address Template", "module", "Contacts")
- frappe.reload_doc('contacts', 'doctype', 'gender')
- frappe.reload_doc('contacts', 'doctype', 'salutation')
-
- update_genders()
- update_salutations()
\ No newline at end of file
diff --git a/frappe/patches/v8_0/update_global_search_table.py b/frappe/patches/v8_0/update_global_search_table.py
deleted file mode 100644
index 4d5c8be9cf..0000000000
--- a/frappe/patches/v8_0/update_global_search_table.py
+++ /dev/null
@@ -1,13 +0,0 @@
-
-import frappe
-
-def execute():
- if not 'published' in frappe.db.get_db_table_columns('__global_search'):
- frappe.db.sql('''alter table __global_search
- add column `title` varchar(140)''')
-
- frappe.db.sql('''alter table __global_search
- add column `route` varchar(140)''')
-
- frappe.db.sql('''alter table __global_search
- add column `published` int(1) not null default 0''')
diff --git a/frappe/patches/v8_0/update_published_in_global_search.py b/frappe/patches/v8_0/update_published_in_global_search.py
deleted file mode 100644
index ae86cb8b24..0000000000
--- a/frappe/patches/v8_0/update_published_in_global_search.py
+++ /dev/null
@@ -1,12 +0,0 @@
-
-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():
- try:
- rebuild_for_doctype(doctype)
- except frappe.DoesNotExistError:
- pass
diff --git a/frappe/patches/v8_0/update_records_in_global_search.py b/frappe/patches/v8_0/update_records_in_global_search.py
deleted file mode 100644
index 316f84b2f0..0000000000
--- a/frappe/patches/v8_0/update_records_in_global_search.py
+++ /dev/null
@@ -1,12 +0,0 @@
-
-import frappe
-from frappe.utils.global_search import get_doctypes_with_global_search, rebuild_for_doctype
-from frappe.utils import update_progress_bar
-
-def execute():
- frappe.cache().delete_value('doctypes_with_global_search')
- doctypes_with_global_search = get_doctypes_with_global_search(with_child_tables=False)
-
- for i, doctype in enumerate(doctypes_with_global_search):
- update_progress_bar("Updating Global Search", i, len(doctypes_with_global_search))
- rebuild_for_doctype(doctype)
diff --git a/frappe/patches/v8_1/__init__.py b/frappe/patches/v8_1/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py b/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py
deleted file mode 100644
index 510018eb47..0000000000
--- a/frappe/patches/v8_1/delete_custom_docperm_if_doctype_not_exists.py
+++ /dev/null
@@ -1,7 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.db.sql("""delete from `tabCustom DocPerm`
- where parent not in ( select name from `tabDocType` )
- """)
diff --git a/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py b/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py
deleted file mode 100644
index 513bb274bc..0000000000
--- a/frappe/patches/v8_1/enable_allow_error_traceback_in_system_settings.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-def execute():
- """ enable the allow_enable_traceback property in system settings """
-
- frappe.reload_doc("core", "doctype", "system_settings")
- doc = frappe.get_doc("System Settings", "System Settings")
- doc.allow_error_traceback = 1
- doc.flags.ignore_permissions=True
- doc.flags.ignore_mandatory=True
- doc.save()
\ No newline at end of file
diff --git a/frappe/patches/v8_1/update_format_options_in_auto_email_report.py b/frappe/patches/v8_1/update_format_options_in_auto_email_report.py
deleted file mode 100644
index 8bea2b7bf5..0000000000
--- a/frappe/patches/v8_1/update_format_options_in_auto_email_report.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-def execute():
- """ change the XLS option as XLSX in the auto email report """
-
- frappe.reload_doc("email", "doctype", "auto_email_report")
-
- auto_email_list = frappe.get_all("Auto Email Report", filters={"format": "XLS"})
- for auto_email in auto_email_list:
- frappe.db.set_value("Auto Email Report", auto_email.name, "format", "XLSX")
diff --git a/frappe/patches/v8_10/__init__.py b/frappe/patches/v8_10/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v8_10/delete_static_web_page_from_global_search.py b/frappe/patches/v8_10/delete_static_web_page_from_global_search.py
deleted file mode 100644
index aa6a053412..0000000000
--- a/frappe/patches/v8_10/delete_static_web_page_from_global_search.py
+++ /dev/null
@@ -1,5 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.db.sql("""delete from `__global_search` where doctype='Static Web Page'""");
\ No newline at end of file
diff --git a/frappe/patches/v8_5/__init__.py b/frappe/patches/v8_5/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py b/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py
deleted file mode 100644
index 5851e2855b..0000000000
--- a/frappe/patches/v8_5/delete_email_group_member_with_invalid_emails.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-from frappe.utils import validate_email_address
-
-def execute():
- ''' update/delete the email group member with the wrong email '''
-
- email_group_members = frappe.get_all("Email Group Member", fields=["name", "email"])
- for member in email_group_members:
- validated_email = validate_email_address(member.email)
- if (validated_email==member.email):
- pass
- else:
- try:
- frappe.db.set_value("Email Group Member", member.name, "email", validated_email)
- except Exception:
- frappe.delete_doc(doctype="Email Group Member", name=member.name, force=1, ignore_permissions=True)
\ No newline at end of file
diff --git a/frappe/patches/v8_5/patch_event_colors.py b/frappe/patches/v8_5/patch_event_colors.py
deleted file mode 100644
index 3c34f7946b..0000000000
--- a/frappe/patches/v8_5/patch_event_colors.py
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import frappe
-
-def execute():
-
- if not frappe.db.sql("SHOW COLUMNS FROM `tabEvent` LIKE 'color';"):
- return
-
- colors = ['red', 'green', 'blue', 'yellow', 'skyblue', 'orange']
- hex_colors = ['#ffc4c4', '#cef6d1', '#d2d2ff', '#fffacd', '#d2f1ff', '#ffd2c2']
-
- def get_hex_for_color(color):
- index = colors.index(color)
- return hex_colors[index]
-
- query = '''
- update tabEvent
- set color='{hex}'
- where color='{color}'
- '''
-
- for color in colors:
- frappe.db.sql(query.format(color=color, hex=get_hex_for_color(color)))
-
- frappe.db.commit()
diff --git a/frappe/patches/v8_7/__init__.py b/frappe/patches/v8_7/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v8_x/__init__.py b/frappe/patches/v8_x/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/patches/v8_x/update_user_permission.py b/frappe/patches/v8_x/update_user_permission.py
deleted file mode 100644
index 387751500f..0000000000
--- a/frappe/patches/v8_x/update_user_permission.py
+++ /dev/null
@@ -1,28 +0,0 @@
-
-import frappe
-
-def execute():
- frappe.reload_doc('core', 'doctype', 'user_permission')
- frappe.delete_doc('core', 'page', 'user-permissions')
- for perm in frappe.db.sql("""
- select
- name, parent, defkey, defvalue
- from
- tabDefaultValue
- where
- parent not in ('__default', '__global')
- and
- substr(defkey,1,1)!='_'
- and
- parenttype='User Permission'
- """, as_dict=True):
- if frappe.db.exists(perm.defkey, perm.defvalue) and frappe.db.exists('User', perm.parent):
- frappe.get_doc(dict(
- doctype='User Permission',
- user=perm.parent,
- allow=perm.defkey,
- for_value=perm.defvalue,
- apply_for_all_roles=0,
- )).insert(ignore_permissions = True)
-
- frappe.db.sql('delete from tabDefaultValue where parenttype="User Permission"')
diff --git a/frappe/patches/v9_1/__init__.py b/frappe/patches/v9_1/__init__.py
deleted file mode 100644
index 8b13789179..0000000000
--- a/frappe/patches/v9_1/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py b/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py
deleted file mode 100644
index f63e86a340..0000000000
--- a/frappe/patches/v9_1/add_sms_sender_name_as_parameters.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-import frappe
-
-def execute():
- frappe.reload_doc("core", "doctype", "sms_parameter")
- sms_sender_name = frappe.db.get_single_value("SMS Settings", "sms_sender_name")
- if sms_sender_name:
- frappe.reload_doc("core", "doctype", "sms_settings")
- sms_settings = frappe.get_doc("SMS Settings")
- sms_settings.append("parameters", {
- "parameter": "sender_name",
- "value": sms_sender_name
- })
- sms_settings.flags.ignore_mandatory = True
- sms_settings.flags.ignore_permissions = True
- sms_settings.save()
diff --git a/frappe/patches/v9_1/move_feed_to_activity_log.py b/frappe/patches/v9_1/move_feed_to_activity_log.py
deleted file mode 100644
index a549296357..0000000000
--- a/frappe/patches/v9_1/move_feed_to_activity_log.py
+++ /dev/null
@@ -1,24 +0,0 @@
-
-import frappe
-from frappe.utils.background_jobs import enqueue
-
-def execute():
- comm_records_count = frappe.db.count("Communication", {"comment_type": "Updated"})
- if comm_records_count > 100000:
- enqueue(method=move_data_from_communication_to_activity_log, queue='short', now=True)
- else:
- move_data_from_communication_to_activity_log()
-
-def move_data_from_communication_to_activity_log():
- frappe.reload_doc("core", "doctype", "communication")
- frappe.reload_doc("core", "doctype", "activity_log")
-
- frappe.db.sql("""insert into `tabActivity Log` (name, owner, modified, creation, status, communication_date,
- reference_doctype, reference_name, timeline_doctype, timeline_name, link_doctype, link_name, subject, content, user)
- select name, owner, modified, creation, status, communication_date,
- reference_doctype, reference_name, timeline_doctype, timeline_name, link_doctype, link_name, subject, content, user
- from `tabCommunication`
- where comment_type = 'Updated'""")
-
- frappe.db.sql("""delete from `tabCommunication` where comment_type = 'Updated'""")
- frappe.delete_doc("DocType", "Authentication Log")
\ No newline at end of file
diff --git a/frappe/patches/v9_1/resave_domain_settings.py b/frappe/patches/v9_1/resave_domain_settings.py
deleted file mode 100644
index 5814871c2e..0000000000
--- a/frappe/patches/v9_1/resave_domain_settings.py
+++ /dev/null
@@ -1,13 +0,0 @@
-
-import frappe
-
-def execute():
- domain_settings = frappe.get_doc('Domain Settings')
- active_domains = [d.domain for d in domain_settings.active_domains]
- try:
- for d in ('Education', 'Healthcare', 'Hospitality'):
- if d in active_domains and frappe.db.exists('Domain', d):
- domain = frappe.get_doc('Domain', d)
- domain.setup_domain()
- except frappe.LinkValidationError:
- pass
diff --git a/frappe/patches/v9_1/revert_domain_settings.py b/frappe/patches/v9_1/revert_domain_settings.py
deleted file mode 100644
index 99c5561d78..0000000000
--- a/frappe/patches/v9_1/revert_domain_settings.py
+++ /dev/null
@@ -1,11 +0,0 @@
-
-import frappe
-
-def execute():
- domain_settings = frappe.get_doc('Domain Settings')
- active_domains = [d.domain for d in domain_settings.active_domains]
-
- for domain_name in ('Education', 'Healthcare', 'Hospitality'):
- if frappe.db.exists('Domain', domain_name) and domain_name not in active_domains:
- domain = frappe.get_doc('Domain', domain_name)
- domain.remove_domain()
\ No newline at end of file
diff --git a/frappe/permissions.py b/frappe/permissions.py
index c25a7c3947..07b4a2e68f 100644
--- a/frappe/permissions.py
+++ b/frappe/permissions.py
@@ -308,7 +308,7 @@ def has_controller_permissions(doc, ptype, user=None):
return None
def get_doctypes_with_read():
- return list(set([p.parent if type(p.parent) == str else p.parent.encode('UTF8') for p in get_valid_perms()]))
+ return list({p.parent if type(p.parent) == str else p.parent.encode('UTF8') for p in get_valid_perms()})
def get_valid_perms(doctype=None, user=None):
'''Get valid permissions for the current user from DocPerm and Custom DocPerm'''
diff --git a/frappe/printing/doctype/print_settings/print_settings.json b/frappe/printing/doctype/print_settings/print_settings.json
index d64cb4c6d3..31962be050 100644
--- a/frappe/printing/doctype/print_settings/print_settings.json
+++ b/frappe/printing/doctype/print_settings/print_settings.json
@@ -148,7 +148,7 @@
"label": "Print Style"
},
{
- "default": "Modern",
+ "default": "Redesign",
"fieldname": "print_style",
"fieldtype": "Link",
"in_list_view": 1,
@@ -183,7 +183,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2020-10-22 23:42:09.471022",
+ "modified": "2021-02-15 14:16:18.474254",
"modified_by": "Administrator",
"module": "Printing",
"name": "Print Settings",
diff --git a/frappe/public/js/frappe/form/controls/text_editor.js b/frappe/public/js/frappe/form/controls/text_editor.js
index 059b0f76f8..99e87c5f21 100644
--- a/frappe/public/js/frappe/form/controls/text_editor.js
+++ b/frappe/public/js/frappe/form/controls/text_editor.js
@@ -1,7 +1,10 @@
import Quill from 'quill';
import ImageResize from 'quill-image-resize';
+import MagicUrl from 'quill-magic-url';
+
Quill.register('modules/imageResize', ImageResize);
+Quill.register('modules/magicUrl', MagicUrl);
const CodeBlockContainer = Quill.import('formats/code-block-container');
CodeBlockContainer.tagName = 'PRE';
Quill.register(CodeBlockContainer, true);
@@ -148,7 +151,8 @@ frappe.ui.form.ControlTextEditor = class ControlTextEditor extends frappe.ui.for
modules: {
toolbar: this.get_toolbar_options(),
table: true,
- imageResize: {}
+ imageResize: {},
+ magicUrl: true
},
theme: 'snow'
};
diff --git a/frappe/public/js/frappe/form/dashboard.js b/frappe/public/js/frappe/form/dashboard.js
index c1c95d94cf..eb7a6edc5d 100644
--- a/frappe/public/js/frappe/form/dashboard.js
+++ b/frappe/public/js/frappe/form/dashboard.js
@@ -5,6 +5,7 @@ frappe.ui.form.Dashboard = class FormDashboard {
constructor(opts) {
$.extend(this, opts);
this.setup_dashboard_sections();
+ this.set_open_count = frappe.utils.throttle(this.set_open_count, 500);
}
setup_dashboard_sections() {
diff --git a/frappe/public/js/frappe/form/footer/form_timeline.js b/frappe/public/js/frappe/form/footer/form_timeline.js
index ab83ed2f71..115a62e098 100644
--- a/frappe/public/js/frappe/form/footer/form_timeline.js
+++ b/frappe/public/js/frappe/form/footer/form_timeline.js
@@ -190,6 +190,7 @@ class FormTimeline extends BaseTimeline {
}
doc.owner = doc.sender;
doc.user_full_name = doc.sender_full_name;
+ doc.content = frappe.dom.remove_script_and_style(doc.content);
let communication_content = $(frappe.render_template('timeline_message_box', { doc }));
if (allow_reply) {
this.setup_reply(communication_content, doc);
@@ -248,6 +249,7 @@ class FormTimeline extends BaseTimeline {
}
get_comment_timeline_content(doc) {
+ doc.content = frappe.dom.remove_script_and_style(doc.content);
const comment_content = $(frappe.render_template('timeline_message_box', { doc }));
this.setup_comment_actions(comment_content, doc);
return comment_content;
diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js
index 35ebf9274d..a24c6ab0d6 100644
--- a/frappe/public/js/frappe/form/form.js
+++ b/frappe/public/js/frappe/form/form.js
@@ -1607,7 +1607,9 @@ frappe.ui.form.Form = class FrappeForm {
}
show_tour(on_finish) {
- if (!Array.isArray(frappe.tour[this.doctype])) {
+ const tour_info = frappe.tour[this.doctype];
+
+ if (!Array.isArray(tour_info)) {
return;
}
@@ -1619,23 +1621,29 @@ frappe.ui.form.Form = class FrappeForm {
keyboardControl: true,
nextBtnText: 'Next',
prevBtnText: 'Previous',
- opacity: 0.25,
- onNext: () => {
- if (!driver.hasNextStep()) {
- on_finish && on_finish();
- }
- }
+ opacity: 0.25
});
this.layout.sections.forEach(section => section.collapse(false));
- let steps = frappe.tour[this.doctype].map(step => {
+ let steps = tour_info.map(step => {
let field = this.get_docfield(step.fieldname);
return {
element: `.frappe-control[data-fieldname='${step.fieldname}']`,
popover: {
title: step.title || field.label,
- description: step.description
+ description: step.description,
+ position: step.position || 'bottom'
+ },
+ onNext: () => {
+ const next_condition_satisfied = this.layout.evaluate_depends_on_value(step.next_step_condition || true);
+ if (!next_condition_satisfied) {
+ driver.preventMove();
+ }
+
+ if (!driver.hasNextStep()) {
+ on_finish && on_finish();
+ }
}
};
});
diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js
index 89c34ed80c..b9a838688d 100644
--- a/frappe/public/js/frappe/form/formatters.js
+++ b/frappe/public/js/frappe/form/formatters.js
@@ -221,9 +221,13 @@ frappe.form.formatters = {
Tag: function(value) {
var html = "";
$.each((value || "").split(","), function(i, v) {
- if(v) html+= ''+v +'';
+ if (v) html += `
+
+ ${v}
+ `;
});
return html;
},
@@ -310,6 +314,7 @@ frappe.form.get_formatter = function(fieldtype) {
frappe.format = function(value, df, options, doc) {
if(!df) df = {"fieldtype":"Data"};
+ if (df.fieldname == '_user_tags') df.fieldtype = 'Tag';
var fieldtype = df.fieldtype || "Data";
// format Dynamic Link as a Link
diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js
index a77791d0a2..ebc3fa19f5 100644
--- a/frappe/public/js/frappe/form/grid.js
+++ b/frappe/public/js/frappe/form/grid.js
@@ -210,9 +210,9 @@ export default class Grid {
delete_all_rows() {
frappe.confirm(__("Are you sure you want to delete all rows?"), () => {
- this.frm.doc[this.df.fieldname] = [];
- $(this.parent).find('.rows').empty();
- this.grid_rows = [];
+ this.grid_rows.forEach(row => {
+ row.remove();
+ });
this.frm.script_manager.trigger(this.df.fieldname + "_delete", this.doctype);
this.wrapper.find('.grid-heading-row .grid-row-check:checked:first').prop('checked', 0);
@@ -236,6 +236,10 @@ export default class Grid {
}
refresh_remove_rows_button() {
+ if (this.df.cannot_delete_rows) {
+ return;
+ }
+
this.remove_rows_button.toggleClass('hidden',
this.wrapper.find('.grid-body .grid-row-check:checked:first').length ? false : true);
this.remove_all_rows_button.toggleClass('hidden',
diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js
index 33db00dede..8c51314066 100644
--- a/frappe/public/js/frappe/form/grid_row.js
+++ b/frappe/public/js/frappe/form/grid_row.js
@@ -569,6 +569,9 @@ export default class GridRow {
.find('.grid-insert-row-below, .grid-insert-row, .grid-duplicate-row, .grid-append-row')
.toggle(!cannot_add_rows);
+ this.wrapper.find('.grid-delete-row')
+ .toggle(!(this.grid.df && this.grid.df.cannot_delete_rows));
+
frappe.dom.freeze("", "dark");
if (cur_frm) cur_frm.cur_grid = this;
this.wrapper.addClass("grid-row-open");
diff --git a/frappe/public/js/frappe/ui/filters/filter_list.js b/frappe/public/js/frappe/ui/filters/filter_list.js
index 611ab024bf..72312d7f13 100644
--- a/frappe/public/js/frappe/ui/filters/filter_list.js
+++ b/frappe/public/js/frappe/ui/filters/filter_list.js
@@ -323,9 +323,12 @@ frappe.ui.FilterGroup = class {
}
add_filters_to_filter_group(filters) {
- filters.forEach((filter) => {
- this.add_filter(filter[0], filter[1], filter[2], filter[3]);
- });
+ if (filters.length) {
+ this.toggle_empty_filters(false);
+ filters.forEach((filter) => {
+ this.add_filter(filter[0], filter[1], filter[2], filter[3]);
+ });
+ }
}
add(filters, refresh = true) {
diff --git a/frappe/public/js/frappe/ui/group_by/group_by.js b/frappe/public/js/frappe/ui/group_by/group_by.js
index 3ebf9c9d3d..692d675c62 100644
--- a/frappe/public/js/frappe/ui/group_by/group_by.js
+++ b/frappe/public/js/frappe/ui/group_by/group_by.js
@@ -381,10 +381,11 @@ frappe.ui.GroupBy = class {
this.group_by_fields = {};
this.all_fields = {};
- let fields = this.report_view.meta.fields.filter((f) =>
+ const fields = this.report_view.meta.fields.filter((f) =>
['Select', 'Link', 'Data', 'Int', 'Check'].includes(f.fieldtype)
);
- this.group_by_fields[this.doctype] = fields;
+ const tag_field = {fieldname: '_user_tags', fieldtype: 'Data', label: __('Tags')};
+ this.group_by_fields[this.doctype] = fields.concat(tag_field);
this.all_fields[this.doctype] = this.report_view.meta.fields;
const standard_fields_filter = (df) =>
diff --git a/frappe/public/js/frappe/ui/messages.js b/frappe/public/js/frappe/ui/messages.js
index 2e8ba7d206..067fed233c 100644
--- a/frappe/public/js/frappe/ui/messages.js
+++ b/frappe/public/js/frappe/ui/messages.js
@@ -26,13 +26,13 @@ frappe.throw = function(msg) {
frappe.confirm = function(message, confirm_action, reject_action) {
var d = new frappe.ui.Dialog({
- title: __("Confirm"),
- primary_action_label: __("Yes"),
+ title: __("Confirm", null, "Title of confirmation dialog"),
+ primary_action_label: __("Yes", null, "Approve confirmation dialog"),
primary_action: () => {
confirm_action && confirm_action();
d.hide();
},
- secondary_action_label: __("No"),
+ secondary_action_label: __("No", null, "Dismiss confirmation dialog"),
secondary_action: () => d.hide(),
});
@@ -88,9 +88,9 @@ frappe.prompt = function(fields, callback, title, primary_label) {
if(!$.isArray(fields)) fields = [fields];
var d = new frappe.ui.Dialog({
fields: fields,
- title: title || __("Enter Value"),
+ title: title || __("Enter Value", null, "Title of prompt dialog"),
});
- d.set_primary_action(primary_label || __("Submit"), function() {
+ d.set_primary_action(primary_label || __("Submit", null, "Primary action of prompt dialog"), function() {
var values = d.get_values();
if(!values) {
return;
diff --git a/frappe/public/js/frappe/ui/page.js b/frappe/public/js/frappe/ui/page.js
index e740718ef9..22fdf476b8 100644
--- a/frappe/public/js/frappe/ui/page.js
+++ b/frappe/public/js/frappe/ui/page.js
@@ -377,11 +377,12 @@ frappe.ui.Page = class Page {
});
}
- add_actions_menu_item(label, click, standard) {
+ add_actions_menu_item(label, click, standard, shortcut) {
return this.add_dropdown_item({
label,
click,
standard,
+ shortcut,
parent: this.actions,
show_parent: false
});
@@ -409,6 +410,9 @@ frappe.ui.Page = class Page {
parent.parent().removeClass("hide");
}
+ let $link = this.is_in_group_button_dropdown(parent, 'li > a.grey-link', label);
+ if ($link) return $link;
+
let $li;
let $icon = ``;
@@ -440,9 +444,8 @@ frappe.ui.Page = class Page {
`);
}
- var $link = $li.find("a").on("click", click);
- if (this.is_in_group_button_dropdown(parent, 'li > a.grey-link', label)) return;
+ $link = $li.find("a").on("click", click);
if (standard) {
$li.appendTo(parent);
@@ -508,7 +511,7 @@ frappe.ui.Page = class Page {
let item = $(this).html();
return $(item).attr('data-label') === label;
});
- return result.length > 0;
+ return result.length > 0 && result;
}
clear_btn_group(parent) {
diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js
index b29b6b87e6..6a324f6034 100644
--- a/frappe/public/js/frappe/views/reports/report_view.js
+++ b/frappe/public/js/frappe/views/reports/report_view.js
@@ -410,7 +410,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
x_fields.push({
label: col.content,
fieldname: col.id,
- value: col.id,
+ value: col.id,
});
// numeric values in y
@@ -1024,8 +1024,12 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
return docfield.fieldtype === 'Date' ? 'right' : 'left';
})();
+ let id = fieldname;
+
// child table column
- const id = doctype !== this.doctype ? `${doctype}:${fieldname}` : fieldname;
+ if (doctype !== this.doctype && fieldname !== '_aggregate_column') {
+ id = `${doctype}:${fieldname}`;
+ }
let width = (docfield ? cint(docfield.width) : null) || null;
if (this.report_doc) {
diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js
index eefb78c29a..3f5a4acd73 100644
--- a/frappe/public/js/frappe/widgets/widget_dialog.js
+++ b/frappe/public/js/frappe/widgets/widget_dialog.js
@@ -271,18 +271,19 @@ class ShortcutDialog extends WidgetDialog {
}
process_data(data) {
- let stats_filter = {};
if (this.dialog.get_value("type") == "DocType" && this.filter_group) {
let filters = this.filter_group.get_filters();
+ let stats_filter = null;
if (filters.length) {
+ stats_filter = {};
filters.forEach((arr) => {
stats_filter[arr[1]] = [arr[2], arr[3]];
});
-
- data.stats_filter = JSON.stringify(stats_filter);
+ stats_filter = JSON.stringify(stats_filter);
}
+ data.stats_filter = stats_filter;
}
data.label = data.label
diff --git a/frappe/public/scss/common/controls.scss b/frappe/public/scss/common/controls.scss
index 83fc4461d6..c939c6de39 100644
--- a/frappe/public/scss/common/controls.scss
+++ b/frappe/public/scss/common/controls.scss
@@ -241,6 +241,7 @@ textarea.form-control {
// rating
.rating {
+ cursor: pointer;
--star-fill: var(--gray-300);
.star-hover {
--star-fill: var(--yellow-100);
@@ -248,6 +249,24 @@ textarea.form-control {
.star-click {
--star-fill: var(--yellow-300);
}
+
+ .rating-box {
+ background-color: var(--gray-300);
+ border-radius: 5px;
+ font-size: 14px;
+ text-align: center;
+ padding: 2px;
+ cursor: pointer;
+ width: 25px;
+ height: 25px;
+ margin: 4px 2px;
+ }
+ .rating-hover {
+ background-color: var(--yellow-100);
+ }
+ .rating-click {
+ background-color: var(--yellow-300);
+ }
}
.frappe-control .control-value {
diff --git a/frappe/public/scss/common/grid.scss b/frappe/public/scss/common/grid.scss
index aac949b1bf..57d0583b35 100644
--- a/frappe/public/scss/common/grid.scss
+++ b/frappe/public/scss/common/grid.scss
@@ -53,7 +53,12 @@
display: none;
}
+.form-grid .grid-heading-row .template-row {
+ margin-left: 20px;
+}
+
.form-grid .template-row {
+ width: calc(100% - 30px);
padding: 8px 15px;
}
diff --git a/frappe/public/scss/common/quill.scss b/frappe/public/scss/common/quill.scss
index 6f6e09dc70..12706d6b7f 100644
--- a/frappe/public/scss/common/quill.scss
+++ b/frappe/public/scss/common/quill.scss
@@ -176,6 +176,7 @@
}
.ql-editor.read-mode {
+ height: unset;
padding: 0;
.mention {
--user-mention-bg-color: var(--control-bg);
diff --git a/frappe/public/scss/website/blog.scss b/frappe/public/scss/website/blog.scss
index 9918b490c5..ea82efed21 100644
--- a/frappe/public/scss/website/blog.scss
+++ b/frappe/public/scss/website/blog.scss
@@ -14,6 +14,10 @@
position: relative;
width: 100%;
+ .card {
+ border: 1px solid var(--border-color)
+ }
+
.card-body {
display: flex;
flex-direction: column;
diff --git a/frappe/public/scss/website/markdown.scss b/frappe/public/scss/website/markdown.scss
index c5f44d20d8..6f009df393 100644
--- a/frappe/public/scss/website/markdown.scss
+++ b/frappe/public/scss/website/markdown.scss
@@ -48,7 +48,6 @@ $font-sizes-mobile: (
}
li {
- text-indent: 0.25rem;
padding-top: 1px;
padding-bottom: 1px;
}
diff --git a/frappe/search/website_search.py b/frappe/search/website_search.py
index 452ea2a427..49bdade936 100644
--- a/frappe/search/website_search.py
+++ b/frappe/search/website_search.py
@@ -9,7 +9,7 @@ from whoosh.fields import ID, TEXT, Schema
import frappe
from frappe.search.full_text_search import FullTextSearch
from frappe.utils import set_request, update_progress_bar
-from frappe.website.render import render_page
+from frappe.website.serve import get_response_content
INDEX_NAME = "web_routes"
@@ -61,7 +61,7 @@ class WebsiteSearch(FullTextSearch):
try:
set_request(method="GET", path=route)
- content = render_page(route)
+ content = get_response_content(route)
soup = BeautifulSoup(content, "html.parser")
page_content = soup.find(class_="page_content")
text_content = page_content.text if page_content else ""
diff --git a/frappe/sessions.py b/frappe/sessions.py
index 1bc78448e7..4d922d6769 100644
--- a/frappe/sessions.py
+++ b/frappe/sessions.py
@@ -167,7 +167,8 @@ def get_csrf_token():
def generate_csrf_token():
frappe.local.session.data.csrf_token = frappe.generate_hash()
- frappe.local.session_obj.update(force=True)
+ if not frappe.flags.in_test:
+ frappe.local.session_obj.update(force=True)
class Session:
def __init__(self, user, resume=False, full_name=None, user_type=None):
diff --git a/frappe/templates/includes/comments/comments.html b/frappe/templates/includes/comments/comments.html
index c490bedd72..935fa5367e 100644
--- a/frappe/templates/includes/comments/comments.html
+++ b/frappe/templates/includes/comments/comments.html
@@ -49,8 +49,10 @@
{% endif %}
\ No newline at end of file
diff --git a/frappe/templates/includes/feedback/feedback.py b/frappe/templates/includes/feedback/feedback.py
new file mode 100644
index 0000000000..1830a3e09e
--- /dev/null
+++ b/frappe/templates/includes/feedback/feedback.py
@@ -0,0 +1,63 @@
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+from __future__ import unicode_literals
+
+import frappe
+
+from frappe import _
+
+@frappe.whitelist(allow_guest=True)
+def add_feedback(reference_doctype, reference_name, rating, feedback, feedback_email):
+ doc = frappe.get_doc(reference_doctype, reference_name)
+ if doc.disable_feedback == 1:
+ return
+
+ doc = frappe.new_doc('Feedback')
+ doc.reference_doctype = reference_doctype
+ doc.reference_name = reference_name
+ doc.rating = rating
+ doc.feedback = feedback
+ doc.email = feedback_email
+ doc.save(ignore_permissions=True)
+
+ subject = _('New Feedback on {0}: {1}').format(reference_doctype, reference_name)
+ send_mail(doc, subject)
+ return doc
+
+@frappe.whitelist()
+def update_feedback(reference_doctype, reference_name, rating, feedback, feedback_email):
+ doc = frappe.get_doc(reference_doctype, reference_name)
+ if doc.disable_feedback == 1:
+ return
+
+ filters = {
+ "email": feedback_email,
+ "reference_doctype": reference_doctype,
+ "reference_name": reference_name
+ }
+ d = frappe.get_all('Feedback', filters=filters, limit=1)
+ doc = frappe.get_doc('Feedback', d[0].name)
+ doc.rating = rating
+ doc.feedback = feedback
+ doc.save(ignore_permissions=True)
+
+ subject = _('Feedback updated on {0}: {1}').format(reference_doctype, reference_name)
+ send_mail(doc, subject)
+ return doc
+
+def send_mail(feedback, subject):
+ doc = frappe.get_doc(feedback.reference_doctype, feedback.reference_name)
+
+ message = ("This is for testing
{% block content %}{% endblock %}
+ {%- block script %}
+ {% if colocated_js -%}
+
+ {%- endif %}
+ {%- endblock %}
diff --git a/frappe/test_runner.py b/frappe/test_runner.py
index 1f99e55fb8..0c30fbbd00 100644
--- a/frappe/test_runner.py
+++ b/frappe/test_runner.py
@@ -175,6 +175,7 @@ def run_tests_for_module(module, verbose=False, tests=(), profile=False, junit_x
for doctype in module.test_dependencies:
make_test_records(doctype, verbose=verbose)
+ frappe.db.commit()
return _run_unittest(module, verbose=verbose, tests=tests, profile=profile, junit_xml_output=junit_xml_output)
def _run_unittest(modules, verbose=False, tests=(), profile=False, junit_xml_output=False):
diff --git a/frappe/tests/test_api.py b/frappe/tests/test_api.py
index 7e77aab779..29939fea1c 100644
--- a/frappe/tests/test_api.py
+++ b/frappe/tests/test_api.py
@@ -39,6 +39,11 @@ class TestResourceAPI(unittest.TestCase):
for name in self.GENERATED_DOCUMENTS:
frappe.delete_doc_if_exists(self.DOCTYPE, name)
+ def setUp(self):
+ # commit to ensure consistency in session (postgres CI randomly fails)
+ if frappe.conf.db_type == "postgres":
+ frappe.db.commit()
+
@property
def sid(self):
if not getattr(self, "_sid", None):
diff --git a/frappe/tests/test_commands.py b/frappe/tests/test_commands.py
index b6cd0b575c..07bdf8791e 100644
--- a/frappe/tests/test_commands.py
+++ b/frappe/tests/test_commands.py
@@ -80,7 +80,7 @@ def exists_in_backup(doctypes, file):
)
with gzip.open(file, "rb") as f:
content = f.read().decode("utf8")
- return all([predicate.format(doctype).lower() in content.lower() for doctype in doctypes])
+ return all(predicate.format(doctype).lower() in content.lower() for doctype in doctypes)
class BaseTestCommands(unittest.TestCase):
@@ -355,12 +355,12 @@ class TestCommands(BaseTestCommands):
# test 2: bare functionality for single site
self.execute("bench --site {site} list-apps")
self.assertEqual(self.returncode, 0)
- list_apps = set([
+ list_apps = set(
_x.split()[0] for _x in self.stdout.split("\n")
- ])
+ )
doctype = frappe.get_single("Installed Applications").installed_applications
if doctype:
- installed_apps = set([x.app_name for x in doctype])
+ installed_apps = set(x.app_name for x in doctype)
else:
installed_apps = set(frappe.get_installed_apps())
self.assertSetEqual(list_apps, installed_apps)
diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py
index 42ebd05b67..89975b46d6 100644
--- a/frappe/tests/test_db_query.py
+++ b/frappe/tests/test_db_query.py
@@ -21,6 +21,18 @@ class TestReportview(unittest.TestCase):
def test_basic(self):
self.assertTrue({"name":"DocType"} in DatabaseQuery("DocType").execute(limit_page_length=None))
+ def test_extract_tables(self):
+ db_query = DatabaseQuery("DocType")
+ add_custom_field("DocType", 'test_tab_field', 'Data')
+
+ db_query.fields = ["tabNote.creation", "test_tab_field", "tabDocType.test_tab_field"]
+ db_query.extract_tables()
+ self.assertIn("`tabNote`", db_query.tables)
+ self.assertIn("`tabDocType`", db_query.tables)
+ self.assertNotIn("test_tab_field", db_query.tables)
+
+ clear_custom_fields("DocType")
+
def test_build_match_conditions(self):
clear_user_permissions_for_doctype('Blog Post', 'test2@example.com')
diff --git a/frappe/tests/test_recorder.py b/frappe/tests/test_recorder.py
index 08dbde0144..d9386ca25b 100644
--- a/frappe/tests/test_recorder.py
+++ b/frappe/tests/test_recorder.py
@@ -7,7 +7,7 @@ import unittest
import frappe
import frappe.recorder
from frappe.utils import set_request
-from frappe.website.render import render_page
+from frappe.website.serve import get_response_content
import sqlparse
@@ -121,5 +121,5 @@ class TestRecorder(unittest.TestCase):
self.assertEqual(call['exact_copies'], query[1])
def test_error_page_rendering(self):
- content = render_page("error")
+ content = get_response_content("error")
self.assertIn("Error", content)
diff --git a/frappe/tests/test_website.py b/frappe/tests/test_website.py
index 52ddc5ef71..6f265d9b94 100644
--- a/frappe/tests/test_website.py
+++ b/frappe/tests/test_website.py
@@ -1,38 +1,41 @@
import unittest
import frappe
-from frappe.website import render
-from frappe.website.utils import get_home_page
from frappe.utils import set_request
+from frappe.website.serve import get_response, get_response_content
+from frappe.website.utils import (build_response, clear_website_cache, get_home_page)
class TestWebsite(unittest.TestCase):
+ def setUp(self):
+ frappe.set_user('Guest')
- def test_home_page_for_role(self):
- frappe.delete_doc_if_exists('User', 'test-user-for-home-page@example.com')
- frappe.delete_doc_if_exists('Role', 'home-page-test')
- frappe.delete_doc_if_exists('Web Page', 'home-page-test')
+ def tearDown(self):
+ frappe.set_user('Administrator')
+
+ def test_home_page(self):
+ frappe.set_user('Administrator')
+ # test home page via role
user = frappe.get_doc(dict(
doctype='User',
email='test-user-for-home-page@example.com',
- first_name='test')).insert()
+ first_name='test')).insert(ignore_if_duplicate=True)
role = frappe.get_doc(dict(
doctype = 'Role',
role_name = 'home-page-test',
desk_access = 0,
- home_page = '/home-page-test'
- )).insert()
+ )).insert(ignore_if_duplicate=True)
user.add_roles(role.name)
user.save()
+ frappe.db.set_value('Role', 'home-page-test', 'home_page', 'home-page-test')
frappe.set_user('test-user-for-home-page@example.com')
self.assertEqual(get_home_page(), 'home-page-test')
frappe.set_user('Administrator')
- role.home_page = ''
- role.save()
+ frappe.db.set_value('Role', 'home-page-test', 'home_page', '')
# home page via portal settings
frappe.db.set_value('Portal Settings', None, 'default_portal_home', 'test-portal-home')
@@ -41,10 +44,45 @@ class TestWebsite(unittest.TestCase):
frappe.cache().hdel('home_page', frappe.session.user)
self.assertEqual(get_home_page(), 'test-portal-home')
- def test_page_load(self):
+ frappe.db.set_value("Portal Settings", None, "default_portal_home", '')
+ clear_website_cache()
+
+ # home page via website settings
+ frappe.db.set_value("Website Settings", None, "home_page", 'contact')
+ self.assertEqual(get_home_page(), 'contact')
+
+ frappe.db.set_value("Website Settings", None, "home_page", None)
+ clear_website_cache()
+
+ # fallback homepage
+ self.assertEqual(get_home_page(), 'me')
+
+ # fallback homepage for guest
frappe.set_user('Guest')
+ self.assertEqual(get_home_page(), 'login')
+ frappe.set_user('Administrator')
+
+ # test homepage via hooks
+ clear_website_cache()
+ set_home_page_hook('get_website_user_home_page', 'frappe.www._test._test_home_page.get_website_user_home_page')
+ self.assertEqual(get_home_page(), '_test/_test_folder')
+
+ clear_website_cache()
+ set_home_page_hook('website_user_home_page', 'login')
+ self.assertEqual(get_home_page(), 'login')
+
+ clear_website_cache()
+ set_home_page_hook('home_page', 'about')
+ self.assertEqual(get_home_page(), 'about')
+
+ clear_website_cache()
+ set_home_page_hook('role_home_page', {'home-page-test': 'home-page-test'})
+ self.assertEqual(get_home_page(), 'home-page-test')
+
+
+ def test_page_load(self):
set_request(method='POST', path='login')
- response = render.render()
+ response = get_response()
self.assertEqual(response.status_code, 200)
@@ -52,14 +90,52 @@ class TestWebsite(unittest.TestCase):
self.assertTrue('// login.js' in html)
self.assertTrue('' in html)
+
+ def test_static_page(self):
+ set_request(method='GET', path='/_test/static-file-test.png')
+ response = get_response()
+ self.assertEqual(response.status_code, 200)
+
+ def test_error_page(self):
+ set_request(method='GET', path='/_test/problematic_page')
+ response = get_response()
+ self.assertEqual(response.status_code, 500)
+
+ def test_login(self):
+ set_request(method='GET', path='/login')
+ response = get_response()
+ self.assertEqual(response.status_code, 200)
+
+ html = frappe.safe_decode(response.get_data())
+
+ self.assertTrue('// login.js' in html)
+ self.assertTrue('' in html)
+
+ def test_app(self):
frappe.set_user('Administrator')
+ set_request(method='GET', path='/app')
+ response = get_response()
+ self.assertEqual(response.status_code, 200)
+
+ html = frappe.safe_decode(response.get_data())
+ self.assertTrue('window.app = true;' in html)
+ frappe.local.session_obj = None
+
+ def test_not_found(self):
+ set_request(method='GET', path='/_test/missing')
+ response = get_response()
+ self.assertEqual(response.status_code, 404)
+
def test_redirect(self):
import frappe.hooks
+ frappe.set_user('Administrator')
+
frappe.hooks.website_redirects = [
dict(source=r'/testfrom', target=r'://testto1'),
dict(source=r'/testfromregex.*', target=r'://testto2'),
- dict(source=r'/testsub/(.*)', target=r'://testto3/\1')
+ dict(source=r'/testsub/(.*)', target=r'://testto3/\1'),
+ dict(source=r'/courses/course\?course=(.*)', target=r'/courses/\1', match_with_query_string=True),
]
website_settings = frappe.get_doc('Website Settings')
@@ -69,32 +145,82 @@ class TestWebsite(unittest.TestCase):
})
website_settings.save()
- frappe.cache().delete_key('app_hooks')
- frappe.cache().delete_key('website_redirects')
-
set_request(method='GET', path='/testfrom')
- response = render.render()
+ response = get_response()
self.assertEqual(response.status_code, 301)
self.assertEqual(response.headers.get('Location'), r'://testto1')
set_request(method='GET', path='/testfromregex/test')
- response = render.render()
+ response = get_response()
self.assertEqual(response.status_code, 301)
self.assertEqual(response.headers.get('Location'), r'://testto2')
set_request(method='GET', path='/testsub/me')
- response = render.render()
+ response = get_response()
self.assertEqual(response.status_code, 301)
self.assertEqual(response.headers.get('Location'), r'://testto3/me')
set_request(method='GET', path='/test404')
- response = render.render()
+ response = get_response()
self.assertEqual(response.status_code, 404)
set_request(method='GET', path='/testsource')
- response = render.render()
+ response = get_response()
self.assertEqual(response.status_code, 301)
self.assertEqual(response.headers.get('Location'), '/testtarget')
+ set_request(method='GET', path='/courses/course?course=data')
+ response = get_response()
+ self.assertEqual(response.status_code, 301)
+ self.assertEqual(response.headers.get('Location'), '/courses/data')
+
delattr(frappe.hooks, 'website_redirects')
frappe.cache().delete_key('app_hooks')
+
+ def test_custom_page_renderer(self):
+ import frappe.hooks
+ frappe.hooks.page_renderer = ['frappe.tests.test_website.CustomPageRenderer']
+ frappe.cache().delete_key('app_hooks')
+ set_request(method='GET', path='/custom')
+ response = get_response()
+ self.assertEqual(response.status_code, 3984)
+
+ set_request(method='GET', path='/new')
+ content = get_response_content()
+ self.assertIn("
" not in page_info.source): - page_info.source = '''{{% extends "{0}" %}} - {{% block page_content %}}{1}{{% endblock %}}'''.format(page_info.base_template, page_info.source) - - if "" in page_info.source: - page_info.no_breadcrumbs = 1 - - if "" in page_info.source: - page_info.show_sidebar = 1 - - if "" in page_info.source: - page_info.add_breadcrumbs = 1 - - if "" in page_info.source: - page_info.no_header = 1 - - if "" in page_info.source: - page_info.add_next_prev_links = 1 - - if "" in page_info.source: - page_info.no_cache = 1 - - if "" in page_info.source: - page_info.sitemap = 0 - - if "" in page_info.source: - page_info.sitemap = 1 - def load_properties_from_controller(page_info): if not page_info.controller: return @@ -432,8 +267,10 @@ def get_doctypes_with_web_view(): def _get(): installed_apps = frappe.get_installed_apps() doctypes = frappe.get_hooks("website_generators") - doctypes += [d.name for d in frappe.get_all('DocType', 'name, module', - dict(has_web_view=1)) if frappe.local.module_app[frappe.scrub(d.module)] in installed_apps] + doctypes_with_web_view = frappe.get_all('DocType', fields=['name', 'module'], + filters=dict(has_web_view=1)) + module_app_map = frappe.local.module_app + doctypes += [d.name for d in doctypes_with_web_view if module_app_map[frappe.scrub(d.module)] in installed_apps] return doctypes return frappe.cache().get_value('doctypes_with_web_view', _get) diff --git a/frappe/website/serve.py b/frappe/website/serve.py new file mode 100644 index 0000000000..fe7fc77064 --- /dev/null +++ b/frappe/website/serve.py @@ -0,0 +1,29 @@ +import frappe +from frappe.website.page_renderers.error_page import ErrorPage +from frappe.website.page_renderers.not_permitted_page import NotPermittedPage +from frappe.website.page_renderers.redirect_page import RedirectPage +from frappe.website.path_resolver import PathResolver + + +def get_response(path=None, http_status_code=200): + """Resolves path and renders page""" + response = None + path = path or frappe.local.request.path + endpoint = path + + try: + path_resolver = PathResolver(path) + endpoint, renderer_instance = path_resolver.resolve() + response = renderer_instance.render() + except frappe.Redirect: + return RedirectPage(endpoint or path, http_status_code).render() + except frappe.PermissionError as e: + response = NotPermittedPage(endpoint, http_status_code, exception=e).render() + except Exception as e: + response = ErrorPage(exception=e).render() + + return response + +def get_response_content(path=None, http_status_code=200): + response = get_response(path, http_status_code) + return str(response.data, 'utf-8') diff --git a/frappe/website/utils.py b/frappe/website/utils.py index aa98595e2d..0f5f182ea2 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -1,9 +1,18 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -import re +import json +import mimetypes import os -import frappe +import re +from functools import wraps +import yaml +from six import iteritems +from werkzeug.wrappers import Response + +import frappe +from frappe import _ +from frappe.model.document import Document from frappe.utils import md_to_html @@ -128,14 +137,8 @@ def get_home_page_via_hooks(): return home_page -def is_signup_enabled(): - if getattr(frappe.local, "is_signup_enabled", None) is None: - frappe.local.is_signup_enabled = True - if frappe.utils.cint(frappe.db.get_value("Website Settings", - "Website Settings", "disable_signup")): - frappe.local.is_signup_enabled = False - - return frappe.local.is_signup_enabled +def is_signup_disabled(): + return frappe.db.get_single_value('Website Settings', 'disable_signup', True) def cleanup_page_name(title): """make page name from title""" @@ -145,91 +148,15 @@ def cleanup_page_name(title): name = title.lower() name = re.sub(r'[~!@#$%^&*+()<>,."\'\?]', '', name) name = re.sub('[:/]', '-', name) - name = '-'.join(name.split()) - # replace repeating hyphens name = re.sub(r"(-)\1+", r"\1", name) - return name[:140] -def get_shade(color, percent): - color, color_format = detect_color_format(color) - r, g, b, a = color - - avg = (float(int(r) + int(g) + int(b)) / 3) - # switch dark and light shades - if avg > 128: - percent = -percent - - # stronger diff for darker shades - if percent < 25 and avg < 64: - percent = percent * 2 - - new_color = [] - for channel_value in (r, g, b): - new_color.append(get_shade_for_channel(channel_value, percent)) - - r, g, b = new_color - - return format_color(r, g, b, a, color_format) - - -def detect_color_format(color): - if color.startswith("rgba"): - color_format = "rgba" - color = [c.strip() for c in color[5:-1].split(",")] - - elif color.startswith("rgb"): - color_format = "rgb" - color = [c.strip() for c in color[4:-1].split(",")] + [1] - - else: - # assume hex - color_format = "hex" - - if color.startswith("#"): - color = color[1:] - - if len(color) == 3: - # hex in short form like #fff - color = "{0}{0}{1}{1}{2}{2}".format(*tuple(color)) - - color = [int(color[0:2], 16), int(color[2:4], 16), int(color[4:6], 16), 1] - - return color, color_format - - -def get_shade_for_channel(channel_value, percent): - v = int(channel_value) + int(int('ff', 16) * (float(percent)/100)) - if v < 0: - v=0 - if v > 255: - v=255 - - return v - - -def format_color(r, g, b, a, color_format): - if color_format == "rgba": - return "rgba({0}, {1}, {2}, {3})".format(r, g, b, a) - - elif color_format == "rgb": - return "rgb({0}, {1}, {2})".format(r, g, b) - - else: - # assume hex - return "#{0}{1}{2}".format(convert_to_hex(r), convert_to_hex(g), convert_to_hex(b)) - - -def convert_to_hex(channel_value): - h = hex(channel_value)[2:] - - if len(h) < 2: - h = "0" + h - - return h +def get_shade(color, percent=None): + frappe.msgprint(_('get_shade method has been deprecated.')) + return color def abs_url(path): """Deconstructs and Reconstructs a URL into an absolute URL or a URL relative from root '/'""" @@ -359,25 +286,6 @@ def extract_comment_tag(source, tag): return None -def add_missing_headers(): - '''Walk and add missing headers in docs (to be called from bench execute)''' - path = frappe.get_app_path('erpnext', 'docs') - for basepath, folders, files in os.walk(path): - for fname in files: - if fname.endswith('.md'): - with open(os.path.join(basepath, fname), 'r') as f: - content = frappe.as_unicode(f.read()) - - if not content.startswith('# ') and not '
Test content
+{next} +{% endblock %} diff --git a/frappe/www/_test/_test_folder/_test_page.js b/frappe/www/_test/_test_folder/_test_page.js new file mode 100644 index 0000000000..6e0c1f3a87 --- /dev/null +++ b/frappe/www/_test/_test_folder/_test_page.js @@ -0,0 +1 @@ +console.log('test data'); \ No newline at end of file diff --git a/frappe/www/_test/_test_folder/_test_page.py b/frappe/www/_test/_test_folder/_test_page.py new file mode 100644 index 0000000000..1813a06bac --- /dev/null +++ b/frappe/www/_test/_test_folder/_test_page.py @@ -0,0 +1,3 @@ +def get_context(context): + context.base_template_path = 'frappe/templates/test/_test_base.html' + context.add_breadcrumbs = 1 diff --git a/frappe/www/_test/_test_folder/_test_toc.md b/frappe/www/_test/_test_folder/_test_toc.md new file mode 100644 index 0000000000..02cc3c82be --- /dev/null +++ b/frappe/www/_test/_test_folder/_test_toc.md @@ -0,0 +1,19 @@ +--- +title: Test TOC +add_breadcrumbs: 1 +show_sidebar: 0 + +metatags: + description: Test Description. + keywords: Frappe Framework. +--- + +# Level 1 + +## Level 1.1 + +## Level 1.2 + +## Level 1.3 + +### Level 1.3.1 diff --git a/frappe/www/_test/_test_folder/index.md b/frappe/www/_test/_test_folder/index.md new file mode 100644 index 0000000000..1a5a9e7f81 --- /dev/null +++ b/frappe/www/_test/_test_folder/index.md @@ -0,0 +1,9 @@ +--- +title: Test TOC +add_breadcrumbs: 1 +show_sidebar: 1 +--- + +# Index + +{index} \ No newline at end of file diff --git a/frappe/patches/v5_2/__init__.py b/frappe/www/_test/_test_folder/new.csv/__init__.py similarity index 100% rename from frappe/patches/v5_2/__init__.py rename to frappe/www/_test/_test_folder/new.csv/__init__.py diff --git a/frappe/www/_test/_test_folder/new.csv/index.html b/frappe/www/_test/_test_folder/new.csv/index.html new file mode 100644 index 0000000000..7a1bb69558 --- /dev/null +++ b/frappe/www/_test/_test_folder/new.csv/index.html @@ -0,0 +1,12 @@ + + +
+ + + +
+ +
+