瀏覽代碼

[merge] 5.3.1

version-14
Rushabh Mehta 10 年之前
父節點
當前提交
a7bd640b24
共有 69 個文件被更改,包括 25083 次插入13026 次删除
  1. +40
    -0
      frappe/change_log/v5/v5_3_0.md
  2. +1
    -1
      frappe/commands.py
  3. +29
    -1
      frappe/core/doctype/communication/communication.py
  4. +2
    -2
      frappe/core/doctype/doctype/doctype.py
  5. +1
    -1
      frappe/core/page/permission_manager/permission_manager.js
  6. +10
    -2
      frappe/data/languages.txt
  7. +3
    -1
      frappe/desk/form/save.py
  8. +3
    -1
      frappe/desk/form/utils.py
  9. +18
    -5
      frappe/email/bulk.py
  10. +5
    -3
      frappe/email/doctype/email_account/email_account.py
  11. +7
    -2
      frappe/email/receive.py
  12. +1
    -0
      frappe/exceptions.py
  13. +29
    -18
      frappe/model/base_document.py
  14. +1
    -0
      frappe/patches.txt
  15. +0
    -0
      frappe/patches/v5_3/__init__.py
  16. +18
    -0
      frappe/patches/v5_3/rename_chinese_languages.py
  17. +6
    -4
      frappe/permissions.py
  18. +1
    -1
      frappe/public/js/frappe/form/formatters.js
  19. +16
    -1
      frappe/public/js/frappe/form/save.js
  20. +1
    -1
      frappe/public/js/frappe/ui/toolbar/about.js
  21. +1
    -1
      frappe/public/js/frappe/ui/toolbar/navbar.html
  22. +1
    -1
      frappe/public/js/frappe/ui/toolbar/offcanvas_left_sidebar.html
  23. +9
    -0
      frappe/share.py
  24. +33
    -1
      frappe/tasks.py
  25. +393
    -353
      frappe/translations/ar.csv
  26. +392
    -349
      frappe/translations/bg.csv
  27. +0
    -0
      frappe/translations/bo.csv
  28. +392
    -352
      frappe/translations/bs.csv
  29. +392
    -352
      frappe/translations/ca.csv
  30. +392
    -352
      frappe/translations/cs.csv
  31. +392
    -349
      frappe/translations/da.csv
  32. +397
    -357
      frappe/translations/de.csv
  33. +392
    -349
      frappe/translations/el.csv
  34. +392
    -352
      frappe/translations/es.csv
  35. +393
    -350
      frappe/translations/fa.csv
  36. +1509
    -0
      frappe/translations/fi.csv
  37. +394
    -354
      frappe/translations/fr.csv
  38. +379
    -339
      frappe/translations/he.csv
  39. +392
    -352
      frappe/translations/hi.csv
  40. +392
    -352
      frappe/translations/hr.csv
  41. +392
    -349
      frappe/translations/hu.csv
  42. +400
    -360
      frappe/translations/id.csv
  43. +392
    -352
      frappe/translations/it.csv
  44. +397
    -359
      frappe/translations/ja.csv
  45. +1356
    -0
      frappe/translations/km.csv
  46. +392
    -352
      frappe/translations/kn.csv
  47. +408
    -368
      frappe/translations/ko.csv
  48. +392
    -349
      frappe/translations/lv.csv
  49. +1509
    -0
      frappe/translations/mk.csv
  50. +392
    -349
      frappe/translations/mr.csv
  51. +1509
    -0
      frappe/translations/my.csv
  52. +393
    -353
      frappe/translations/nl.csv
  53. +1509
    -0
      frappe/translations/no.csv
  54. +642
    -602
      frappe/translations/pl.csv
  55. +392
    -352
      frappe/translations/pt-BR.csv
  56. +392
    -352
      frappe/translations/pt.csv
  57. +392
    -352
      frappe/translations/ro.csv
  58. +392
    -352
      frappe/translations/ru.csv
  59. +0
    -0
      frappe/translations/se.csv
  60. +392
    -352
      frappe/translations/sk.csv
  61. +1509
    -0
      frappe/translations/sq.csv
  62. +392
    -352
      frappe/translations/sr.csv
  63. +1509
    -0
      frappe/translations/sv.csv
  64. +392
    -352
      frappe/translations/ta.csv
  65. +392
    -352
      frappe/translations/th.csv
  66. +396
    -356
      frappe/translations/tr.csv
  67. +392
    -352
      frappe/translations/vi.csv
  68. +392
    -352
      frappe/translations/zh-cn.csv
  69. +437
    -398
      frappe/translations/zh-tw.csv

