Merge branch 'develop'
This commit is contained in:
commit
0e9b7635cb
120 changed files with 2426 additions and 1804 deletions
|
|
@ -310,7 +310,7 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message
|
|||
as_markdown=False, bulk=False, reference_doctype=None, reference_name=None,
|
||||
unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
|
||||
attachments=None, content=None, doctype=None, name=None, reply_to=None,
|
||||
cc=(), message_id=None, as_bulk=False, send_after=None, expose_recipients=False,
|
||||
cc=(), show_as_cc=(), message_id=None, as_bulk=False, send_after=None, expose_recipients=False,
|
||||
bulk_priority=1):
|
||||
"""Send email using user's default **Email Account** or global default **Email Account**.
|
||||
|
||||
|
|
@ -339,7 +339,7 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message
|
|||
subject=subject, message=content or message,
|
||||
reference_doctype = doctype or reference_doctype, reference_name = name or reference_name,
|
||||
unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, unsubscribe_message=unsubscribe_message,
|
||||
attachments=attachments, reply_to=reply_to, cc=cc, message_id=message_id, send_after=send_after,
|
||||
attachments=attachments, reply_to=reply_to, cc=cc, show_as_cc=show_as_cc, message_id=message_id, send_after=send_after,
|
||||
expose_recipients=expose_recipients, bulk_priority=bulk_priority)
|
||||
else:
|
||||
import frappe.email
|
||||
|
|
@ -469,11 +469,14 @@ def get_precision(doctype, fieldname, currency=None, doc=None):
|
|||
from frappe.model.meta import get_field_precision
|
||||
return get_field_precision(get_meta(doctype).get_field(fieldname), doc, currency)
|
||||
|
||||
def generate_hash(txt=None):
|
||||
def generate_hash(txt=None, length=None):
|
||||
"""Generates random hash for given text + current timestamp + random string."""
|
||||
import hashlib, time
|
||||
from .utils import random_string
|
||||
return hashlib.sha224((txt or "") + repr(time.time()) + repr(random_string(8))).hexdigest()
|
||||
digest = hashlib.sha224((txt or "") + repr(time.time()) + repr(random_string(8))).hexdigest()
|
||||
if length:
|
||||
digest = digest[:length]
|
||||
return digest
|
||||
|
||||
def reset_metadata_version():
|
||||
"""Reset `metadata_version` (Client (Javascript) build ID) hash."""
|
||||
|
|
@ -735,6 +738,9 @@ def get_file_json(path):
|
|||
def read_file(path, raise_not_found=False):
|
||||
"""Open a file and return its content as Unicode."""
|
||||
from frappe.utils import cstr
|
||||
if isinstance(path, unicode):
|
||||
path = path.encode("utf-8")
|
||||
|
||||
if os.path.exists(path):
|
||||
with open(path, "r") as f:
|
||||
return cstr(f.read())
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
from __future__ import unicode_literals
|
||||
__version__ = "6.4.9"
|
||||
__version__ = "6.5.0"
|
||||
|
|
|
|||
2
frappe/change_log/v6/v6_5_0.md
Normal file
2
frappe/change_log/v6/v6_5_0.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
- **Linked With** will now show links from Dynamic Links
|
||||
- **Data** field-type size truncated to 140 characters from 255 (by default). Can be changed by setting the **length** property from **Customize Form View**
|
||||
|
|
@ -383,15 +383,28 @@ def reset_perms(context):
|
|||
|
||||
@click.command('execute')
|
||||
@click.argument('method')
|
||||
@click.option('--args')
|
||||
@click.option('--kwargs')
|
||||
@pass_context
|
||||
def execute(context, method):
|
||||
def execute(context, method, args=None, kwargs=None):
|
||||
"execute a function"
|
||||
for site in context.sites:
|
||||
try:
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
print frappe.local.site
|
||||
ret = frappe.get_attr(method)()
|
||||
|
||||
if args:
|
||||
args = eval(args)
|
||||
else:
|
||||
args = ()
|
||||
|
||||
if kwargs:
|
||||
kwargs = eval(args)
|
||||
else:
|
||||
kwargs = {}
|
||||
|
||||
ret = frappe.get_attr(method)(*args, **kwargs)
|
||||
|
||||
if frappe.db:
|
||||
frappe.db.commit()
|
||||
|
|
@ -862,7 +875,9 @@ def drop_site(site, root_login='root', root_password=None):
|
|||
def get_version(context):
|
||||
frappe.init(site=context.sites[0])
|
||||
for m in sorted(frappe.local.app_modules.keys()):
|
||||
print "{0} {1}".format(m, frappe.get_module(m).__version__)
|
||||
module = frappe.get_module(m)
|
||||
if hasattr(module, "__version__"):
|
||||
print "{0} {1}".format(m, module.__version__)
|
||||
|
||||
# commands = [
|
||||
# new_site,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Status",
|
||||
"label": "Status",
|
||||
"no_copy": 0,
|
||||
"options": "\nQueued\nRunning\nSucceeded\nFailed\n",
|
||||
"permlevel": 0,
|
||||
|
|
@ -195,7 +195,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-07 08:08:22.193911",
|
||||
"modified": "2015-10-02 07:38:38.658939",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Async Task",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-03-24 16:03:52.359042",
|
||||
"modified": "2015-10-02 07:38:39.485971",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Block Module",
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-06-08 12:31:15.122312",
|
||||
"modified": "2015-10-02 07:38:41.308408",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Comment",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ from frappe.model.document import Document
|
|||
from frappe.model.db_schema import add_column
|
||||
from frappe.utils import get_fullname
|
||||
|
||||
exclude_from_linked_with = True
|
||||
|
||||
class Comment(Document):
|
||||
"""Comments are added to Documents via forms or views like blogs etc."""
|
||||
no_feed_on_delete = True
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Series",
|
||||
"label": "Series",
|
||||
"no_copy": 0,
|
||||
"options": "COMM-",
|
||||
"permlevel": 0,
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Communication Medium",
|
||||
"label": "Communication Medium",
|
||||
"no_copy": 0,
|
||||
"options": "\nChat\nPhone\nEmail\nSMS\nVisit\nOther",
|
||||
"permlevel": 0,
|
||||
|
|
@ -153,7 +153,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Status",
|
||||
"label": "Status",
|
||||
"no_copy": 0,
|
||||
"options": "Open\nReplied\nClosed\nLinked",
|
||||
"permlevel": 0,
|
||||
|
|
@ -176,7 +176,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Sent or Received",
|
||||
"label": "Sent or Received",
|
||||
"no_copy": 0,
|
||||
"options": "Sent\nReceived",
|
||||
"permlevel": 0,
|
||||
|
|
@ -199,7 +199,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Delivery Status",
|
||||
"label": "Delivery Status",
|
||||
"no_copy": 0,
|
||||
"options": "\nSent\nBounced\nOpened\nMarked As Spam\nRejected\nDelayed\nSoft-Bounced\nClicked\nRecipient Unsubscribed",
|
||||
"permlevel": 0,
|
||||
|
|
@ -573,7 +573,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-24 02:27:43.536919",
|
||||
"modified": "2015-10-02 07:38:41.472917",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Communication",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals, absolute_import
|
|||
import frappe
|
||||
import json
|
||||
from email.utils import formataddr, parseaddr
|
||||
from frappe.utils import get_url, get_formatted_email, cstr, cint, validate_email_add, split_emails
|
||||
from frappe.utils import get_url, get_formatted_email, cint, validate_email_add, split_emails
|
||||
from frappe.utils.file_manager import get_file
|
||||
import frappe.email.smtp
|
||||
from frappe import _
|
||||
|
|
@ -106,7 +106,7 @@ class Communication(Document):
|
|||
from frappe.tasks import sendmail
|
||||
sendmail.delay(frappe.local.site, self.name,
|
||||
print_html=print_html, print_format=print_format, attachments=attachments,
|
||||
recipients=recipients, cc=cc)
|
||||
recipients=recipients, cc=cc, lang=frappe.local.lang)
|
||||
|
||||
def _notify(self, print_html=None, print_format=None, attachments=None,
|
||||
recipients=None, cc=None):
|
||||
|
|
@ -115,6 +115,7 @@ class Communication(Document):
|
|||
|
||||
frappe.sendmail(
|
||||
recipients=(recipients or []) + (cc or []),
|
||||
show_as_cc=(cc or []),
|
||||
expose_recipients=True,
|
||||
sender=self.sender,
|
||||
reply_to=self.incoming_email_account,
|
||||
|
|
@ -230,8 +231,8 @@ class Communication(Document):
|
|||
cc = split_emails(self.cc)
|
||||
|
||||
if self.reference_doctype and self.reference_name:
|
||||
if not cc or fetched_from_email_account:
|
||||
# if CC is not mentioned from the UI or is a fetched email, add follows to CC
|
||||
if fetched_from_email_account:
|
||||
# if it is a fetched email, add follows to CC
|
||||
cc.append(self.get_owner_email())
|
||||
cc += self.get_assignees()
|
||||
cc += self.get_starrers()
|
||||
|
|
@ -261,7 +262,7 @@ class Communication(Document):
|
|||
exclude += [d[0] for d in frappe.db.get_all("Email Unsubscribe", ["email"],
|
||||
{"reference_doctype": self.reference_doctype, "reference_name": self.reference_name}, as_list=True)]
|
||||
|
||||
cc = self.filter_email_list(cc, exclude)
|
||||
cc = self.filter_email_list(cc, exclude, is_cc=True)
|
||||
|
||||
if getattr(self, "send_me_a_copy", False) and self.sender not in cc:
|
||||
self.all_email_addresses.append(self.sender)
|
||||
|
|
@ -269,7 +270,7 @@ class Communication(Document):
|
|||
|
||||
return cc
|
||||
|
||||
def filter_email_list(self, email_list, exclude):
|
||||
def filter_email_list(self, email_list, exclude, is_cc=False):
|
||||
# temp variables
|
||||
filtered = []
|
||||
email_address_list = []
|
||||
|
|
@ -285,6 +286,12 @@ class Communication(Document):
|
|||
if email_address in exclude:
|
||||
continue
|
||||
|
||||
if is_cc:
|
||||
is_user_enabled = frappe.db.get_value("User", email_address, "enabled")
|
||||
if is_user_enabled==0:
|
||||
# don't send to disabled users
|
||||
continue
|
||||
|
||||
# make sure of case-insensitive uniqueness of email address
|
||||
if email_address not in email_address_list:
|
||||
# append the full email i.e. "Human <human@example.com>"
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-02-19 01:06:59.622792",
|
||||
"modified": "2015-10-02 07:38:44.346115",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DefaultValue",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -591,7 +591,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-07-22 07:39:40.471092",
|
||||
"modified": "2015-10-02 07:38:46.740536",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocPerm",
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-07-17 07:02:10.632582",
|
||||
"modified": "2015-10-02 07:38:47.029636",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocShare",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from frappe.model.document import Document
|
|||
from frappe import _
|
||||
from frappe.utils import get_fullname
|
||||
|
||||
exclude_from_linked_with = True
|
||||
|
||||
class DocShare(Document):
|
||||
no_feed_on_delete = True
|
||||
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Document Type",
|
||||
"label": "Document Type",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "document_type",
|
||||
"oldfieldtype": "Select",
|
||||
|
|
@ -312,7 +312,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Name Case",
|
||||
"label": "Name Case",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "name_case",
|
||||
"oldfieldtype": "Select",
|
||||
|
|
@ -448,7 +448,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Sort Order",
|
||||
"label": "Sort Order",
|
||||
"no_copy": 0,
|
||||
"options": "ASC\nDESC",
|
||||
"permlevel": 0,
|
||||
|
|
@ -851,7 +851,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-07-28 16:18:11.925264",
|
||||
"modified": "2015-10-02 07:38:47.199387",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocType",
|
||||
|
|
|
|||
12
frappe/core/doctype/doctype/test_doctype.py
Normal file
12
frappe/core/doctype/doctype/test_doctype.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('DocType')
|
||||
|
||||
class TestDocType(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
"allow_copy": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"autoname": "",
|
||||
"creation": "2012-12-12 11:19:22",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
|
|
@ -189,7 +189,7 @@
|
|||
"collapsible": 0,
|
||||
"depends_on": "eval:!doc.is_folder",
|
||||
"fieldname": "file_url",
|
||||
"fieldtype": "Data",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
|
|
@ -205,6 +205,28 @@
|
|||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "thumbnail_url",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Thumbnail URL",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
|
|
@ -433,7 +455,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-18 06:22:10.902847",
|
||||
"modified": "2015-10-19 02:13:43.935641",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "File",
|
||||
|
|
|
|||
|
|
@ -13,10 +13,14 @@ from frappe.utils.file_manager import delete_file_data_content
|
|||
from frappe import _
|
||||
|
||||
from frappe.utils.nestedset import NestedSet
|
||||
from frappe.utils import strip
|
||||
import json
|
||||
import urllib
|
||||
|
||||
class FolderNotEmpty(frappe.ValidationError): pass
|
||||
|
||||
exclude_from_linked_with = True
|
||||
|
||||
class File(NestedSet):
|
||||
nsm_parent_field = 'folder'
|
||||
no_feed_on_delete = True
|
||||
|
|
@ -40,7 +44,7 @@ class File(NestedSet):
|
|||
# home
|
||||
self.name = self.file_name
|
||||
else:
|
||||
self.name = self.file_url
|
||||
self.name = frappe.generate_hash("", 10)
|
||||
|
||||
def after_insert(self):
|
||||
self.update_parent_folder_size()
|
||||
|
|
@ -53,7 +57,8 @@ class File(NestedSet):
|
|||
return frappe.db.sql_list("select name from tabFile where folder='%s'"%self.name) or []
|
||||
|
||||
def validate(self):
|
||||
self.validate_duplicate_entry()
|
||||
if self.is_new():
|
||||
self.validate_duplicate_entry()
|
||||
self.validate_folder()
|
||||
self.set_folder_size()
|
||||
|
||||
|
|
@ -92,6 +97,8 @@ class File(NestedSet):
|
|||
|
||||
def validate_duplicate_entry(self):
|
||||
if not self.flags.ignore_duplicate_entry_error and not self.is_folder:
|
||||
# check duplicate name
|
||||
|
||||
# check duplicate assignement
|
||||
n_records = frappe.db.sql("""select name from `tabFile`
|
||||
where content_hash=%s
|
||||
|
|
@ -111,12 +118,55 @@ class File(NestedSet):
|
|||
super(File, self).on_trash()
|
||||
self.delete_file()
|
||||
|
||||
def make_thumbnail(self):
|
||||
from PIL import Image, ImageOps
|
||||
import os
|
||||
|
||||
if self.file_url:
|
||||
if self.file_url.startswith("/files"):
|
||||
try:
|
||||
image = Image.open(frappe.get_site_path("public", self.file_url))
|
||||
filename, extn = self.file_url.rsplit(".", 1)
|
||||
except IOError:
|
||||
frappe.msgprint("Unable to read file format for {0}".format(self.file_url))
|
||||
|
||||
else:
|
||||
# downlaod
|
||||
import requests, StringIO
|
||||
file_url = frappe.utils.get_url(self.file_url)
|
||||
r = requests.get(file_url, stream=True)
|
||||
r.raise_for_status()
|
||||
image = Image.open(StringIO.StringIO(r.content))
|
||||
filename, extn = self.file_url.rsplit("/", 1)[1].rsplit(".", 1)
|
||||
filename = "/files/" + strip(urllib.unquote(filename))
|
||||
|
||||
thumbnail = ImageOps.fit(
|
||||
image,
|
||||
(300, 300),
|
||||
Image.ANTIALIAS
|
||||
)
|
||||
|
||||
thumbnail_url = filename + "_small." + extn
|
||||
|
||||
path = os.path.abspath(frappe.get_site_path("public", thumbnail_url.lstrip("/")))
|
||||
|
||||
try:
|
||||
thumbnail.save(path)
|
||||
self.db_set("thumbnail_url", thumbnail_url)
|
||||
except IOError:
|
||||
frappe.msgprint("Unable to write file format for {0}".format(path))
|
||||
|
||||
return thumbnail_url
|
||||
|
||||
|
||||
def after_delete(self):
|
||||
self.update_parent_folder_size()
|
||||
|
||||
def check_folder_is_empty(self):
|
||||
"""Throw exception if folder is not empty"""
|
||||
if self.is_folder and frappe.get_all("File", filters={"folder": self.name}):
|
||||
files = frappe.get_all("File", filters={"folder": self.name}, fields=("name", "file_name"))
|
||||
|
||||
if self.is_folder and files:
|
||||
frappe.throw(_("Folder {0} is not empty").format(self.name), FolderNotEmpty)
|
||||
|
||||
def check_reference_doc_permission(self):
|
||||
|
|
@ -138,6 +188,9 @@ class File(NestedSet):
|
|||
{"content_hash": self.content_hash, "name": ["!=", self.name]})):
|
||||
delete_file_data_content(self)
|
||||
|
||||
elif self.file_url:
|
||||
delete_file_data_content(self, only_thumbnail=True)
|
||||
|
||||
def on_rollback(self):
|
||||
self.on_trash()
|
||||
|
||||
|
|
@ -178,7 +231,10 @@ def create_new_folder(file_name, folder):
|
|||
|
||||
@frappe.whitelist()
|
||||
def move_file(file_list, new_parent, old_parent):
|
||||
for file_obj in json.loads(file_list):
|
||||
if isinstance(file_list, basestring):
|
||||
file_list = json.loads(file_list)
|
||||
|
||||
for file_obj in file_list:
|
||||
setup_folder_path(file_obj.get("name"), new_parent)
|
||||
|
||||
# recalculate sizes
|
||||
|
|
|
|||
|
|
@ -5,37 +5,26 @@ from __future__ import unicode_literals
|
|||
|
||||
import frappe
|
||||
import unittest
|
||||
from frappe.utils.file_manager import save_file, get_file, get_files_path
|
||||
from frappe.utils.file_manager import save_file, get_files_path
|
||||
from frappe import _
|
||||
from frappe.core.doctype.file.file import move_file
|
||||
import json
|
||||
# test_records = frappe.get_test_records('File')
|
||||
|
||||
class TestFile(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.delete_test_data()
|
||||
self.upload_file()
|
||||
|
||||
def delete_test_data(self):
|
||||
for file_name in ["folder_copy.txt", "file_copy.txt", "Test Folder 2"]:
|
||||
file_name = frappe.db.get_value("File", {"file_name": file_name}, "name")
|
||||
if file_name:
|
||||
file = frappe.get_doc("File", file_name)
|
||||
ancestors = file.get_ancestors()
|
||||
file.delete()
|
||||
self.delete_ancestors(ancestors)
|
||||
|
||||
def delete_ancestors(self, ancestors):
|
||||
for folder in ancestors:
|
||||
if folder != "Home":
|
||||
folder = frappe.get_doc("File", folder)
|
||||
folder.delete()
|
||||
def delete_test_data(self):
|
||||
for f in frappe.db.sql('''select name, file_name from tabFile where
|
||||
is_home_folder = 0 and is_attachments_folder = 0 order by rgt-lft asc'''):
|
||||
frappe.delete_doc("File", f[0])
|
||||
|
||||
def upload_file(self):
|
||||
self.saved_file = save_file('file_copy.txt', "Testing file copy example.",\
|
||||
"", "", self.get_folder("Test Folder 1", "Home").name)
|
||||
self.saved_filename = get_files_path(self.saved_file.file_name)
|
||||
|
||||
|
||||
def get_folder(self, folder_name, parent_folder="Home"):
|
||||
return frappe.get_doc({
|
||||
"doctype": "File",
|
||||
|
|
@ -46,61 +35,57 @@ class TestFile(unittest.TestCase):
|
|||
|
||||
def tests_after_upload(self):
|
||||
self.assertEqual(self.saved_file.folder, _("Home/Test Folder 1"))
|
||||
|
||||
|
||||
folder_size = frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size")
|
||||
saved_file_size = frappe.db.get_value("File", self.saved_file.name, "file_size")
|
||||
|
||||
|
||||
self.assertEqual(folder_size, saved_file_size)
|
||||
|
||||
|
||||
def test_file_copy(self):
|
||||
folder = self.get_folder("Test Folder 2", "Home")
|
||||
|
||||
file = frappe.get_doc("File", "/files/file_copy.txt")
|
||||
|
||||
file_dict = [{"name": file.name}]
|
||||
|
||||
move_file(json.dumps(file_dict), folder.name, file.folder)
|
||||
|
||||
file = frappe.get_doc("File", "/files/file_copy.txt")
|
||||
|
||||
|
||||
file = frappe.get_doc("File", {"file_name":"file_copy.txt"})
|
||||
move_file([{"name": file.name}], folder.name, file.folder)
|
||||
file = frappe.get_doc("File", {"file_name":"file_copy.txt"})
|
||||
|
||||
self.assertEqual(_("Home/Test Folder 2"), file.folder)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 2"), "file_size"), file.file_size)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), None)
|
||||
|
||||
|
||||
def test_folder_copy(self):
|
||||
folder = self.get_folder("Test Folder 2", "Home")
|
||||
folder = self.get_folder("Test Folder 3", "Home/Test Folder 2")
|
||||
|
||||
|
||||
self.saved_file = save_file('folder_copy.txt', "Testing folder copy example.", "", "", folder.name)
|
||||
|
||||
file_dict = [{"name": folder.name}]
|
||||
|
||||
move_file(json.dumps(file_dict), 'Home/Test Folder 1', folder.folder)
|
||||
|
||||
file = frappe.get_doc("File", "/files/folder_copy.txt")
|
||||
|
||||
|
||||
move_file([{"name": folder.name}], 'Home/Test Folder 1', folder.folder)
|
||||
|
||||
file = frappe.get_doc("File", {"file_name":"folder_copy.txt"})
|
||||
file_copy_txt = frappe.get_value("File", {"file_name":"file_copy.txt"})
|
||||
if file_copy_txt:
|
||||
frappe.get_doc("File", file_copy_txt).delete()
|
||||
|
||||
self.assertEqual(_("Home/Test Folder 1/Test Folder 3"), file.folder)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), file.file_size)
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 2"), "file_size"), None)
|
||||
|
||||
|
||||
def test_non_parent_folder(self):
|
||||
d = frappe.get_doc({
|
||||
"doctype": "File",
|
||||
"file_name": _("Test_Folder"),
|
||||
"is_folder": 1
|
||||
})
|
||||
|
||||
|
||||
self.assertRaises(frappe.ValidationError, d.save)
|
||||
|
||||
|
||||
def test_on_delete(self):
|
||||
file = frappe.get_doc("File", "/files/file_copy.txt")
|
||||
file = frappe.get_doc("File", {"file_name":"file_copy.txt"})
|
||||
file.delete()
|
||||
|
||||
|
||||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), None)
|
||||
|
||||
|
||||
folder = self.get_folder("Test Folder 3", "Home/Test Folder 1")
|
||||
self.saved_file = save_file('folder_copy.txt', "Testing folder copy example.", "", "", folder.name)
|
||||
|
||||
folder = frappe.get_doc("File", "Home/Test Folder 1/Test Folder 3")
|
||||
self.assertRaises(frappe.ValidationError, folder.delete)
|
||||
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-02-05 05:11:41.388856",
|
||||
"modified": "2015-10-02 07:38:57.452736",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Module Def",
|
||||
|
|
|
|||
12
frappe/core/doctype/module_def/test_module_def.py
Normal file
12
frappe/core/doctype/module_def/test_module_def.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Module Def')
|
||||
|
||||
class TestModuleDef(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -149,7 +149,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Standard",
|
||||
"label": "Standard",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "standard",
|
||||
"oldfieldtype": "Select",
|
||||
|
|
@ -217,7 +217,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-11 12:19:55.121822",
|
||||
"modified": "2015-10-02 07:38:59.403028",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Page",
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-02-19 01:07:00.897854",
|
||||
"modified": "2015-10-02 07:38:59.530013",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Page Role",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "patch",
|
||||
"fieldtype": "Data",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2013-12-20 19:24:15",
|
||||
"modified": "2015-10-02 07:38:59.666628",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Patch Log",
|
||||
|
|
|
|||
12
frappe/core/doctype/patch_log/test_patch_log.py
Normal file
12
frappe/core/doctype/patch_log/test_patch_log.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Patch Log')
|
||||
|
||||
class TestPatchLog(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Is Standard",
|
||||
"label": "Is Standard",
|
||||
"no_copy": 0,
|
||||
"options": "No\nYes",
|
||||
"permlevel": 0,
|
||||
|
|
@ -147,7 +147,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Report Type",
|
||||
"label": "Report Type",
|
||||
"no_copy": 0,
|
||||
"options": "Report Builder\nQuery Report\nScript Report",
|
||||
"permlevel": 0,
|
||||
|
|
@ -300,7 +300,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-07 15:51:26",
|
||||
"modified": "2015-10-02 07:39:07.933259",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Report",
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-07 15:51:26",
|
||||
"modified": "2015-10-02 07:39:08.074305",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Role",
|
||||
|
|
|
|||
|
|
@ -10,4 +10,6 @@ class Role(Document):
|
|||
def after_insert(self):
|
||||
# Add role to Administrator
|
||||
if frappe.flags.in_install != "frappe":
|
||||
frappe.get_doc("User", "Administrator").add_roles(self.name)
|
||||
user = frappe.get_doc("User", "Administrator")
|
||||
user.flags.ignore_permissions = True
|
||||
user.add_roles(self.name)
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-05-28 02:49:12.819934",
|
||||
"modified": "2015-10-02 07:39:12.348067",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Scheduler Log",
|
||||
|
|
|
|||
|
|
@ -1263,7 +1263,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 5,
|
||||
"modified": "2015-08-18 11:58:00.000691",
|
||||
"modified": "2015-10-19 03:04:48.829054",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "User",
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-02-19 01:07:02.561834",
|
||||
"modified": "2015-10-02 07:39:18.179539",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "UserRole",
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-07-28 16:18:12.706419",
|
||||
"modified": "2015-10-02 07:39:18.235343",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Version",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -44,7 +44,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Script Type",
|
||||
"label": "Script Type",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "script_type",
|
||||
"oldfieldtype": "Select",
|
||||
|
|
@ -114,7 +114,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-08-27 06:36:20.439949",
|
||||
"modified": "2015-10-02 07:38:43.345211",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Custom Script",
|
||||
|
|
|
|||
12
frappe/custom/doctype/custom_script/test_custom_script.py
Normal file
12
frappe/custom/doctype/custom_script/test_custom_script.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Custom Script')
|
||||
|
||||
class TestCustomScript(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -234,7 +234,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Sort Order",
|
||||
"label": "Sort Order",
|
||||
"no_copy": 0,
|
||||
"options": "ASC\nDESC",
|
||||
"permlevel": 0,
|
||||
|
|
@ -301,7 +301,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"modified": "2015-07-27 01:00:32.901851",
|
||||
"modified": "2015-10-02 07:17:18.939161",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Customize Form",
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ class CustomizeForm(Document):
|
|||
'description': 'Text',
|
||||
'default': 'Text',
|
||||
'precision': 'Select',
|
||||
'read_only': 'Check'
|
||||
'read_only': 'Check',
|
||||
'length': 'Int'
|
||||
}
|
||||
|
||||
allowed_fieldtype_change = (('Currency', 'Float', 'Percent'), ('Small Text', 'Data'),
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Type",
|
||||
"label": "Type",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "fieldtype",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Attach\nButton\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime",
|
||||
"options": "Attach\nAttach Image\nButton\nCheck\nCode\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nHeading\nHTML\nImage\nInt\nLink\nLong Text\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
|
|
@ -202,7 +202,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Precision",
|
||||
"label": "Precision",
|
||||
"no_copy": 0,
|
||||
"options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9",
|
||||
"permlevel": 0,
|
||||
|
|
@ -215,6 +215,29 @@
|
|||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'], doc.fieldtype)",
|
||||
"fieldname": "length",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Length",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
|
|
@ -709,7 +732,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-10-01 07:59:15.490247",
|
||||
"modified": "2015-10-03 07:38:44.026280",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Customize Form Field",
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "DocType or Field",
|
||||
"label": "DocType or Field",
|
||||
"no_copy": 0,
|
||||
"options": "\nDocField\nDocType",
|
||||
"permlevel": 0,
|
||||
|
|
@ -233,7 +233,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-02-05 05:11:43.216164",
|
||||
"modified": "2015-10-02 07:39:02.618929",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Property Setter",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Property Setter')
|
||||
|
||||
class TestPropertySetter(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -36,8 +36,8 @@ CREATE TABLE `tabDocField` (
|
|||
`no_copy` int(1) NOT NULL DEFAULT 0,
|
||||
`allow_on_submit` int(1) NOT NULL DEFAULT 0,
|
||||
`trigger` varchar(255) DEFAULT NULL,
|
||||
`collapsible_depends_on` varchar(255) DEFAULT NULL,
|
||||
`depends_on` varchar(255) DEFAULT NULL,
|
||||
`collapsible_depends_on` text,
|
||||
`depends_on` text,
|
||||
`permlevel` int(11) DEFAULT '0',
|
||||
`ignore_user_permissions` int(1) NOT NULL DEFAULT 0,
|
||||
`width` varchar(255) DEFAULT NULL,
|
||||
|
|
@ -48,6 +48,7 @@ CREATE TABLE `tabDocField` (
|
|||
`in_list_view` int(1) NOT NULL DEFAULT 0,
|
||||
`read_only` int(1) NOT NULL DEFAULT 0,
|
||||
`precision` varchar(255) DEFAULT NULL,
|
||||
`length` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`name`),
|
||||
KEY `parent` (`parent`),
|
||||
KEY `label` (`label`),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ ar العربية
|
|||
bg bǎlgarski
|
||||
bo ལྷ་སའི་སྐད་
|
||||
bs bosanski
|
||||
bn বাঙালি
|
||||
bn বাংলা
|
||||
ca català
|
||||
cs česky
|
||||
da dansk
|
||||
|
|
@ -36,9 +36,8 @@ pt português
|
|||
pt-BR português brasileiro
|
||||
ro român
|
||||
ru русский
|
||||
si slovenščina
|
||||
sk slovenčina
|
||||
sv svenska
|
||||
sk slovenčina
|
||||
sq shqiptar
|
||||
sr српски
|
||||
ta தமிழ்
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Event Type",
|
||||
"label": "Event Type",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "event_type",
|
||||
"oldfieldtype": "Select",
|
||||
|
|
@ -253,7 +253,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Repeat On",
|
||||
"label": "Repeat On",
|
||||
"no_copy": 0,
|
||||
"options": "\nEvery Day\nEvery Week\nEvery Month\nEvery Year",
|
||||
"permlevel": 0,
|
||||
|
|
@ -635,7 +635,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-07 15:51:26",
|
||||
"modified": "2015-10-02 07:38:49.897665",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Event",
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-02-19 01:07:00.166770",
|
||||
"modified": "2015-10-02 07:38:50.115057",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Event Role",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Feed Type",
|
||||
"label": "Feed Type",
|
||||
"no_copy": 0,
|
||||
"options": "\nComment\nLogin\nLabel\nInfo",
|
||||
"permlevel": 0,
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
|
|
@ -145,7 +145,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-07 15:51:26",
|
||||
"modified": "2015-10-02 07:38:50.611929",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Feed",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ from frappe.model.document import Document
|
|||
from frappe.utils import get_fullname
|
||||
from frappe import _
|
||||
|
||||
exclude_from_linked_with = True
|
||||
|
||||
class Feed(Document):
|
||||
pass
|
||||
|
||||
|
|
|
|||
12
frappe/desk/doctype/feed/test_feed.py
Normal file
12
frappe/desk/doctype/feed/test_feed.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Feed')
|
||||
|
||||
class TestFeed(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-11 12:20:04.912891",
|
||||
"modified": "2015-10-02 07:38:57.968895",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Note",
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ frappe.ui.form.on("ToDo", {
|
|||
frm.save();
|
||||
}, null, "btn-default");
|
||||
}
|
||||
frm.add_custom_button(__("New"), function() {
|
||||
newdoc("ToDo")
|
||||
}, null, "btn-default");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Status",
|
||||
"label": "Status",
|
||||
"no_copy": 0,
|
||||
"options": "Open\nClosed",
|
||||
"permlevel": 0,
|
||||
|
|
@ -108,7 +108,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Priority",
|
||||
"label": "Priority",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "priority",
|
||||
"oldfieldtype": "Data",
|
||||
|
|
@ -335,7 +335,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-09-07 15:51:26",
|
||||
"modified": "2015-10-02 07:39:17.248993",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "ToDo",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from frappe.utils import get_fullname
|
|||
|
||||
subject_field = "description"
|
||||
sender_field = "sender"
|
||||
exclude_from_linked_with = True
|
||||
|
||||
class ToDo(Document):
|
||||
def validate(self):
|
||||
|
|
|
|||
|
|
@ -103,8 +103,8 @@ def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE',
|
|||
user_info = get_fullnames()
|
||||
|
||||
# Search for email address in description -- i.e. assignee
|
||||
from frappe.utils import get_url_to_form
|
||||
assignment = get_url_to_form(doc_type, doc_name, label="%s: %s" % (doc_type, doc_name))
|
||||
from frappe.utils import get_link_to_form
|
||||
assignment = get_link_to_form(doc_type, doc_name, label="%s: %s" % (doc_type, doc_name))
|
||||
owner_name = user_info.get(owner, {}).get('fullname')
|
||||
user_name = user_info.get(frappe.session.get('user'), {}).get('fullname')
|
||||
if action=='CLOSE':
|
||||
|
|
|
|||
177
frappe/desk/form/linked_with.py
Normal file
177
frappe/desk/form/linked_with.py
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe, json
|
||||
from frappe.model.meta import is_single
|
||||
from frappe.modules import load_doctype_module
|
||||
import frappe.desk.form.meta
|
||||
import frappe.desk.form.load
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_linked_docs(doctype, name, linkinfo=None):
|
||||
results = frappe.cache().get_value("linked_with:{doctype}:{name}".format(doctype=doctype, name=name))
|
||||
if results:
|
||||
return results
|
||||
|
||||
meta = frappe.desk.form.meta.get_meta(doctype)
|
||||
results = {}
|
||||
|
||||
if isinstance(linkinfo, basestring):
|
||||
# additional fields are added in linkinfo
|
||||
linkinfo = json.loads(linkinfo)
|
||||
|
||||
if not linkinfo:
|
||||
return results
|
||||
|
||||
me = frappe.db.get_value(doctype, name, ["parenttype", "parent"], as_dict=True)
|
||||
for dt, link in linkinfo.items():
|
||||
link["doctype"] = dt
|
||||
link_meta_bundle = frappe.desk.form.load.get_meta_bundle(dt)
|
||||
linkmeta = link_meta_bundle[0]
|
||||
if not linkmeta.get("issingle"):
|
||||
fields = [d.fieldname for d in linkmeta.get("fields", {"in_list_view":1,
|
||||
"fieldtype": ["not in", ["Image", "HTML", "Button", "Table"]]})] \
|
||||
+ ["name", "modified", "docstatus"]
|
||||
|
||||
if link.get("add_fields"):
|
||||
fields += link["add_fields"]
|
||||
|
||||
fields = ["`tab{dt}`.`{fn}`".format(dt=dt, fn=sf.strip()) for sf in fields if sf
|
||||
and "`tab" not in sf]
|
||||
|
||||
try:
|
||||
if link.get("get_parent"):
|
||||
if me and me.parent and me.parenttype == dt:
|
||||
ret = frappe.get_list(doctype=dt, fields=fields,
|
||||
filters=[[dt, "name", '=', me.parent]])
|
||||
else:
|
||||
ret = None
|
||||
|
||||
elif link.get("child_doctype"):
|
||||
filters = [[link.get('child_doctype'), link.get("fieldname"), '=', name]]
|
||||
|
||||
# dynamic link
|
||||
if link.get("doctype_fieldname"):
|
||||
filters.append([link.get('child_doctype'), link.get("doctype_fieldname"), "=", doctype])
|
||||
|
||||
ret = frappe.get_list(doctype=dt, fields=fields, filters=filters)
|
||||
|
||||
else:
|
||||
filters = [[dt, link.get("fieldname"), '=', name]]
|
||||
|
||||
# dynamic link
|
||||
if link.get("doctype_fieldname"):
|
||||
filters.append([dt, link.get("doctype_fieldname"), "=", doctype])
|
||||
|
||||
ret = frappe.get_list(doctype=dt, fields=fields, filters=filters)
|
||||
|
||||
except frappe.PermissionError:
|
||||
if frappe.local.message_log:
|
||||
frappe.local.message_log.pop()
|
||||
|
||||
continue
|
||||
|
||||
if ret:
|
||||
results[dt] = ret
|
||||
|
||||
frappe.cache().set_value("linked_with:{doctype}:{name}".format(doctype=doctype, name=name), results, user=True)
|
||||
|
||||
return results
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_linked_doctypes(doctype):
|
||||
"""add list of doctypes this doctype is 'linked' with.
|
||||
|
||||
Example, for Customer:
|
||||
|
||||
{"Address": {"fieldname": "customer"}..}
|
||||
"""
|
||||
return frappe.cache().hget("linked_doctypes", doctype, lambda: _get_linked_doctypes(doctype))
|
||||
|
||||
def _get_linked_doctypes(doctype):
|
||||
ret = {}
|
||||
|
||||
# find fields where this doctype is linked
|
||||
ret.update(get_linked_fields(doctype))
|
||||
|
||||
ret.update(get_dynamic_linked_fields(doctype))
|
||||
|
||||
# find links of parents
|
||||
links = frappe.db.sql("""select dt from `tabCustom Field`
|
||||
where (fieldtype="Table" and options=%s)""", (doctype))
|
||||
links += frappe.db.sql("""select parent from tabDocField
|
||||
where (fieldtype="Table" and options=%s)""", (doctype))
|
||||
|
||||
for dt, in links:
|
||||
if not dt in ret:
|
||||
ret[dt] = {"get_parent": True}
|
||||
|
||||
for dt in ret.keys():
|
||||
doctype_module = load_doctype_module(dt)
|
||||
|
||||
if getattr(doctype_module, "exclude_from_linked_with", False):
|
||||
del ret[dt]
|
||||
|
||||
return ret
|
||||
|
||||
def get_linked_fields(doctype):
|
||||
links = frappe.db.sql("""select parent, fieldname from tabDocField
|
||||
where (fieldtype="Link" and options=%s)
|
||||
or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype))
|
||||
links += frappe.db.sql("""select dt as parent, fieldname from `tabCustom Field`
|
||||
where (fieldtype="Link" and options=%s)
|
||||
or (fieldtype="Select" and options=%s)""", (doctype, "link:"+ doctype))
|
||||
|
||||
links = dict(links)
|
||||
|
||||
ret = {}
|
||||
|
||||
if links:
|
||||
for dt in links:
|
||||
ret[dt] = { "fieldname": links[dt] }
|
||||
|
||||
# find out if linked in a child table
|
||||
for parent, options in frappe.db.sql("""select parent, options from tabDocField
|
||||
where fieldtype="Table"
|
||||
and options in (select name from tabDocType
|
||||
where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)):
|
||||
|
||||
ret[parent] = {"child_doctype": options, "fieldname": links[options] }
|
||||
if options in ret:
|
||||
del ret[options]
|
||||
|
||||
return ret
|
||||
|
||||
def get_dynamic_linked_fields(doctype):
|
||||
ret = {}
|
||||
|
||||
links = frappe.db.sql("""select parent as doctype, fieldname, options as doctype_fieldname
|
||||
from `tabDocField` where fieldtype='Dynamic Link'""", as_dict=True)
|
||||
links += frappe.db.sql("""select dt as doctype, fieldname, options as doctype_fieldname
|
||||
from `tabCustom Field` where fieldtype='Dynamic Link'""", as_dict=True)
|
||||
|
||||
for df in links:
|
||||
if is_single(df.doctype):
|
||||
continue
|
||||
|
||||
# optimized to get both link exists and parenttype
|
||||
possible_link = frappe.db.sql("""select distinct `{doctype_fieldname}`, parenttype
|
||||
from `tab{doctype}` where `{doctype_fieldname}`=%s""".format(**df), doctype, as_dict=True)
|
||||
if possible_link:
|
||||
for d in possible_link:
|
||||
# is child
|
||||
if d.parenttype:
|
||||
ret[d.parenttype] = {
|
||||
"child_doctype": df.doctype,
|
||||
"fieldname": df.fieldname,
|
||||
"doctype_fieldname": df.doctype_fieldname
|
||||
}
|
||||
|
||||
else:
|
||||
ret[df.doctype] = {
|
||||
"fieldname": df.fieldname,
|
||||
"doctype_fieldname": df.doctype_fieldname
|
||||
}
|
||||
|
||||
return ret
|
||||
|
|
@ -36,7 +36,6 @@ class FormMeta(Meta):
|
|||
self.add_linked_document_type()
|
||||
|
||||
if not self.istable:
|
||||
self.add_linked_with()
|
||||
self.add_code()
|
||||
self.load_print_formats()
|
||||
self.load_workflows()
|
||||
|
|
@ -130,52 +129,6 @@ class FormMeta(Meta):
|
|||
# edge case where options="[Select]"
|
||||
pass
|
||||
|
||||
def add_linked_with(self):
|
||||
"""add list of doctypes this doctype is 'linked' with.
|
||||
|
||||
Example, for Customer:
|
||||
|
||||
{"Address": {"fieldname": "customer"}..}
|
||||
"""
|
||||
|
||||
# find fields where this doctype is linked
|
||||
links = frappe.db.sql("""select parent, fieldname from tabDocField
|
||||
where (fieldtype="Link" and options=%s)
|
||||
or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name))
|
||||
links += frappe.db.sql("""select dt as parent, fieldname from `tabCustom Field`
|
||||
where (fieldtype="Link" and options=%s)
|
||||
or (fieldtype="Select" and options=%s)""", (self.name, "link:"+ self.name))
|
||||
|
||||
links = dict(links)
|
||||
|
||||
ret = {}
|
||||
|
||||
for dt in links:
|
||||
ret[dt] = { "fieldname": links[dt] }
|
||||
|
||||
if links:
|
||||
# find out if linked in a child table
|
||||
for parent, options in frappe.db.sql("""select parent, options from tabDocField
|
||||
where fieldtype="Table"
|
||||
and options in (select name from tabDocType
|
||||
where istable=1 and name in (%s))""" % ", ".join(["%s"] * len(links)) ,tuple(links)):
|
||||
|
||||
ret[parent] = {"child_doctype": options, "fieldname": links[options] }
|
||||
if options in ret:
|
||||
del ret[options]
|
||||
|
||||
# find links of parents
|
||||
links = frappe.db.sql("""select dt from `tabCustom Field`
|
||||
where (fieldtype="Table" and options=%s)""", (self.name))
|
||||
links += frappe.db.sql("""select parent from tabDocField
|
||||
where (fieldtype="Table" and options=%s)""", (self.name))
|
||||
|
||||
for dt, in links:
|
||||
if not dt in ret:
|
||||
ret[dt] = {"get_parent": True}
|
||||
|
||||
self.set("__linked_with", ret, as_value=True)
|
||||
|
||||
def load_print_formats(self):
|
||||
print_formats = frappe.db.sql("""select * FROM `tabPrint Format`
|
||||
WHERE doc_type=%s AND docstatus<2 and ifnull(disabled, 0)=0""", (self.name,), as_dict=1,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, unittest
|
||||
|
||||
from frappe.desk.form.utils import get_linked_docs
|
||||
from frappe.desk.form.linked_with import get_linked_docs, get_linked_doctypes
|
||||
|
||||
class TestForm(unittest.TestCase):
|
||||
def test_linked_with(self):
|
||||
results = get_linked_docs("Role", "System Manager")
|
||||
results = get_linked_docs("Role", "System Manager", linkinfo=get_linked_doctypes("Role"))
|
||||
self.assertTrue("User" in results)
|
||||
self.assertTrue("DocType" in results)
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
frappe.connect()
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -105,54 +105,3 @@ def get_next(doctype, value, prev, filters=None, order_by="modified desc"):
|
|||
else:
|
||||
return res[0][0]
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_linked_docs(doctype, name, metadata_loaded=None, no_metadata=False):
|
||||
if not metadata_loaded: metadata_loaded = []
|
||||
meta = frappe.desk.form.meta.get_meta(doctype)
|
||||
linkinfo = meta.get("__linked_with")
|
||||
results = {}
|
||||
|
||||
if not linkinfo:
|
||||
return results
|
||||
|
||||
me = frappe.db.get_value(doctype, name, ["parenttype", "parent"], as_dict=True)
|
||||
for dt, link in linkinfo.items():
|
||||
link["doctype"] = dt
|
||||
link_meta_bundle = frappe.desk.form.load.get_meta_bundle(dt)
|
||||
linkmeta = link_meta_bundle[0]
|
||||
if not linkmeta.get("issingle"):
|
||||
fields = [d.fieldname for d in linkmeta.get("fields", {"in_list_view":1,
|
||||
"fieldtype": ["not in", ["Image", "HTML", "Button", "Table"]]})] \
|
||||
+ ["name", "modified", "docstatus"]
|
||||
|
||||
fields = ["`tab{dt}`.`{fn}`".format(dt=dt, fn=sf.strip()) for sf in fields if sf]
|
||||
|
||||
try:
|
||||
if link.get("get_parent"):
|
||||
if me and me.parent and me.parenttype == dt:
|
||||
ret = frappe.get_list(doctype=dt, fields=fields,
|
||||
filters=[[dt, "name", '=', me.parent]])
|
||||
else:
|
||||
ret = None
|
||||
|
||||
elif link.get("child_doctype"):
|
||||
ret = frappe.get_list(doctype=dt, fields=fields,
|
||||
filters=[[link.get('child_doctype'), link.get("fieldname"), '=', name]])
|
||||
|
||||
else:
|
||||
ret = frappe.get_list(doctype=dt, fields=fields,
|
||||
filters=[[dt, link.get("fieldname"), '=', name]])
|
||||
|
||||
except frappe.PermissionError:
|
||||
if frappe.local.message_log:
|
||||
frappe.local.message_log.pop()
|
||||
|
||||
continue
|
||||
|
||||
if ret:
|
||||
results[dt] = ret
|
||||
|
||||
if not no_metadata and not dt in metadata_loaded:
|
||||
frappe.local.response.docs.extend(link_meta_bundle)
|
||||
|
||||
return results
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class BulkLimitCrossedError(frappe.ValidationError): pass
|
|||
|
||||
def send(recipients=None, sender=None, subject=None, message=None, reference_doctype=None,
|
||||
reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
|
||||
attachments=None, reply_to=None, cc=(), message_id=None, send_after=None,
|
||||
attachments=None, reply_to=None, cc=(), show_as_cc=(), message_id=None, send_after=None,
|
||||
expose_recipients=False, bulk_priority=1):
|
||||
"""Add email to sending queue (Bulk Email)
|
||||
|
||||
|
|
@ -89,6 +89,14 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
|
|||
email_content = email_content.replace("<!--unsubscribe link here-->", unsubscribe_link.html)
|
||||
email_text_context += unsubscribe_link.text
|
||||
|
||||
# show as cc
|
||||
cc_message = ""
|
||||
if email in show_as_cc:
|
||||
cc_message = _("This email was sent to you as CC")
|
||||
|
||||
email_content = email_content.replace("<!-- cc message -->", cc_message)
|
||||
email_text_context = cc_message + "\n" + email_text_context
|
||||
|
||||
# add to queue
|
||||
add(email, sender, subject, email_content, email_text_context, reference_doctype,
|
||||
reference_name, attachments, reply_to, cc, message_id, send_after, bulk_priority)
|
||||
|
|
@ -230,7 +238,7 @@ def flush(from_test=False):
|
|||
frappe.db.sql("""update `tabBulk Email` set status='Expired'
|
||||
where datediff(curdate(), creation) > 3""", auto_commit=auto_commit)
|
||||
|
||||
for i in xrange(100):
|
||||
for i in xrange(500):
|
||||
email = frappe.db.sql("""select * from `tabBulk Email` where
|
||||
status='Not Sent' and ifnull(send_after, "2000-01-01 00:00:00") < %s
|
||||
order by priority desc, creation asc limit 1 for update""", now_datetime(), as_dict=1)
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Status",
|
||||
"label": "Status",
|
||||
"no_copy": 0,
|
||||
"options": "\nNot Sent\nSending\nSent\nError",
|
||||
"permlevel": 0,
|
||||
|
|
@ -215,7 +215,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-29 05:16:31.857121",
|
||||
"modified": "2015-10-02 07:38:40.795371",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Bulk Email",
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Service",
|
||||
"label": "Service",
|
||||
"no_copy": 0,
|
||||
"options": "\nGMail\nYahoo Mail\nOutlook.com",
|
||||
"permlevel": 0,
|
||||
|
|
@ -780,7 +780,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-18 01:34:31.784444",
|
||||
"modified": "2015-10-02 07:38:47.651995",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Email Account",
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "To add dynamic subject, use jinja tags like\n\n<div><pre><code>{{ doc.name }} Delivered</code></pre></div>",
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
|
|
@ -411,7 +412,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-07-09 00:27:00.169741",
|
||||
"modified": "2015-10-16 01:35:51.254775",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Email Alert",
|
||||
|
|
|
|||
|
|
@ -94,7 +94,11 @@ def evaluate_alert(doc, alert, event):
|
|||
if not recipients:
|
||||
return
|
||||
|
||||
frappe.sendmail(recipients=recipients, subject=alert.subject,
|
||||
subject = alert.subject
|
||||
if "{" in subject:
|
||||
subject = frappe.render_template(alert.subject, {"doc": doc, "alert": alert})
|
||||
|
||||
frappe.sendmail(recipients=recipients, subject=subject,
|
||||
message= frappe.render_template(alert.message, {"doc": doc, "alert":alert}),
|
||||
bulk=True, reference_doctype = doc.doctype, reference_name = doc.name,
|
||||
attachments = [frappe.attach_print(doc.doctype, doc.name)] if alert.attach_print else None)
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2014-07-11 17:54:53.298536",
|
||||
"modified": "2015-10-02 07:38:48.185785",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Email Alert Recipient",
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-08-05 06:02:12.805282",
|
||||
"modified": "2015-10-02 07:38:48.744583",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Email Unsubscribe",
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-07 15:51:26",
|
||||
"modified": "2015-10-02 07:39:13.407883",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Standard Reply",
|
||||
|
|
|
|||
12
frappe/email/doctype/standard_reply/test_standard_reply.py
Normal file
12
frappe/email/doctype/standard_reply/test_standard_reply.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Standard Reply')
|
||||
|
||||
class TestStandardReply(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -77,6 +77,7 @@ def get_default_outgoing_email_account(raise_exception_not_set=True):
|
|||
"sender": frappe.conf.get("auto_email_id", "notifications@example.com")
|
||||
})
|
||||
email_account.from_site_config = True
|
||||
email_account.name = frappe.conf.get("email_sender_name") or "Frappe"
|
||||
|
||||
if not email_account and not raise_exception_not_set:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ class MandatoryError(ValidationError): pass
|
|||
class InvalidSignatureError(ValidationError): pass
|
||||
class RateLimitExceededError(ValidationError): pass
|
||||
class CannotChangeConstantError(ValidationError): pass
|
||||
class CharacterLengthExceededError(ValidationError): pass
|
||||
class UpdateAfterSubmitError(ValidationError): pass
|
||||
class LinkValidationError(ValidationError): pass
|
||||
class CancelledLinkError(LinkValidationError): pass
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-07 15:51:26",
|
||||
"modified": "2015-10-02 07:38:42.770807",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Geo",
|
||||
"name": "Country",
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Number Format",
|
||||
"label": "Number Format",
|
||||
"no_copy": 0,
|
||||
"options": "\n#,###.##\n#.###,##\n# ###.##\n# ###,##\n#'###.##\n#, ###.##\n#,##,###.##\n#,###.###\n#.###\n#,###",
|
||||
"permlevel": 0,
|
||||
|
|
@ -153,7 +153,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-14 03:17:04.837607",
|
||||
"modified": "2015-10-02 07:38:42.863135",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Geo",
|
||||
"name": "Currency",
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ to ERPNext.
|
|||
"""
|
||||
|
||||
app_icon = "octicon octicon-circuit-board"
|
||||
app_version = "6.4.9"
|
||||
app_version = "6.5.0"
|
||||
app_color = "orange"
|
||||
github_link = "https://github.com/frappe/frappe"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from frappe.model import default_fields
|
|||
from frappe.model.naming import set_new_name
|
||||
from frappe.modules import load_doctype_module
|
||||
from frappe.model import display_fieldtypes
|
||||
from frappe.model.db_schema import type_map, varchar_len
|
||||
|
||||
_classes = {}
|
||||
|
||||
|
|
@ -444,6 +445,25 @@ class BaseDocument(object):
|
|||
frappe.throw(_("Value cannot be changed for {0}").format(self.meta.get_label(fieldname)),
|
||||
frappe.CannotChangeConstantError)
|
||||
|
||||
def _validate_length(self):
|
||||
if frappe.flags.in_install:
|
||||
return
|
||||
|
||||
for fieldname, value in self.get_valid_dict().iteritems():
|
||||
df = self.meta.get_field(fieldname)
|
||||
if df and df.fieldtype in type_map and type_map[df.fieldtype][0]=="varchar":
|
||||
max_length = cint(df.get("length")) or cint(varchar_len)
|
||||
|
||||
if len(cstr(value)) > max_length:
|
||||
if self.parentfield and self.idx:
|
||||
reference = _("{0}, Row {1}").format(_(self.doctype), self.idx)
|
||||
|
||||
else:
|
||||
reference = "{0} {1}".format(_(self.doctype), self.name)
|
||||
|
||||
frappe.throw(_("{0}: '{1}' will get truncated, as max characters allowed is {2}")\
|
||||
.format(reference, _(df.label), max_length), frappe.CharacterLengthExceededError)
|
||||
|
||||
def _validate_update_after_submit(self):
|
||||
# get the full doc with children
|
||||
db_values = frappe.get_doc(self.doctype, self.name).as_dict()
|
||||
|
|
|
|||
|
|
@ -13,9 +13,13 @@ import os
|
|||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cstr, cint
|
||||
import MySQLdb
|
||||
|
||||
class InvalidColumnName(frappe.ValidationError): pass
|
||||
|
||||
varchar_len = '140'
|
||||
standard_varchar_columns = ('name', 'owner', 'modified_by', 'parent', 'parentfield', 'parenttype')
|
||||
|
||||
type_map = {
|
||||
'Currency': ('decimal', '18,6')
|
||||
,'Int': ('int', '11')
|
||||
|
|
@ -30,14 +34,14 @@ type_map = {
|
|||
,'Datetime': ('datetime', '6')
|
||||
,'Time': ('time', '6')
|
||||
,'Text': ('text', '')
|
||||
,'Data': ('varchar', '255')
|
||||
,'Link': ('varchar', '255')
|
||||
,'Dynamic Link':('varchar', '255')
|
||||
,'Password': ('varchar', '255')
|
||||
,'Select': ('varchar', '255')
|
||||
,'Read Only': ('varchar', '255')
|
||||
,'Attach': ('varchar', '255')
|
||||
,'Attach Image':('varchar', '255')
|
||||
,'Data': ('varchar', varchar_len)
|
||||
,'Link': ('varchar', varchar_len)
|
||||
,'Dynamic Link':('varchar', varchar_len)
|
||||
,'Password': ('varchar', varchar_len)
|
||||
,'Select': ('varchar', varchar_len)
|
||||
,'Read Only': ('varchar', varchar_len)
|
||||
,'Attach': ('text', '')
|
||||
,'Attach Image':('text', '')
|
||||
}
|
||||
|
||||
default_columns = ['name', 'creation', 'modified', 'modified_by', 'owner',
|
||||
|
|
@ -57,8 +61,10 @@ def updatedb(dt):
|
|||
raise Exception, 'Wrong doctype "%s" in updatedb' % dt
|
||||
|
||||
if not res[0][0]:
|
||||
frappe.db.commit()
|
||||
tab = DbTable(dt, 'tab')
|
||||
tab.validate()
|
||||
|
||||
frappe.db.commit()
|
||||
tab.sync()
|
||||
frappe.db.begin()
|
||||
|
||||
|
|
@ -80,12 +86,57 @@ class DbTable:
|
|||
# load
|
||||
self.get_columns_from_docfields()
|
||||
|
||||
def validate(self):
|
||||
"""Check if change in varchar length isn't truncating the columns"""
|
||||
if self.is_new():
|
||||
return
|
||||
|
||||
self.get_columns_from_db()
|
||||
|
||||
columns = [frappe._dict({"fieldname": f, "fieldtype": "Data"}) for f in standard_varchar_columns]
|
||||
columns += self.columns.values()
|
||||
|
||||
for col in columns:
|
||||
if col.fieldtype in type_map and type_map[col.fieldtype][0]=="varchar":
|
||||
|
||||
# validate length range
|
||||
new_length = cint(col.length) or cint(varchar_len)
|
||||
if not (1 <= new_length <= 255):
|
||||
frappe.throw(_("Length of {0} should be between 1 and 255").format(col.fieldname))
|
||||
|
||||
try:
|
||||
# check for truncation
|
||||
max_length = frappe.db.sql("""select max(length(`{fieldname}`)) from `tab{doctype}`"""\
|
||||
.format(fieldname=col.fieldname, doctype=self.doctype))
|
||||
|
||||
except MySQLdb.OperationalError, e:
|
||||
if e.args[0]==1054:
|
||||
# Unknown column 'column_name' in 'field list'
|
||||
continue
|
||||
|
||||
else:
|
||||
raise
|
||||
|
||||
if max_length and max_length[0][0] > new_length:
|
||||
current_type = self.current_columns[col.fieldname]["type"]
|
||||
current_length = re.findall('varchar\(([\d]+)\)', current_type)[0]
|
||||
|
||||
if col.fieldname in self.columns:
|
||||
self.columns[col.fieldname].length = current_length
|
||||
|
||||
frappe.msgprint(_("Reverting length to {0} for '{1}' in '{2}'; Setting the length as {3} will cause truncation of data.")\
|
||||
.format(current_length, col.fieldname, self.doctype, new_length))
|
||||
|
||||
|
||||
def sync(self):
|
||||
if not self.name in DbManager(frappe.db).get_tables_list(frappe.db.cur_db_name):
|
||||
if self.is_new():
|
||||
self.create()
|
||||
else:
|
||||
self.alter()
|
||||
|
||||
def is_new(self):
|
||||
return self.name not in DbManager(frappe.db).get_tables_list(frappe.db.cur_db_name)
|
||||
|
||||
def create(self):
|
||||
add_text = ''
|
||||
|
||||
|
|
@ -99,21 +150,21 @@ class DbTable:
|
|||
|
||||
# create table
|
||||
frappe.db.sql("""create table `%s` (
|
||||
name varchar(255) not null primary key,
|
||||
name varchar({varchar_len}) not null primary key,
|
||||
creation datetime(6),
|
||||
modified datetime(6),
|
||||
modified_by varchar(255),
|
||||
owner varchar(255),
|
||||
modified_by varchar({varchar_len}),
|
||||
owner varchar({varchar_len}),
|
||||
docstatus int(1) default '0',
|
||||
parent varchar(255),
|
||||
parentfield varchar(255),
|
||||
parenttype varchar(255),
|
||||
parent varchar({varchar_len}),
|
||||
parentfield varchar({varchar_len}),
|
||||
parenttype varchar({varchar_len}),
|
||||
idx int(8),
|
||||
%sindex parent(parent))
|
||||
ENGINE=InnoDB
|
||||
ROW_FORMAT=COMPRESSED
|
||||
CHARACTER SET=utf8mb4
|
||||
COLLATE=utf8mb4_unicode_ci""" % (self.name, add_text))
|
||||
COLLATE=utf8mb4_unicode_ci""".format(varchar_len=varchar_len) % (self.name, add_text))
|
||||
|
||||
def get_column_definitions(self):
|
||||
column_list = [] + default_columns
|
||||
|
|
@ -139,6 +190,7 @@ class DbTable:
|
|||
get columns from docfields and custom fields
|
||||
"""
|
||||
fl = frappe.db.sql("SELECT * FROM tabDocField WHERE parent = %s", self.doctype, as_dict = 1)
|
||||
lengths = {}
|
||||
precisions = {}
|
||||
uniques = {}
|
||||
|
||||
|
|
@ -148,19 +200,26 @@ class DbTable:
|
|||
WHERE dt = %s AND docstatus < 2""", (self.doctype,), as_dict=1)
|
||||
if custom_fl: fl += custom_fl
|
||||
|
||||
# get precision from property setters
|
||||
for ps in frappe.get_all("Property Setter", fields=["field_name", "value"],
|
||||
filters={"doc_type": self.doctype, "doctype_or_field": "DocField", "property": "precision"}):
|
||||
precisions[ps.field_name] = ps.value
|
||||
# apply length, precision and unique from property setters
|
||||
for ps in frappe.get_all("Property Setter", fields=["field_name", "property", "value"],
|
||||
filters={
|
||||
"doc_type": self.doctype,
|
||||
"doctype_or_field": "DocField",
|
||||
"property": ["in", ["precision", "length", "unique"]]
|
||||
}):
|
||||
|
||||
# apply unique from property setters
|
||||
for ps in frappe.get_all("Property Setter", fields=["field_name", "value"],
|
||||
filters={"doc_type": self.doctype, "doctype_or_field": "DocField", "property": "unique"}):
|
||||
if ps.property=="length":
|
||||
lengths[ps.field_name] = cint(ps.value)
|
||||
|
||||
elif ps.property=="precision":
|
||||
precisions[ps.field_name] = cint(ps.value)
|
||||
|
||||
elif ps.property=="unique":
|
||||
uniques[ps.field_name] = cint(ps.value)
|
||||
|
||||
for f in fl:
|
||||
self.columns[f['fieldname']] = DbColumn(self, f['fieldname'],
|
||||
f['fieldtype'], f.get('length'), f.get('default'), f.get('search_index'),
|
||||
f['fieldtype'], lengths.get(f["fieldname"]) or f.get('length'), f.get('default'), f.get('search_index'),
|
||||
f.get('options'), uniques.get(f["fieldname"], f.get('unique')), precisions.get(f['fieldname']) or f.get('precision'))
|
||||
|
||||
def get_columns_from_db(self):
|
||||
|
|
@ -201,7 +260,6 @@ class DbTable:
|
|||
frappe.db.sql("set foreign_key_checks=1")
|
||||
|
||||
def alter(self):
|
||||
self.get_columns_from_db()
|
||||
for col in self.columns.values():
|
||||
col.build_for_alter_table(self.current_columns.get(col.fieldname, None))
|
||||
|
||||
|
|
@ -268,7 +326,7 @@ class DbColumn:
|
|||
self.precision = precision
|
||||
|
||||
def get_definition(self, with_default=1):
|
||||
column_def = get_definition(self.fieldtype, self.precision)
|
||||
column_def = get_definition(self.fieldtype, precision=self.precision, length=self.length)
|
||||
|
||||
if not column_def:
|
||||
return column_def
|
||||
|
|
@ -287,7 +345,7 @@ class DbColumn:
|
|||
return column_def
|
||||
|
||||
def build_for_alter_table(self, current_def):
|
||||
column_def = get_definition(self.fieldtype)
|
||||
column_def = get_definition(self.fieldtype, self.precision, self.length)
|
||||
|
||||
# no columns
|
||||
if not column_def:
|
||||
|
|
@ -471,20 +529,28 @@ def remove_all_foreign_keys():
|
|||
for f in fklist:
|
||||
frappe.db.sql("alter table `tab%s` drop foreign key `%s`" % (t[0], f[1]))
|
||||
|
||||
def get_definition(fieldtype, precision=None):
|
||||
def get_definition(fieldtype, precision=None, length=None):
|
||||
d = type_map.get(fieldtype)
|
||||
|
||||
if not d:
|
||||
return
|
||||
|
||||
ret = d[0]
|
||||
coltype = d[0]
|
||||
size = None
|
||||
if d[1]:
|
||||
length = d[1]
|
||||
if fieldtype in ["Float", "Currency", "Percent"] and cint(precision) > 6:
|
||||
length = '18,9'
|
||||
ret += '(' + length + ')'
|
||||
size = d[1]
|
||||
|
||||
return ret
|
||||
if size:
|
||||
if fieldtype in ["Float", "Currency", "Percent"] and cint(precision) > 6:
|
||||
size = '18,9'
|
||||
|
||||
if coltype == "varchar" and length:
|
||||
size = length
|
||||
|
||||
if size is not None:
|
||||
coltype = "{coltype}({size})".format(coltype=coltype, size=size)
|
||||
|
||||
return coltype
|
||||
|
||||
def add_column(doctype, column_name, fieldtype, precision=None):
|
||||
frappe.db.commit()
|
||||
|
|
|
|||
|
|
@ -337,11 +337,13 @@ class Document(BaseDocument):
|
|||
self._validate_links()
|
||||
self._validate_selects()
|
||||
self._validate_constants()
|
||||
self._validate_length()
|
||||
|
||||
children = self.get_all_children()
|
||||
for d in children:
|
||||
d._validate_selects()
|
||||
d._validate_constants()
|
||||
d._validate_length()
|
||||
|
||||
# extract images after validations to save processing if some validation error is raised
|
||||
self._extract_images_from_text_editor()
|
||||
|
|
@ -616,11 +618,34 @@ class Document(BaseDocument):
|
|||
elif self._action=="update_after_submit":
|
||||
self.run_method("on_update_after_submit")
|
||||
|
||||
frappe.cache().hdel("last_modified", self.doctype)
|
||||
self.clear_cache()
|
||||
self.notify_update()
|
||||
|
||||
self.latest = None
|
||||
|
||||
def clear_cache(self):
|
||||
frappe.cache().hdel("last_modified", self.doctype)
|
||||
self.clear_linked_with_cache()
|
||||
|
||||
def clear_linked_with_cache(self):
|
||||
cache = frappe.cache()
|
||||
def _clear_cache(d):
|
||||
for df in (d.meta.get_link_fields() + d.meta.get_dynamic_link_fields()):
|
||||
if d.get(df.fieldname):
|
||||
doctype = df.options if df.fieldtype=="Link" else d.get(df.options)
|
||||
name = d.get(df.fieldname)
|
||||
|
||||
if df.fieldtype=="Dynamic Link":
|
||||
# clear linked doctypes list
|
||||
cache.hdel("linked_doctypes", doctype)
|
||||
|
||||
# delete linked with cache for all users
|
||||
cache.delete_value("user:*:linked_with:{doctype}:{name}".format(doctype=doctype, name=name))
|
||||
|
||||
_clear_cache(self)
|
||||
for d in self.get_all_children():
|
||||
_clear_cache(d)
|
||||
|
||||
def notify_update(self):
|
||||
"""Publish realtime that the current document is modified"""
|
||||
frappe.publish_realtime("doc_update", {"modified": self.modified, "doctype": self.doctype, "name": self.name},
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ class Meta(Document):
|
|||
def get_link_fields(self):
|
||||
return self.get("fields", {"fieldtype": "Link", "options":["!=", "[Select]"]})
|
||||
|
||||
def get_dynamic_link_fields(self):
|
||||
return self.get("fields", {"fieldtype": "Dynamic Link"})
|
||||
|
||||
def get_select_fields(self):
|
||||
return self.get("fields", {"fieldtype": "Select", "options":["not in",
|
||||
["[Select]", "Loading..."]]})
|
||||
|
|
@ -329,14 +332,19 @@ def trim_tables():
|
|||
frappe.db.sql_ddl(query)
|
||||
|
||||
def clear_cache(doctype=None):
|
||||
frappe.cache().delete_value("is_table")
|
||||
frappe.cache().delete_value("doctype_modules")
|
||||
cache = frappe.cache()
|
||||
|
||||
groups = ["meta", "form_meta", "table_columns", "last_modified"]
|
||||
cache.delete_value("is_table")
|
||||
cache.delete_value("doctype_modules")
|
||||
|
||||
groups = ["meta", "form_meta", "table_columns", "last_modified", "linked_doctypes"]
|
||||
|
||||
def clear_single(dt):
|
||||
for name in groups:
|
||||
frappe.cache().hdel(name, dt)
|
||||
cache.hdel(name, dt)
|
||||
|
||||
# also clear linked_with list cache
|
||||
cache.delete_keys("user:*:linked_with:{doctype}:".format(doctype=doctype))
|
||||
|
||||
if doctype:
|
||||
clear_single(doctype)
|
||||
|
|
@ -353,5 +361,5 @@ def clear_cache(doctype=None):
|
|||
else:
|
||||
# clear all
|
||||
for name in groups:
|
||||
frappe.cache().delete_value(name)
|
||||
cache.delete_value(name)
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ def make_autoname(key, doctype=''):
|
|||
DE/09/01/0001 where 09 is the year, 01 is the month and 0001 is the series
|
||||
"""
|
||||
if key=="hash":
|
||||
return frappe.generate_hash(doctype)[:10]
|
||||
return frappe.generate_hash(doctype, 10)
|
||||
|
||||
if not "#" in key:
|
||||
key = key + ".#####"
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ execute:frappe.db.sql("""update `tabPatch Log` set patch=replace(patch, '.4_0.',
|
|||
frappe.patches.v5_0.convert_to_barracuda_and_utf8mb4
|
||||
frappe.patches.v6_1.rename_file_data
|
||||
execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2014-01-24
|
||||
execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2015-08-20
|
||||
execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2015-10-16
|
||||
execute:frappe.reload_doc('core', 'doctype', 'docperm') #2014-06-24
|
||||
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') #2014-06-03
|
||||
execute:frappe.reload_doc('core', 'doctype', 'version') #2014-02-21
|
||||
|
|
@ -94,3 +95,5 @@ 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")
|
||||
frappe.patches.v6_4.reduce_varchar_length
|
||||
frappe.patches.v6_4.rename_bengali_language
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.translate import rename_language
|
||||
|
||||
def execute():
|
||||
language_map = {
|
||||
|
|
@ -8,11 +9,5 @@ def execute():
|
|||
"中國(繁體)": "正體中文"
|
||||
}
|
||||
|
||||
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 })
|
||||
rename_language(old_name, new_name)
|
||||
|
|
|
|||
0
frappe/patches/v6_4/__init__.py
Normal file
0
frappe/patches/v6_4/__init__.py
Normal file
33
frappe/patches/v6_4/reduce_varchar_length.py
Normal file
33
frappe/patches/v6_4/reduce_varchar_length.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
for doctype in frappe.get_all("DocType", filters={"issingle": 0}):
|
||||
doctype = doctype.name
|
||||
|
||||
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(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 max_length > 140:
|
||||
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)
|
||||
7
frappe/patches/v6_4/rename_bengali_language.py
Normal file
7
frappe/patches/v6_4/rename_bengali_language.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.translate import rename_language
|
||||
|
||||
def execute():
|
||||
rename_language("বাঙালি", "বাংলা")
|
||||
|
|
@ -116,7 +116,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 3,
|
||||
"modified": "2015-09-07 15:51:26",
|
||||
"modified": "2015-10-02 07:38:56.001216",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Print",
|
||||
"name": "Letter Head",
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 1,
|
||||
"in_list_view": 0,
|
||||
"label": "Standard",
|
||||
"label": "Standard",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "standard",
|
||||
"oldfieldtype": "Select",
|
||||
|
|
@ -153,7 +153,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Print Format Type",
|
||||
"label": "Print Format Type",
|
||||
"no_copy": 0,
|
||||
"options": "Server\nClient",
|
||||
"permlevel": 0,
|
||||
|
|
@ -268,7 +268,7 @@
|
|||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Font",
|
||||
"label": "Font",
|
||||
"no_copy": 0,
|
||||
"options": "Default\nArial\nHelvetica\nVerdana\nMonospace",
|
||||
"permlevel": 0,
|
||||
|
|
@ -447,7 +447,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2015-09-09 05:46:11.025962",
|
||||
"modified": "2015-10-02 07:39:00.918464",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Print",
|
||||
"name": "Print Format",
|
||||
|
|
|
|||
|
|
@ -353,6 +353,11 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({
|
|||
.addClass("input-with-feedback form-control")
|
||||
.prependTo(this.input_area)
|
||||
|
||||
if (in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'],
|
||||
this.df.fieldtype)) {
|
||||
this.$input.attr("maxlength", this.df.length || 140);
|
||||
}
|
||||
|
||||
this.set_input_attributes();
|
||||
this.input = this.$input.get(0);
|
||||
this.has_input = true;
|
||||
|
|
|
|||
|
|
@ -12,20 +12,13 @@ frappe.ui.form.LinkedWith = Class.extend({
|
|||
if(!this.dialog)
|
||||
this.make_dialog();
|
||||
|
||||
this.dialog.fields_dict.list.$wrapper.html('<div class="text-muted text-center">'
|
||||
+ __("Loading") + '...</div>');
|
||||
|
||||
this.dialog.show();
|
||||
},
|
||||
make_dialog: function() {
|
||||
var me = this;
|
||||
this.linked_with = this.frm.meta.__linked_with;
|
||||
|
||||
var links = [];
|
||||
$.each(this.linked_with, function(doctype, tmp) {
|
||||
if(frappe.model.can_get_report(doctype)) {
|
||||
links.push({label: __(doctype), value: doctype});
|
||||
}
|
||||
});
|
||||
|
||||
links = frappe.utils.sort(links, "label");
|
||||
|
||||
this.dialog = new frappe.ui.Dialog({
|
||||
hide_on_page_refresh: true,
|
||||
|
|
@ -37,49 +30,116 @@ frappe.ui.form.LinkedWith = Class.extend({
|
|||
|
||||
this.dialog.$wrapper.find(".modal-dialog").addClass("linked-with-dialog");
|
||||
|
||||
if(!links) {
|
||||
this.dialog.fields_dict.list.$wrapper.html("<div class='alert alert-warning'>"
|
||||
+ this.frm.doctype + ": "
|
||||
+ (this.linked_with ? __("Not Linked to any record.") : __("Not enough permission to see links."))
|
||||
+ "</div>")
|
||||
return;
|
||||
}
|
||||
|
||||
this.dialog.on_page_show = function() {
|
||||
me.dialog.fields_dict.list.$wrapper.html('<div class="text-muted text-center">'
|
||||
+ __("Loading") + '...</div>');
|
||||
|
||||
frappe.call({
|
||||
method:"frappe.desk.form.utils.get_linked_docs",
|
||||
args: {
|
||||
doctype: me.frm.doctype,
|
||||
name: me.frm.docname,
|
||||
metadata_loaded: keys(locals.DocType)
|
||||
},
|
||||
callback: function(r) {
|
||||
var parent = me.dialog.fields_dict.list.$wrapper.empty();
|
||||
|
||||
if(keys(r.message || {}).length) {
|
||||
$.each(keys(r.message).sort(), function(i, doctype) {
|
||||
var listview = frappe.views.get_listview(doctype, me);
|
||||
listview.no_delete = true;
|
||||
|
||||
var wrapper = $('<div class="panel panel-default"><div>').appendTo(parent);
|
||||
$('<div class="panel-heading">').html(__(doctype).bold()).appendTo(wrapper);
|
||||
var body = $('<div class="panel-body">').appendTo(wrapper);
|
||||
|
||||
$.each(r.message[doctype], function(i, d) {
|
||||
d.doctype = doctype;
|
||||
listview.render($('<div class="list-row"></div>')
|
||||
.appendTo(body), d, me);
|
||||
})
|
||||
})
|
||||
} else {
|
||||
parent.html(__("Not Linked to any record."));
|
||||
// execute ajax calls sequentially
|
||||
// 1. get linked doctypes
|
||||
// 2. load all doctypes
|
||||
// 3. load linked docs
|
||||
$.when(me.get_linked_doctypes())
|
||||
.then(function() { return me.load_doctypes() })
|
||||
.then(function() {
|
||||
if (me.links_not_permitted_or_missing()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return me.get_linked_docs();
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
load_doctypes: function() {
|
||||
var me = this;
|
||||
var already_loaded = Object.keys(locals.DocType);
|
||||
var doctypes_to_load = [];
|
||||
$.each(Object.keys(me.frm.__linked_doctypes), function(i, v) {
|
||||
if (already_loaded.indexOf(v)===-1) {
|
||||
doctypes_to_load.push(v);
|
||||
}
|
||||
});
|
||||
|
||||
// load all doctypes sequentially using with_doctype
|
||||
return $.when.apply($, $.map(doctypes_to_load, function(dt) {
|
||||
return frappe.model.with_doctype(dt, function() {
|
||||
if (frappe.listview_settings[dt]) {
|
||||
// add additional fields to __linked_doctypes
|
||||
me.frm.__linked_doctypes[dt].add_fields = frappe.listview_settings[dt].add_fields;
|
||||
}
|
||||
});
|
||||
}));
|
||||
},
|
||||
|
||||
links_not_permitted_or_missing: function() {
|
||||
var me = this;
|
||||
var links = [];
|
||||
$.each(me.frm.__linked_doctypes, function(doctype, tmp) {
|
||||
if(frappe.model.can_get_report(doctype)) {
|
||||
links.push({label: __(doctype), value: doctype});
|
||||
}
|
||||
});
|
||||
|
||||
links = frappe.utils.sort(links, "label");
|
||||
|
||||
if(!links) {
|
||||
me.dialog.fields_dict.list.$wrapper.html("<div class='alert alert-warning'>"
|
||||
+ me.frm.doctype + ": "
|
||||
+ (me.frm.__linked_doctypes ? __("Not Linked to any record.") : __("Not enough permission to see links."))
|
||||
+ "</div>")
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
get_linked_doctypes: function() {
|
||||
var me = this;
|
||||
if (this.frm.__linked_doctypes) {
|
||||
return;
|
||||
}
|
||||
|
||||
return frappe.call({
|
||||
method: "frappe.desk.form.linked_with.get_linked_doctypes",
|
||||
args: {
|
||||
doctype: this.frm.doctype
|
||||
},
|
||||
callback: function(r) {
|
||||
me.frm.__linked_doctypes = r.message;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
get_linked_docs: function() {
|
||||
var me = this;
|
||||
|
||||
return frappe.call({
|
||||
method:"frappe.desk.form.linked_with.get_linked_docs",
|
||||
args: {
|
||||
doctype: me.frm.doctype,
|
||||
name: me.frm.docname,
|
||||
linkinfo: me.frm.__linked_doctypes
|
||||
},
|
||||
callback: function(r) {
|
||||
var parent = me.dialog.fields_dict.list.$wrapper.empty();
|
||||
|
||||
if(keys(r.message || {}).length) {
|
||||
$.each(keys(r.message).sort(), function(i, doctype) {
|
||||
var listview = frappe.views.get_listview(doctype, me);
|
||||
listview.no_delete = true;
|
||||
|
||||
var wrapper = $('<div class="panel panel-default"><div>').appendTo(parent);
|
||||
$('<div class="panel-heading">').html(__(doctype).bold()).appendTo(wrapper);
|
||||
var body = $('<div class="panel-body">').appendTo(wrapper);
|
||||
|
||||
$.each(r.message[doctype], function(i, d) {
|
||||
d.doctype = doctype;
|
||||
listview.render($('<div class="list-row"></div>')
|
||||
.appendTo(body), d, me);
|
||||
})
|
||||
})
|
||||
} else {
|
||||
parent.html(__("Not Linked to any record."));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ $.extend(frappe.model, {
|
|||
|
||||
with_doctype: function(doctype, callback) {
|
||||
if(locals.DocType[doctype]) {
|
||||
callback();
|
||||
callback && callback();
|
||||
} else {
|
||||
var cached_timestamp = null;
|
||||
if(localStorage["_doctype:" + doctype]) {
|
||||
|
|
@ -112,7 +112,7 @@ $.extend(frappe.model, {
|
|||
}
|
||||
frappe.model.init_doctype(doctype);
|
||||
frappe.defaults.set_user_permissions(r.user_permissions);
|
||||
callback(r);
|
||||
callback && callback(r);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ frappe.ui.FilterList = Class.extend({
|
|||
|
||||
var filter = new frappe.ui.Filter({
|
||||
flist: this,
|
||||
doctype: doctype,
|
||||
_doctype: doctype,
|
||||
fieldname: fieldname,
|
||||
condition: condition,
|
||||
value: value,
|
||||
|
|
@ -168,7 +168,7 @@ frappe.ui.Filter = Class.extend({
|
|||
// set the field
|
||||
if(me.fieldname) {
|
||||
// pre-sets given (could be via tags!)
|
||||
this.set_values(me.doctype, me.fieldname, me.condition, me.value);
|
||||
this.set_values(me._doctype, me.fieldname, me.condition, me.value);
|
||||
} else {
|
||||
me.set_field(me.doctype, 'name');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,6 +190,11 @@ frappe.search.verbs = [
|
|||
|
||||
// doctype list
|
||||
function(txt) {
|
||||
if (txt.toLowerCase().indexOf(" list")) {
|
||||
// remove list keyword
|
||||
txt = txt.replace(/ list/ig, "").trim();
|
||||
}
|
||||
|
||||
frappe.search.find(frappe.boot.user.can_read, txt, function(match) {
|
||||
if(in_list(frappe.boot.single_types, match)) {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ frappe.views.CommunicationComposer = Class.extend({
|
|||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
if(form_values.send_email && r.message["emails_not_sent_to"]) {
|
||||
msgprint( __("Email not sent to {0}",
|
||||
msgprint( __("Email not sent to {0} (unsubscribed / disabled)",
|
||||
[ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ]) );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ frappe.views.Gantt = frappe.views.CalendarBase.extend({
|
|||
gantt_area.gantt({
|
||||
source: me.get_source(r),
|
||||
navigate: "scroll",
|
||||
scale: "days",
|
||||
scale: me.gantt_scale || "days",
|
||||
minScale: "hours",
|
||||
maxScale: "months",
|
||||
itemsPerPage: 20,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ def clear_cache(user=None):
|
|||
cache = frappe.cache()
|
||||
|
||||
groups = ("bootinfo", "user_recent", "user_roles", "user_doc", "lang",
|
||||
"defaults", "user_permissions", "roles", "home_page")
|
||||
"defaults", "user_permissions", "roles", "home_page", "linked_with")
|
||||
|
||||
if user:
|
||||
for name in groups:
|
||||
|
|
|
|||
|
|
@ -186,10 +186,13 @@ def run_async_task(self, site=None, user=None, cmd=None, form_dict=None, hijack_
|
|||
|
||||
@celery_task()
|
||||
def sendmail(site, communication_name, print_html=None, print_format=None, attachments=None,
|
||||
recipients=None, cc=None):
|
||||
recipients=None, cc=None, lang=None):
|
||||
try:
|
||||
frappe.connect(site=site)
|
||||
|
||||
if lang:
|
||||
frappe.local.lang = lang
|
||||
|
||||
# upto 3 retries
|
||||
for i in xrange(3):
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
<body style="line-height: 1.5; color: #36414C;">
|
||||
<!-- body -->
|
||||
<div style="font-family: Helvetica, Arial, sans-serif; font-size: 14px; padding: 10px;">
|
||||
<em><!-- cc message --></em>
|
||||
{{ content }}
|
||||
{{ signature }}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -150,3 +150,9 @@ class TestDocument(unittest.TestCase):
|
|||
d.load_from_db()
|
||||
d.starts_on = "2014-01-01"
|
||||
d.validate_update_after_submit()
|
||||
|
||||
def test_varchar_length(self):
|
||||
d = self.test_insert()
|
||||
d.subject = "abcde"*100
|
||||
self.assertRaises(frappe.CharacterLengthExceededError, d.save)
|
||||
|
||||
|
|
|
|||
|
|
@ -529,3 +529,11 @@ def deduplicate_messages(messages):
|
|||
|
||||
def get_bench_dir():
|
||||
return os.path.join(frappe.__file__, '..', '..', '..', '..')
|
||||
|
||||
def rename_language(old_name, new_name):
|
||||
language_in_system_settings = frappe.db.get_single_value("System Settings", "language")
|
||||
if language_in_system_settings == old_name:
|
||||
frappe.db.set_value("System Settings", "System Settings", "language", new_name)
|
||||
|
||||
frappe.db.sql("""update `tabUser` set language=%(new_name)s where language=%(old_name)s""",
|
||||
{ "old_name": old_name, "new_name": new_name })
|
||||
|
|
@ -513,6 +513,9 @@ def get_url(uri=None, full_address=False):
|
|||
"""get app url from request"""
|
||||
host_name = frappe.local.conf.host_name
|
||||
|
||||
if uri and (uri.startswith("http://") or uri.startswith("https://")):
|
||||
return uri
|
||||
|
||||
if not host_name:
|
||||
if hasattr(frappe.local, "request") and frappe.local.request and frappe.local.request.host:
|
||||
protocol = 'https' == frappe.get_request_header('X-Forwarded-Proto', "") and 'https://' or 'http://'
|
||||
|
|
@ -538,10 +541,16 @@ def get_url(uri=None, full_address=False):
|
|||
def get_host_name():
|
||||
return get_url().rsplit("//", 1)[-1]
|
||||
|
||||
def get_url_to_form(doctype, name, label=None):
|
||||
def get_link_to_form(doctype, name, label=None):
|
||||
if not label: label = name
|
||||
|
||||
return """<a href="/desk#!Form/%(doctype)s/%(name)s">%(label)s</a>""" % locals()
|
||||
return """<a href="{0}">{1}</a>""".format(get_url_to_form(doctype, name), label)
|
||||
|
||||
def get_url_to_form(doctype, name):
|
||||
return get_url(uri = "desk/#Form/{0}/{1}".format(doctype, name))
|
||||
|
||||
def get_url_to_list(doctype):
|
||||
return get_url(uri = "desk/#List/{0}".format(doctype))
|
||||
|
||||
operator_map = {
|
||||
# startswith
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from frappe.utils import get_site_path, get_hook_method, get_files_path, random_
|
|||
from frappe import _
|
||||
from frappe import conf
|
||||
from copy import copy
|
||||
import urllib
|
||||
|
||||
class MaxFileSizeReachedError(frappe.ValidationError): pass
|
||||
|
||||
|
|
@ -59,6 +60,8 @@ def save_url(file_url, dt, dn, folder):
|
|||
# frappe.msgprint("URL must start with 'http://' or 'https://'")
|
||||
# return None, None
|
||||
|
||||
file_url = urllib.unquote(file_url)
|
||||
|
||||
f = frappe.get_doc({
|
||||
"doctype": "File",
|
||||
"file_url": file_url,
|
||||
|
|
@ -241,22 +244,30 @@ def remove_file(fid, attached_to_doctype=None, attached_to_name=None):
|
|||
|
||||
return comment
|
||||
|
||||
def delete_file_data_content(doc):
|
||||
def delete_file_data_content(doc, only_thumbnail=False):
|
||||
method = get_hook_method('delete_file_data_content', fallback=delete_file_from_filesystem)
|
||||
method(doc)
|
||||
method(doc, only_thumbnail=only_thumbnail)
|
||||
|
||||
def delete_file_from_filesystem(doc):
|
||||
path = doc.file_name
|
||||
|
||||
if path.startswith("files/"):
|
||||
path = frappe.utils.get_site_path("public", doc.file_name)
|
||||
def delete_file_from_filesystem(doc, only_thumbnail=False):
|
||||
"""Delete file, thumbnail from File document"""
|
||||
if only_thumbnail:
|
||||
delete_file(doc.thumbnail_url)
|
||||
else:
|
||||
path = frappe.utils.get_site_path("public", "files", doc.file_name)
|
||||
delete_file(doc.file_url)
|
||||
delete_file(doc.thumbnail_url)
|
||||
|
||||
path = encode(path)
|
||||
def delete_file(path):
|
||||
"""Delete file from `public folder`"""
|
||||
if path and path.startswith("/files/"):
|
||||
parts = os.path.split(path)
|
||||
path = frappe.utils.get_site_path("public", "files", parts[-1])
|
||||
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
if "/../" in path:
|
||||
frappe.msgprint(_("It is risky to delete this file: {0}. Please contact your System Manager.").format(path))
|
||||
|
||||
path = encode(path)
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
|
||||
def get_file(fname):
|
||||
f = frappe.db.sql("""select file_name from `tabFile`
|
||||
|
|
|
|||
|
|
@ -147,4 +147,3 @@ class RedisWrapper(redis.Redis):
|
|||
return super(redis.Redis, self).hkeys(self.make_key(name))
|
||||
except redis.exceptions.ConnectionError:
|
||||
return []
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-02-19 09:29:46.804175",
|
||||
"modified": "2015-10-19 03:04:52.537466",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "About Us Team Member",
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@
|
|||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-11 12:20:05.555186",
|
||||
"modified": "2015-10-02 07:38:39.540340",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Blog Category",
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue