From fec4b37e1ac7d307badafe9a9221f739fbb37362 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 5 May 2014 15:15:52 +0530 Subject: [PATCH 01/98] minor website --- frappe/templates/includes/blog.js | 4 +++- frappe/templates/pages/contact.html | 11 ++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/frappe/templates/includes/blog.js b/frappe/templates/includes/blog.js index f70203d1f3..de1eb250ec 100644 --- a/frappe/templates/includes/blog.js +++ b/frappe/templates/includes/blog.js @@ -46,6 +46,8 @@ var blog = { b.avatar = "/" + b.avatar; } + b.month = b.month.toUpperCase().substr(0,3); + $(repl('
\
\

%(day)s

\ @@ -61,7 +63,7 @@ var blog = {

%(content)s

\

\ \ - %(full_name)s wrote this on %(published)s / %(comment_text)s

\ + %(full_name)s / %(comment_text)s

\
\

', b)).appendTo($wrap); }); diff --git a/frappe/templates/pages/contact.html b/frappe/templates/pages/contact.html index 9df1b84337..6b09f0d3f2 100644 --- a/frappe/templates/pages/contact.html +++ b/frappe/templates/pages/contact.html @@ -1,15 +1,17 @@ {% block title %}{{ heading or "Contact Us"}}{% endblock %} {% block content %} +
-

+

-
@@ -53,9 +55,8 @@
{% endif %}
- {{ introduction or ""}}
-{% endblock %} \ No newline at end of file +{% endblock %} From 49de172a6dbd7214110314aa5698a8abdbb3fed0 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 5 May 2014 15:59:08 +0530 Subject: [PATCH 02/98] website context bugfix --- frappe/website/context.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/frappe/website/context.py b/frappe/website/context.py index 554a4edcd9..db1e011b7e 100644 --- a/frappe/website/context.py +++ b/frappe/website/context.py @@ -16,6 +16,12 @@ def get_context(path): context = None cache_key = "page_context:{}".format(path) + def add_data_path(context): + if not context.data: + context.data = {} + + context.data["path"] = path + # try from memcache if can_cache(): context = frappe.cache().get_value(cache_key) @@ -27,16 +33,15 @@ def get_context(path): context["access"] = get_access(context.pathname) context = build_context(context) + add_data_path(context) if can_cache(context.no_cache): frappe.cache().set_value(cache_key, context) else: context["access"] = get_access(context.pathname) + add_data_path(context) - if not context.data: - context.data = {} - context.data["path"] = path context.update(context.data or {}) return context From 98ad3c41b4e96a4dd1a138fdc578dc8e99f5de9c Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 5 May 2014 16:40:40 +0530 Subject: [PATCH 03/98] Fixes in Print, Test Cases, Permissions --- .../doctype/communication/communication.py | 2 +- .../page/user_properties/user_properties.py | 36 +++++++++---------- frappe/permissions.py | 3 +- frappe/templates/pages/website_script.js | 2 +- .../website/doctype/blog_post/blog_post.json | 6 ++-- .../doctype/blog_post/test_blog_post.py | 2 +- 6 files changed, 26 insertions(+), 25 deletions(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 182ad60b00..ada190ca58 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -118,7 +118,7 @@ def send_comm_email(d, name, sent_via=None, print_html=None, attachments='[]', s footer = set_portal_link(sent_via, d) send_print_in_body = frappe.db.get_value("Outgoing Email Settings", None, "send_print_in_body_and_attachment") - if not send_print_in_body: + if print_html and not send_print_in_body: d.content += "

Please see attachment for document details.

" mail = get_email(d.recipients, sender=d.sender, subject=d.subject, diff --git a/frappe/core/page/user_properties/user_properties.py b/frappe/core/page/user_properties/user_properties.py index 47f58cec35..303329dc62 100644 --- a/frappe/core/page/user_properties/user_properties.py +++ b/frappe/core/page/user_properties/user_properties.py @@ -18,17 +18,17 @@ def get_users_and_links(): def get_properties(parent=None, defkey=None, defvalue=None): if defkey and not frappe.permissions.can_restrict(defkey, defvalue): raise frappe.PermissionError - + conditions, values = _build_conditions(locals()) - - properties = frappe.db.sql("""select name, parent, defkey, defvalue + + properties = frappe.db.sql("""select name, parent, defkey, defvalue from tabDefaultValue where parent not in ('__default', '__global') and substr(defkey,1,1)!='_' and parenttype='Restriction' {conditions} order by parent, defkey""".format(conditions=conditions), values, as_dict=True) - + if not defkey: out = [] doctypes = get_restrictable_doctypes() @@ -36,9 +36,9 @@ def get_properties(parent=None, defkey=None, defvalue=None): if p.defkey in doctypes: out.append(p) properties = out - + return properties - + def _build_conditions(filters): conditions = [] values = {} @@ -46,7 +46,7 @@ def _build_conditions(filters): if filters.get(key): conditions.append("and `{key}`=%({key})s".format(key=key)) values[key] = value - + return "\n".join(conditions), values @frappe.whitelist() @@ -54,35 +54,35 @@ def remove(user, name, defkey, defvalue): if not frappe.permissions.can_restrict_user(user, defkey, defvalue): raise frappe.PermissionError("Cannot Remove Restriction for User: {user} on DocType: {doctype} and Name: {name}".format( user=user, doctype=defkey, name=defvalue)) - - frappe.defaults.clear_default(name=name) - + + frappe.defaults.clear_default(key=defkey, value=defvalue, parent=user, name=name) + def clear_restrictions(doctype): frappe.defaults.clear_default(parenttype="Restriction", key=doctype) - + @frappe.whitelist() def add(user, defkey, defvalue): if not frappe.permissions.can_restrict_user(user, defkey, defvalue): raise frappe.PermissionError("Cannot Restrict User: {user} for DocType: {doctype} and Name: {name}".format( user=user, doctype=defkey, name=defvalue)) - + # check if already exists - d = frappe.db.sql("""select name from tabDefaultValue + d = frappe.db.sql("""select name from tabDefaultValue where parent=%s and parenttype='Restriction' and defkey=%s and defvalue=%s""", (user, defkey, defvalue)) - + if not d: frappe.defaults.add_default(defkey, defvalue, user, "Restriction") - + def get_restrictable_doctypes(): user_roles = frappe.get_roles() condition = "" values = [] if "System Manager" not in user_roles: - condition = """and exists(select `tabDocPerm`.name from `tabDocPerm` + condition = """and exists(select `tabDocPerm`.name from `tabDocPerm` where `tabDocPerm`.parent=`tabDocType`.name and `tabDocPerm`.`restrict`=1 and `tabDocPerm`.role in ({roles}))""".format(roles=", ".join(["%s"]*len(user_roles))) values = user_roles - - return frappe.db.sql_list("""select name from tabDocType + + return frappe.db.sql_list("""select name from tabDocType where ifnull(issingle,0)=0 and ifnull(istable,0)=0 {condition}""".format(condition=condition), tuple(values)) diff --git a/frappe/permissions.py b/frappe/permissions.py index 0663e11f8f..466318cd4f 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -71,11 +71,12 @@ def has_unrestricted_access(doc, verbose=True): restrictions = get_restrictions() meta = frappe.get_meta(doc.get("doctype")) + user_perms = get_user_perms(meta) if get_user_perms(meta).restricted: if doc.owner == frappe.session.user: # owner is always allowed for restricted permissions return True - elif not restrictions: + elif not (restrictions and restrictions.get(doc.get("doctype"))): return False else: if not restrictions: diff --git a/frappe/templates/pages/website_script.js b/frappe/templates/pages/website_script.js index 63a4634248..3b84bdd084 100644 --- a/frappe/templates/pages/website_script.js +++ b/frappe/templates/pages/website_script.js @@ -10,4 +10,4 @@ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) ga('create', '{{ google_analytics_id }}', 'auto'); ga('send', 'pageview'); -{%- endif %} \ No newline at end of file +{%- endif %} diff --git a/frappe/website/doctype/blog_post/blog_post.json b/frappe/website/doctype/blog_post/blog_post.json index 22b4cc68cd..269e5427df 100644 --- a/frappe/website/doctype/blog_post/blog_post.json +++ b/frappe/website/doctype/blog_post/blog_post.json @@ -1,7 +1,7 @@ { "allow_attach": 1, "allow_import": 1, - "creation": "2013-03-28 10:35:30.000000", + "creation": "2013-03-28 10:35:30", "docstatus": 0, "doctype": "DocType", "fields": [ @@ -95,7 +95,7 @@ "icon": "icon-quote-left", "idx": 1, "max_attachments": 5, - "modified": "2014-02-20 12:55:07.000000", + "modified": "2014-05-05 04:00:24.210166", "modified_by": "Administrator", "module": "Website", "name": "Blog Post", @@ -104,7 +104,7 @@ { "cancel": 0, "create": 1, - "delete": 0, + "delete": 1, "email": 1, "permlevel": 0, "print": 1, diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index ceda432045..8d692d1e8c 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -105,7 +105,7 @@ class TestBlogPost(unittest.TestCase): self.assertRaises(frappe.PermissionError, add, "test2@example.com", "Blog Post", "_test-blog-post") - def test_not_allowed_to_restrict(self): + def test_not_allowed_on_restrict(self): self.add_restriction_to_user2() frappe.set_user("test2@example.com") From 90b637e8447a9777736817a7937ea5cfad2280ee Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 5 May 2014 17:18:58 +0530 Subject: [PATCH 04/98] Fixed Selenium Tests --- frappe/cli.py | 5 +++-- frappe/tests/test_client_login.py | 8 ++++---- frappe/utils/sel.py | 10 +++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/frappe/cli.py b/frappe/cli.py index 1cda01b2fa..f58e557938 100755 --- a/frappe/cli.py +++ b/frappe/cli.py @@ -155,6 +155,7 @@ def setup_test(parser): parser.add_argument("--tests", metavar="TEST FUNCTION", nargs="*", help="Run one or more specific test functions") parser.add_argument("--serve_test", action="store_true", help="Run development server for testing") + parser.add_argument("--driver", nargs="?", help="Run selenium using given driver") def setup_utilities(parser): @@ -705,11 +706,11 @@ def smtp_debug_server(): os.execv(python, [python, '-m', "smtpd", "-n", "-c", "DebuggingServer", "localhost:25"]) @cmd -def run_tests(app=None, module=None, doctype=None, verbose=False, tests=()): +def run_tests(app=None, module=None, doctype=None, verbose=False, tests=(), driver=None): import frappe.test_runner from frappe.utils import sel - sel.start(verbose) + sel.start(verbose, driver) ret = 1 try: diff --git a/frappe/tests/test_client_login.py b/frappe/tests/test_client_login.py index 13fa82777c..b8517938e7 100644 --- a/frappe/tests/test_client_login.py +++ b/frappe/tests/test_client_login.py @@ -6,16 +6,16 @@ from frappe.utils import sel class TestLogin(unittest.TestCase): def setUp(self): - sel.login(sel.get_localhost()) + sel.login() def test_login(self): - self.assertEquals(sel.driver.current_url, sel.get_localhost() + "/desk") + self.assertEquals(sel._driver.current_url, sel.get_localhost() + "/desk") def test_to_do(self): - sel.module("ToDo") + sel.go_to_module("ToDo") sel.find('.appframe-iconbar .icon-plus')[0].click() sel.wait_for_page("Form/ToDo") - sel.set_field("description", "test description") + sel.set_field("description", "test description", "textarea") sel.primary_action() self.assertTrue(sel.wait_for_state("clean")) diff --git a/frappe/utils/sel.py b/frappe/utils/sel.py index edb03d9a18..9783561569 100644 --- a/frappe/utils/sel.py +++ b/frappe/utils/sel.py @@ -27,11 +27,11 @@ input_wait = 0 def get_localhost(): return "{host}:{port}".format(host=host, port=port) -def start(verbose=None, driver="PhantomJS"): +def start(verbose=None, driver=None): global _driver, _verbose _verbose = verbose - _driver = getattr(webdriver, driver)() + _driver = getattr(webdriver, driver or "PhantomJS")() signal.signal(signal.SIGINT, signal_handler) @@ -55,7 +55,7 @@ def login(wait_for_id="#page-desktop"): global logged_in if logged_in: return - get(host + ":" + port + "/login") + get(get_localhost() + "/login") wait("#login_email") set_input("#login_email", "Administrator") set_input("#login_password", "admin" + Keys.RETURN) @@ -102,8 +102,8 @@ def find(selector, everywhere=False): selector = cur_route + " " + selector return _driver.find_elements_by_css_selector(selector) -def set_field(fieldname, value): - set_input('input[data-fieldname="{0}"]'.format(fieldname), value + Keys.TAB) +def set_field(fieldname, value, fieldtype="input"): + set_input('{0}[data-fieldname="{1}"]'.format(fieldtype, fieldname), value + Keys.TAB) wait_for_ajax() def set_select(fieldname, value): From 484191754a72bbec8f1f29e093ba40a20a9e57d5 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 5 May 2014 17:51:38 +0530 Subject: [PATCH 05/98] Whitelisting of controller methods --- frappe/api.py | 5 +++-- frappe/model/document.py | 13 +++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frappe/api.py b/frappe/api.py index b29874104e..cfa525eff6 100644 --- a/frappe/api.py +++ b/frappe/api.py @@ -44,11 +44,12 @@ def handle(): elif call=="resource": if "run_method" in frappe.local.form_dict: doc = frappe.get_doc(doctype, name) + doc.is_whitelisted(frappe.local.form_dict.run_method) if frappe.local.request.method=="GET": if not doc.has_permission("read"): frappe.throw(_("Not permitted"), frappe.PermissionError) - doc.run_method(frappe.local.form_dict.run_method, **frappe.local.form_dict) + doc.run_method(frappe.local.form_dict.run_method, **frappe.local.form_dict) if frappe.local.request.method=="POST": if not doc.has_permission("write"): @@ -69,7 +70,7 @@ def handle(): doc = frappe.get_doc(doctype, name) # Not checking permissions here because it's checked in doc.save doc.update(data) - frappe.local.response.update({ + frappe.local.response.update({ "data": doc.save().as_dict() }) frappe.db.commit() diff --git a/frappe/model/document.py b/frappe/model/document.py index 6118f02d46..1f88dbd568 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -8,6 +8,7 @@ from frappe.utils import flt, cint, cstr, now from frappe.modules import load_doctype_module from frappe.model.base_document import BaseDocument from frappe.model.naming import set_new_name +from werkzeug.exceptions import NotFound, Forbidden # once_only validation # methods @@ -400,6 +401,11 @@ class Document(BaseDocument): elif self._action=="update_after_submit": self.run_method("on_update_after_submit") + @staticmethod + def whitelist(f): + f.whitelisted = True + return f + @staticmethod def hook(f): def add_to_return_value(self, new_return_value): @@ -433,6 +439,13 @@ class Document(BaseDocument): return composer + def is_whitelisted(self, method): + fn = getattr(self, method, None) + if not fn: + raise NotFound("Method {0} not found".format(method)) + elif not getattr(fn, "whitelisted", False): + raise Forbidden("Method {0} not whitelisted".format(method)) + def validate_value(self, fieldname, condition, val2, doc=None, raise_exception=None): """check that value of fieldname should be 'condition' val2 else throw exception""" From a8e24afadea9567b0e2bd852470997094dcc4f6b Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 5 May 2014 18:25:01 +0530 Subject: [PATCH 06/98] Reload ignore_doctypes if force=True in import_doc --- frappe/modules/import_file.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/modules/import_file.py b/frappe/modules/import_file.py index 9c3a90ddae..ca905d6fa1 100644 --- a/frappe/modules/import_file.py +++ b/frappe/modules/import_file.py @@ -47,7 +47,7 @@ def import_file_by_path(path, force=False): original_modified = doc.get("modified") - import_doc(doc) + import_doc(doc, force=force) if original_modified: # since there is a new timestamp on the file, update timestamp in @@ -78,7 +78,7 @@ ignore_values = { ignore_doctypes = ["Page Role", "DocPerm"] -def import_doc(docdict): +def import_doc(docdict, force=False): docdict["__islocal"] = 1 doc = frappe.get_doc(docdict) @@ -87,14 +87,14 @@ def import_doc(docdict): if frappe.db.exists(doc.doctype, doc.name): old_doc = frappe.get_doc(doc.doctype, doc.name) - if doc.doctype in ignore_values: + if doc.doctype in ignore_values and not force: # update ignore values for key in ignore_values.get(doc.doctype) or []: doc.set(key, old_doc.get(key)) # update ignored docs into new doc for df in doc.meta.get_table_fields(): - if df.options in ignore_doctypes: + if df.options in ignore_doctypes and not force: doc.set(df.fieldname, []) ignore.append(df.options) From e153f32cd33783f6ff965b7045a9b7e9fb780292 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 5 May 2014 18:34:04 +0530 Subject: [PATCH 07/98] Do not allow installing hidden apps from applications page --- frappe/core/page/applications/applications.js | 8 ++++---- frappe/core/page/applications/applications.py | 14 ++++++++++++-- frappe/installer.py | 1 - 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/frappe/core/page/applications/applications.js b/frappe/core/page/applications/applications.js index ce0a0700a2..14c5941900 100644 --- a/frappe/core/page/applications/applications.js +++ b/frappe/core/page/applications/applications.js @@ -1,4 +1,4 @@ -frappe.pages['applications'].onload = function(wrapper) { +frappe.pages['applications'].onload = function(wrapper) { frappe.ui.make_app_page({ parent: wrapper, title: __('Application Installer'), @@ -10,7 +10,7 @@ frappe.pages['applications'].onload = function(wrapper) { method:"frappe.core.page.applications.applications.get_app_list", callback: function(r) { var $main = $(wrapper).find(".layout-main"); - + if(!keys(r.message).length) { $main.html('
No Apps Installed
'); return; @@ -54,7 +54,7 @@ frappe.pages['applications'].onload = function(wrapper) {
\ ', app)) $app.appendTo($main) - + if(app.installed) { $btn = $(''); @@ -63,7 +63,7 @@ frappe.pages['applications'].onload = function(wrapper) { .attr("data-app", app.app_name) .on("click", function() { frappe.call({ - method:"frappe.installer.install_app", + method:"frappe.core.page.applications.applications.install_app", args: {name: $(this).attr("data-app")}, callback: function(r) { if(!r.exc) { diff --git a/frappe/core/page/applications/applications.py b/frappe/core/page/applications/applications.py index ade393014c..0162bf4530 100644 --- a/frappe/core/page/applications/applications.py +++ b/frappe/core/page/applications/applications.py @@ -3,6 +3,8 @@ from __future__ import unicode_literals import frappe +import frappe.installer +from frappe import _ @frappe.whitelist() def get_app_list(): @@ -19,8 +21,16 @@ def get_app_list(): "app_publisher", "app_version", "app_url", "app_color"): val = app_hooks.get(key) or [] out[app][key] = val[0] if len(val) else "" - + if app in installed: out[app]["installed"] = 1 - + return out + +@frappe.whitelist() +def install_app(name): + app_hooks = frappe.get_hooks(app_name=name) + if app_hooks.get('hide_in_installer'): + frappe.throw(_("You cannot install this app")) + + frappe.installer.install_app(name) diff --git a/frappe/installer.py b/frappe/installer.py index b5054e3191..d6332bd3aa 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -81,7 +81,6 @@ def make_connection(root_login, root_password): root_password = getpass.getpass("MySQL root password: ") return frappe.database.Database(user=root_login, password=root_password) -@frappe.whitelist() def install_app(name, verbose=False, set_as_patched=True): frappe.flags.in_install_app = name frappe.clear_cache() From 5d4741b4e2a8a0e6543191ae9f05bc55606b62e9 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Mon, 5 May 2014 19:11:57 +0530 Subject: [PATCH 08/98] Release 4.0.0 --- frappe/__init__.py | 2 ++ setup.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 826a0caf5b..8d9eed74a3 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -16,6 +16,8 @@ import json from .exceptions import * +__version__ = "4.0.0" + local = Local() class _dict(dict): diff --git a/setup.py b/setup.py index c69c5c2e3e..f7c96349ac 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages import os -version = '4.0.0-beta' +version = '4.0.0' with open("requirements.txt", "r") as f: install_requires = f.readlines() @@ -21,4 +21,4 @@ setup( 'frappe = frappe.cli:main' ] } -) \ No newline at end of file +) From d93615dd3e0eed557cda319d15f31347442f9ba6 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 6 May 2014 11:48:20 +0530 Subject: [PATCH 09/98] Module view: use route if specified. Fixes frappe/erpnext#1584 --- frappe/public/js/frappe/views/moduleview.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/frappe/public/js/frappe/views/moduleview.js b/frappe/public/js/frappe/views/moduleview.js index 316de269e3..b5f3f3d218 100644 --- a/frappe/public/js/frappe/views/moduleview.js +++ b/frappe/public/js/frappe/views/moduleview.js @@ -216,15 +216,17 @@ frappe.views.moduleview.ModuleView = Class.extend({ }); } else { var route = item.route; - if(item.type==="doctype") { - route = "List/" + encodeURIComponent(item.name); - } else if(item.type==="page") { - route = item.route || item.link || item.name; - } else if(item.type==="report") { - if(item.is_query_report) { - route = "query-report/" + encodeURIComponent(item.name); - } else { - route = "Report/" + encodeURIComponent(item.doctype) + "/" + encodeURIComponent(item.name); + if(!route) { + if(item.type==="doctype") { + route = "List/" + encodeURIComponent(item.name); + } else if(item.type==="page") { + route = item.route || item.link || item.name; + } else if(item.type==="report") { + if(item.is_query_report) { + route = "query-report/" + encodeURIComponent(item.name); + } else { + route = "Report/" + encodeURIComponent(item.doctype) + "/" + encodeURIComponent(item.name); + } } } From e62d6bfcb3fcaf90a1b2818f340cd0e677de4ed6 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 6 May 2014 12:56:29 +0530 Subject: [PATCH 10/98] PJax - Reload first page when visited using browser's back button --- frappe/website/js/website.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/frappe/website/js/website.js b/frappe/website/js/website.js index 2b2f3cd695..48b28c4038 100644 --- a/frappe/website/js/website.js +++ b/frappe/website/js/website.js @@ -189,7 +189,6 @@ $.extend(frappe, { if(frappe.supports_pjax()) { // hack for chrome's onload popstate call window.initial_href = window.location.href - $(document).on("click", "#wrap a", frappe.handle_click); $(window).on("popstate", function(event) { @@ -201,12 +200,12 @@ $.extend(frappe, { window.previous_href = location.href; var state = event.originalEvent.state; - - if(state) { - frappe.render_json(state); - } else { + if(!state) { console.log("state not found!"); + frappe.set_force_reload(true); + state = window.history.state; } + frappe.render_json(state); }); } }, From 430698207b172e6bebafa48d900ef9e913f7879f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 6 May 2014 14:03:35 +0530 Subject: [PATCH 11/98] Cannada translation --- frappe/data/languages.txt | 3 +- frappe/public/js/frappe/views/grid_report.js | 176 +-- frappe/translations/kn.csv | 1360 ++++++++++++++++++ 3 files changed, 1450 insertions(+), 89 deletions(-) create mode 100644 frappe/translations/kn.csv diff --git a/frappe/data/languages.txt b/frappe/data/languages.txt index a2938d24af..27ff92b3fb 100644 --- a/frappe/data/languages.txt +++ b/frappe/data/languages.txt @@ -7,6 +7,7 @@ fr français hi हिंदी hr hrvatski it italiano +kn ಕನ್ನಡ nl nederlands pt-BR português brasileiro pt português @@ -14,4 +15,4 @@ sr српски ta தமிழ் th ไทย zh-cn 中国(简体 -zh-tw 中國(繁體 \ No newline at end of file +zh-tw 中國(繁體 diff --git a/frappe/public/js/frappe/views/grid_report.js b/frappe/public/js/frappe/views/grid_report.js index d3ef7b70e4..3dc7314cd0 100644 --- a/frappe/public/js/frappe/views/grid_report.js +++ b/frappe/public/js/frappe/views/grid_report.js @@ -20,7 +20,7 @@ $.extend(frappe.report_dump, { $.each(r.message, function(doctype, doctype_data) { frappe.report_dump.set_data(doctype, doctype_data); }); - + // reverse map names $.each(r.message, function(doctype, doctype_data) { // only if not pre-loaded @@ -38,7 +38,7 @@ $.extend(frappe.report_dump, { } } }); - + callback(); }, progress_bar: progress_bar @@ -62,7 +62,7 @@ $.extend(frappe.report_dump, { var row = make_row(d); replace_dict[row.name] = row; }); - + // replace old data $.each(frappe.report_dump.data[doctype], function(i, d) { if(replace_dict[d.name]) { @@ -75,13 +75,13 @@ $.extend(frappe.report_dump, { data.push(d); } }); - + // add new records $.each(replace_dict, function(name, d) { data.push(d); }) } else { - + // first loading $.each(doctype_data.data, function(i, d) { data.push(make_row(d)); @@ -96,20 +96,20 @@ frappe.provide("frappe.views"); frappe.views.GridReport = Class.extend({ init: function(opts) { frappe.require("assets/js/slickgrid.min.js"); - + this.filter_inputs = {}; this.preset_checks = []; this.tree_grid = {show: false}; var me = this; $.extend(this, opts); - + this.wrapper = $('
').appendTo(this.parent); - + if(this.filters) { this.make_filters(); } this.make_waiting(); - + this.get_data_and_refresh(); }, bind_show: function() { @@ -118,14 +118,14 @@ frappe.views.GridReport = Class.extend({ // this must be called after init // because "frappe.container.page" will only be set // once "load" event is over. - + var me = this; $(this.page).bind('show', function() { // reapply filters on show frappe.cur_grid_report = me; me.get_data_and_refresh(); }); - + }, get_data_and_refresh: function() { var me = this; @@ -139,7 +139,7 @@ frappe.views.GridReport = Class.extend({ var progress_bar = null; if(!this.setup_filters_done) progress_bar = this.wrapper.find(".progress .progress-bar"); - + frappe.report_dump.with_data(this.doctypes, function() { if(!me.setup_filters_done) { me.setup_filters(); @@ -160,28 +160,28 @@ frappe.views.GridReport = Class.extend({ function(d) { return d.name; }); me.set_autocomplete(v, opts.list); } - }); + }); // refresh - this.filter_inputs.refresh && this.filter_inputs.refresh.click(function() { + this.filter_inputs.refresh && this.filter_inputs.refresh.click(function() { me.get_data(function() { me.refresh(); }); }); - + // reset filters - this.filter_inputs.reset_filters && this.filter_inputs.reset_filters.click(function() { - me.init_filter_values(); + this.filter_inputs.reset_filters && this.filter_inputs.reset_filters.click(function() { + me.init_filter_values(); me.refresh(); }); - + // range this.filter_inputs.range && this.filter_inputs.range.on("change", function() { me.refresh(); }); - + // plot check - if(this.setup_plot_check) + if(this.setup_plot_check) this.setup_plot_check(); }, set_filter: function(key, value) { @@ -223,7 +223,7 @@ frappe.views.GridReport = Class.extend({ filter.val(""); } }); - + this.set_default_values(); }, @@ -232,7 +232,7 @@ frappe.views.GridReport = Class.extend({ from_date: dateutil.str_to_user(sys_defaults.year_start_date), to_date: dateutil.str_to_user(sys_defaults.year_end_date) } - + var me = this; $.each(values, function(i, v) { if(me.filter_inputs[i] && !me.filter_inputs[i].val()) @@ -281,7 +281,7 @@ frappe.views.GridReport = Class.extend({ }); }, make_waiting: function() { - this.waiting = frappe.messages.waiting(this.wrapper, __("Loading Report")+"...", '10'); + this.waiting = frappe.messages.waiting(this.wrapper, __("Loading Report")+"...", '10'); }, load_filter_values: function() { var me = this; @@ -298,12 +298,12 @@ frappe.views.GridReport = Class.extend({ } } }); - + if(this.filter_inputs.from_date && this.filter_inputs.to_date && (this.to_date < this.from_date)) { msgprint(__("From Date must be before To Date")); return; } - + }, make_name_map: function(data, key) { @@ -314,7 +314,7 @@ frappe.views.GridReport = Class.extend({ }) return map; }, - + reset_item_values: function(item) { var me = this; $.each(this.columns, function(i, col) { @@ -323,7 +323,7 @@ frappe.views.GridReport = Class.extend({ } }); }, - + round_item_values: function(item) { var me = this; $.each(this.columns, function(i, col) { @@ -332,17 +332,17 @@ frappe.views.GridReport = Class.extend({ } }); }, - + round_off_data: function() { var me = this; $.each(this.data, function(i, d) { me.round_item_values(d); }); }, - + refresh: function() { this.waiting.toggle(false); - if(!this.grid_wrapper) + if(!this.grid_wrapper) this.make(); this.show_zero = $('.show-zero input:checked').length; this.load_filter_values(); @@ -361,15 +361,15 @@ frappe.views.GridReport = Class.extend({ setup_dataview_columns: function() { this.dataview_columns = $.map(this.columns, function(col) { return !col.hidden ? col : null; - }); + }); }, make: function() { var me = this; - + // plot wrapper this.plot_area = $('').appendTo(this.wrapper); - + // print / export $('
\ ').appendTo(this.wrapper); - + this.wrapper.find(".grid-report-export").click(function() { return me.export(); }); - + // grid wrapper this.grid_wrapper = $("
") @@ -392,10 +392,10 @@ frappe.views.GridReport = Class.extend({ +'
').appendTo(this.wrapper); this.bind_show(); - + frappe.cur_grid_report = this; $(this.wrapper).trigger('make'); - + }, apply_filters_from_route: function() { var me = this; @@ -432,12 +432,12 @@ frappe.views.GridReport = Class.extend({ me.grid.invalidateRows(args.rows); me.grid.render(); }); - + this.dataView.onRowCountChanged.subscribe(function (e, args) { me.grid.updateRowCount(); me.grid.render(); }); - + this.tree_grid.show && this.add_tree_grid_events(); }, prepare_data_view: function() { @@ -459,13 +459,13 @@ frappe.views.GridReport = Class.extend({ // from all filter_inputs var filters = this.filter_inputs; if(item._show) return true; - + for (i in filters) { if(!this.apply_filter(item, i)) { return false; } } - + return true; }, apply_filter: function(item, fieldname) { @@ -485,11 +485,11 @@ frappe.views.GridReport = Class.extend({ if(col.formatter==me.currency_formatter && !col.hidden) { if(flt(item[col.field]) > 0.001 || flt(item[col.field]) < -0.001) { return true; - } + } } } return false; - } + } return true; }, show_zero_check: function() { @@ -497,7 +497,7 @@ frappe.views.GridReport = Class.extend({ this.wrapper.bind('make', function() { me.wrapper.find('.show-zero').toggle(true).find('input').click(function(){ me.refresh(); - }); + }); }); }, is_default: function(fieldname) { @@ -535,16 +535,16 @@ frappe.views.GridReport = Class.extend({ // link_formatter must have // filter_input, open_btn (true / false), doctype (will be eval'd) if(!value) return ""; - + var me = frappe.cur_grid_report; - + if(dataContext._show) { return repl('%(value)s', { _style: dataContext._style || "", value: value }); } - + // make link to add a filter var link_formatter = me.dataview_columns[cell].link_formatter; if (link_formatter.filter_input) { @@ -562,8 +562,8 @@ frappe.views.GridReport = Class.extend({ // make icon to open form if(link_formatter.open_btn) { - var doctype = link_formatter.doctype - ? eval(link_formatter.doctype) + var doctype = link_formatter.doctype + ? eval(link_formatter.doctype) : dataContext.doctype; html += me.get_link_open_icon(doctype, value); } @@ -576,21 +576,21 @@ frappe.views.GridReport = Class.extend({ return repl(' \ ', { doctype: doctype, - name: encodeURIComponent(name) + name: encodeURIComponent(name) }); }, make_date_range_columns: function() { this.columns = []; - + var me = this; var range = this.filter_inputs.range.val(); this.from_date = dateutil.user_to_str(this.filter_inputs.from_date.val()); this.to_date = dateutil.user_to_str(this.filter_inputs.to_date.val()); var date_diff = dateutil.get_diff(this.to_date, this.from_date); - + me.column_map = {}; me.last_date = null; - + var add_column = function(date) { me.columns.push({ id: date, @@ -600,22 +600,22 @@ frappe.views.GridReport = Class.extend({ width: 100 }); } - + var build_columns = function(condition) { // add column for each date range for(var i=0; i <= date_diff; i++) { var date = dateutil.add_days(me.from_date, i); if(!condition) condition = function() { return true; } - + if(condition(date)) add_column(date); me.last_date = date; - + if(me.columns.length) { me.column_map[date] = me.columns[me.columns.length-1]; } } } - + // make columns for all date ranges if(range=='Daily') { build_columns(); @@ -623,7 +623,7 @@ frappe.views.GridReport = Class.extend({ build_columns(function(date) { if(!me.last_date) return true; return !(dateutil.get_diff(date, me.from_date) % 7) - }); + }); } else if(range=='Monthly') { build_columns(function(date) { if(!me.last_date) return true; @@ -633,7 +633,7 @@ frappe.views.GridReport = Class.extend({ build_columns(function(date) { if(!me.last_date) return true; return dateutil.str_to_obj(date).getDate()==1 && in_list([0,3,6,9], dateutil.str_to_obj(date).getMonth()) - }); + }); } else if(range=='Yearly') { build_columns(function(date) { if(!me.last_date) return true; @@ -641,15 +641,15 @@ frappe.views.GridReport = Class.extend({ return date==v.year_start_date ? true : null; }).length; }); - + } - + // set label as last date of period $.each(this.columns, function(i, col) { col.name = me.columns[i+1] ? dateutil.str_to_user(dateutil.add_days(me.columns[i+1].id, -1)) - : dateutil.str_to_user(me.to_date); - }); + : dateutil.str_to_user(me.to_date); + }); }, trigger_refresh_on_change: function(filters) { var me = this; @@ -670,10 +670,10 @@ frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({ } frappe.require('assets/frappe/js/lib/flot/jquery.flot.js'); frappe.require('assets/frappe/js/lib/flot/jquery.flot.downsample.js'); - + this.plot = $.plot(this.plot_area.toggle(true), plot_data, this.get_plot_options()); - + this.setup_plot_hover(); }, setup_plot_check: function() { @@ -692,7 +692,7 @@ frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({ }); } me.render_plot(); - }); + }); }); }, setup_plot_hover: function() { @@ -718,16 +718,16 @@ frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({ me.previousPoint = item.dataIndex; $("#" + me.tooltip_id).remove(); - showTooltip(item.pageX, item.pageY, + showTooltip(item.pageX, item.pageY, me.get_tooltip_text(item.series.label, item.datapoint[0], item.datapoint[1])); } } else { $("#" + me.tooltip_id).remove(); - me.previousPoint = null; + me.previousPoint = null; } }); - + }, get_tooltip_text: function(label, x, y) { var date = dateutil.obj_to_user(new Date(x)); @@ -749,19 +749,19 @@ frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({ points: {show: true}, lines: {show: true, fill: true}, }); - - // prepend opening - data[data.length-1].data = [[dateutil.str_to_obj(me.from_date).getTime(), + + // prepend opening + data[data.length-1].data = [[dateutil.str_to_obj(me.from_date).getTime(), item.opening]].concat(data[data.length-1].data); } }); - + return data.length ? data : false; }, get_plot_options: function() { return { grid: { hoverable: true, clickable: true }, - xaxis: { mode: "time", + xaxis: { mode: "time", min: dateutil.str_to_obj(this.from_date).getTime(), max: dateutil.str_to_obj(this.to_date).getTime() }, series: { downsample: { threshold: 1000 } } @@ -780,7 +780,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ }); if (!this.tl) this.tl = {}; if (!this.tl[parent_doctype]) this.tl[parent_doctype] = []; - + $.each(frappe.report_dump.data[parent_doctype], function(i, parent) { if(tmap[parent.name]) { $.each(tmap[parent.name], function(i, d) { @@ -811,15 +811,15 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ var me = frappe.cur_grid_report; value = value.replace(/&/g,"&").replace(//g,">"); var data = me.data; - var spacer = ""; var idx = me.dataView.getIdxById(dataContext.id); var link = me.tree_grid.formatter(dataContext); - + if(dataContext.doctype) { - link += me.get_link_open_icon(dataContext.doctype, dataContext.name); + link += me.get_link_open_icon(dataContext.doctype, dataContext.name); } - + if (data[idx + 1] && data[idx + 1].indent > data[idx].indent) { if (dataContext._collapsed) { return spacer + "  " + link; @@ -833,7 +833,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ tree_dataview_filter: function(item) { var me = frappe.cur_grid_report; if(!me.apply_filters(item)) return false; - + var parent = item[me.tree_grid.parent_field]; while (parent) { if (me.item_by_name[parent]._collapsed) { @@ -846,7 +846,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ prepare_tree: function(item_dt, group_dt) { var group_data = frappe.report_dump.data[group_dt]; var item_data = frappe.report_dump.data[item_dt]; - + // prepare map with child in respective group var me = this; var item_group_map = {}; @@ -855,12 +855,12 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ var parent = item[me.tree_grid.parent_field]; if(!item_group_map[parent]) item_group_map[parent] = []; if(group_ids.indexOf(item.name)==-1) { - item_group_map[parent].push(item); + item_group_map[parent].push(item); } else { msgprint(__("Ignoring Item {0}, because a group exists with the same name!", [item.name.bold()])); } }); - + // arrange items besides their parent item groups var items = []; $.each(group_data, function(i, group){ @@ -884,7 +884,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ d.indent = indent; }); }, - + export: function() { var msgbox = msgprint($.format('

{0}

\

{1}

\ @@ -902,7 +902,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ var with_groups = $(msgbox.body).find("[name='with_groups']").prop("checked"); var with_ledgers = $(msgbox.body).find("[name='with_ledgers']").prop("checked"); - var data = frappe.slickgrid_tools.get_view_data(me.columns, me.dataView, + var data = frappe.slickgrid_tools.get_view_data(me.columns, me.dataView, function(row, item) { if(with_groups) { // add row @@ -914,15 +914,15 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ if(with_ledgers && (item.group_or_ledger != "Group" && !item.is_group)) { return true; } - + return false; }); - + frappe.tools.downloadify(data, ["Report Manager", "System Manager"], me); return false; }) return false; }, - + }); diff --git a/frappe/translations/kn.csv b/frappe/translations/kn.csv new file mode 100644 index 0000000000..d501411dc1 --- /dev/null +++ b/frappe/translations/kn.csv @@ -0,0 +1,1360 @@ + by Role , + is not set, +""" does not exists",""" ಅಸ್ತಿತ್ವದಲ್ಲಿಲ್ಲ" +"""Company History""",""" ಕಂಪೆನಿ ಇತಿಹಾಸ """ +"""Team Members"" or ""Management""","""ತಂಡದ ಸದಸ್ಯರು"" ಅಥವಾ "" ನಿರ್ವಹಣೆ """ +'In List View' not allowed for type {0} in row {1},' ListView ರಲ್ಲಿ ' ಅವಕಾಶ ಮಾದರಿ {0} ಸತತವಾಗಿ {1} +'link:' type Select {0} getting replaced,' ಲಿಂಕ್ : ' ಆಯ್ಕೆಮಾಡಿ {0} ಬದಲಿಗೆ ಗೆಟ್ಟಿಂಗ್ +"000 is black, fff is white","000 ಕಪ್ಪು , fff ಬಿಳಿ" +2 days ago,2 ದಿನಗಳ ಹಿಂದೆ +"[?]","ಕವಿದ href=""https://en.wikipedia.org/wiki/Transport_Layer_Security"" target = [ ? ] " +"\ +
  • field:[fieldname] - By Field\ +
  • naming_series: - By Naming Series (field called naming_series must be present\ +
  • Prompt - Prompt user for a name\ +
  • [series] - Series by prefix (separated by a dot); for example PRE.#####\ +')"">Naming Options", +A user can be restricted to multiple records of the same type.,ಬಳಕೆದಾರ ಅದೇ ರೀತಿಯ ಅನೇಕ ದಾಖಲೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ ಮಾಡಬಹುದು . +About,ಕುರಿತು +About Us Settings,ನಮ್ಮ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬಗ್ಗೆ +About Us Team Member,ನಮ್ಮ ತಂಡದ ಸದಸ್ಯರು ಬಗ್ಗೆ +Action,ಕ್ರಿಯೆ +Actions,ಕ್ರಿಯೆಗಳು +"Actions for workflow (e.g. Approve, Cancel).","ಕೆಲಸದೊತ್ತಡದ ಕ್ರಿಯೆಗಳು ( ಇ ಜಿ ಅನುಮೋದಿಸಿ , ರದ್ದು ) ." +Add,ಸೇರಿಸು +Add A New Rule,ಹೊಸ ನಿಯಮ ಸೇರಿಸಿ +Add A Restriction,ನಿರ್ಬಂಧದ ಸೇರಿಸಿ +Add Attachments,ಲಗತ್ತುಗಳನ್ನು ಸೇರಿಸಿ +Add Bookmark,ಸೇರಿಸಿ ಬುಕ್ಮಾರ್ಕ್ +Add CSS,ಸಿಎಸ್ಎಸ್ ಸೇರಿಸಿ +Add Column,ಅಂಕಣ ಸೇರಿಸಿ +Add Google Analytics ID: eg. UA-89XXX57-1. Please search help on Google Analytics for more information.,ಗೂಗಲ್ ಅನಾಲಿಟಿಕ್ಸ್ ID ಸೇರಿಸಿ : ಯುಎ ಉದಾ 89XXX57 - 1 . . ಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ ಗೂಗಲ್ ಅನಾಲಿಟಿಕ್ಸ್ ಸಹಾಯ ಹುಡುಕಲು ದಯವಿಟ್ಟು . +Add Message,ಸಂದೇಶ ಸೇರಿಸಿ +Add New Permission Rule,ಒಂದು ಹೊಸ ಅನುಮತಿ ರೂಲ್ ಸೇರಿಸಿ +Add Reply,ಉತ್ತರಿಸಿ ಸೇರಿಸಿ +Add Serial No,ಸೀರಿಯಲ್ ನಂ ಸೇರಿಸಿ +Add This To User's Restrictions,ಬಳಕೆದಾರರ ನಿರ್ಬಂಧಗಳು ಈ ಸೇರಿಸಿ +Add Total Row,ಒಟ್ಟು ರೋ ಸೇರಿಸಿ +Add a New Role,ಹೊಸ ಪಾತ್ರ ಸೇರಿಸಿ +Add a banner to the site. (small banners are usually good),ಸೈಟ್ ಒಂದು ಬ್ಯಾನರ್ ಸೇರಿಸಿ . ( ಸಣ್ಣ ಬ್ಯಾನರ್ ಸಾಮಾನ್ಯವಾಗಿ ಉತ್ತಮ ) +Add all roles,ಎಲ್ಲಾ ಪಾತ್ರಗಳನ್ನು ಸೇರಿಸಿ +Add attachment,ಲಗತ್ತು ಸೇರಿಸಿ +Add code as <script>, +{% endblock %} diff --git a/frappe/templates/pages/list.py b/frappe/templates/pages/list.py new file mode 100644 index 0000000000..92a10c3421 --- /dev/null +++ b/frappe/templates/pages/list.py @@ -0,0 +1,46 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe, os +from frappe.modules import get_doc_path +from jinja2 import Environment, Template, FileSystemLoader + +no_cache = 1 +no_sitemap = 1 + +def get_context(context): + context.type = frappe.local.form_dict.type + context.txt = frappe.local.form_dict.txt + context.update(get_items(context.type, context.txt)) + return context + +@frappe.whitelist(allow_guest=True) +def get_items(type, txt, limit_start=0): + meta = frappe.get_meta(type) + filters, or_filters = [], [] + out = frappe._dict() + + if txt: + if meta.search_fields: + for f in meta.get_search_fields(): + or_filters.append([type, f.strip(), "like", "%" + txt + "%"]) + else: + filters.append([type, "name", "like", "%" + txt + "%"]) + + + out.raw_items = frappe.get_list(type, fields = meta.get_list_fields(), + filters=filters, or_filters = or_filters, limit_start=limit_start, + limit_page_length = 20) + template_path = os.path.join(get_doc_path(meta.module, "DocType", meta.name), "list_item.html") + + if os.path.exists(template_path): + env = Environment(loader = FileSystemLoader(".")) + template = env.get_template(template_path) + else: + template = Template("""""") + + out.items = [template.render(item=i, doctype=type) for i in out.raw_items] + + return out diff --git a/frappe/templates/pages/message.html b/frappe/templates/pages/message.html index df8f6ac7af..2c70bf8457 100644 --- a/frappe/templates/pages/message.html +++ b/frappe/templates/pages/message.html @@ -7,4 +7,4 @@

    {{ message }}

    -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/frappe/templates/pages/view.html b/frappe/templates/pages/view.html new file mode 100644 index 0000000000..699e1b12e8 --- /dev/null +++ b/frappe/templates/pages/view.html @@ -0,0 +1,40 @@ +{% block title %}{{ doc.doctype }} / {{ doc.name }}{% endblock %} + +{% block header %} +

    {{ doc.name }}

    +

    {{ doc.doctype }} +{% endblock %} + +{% block content %} +{% if custom_view %} +{{ custom_view }} +{% else %} +{% for df in meta.fields %} + {% if not df.hidden and not df.permlevel and not df.print_hide %} + {% if df.fieldtype=="Section Break" %} +

    {{ df.label or "" }}

    + {% elif df.fieldtype=="Column Break" %} + {% elif df.fieldtype=="Table" %} + + {% else %} +
    +
    + {% if df.fieldtype not in ("Image",) %} + + {% endif %} +
    +
    + {% if df.fieldtype=="Check" %} + + {% elif df.fieldtype=="Image" %} + + {% else %} + {{ doc[df.fieldname] }} + {% endif %} +
    +
    + {% endif %} + {% endif %} +{% endfor %} +{% endif %} +{% endblock %} diff --git a/frappe/templates/pages/view.py b/frappe/templates/pages/view.py new file mode 100644 index 0000000000..e6f31d3146 --- /dev/null +++ b/frappe/templates/pages/view.py @@ -0,0 +1,14 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe + +no_cache = 1 +no_sitemap = 1 + +def get_context(context): + return { + "doc": frappe.get_doc(frappe.local.form_dict.doctype, frappe.local.form_dict.name), + "meta": frappe.get_meta(frappe.local.form_dict.doctype) + } diff --git a/frappe/website/js/website.js b/frappe/website/js/website.js index fba7a35934..ee399ca7e0 100644 --- a/frappe/website/js/website.js +++ b/frappe/website/js/website.js @@ -221,6 +221,9 @@ $.extend(frappe, { if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey ) return; + if (link.getAttribute("target")) + return; + // Ignore cross origin links if ( location.protocol !== link.protocol || location.hostname !== link.hostname ) return; diff --git a/frappe/website/render.py b/frappe/website/render.py index c512eb1768..ef16616bfe 100644 --- a/frappe/website/render.py +++ b/frappe/website/render.py @@ -8,7 +8,7 @@ from werkzeug.wrappers import Response from frappe.website.context import get_context from frappe.website.utils import scrub_relative_urls, get_home_page, can_cache -from frappe.website.permissions import get_access, clear_permissions +from frappe.website.permissions import clear_permissions class PageNotFoundError(Exception): pass @@ -18,9 +18,25 @@ def render(path, http_status_code=None): try: data = render_page(path) - except frappe.DoesNotExistError, e: - path = "404" + doctype, name = get_doctype_from_path(path) + if doctype and name: + path = "view" + frappe.local.form_dict.doctype = doctype + frappe.local.form_dict.name = name + elif doctype: + path = "list" + frappe.local.form_dict.type = doctype + else: + path = "404" + http_status_code = e.http_status_code + + data = render_page(path) + + except frappe.PermissionError, e: + path = "message" + frappe.local.message = "Did you log out?" + frappe.local.message_title = "Not Permitted" data = render_page(path) http_status_code = e.http_status_code @@ -31,6 +47,24 @@ def render(path, http_status_code=None): return build_response(path, data, http_status_code or 200) +def get_doctype_from_path(path): + doctypes = [d[0] for d in frappe.get_list("DocType", as_list=True)] + + parts = path.split("/") + + doctype = parts[0] + name = parts[1] if len(parts) > 1 else None + + if doctype in doctypes: + return doctype, name + + # try scrubbed + doctype = doctype.replace("_", " ").title() + if doctype in doctypes: + return doctype, name + + return None, None + def build_response(path, data, http_status_code): # build response response = Response() diff --git a/frappe/widgets/search.py b/frappe/widgets/search.py index 3a4200d7dd..537e263868 100644 --- a/frappe/widgets/search.py +++ b/frappe/widgets/search.py @@ -61,7 +61,7 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0, # build from doctype if txt: if meta.search_fields: - for f in meta.search_fields.split(","): + for f in meta.get_search_fields(): or_filters.append([doctype, f.strip(), "like", "%" + txt + "%"]) else: filters.append([doctype, searchfield or "name", "like", From 040b88e8e991270a86b30b689b763255a30d32b4 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 8 May 2014 09:56:11 +0530 Subject: [PATCH 23/98] updates to pages --- .../customize_form/customize_form.json | 33 +++- .../doctype/customize_form/customize_form.py | 2 + frappe/core/doctype/doctype/doctype.json | 40 ++-- frappe/data/Framework.sql | 180 +++++++++--------- frappe/model/db_query.py | 11 +- frappe/model/meta.py | 5 +- frappe/templates/pages/list.py | 7 +- frappe/templates/pages/view.html | 64 +++++-- 8 files changed, 211 insertions(+), 131 deletions(-) diff --git a/frappe/core/doctype/customize_form/customize_form.json b/frappe/core/doctype/customize_form/customize_form.json index 5db77d1bbf..a95988f208 100644 --- a/frappe/core/doctype/customize_form/customize_form.json +++ b/frappe/core/doctype/customize_form/customize_form.json @@ -1,6 +1,6 @@ { "autoname": "DL.####", - "creation": "2013-01-29 17:55:08.000000", + "creation": "2013-01-29 17:55:08", "docstatus": 0, "doctype": "DocType", "fields": [ @@ -8,6 +8,7 @@ "fieldname": "doc_type", "fieldtype": "Link", "hidden": 0, + "in_list_view": 1, "label": "Enter Form Type", "no_copy": 0, "options": "DocType", @@ -25,23 +26,37 @@ "fieldtype": "Column Break", "permlevel": 0 }, + { + "fieldname": "default_print_format", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Default Print Format", + "no_copy": 0, + "options": "Print Format", + "permlevel": 0, + "search_index": 0 + }, { "description": "Fields separated by comma (,) will be included in the
    Search By list of Search dialog box", "fieldname": "search_fields", "fieldtype": "Data", + "in_list_view": 1, "label": "Search Fields", "no_copy": 0, "permlevel": 0, "search_index": 0 }, { - "fieldname": "default_print_format", - "fieldtype": "Link", - "label": "Default Print Format", - "no_copy": 0, - "options": "Print Format", - "permlevel": 0, - "search_index": 0 + "fieldname": "sort_field", + "fieldtype": "Data", + "label": "Sort Field", + "permlevel": 0 + }, + { + "fieldname": "sort_order", + "fieldtype": "Data", + "label": "Sort Order", + "permlevel": 0 }, { "fieldname": "column_break1", @@ -111,7 +126,7 @@ "icon": "icon-glass", "idx": 1, "issingle": 1, - "modified": "2014-01-15 16:16:22.000000", + "modified": "2014-05-08 09:27:44.167026", "modified_by": "Administrator", "module": "Core", "name": "Customize Form", diff --git a/frappe/core/doctype/customize_form/customize_form.py b/frappe/core/doctype/customize_form/customize_form.py index 0af80da2fd..0e0eefdcfa 100644 --- a/frappe/core/doctype/customize_form/customize_form.py +++ b/frappe/core/doctype/customize_form/customize_form.py @@ -14,6 +14,8 @@ from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype class CustomizeForm(Document): doctype_properties = { 'search_fields': 'Data', + 'sort_field': 'Data', + 'sort_order': 'Data', 'default_print_format': 'Data', 'read_only_onload': 'Check', 'allow_attach': 'Check', diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 9b89f80b18..646600a471 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -138,6 +138,17 @@ "options": "\nTitle Case\nUPPER CASE", "permlevel": 0 }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "permlevel": 0, + "reqd": 0, + "search_index": 0 + }, { "fieldname": "column_break_15", "fieldtype": "Column Break", @@ -162,15 +173,20 @@ "search_index": 0 }, { - "fieldname": "description", - "fieldtype": "Small Text", - "hidden": 0, - "label": "Description", - "oldfieldname": "description", - "oldfieldtype": "Text", - "permlevel": 0, - "reqd": 0, - "search_index": 0 + "default": "modified", + "description": "", + "fieldname": "sort_field", + "fieldtype": "Data", + "label": "Sort Field", + "permlevel": 0 + }, + { + "default": "DESC", + "fieldname": "sort_order", + "fieldtype": "Select", + "label": "Sort Order", + "options": "ASC\nDESC", + "permlevel": 0 }, { "depends_on": "eval:!doc.istable", @@ -329,7 +345,7 @@ "idx": 1, "issingle": 0, "istable": 0, - "modified": "2014-05-01 05:27:22.582492", + "modified": "2014-05-08 09:23:56.952829", "modified_by": "Administrator", "module": "Core", "name": "DocType", @@ -361,5 +377,7 @@ } ], "read_only": 0, - "search_fields": "module" + "search_fields": "module", + "sort_field": "name", + "sort_order": "ASC" } \ No newline at end of file diff --git a/frappe/data/Framework.sql b/frappe/data/Framework.sql index f6055f257c..819b22b4a9 100644 --- a/frappe/data/Framework.sql +++ b/frappe/data/Framework.sql @@ -8,21 +8,21 @@ DROP TABLE IF EXISTS `tabDocField`; CREATE TABLE `tabDocField` ( - `name` varchar(120) NOT NULL, + `name` varchar(255) NOT NULL, `creation` datetime(6) DEFAULT NULL, `modified` datetime(6) DEFAULT NULL, - `modified_by` varchar(40) DEFAULT NULL, - `owner` varchar(40) DEFAULT NULL, + `modified_by` varchar(255) DEFAULT NULL, + `owner` varchar(255) DEFAULT NULL, `docstatus` int(1) DEFAULT '0', - `parent` varchar(120) DEFAULT NULL, - `parentfield` varchar(120) DEFAULT NULL, - `parenttype` varchar(120) DEFAULT NULL, + `parent` varchar(255) DEFAULT NULL, + `parentfield` varchar(255) DEFAULT NULL, + `parenttype` varchar(255) DEFAULT NULL, `idx` int(8) DEFAULT NULL, - `fieldname` varchar(180) DEFAULT NULL, - `label` varchar(180) DEFAULT NULL, - `oldfieldname` varchar(180) DEFAULT NULL, - `fieldtype` varchar(180) DEFAULT NULL, - `oldfieldtype` varchar(180) DEFAULT NULL, + `fieldname` varchar(255) DEFAULT NULL, + `label` varchar(255) DEFAULT NULL, + `oldfieldname` varchar(255) DEFAULT NULL, + `fieldtype` varchar(255) DEFAULT NULL, + `oldfieldtype` varchar(255) DEFAULT NULL, `options` text, `search_index` int(1) DEFAULT NULL, `hidden` int(1) DEFAULT NULL, @@ -32,12 +32,12 @@ CREATE TABLE `tabDocField` ( `reqd` int(1) DEFAULT NULL, `no_copy` int(1) DEFAULT NULL, `allow_on_submit` int(1) DEFAULT NULL, - `trigger` varchar(180) DEFAULT NULL, - `depends_on` varchar(180) DEFAULT NULL, + `trigger` varchar(255) DEFAULT NULL, + `depends_on` varchar(255) DEFAULT NULL, `permlevel` int(11) DEFAULT '0', `ignore_restrictions` int(1) DEFAULT NULL, - `width` varchar(180) DEFAULT NULL, - `print_width` varchar(180) DEFAULT NULL, + `width` varchar(255) DEFAULT NULL, + `print_width` varchar(255) DEFAULT NULL, `default` text, `description` text, `in_filter` int(1) DEFAULT NULL, @@ -57,22 +57,22 @@ CREATE TABLE `tabDocField` ( -- DROP TABLE IF EXISTS `tabDocPerm`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!255101 SET @saved_cs_client = @@character_set_client */; +/*!255101 SET character_set_client = utf8 */; CREATE TABLE `tabDocPerm` ( - `name` varchar(120) NOT NULL, + `name` varchar(255) NOT NULL, `creation` datetime(6) DEFAULT NULL, `modified` datetime(6) DEFAULT NULL, - `modified_by` varchar(40) DEFAULT NULL, - `owner` varchar(40) DEFAULT NULL, + `modified_by` varchar(255) DEFAULT NULL, + `owner` varchar(255) DEFAULT NULL, `docstatus` int(1) DEFAULT '0', - `parent` varchar(120) DEFAULT NULL, - `parentfield` varchar(120) DEFAULT NULL, - `parenttype` varchar(120) DEFAULT NULL, + `parent` varchar(255) DEFAULT NULL, + `parentfield` varchar(255) DEFAULT NULL, + `parenttype` varchar(255) DEFAULT NULL, `idx` int(8) DEFAULT NULL, `permlevel` int(11) DEFAULT '0', - `role` varchar(180) DEFAULT NULL, - `match` varchar(180) DEFAULT NULL, + `role` varchar(255) DEFAULT NULL, + `match` varchar(255) DEFAULT NULL, `read` int(1) DEFAULT NULL, `write` int(1) DEFAULT NULL, `create` int(1) DEFAULT NULL, @@ -89,43 +89,45 @@ CREATE TABLE `tabDocPerm` ( PRIMARY KEY (`name`), KEY `parent` (`parent`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; +/*!255101 SET character_set_client = @saved_cs_client */; -- -- Table structure for table `tabDocType` -- DROP TABLE IF EXISTS `tabDocType`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!255101 SET @saved_cs_client = @@character_set_client */; +/*!255101 SET character_set_client = utf8 */; CREATE TABLE `tabDocType` ( - `name` varchar(180) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', `creation` datetime(6) DEFAULT NULL, `modified` datetime(6) DEFAULT NULL, - `modified_by` varchar(40) DEFAULT NULL, - `owner` varchar(180) DEFAULT NULL, + `modified_by` varchar(255) DEFAULT NULL, + `owner` varchar(255) DEFAULT NULL, `docstatus` int(1) DEFAULT '0', - `parent` varchar(120) DEFAULT NULL, - `parentfield` varchar(120) DEFAULT NULL, - `parenttype` varchar(120) DEFAULT NULL, + `parent` varchar(255) DEFAULT NULL, + `parentfield` varchar(255) DEFAULT NULL, + `parenttype` varchar(255) DEFAULT NULL, `idx` int(8) DEFAULT NULL, - `search_fields` varchar(180) DEFAULT NULL, + `search_fields` varchar(255) DEFAULT NULL, `issingle` int(1) DEFAULT NULL, `istable` int(1) DEFAULT NULL, `version` int(11) DEFAULT NULL, - `module` varchar(180) DEFAULT NULL, - `plugin` varchar(180) DEFAULT NULL, - `autoname` varchar(180) DEFAULT NULL, - `name_case` varchar(180) DEFAULT NULL, - `title_field` varchar(180) DEFAULT NULL, + `module` varchar(255) DEFAULT NULL, + `plugin` varchar(255) DEFAULT NULL, + `autoname` varchar(255) DEFAULT NULL, + `name_case` varchar(255) DEFAULT NULL, + `title_field` varchar(255) DEFAULT NULL, + `sort_field` varchar(255) DEFAULT NULL, + `sort_order` varchar(255) DEFAULT NULL, `description` text, - `colour` varchar(180) DEFAULT NULL, + `colour` varchar(255) DEFAULT NULL, `read_only` int(1) DEFAULT NULL, `in_create` int(1) DEFAULT NULL, `show_in_menu` int(1) DEFAULT NULL, `menu_index` int(11) DEFAULT NULL, - `parent_node` varchar(180) DEFAULT NULL, - `smallicon` varchar(180) DEFAULT NULL, + `parent_node` varchar(255) DEFAULT NULL, + `smallicon` varchar(255) DEFAULT NULL, `allow_print` int(1) DEFAULT NULL, `allow_email` int(1) DEFAULT NULL, `allow_copy` int(1) DEFAULT NULL, @@ -136,47 +138,37 @@ CREATE TABLE `tabDocType` ( `allow_attach` int(1) DEFAULT NULL, `use_template` int(1) DEFAULT NULL, `max_attachments` int(11) DEFAULT NULL, - `section_style` varchar(180) DEFAULT NULL, - `client_script` mediumtext, - `client_script_core` mediumtext, - `server_code` mediumtext, - `server_code_core` mediumtext, - `server_code_compiled` mediumtext, - `client_string` mediumtext, - `server_code_error` varchar(180) DEFAULT NULL, - `print_outline` varchar(180) DEFAULT NULL, - `dt_template` mediumtext, + `print_outline` varchar(255) DEFAULT NULL, `is_transaction_doc` int(1) DEFAULT NULL, - `change_log` mediumtext, `read_only_onload` int(1) DEFAULT NULL, `allow_trash` int(1) DEFAULT NULL, `in_dialog` int(1) DEFAULT NULL, - `document_type` varchar(180) DEFAULT NULL, - `icon` varchar(180) DEFAULT NULL, - `tag_fields` varchar(180) DEFAULT NULL, - `subject` varchar(180) DEFAULT NULL, + `document_type` varchar(255) DEFAULT NULL, + `icon` varchar(255) DEFAULT NULL, + `tag_fields` varchar(255) DEFAULT NULL, + `subject` varchar(255) DEFAULT NULL, `_last_update` varchar(32) DEFAULT NULL, - `default_print_format` varchar(180) DEFAULT NULL, + `default_print_format` varchar(255) DEFAULT NULL, `is_submittable` int(1) DEFAULT NULL, - `_user_tags` varchar(180) DEFAULT NULL, + `_user_tags` varchar(255) DEFAULT NULL, `custom` int(1) DEFAULT NULL, PRIMARY KEY (`name`), KEY `parent` (`parent`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; +/*!255101 SET character_set_client = @saved_cs_client */; -- -- Table structure for table `tabSeries` -- DROP TABLE IF EXISTS `tabSeries`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!255101 SET @saved_cs_client = @@character_set_client */; +/*!255101 SET character_set_client = utf8 */; CREATE TABLE `tabSeries` ( `name` varchar(100) DEFAULT NULL, `current` int(10) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; +/*!255101 SET character_set_client = @saved_cs_client */; -- @@ -184,18 +176,18 @@ CREATE TABLE `tabSeries` ( -- DROP TABLE IF EXISTS `tabSessions`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!255101 SET @saved_cs_client = @@character_set_client */; +/*!255101 SET character_set_client = utf8 */; CREATE TABLE `tabSessions` ( `user` varchar(255) DEFAULT NULL, - `sid` varchar(120) DEFAULT NULL, + `sid` varchar(255) DEFAULT NULL, `sessiondata` longtext, `ipaddress` varchar(16) DEFAULT NULL, `lastupdate` datetime(6) DEFAULT NULL, `status` varchar(20) DEFAULT NULL, KEY `sid` (`sid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*!40101 SET character_set_client = @saved_cs_client */; +/*!255101 SET character_set_client = @saved_cs_client */; -- @@ -203,11 +195,11 @@ CREATE TABLE `tabSessions` ( -- DROP TABLE IF EXISTS `tabSingles`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!255101 SET @saved_cs_client = @@character_set_client */; +/*!255101 SET character_set_client = utf8 */; CREATE TABLE `tabSingles` ( - `doctype` varchar(40) DEFAULT NULL, - `field` varchar(40) DEFAULT NULL, + `doctype` varchar(255) DEFAULT NULL, + `field` varchar(255) DEFAULT NULL, `value` text, KEY `singles_doctype_field_index` (`doctype`, `field`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -217,11 +209,11 @@ CREATE TABLE `tabSingles` ( -- DROP TABLE IF EXISTS `__Auth`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; +/*!255101 SET @saved_cs_client = @@character_set_client */; +/*!255101 SET character_set_client = utf8 */; CREATE TABLE `__Auth` ( - `user` VARCHAR(180) NOT NULL PRIMARY KEY, - `password` VARCHAR(180) NOT NULL, + `user` VARCHAR(255) NOT NULL PRIMARY KEY, + `password` VARCHAR(255) NOT NULL, KEY `user` (`user`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -231,22 +223,22 @@ CREATE TABLE `__Auth` ( DROP TABLE IF EXISTS `tabFile Data`; CREATE TABLE `tabFile Data` ( - `name` varchar(120) NOT NULL, + `name` varchar(255) NOT NULL, `creation` datetime(6) DEFAULT NULL, `modified` datetime(6) DEFAULT NULL, - `modified_by` varchar(40) DEFAULT NULL, - `owner` varchar(40) DEFAULT NULL, + `modified_by` varchar(255) DEFAULT NULL, + `owner` varchar(255) DEFAULT NULL, `docstatus` int(1) DEFAULT '0', - `parent` varchar(120) DEFAULT NULL, - `parentfield` varchar(120) DEFAULT NULL, - `parenttype` varchar(120) DEFAULT NULL, + `parent` varchar(255) DEFAULT NULL, + `parentfield` varchar(255) DEFAULT NULL, + `parenttype` varchar(255) DEFAULT NULL, `idx` int(8) DEFAULT NULL, - `file_name` varchar(180) DEFAULT NULL, - `file_url` varchar(180) DEFAULT NULL, - `module` varchar(180) DEFAULT NULL, - `attached_to_name` varchar(180) DEFAULT NULL, + `file_name` varchar(255) DEFAULT NULL, + `file_url` varchar(255) DEFAULT NULL, + `module` varchar(255) DEFAULT NULL, + `attached_to_name` varchar(255) DEFAULT NULL, `file_size` int(11) DEFAULT NULL, - `attached_to_doctype` varchar(180) DEFAULT NULL, + `attached_to_doctype` varchar(255) DEFAULT NULL, PRIMARY KEY (`name`), KEY `parent` (`parent`), KEY `attached_to_name` (`attached_to_name`), @@ -259,18 +251,18 @@ CREATE TABLE `tabFile Data` ( DROP TABLE IF EXISTS `tabDefaultValue`; CREATE TABLE `tabDefaultValue` ( - `name` varchar(120) NOT NULL, + `name` varchar(255) NOT NULL, `creation` datetime(6) DEFAULT NULL, `modified` datetime(6) DEFAULT NULL, - `modified_by` varchar(40) DEFAULT NULL, - `owner` varchar(40) DEFAULT NULL, + `modified_by` varchar(255) DEFAULT NULL, + `owner` varchar(255) DEFAULT NULL, `docstatus` int(1) DEFAULT '0', - `parent` varchar(120) DEFAULT NULL, - `parentfield` varchar(120) DEFAULT NULL, - `parenttype` varchar(120) DEFAULT NULL, + `parent` varchar(255) DEFAULT NULL, + `parentfield` varchar(255) DEFAULT NULL, + `parenttype` varchar(255) DEFAULT NULL, `idx` int(8) DEFAULT NULL, `defvalue` text, - `defkey` varchar(180) DEFAULT NULL, + `defkey` varchar(255) DEFAULT NULL, PRIMARY KEY (`name`), KEY `parent` (`parent`), KEY `defaultvalue_parent_defkey_index` (`parent`,`defkey`) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index db18b021f5..da49f92fde 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -74,7 +74,8 @@ class DatabaseQuery(object): args.conditions = ' and '.join(self.conditions) args.fields = ', '.join(self.fields) - args.order_by = self.order_by or self.tables[0] + '.modified desc' + self.set_order_by(args) + args.group_by = self.group_by and (" group by " + self.group_by) or "" self.check_sort_by_table(args.order_by) @@ -279,6 +280,14 @@ class DatabaseQuery(object): query = query.replace('%(key)s', 'name') return frappe.db.sql(query, as_dict = (not self.as_list)) + def set_order_by(self, args): + meta = frappe.get_meta(self.doctype) + if self.order_by: + args.order_by = self.order_by + else: + args.order_by = "`tab{0}`.`{1}` {2}".format(self.doctype, + meta.sort_field or "modified", meta.sort_order or "desc") + def check_sort_by_table(self, order_by): if "." in order_by: tbl = order_by.split('.')[0] diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 6a5d001202..b40097ea43 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -108,8 +108,11 @@ class Meta(Document): return search_fields def get_list_fields(self): - return ["name"] + [d.fieldname \ + list_fields = ["name"] + [d.fieldname \ for d in self.fields if (d.in_list_view and d.fieldtype in type_map)] + if self.title_field and self.title_field not in list_fields: + list_fields.append(self.title_field) + return list_fields def process(self): # don't process for special doctypes diff --git a/frappe/templates/pages/list.py b/frappe/templates/pages/list.py index 92a10c3421..4edc75de88 100644 --- a/frappe/templates/pages/list.py +++ b/frappe/templates/pages/list.py @@ -39,8 +39,11 @@ def get_items(type, txt, limit_start=0): template = env.get_template(template_path) else: template = Template("""""") + {{ item[title_field] }}
  • """) - out.items = [template.render(item=i, doctype=type) for i in out.raw_items] + out.items = [template.render(item=i, doctype=type, + title_field = meta.title_field or "name") for i in out.raw_items] + + out.meta = meta return out diff --git a/frappe/templates/pages/view.html b/frappe/templates/pages/view.html index 699e1b12e8..3a29dfedae 100644 --- a/frappe/templates/pages/view.html +++ b/frappe/templates/pages/view.html @@ -1,36 +1,74 @@ -{% block title %}{{ doc.doctype }} / {{ doc.name }}{% endblock %} +{% block title %}{{ doc.doctype }} / {{ doc[meta.title_field or "name"] }}{% endblock %} {% block header %} -

    {{ doc.name }}

    -

    {{ doc.doctype }} +

    {{ doc[meta.title_field or "name"] }}

    +

    {{ doc.doctype }}

    {% endblock %} {% block content %} {% if custom_view %} {{ custom_view }} {% else %} + +{% macro print_value(df, d, meta) -%} + {% if df.fieldtype=="Check" %} + + {% elif df.fieldtype=="Image" %} + + {% else %} + {{ d[df.fieldname] or "" }} + {% endif %} +{%- endmacro %} + +{% macro get_width(fieldtype) -%} + {%- if fieldtype in ("Int", "Check") -%}{{ 60 }} + {%- else -%}{{ 150 }}{% endif -%} +{%- endmacro %} + {% for df in meta.fields %} {% if not df.hidden and not df.permlevel and not df.print_hide %} {% if df.fieldtype=="Section Break" %} -

    {{ df.label or "" }}

    +

    {{ df.label or "" }}

    {% elif df.fieldtype=="Column Break" %} {% elif df.fieldtype=="Table" %} - + {% set table_meta = frappe.get_meta(df.options) %} +
    + + + + + {% for tdf in table_meta.fields %} + {% if tdf.fieldtype not in ("Column Break", "Section Break") + and tdf.label %} + + {% endif %} + {% endfor %} + + + + {% for d in doc[df.fieldname] %} + + + {% for tdf in table_meta.fields %} + {% if tdf.fieldtype not in ("Column Break", "Section Break") %} + + {% endif %} + {% endfor %} + + {% endfor %} + +
    Sr + {{ tdf.label }}
    {{ d.idx }}{{ print_value(tdf, d, table_meta) }}
    +
    {% else %}
    - {% if df.fieldtype not in ("Image",) %} + {% if df.fieldtype not in ("Image","HTML") %} {% endif %}
    - {% if df.fieldtype=="Check" %} - - {% elif df.fieldtype=="Image" %} - - {% else %} - {{ doc[df.fieldname] }} - {% endif %} + {{ print_value(df, doc, meta) }}
    {% endif %} From 28418fecf23af6ebe4bd93f22e56f88d51d0a28e Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 8 May 2014 10:17:07 +0530 Subject: [PATCH 24/98] added sort_field and sort_order to doctype so that sorting can be user defined. Also added to customize form view --- frappe/utils/boilerplate.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 035525ec55..4ef4c72e7a 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -115,6 +115,17 @@ app_version = "0.0.1" # web_include_css = "/assets/{app_name}/css/{app_name}.css" # web_include_js = "/assets/{app_name}/js/{app_name}.js" +# Home Pages +# ---------- + +# application home page (will override Website Settings) +# home_page = "login" + +# website user home page (by Role) +# role_home_page = { +# "Role": "home_page" +# } + # Installation # ------------ From 919eb9eb809afcdf1ea457841f165abad7892946 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 8 May 2014 10:33:58 +0530 Subject: [PATCH 25/98] added hook for role_home_page --- frappe/sessions.py | 2 +- frappe/website/utils.py | 24 +++++++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/frappe/sessions.py b/frappe/sessions.py index 48e02f3eec..5a714fe9da 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -32,7 +32,7 @@ def clear_cache(user=None): "time_zone"]) def delete_user_cache(user): - for key in ("bootinfo", "lang", "roles", "restrictions"): + for key in ("bootinfo", "lang", "roles", "restrictions", "home_page"): cache.delete_value(key + ":" + user) def clear_notifications(user=None): diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 7a60df6338..fd605316ec 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -21,12 +21,26 @@ def can_cache(no_cache=False): return not (frappe.conf.disable_website_cache or no_cache) def get_home_page(): - home_page = frappe.cache().get_value("home_page", \ - lambda: (frappe.get_hooks("home_page") \ - or [frappe.db.get_value("Website Settings", None, "home_page") \ - or "login"])[0]) + def _get_home_page(): + role_home_page = frappe.get_hooks("role_home_page") + home_page = None - return home_page + for role in frappe.get_roles(): + if role in role_home_page: + home_page = role_home_page[role][0] + break + + if not home_page: + home_page = frappe.get_hooks("home_page") + if home_page: + home_page = home_page[0] + + if not home_page: + home_page = frappe.db.get_value("Website Settings", None, "home_page") or "login" + + return home_page + + return frappe.cache().get_value("home_page:" + frappe.session.user, _get_home_page) def is_signup_enabled(): if getattr(frappe.local, "is_signup_enabled", None) is None: From 6653f2e2a170c53c158979d5bd586e2db4d62946 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 8 May 2014 16:51:41 +0530 Subject: [PATCH 26/98] views and lists --- frappe/templates/pages/list.py | 13 +++++++------ frappe/templates/pages/view.html | 2 +- frappe/templates/pages/view.py | 6 ++++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/frappe/templates/pages/list.py b/frappe/templates/pages/list.py index 4edc75de88..0e05b2f288 100644 --- a/frappe/templates/pages/list.py +++ b/frappe/templates/pages/list.py @@ -29,20 +29,21 @@ def get_items(type, txt, limit_start=0): filters.append([type, "name", "like", "%" + txt + "%"]) - out.raw_items = frappe.get_list(type, fields = meta.get_list_fields(), + out.raw_items = frappe.get_list(type, fields = ["*"], filters=filters, or_filters = or_filters, limit_start=limit_start, limit_page_length = 20) template_path = os.path.join(get_doc_path(meta.module, "DocType", meta.name), "list_item.html") if os.path.exists(template_path): - env = Environment(loader = FileSystemLoader(".")) + env = Environment(loader = FileSystemLoader("/")) + #template_path = os.path.relpath(template_path) template = env.get_template(template_path) else: - template = Template("""""") + template = Template("""""") - out.items = [template.render(item=i, doctype=type, - title_field = meta.title_field or "name") for i in out.raw_items] + out.items = [template.render(doc=i, doctype=type, + title_field = meta.title_field or "name") for i in out.raw_items] out.meta = meta diff --git a/frappe/templates/pages/view.html b/frappe/templates/pages/view.html index 3a29dfedae..3c0acf0a37 100644 --- a/frappe/templates/pages/view.html +++ b/frappe/templates/pages/view.html @@ -2,7 +2,7 @@ {% block header %}

    {{ doc[meta.title_field or "name"] }}

    -

    {{ doc.doctype }}

    +

    {{ doc.doctype }} {{ _("List") }}

    {% endblock %} {% block content %} diff --git a/frappe/templates/pages/view.py b/frappe/templates/pages/view.py index e6f31d3146..7e72a8895c 100644 --- a/frappe/templates/pages/view.py +++ b/frappe/templates/pages/view.py @@ -8,7 +8,9 @@ no_cache = 1 no_sitemap = 1 def get_context(context): + doc = frappe.get_doc(frappe.local.form_dict.doctype, frappe.local.form_dict.name) + doc.run_method("make_view") return { - "doc": frappe.get_doc(frappe.local.form_dict.doctype, frappe.local.form_dict.name), - "meta": frappe.get_meta(frappe.local.form_dict.doctype) + "doc": doc, + "meta": doc.meta } From 7a5430fc2fd17428ad49b3fb5fe4a8f138e3be5c Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 8 May 2014 16:53:59 +0530 Subject: [PATCH 27/98] removed debug from search.py --- frappe/widgets/search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/widgets/search.py b/frappe/widgets/search.py index 537e263868..4e45eab0f8 100644 --- a/frappe/widgets/search.py +++ b/frappe/widgets/search.py @@ -74,7 +74,7 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0, frappe.response["values"] = frappe.widgets.reportview.execute(doctype, filters=filters, fields = get_std_fields_list(meta, searchfield or "name"), or_filters = or_filters, limit_start = start, - limit_page_length=page_len, as_list=True, debug=1) + limit_page_length=page_len, as_list=True) def get_std_fields_list(meta, key): # get additional search fields From a060b2b4a2ed1d5e82da570fbade192e3067d89e Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 9 May 2014 10:50:05 +0530 Subject: [PATCH 28/98] added bulk option in frappe.sendmail and header extension --- frappe/__init__.py | 18 +++++++++++++----- frappe/templates/base.html | 9 +++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index e123edce68..90d2fe31bd 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -215,12 +215,20 @@ def set_user(username): def get_request_header(key, default=None): return request.headers.get(key, default) -def sendmail(recipients=(), sender="", subject="No Subject", message="No Message", as_markdown=False): - import frappe.utils.email_lib - if as_markdown: - frappe.utils.email_lib.sendmail_md(recipients, sender=sender, subject=subject, msg=message) +def sendmail(recipients=(), sender="", subject="No Subject", message="No Message", + as_markdown=False, bulk=False): + + if bulk: + import frappe.utils.email_lib.bulk + frappe.utils.email_lib.bulk.send(recipients=recipients, sender=sender, + subject=subject, message=msg, add_unsubscribe_link=False) + else: - frappe.utils.email_lib.sendmail(recipients, sender=sender, subject=subject, msg=message) + import frappe.utils.email_lib + if as_markdown: + frappe.utils.email_lib.sendmail_md(recipients, sender=sender, subject=subject, msg=message) + else: + frappe.utils.email_lib.sendmail(recipients, sender=sender, subject=subject, msg=message) logger = None whitelisted = [] diff --git a/frappe/templates/base.html b/frappe/templates/base.html index 1f5f1b6754..7e7ebca807 100644 --- a/frappe/templates/base.html +++ b/frappe/templates/base.html @@ -20,10 +20,6 @@ https://frappe.io/apps/frappe {%- endfor -%} {%- endif -%} - {%- for link in web_include_js %} - - {%- endfor -%} - {%- for link in web_include_css %} {%- endfor -%} @@ -38,6 +34,7 @@ https://frappe.io/apps/frappe
    {%- block banner -%} + {% include "templates/includes/banner_extension.html" %} {% if banner_html -%}
    {{ banner_html or "" }}
    {%- endif %} @@ -92,6 +89,10 @@ https://frappe.io/apps/frappe {%- block footer -%}{% include "templates/includes/footer.html" %}{%- endblock -%}
    + {%- for link in web_include_js %} + + {%- endfor -%} + {%- block script %} + From 6d26edcae4abeca6ea332f874c9987727057950d Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 12 May 2014 17:21:48 +0530 Subject: [PATCH 54/98] fixes frappe/erpnext#1629 --- frappe/templates/pages/about.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/templates/pages/about.html b/frappe/templates/pages/about.html index e1242c86e5..a9b2940062 100644 --- a/frappe/templates/pages/about.html +++ b/frappe/templates/pages/about.html @@ -2,7 +2,7 @@ {% block content %}
    - {{ obj.doc.company_introduction or """

    Some Introduction about your company that you would + {{ doc.company_introduction or """

    Some Introduction about your company that you would like your website visitor to know. More people than you think will read your About page. People always like to know who the are doing business with. @@ -11,7 +11,7 @@ list of key team members in Website > About Us Settings

    """ }} {% if obj.get({"doctype":"Company History"}) %} -

    {{ obj.doc.company_history_heading or "Company History" }}

    +

    {{ doc.company_history_heading or "Company History" }}

    {% for d in obj.get({"doctype":"Company History"}) %}

    {{ d.year }}

    @@ -21,7 +21,7 @@ {% endif %} {% if obj.get({"doctype":"About Us Team Member"}) %} -

    {{ obj.doc.team_members_heading or "Team Members" }}

    +

    {{ doc.team_members_heading or "Team Members" }}

    {% for d in obj.get({"doctype":"About Us Team Member"}) %}
    @@ -35,6 +35,6 @@
    {% endfor %} {% endif %} - {{ obj.doc.footer or "" }} + {{ doc.footer or "" }}
    {% endblock %} From 58d0f9d467cb5e677634a176d8290deb3174008f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 May 2014 17:40:46 +0530 Subject: [PATCH 55/98] Show Custom reports on module page fixes #1619 --- frappe/widgets/moduleview.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/widgets/moduleview.py b/frappe/widgets/moduleview.py index 847ca70176..c7ecc3d52f 100644 --- a/frappe/widgets/moduleview.py +++ b/frappe/widgets/moduleview.py @@ -169,7 +169,8 @@ def get_doctype_count_from_table(doctype): def get_report_list(module, is_standard="No"): """return list on new style reports for modules""" reports = frappe.get_list("Report", fields=["name", "ref_doctype", "report_type"], filters= - {"is_standard": is_standard, "disabled": ("in", ("0", "NULL")), "module": module}, order_by="name") + {"is_standard": is_standard, "disabled": ("in", ("0", "NULL", "")), "module": module}, + order_by="name") out = [] for r in reports: From 2978793e37bc0bb8ecf36fbe31b031ff3abced12 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 May 2014 17:41:23 +0530 Subject: [PATCH 56/98] set route options for Report --- frappe/core/doctype/report/report.js | 6 +++--- frappe/core/doctype/report/report.json | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frappe/core/doctype/report/report.js b/frappe/core/doctype/report/report.js index 4f3e162577..d8d4bf90cc 100644 --- a/frappe/core/doctype/report/report.js +++ b/frappe/core/doctype/report/report.js @@ -2,7 +2,7 @@ cur_frm.cscript.refresh = function(doc) { cur_frm.add_custom_button("Show Report", function() { switch(doc.report_type) { case "Report Builder": - frappe.set_route("Report", doc.name); + frappe.set_route("Report", doc.ref_doctype, doc.name); break; case "Query Report": frappe.set_route("query-report", doc.name); @@ -12,7 +12,7 @@ cur_frm.cscript.refresh = function(doc) { break; } }, "icon-table") - + cur_frm.set_intro(""); switch(doc.report_type) { case "Report Builder": @@ -30,4 +30,4 @@ cur_frm.cscript.refresh = function(doc) { cur_frm.set_intro(__("Write a Python file in the same folder where this is saved and return column and result.")) break; } -} \ No newline at end of file +} diff --git a/frappe/core/doctype/report/report.json b/frappe/core/doctype/report/report.json index f4a5bde8b2..62e81b08b9 100644 --- a/frappe/core/doctype/report/report.json +++ b/frappe/core/doctype/report/report.json @@ -1,6 +1,6 @@ { "autoname": "field:report_name", - "creation": "2013-03-09 15:45:57.000000", + "creation": "2013-03-09 15:45:57", "docstatus": 0, "doctype": "DocType", "document_type": "System", @@ -101,7 +101,7 @@ ], "icon": "icon-table", "idx": 1, - "modified": "2014-03-07 15:20:02.000000", + "modified": "2014-05-12 17:08:04.185601", "modified_by": "Administrator", "module": "Core", "name": "Report", @@ -157,5 +157,7 @@ "role": "All", "submit": 0 } - ] + ], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file From 9901915704a4d034ad6069f8bea26572feb19d6a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 May 2014 17:41:49 +0530 Subject: [PATCH 57/98] Update custom reports for renamed fields. Fixes #1620 --- frappe/model/__init__.py | 98 ++++++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 5beafb2936..6e78f27033 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -1,9 +1,11 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt # model __init__.py from __future__ import unicode_literals import frappe +import json + no_value_fields = ['Section Break', 'Column Break', 'HTML', 'Table', 'Button', 'Image'] default_fields = ['doctype','name','owner','creation','modified','modified_by','parent','parentfield','parenttype','idx','docstatus'] @@ -18,10 +20,10 @@ def insert(doclist): d["__islocal"] = 1 else: d.set("__islocal", 1) - + wrapper = frappe.get_doc(doclist) wrapper.save() - + return wrapper def rename(doctype, old, new, debug=False): @@ -29,17 +31,17 @@ def rename(doctype, old, new, debug=False): frappe.model.rename_doc.rename_doc(doctype, old, new, debug) def copytables(srctype, src, srcfield, tartype, tar, tarfield, srcfields, tarfields=[]): - if not tarfields: + if not tarfields: tarfields = srcfields l = [] data = src.get(srcfield) for d in data: newrow = tar.append(tarfield) newrow.idx = d.idx - + for i in range(len(srcfields)): newrow.set(tarfields[i], d.get(srcfields[i])) - + l.append(newrow) return l @@ -60,15 +62,15 @@ def delete_fields(args_dict, delete=0): for dt in args_dict.keys(): fields = args_dict[dt] if not fields: continue - + frappe.db.sql("""\ DELETE FROM `tabDocField` WHERE parent=%s AND fieldname IN (%s) """ % ('%s', ", ".join(['"' + f + '"' for f in fields])), dt) - + # Delete the data / column only if delete is specified if not delete: continue - + if frappe.db.get_value("DocType", dt, "issingle"): frappe.db.sql("""\ DELETE FROM `tabSingles` @@ -84,13 +86,13 @@ def delete_fields(args_dict, delete=0): def rename_field(doctype, old_fieldname, new_fieldname): """This functions assumes that doctype is already synced""" - + meta = frappe.get_meta(doctype) new_field = meta.get_field(new_fieldname) if not new_field: print "rename_field: " + (new_fieldname) + " not found in " + doctype return - + if new_field.fieldtype == "Table": # change parentfield of table mentioned in options frappe.db.sql("""update `tab%s` set parentfield=%s @@ -99,22 +101,78 @@ def rename_field(doctype, old_fieldname, new_fieldname): elif new_field.fieldtype not in no_value_fields: if meta.issingle: frappe.db.sql("""update `tabSingles` set field=%s - where doctype=%s and field=%s""", + where doctype=%s and field=%s""", (new_fieldname, doctype, old_fieldname)) else: # copy field value frappe.db.sql("""update `tab%s` set `%s`=`%s`""" % \ (doctype, new_fieldname, old_fieldname)) - + # update in property setter - frappe.db.sql("""update `tabProperty Setter` set field_name = %s + frappe.db.sql("""update `tabProperty Setter` set field_name = %s where doc_type=%s and field_name=%s""", (new_fieldname, doctype, old_fieldname)) - + + update_reports(doctype, old_fieldname, new_fieldname) update_users_report_view_settings(doctype, old_fieldname) - + +def update_reports(doctype, old_fieldname, new_fieldname): + def _get_new_sort_by(old_sort_by, updated): + new_sort_by = "" + if old_sort_by: + sort_by_doctype, sort_by_field = old_sort_by.split(".") + if sort_by_field == old_fieldname and sort_by_doctype == doctype: + new_sort_by = doctype + "." + new_fieldname + updated = True + else: + new_sort_by = old_sort_by + + return new_sort_by, updated + + reports = frappe.db.sql("""select name, json from tabReport + where report_type = 'Report Builder' and ifnull(is_standard, 'No') = 'Yes' + and json like %s and json like %s""", + ('%%%s%%' % old_fieldname , '%%%s%%' % doctype), as_dict=True) + + for r in reports: + updated = False + val = json.loads(r.json) + + # update filters + new_filters = [] + for f in val.get("filters"): + if f[0] == doctype and f[1] == old_fieldname: + new_filters.append([doctype, new_fieldname, f[2], f[3]]) + updated = True + else: + new_filters.append(f) + + # update columns + new_columns = [] + for c in val.get("columns"): + if c[0] == old_fieldname and c[1] == doctype: + new_columns.append([new_fieldname, doctype]) + updated = True + else: + new_columns.append(c) + + # update sort by + new_sort_by, updated = _get_new_sort_by(val.get("sort_by"), updated) + new_sort_by_next, updated = _get_new_sort_by(val.get("sort_by_next"), updated) + + if updated: + new_val = json.dumps({ + "filters": new_filters, + "columns": new_columns, + "sort_by": new_sort_by, + "sort_order": val.get("sort_order"), + "sort_by_next": new_sort_by_next, + "sort_order_next": val.get("sort_order_next") + }) + + frappe.db.sql("""update `tabReport` set `json`=%s where name=%s""", (new_val, r.name)) + def update_users_report_view_settings(doctype, ref_fieldname): - import json - user_report_cols = frappe.db.sql("""select defkey, defvalue from `tabDefaultValue` where + user_report_cols = frappe.db.sql("""select defkey, defvalue from `tabDefaultValue` where defkey like '_list_settings:%'""") for key, value in user_report_cols: new_columns = [] @@ -124,5 +182,5 @@ def update_users_report_view_settings(doctype, ref_fieldname): new_columns.append([field, field_doctype]) columns_modified=True if columns_modified: - frappe.db.sql("""update `tabDefaultValue` set defvalue=%s - where defkey=%s""" % ('%s', '%s'), (json.dumps(new_columns), key)) \ No newline at end of file + frappe.db.sql("""update `tabDefaultValue` set defvalue=%s + where defkey=%s""" % ('%s', '%s'), (json.dumps(new_columns), key)) From 7ea509274af2c90acaba0e3be45bf7e1b54634aa Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 12 May 2014 19:46:03 +0530 Subject: [PATCH 58/98] Fixed check if patch already executed --- frappe/modules/patch_handler.py | 43 +++++++++++++++------------------ 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/frappe/modules/patch_handler.py b/frappe/modules/patch_handler.py index bd2b18941f..671d18491e 100644 --- a/frappe/modules/patch_handler.py +++ b/frappe/modules/patch_handler.py @@ -1,15 +1,15 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt from __future__ import unicode_literals """ Execute Patch Files To run directly - + python lib/wnf.py patch patch1, patch2 etc python lib/wnf.py patch -f patch1, patch2 etc - + where patch1, patch2 is module name """ import frappe, os @@ -19,21 +19,21 @@ class PatchError(Exception): pass def run_all(): """run all pending patches""" executed = [p[0] for p in frappe.db.sql("""select patch from `tabPatch Log`""")] - + for patch in get_all_patches(): if patch and (patch not in executed): if not run_single(patchmodule = patch): log(patch + ': failed: STOPPED') raise PatchError(patch) - + def get_all_patches(): patches = [] for app in frappe.get_installed_apps(): # 3-to-4 fix - if app=="webnotes": + if app=="webnotes": app="frappe" patches.extend(frappe.get_file_items(frappe.get_pymodule_path(app, "patches.txt"))) - + return patches def reload_doc(args): @@ -42,15 +42,15 @@ def reload_doc(args): def run_single(patchmodule=None, method=None, methodargs=None, force=False): from frappe import conf - + # don't write txt files conf.developer_mode = 0 - + if force or method or not executed(patchmodule): return execute_patch(patchmodule, method, methodargs) else: return True - + def execute_patch(patchmodule, method=None, methodargs=None): """execute the patch""" success = False @@ -66,7 +66,7 @@ def execute_patch(patchmodule, method=None, methodargs=None): update_patch_log(patchmodule) elif method: method(**methodargs) - + frappe.db.commit() success = True except Exception, e: @@ -88,9 +88,9 @@ def add_to_patch_log(tb): # TODO use get_site_base_path with open(os.path.join(os.path.dirname(conf.__file__), 'app', 'patches','patch.log'),'a') as patchlog: patchlog.write('\n\n' + tb) - + def update_patch_log(patchmodule): - """update patch_file in patch log""" + """update patch_file in patch log""" if frappe.db.table_exists("__PatchLog"): frappe.db.sql("""INSERT INTO `__PatchLog` VALUES (%s, now())""", \ patchmodule) @@ -99,14 +99,11 @@ def update_patch_log(patchmodule): def executed(patchmodule): """return True if is executed""" - if frappe.db.table_exists("__PatchLog"): - done = frappe.db.sql("""select patch from __PatchLog where patch=%s""", patchmodule) - else: - done = frappe.db.get_value("Patch Log", {"patch": patchmodule}) - if done: - print "Patch %s already executed in %s" % (patchmodule, frappe.db.cur_db_name) + done = frappe.db.get_value("Patch Log", {"patch": patchmodule}) + # if done: + # print "Patch %s already executed in %s" % (patchmodule, frappe.db.cur_db_name) return done - + def block_user(block): """stop/start execution till patch is run""" frappe.db.begin() @@ -114,7 +111,7 @@ def block_user(block): frappe.db.set_global('__session_status', block and 'stop' or None) frappe.db.set_global('__session_status_message', block and msg or None) frappe.db.commit() - + def check_session_stopped(): if frappe.db.get_global("__session_status")=='stop': frappe.msgprint(frappe.db.get_global("__session_status_message")) @@ -123,9 +120,9 @@ def check_session_stopped(): def setup(): frappe.db.sql("""CREATE TABLE IF NOT EXISTS `__PatchLog` ( patch TEXT, applied_on DATETIME) engine=InnoDB""") - + def log(msg): if getattr(frappe.local, "patch_log_list", None) is None: frappe.local.patch_log_list = [] - + frappe.local.patch_log_list.append(msg) From 416a6d3f39d5a20ae403b5c469b1335548bbf252 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 12 May 2014 15:17:35 +0530 Subject: [PATCH 59/98] Fixed naming.py --- frappe/core/page/data_import_tool/importer.py | 3 -- frappe/model/naming.py | 41 +++++++++---------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/frappe/core/page/data_import_tool/importer.py b/frappe/core/page/data_import_tool/importer.py index 7c05ceb5fe..36034b01bf 100644 --- a/frappe/core/page/data_import_tool/importer.py +++ b/frappe/core/page/data_import_tool/importer.py @@ -186,9 +186,6 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False, doc = None doc = get_doc(row_idx) - if doc.get("name"): - doc["_new_name_set"] = True - try: frappe.local.message_log = [] if doc.get("parentfield"): diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 782c88a91d..35d56866af 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -7,13 +7,9 @@ import frappe from frappe.utils import now_datetime, cint def set_new_name(doc): - if getattr(doc, "_new_name_set", False): - # already set by doc + if doc.name: return - doc._new_name_set = True - autoname = frappe.get_meta(doc.doctype).autoname - # amendments if getattr(doc, "amended_from", None): return _get_amended_name(doc) @@ -22,27 +18,30 @@ def set_new_name(doc): if tmp and not isinstance(tmp, basestring): # autoname in a function, not a property doc.autoname() - if doc.name: - return + if doc.name: + return + + autoname = frappe.get_meta(doc.doctype).autoname # based on a field - if autoname and autoname.startswith('field:'): - n = doc.get(autoname[6:]) - if not n: - raise Exception, 'Name is required' - doc.name = n.strip() + if autoname: + if autoname.startswith('field:'): + n = doc.get(autoname[6:]) + if not n: + raise Exception, 'Name is required' + doc.name = n.strip() - elif autoname and autoname.startswith("naming_series:"): - if not doc.naming_series: - doc.naming_series = get_default_naming_series(doc.doctype) + elif autoname.startswith("naming_series:"): + if not doc.naming_series: + doc.naming_series = get_default_naming_series(doc.doctype) - if not doc.naming_series: - frappe.msgprint(frappe._("Naming Series mandatory"), raise_exception=True) - doc.name = make_autoname(doc.naming_series+'.#####') + if not doc.naming_series: + frappe.msgprint(frappe._("Naming Series mandatory"), raise_exception=True) + doc.name = make_autoname(doc.naming_series+'.#####') - # call the method! - elif autoname and autoname!='Prompt': - doc.name = make_autoname(autoname, doc.doctype) + # call the method! + elif autoname!='Prompt': + doc.name = make_autoname(autoname, doc.doctype) # given elif doc.get('__newname', None): From f1df6cc42bccda2d1bcc282b56959e66ea096fe9 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 12 May 2014 20:07:29 +0530 Subject: [PATCH 60/98] Run sync_fixtures when installing app --- frappe/installer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/installer.py b/frappe/installer.py index d6332bd3aa..2dc37116d1 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -13,6 +13,7 @@ import getpass from frappe import _ from frappe.model.db_schema import DbManager from frappe.model.sync import sync_for +from frappe.utils.fixtures import sync_fixtures def install_db(root_login="root", root_password=None, db_name=None, source_sql=None, admin_password = 'admin', verbose=True, force=0, site_config=None, reinstall=False): @@ -104,6 +105,7 @@ def install_app(name, verbose=False, set_as_patched=True): if name != "frappe": add_module_defs(name) + sync_for(name, force=True, sync_everything=True, verbose=verbose) add_to_installed_apps(name) @@ -114,6 +116,8 @@ def install_app(name, verbose=False, set_as_patched=True): for after_install in app_hooks.after_install or []: frappe.get_attr(after_install)() + sync_fixtures() + frappe.flags.in_install_app = False def add_to_installed_apps(app_name, rebuild_sitemap=True): From 60d342a36f3a60bfca92f7c4b257cf47c2b394bf Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 13 May 2014 12:49:35 +0530 Subject: [PATCH 61/98] Custom reports route from moduleview page --- frappe/widgets/moduleview.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/widgets/moduleview.py b/frappe/widgets/moduleview.py index c7ecc3d52f..b1adaab896 100644 --- a/frappe/widgets/moduleview.py +++ b/frappe/widgets/moduleview.py @@ -179,7 +179,8 @@ def get_report_list(module, is_standard="No"): "doctype": r.ref_doctype, "is_query_report": 1 if r.report_type in ("Query Report", "Script Report") else 0, "description": r.report_type, - "label": _(r.name) + "label": _(r.name), + "name": r.name }) return out From 51b3534a0f2f370a2ac3499190f0a90820021dd5 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 13 May 2014 15:09:58 +0530 Subject: [PATCH 62/98] Changed patch folder 4_0 to v4_0 --- frappe/data/languages.txt | 4 +-- frappe/patches.txt | 33 ++++++++++--------- frappe/patches/{4_0 => v4_0}/__init__.py | 0 .../{4_0 => v4_0}/add_delete_permission.py | 0 .../{4_0 => v4_0}/deprecate_control_panel.py | 0 .../{4_0 => v4_0}/deprecate_link_selects.py | 0 .../{4_0 => v4_0}/file_manager_hooks.py | 0 .../{4_0 => v4_0}/move_match_to_restricted.py | 0 .../patches/{4_0 => v4_0}/private_backups.py | 0 .../{4_0 => v4_0}/remove_index_sitemap.py | 0 .../{4_0 => v4_0}/remove_old_parent.py | 1 + .../{4_0 => v4_0}/rename_profile_to_user.py | 0 .../{4_0 => v4_0}/rename_sitemap_to_route.py | 0 .../{4_0 => v4_0}/set_module_in_report.py | 0 .../set_todo_checked_as_closed.py | 0 .../{4_0 => v4_0}/set_website_route_idx.py | 0 .../patches/{4_0 => v4_0}/update_datetime.py | 0 .../{4_0 => v4_0}/webnotes_to_frappe.py | 0 .../website_sitemap_hierarchy.py | 0 19 files changed, 20 insertions(+), 18 deletions(-) rename frappe/patches/{4_0 => v4_0}/__init__.py (100%) rename frappe/patches/{4_0 => v4_0}/add_delete_permission.py (100%) rename frappe/patches/{4_0 => v4_0}/deprecate_control_panel.py (100%) rename frappe/patches/{4_0 => v4_0}/deprecate_link_selects.py (100%) rename frappe/patches/{4_0 => v4_0}/file_manager_hooks.py (100%) rename frappe/patches/{4_0 => v4_0}/move_match_to_restricted.py (100%) rename frappe/patches/{4_0 => v4_0}/private_backups.py (100%) rename frappe/patches/{4_0 => v4_0}/remove_index_sitemap.py (100%) rename frappe/patches/{4_0 => v4_0}/remove_old_parent.py (84%) rename frappe/patches/{4_0 => v4_0}/rename_profile_to_user.py (100%) rename frappe/patches/{4_0 => v4_0}/rename_sitemap_to_route.py (100%) rename frappe/patches/{4_0 => v4_0}/set_module_in_report.py (100%) rename frappe/patches/{4_0 => v4_0}/set_todo_checked_as_closed.py (100%) rename frappe/patches/{4_0 => v4_0}/set_website_route_idx.py (100%) rename frappe/patches/{4_0 => v4_0}/update_datetime.py (100%) rename frappe/patches/{4_0 => v4_0}/webnotes_to_frappe.py (100%) rename frappe/patches/{4_0 => v4_0}/website_sitemap_hierarchy.py (100%) diff --git a/frappe/data/languages.txt b/frappe/data/languages.txt index 27ff92b3fb..ed686d7802 100644 --- a/frappe/data/languages.txt +++ b/frappe/data/languages.txt @@ -14,5 +14,5 @@ pt português sr српски ta தமிழ் th ไทย -zh-cn 中国(简体 -zh-tw 中國(繁體 +zh-cn 中国(简体) +zh-tw 中國(繁體) diff --git a/frappe/patches.txt b/frappe/patches.txt index dee08bb327..74e3f77eae 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -1,5 +1,6 @@ execute:import inlinestyler # new requirement +execute:frappe.db.sql("""update `tabPatch Log` set patch=replace(patch, '.4_0.', '.v4_0.')""") #2014-05-12 execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2014-01-24 execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2014-03-01 execute:frappe.reload_doc('core', 'doctype', 'docperm') #2013-13-26 @@ -8,22 +9,22 @@ execute:frappe.reload_doc('core', 'doctype', 'report') #2013-13-26 execute:frappe.reload_doc('core', 'doctype', 'version') #2014-02-21 execute:frappe.db.sql("alter table `tabSessions` modify `user` varchar(255), engine=InnoDB") -frappe.patches.4_0.remove_index_sitemap -frappe.patches.4_0.add_delete_permission -frappe.patches.4_0.move_match_to_restricted -frappe.patches.4_0.set_todo_checked_as_closed -frappe.patches.4_0.website_sitemap_hierarchy -frappe.patches.4_0.webnotes_to_frappe +frappe.patches.v4_0.remove_index_sitemap +frappe.patches.v4_0.add_delete_permission +frappe.patches.v4_0.move_match_to_restricted +frappe.patches.v4_0.set_todo_checked_as_closed +frappe.patches.v4_0.website_sitemap_hierarchy +frappe.patches.v4_0.webnotes_to_frappe execute:frappe.reset_perms("Module Def") -frappe.patches.4_0.rename_sitemap_to_route -frappe.patches.4_0.rename_profile_to_user -frappe.patches.4_0.set_website_route_idx +frappe.patches.v4_0.rename_sitemap_to_route +frappe.patches.v4_0.rename_profile_to_user +frappe.patches.v4_0.set_website_route_idx execute:import frappe.installer;frappe.installer.make_site_dirs() #2014-02-19 -frappe.patches.4_0.private_backups -frappe.patches.4_0.set_module_in_report -frappe.patches.4_0.remove_old_parent -frappe.patches.4_0.update_datetime -frappe.patches.4_0.deprecate_control_panel -frappe.patches.4_0.file_manager_hooks +frappe.patches.v4_0.private_backups +frappe.patches.v4_0.set_module_in_report +frappe.patches.v4_0.remove_old_parent +frappe.patches.v4_0.update_datetime +frappe.patches.v4_0.deprecate_control_panel +frappe.patches.v4_0.file_manager_hooks execute:frappe.get_doc("User", "Guest").save() -frappe.patches.4_0.deprecate_link_selects +frappe.patches.v4_0.deprecate_link_selects diff --git a/frappe/patches/4_0/__init__.py b/frappe/patches/v4_0/__init__.py similarity index 100% rename from frappe/patches/4_0/__init__.py rename to frappe/patches/v4_0/__init__.py diff --git a/frappe/patches/4_0/add_delete_permission.py b/frappe/patches/v4_0/add_delete_permission.py similarity index 100% rename from frappe/patches/4_0/add_delete_permission.py rename to frappe/patches/v4_0/add_delete_permission.py diff --git a/frappe/patches/4_0/deprecate_control_panel.py b/frappe/patches/v4_0/deprecate_control_panel.py similarity index 100% rename from frappe/patches/4_0/deprecate_control_panel.py rename to frappe/patches/v4_0/deprecate_control_panel.py diff --git a/frappe/patches/4_0/deprecate_link_selects.py b/frappe/patches/v4_0/deprecate_link_selects.py similarity index 100% rename from frappe/patches/4_0/deprecate_link_selects.py rename to frappe/patches/v4_0/deprecate_link_selects.py diff --git a/frappe/patches/4_0/file_manager_hooks.py b/frappe/patches/v4_0/file_manager_hooks.py similarity index 100% rename from frappe/patches/4_0/file_manager_hooks.py rename to frappe/patches/v4_0/file_manager_hooks.py diff --git a/frappe/patches/4_0/move_match_to_restricted.py b/frappe/patches/v4_0/move_match_to_restricted.py similarity index 100% rename from frappe/patches/4_0/move_match_to_restricted.py rename to frappe/patches/v4_0/move_match_to_restricted.py diff --git a/frappe/patches/4_0/private_backups.py b/frappe/patches/v4_0/private_backups.py similarity index 100% rename from frappe/patches/4_0/private_backups.py rename to frappe/patches/v4_0/private_backups.py diff --git a/frappe/patches/4_0/remove_index_sitemap.py b/frappe/patches/v4_0/remove_index_sitemap.py similarity index 100% rename from frappe/patches/4_0/remove_index_sitemap.py rename to frappe/patches/v4_0/remove_index_sitemap.py diff --git a/frappe/patches/4_0/remove_old_parent.py b/frappe/patches/v4_0/remove_old_parent.py similarity index 84% rename from frappe/patches/4_0/remove_old_parent.py rename to frappe/patches/v4_0/remove_old_parent.py index 7c738f43f4..588cda9121 100644 --- a/frappe/patches/4_0/remove_old_parent.py +++ b/frappe/patches/v4_0/remove_old_parent.py @@ -7,3 +7,4 @@ import frappe def execute(): for doctype in frappe.db.sql_list("""select name from `tabDocType` where istable=1"""): frappe.db.sql("""delete from `tab{0}` where parent like "old_par%:%" """.format(doctype)) + frappe.db.sql("""delete from `tabDocField` where parent="0" """) diff --git a/frappe/patches/4_0/rename_profile_to_user.py b/frappe/patches/v4_0/rename_profile_to_user.py similarity index 100% rename from frappe/patches/4_0/rename_profile_to_user.py rename to frappe/patches/v4_0/rename_profile_to_user.py diff --git a/frappe/patches/4_0/rename_sitemap_to_route.py b/frappe/patches/v4_0/rename_sitemap_to_route.py similarity index 100% rename from frappe/patches/4_0/rename_sitemap_to_route.py rename to frappe/patches/v4_0/rename_sitemap_to_route.py diff --git a/frappe/patches/4_0/set_module_in_report.py b/frappe/patches/v4_0/set_module_in_report.py similarity index 100% rename from frappe/patches/4_0/set_module_in_report.py rename to frappe/patches/v4_0/set_module_in_report.py diff --git a/frappe/patches/4_0/set_todo_checked_as_closed.py b/frappe/patches/v4_0/set_todo_checked_as_closed.py similarity index 100% rename from frappe/patches/4_0/set_todo_checked_as_closed.py rename to frappe/patches/v4_0/set_todo_checked_as_closed.py diff --git a/frappe/patches/4_0/set_website_route_idx.py b/frappe/patches/v4_0/set_website_route_idx.py similarity index 100% rename from frappe/patches/4_0/set_website_route_idx.py rename to frappe/patches/v4_0/set_website_route_idx.py diff --git a/frappe/patches/4_0/update_datetime.py b/frappe/patches/v4_0/update_datetime.py similarity index 100% rename from frappe/patches/4_0/update_datetime.py rename to frappe/patches/v4_0/update_datetime.py diff --git a/frappe/patches/4_0/webnotes_to_frappe.py b/frappe/patches/v4_0/webnotes_to_frappe.py similarity index 100% rename from frappe/patches/4_0/webnotes_to_frappe.py rename to frappe/patches/v4_0/webnotes_to_frappe.py diff --git a/frappe/patches/4_0/website_sitemap_hierarchy.py b/frappe/patches/v4_0/website_sitemap_hierarchy.py similarity index 100% rename from frappe/patches/4_0/website_sitemap_hierarchy.py rename to frappe/patches/v4_0/website_sitemap_hierarchy.py From d6cc80f37b824ec0194749215610d47a27db7831 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 13 May 2014 16:13:24 +0530 Subject: [PATCH 63/98] Custom field validation msg fixes --- frappe/core/doctype/custom_field/custom_field.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/custom_field/custom_field.py b/frappe/core/doctype/custom_field/custom_field.py index 5b64ef30a4..54c21ef3e4 100644 --- a/frappe/core/doctype/custom_field/custom_field.py +++ b/frappe/core/doctype/custom_field/custom_field.py @@ -59,7 +59,8 @@ class CustomField(Document): if not self.insert_after: return if not frappe.get_meta(self.dt).get_field(self.insert_after): - frappe.throw(_("Field {0} does not exist"), frappe.DoesNotExistError) + frappe.throw(_("Insert After field: {0} mentioned in Custom Field: {1} does not exist") + .format(self.insert_after, self.label), frappe.DoesNotExistError) frappe.db.sql("""\ DELETE FROM `tabProperty Setter` From a18a3d1e1760572ab45fd8730d766bd11ffc7682 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 13 May 2014 16:44:59 +0530 Subject: [PATCH 64/98] Fixed /about. Fixes frappe/erpnext#1629 --- frappe/templates/pages/about.html | 24 ++++++++++++------------ frappe/templates/pages/about.py | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/frappe/templates/pages/about.html b/frappe/templates/pages/about.html index a9b2940062..77ba712e17 100644 --- a/frappe/templates/pages/about.html +++ b/frappe/templates/pages/about.html @@ -2,27 +2,27 @@ {% block content %}
    - {{ doc.company_introduction or """

    Some Introduction about your company that you would - like your website visitor to know. - More people than you think will read your About page. - People always like to know who the are doing business with. - Be authentic and avoid using jargon like 'value added services' etc. - Be sure to update your company history and + {{ doc.company_introduction or """

    Some Introduction about your company that you would + like your website visitor to know. + More people than you think will read your About page. + People always like to know who the are doing business with. + Be authentic and avoid using jargon like 'value added services' etc. + Be sure to update your company history and list of key team members in Website > About Us Settings

    """ }} - - {% if obj.get({"doctype":"Company History"}) %} + + {% if doc.get({"doctype":"Company History"}) %}

    {{ doc.company_history_heading or "Company History" }}

    - {% for d in obj.get({"doctype":"Company History"}) %} + {% for d in doc.get({"doctype":"Company History"}) %}

    {{ d.year }}

    {{ d.highlight }}

    {% endfor %} {% endif %} - - {% if obj.get({"doctype":"About Us Team Member"}) %} + + {% if doc.get({"doctype":"About Us Team Member"}) %}

    {{ doc.team_members_heading or "Team Members" }}

    - {% for d in obj.get({"doctype":"About Us Team Member"}) %} + {% for d in doc.get({"doctype":"About Us Team Member"}) %}
    diff --git a/frappe/templates/pages/about.py b/frappe/templates/pages/about.py index 13554fe53b..b693a703c4 100644 --- a/frappe/templates/pages/about.py +++ b/frappe/templates/pages/about.py @@ -1,8 +1,8 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt from __future__ import unicode_literals import frappe def get_context(context): - return { "obj": frappe.get_doc("About Us Settings", "About Us Settings") } + return { "doc": frappe.get_doc("About Us Settings", "About Us Settings") } From 32407a8e1a1a2b5f17640ff98d9f648c095bc747 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 13 May 2014 16:45:35 +0530 Subject: [PATCH 65/98] Ability to show section border option when creating layout --- frappe/public/js/frappe/form/layout.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js index 472fd57ea5..ad3077bca6 100644 --- a/frappe/public/js/frappe/form/layout.js +++ b/frappe/public/js/frappe/form/layout.js @@ -122,12 +122,20 @@ frappe.ui.form.Layout = Class.extend({ if(df && df.idx===1) head.css({"margin-top": "0px"}) - if(this.sections.length > 1) - this.section.css({ - "margin-top": "15px", - "border-top": "1px solid #eee" - }); } + + if(df.label || df.show_section_border) { + if(this.sections.length > 1) { + this.section.css("border-top", "1px solid #eee"); + + if (df.label) { + this.section.css("margin-top", "15px"); + } else { + this.section.css("padding-top", "15px"); + } + } + } + if(df.description) { $('
    ' + df.description + '
    ') .css("padding-left", "40px") From 275f7553b4da3360369129b27679e3f84be5b96e Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 13 May 2014 17:54:17 +0530 Subject: [PATCH 66/98] Changed Print Format files to lowercase --- frappe/model/sync.py | 10 +++++----- frappe/modules/__init__.py | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/frappe/model/sync.py b/frappe/model/sync.py index 1ad92d78ec..0c285f423a 100644 --- a/frappe/model/sync.py +++ b/frappe/model/sync.py @@ -25,14 +25,14 @@ def walk_and_sync(start_path, force=0, sync_everything = False, verbose=False): """walk and sync all doctypes and pages""" modules = [] - - document_type = ['doctype', 'page', 'report'] + + document_type = ['doctype', 'page', 'report', 'print_format'] for path, folders, files in os.walk(start_path): # sort folders so that doctypes are synced before pages or reports for dontwalk in (".git", "locale", "public"): - if dontwalk in folders: + if dontwalk in folders: folders.remove(dontwalk) folders.sort() @@ -47,10 +47,10 @@ def walk_and_sync(start_path, force=0, sync_everything = False, verbose=False): module_name = path.split(os.sep)[-3] doctype = path.split(os.sep)[-2] name = path.split(os.sep)[-1] - + if import_file_by_path(os.path.join(path, f), force=force) and verbose: print module_name + ' | ' + doctype + ' | ' + name frappe.db.commit() - + return modules diff --git a/frappe/modules/__init__.py b/frappe/modules/__init__.py index 50950de9dc..9f8890c482 100644 --- a/frappe/modules/__init__.py +++ b/frappe/modules/__init__.py @@ -1,5 +1,5 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt from __future__ import unicode_literals """ @@ -8,12 +8,12 @@ from __future__ import unicode_literals import frappe, os import frappe.utils -lower_case_files_for = ['DocType', 'Page', 'Report', - "Workflow", 'Module Def', 'Desktop Item', 'Workflow State', 'Workflow Action'] +lower_case_files_for = ['DocType', 'Page', 'Report', + "Workflow", 'Module Def', 'Desktop Item', 'Workflow State', 'Workflow Action', 'Print Format'] def scrub(txt): return frappe.scrub(txt) - + def scrub_dt_dn(dt, dn): """Returns in lowercase and code friendly names of doctype and name for certain types""" ndt, ndn = dt, dn @@ -21,11 +21,11 @@ def scrub_dt_dn(dt, dn): ndt, ndn = scrub(dt), scrub(dn) return ndt, ndn - + def get_module_path(module): """Returns path of the given module""" return frappe.get_module_path(module) - + def get_doc_path(module, doctype, name): dt, dn = scrub_dt_dn(doctype, name) return os.path.join(get_module_path(module), dt, dn) @@ -48,9 +48,9 @@ def load_doctype_module(doctype, module=None, prefix=""): if not module: module = get_doctype_module(doctype) return frappe.get_module(get_module_name(doctype, module, prefix)) - + def get_module_name(doctype, module, prefix=""): from frappe.modules import scrub return '{app}.{module}.doctype.{doctype}.{prefix}{doctype}'.format(\ - app = scrub(frappe.local.module_app[scrub(module)]), + app = scrub(frappe.local.module_app[scrub(module)]), module = scrub(module), doctype = scrub(doctype), prefix=prefix) From df491cd278b6f9234c97880ab83cafb6ee5d8592 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 13 May 2014 18:54:17 +0530 Subject: [PATCH 67/98] minor fix --- frappe/model/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 6e78f27033..5be7db4b98 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -129,7 +129,7 @@ def update_reports(doctype, old_fieldname, new_fieldname): return new_sort_by, updated reports = frappe.db.sql("""select name, json from tabReport - where report_type = 'Report Builder' and ifnull(is_standard, 'No') = 'Yes' + where report_type = 'Report Builder' and ifnull(is_standard, 'No') = 'No' and json like %s and json like %s""", ('%%%s%%' % old_fieldname , '%%%s%%' % doctype), as_dict=True) From b0b163918c5fa607c75f804bcd704d1a6f8b51e4 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 13 May 2014 19:36:13 +0530 Subject: [PATCH 68/98] Removed unused code from patch_handler --- frappe/modules/patch_handler.py | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/frappe/modules/patch_handler.py b/frappe/modules/patch_handler.py index 671d18491e..d28863cb1d 100644 --- a/frappe/modules/patch_handler.py +++ b/frappe/modules/patch_handler.py @@ -73,29 +73,15 @@ def execute_patch(patchmodule, method=None, methodargs=None): frappe.db.rollback() tb = frappe.get_traceback() log(tb) - import os - if frappe.request: - add_to_patch_log(tb) block_user(False) if success: log('Success') return success -def add_to_patch_log(tb): - """add error log to patches/patch.log""" - import conf, os - # TODO use get_site_base_path - with open(os.path.join(os.path.dirname(conf.__file__), 'app', 'patches','patch.log'),'a') as patchlog: - patchlog.write('\n\n' + tb) - def update_patch_log(patchmodule): """update patch_file in patch log""" - if frappe.db.table_exists("__PatchLog"): - frappe.db.sql("""INSERT INTO `__PatchLog` VALUES (%s, now())""", \ - patchmodule) - else: - frappe.get_doc({"doctype": "Patch Log", "patch": patchmodule}).insert() + frappe.get_doc({"doctype": "Patch Log", "patch": patchmodule}).insert() def executed(patchmodule): """return True if is executed""" @@ -117,10 +103,6 @@ def check_session_stopped(): frappe.msgprint(frappe.db.get_global("__session_status_message")) raise frappe.SessionStopped('Session Stopped') -def setup(): - frappe.db.sql("""CREATE TABLE IF NOT EXISTS `__PatchLog` ( - patch TEXT, applied_on DATETIME) engine=InnoDB""") - def log(msg): if getattr(frappe.local, "patch_log_list", None) is None: frappe.local.patch_log_list = [] From ac2261e114c7eaf2c83867310c78110ebdb3ea9d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 13 May 2014 21:00:41 +0530 Subject: [PATCH 69/98] Fixed patch handler and update_reports method --- frappe/cli.py | 9 ------- frappe/model/__init__.py | 45 +++++++++++++++++---------------- frappe/modules/import_file.py | 2 +- frappe/modules/patch_handler.py | 8 +++--- frappe/patches.txt | 2 +- 5 files changed, 28 insertions(+), 38 deletions(-) diff --git a/frappe/cli.py b/frappe/cli.py index 4a1994026e..60b882800e 100755 --- a/frappe/cli.py +++ b/frappe/cli.py @@ -365,11 +365,7 @@ def latest(rebuild_website_config=True, quiet=False): try: # run patches - frappe.local.patch_log_list = [] frappe.modules.patch_handler.run_all() - if verbose: - print "\n".join(frappe.local.patch_log_list) - # sync frappe.model.sync.sync_all(verbose=verbose) sync_fixtures() @@ -382,9 +378,6 @@ def latest(rebuild_website_config=True, quiet=False): frappe.translate.clear_cache() - except frappe.modules.patch_handler.PatchError: - print "\n".join(frappe.local.patch_log_list) - raise finally: frappe.destroy() @@ -400,9 +393,7 @@ def sync_all(force=False, quiet=False): def patch(patch_module, force=False): import frappe.modules.patch_handler frappe.connect() - frappe.local.patch_log_list = [] frappe.modules.patch_handler.run_single(patch_module, force=force) - print "\n".join(frappe.local.patch_log_list) frappe.destroy() @cmd diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 5be7db4b98..2654c3f765 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -116,57 +116,58 @@ def rename_field(doctype, old_fieldname, new_fieldname): update_users_report_view_settings(doctype, old_fieldname) def update_reports(doctype, old_fieldname, new_fieldname): - def _get_new_sort_by(old_sort_by, updated): - new_sort_by = "" - if old_sort_by: - sort_by_doctype, sort_by_field = old_sort_by.split(".") - if sort_by_field == old_fieldname and sort_by_doctype == doctype: - new_sort_by = doctype + "." + new_fieldname - updated = True - else: - new_sort_by = old_sort_by + def _get_new_sort_by(report_dict, report): + sort_by = report_dict("sort_by") or "" + if sort_by: + sort_by = (report_dict("sort_by") or "").split(".") + if len(sort_by) > 1: + if sort_by[0]==doctype and sort_by[1]==old_fieldname: + sort_by = doctype + "." + new_fieldname + report_dict["updated"] = True + elif report.ref_doctype == doctype and sort_by[0]==old_fieldname: + sort_by = doctype + "." + new_fieldname + report_dict["updated"] = True - return new_sort_by, updated + return sort_by - reports = frappe.db.sql("""select name, json from tabReport + reports = frappe.db.sql("""select name, ref_doctype, json from tabReport where report_type = 'Report Builder' and ifnull(is_standard, 'No') = 'No' and json like %s and json like %s""", ('%%%s%%' % old_fieldname , '%%%s%%' % doctype), as_dict=True) for r in reports: - updated = False - val = json.loads(r.json) + report_dict = json.loads(r.json) # update filters new_filters = [] - for f in val.get("filters"): + for f in report_dict.get("filters"): if f[0] == doctype and f[1] == old_fieldname: new_filters.append([doctype, new_fieldname, f[2], f[3]]) - updated = True + report_dict["updated"] = True else: new_filters.append(f) # update columns new_columns = [] - for c in val.get("columns"): + for c in report_dict.get("columns"): if c[0] == old_fieldname and c[1] == doctype: new_columns.append([new_fieldname, doctype]) - updated = True + report_dict["updated"] = True else: new_columns.append(c) # update sort by - new_sort_by, updated = _get_new_sort_by(val.get("sort_by"), updated) - new_sort_by_next, updated = _get_new_sort_by(val.get("sort_by_next"), updated) + new_sort_by = _get_new_sort_by(report_dict, r) + new_sort_by_next = _get_new_sort_by(report_dict, r) - if updated: + if report_dict.get("updated"): new_val = json.dumps({ "filters": new_filters, "columns": new_columns, "sort_by": new_sort_by, - "sort_order": val.get("sort_order"), + "sort_order": report_dict.get("sort_order"), "sort_by_next": new_sort_by_next, - "sort_order_next": val.get("sort_order_next") + "sort_order_next": report_dict.get("sort_order_next") }) frappe.db.sql("""update `tabReport` set `json`=%s where name=%s""", (new_val, r.name)) diff --git a/frappe/modules/import_file.py b/frappe/modules/import_file.py index ca905d6fa1..87db8652aa 100644 --- a/frappe/modules/import_file.py +++ b/frappe/modules/import_file.py @@ -51,7 +51,7 @@ def import_file_by_path(path, force=False): if original_modified: # since there is a new timestamp on the file, update timestamp in - if doc["doctype"] == doc["name"]: + if doc["doctype"] == doc["name"] and doc["name"]!="DocType": frappe.db.sql("""update tabSingles set value=%s where field="modified" and doctype=%s""", (original_modified, doc["name"])) else: diff --git a/frappe/modules/patch_handler.py b/frappe/modules/patch_handler.py index d28863cb1d..2f7fee8055 100644 --- a/frappe/modules/patch_handler.py +++ b/frappe/modules/patch_handler.py @@ -57,7 +57,8 @@ def execute_patch(patchmodule, method=None, methodargs=None): block_user(True) frappe.db.begin() try: - log('Executing %s in %s' % (patchmodule or str(methodargs), frappe.db.cur_db_name)) + log('Executing {patch} in {site} ({db})'.format(patch=patchmodule or str(methodargs), + site=frappe.local.site, db=frappe.db.cur_db_name)) if patchmodule: if patchmodule.startswith("execute:"): exec patchmodule.split("execute:")[1] in globals() @@ -104,7 +105,4 @@ def check_session_stopped(): raise frappe.SessionStopped('Session Stopped') def log(msg): - if getattr(frappe.local, "patch_log_list", None) is None: - frappe.local.patch_log_list = [] - - frappe.local.patch_log_list.append(msg) + print msg diff --git a/frappe/patches.txt b/frappe/patches.txt index 74e3f77eae..623bad9928 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -9,6 +9,7 @@ execute:frappe.reload_doc('core', 'doctype', 'report') #2013-13-26 execute:frappe.reload_doc('core', 'doctype', 'version') #2014-02-21 execute:frappe.db.sql("alter table `tabSessions` modify `user` varchar(255), engine=InnoDB") +frappe.patches.v4_0.remove_old_parent frappe.patches.v4_0.remove_index_sitemap frappe.patches.v4_0.add_delete_permission frappe.patches.v4_0.move_match_to_restricted @@ -22,7 +23,6 @@ frappe.patches.v4_0.set_website_route_idx execute:import frappe.installer;frappe.installer.make_site_dirs() #2014-02-19 frappe.patches.v4_0.private_backups frappe.patches.v4_0.set_module_in_report -frappe.patches.v4_0.remove_old_parent frappe.patches.v4_0.update_datetime frappe.patches.v4_0.deprecate_control_panel frappe.patches.v4_0.file_manager_hooks From 7f72044f2e6e4d811e8cfd4cda0b903598ee9c2a Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Tue, 13 May 2014 21:55:24 +0530 Subject: [PATCH 70/98] fix in update_reports --- frappe/model/__init__.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 2654c3f765..da6a4f51f8 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -116,10 +116,10 @@ def rename_field(doctype, old_fieldname, new_fieldname): update_users_report_view_settings(doctype, old_fieldname) def update_reports(doctype, old_fieldname, new_fieldname): - def _get_new_sort_by(report_dict, report): - sort_by = report_dict("sort_by") or "" + def _get_new_sort_by(report_dict, report, key): + sort_by = report_dict.get(key) or "" if sort_by: - sort_by = (report_dict("sort_by") or "").split(".") + sort_by = sort_by.split(".") if len(sort_by) > 1: if sort_by[0]==doctype and sort_by[1]==old_fieldname: sort_by = doctype + "." + new_fieldname @@ -127,6 +127,8 @@ def update_reports(doctype, old_fieldname, new_fieldname): elif report.ref_doctype == doctype and sort_by[0]==old_fieldname: sort_by = doctype + "." + new_fieldname report_dict["updated"] = True + else: + sort_by = '.'.join(sort_by) return sort_by @@ -157,8 +159,8 @@ def update_reports(doctype, old_fieldname, new_fieldname): new_columns.append(c) # update sort by - new_sort_by = _get_new_sort_by(report_dict, r) - new_sort_by_next = _get_new_sort_by(report_dict, r) + new_sort_by = _get_new_sort_by(report_dict, r, "sort_by") + new_sort_by_next = _get_new_sort_by(report_dict, r, "sort_by_next") if report_dict.get("updated"): new_val = json.dumps({ From edcfbba72b7f33ecd2c98e3ba449398d0eaab213 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 13 May 2014 22:47:16 +0530 Subject: [PATCH 71/98] Fixed get_user_lang --- frappe/translate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index 19ee7bb9e6..373d6382cb 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -66,7 +66,7 @@ def get_user_lang(user=None): default_lang = frappe.db.get_default("lang") lang = default_lang or frappe.local.lang - frappe.cache().set_value("lang:" + user, lang) + frappe.cache().set_value("lang:" + user, lang or "en") return lang @@ -133,7 +133,7 @@ def get_lang_js(fortype, name): return "\n\n$.extend(frappe._messages, %s)" % json.dumps(get_dict(fortype, name)) def get_full_dict(lang): - if not lang or lang == "en": + if lang == "en": return {} return frappe.cache().get_value("lang:" + lang, lambda:load_lang(lang)) From a1362fd0b4b3587faeb788eddd40f530413270b1 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Tue, 13 May 2014 22:50:05 +0530 Subject: [PATCH 72/98] speedup clear cache during patches by clearing tabSessions --- frappe/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/patches.txt b/frappe/patches.txt index 623bad9928..13de606fce 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -1,6 +1,7 @@ execute:import inlinestyler # new requirement execute:frappe.db.sql("""update `tabPatch Log` set patch=replace(patch, '.4_0.', '.v4_0.')""") #2014-05-12 +execute:frappe.db.sql("""delete from tabSessions""") #2014-05-13 execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2014-01-24 execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2014-03-01 execute:frappe.reload_doc('core', 'doctype', 'docperm') #2013-13-26 From 7d6b89cdb3f4fba9579ccd6928876d1c5a82b836 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Tue, 13 May 2014 22:58:45 +0530 Subject: [PATCH 73/98] Revert "speedup clear cache during patches by clearing tabSessions" This reverts commit a1362fd0b4b3587faeb788eddd40f530413270b1. --- frappe/patches.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/patches.txt b/frappe/patches.txt index 13de606fce..623bad9928 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -1,7 +1,6 @@ execute:import inlinestyler # new requirement execute:frappe.db.sql("""update `tabPatch Log` set patch=replace(patch, '.4_0.', '.v4_0.')""") #2014-05-12 -execute:frappe.db.sql("""delete from tabSessions""") #2014-05-13 execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2014-01-24 execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2014-03-01 execute:frappe.reload_doc('core', 'doctype', 'docperm') #2013-13-26 From dd93e2b3540b802b3b0cdbe46d2d99a13d43520b Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 14 May 2014 13:32:24 +0530 Subject: [PATCH 74/98] Patch to set language in System Settings --- frappe/patches.txt | 1 + .../v4_0/set_language_in_system_settings.py | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 frappe/patches/v4_0/set_language_in_system_settings.py diff --git a/frappe/patches.txt b/frappe/patches.txt index 623bad9928..fca6092bdd 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -28,3 +28,4 @@ frappe.patches.v4_0.deprecate_control_panel frappe.patches.v4_0.file_manager_hooks execute:frappe.get_doc("User", "Guest").save() frappe.patches.v4_0.deprecate_link_selects +frappe.patches.v4_0.set_language_in_system_settings diff --git a/frappe/patches/v4_0/set_language_in_system_settings.py b/frappe/patches/v4_0/set_language_in_system_settings.py new file mode 100644 index 0000000000..162641cd69 --- /dev/null +++ b/frappe/patches/v4_0/set_language_in_system_settings.py @@ -0,0 +1,23 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe +from collections import Counter +from frappe.core.doctype.user.user import STANDARD_USERS + +def execute(): + if frappe.db.get_value("System Settings", "System Settings", "language"): + return + + # find most common language + lang = frappe.db.sql_list("""select language from `tabUser` + where ifnull(language, '')!='' and language not like "Loading%%" and name not in ({standard_users})""".format( + standard_users=", ".join(["%s"]*len(STANDARD_USERS))), tuple(STANDARD_USERS)) + lang = Counter(lang).most_common(1) + lang = (len(lang) > 0) and lang[0][0] or "english" + + # set language in System Settings + system_settings = frappe.get_doc("System Settings") + system_settings.language = lang + system_settings.save() From da26421eeb765b023e28be238dff78dd6fa9247e Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 14 May 2014 13:45:02 +0530 Subject: [PATCH 75/98] Set gravatar for users whose image is missing --- frappe/patches.txt | 1 + frappe/patches/v4_0/set_user_gravatar.py | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 frappe/patches/v4_0/set_user_gravatar.py diff --git a/frappe/patches.txt b/frappe/patches.txt index fca6092bdd..5b3f89b96c 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -29,3 +29,4 @@ frappe.patches.v4_0.file_manager_hooks execute:frappe.get_doc("User", "Guest").save() frappe.patches.v4_0.deprecate_link_selects frappe.patches.v4_0.set_language_in_system_settings +frappe.patches.v4_0.set_user_gravatar diff --git a/frappe/patches/v4_0/set_user_gravatar.py b/frappe/patches/v4_0/set_user_gravatar.py new file mode 100644 index 0000000000..78bd72f4dc --- /dev/null +++ b/frappe/patches/v4_0/set_user_gravatar.py @@ -0,0 +1,11 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + for name in frappe.db.sql_list("select name from `tabUser` where ifnull(user_image, '')=''"): + user = frappe.get_doc("User", name) + user.update_gravatar() + user.db_set("user_image", user.user_image) From 757baf30128fe8a3b8174518144d55f410d29503 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 14 May 2014 17:36:48 +0530 Subject: [PATCH 76/98] Don't clear Actions menu on dirty, prevent mapping of doc if unsaved, downloadify fix, set global language fix --- .../v4_0/set_language_in_system_settings.py | 1 + frappe/public/js/frappe/form/toolbar.js | 5 +++- frappe/public/js/frappe/misc/tools.js | 30 +++++++++---------- frappe/public/js/frappe/model/create_new.js | 8 +++++ 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/frappe/patches/v4_0/set_language_in_system_settings.py b/frappe/patches/v4_0/set_language_in_system_settings.py index 162641cd69..8c9b2e43d0 100644 --- a/frappe/patches/v4_0/set_language_in_system_settings.py +++ b/frappe/patches/v4_0/set_language_in_system_settings.py @@ -20,4 +20,5 @@ def execute(): # set language in System Settings system_settings = frappe.get_doc("System Settings") system_settings.language = lang + system_settings.ignore_mandatory = True system_settings.save() diff --git a/frappe/public/js/frappe/form/toolbar.js b/frappe/public/js/frappe/form/toolbar.js index 1979feb745..b8401c69a8 100644 --- a/frappe/public/js/frappe/form/toolbar.js +++ b/frappe/public/js/frappe/form/toolbar.js @@ -207,7 +207,10 @@ frappe.ui.form.Toolbar = Class.extend({ current = this.appframe.get_title_right_text(), status = null; - this.appframe.clear_primary_action(); + if (!this.frm.doc.__unsaved) { + // don't clear actions menu if dirty + this.appframe.clear_primary_action(); + } if (this.can_submit()) { status = "Submit"; diff --git a/frappe/public/js/frappe/misc/tools.js b/frappe/public/js/frappe/misc/tools.js index 5614181da0..1a8d6195d4 100644 --- a/frappe/public/js/frappe/misc/tools.js +++ b/frappe/public/js/frappe/misc/tools.js @@ -8,17 +8,17 @@ frappe.tools.downloadify = function(data, roles, me) { msgprint(__("Export not allowed. You need {0} role to export.", [frappe.utils.comma_or(roles)])); return; } - + var _get_data = function() { return frappe.tools.to_csv(data); }; var flash_disabled = (navigator.mimeTypes["application/x-shockwave-flash"] == undefined); - + var download_from_server = function() { open_url_post("/", { args: { data: data, filename: me.title }, cmd: "frappe.utils.datautils.send_csv_to_client" }, true); } - + // save file > abt 200 kb using server call if((_get_data().length > 200000) || flash_disabled) { download_from_server(); @@ -36,8 +36,8 @@ frappe.tools.downloadify = function(data, roles, me) { return me.title + '.csv'; }, data: _get_data, - swf: 'lib/js/lib/downloadify/downloadify.swf', - downloadImage: 'lib/js/lib/downloadify/download.png', + swf: 'assets/frappe/js/lib/downloadify/downloadify.swf', + downloadImage: 'assets/frappe/js/lib/downloadify/download.png', onComplete: function(){ $(msgobj.msg_area).html("

    Saved

    ") }, @@ -46,7 +46,7 @@ frappe.tools.downloadify = function(data, roles, me) { width: 100, height: 30, transparent: true, - append: false + append: false }); } }; @@ -56,11 +56,11 @@ frappe.markdown = function(txt) { frappe.require('assets/frappe/js/lib/markdown.js'); frappe.md2html = new Showdown.converter(); } - + while(txt.substr(0,1)==="\n") { txt = txt.substr(1); } - + // remove leading tab (if they exist in the first line) var whitespace_len = 0, first_line = txt.split("\n")[0]; @@ -69,7 +69,7 @@ frappe.markdown = function(txt) { whitespace_len++; first_line = first_line.substr(1); } - + if(whitespace_len && whitespace_len != first_line.length) { var txt1 = []; $.each(txt.split("\n"), function(i, t) { @@ -77,7 +77,7 @@ frappe.markdown = function(txt) { }) txt = txt1.join("\n"); } - + return frappe.md2html.makeHtml(txt); } @@ -109,9 +109,9 @@ frappe.slickgrid_tools = { } row.push(val); }); - + if(!filter || filter(row, d)) { - res.push(row); + res.push(row); } } return [col_row].concat(res); @@ -119,7 +119,7 @@ frappe.slickgrid_tools = { add_property_setter_on_resize: function(grid) { grid.onColumnsResized.subscribe(function(e, args) { $.each(grid.getColumns(), function(i, col) { - if(col.docfield && col.previousWidth != col.width && + if(col.docfield && col.previousWidth != col.width && !in_list(frappe.model.std_fields_list, col.docfield.fieldname) ) { frappe.call({ method:"frappe.client.make_width_property_setter", @@ -131,7 +131,7 @@ frappe.slickgrid_tools = { field_name: col.docfield.fieldname, property: 'width', value: col.width, - "__islocal": 1 + "__islocal": 1 }] } }); @@ -140,5 +140,5 @@ frappe.slickgrid_tools = { } }); }); - } + } }; diff --git a/frappe/public/js/frappe/model/create_new.js b/frappe/public/js/frappe/model/create_new.js index 2b56ffe896..839ca14b2b 100644 --- a/frappe/public/js/frappe/model/create_new.js +++ b/frappe/public/js/frappe/model/create_new.js @@ -166,6 +166,14 @@ $.extend(frappe.model, { }, open_mapped_doc: function(opts) { + if (opts.frm && opts.frm.doc.__unsaved) { + frappe.throw(__("You have unsaved changes in this form. Please save before you continue.")); + + } else if (!opts.source_name && opts.frm) { + opts.source_name = opts.frm.doc.name; + + } + return frappe.call({ type: "GET", method: opts.method, From 5b29d145b37ac3380b801e47ef3fe9da0b60b016 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 14 May 2014 18:14:38 +0530 Subject: [PATCH 77/98] Set report filter label while loading --- frappe/public/js/frappe/ui/filters.js | 106 +++++++++++++------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/frappe/public/js/frappe/ui/filters.js b/frappe/public/js/frappe/ui/filters.js index 50ba37d494..1b9409e1a6 100644 --- a/frappe/public/js/frappe/ui/filters.js +++ b/frappe/public/js/frappe/ui/filters.js @@ -18,7 +18,7 @@ frappe.ui.FilterList = Class.extend({ me.listobj.run(); }); }, - + show_filters: function() { this.$w.find('.show_filters').toggle(); if(!this.filters.length) @@ -29,7 +29,7 @@ frappe.ui.FilterList = Class.extend({ this.filters = []; this.$w.find('.filter_area').empty(); }, - + add_filter: function(tablename, fieldname, condition, value) { this.push_new_filter(tablename, fieldname, condition, value); // list must be expanded @@ -37,7 +37,7 @@ frappe.ui.FilterList = Class.extend({ this.$w.find('.show_filters').toggle(true); } }, - + push_new_filter: function(tablename, fieldname, condition, value) { this.filters.push(new frappe.ui.Filter({ flist: this, @@ -47,7 +47,7 @@ frappe.ui.FilterList = Class.extend({ value: value })); }, - + get_filters: function() { // get filter values as dict var values = []; @@ -57,7 +57,7 @@ frappe.ui.FilterList = Class.extend({ }) return values; }, - + // remove hidden filters update_filters: function() { var fl = []; @@ -66,7 +66,7 @@ frappe.ui.FilterList = Class.extend({ }) this.filters = fl; }, - + get_filter: function(fieldname) { for(var i in this.filters) { if(this.filters[i].field && this.filters[i].field.df.fieldname==fieldname) @@ -109,29 +109,30 @@ frappe.ui.Filter = Class.extend({ make_select: function() { var me = this; this.fieldselect = new frappe.ui.FieldSelect({ - parent: this.$w.find('.fieldname_select_area'), - doctype: this.doctype, - filter_fields: this.filter_fields, + parent: this.$w.find('.fieldname_select_area'), + doctype: this.doctype, + filter_fields: this.filter_fields, select: function(doctype, fieldname) { me.set_field(doctype, fieldname); } }); + this.fieldselect.set_value(this.doctype, this.fieldname); }, set_events: function() { var me = this; - - this.$w.find('a.close').bind('click', function() { + + this.$w.find('a.close').bind('click', function() { me.$w.css('display','none'); var value = me.field.get_parsed_value(); var fieldname = me.field.df.fieldname; me.field = null; - + // hide filter section if(!me.flist.get_filters().length) { me.flist.$w.find('.set_filters').toggle(true); me.flist.$w.find('.show_filters').toggle(false); } - + me.flist.update_filters(); me.flist.listobj.dirty = true; me.flist.listobj.run(); @@ -144,33 +145,33 @@ frappe.ui.Filter = Class.extend({ me.set_field(me.field.df.parent, me.field.df.fieldname, 'Data'); if(!me.field.desc_area) me.field.desc_area = $a(me.field.wrapper, 'span', 'help', null, - 'values separated by comma'); + 'values separated by comma'); } else { - me.set_field(me.field.df.parent, me.field.df.fieldname, null, - me.$w.find('.condition').val()); + me.set_field(me.field.df.parent, me.field.df.fieldname, null, + me.$w.find('.condition').val()); } }); - + // set the field if(me.fieldname) { // presents given (could be via tags!) this.set_values(me.tablename, me.fieldname, me.condition, me.value); } else { me.set_field(me.doctype, 'name'); - } + } }, - + set_values: function(tablename, fieldname, condition, value) { // presents given (could be via tags!) this.set_field(tablename, fieldname); if(condition) this.$w.find('.condition').val(condition).change(); if(value!=null) this.field.set_input(value); }, - + set_field: function(doctype, fieldname, fieldtype, condition) { var me = this; - + // set in fieldname (again) var cur = me.field ? { fieldname: me.field.df.fieldname, @@ -185,13 +186,12 @@ frappe.ui.Filter = Class.extend({ return; } - var df = copy_dict(me.fieldselect.fields_by_name[doctype][fieldname]); this.set_fieldtype(df, fieldtype); - - // called when condition is changed, + + // called when condition is changed, // don't change if all is well - if(me.field && cur.fieldname == fieldname && df.fieldtype == cur.fieldtype && + if(me.field && cur.fieldname == fieldname && df.fieldtype == cur.fieldtype && df.parent == cur.parent) { return; } @@ -199,13 +199,13 @@ frappe.ui.Filter = Class.extend({ // clear field area and make field me.fieldselect.selected_doctype = doctype; me.fieldselect.selected_fieldname = fieldname; - + // save old text var old_text = null; if(me.field) { old_text = me.field.get_parsed_value(); } - + var field_area = me.$w.find('.filter_field').empty().get(0); var f = frappe.ui.form.make_control({ df: df, @@ -213,13 +213,13 @@ frappe.ui.Filter = Class.extend({ only_input: true, }) f.refresh(); - + me.field = f; - if(old_text) + if(old_text && me.field.df.fieldtype===cur.fieldtype) me.field.set_input(old_text); - + if(!condition) this.set_default_condition(df, fieldtype); - + // run on enter $(me.field.wrapper).find(':input').keydown(function(ev) { if(ev.which==13) { @@ -227,33 +227,33 @@ frappe.ui.Filter = Class.extend({ } }) }, - + set_fieldtype: function(df, fieldtype) { // reset if(df.original_type) df.fieldtype = df.original_type; else df.original_type = df.fieldtype; - + df.description = ''; df.reqd = 0; - + // given if(fieldtype) { df.fieldtype = fieldtype; return; - } - + } + // scrub if(df.fieldtype=='Check') { df.fieldtype='Select'; df.options='No\nYes'; } else if(['Text','Small Text','Text Editor','Code','Tags','Comments'].indexOf(df.fieldtype)!=-1) { - df.fieldtype = 'Data'; + df.fieldtype = 'Data'; } else if(df.fieldtype=='Link' && this.$w.find('.condition').val()!="=") { df.fieldtype = 'Data'; } }, - + set_default_condition: function(df, fieldtype) { if(!fieldtype) { // set as "like" for data fields @@ -261,27 +261,27 @@ frappe.ui.Filter = Class.extend({ this.$w.find('.condition').val('like'); } else { this.$w.find('.condition').val('='); - } - } + } + } }, - + get_value: function() { var me = this; var val = me.field.get_parsed_value(); var cond = me.$w.find('.condition').val(); - + if(me.field.df.original_type == 'Check') { val = (val=='Yes' ? 1 :0); } - + if(cond=='like') { // add % only if not there at the end if ((val.length === 0) || (val.lastIndexOf("%") !== (val.length - 1))) { val = (val || "") + '%'; } } else if(val === '%') val = null; - - return [me.fieldselect.selected_doctype, + + return [me.fieldselect.selected_doctype, me.field.df.fieldname, me.$w.find('.condition').val(), val]; } @@ -311,10 +311,10 @@ frappe.ui.FieldSelect = Class.extend({ return false; } }); - + if(this.filter_fields) { for(var i in this.filter_fields) - this.add_field_option(this.filter_fields[i]) + this.add_field_option(this.filter_fields[i]) } else { this.build_options(); } @@ -340,14 +340,14 @@ frappe.ui.FieldSelect = Class.extend({ var me = this; this.clear(); if(!doctype) return; - + // old style if(doctype.indexOf(".")!==-1) { parts = doctype.split("."); doctype = parts[0]; fieldname = parts[1]; } - + $.each(this.options, function(i, v) { if(v.doctype===doctype && v.fieldname===fieldname) { me.selected_doctype = doctype; @@ -365,7 +365,7 @@ frappe.ui.FieldSelect = Class.extend({ if(d.fieldname=="name") opts.options = me.doctype; return $.extend(copy_dict(d), opts); }); - + // add parenttype column var doctype_obj = locals['DocType'][me.doctype]; if(doctype_obj && cint(doctype_obj.istable)) { @@ -376,7 +376,7 @@ frappe.ui.FieldSelect = Class.extend({ parent: me.doctype, }]); } - + // blank if(this.with_blank) { this.options.push({ @@ -414,7 +414,7 @@ frappe.ui.FieldSelect = Class.extend({ var label = df.label + ' (' + df.parent + ')'; var table = df.parent; } - if(frappe.model.no_value_type.indexOf(df.fieldtype)==-1 && + if(frappe.model.no_value_type.indexOf(df.fieldtype)==-1 && !(me.fields_by_name[df.parent] && me.fields_by_name[df.parent][df.fieldname])) { this.options.push({ label: __(label), @@ -423,7 +423,7 @@ frappe.ui.FieldSelect = Class.extend({ doctype: df.parent }) if(!me.fields_by_name[df.parent]) me.fields_by_name[df.parent] = {}; - me.fields_by_name[df.parent][df.fieldname] = df; + me.fields_by_name[df.parent][df.fieldname] = df; } }, }) From 5556e320d0a3ec7bd8a480d2ab311b6373dbc446 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 14 May 2014 18:15:24 +0530 Subject: [PATCH 78/98] update reports sort by for renamed fields --- frappe/model/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index da6a4f51f8..067df6bc27 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -127,7 +127,8 @@ def update_reports(doctype, old_fieldname, new_fieldname): elif report.ref_doctype == doctype and sort_by[0]==old_fieldname: sort_by = doctype + "." + new_fieldname report_dict["updated"] = True - else: + + if isinstance(sort_by, list): sort_by = '.'.join(sort_by) return sort_by From 6cf7913a6873973c9974916ce79e9cf4ee247edc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 14 May 2014 18:48:04 +0530 Subject: [PATCH 79/98] Set Custom Field's Insert After values as fieldname --- .../core/doctype/custom_field/custom_field.js | 2 +- .../core/doctype/custom_field/custom_field.py | 7 ++++--- frappe/patches.txt | 1 + .../v4_0/update_custom_field_insert_after.py | 18 ++++++++++++++++++ 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 frappe/patches/v4_0/update_custom_field_insert_after.py diff --git a/frappe/core/doctype/custom_field/custom_field.js b/frappe/core/doctype/custom_field/custom_field.js index 90a290ad98..040cb10616 100644 --- a/frappe/core/doctype/custom_field/custom_field.js +++ b/frappe/core/doctype/custom_field/custom_field.js @@ -67,7 +67,7 @@ cur_frm.cscript.dt = function(doc, dt, dn) { var fieldnames = $.map(r.message, function(v) { return v.value; }); if(insert_after==null || !in_list(fieldnames, insert_after)) { - insert_after = fieldnames[0]; + insert_after = fieldnames[-1]; } cur_frm.set_value('insert_after', insert_after); diff --git a/frappe/core/doctype/custom_field/custom_field.py b/frappe/core/doctype/custom_field/custom_field.py index 54c21ef3e4..46b44d0e7d 100644 --- a/frappe/core/doctype/custom_field/custom_field.py +++ b/frappe/core/doctype/custom_field/custom_field.py @@ -58,9 +58,10 @@ class CustomField(Document): def create_property_setter(self): if not self.insert_after: return - if not frappe.get_meta(self.dt).get_field(self.insert_after): - frappe.throw(_("Insert After field: {0} mentioned in Custom Field: {1} does not exist") - .format(self.insert_after, self.label), frappe.DoesNotExistError) + dt_meta = frappe.get_meta(self.dt) + if not dt_meta.get_field(self.insert_after): + frappe.throw(_("Insert After field '{0}' mentioned in Custom Field '{1}', does not exist") + .format(dt_meta.get_label(self.insert_after), self.label), frappe.DoesNotExistError) frappe.db.sql("""\ DELETE FROM `tabProperty Setter` diff --git a/frappe/patches.txt b/frappe/patches.txt index 5b3f89b96c..1ba701174d 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -30,3 +30,4 @@ execute:frappe.get_doc("User", "Guest").save() frappe.patches.v4_0.deprecate_link_selects frappe.patches.v4_0.set_language_in_system_settings frappe.patches.v4_0.set_user_gravatar +frappe.patches.v4_0.update_custom_field_insert_after diff --git a/frappe/patches/v4_0/update_custom_field_insert_after.py b/frappe/patches/v4_0/update_custom_field_insert_after.py new file mode 100644 index 0000000000..0e820cd0e3 --- /dev/null +++ b/frappe/patches/v4_0/update_custom_field_insert_after.py @@ -0,0 +1,18 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + for d in frappe.db.sql("""select name, dt, insert_after from `tabCustom Field` + where docstatus < 2""", as_dict=1): + dt_meta = frappe.get_meta(d.dt) + if not dt_meta.get_field(d.insert_after): + cf = frappe.get_doc("Custom Field", d.name) + df = dt_meta.get("fields", {"label": d.insert_after}) + if df: + cf.insert_after = df[0].fieldname + else: + cf.insert_after = None + cf.save() From bc14dee451e4c2adb2ff5a157b98d20550f10621 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 14 May 2014 18:09:27 +0530 Subject: [PATCH 80/98] Added frappe --clear_all_sessions --- frappe/cli.py | 10 ++++++++++ frappe/sessions.py | 9 +++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/frappe/cli.py b/frappe/cli.py index 60b882800e..a007f83758 100755 --- a/frappe/cli.py +++ b/frappe/cli.py @@ -232,6 +232,8 @@ def setup_utilities(parser): help="Clear cache, doctype cache and defaults") parser.add_argument("--reset_perms", default=False, action="store_true", help="Reset permissions for all doctypes") + parser.add_argument("--clear_all_sessions", default=False, action="store_true", + help="Clear sessions of all users (logs them out)") # scheduler parser.add_argument("--run_scheduler", default=False, action="store_true", @@ -501,6 +503,14 @@ def clear_web(): frappe.website.render.clear_cache() frappe.destroy() +@cmd +def clear_all_sessions(): + import frappe.sessions + frappe.connect() + frappe.sessions.clear_all_sessions() + frappe.db.commit() + frappe.destroy() + @cmd def build_sitemap(): from frappe.website import rebuild_config diff --git a/frappe/sessions.py b/frappe/sessions.py index 4bbe4b821f..eb4447b234 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -71,13 +71,18 @@ def clear_sessions(user=None, keep_current=False): if keep_current and frappe.session.sid==sid[0]: continue else: - frappe.cache().delete_value("session:" + sid[0]) - frappe.db.sql("""delete from tabSessions where sid=%s""", (sid[0],)) + delete_session(sid[0]) def delete_session(sid=None): frappe.cache().delete_value("session:" + sid) frappe.db.sql("""delete from tabSessions where sid=%s""", sid) +def clear_all_sessions(): + """This effectively logs out all users""" + frappe.only_for("Administrator") + for sid in frappe.db.sql_list("select sid from `tabSessions`"): + delete_session(sid) + def get(): """get session boot info""" from frappe.core.doctype.notification_count.notification_count import \ From 2666e196414f6f00a20d65de16324035c95e9324 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 14 May 2014 18:51:59 +0530 Subject: [PATCH 81/98] Clear expired sessions every day --- frappe/hooks.py | 1 + frappe/sessions.py | 25 ++++++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/frappe/hooks.py b/frappe/hooks.py index 5b551d841d..95f2299710 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -66,6 +66,7 @@ scheduler_events = { "frappe.utils.email_lib.bulk.clear_outbox", "frappe.core.doctype.notification_count.notification_count.delete_event_notification_count", "frappe.core.doctype.event.event.send_event_digest", + "frappe.sessions.clear_expired_sessions", ], "hourly": [ "frappe.templates.generators.website_group.clear_event_cache" diff --git a/frappe/sessions.py b/frappe/sessions.py index eb4447b234..db9c8100d0 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -83,6 +83,12 @@ def clear_all_sessions(): for sid in frappe.db.sql_list("select sid from `tabSessions`"): delete_session(sid) +def clear_expired_sessions(): + """This function is meant to be called from scheduler""" + for sid in frappe.db.sql_list("""select sid + from tabSessions where TIMEDIFF(NOW(), lastupdate) > TIME(%s)""", get_expiry_period()): + delete_session(sid) + def get(): """get session boot info""" from frappe.core.doctype.notification_count.notification_count import \ @@ -145,7 +151,7 @@ class Session: self.data['data']['session_ip'] = frappe.get_request_header('REMOTE_ADDR') if self.user != "Guest": self.data['data']['last_updated'] = frappe.utils.now() - self.data['data']['session_expiry'] = self.get_expiry_period() + self.data['data']['session_expiry'] = get_expiry_period() self.data['data']['session_country'] = get_geo_ip_country(frappe.get_request_header('REMOTE_ADDR')) # insert session @@ -218,7 +224,7 @@ class Session: rec = frappe.db.sql("""select user, sessiondata from tabSessions where sid=%s and TIMEDIFF(NOW(), lastupdate) < TIME(%s)""", (self.sid, - self.get_expiry_period())) + get_expiry_period())) if rec: data = frappe._dict(eval(rec and rec[0][1] or '{}')) data.user = rec[0][0] @@ -267,17 +273,14 @@ class Session: frappe.utils.now()) frappe.cache().set_value("session:" + self.sid, self.data) - def get_expiry_period(self): - exp_sec = frappe.defaults.get_global_default("session_expiry") or "06:00:00" +def get_expiry_period(): + exp_sec = frappe.defaults.get_global_default("session_expiry") or "06:00:00" - # incase seconds is missing - if exp_sec: - if len(exp_sec.split(':')) == 2: - exp_sec = exp_sec + ':00' - else: - exp_sec = "2:00:00" + # incase seconds is missing + if len(exp_sec.split(':')) == 2: + exp_sec = exp_sec + ':00' - return exp_sec + return exp_sec def get_geo_ip_country(ip_addr): try: From bfd8b88b39e7d4ccd362ebee98dc009cf8152dcd Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 14 May 2014 18:57:35 +0530 Subject: [PATCH 82/98] Set frappe.flags.in_patch when running a patch --- frappe/modules/patch_handler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/modules/patch_handler.py b/frappe/modules/patch_handler.py index 2f7fee8055..e7dbd01ff5 100644 --- a/frappe/modules/patch_handler.py +++ b/frappe/modules/patch_handler.py @@ -93,6 +93,7 @@ def executed(patchmodule): def block_user(block): """stop/start execution till patch is run""" + frappe.local.flags.in_patch = block frappe.db.begin() msg = "Patches are being executed in the system. Please try again in a few moments." frappe.db.set_global('__session_status', block and 'stop' or None) From 5a56ee3a1f2939dea887ab252ced391e574840f9 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 14 May 2014 19:39:59 +0530 Subject: [PATCH 83/98] Fixed rss.xml --- frappe/templates/pages/rss.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/frappe/templates/pages/rss.py b/frappe/templates/pages/rss.py index a02f1dda11..ac77be2d2d 100644 --- a/frappe/templates/pages/rss.py +++ b/frappe/templates/pages/rss.py @@ -1,5 +1,5 @@ # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# MIT License. See license.txt from __future__ import unicode_literals import frappe @@ -11,11 +11,11 @@ base_template_path = "templates/pages/rss.xml" def get_context(context): """generate rss feed""" - + host = get_request_site_address() - + blog_list = frappe.db.sql("""\ - select page_name as name, published_on, modified, title, content from `tabBlog Post` + select page_name as name, published_on, modified, title, content from `tabBlog Post` where ifnull(published,0)=1 order by published_on desc limit 20""", as_dict=1) @@ -23,22 +23,21 @@ def get_context(context): blog_page = cstr(urllib.quote(blog.name.encode("utf-8"))) + ".html" blog.link = urllib.basejoin(host, blog_page) blog.content = escape_html(blog.content or "") - + if blog_list: modified = max((blog['modified'] for blog in blog_list)) else: modified = now() - ws = frappe.get_doc('Website Settings', 'Website Settings') + blog_settings = frappe.get_doc('Blog Settings', 'Blog Settings') context = { - 'title': ws.title_prefix, - 'description': ws.description or ((ws.title_prefix or "") + ' Blog'), + 'title': blog_settings.blog_title or "Blog", + 'description': blog_settings.blog_introduction or "", 'modified': modified, 'items': blog_list, 'link': host + '/blog' } - + # print context return context - \ No newline at end of file From 18f897aa0eddfe7ceb59ef75b69980a8e39e5db4 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Wed, 14 May 2014 21:38:10 +0530 Subject: [PATCH 84/98] hotfix: escape curly in boilerplate --- frappe/utils/boilerplate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 4ef4c72e7a..85c23a1f48 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -122,9 +122,9 @@ app_version = "0.0.1" # home_page = "login" # website user home page (by Role) -# role_home_page = { +# role_home_page = {{ # "Role": "home_page" -# } +# }} # Installation # ------------ From d15d0620fdb2f225a8fb95fcf0f6c848da05507f Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Wed, 14 May 2014 23:00:19 +0530 Subject: [PATCH 85/98] fix --backup return code --- frappe/cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/cli.py b/frappe/cli.py index 60b882800e..0798379eac 100755 --- a/frappe/cli.py +++ b/frappe/cli.py @@ -439,7 +439,6 @@ def backup(with_files=False, backup_path_db=None, backup_path_files=None, quiet= if with_files: print "files backup taken -", odb.backup_path_files, "- on", now() frappe.destroy() - return odb @cmd def move(dest_dir=None, site=None): From 51ad31e8959ffad043c458a131118888b9e49463 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Wed, 14 May 2014 23:00:52 +0530 Subject: [PATCH 86/98] email auth for exception mails --- frappe/celery_app.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/celery_app.py b/frappe/celery_app.py index c7dedcf249..eb591ec3ea 100644 --- a/frappe/celery_app.py +++ b/frappe/celery_app.py @@ -54,6 +54,10 @@ def setup_celery(app, conf): app.conf.EMAIL_PORT = conf.mail_port if conf.mail_server: app.conf.EMAIL_HOST = conf.mail_server + if conf.mail_user: + app.conf.EMAIL_HOST_USER = conf.mail_user + if conf.mail_password: + app.conf.EMAIL_HOST_PASSWORD = conf.mail_password class SiteRouter(object): def route_for_task(self, task, args=None, kwargs=None): From aa90e2a0493f896b8b9a84b3a59cdd0a82f9d215 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Wed, 14 May 2014 23:01:08 +0530 Subject: [PATCH 87/98] optimize mysqldump --- frappe/utils/backups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py index 81aa373f6d..db998c3f0e 100644 --- a/frappe/utils/backups.py +++ b/frappe/utils/backups.py @@ -88,7 +88,7 @@ class BackupGenerator: # escape reserved characters args = dict([item[0], frappe.utils.esc(item[1], '$ ')] for item in self.__dict__.copy().items()) - cmd_string = """mysqldump -u %(user)s -p%(password)s %(db_name)s -h %(db_host)s | gzip -c > %(backup_path_db)s""" % args + cmd_string = """mysqldump --single-transaction --quick --lock-tables=false -u %(user)s -p%(password)s %(db_name)s -h %(db_host)s | gzip -c > %(backup_path_db)s""" % args err, out = frappe.utils.execute_in_shell(cmd_string) def send_email(self): From 032f430a13b74f63f7ccc9e4ceb2d1ce070a57c5 Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Thu, 15 May 2014 15:58:00 +0530 Subject: [PATCH 88/98] make celery emails config a dict --- frappe/celery_app.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/frappe/celery_app.py b/frappe/celery_app.py index eb591ec3ea..ecb2313bcb 100644 --- a/frappe/celery_app.py +++ b/frappe/celery_app.py @@ -49,15 +49,8 @@ def setup_celery(app, conf): if conf.celery_error_emails: app.conf.CELERY_SEND_TASK_ERROR_EMAILS = True - app.conf.ADMINS = conf.celery_error_email_recepients - if conf.mail_port: - app.conf.EMAIL_PORT = conf.mail_port - if conf.mail_server: - app.conf.EMAIL_HOST = conf.mail_server - if conf.mail_user: - app.conf.EMAIL_HOST_USER = conf.mail_user - if conf.mail_password: - app.conf.EMAIL_HOST_PASSWORD = conf.mail_password + for k, v in conf.celery_error_emails.iteritems(): + setattr(app.conf, k, v) class SiteRouter(object): def route_for_task(self, task, args=None, kwargs=None): From b76177b1e7d4bf5a347030f7745cd06a6b72279c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 15 May 2014 17:15:31 +0530 Subject: [PATCH 89/98] Merged language patch with another patch global_defaults_to_system_settings --- frappe/patches.txt | 1 - .../v4_0/set_language_in_system_settings.py | 24 ------------------- 2 files changed, 25 deletions(-) delete mode 100644 frappe/patches/v4_0/set_language_in_system_settings.py diff --git a/frappe/patches.txt b/frappe/patches.txt index 1ba701174d..3c8dbd23bd 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -28,6 +28,5 @@ frappe.patches.v4_0.deprecate_control_panel frappe.patches.v4_0.file_manager_hooks execute:frappe.get_doc("User", "Guest").save() frappe.patches.v4_0.deprecate_link_selects -frappe.patches.v4_0.set_language_in_system_settings frappe.patches.v4_0.set_user_gravatar frappe.patches.v4_0.update_custom_field_insert_after diff --git a/frappe/patches/v4_0/set_language_in_system_settings.py b/frappe/patches/v4_0/set_language_in_system_settings.py deleted file mode 100644 index 8c9b2e43d0..0000000000 --- a/frappe/patches/v4_0/set_language_in_system_settings.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals -import frappe -from collections import Counter -from frappe.core.doctype.user.user import STANDARD_USERS - -def execute(): - if frappe.db.get_value("System Settings", "System Settings", "language"): - return - - # find most common language - lang = frappe.db.sql_list("""select language from `tabUser` - where ifnull(language, '')!='' and language not like "Loading%%" and name not in ({standard_users})""".format( - standard_users=", ".join(["%s"]*len(STANDARD_USERS))), tuple(STANDARD_USERS)) - lang = Counter(lang).most_common(1) - lang = (len(lang) > 0) and lang[0][0] or "english" - - # set language in System Settings - system_settings = frappe.get_doc("System Settings") - system_settings.language = lang - system_settings.ignore_mandatory = True - system_settings.save() From fb2d9cd38eeb817cd8b719ea988f4c428ea824a3 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 15 May 2014 23:05:45 +0530 Subject: [PATCH 90/98] Fixed naming via Prompt --- frappe/model/naming.py | 11 ++++++----- frappe/widgets/form/save.py | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 35d56866af..48b551f209 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -40,12 +40,13 @@ def set_new_name(doc): doc.name = make_autoname(doc.naming_series+'.#####') # call the method! - elif autoname!='Prompt': - doc.name = make_autoname(autoname, doc.doctype) + elif autoname=='Prompt': + # set from __newname in save.py + if not doc.name: + frappe.throw(frappe._("Name not set via Prompt")) - # given - elif doc.get('__newname', None): - doc.name = doc.get('__newname') + else: + doc.name = make_autoname(autoname, doc.doctype) # default name for table elif doc.meta.istable: diff --git a/frappe/widgets/form/save.py b/frappe/widgets/form/save.py index 3f35fd1f1d..09b4d34199 100644 --- a/frappe/widgets/form/save.py +++ b/frappe/widgets/form/save.py @@ -61,3 +61,6 @@ def set_local_name(doc): _set_local_name(doc) for child in doc.get_all_children(): _set_local_name(child) + + if doc.get("__newname"): + doc.name = doc.get("__newname") From 0751a1b02f069cb50452d54ba9ca1204051c149b Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Fri, 16 May 2014 13:35:57 +0530 Subject: [PATCH 91/98] fix copy doc naming bug in test runner --- frappe/test_runner.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/test_runner.py b/frappe/test_runner.py index 1e66d851ec..78ff15dce9 100644 --- a/frappe/test_runner.py +++ b/frappe/test_runner.py @@ -193,6 +193,8 @@ def make_test_objects(doctype, test_records, verbose=None): doc["doctype"] = doctype d = frappe.copy_doc(doc) + if doc.get('name'): + d.name = doc.get('name') if frappe.local.test_objects.get(d.doctype): # do not create test records, if already exists From a961af72c43c418a477b10d3061cfe47b5e3912c Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Fri, 16 May 2014 19:30:40 +0530 Subject: [PATCH 92/98] raise exception on scheduler lock timeout --- frappe/utils/scheduler.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frappe/utils/scheduler.py b/frappe/utils/scheduler.py index d1fdca751b..b50317f821 100644 --- a/frappe/utils/scheduler.py +++ b/frappe/utils/scheduler.py @@ -22,11 +22,8 @@ def enqueue_events(site): return # lock before queuing begins - try: - lock = create_lock('scheduler') - if not lock: - return - except LockTimeoutError: + lock = create_lock('scheduler') + if not lock: return nowtime = frappe.utils.now_datetime() From e6ad4830a97898b8e74850bb7134427fc9d661d9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 16 May 2014 19:58:16 +0530 Subject: [PATCH 93/98] traslation issue fixed in desktop --- frappe/boot.py | 2 +- frappe/config/desktop.py | 95 ++++++++++++++++++++-------------------- 2 files changed, 49 insertions(+), 48 deletions(-) diff --git a/frappe/boot.py b/frappe/boot.py index 868a5b978b..2bb61eefc4 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -34,7 +34,7 @@ def get_bootinfo(): bootinfo.modules = {} for app in frappe.get_installed_apps(): try: - bootinfo.modules.update(frappe.get_attr(app + ".config.desktop.data") or {}) + bootinfo.modules.update(frappe.get_attr(app + ".config.desktop.get_data")() or {}) except ImportError: pass diff --git a/frappe/config/desktop.py b/frappe/config/desktop.py index e1b2f7149a..cf12e060ae 100644 --- a/frappe/config/desktop.py +++ b/frappe/config/desktop.py @@ -1,49 +1,50 @@ from frappe import _ -data = { - "Calendar": { - "color": "#2980b9", - "icon": "icon-calendar", - "label": _("Calendar"), - "link": "Calendar/Event", - "type": "view" - }, - "Messages": { - "color": "#9b59b6", - "icon": "icon-comments", - "label": _("Messages"), - "link": "messages", - "type": "page" - }, - "To Do": { - "color": "#f1c40f", - "icon": "icon-check", - "label": _("To Do"), - "link": "List/ToDo", - "doctype": "ToDo", - "type": "list" - }, - "Website": { - "color": "#16a085", - "icon": "icon-globe", - "type": "module" - }, - "Installer": { - "color": "#888", - "icon": "icon-download", - "link": "applications", - "type": "page", - "label": _("Installer") - }, - "Setup": { - "color": "#bdc3c7", - "icon": "icon-wrench", - "type": "module" - }, - "Core": { - "color": "#589494", - "icon": "icon-cog", - "type": "module", - "system_manager": 1 - }, -} +def get_data(): + return { + "Calendar": { + "color": "#2980b9", + "icon": "icon-calendar", + "label": _("Calendar"), + "link": "Calendar/Event", + "type": "view" + }, + "Messages": { + "color": "#9b59b6", + "icon": "icon-comments", + "label": _("Messages"), + "link": "messages", + "type": "page" + }, + "To Do": { + "color": "#f1c40f", + "icon": "icon-check", + "label": _("To Do"), + "link": "List/ToDo", + "doctype": "ToDo", + "type": "list" + }, + "Website": { + "color": "#16a085", + "icon": "icon-globe", + "type": "module" + }, + "Installer": { + "color": "#888", + "icon": "icon-download", + "link": "applications", + "type": "page", + "label": _("Installer") + }, + "Setup": { + "color": "#bdc3c7", + "icon": "icon-wrench", + "type": "module" + }, + "Core": { + "color": "#589494", + "icon": "icon-cog", + "type": "module", + "system_manager": 1 + }, + } From 648ec7d059c744c1c50f7224ce11eff6de1b0cfe Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Sat, 17 May 2014 00:10:22 +0530 Subject: [PATCH 94/98] Fixed scheduler error logging --- frappe/utils/scheduler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/utils/scheduler.py b/frappe/utils/scheduler.py index b50317f821..8c28533d7b 100644 --- a/frappe/utils/scheduler.py +++ b/frappe/utils/scheduler.py @@ -94,10 +94,10 @@ def log(method, message=None): frappe.db.rollback() frappe.db.begin() - d = frappe.get_doc("Scheduler Log") + d = frappe.new_doc("Scheduler Log") d.method = method d.error = message - d.save() + d.insert() frappe.db.commit() From 5cc654f77489af6e5c1cde3be523fdb8852d5828 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 19 May 2014 18:16:13 +0530 Subject: [PATCH 95/98] Server side onload functionality in multiple docs --- frappe/model/base_document.py | 3 +++ frappe/widgets/form/load.py | 1 + 2 files changed, 4 insertions(+) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index ff4a432064..e723579bce 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -163,6 +163,9 @@ class BaseDocument(object): if self.get("__islocal"): doc["__islocal"] = 1 + elif self.get("__onload"): + doc["__onload"] = self.get("__onload") + return doc def as_json(self): diff --git a/frappe/widgets/form/load.py b/frappe/widgets/form/load.py index b81e2a4bd4..915037acab 100644 --- a/frappe/widgets/form/load.py +++ b/frappe/widgets/form/load.py @@ -27,6 +27,7 @@ def getdoc(doctype, name, user=None): try: doc = frappe.get_doc(doctype, name) + doc.set("__onload", frappe._dict()) doc.run_method("onload") if not doc.has_permission("read"): From b51a3da22261361a4dd36385fe0acb77251ec4f8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 19 May 2014 19:33:27 +0530 Subject: [PATCH 96/98] Set idx while adding communication. Fixes frappe/erpnext#1645 --- frappe/core/doctype/communication/communication.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index ada190ca58..d55d89cdea 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -10,7 +10,7 @@ from frappe.website.utils import is_signup_enabled from frappe.utils import get_url, cstr from frappe.utils.email_lib.email_body import get_email from frappe.utils.email_lib.smtp import send -from frappe.utils import scrub_urls +from frappe.utils import scrub_urls, cint from frappe import _ from frappe.model.document import Document @@ -78,6 +78,9 @@ def _make(doctype=None, name=None, content=None, subject=None, sent_or_received d.communication_medium = communication_medium + d.idx = cint(frappe.db.sql("""select max(idx) from `tabCommunication` + where parenttype=%s and parent=%s""", (doctype, name))[0][0]) + 1 + comm.ignore_permissions = True comm.insert() From f654b59c41ad1b23b4bc0ec420187bce5f1d22eb Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 20 May 2014 13:59:49 +0530 Subject: [PATCH 97/98] Set remove_typography to false as default --- frappe/public/js/frappe/ui/editor.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/ui/editor.js b/frappe/public/js/frappe/ui/editor.js index 4cdeaeb3d7..f4d934ca4d 100644 --- a/frappe/public/js/frappe/ui/editor.js +++ b/frappe/public/js/frappe/ui/editor.js @@ -8,7 +8,7 @@ bsEditor = Class.extend({ init: function(options) { - this.options = $.extend(options || {}, this.default_options); + this.options = $.extend({}, this.default_options, options || {}); this.edit_mode = true; if(this.options.editor) { this.setup_editor(this.options.editor); @@ -100,7 +100,7 @@ bsEditor = Class.extend({ active_toolbar_class: 'btn-info', selection_marker: 'edit-focus-marker', selection_color: 'darkgrey', - remove_typography: true, + remove_typography: false, max_file_size: 1, }, @@ -131,7 +131,9 @@ bsEditor = Class.extend({ if(this.options.remove_typography) { var tmp = $("
    ").html(html); // remove style attributes - tmp.find("*").removeAttr("style"); + tmp.find("*") + .removeAttr("style") + .removeAttr("font"); html = tmp.html(); } From 496c27141a639ccb276ae4f82243fb7559d2015a Mon Sep 17 00:00:00 2001 From: Pratik Vyas Date: Tue, 20 May 2014 16:18:02 +0530 Subject: [PATCH 98/98] Release v4.0.1 --- frappe/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index e40ee6434f..89d71ce19e 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -16,7 +16,7 @@ import json from .exceptions import * -__version__ = "4.0.0" +__version__ = "4.0.1" local = Local() diff --git a/setup.py b/setup.py index f7c96349ac..0b8c925f7c 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages import os -version = '4.0.0' +version = '4.0.1' with open("requirements.txt", "r") as f: install_requires = f.readlines()