+ 40
- 0
frappe/change_log/v5/v5_3_0.md 查看文件

@@ -0,0 +1,40 @@
- Added Language Support for Following languages

<table class="table table-bordered">
<tr>
<td style="width: 30%">bo*</td>
<td>ལྷ་སའི་སྐད་</td>
</tr>
<tr>
<td>fi</td>
<td>suomalainen</td>
</tr>
<tr>
<td>km</td>
<td>ភាសាខ្មែរ</td>
</tr>
<tr>
<td>mk</td>
<td>македонски</td>
</tr>
<tr>
<td>my</td>
<td>Melayu</td>
</tr>
<tr>
<td>no</td>
<td>norsk</td>
</tr>
<tr>
<td>sv</td>
<td>Svenska</td>
</tr>
<tr>
<td>sq</td>
<td>shqiptar</td>
</tr>
</table>

* Unable to find translations for Tibetian via Google

- To contribute to translations, please login to [https://translate.erpnext.com](https://translate.erpnext.com)

+ 1
- 1
frappe/commands.py 查看文件

@@ -103,7 +103,7 @@ def _is_scheduler_enabled():
enable_scheduler = False enable_scheduler = False
try: try:
frappe.connect() frappe.connect()
enable_scheduler = cint(frappe.db.get_default("enable_scheduler"))
enable_scheduler = cint(frappe.db.get_single_value("System Settings", "enable_scheduler")) and True or False
except: except:
pass pass
finally: finally:


+ 29
- 1
frappe/core/doctype/communication/communication.py 查看文件

@@ -72,6 +72,26 @@ class Communication(Document):
["email_id", "always_use_account_email_id_as_sender"], as_dict=True) or frappe._dict() ["email_id", "always_use_account_email_id_as_sender"], as_dict=True) or frappe._dict()


def notify(self, print_html=None, print_format=None, attachments=None, recipients=None, except_recipient=False): def notify(self, print_html=None, print_format=None, attachments=None, recipients=None, except_recipient=False):
"""Calls a delayed celery task 'sendmail' that enqueus email in Bulk Email 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 except_recipient: True when pulling email, the notification shouldn't go to the main recipient

"""
if frappe.flags.in_test:
# for test cases, run synchronously
self._notify(print_html=print_html, print_format=print_format, attachments=attachments,
recipients=recipients, except_recipient=except_recipient)
else:
from frappe.tasks import sendmail
sendmail.delay(frappe.local.site, self.name,
print_html=print_html, print_format=print_format, attachments=attachments,
recipients=recipients, except_recipient=except_recipient)

def _notify(self, print_html=None, print_format=None, attachments=None, recipients=None, except_recipient=False):
self.prepare_to_notify(print_html, print_format, attachments) self.prepare_to_notify(print_html, print_format, attachments)
if not recipients: if not recipients:
recipients = self.get_recipients(except_recipient=except_recipient) recipients = self.get_recipients(except_recipient=except_recipient)
@@ -143,6 +163,7 @@ class Communication(Document):
sender = parseaddr(self.sender)[1] sender = parseaddr(self.sender)[1]


filtered = [] filtered = []
email_addresses = []
for e in list(set(recipients)): for e in list(set(recipients)):
if (e=="Administrator") or ((e==self.sender) and (e not in original_recipients)) or \ if (e=="Administrator") or ((e==self.sender) and (e not in original_recipients)) or \
(e in unsubscribed) or (e in email_accounts): (e in unsubscribed) or (e in email_accounts):
@@ -160,8 +181,11 @@ class Communication(Document):
# while pulling email, don't send email to current recipient # while pulling email, don't send email to current recipient
continue continue


if e not in filtered and email_id not in filtered:
# make sure of case-insensitive uniqueness of email address
if email_id.lower() not in email_addresses:
# append the full email i.e. "Human <human@example.com>"
filtered.append(e) filtered.append(e)
email_addresses.append(email_id.lower())


if getattr(self, "send_me_a_copy", False): if getattr(self, "send_me_a_copy", False):
filtered.append(self.sender) filtered.append(self.sender)
@@ -252,6 +276,10 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
}) })
comm.insert(ignore_permissions=True) comm.insert(ignore_permissions=True)


# needed for communication.notify which uses celery delay
# if not committed, delayed task doesn't find the communication
frappe.db.commit()

recipients = None recipients = None
if send_email: if send_email:
comm.send_me_a_copy = send_me_a_copy comm.send_me_a_copy = send_me_a_copy


+ 2
- 2
frappe/core/doctype/doctype/doctype.py 查看文件

