diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 3506f6153c..861388bae9 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -87,7 +87,7 @@ def search_widget(doctype, txt, query=None, searchfield=None, start=0, filters=filters, fields=fields, or_filters = or_filters, limit_start = start, limit_page_length=page_len, - order_by="if(_relevance, _relevance, 99999), modified desc".format(doctype), + order_by="if(_relevance, _relevance, 99999), idx desc, modified desc".format(doctype), ignore_permissions = True if doctype == "DocType" else False, # for dynamic links as_list=True) diff --git a/frappe/hooks.py b/frappe/hooks.py index 1eafb75b51..9c1c623d47 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -111,6 +111,7 @@ scheduler_events = { "frappe.email.doctype.email_account.email_account.pull", "frappe.email.doctype.email_account.email_account.notify_unreplied", "frappe.utils.error.collect_error_snapshots", + "frappe.model.utils.link_count.update_link_count", ], "daily": [ "frappe.email.bulk.clear_outbox", diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index a3b546ea40..ed8a467789 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -8,6 +8,7 @@ from frappe.utils import (cint, flt, now, cstr, strip_html, getdate, get_datetim sanitize_html, sanitize_email) from frappe.model import default_fields from frappe.model.naming import set_new_name +from frappe.model.utils.link_count import notify_link_count from frappe.modules import load_doctype_module from frappe.model import display_fieldtypes from frappe.model.db_schema import type_map, varchar_len @@ -403,6 +404,7 @@ class BaseDocument(object): for df in (self.meta.get_link_fields() + self.meta.get("fields", {"fieldtype":"Dynamic Link"})): docname = self.get(df.fieldname) + if docname: if df.fieldtype=="Link": doctype = df.options @@ -420,6 +422,8 @@ class BaseDocument(object): setattr(self, df.fieldname, value) + notify_link_count(doctype, docname) + if not value: invalid_links.append((df.fieldname, docname, get_msg(df, docname))) diff --git a/frappe/model/utils/link_count.py b/frappe/model/utils/link_count.py new file mode 100644 index 0000000000..898e89556e --- /dev/null +++ b/frappe/model/utils/link_count.py @@ -0,0 +1,29 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals + +import frappe + +def notify_link_count(doctype, name): + '''updates link count for given document''' + link_count = frappe.cache().get_value('_link_count') + if not link_count: + link_count = {} + + if not (doctype, name) in link_count: + link_count[(doctype, name)] = 1 + else: + link_count[(doctype, name)] += 1 + + frappe.cache().set_value('_link_count', link_count) + +def update_link_count(): + '''increment link count in the `idx` column for the given document''' + link_count = frappe.cache().get_value('_link_count') + if link_count: + for key, count in link_count.iteritems(): + frappe.db.sql('update `tab{0}` set idx = idx + %s where name=%s'.format(key[0]), (count, key[1])) + + # reset the count + frappe.cache().delete_value('_link_count') diff --git a/frappe/tests/test_document.py b/frappe/tests/test_document.py index 62c3bc40d8..6d5b8662c5 100644 --- a/frappe/tests/test_document.py +++ b/frappe/tests/test_document.py @@ -98,7 +98,7 @@ class TestDocument(unittest.TestCase): def test_permission(self): frappe.set_user("Guest") - d = self.assertRaises(frappe.PermissionError, self.test_insert) + self.assertRaises(frappe.PermissionError, self.test_insert) frappe.set_user("Administrator") def test_permission_single(self): @@ -189,3 +189,30 @@ class TestDocument(unittest.TestCase): self.assertTrue(xss not in d.subject) self.assertTrue(escaped_xss in d.subject) + def test_link_count(self): + from frappe.model.utils.link_count import update_link_count + + update_link_count() + + doctype, name = 'User', 'test@example.com' + + d = self.test_insert() + d.ref_type = doctype + d.ref_name = name + + link_count = frappe.cache().get_value('_link_count') or {} + old_count = link_count.get((doctype, name)) or 0 + + d.save() + + link_count = frappe.cache().get_value('_link_count') or {} + new_count = link_count.get((doctype, name)) or 0 + + self.assertEquals(old_count + 1, new_count) + + before_update = frappe.db.get_value(doctype, name, 'idx') + update_link_count() + after_update = frappe.db.get_value(doctype, name, 'idx') + + self.assertEquals(before_update + new_count, after_update) +