@@ -262,8 +262,8 @@ def validate_fields(meta):
def check_dynamic_link_options(d): def check_dynamic_link_options(d):
if d.fieldtype=="Dynamic Link": if d.fieldtype=="Dynamic Link":
doctype_pointer = filter(lambda df: df.fieldname==d.options, fields) doctype_pointer = filter(lambda df: df.fieldname==d.options, fields)
if not doctype_pointer or (doctype_pointer[0].fieldtype!="Link") \
or (doctype_pointer[0].options!="DocType"):
if not doctype_pointer or (doctype_pointer[0].fieldtype not in ("Link", "Select")) \
or (doctype_pointer[0].fieldtype=="Link" and doctype_pointer[0].options!="DocType"):
frappe.throw(_("Options 'Dynamic Link' type of field must point to another Link Field with options as 'DocType'")) frappe.throw(_("Options 'Dynamic Link' type of field must point to another Link Field with options as 'DocType'"))


def check_illegal_default(d): def check_illegal_default(d):


+ 1
- 1
frappe/core/page/permission_manager/permission_manager.js 查看文件

@@ -277,7 +277,7 @@ frappe.PermissionEngine = Class.extend({
}, },


rights: ["read", "write", "create", "delete", "submit", "cancel", "amend", rights: ["read", "write", "create", "delete", "submit", "cancel", "amend",
"print", "email", "report", "import", "export", "set_user_permissions"],
"print", "email", "report", "import", "export", "set_user_permissions", "share"],


set_show_users: function(cell, role) { set_show_users: function(cell, role) {
cell.html("<a class='grey' href='#'>"+__(role)+"</a>") cell.html("<a class='grey' href='#'>"+__(role)+"</a>")


+ 10
- 2
frappe/data/languages.txt 查看文件

@@ -1,5 +1,6 @@
ar العربية ar العربية
bg bǎlgarski bg bǎlgarski
bo ལྷ་སའི་སྐད་
bs bosanski bs bosanski
ca català ca català
cs česky cs česky
@@ -9,6 +10,7 @@ el ελληνικά
es español es español
en english en english
fa پارسی fa پارسی
fi suomalainen
fr français fr français
he עברית he עברית
hi हिंदी hi हिंदी
@@ -17,21 +19,27 @@ hu magyar
id Indonesia id Indonesia
it italiano it italiano
ja 日本語 ja 日本語
km ភាសាខ្មែរ
kn ಕನ್ನಡ kn ಕನ್ನಡ
ko 한국의 ko 한국의
lv latviešu valoda lv latviešu valoda
mr मराठी mr मराठी
mk македонски
my Melayu
nl nederlands nl nederlands
no norsk
pl polski pl polski
pt português pt português
pt-BR português brasileiro pt-BR português brasileiro
ro român ro român
ru русский ru русский
sv svenska
sk slovenčina sk slovenčina
sq shqiptar
sr српски sr српски
ta தமிழ் ta தமிழ்
th ไทย th ไทย
tr Türk tr Türk
vi việt vi việt
zh-cn 中国(简体)
zh-tw 中國(繁體)
zh-cn 簡體中文
zh-tw 正體中文

+ 3
- 1
frappe/desk/form/save.py 查看文件

@@ -32,10 +32,12 @@ def savedocs():
raise raise


@frappe.whitelist() @frappe.whitelist()
def cancel(doctype=None, name=None):
def cancel(doctype=None, name=None, workflow_state_fieldname=None, workflow_state=None):
"""cancel a doclist""" """cancel a doclist"""
try: try:
doc = frappe.get_doc(doctype, name) doc = frappe.get_doc(doctype, name)
if workflow_state_fieldname and workflow_state:
doc.set(workflow_state_fieldname, workflow_state)
doc.cancel() doc.cancel()
send_updated_docs(doc) send_updated_docs(doc)




+ 3
- 1
frappe/desk/form/utils.py 查看文件

@@ -144,7 +144,9 @@ def get_linked_docs(doctype, name, metadata_loaded=None, no_metadata=False):
filters=[[dt, link.get("fieldname"), '=', name]]) filters=[[dt, link.get("fieldname"), '=', name]])


except frappe.PermissionError: except frappe.PermissionError:
frappe.local.message_log.pop()
if frappe.local.message_log:
frappe.local.message_log.pop()

continue continue


if ret: if ret:


+ 18
- 5
frappe/email/bulk.py 查看文件

@@ -4,6 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
import HTMLParser import HTMLParser
import smtplib
from frappe import msgprint, throw, _ from frappe import msgprint, throw, _
from frappe.email.smtp import SMTPServer, get_outgoing_email_account from frappe.email.smtp import SMTPServer, get_outgoing_email_account
from frappe.email.email_body import get_email, get_formatted_html from frappe.email.email_body import get_email, get_formatted_html
@@ -107,14 +108,18 @@ def add(email, sender, subject, formatted, text_content=None,
e.insert(ignore_permissions=True) e.insert(ignore_permissions=True)


def check_bulk_limit(recipients): def check_bulk_limit(recipients):
# get count of mails sent this month
this_month = frappe.db.sql("""select count(*) from `tabBulk Email` where this_month = frappe.db.sql("""select count(*) from `tabBulk Email` where
MONTH(creation)=MONTH(CURDATE())""")[0][0]
status='Sent' and MONTH(creation)=MONTH(CURDATE())""")[0][0]


# if using settings from site_config.json, check bulk limit
# No limit for own email settings # No limit for own email settings
smtp_server = SMTPServer() smtp_server = SMTPServer()


if smtp_server.email_account and getattr(smtp_server.email_account,
"from_site_config", False) or frappe.flags.in_test:
if (smtp_server.email_account
and getattr(smtp_server.email_account, "from_site_config", False)
or frappe.flags.in_test):

monthly_bulk_mail_limit = frappe.conf.get('monthly_bulk_mail_limit') or 500 monthly_bulk_mail_limit = frappe.conf.get('monthly_bulk_mail_limit') or 500


if (this_month + len(recipients)) > monthly_bulk_mail_limit: if (this_month + len(recipients)) > monthly_bulk_mail_limit:
@@ -174,6 +179,9 @@ def flush(from_test=False):


auto_commit = not from_test auto_commit = not from_test


# additional check
check_bulk_limit([])

if frappe.flags.mute_emails or frappe.conf.get("mute_emails") or False: if frappe.flags.mute_emails or frappe.conf.get("mute_emails") or False:
msgprint(_("Emails are muted")) msgprint(_("Emails are muted"))
from_test = True from_test = True
@@ -200,11 +208,16 @@ def flush(from_test=False):
frappe.db.sql("""update `tabBulk Email` set status='Sent' where name=%s""", frappe.db.sql("""update `tabBulk Email` set status='Sent' where name=%s""",
(email["name"],), auto_commit=auto_commit) (email["name"],), auto_commit=auto_commit)


except smtplib.SMTPException:
# bad connection, retry later
frappe.db.sql("""update `tabBulk Email` set status='Not Sent' where name=%s""",
(email["name"],), auto_commit=auto_commit)

except Exception, e: except Exception, e:
frappe.db.sql("""update `tabBulk Email` set status='Error', error=%s frappe.db.sql("""update `tabBulk Email` set status='Error', error=%s
where name=%s""", (unicode(e), email["name"]), auto_commit=auto_commit) where name=%s""", (unicode(e), email["name"]), auto_commit=auto_commit)


def clear_outbox(): def clear_outbox():
"""remove mails older than 30 days in Outbox"""
"""Remove mails older than 31 days in Outbox. Called daily via scheduler."""
frappe.db.sql("""delete from `tabBulk Email` where frappe.db.sql("""delete from `tabBulk Email` where
datediff(now(), creation) > 30""")
datediff(now(), creation) > 31""")

+ 5
- 3
frappe/email/doctype/email_account/email_account.py 查看文件

@@ -124,7 +124,7 @@ class EmailAccount(Document):
exceptions = [] exceptions = []
for raw in incoming_mails: for raw in incoming_mails:
try: try:
self.insert_communication(raw)
communication = self.insert_communication(raw)


except Exception: except Exception:
frappe.db.rollback() frappe.db.rollback()
@@ -132,6 +132,7 @@ class EmailAccount(Document):


else: else:
frappe.db.commit() frappe.db.commit()
communication.notify(attachments=communication._attachments, except_recipient=True)


if exceptions: if exceptions:
raise Exception, frappe.as_json(exceptions) raise Exception, frappe.as_json(exceptions)
@@ -156,7 +157,7 @@ class EmailAccount(Document):
communication.insert(ignore_permissions = 1) communication.insert(ignore_permissions = 1)


# save attachments # save attachments
email.save_attachments_in_doc(communication)
communication._attachments = email.save_attachments_in_doc(communication)


if self.enable_auto_reply and getattr(communication, "is_first", False): if self.enable_auto_reply and getattr(communication, "is_first", False):
self.send_auto_reply(communication, email) self.send_auto_reply(communication, email)
@@ -164,7 +165,8 @@ class EmailAccount(Document):
# notify all participants of this thread # notify all participants of this thread
# convert content to HTML - by default text parts of replies are used. # convert content to HTML - by default text parts of replies are used.
communication.content = markdown2.markdown(communication.content) communication.content = markdown2.markdown(communication.content)
communication.notify(attachments=email.attachments, except_recipient = True)

return communication


def set_thread(self, communication, email): def set_thread(self, communication, email):
"""Appends communication to parent based on thread ID. Will extract """Appends communication to parent based on thread ID. Will extract


+ 7
- 2
frappe/email/receive.py 查看文件

@@ -153,7 +153,7 @@ class POP3Server:
"Connection timed out", "Connection timed out",
) )
for message in messages: for message in messages:
if message in strip(cstr(e.message)):
if message in strip(cstr(e.message)) or message in strip(cstr(getattr(e, 'strerror', ''))):
return True return True
return False return False


@@ -296,10 +296,13 @@ class Email:
def save_attachments_in_doc(self, doc): def save_attachments_in_doc(self, doc):
"""Save email attachments in given document.""" """Save email attachments in given document."""
from frappe.utils.file_manager import save_file, MaxFileSizeReachedError from frappe.utils.file_manager import save_file, MaxFileSizeReachedError
saved_attachments = []

for attachment in self.attachments: for attachment in self.attachments:
try: try:
save_file(attachment['fname'], attachment['fcontent'],
file_data = save_file(attachment['fname'], attachment['fcontent'],
doc.doctype, doc.name) doc.doctype, doc.name)
saved_attachments.append(file_data.file_name)
except MaxFileSizeReachedError: except MaxFileSizeReachedError:
# WARNING: bypass max file size exception # WARNING: bypass max file size exception
pass pass
@@ -307,6 +310,8 @@ class Email:
# same file attached twice?? # same file attached twice??
pass pass


return saved_attachments

def get_thread_id(self): def get_thread_id(self):
"""Extract thread ID from `[]`""" """Extract thread ID from `[]`"""
import re import re


+ 1
- 0
frappe/exceptions.py 查看文件

@@ -53,3 +53,4 @@ class EmptyTableError(ValidationError): pass
class LinkExistsError(ValidationError): pass class LinkExistsError(ValidationError): pass
class InvalidEmailAddressError(ValidationError): pass class InvalidEmailAddressError(ValidationError): pass
class TemplateNotFoundError(ValidationError): pass class TemplateNotFoundError(ValidationError): pass
class UniqueValidationError(ValidationError): pass

+ 29
- 18
frappe/model/base_document.py 查看文件

@@ -161,7 +161,7 @@ class BaseDocument(object):
value.parenttype = self.doctype value.parenttype = self.doctype
value.parentfield = key value.parentfield = key


if value.docstatus==None:
if value.docstatus is None:
value.docstatus = 0 value.docstatus = 0


if not getattr(value, "idx", None): if not getattr(value, "idx", None):
@@ -185,9 +185,8 @@ class BaseDocument(object):
elif df.fieldtype in ("Datetime", "Date") and d[fieldname]=="": elif df.fieldtype in ("Datetime", "Date") and d[fieldname]=="":
d[fieldname] = None d[fieldname] = None


if d[fieldname]=="":
df = self.meta.get_field(fieldname)
if df and df.fieldtype in ("Datetime", "Date"):
elif df.get("unique") and cstr(d[fieldname]).strip()=="":
# unique empty field should be set to None
d[fieldname] = None d[fieldname] = None


return d return d
@@ -264,15 +263,23 @@ class BaseDocument(object):
values = ", ".join(["%s"] * len(columns)) values = ", ".join(["%s"] * len(columns))
), d.values()) ), d.values())
except Exception, e: except Exception, e:
if e.args[0]==1062 and "PRIMARY" in cstr(e.args[1]):
if self.meta.autoname=="hash":
# hash collision? try again
self.name = None
self.db_insert()
return
type, value, traceback = sys.exc_info()
frappe.msgprint(_("Duplicate name {0} {1}").format(self.doctype, self.name))
raise frappe.DuplicateEntryError, (self.doctype, self.name, e), traceback
if e.args[0]==1062:
if "PRIMARY" in cstr(e.args[1]):
if self.meta.autoname=="hash":
# hash collision? try again
self.name = None
self.db_insert()
return

type, value, traceback = sys.exc_info()
frappe.msgprint(_("Duplicate name {0} {1}").format(self.doctype, self.name))
raise frappe.DuplicateEntryError, (self.doctype, self.name, e), traceback

elif "Duplicate" in cstr(e.args[1]):
# unique constraint
self.show_unique_validation_message(e)
else:
raise
else: else:
raise raise


@@ -292,14 +299,18 @@ class BaseDocument(object):
values = ", ".join(["`"+c+"`=%s" for c in columns]) values = ", ".join(["`"+c+"`=%s" for c in columns])
), d.values() + [d.get("name")]) ), d.values() + [d.get("name")])
except Exception, e: except Exception, e:
if e.args[0]==1062:
type, value, traceback = sys.exc_info()
fieldname = str(e).split("'")[-2]
frappe.msgprint(_("{0} must be unique".format(self.meta.get_label(fieldname))))
raise frappe.ValidationError, (self.doctype, self.name, e), traceback
if e.args[0]==1062 and "Duplicate" in cstr(e.args[1]):
self.show_unique_validation_message(e)
else: else:
raise raise


def show_unique_validation_message(self, e):
type, value, traceback = sys.exc_info()
fieldname = str(e).split("'")[-2]
label = fieldname if fieldname.startswith("unique_") else self.meta.get_label(fieldname)
frappe.msgprint(_("{0} must be unique".format(label)))
raise frappe.UniqueValidationError, (self.doctype, self.name, e), traceback

def db_set(self, fieldname, value, update_modified=True): def db_set(self, fieldname, value, update_modified=True):
self.set(fieldname, value) self.set(fieldname, value)
self.set("modified", now()) self.set("modified", now())


+ 1
- 0
frappe/patches.txt 查看文件

@@ -84,6 +84,7 @@ frappe.patches.v5_0.expire_old_scheduler_logs
execute:frappe.permissions.reset_perms("DocType") execute:frappe.permissions.reset_perms("DocType")
execute:frappe.db.sql("delete from `tabProperty Setter` where `property` = 'idx'") execute:frappe.db.sql("delete from `tabProperty Setter` where `property` = 'idx'")
frappe.patches.v5_2.change_checks_to_not_null frappe.patches.v5_2.change_checks_to_not_null
frappe.patches.v5_3.rename_chinese_languages


frappe.patches.v6_0.make_task_log_folder frappe.patches.v6_0.make_task_log_folder
frappe.patches.v6_0.document_type_rename frappe.patches.v6_0.document_type_rename

frappe/translations/en.csv → frappe/patches/v5_3/__init__.py 查看文件


+ 18
- 0
frappe/patches/v5_3/rename_chinese_languages.py 查看文件

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import frappe

def execute():
language_map = {
"中国(简体)": "簡體中文",
"中國(繁體)": "正體中文"
}

language_in_system_settings = frappe.db.get_single_value("System Settings", "language")
if language_in_system_settings in language_map:
new_language_name = language_map[language_in_system_settings]
frappe.db.set_value("System Settings", "System Settings", "language", new_language_name)

for old_name, new_name in language_map.items():
frappe.db.sql("""update `tabUser` set language=%(new_name)s where language=%(old_name)s""",
{ "old_name": old_name, "new_name": new_name })

+ 6
- 4
frappe/permissions.py 查看文件

@@ -44,17 +44,19 @@ def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None):


def false_if_not_shared(): def false_if_not_shared():
if ptype in ("read", "write", "share", "email", "print"): if ptype in ("read", "write", "share", "email", "print"):
shared = frappe.share.get_shared(doctype, user,
["read" if ptype in ("email", "print") else ptype])

if doc: if doc:
doc_name = doc if isinstance(doc, basestring) else doc.name doc_name = doc if isinstance(doc, basestring) else doc.name
shared = frappe.share.get_shared(doctype, user,
["read" if ptype in ("email", "print") else ptype])

if doc_name in shared: if doc_name in shared:
if verbose: print "Shared" if verbose: print "Shared"
if ptype in ("read", "write", "share") or meta.permissions[0].get(ptype): if ptype in ("read", "write", "share") or meta.permissions[0].get(ptype):
return True return True


else:
elif shared:
# if atleast one shared doc of that type, then return True
# this is used in db_query to check if permission on DocType
if verbose: print "Has a shared document" if verbose: print "Has a shared document"
return True return True




+ 1
- 1
frappe/public/js/frappe/form/formatters.js 查看文件

@@ -92,7 +92,7 @@ frappe.form.formatters = {
}, },
Text: function(value) { Text: function(value) {
if(value) { if(value) {
var tags = ["<p", "<div", "<br"];
var tags = ["<p", "<div", "<br", "<table"];
var match = false; var match = false;


for(var i=0; i<tags.length; i++) { for(var i=0; i<tags.length; i++) {


+ 16
- 1
frappe/public/js/frappe/form/save.js 查看文件

@@ -38,9 +38,24 @@ frappe.ui.form.save = function(frm, action, callback, btn) {
}; };


var cancel = function() { var cancel = function() {
var args = {
doctype: frm.doc.doctype,
name: frm.doc.name
};
// update workflow state value if workflow exists
var workflow_state_fieldname = frappe.workflow.get_state_fieldname(frm.doctype);
if(workflow_state_fieldname) {
$.extend(args, {
workflow_state_fieldname: workflow_state_fieldname,
workflow_state: frm.doc[workflow_state_fieldname]
});
}
_call({ _call({
method: "frappe.desk.form.save.cancel", method: "frappe.desk.form.save.cancel",
args: { doctype: frm.doc.doctype, name: frm.doc.name },
args: args,
callback: function(r) { callback: function(r) {
$(document).trigger("save", [frm.doc]); $(document).trigger("save", [frm.doc]);
callback(r); callback(r);


+ 1
- 1
frappe/public/js/frappe/ui/toolbar/about.js 查看文件

@@ -13,7 +13,7 @@ frappe.ui.misc.about = function() {
<h4>Installed Apps</h4>\ <h4>Installed Apps</h4>\
<div id='about-app-versions'>Loading versions...</div>\ <div id='about-app-versions'>Loading versions...</div>\
<hr>\ <hr>\
<p class='text-muted'>&copy; 2014 Frappe Technologies Pvt. Ltd and contributors </p> \
<p class='text-muted'>&copy; 2015 Frappe Technologies Pvt. Ltd and contributors </p> \
</div>", frappe.app)); </div>", frappe.app));


frappe.ui.misc.about_dialog = d; frappe.ui.misc.about_dialog = d;


+ 1
- 1
frappe/public/js/frappe/ui/toolbar/navbar.html 查看文件

@@ -35,7 +35,7 @@
{%= __("About") %}</a></li> {%= __("About") %}</a></li>
<li><a href="https://frappe.io" target="_blank" data-link="docs"> <li><a href="https://frappe.io" target="_blank" data-link="docs">
{%= __("Documentation") %}</a></li> {%= __("Documentation") %}</a></li>
<li><a href="https://discuss.frappe.io" target="_blank">
<li><a href="https://discuss.erpnext.com" target="_blank">
{%= __("Forums") %}</a></li> {%= __("Forums") %}</a></li>
<li><a href="https://github.com/frappe/erpnext/issues" target="_blank"> <li><a href="https://github.com/frappe/erpnext/issues" target="_blank">
{%= __("Report an Issue") %}</a></li> {%= __("Report an Issue") %}</a></li>


+ 1
- 1
frappe/public/js/frappe/ui/toolbar/offcanvas_left_sidebar.html 查看文件

@@ -25,7 +25,7 @@
{%= __("About") %}</a></li> {%= __("About") %}</a></li>
<li><a href="https://frappe.io" target="_blank" data-link="docs"> <li><a href="https://frappe.io" target="_blank" data-link="docs">
{%= __("Documentation") %}</a></li> {%= __("Documentation") %}</a></li>
<li><a href="https://discuss.frappe.io" target="_blank">
<li><a href="https://discuss.erpnext.com" target="_blank">
{%= __("Forums") %}</a></li> {%= __("Forums") %}</a></li>
<li><a href="https://github.com/frappe/erpnext/issues" target="_blank"> <li><a href="https://github.com/frappe/erpnext/issues" target="_blank">
{%= __("Report an Issue") %}</a></li> {%= __("Report an Issue") %}</a></li>


+ 9
- 0
frappe/share.py 查看文件

@@ -3,6 +3,7 @@


from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _
from frappe.utils import cint from frappe.utils import cint


@frappe.whitelist() @frappe.whitelist()
@@ -11,6 +12,8 @@ def add(doctype, name, user=None, read=1, write=0, share=0, everyone=0, flags=No
if not user: if not user:
user = frappe.session.user user = frappe.session.user


check_share_permission(doctype, name)

share_name = get_share_name(doctype, name, user, everyone) share_name = get_share_name(doctype, name, user, everyone)


if share_name: if share_name:
@@ -48,6 +51,8 @@ def remove(doctype, name, user, flags=None):
@frappe.whitelist() @frappe.whitelist()
def set_permission(doctype, name, user, permission_to, value=1, everyone=0): def set_permission(doctype, name, user, permission_to, value=1, everyone=0):
"""Set share permission.""" """Set share permission."""
check_share_permission(doctype, name)

share_name = get_share_name(doctype, name, user, everyone) share_name = get_share_name(doctype, name, user, everyone)
value = int(value) value = int(value)


@@ -124,3 +129,7 @@ def get_share_name(doctype, name, user, everyone):


return share_name return share_name


def check_share_permission(doctype, name):
"""Check if the user can share with other users"""
if not frappe.has_permission(doctype, ptype="share", doc=name):
frappe.throw(_("No permission to {0} {1} {2}".format("share", doctype, name)), frappe.PermissionError)

+ 33
- 1
frappe/tasks.py 查看文件

@@ -11,6 +11,8 @@ from frappe.handler import execute_cmd
from frappe.async import set_task_status, END_LINE, get_std_streams from frappe.async import set_task_status, END_LINE, get_std_streams
import frappe.utils.response import frappe.utils.response
import sys import sys
import time
import MySQLdb


@celery_task() @celery_task()
def sync_queues(): def sync_queues():
@@ -134,7 +136,6 @@ def pull_from_email_account(site, email_account):
finally: finally:
frappe.destroy() frappe.destroy()



@celery_task(bind=True) @celery_task(bind=True)
def run_async_task(self, site, user, cmd, form_dict): def run_async_task(self, site, user, cmd, form_dict):
ret = {} ret = {}
@@ -177,3 +178,34 @@ def run_async_task(self, site, user, cmd, form_dict):
sys.stderr.close() sys.stderr.close()
sys.stdout, sys.stderr = 1, 0 sys.stdout, sys.stderr = 1, 0
return ret return ret


@celery_task()
def sendmail(site, communication_name, print_html=None, print_format=None, attachments=None, recipients=None, except_recipient=False):
try:
frappe.connect(site=site)

# upto 3 retries
for i in xrange(3):
try:
communication = frappe.get_doc("Communication", communication_name)
communication._notify(print_html=print_html, print_format=print_format, attachments=attachments, recipients=recipients, except_recipient=except_recipient)
except MySQLdb.OperationalError, e:
# deadlock, try again
if e.args[0]==1213:
frappe.db.rollback()
time.sleep(1)
continue
else:
raise
else:
break

except:
frappe.db.rollback()

else:
frappe.db.commit()

finally:
frappe.destroy()

+ 393
- 353
frappe/translations/ar.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 349
frappe/translations/bg.csv
文件差異過大導致無法顯示
查看文件


+ 0
- 0
frappe/translations/bo.csv 查看文件


+ 392
- 352
frappe/translations/bs.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/ca.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/cs.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 349
frappe/translations/da.csv
文件差異過大導致無法顯示
查看文件


+ 397
- 357
frappe/translations/de.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 349
frappe/translations/el.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/es.csv
文件差異過大導致無法顯示
查看文件


+ 393
- 350
frappe/translations/fa.csv
文件差異過大導致無法顯示
查看文件


+ 1509
- 0
frappe/translations/fi.csv
文件差異過大導致無法顯示
查看文件


+ 394
- 354
frappe/translations/fr.csv
文件差異過大導致無法顯示
查看文件


+ 379
- 339
frappe/translations/he.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/hi.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/hr.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 349
frappe/translations/hu.csv
文件差異過大導致無法顯示
查看文件


+ 400
- 360
frappe/translations/id.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/it.csv
文件差異過大導致無法顯示
查看文件


+ 397
- 359
frappe/translations/ja.csv
文件差異過大導致無法顯示
查看文件


+ 1356
- 0
frappe/translations/km.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/kn.csv
文件差異過大導致無法顯示
查看文件


+ 408
- 368
frappe/translations/ko.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 349
frappe/translations/lv.csv
文件差異過大導致無法顯示
查看文件


+ 1509
- 0
frappe/translations/mk.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 349
frappe/translations/mr.csv
文件差異過大導致無法顯示
查看文件


+ 1509
- 0
frappe/translations/my.csv
文件差異過大導致無法顯示
查看文件


+ 393
- 353
frappe/translations/nl.csv
文件差異過大導致無法顯示
查看文件


+ 1509
- 0
frappe/translations/no.csv
文件差異過大導致無法顯示
查看文件


+ 642
- 602
frappe/translations/pl.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/pt-BR.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/pt.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/ro.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/ru.csv
文件差異過大導致無法顯示
查看文件


+ 0
- 0
frappe/translations/se.csv 查看文件


+ 392
- 352
frappe/translations/sk.csv
文件差異過大導致無法顯示
查看文件


+ 1509
- 0
frappe/translations/sq.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/sr.csv
文件差異過大導致無法顯示
查看文件


+ 1509
- 0
frappe/translations/sv.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/ta.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/th.csv
文件差異過大導致無法顯示
查看文件


+ 396
- 356
frappe/translations/tr.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/vi.csv
文件差異過大導致無法顯示
查看文件


+ 392
- 352
frappe/translations/zh-cn.csv
文件差異過大導致無法顯示
查看文件


+ 437
- 398
frappe/translations/zh-tw.csv
文件差異過大導致無法顯示
查看文件


Loading…
取消
儲存