feat(Desk): Global Tagsversion-14
@@ -1,58 +0,0 @@ | |||
{ | |||
"allow_copy": 0, | |||
"allow_import": 0, | |||
"allow_rename": 0, | |||
"autoname": "", | |||
"creation": "2016-05-25 09:43:44.767581", | |||
"custom": 0, | |||
"docstatus": 0, | |||
"doctype": "DocType", | |||
"document_type": "", | |||
"fields": [ | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"fieldname": "tag_name", | |||
"fieldtype": "Data", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 1, | |||
"label": "Tags", | |||
"length": 0, | |||
"no_copy": 0, | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
"read_only": 0, | |||
"report_hide": 0, | |||
"reqd": 0, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"unique": 0 | |||
} | |||
], | |||
"hide_heading": 0, | |||
"hide_toolbar": 0, | |||
"idx": 0, | |||
"in_create": 0, | |||
"is_submittable": 0, | |||
"issingle": 0, | |||
"istable": 1, | |||
"max_attachments": 0, | |||
"modified": "2016-05-31 08:29:01.773065", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Tag", | |||
"name_case": "", | |||
"owner": "Administrator", | |||
"permissions": [], | |||
"read_only": 0, | |||
"read_only_onload": 0, | |||
"sort_field": "modified", | |||
"sort_order": "DESC" | |||
} |
@@ -1,11 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright (c) 2015, Frappe Technologies and contributors | |||
# For license information, please see license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
from frappe.model.document import Document | |||
class Tag(Document): | |||
def validate(self): | |||
self.tag_name = self.tag_name.title() |
@@ -1,9 +0,0 @@ | |||
// Copyright (c) 2016, Frappe Technologies and contributors | |||
// For license information, please see license.txt | |||
frappe.ui.form.on('Tag', { | |||
tag_name:function(frm){ | |||
for (var i = 0 ;i<frm.doc.tags.length;i++){ | |||
frm.doc.tags[i].tag_name = toTitle(frm.doc.tags[i].tag_name) | |||
} | |||
} | |||
}); |
@@ -1,147 +0,0 @@ | |||
{ | |||
"allow_copy": 0, | |||
"allow_import": 1, | |||
"allow_rename": 0, | |||
"autoname": "field:category_name", | |||
"beta": 0, | |||
"creation": "2016-05-25 09:49:07.125394", | |||
"custom": 0, | |||
"docstatus": 0, | |||
"doctype": "DocType", | |||
"document_type": "", | |||
"editable_grid": 0, | |||
"fields": [ | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "category_name", | |||
"fieldtype": "Data", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"in_standard_filter": 0, | |||
"label": "Category Name", | |||
"length": 0, | |||
"no_copy": 0, | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
"read_only": 0, | |||
"remember_last_selected_value": 0, | |||
"report_hide": 0, | |||
"reqd": 0, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "tags", | |||
"fieldtype": "Table", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"in_standard_filter": 0, | |||
"label": "Tags", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "Tag", | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
"read_only": 0, | |||
"remember_last_selected_value": 0, | |||
"report_hide": 0, | |||
"reqd": 0, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"columns": 0, | |||
"fieldname": "tagdocs", | |||
"fieldtype": "Table", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"in_standard_filter": 0, | |||
"label": "Doctypes", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "Tag Doc Category", | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
"read_only": 0, | |||
"remember_last_selected_value": 0, | |||
"report_hide": 0, | |||
"reqd": 0, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"unique": 0 | |||
} | |||
], | |||
"hide_heading": 0, | |||
"hide_toolbar": 0, | |||
"idx": 0, | |||
"image_view": 0, | |||
"in_create": 0, | |||
"is_submittable": 0, | |||
"issingle": 0, | |||
"istable": 0, | |||
"max_attachments": 0, | |||
"modified": "2016-12-29 14:40:37.489085", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Tag Category", | |||
"name_case": "Title Case", | |||
"owner": "Administrator", | |||
"permissions": [ | |||
{ | |||
"amend": 0, | |||
"apply_user_permissions": 0, | |||
"cancel": 0, | |||
"create": 1, | |||
"delete": 1, | |||
"email": 0, | |||
"export": 1, | |||
"if_owner": 0, | |||
"import": 1, | |||
"is_custom": 0, | |||
"permlevel": 0, | |||
"print": 0, | |||
"read": 1, | |||
"report": 0, | |||
"role": "System Manager", | |||
"set_user_permissions": 0, | |||
"share": 0, | |||
"submit": 0, | |||
"write": 1 | |||
} | |||
], | |||
"quick_entry": 0, | |||
"read_only": 0, | |||
"read_only_onload": 0, | |||
"sort_field": "modified", | |||
"sort_order": "DESC", | |||
"track_changes": 1, | |||
"track_seen": 0 | |||
} |
@@ -1,12 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright (c) 2015, Frappe Technologies and Contributors | |||
# See license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
import unittest | |||
# test_records = frappe.get_test_records('Tag Categories') | |||
class TestTagCategories(unittest.TestCase): | |||
pass |
@@ -1,58 +0,0 @@ | |||
{ | |||
"allow_copy": 0, | |||
"allow_import": 0, | |||
"allow_rename": 0, | |||
"creation": "2016-05-25 13:09:20.996154", | |||
"custom": 0, | |||
"docstatus": 0, | |||
"doctype": "DocType", | |||
"document_type": "", | |||
"fields": [ | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"fieldname": "tagdoc", | |||
"fieldtype": "Link", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"ignore_xss_filter": 0, | |||
"in_filter": 0, | |||
"in_list_view": 1, | |||
"label": "Doctype to Assign Tags", | |||
"length": 0, | |||
"no_copy": 0, | |||
"options": "DocType", | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
"print_hide_if_no_value": 0, | |||
"read_only": 0, | |||
"report_hide": 0, | |||
"reqd": 0, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"unique": 0 | |||
} | |||
], | |||
"hide_heading": 0, | |||
"hide_toolbar": 0, | |||
"idx": 0, | |||
"in_create": 0, | |||
"is_submittable": 0, | |||
"issingle": 0, | |||
"istable": 1, | |||
"max_attachments": 0, | |||
"modified": "2016-05-30 15:04:45.454688", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Tag Doc Category", | |||
"name_case": "", | |||
"owner": "Administrator", | |||
"permissions": [], | |||
"read_only": 0, | |||
"read_only_onload": 0, | |||
"sort_field": "modified", | |||
"sort_order": "DESC" | |||
} |
@@ -1,10 +0,0 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright (c) 2015, Frappe Technologies and contributors | |||
# For license information, please see license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
from frappe.model.document import Document | |||
class TagDocCategory(Document): | |||
pass |
@@ -961,6 +961,26 @@ class Database(object): | |||
frappe.flags.touched_tables = set() | |||
frappe.flags.touched_tables.update(tables) | |||
def bulk_insert(self, doctype, fields, values): | |||
""" | |||
Insert multiple records at a time | |||
:param doctype: Doctype name | |||
:param fields: list of fields | |||
:params values: list of list of values | |||
""" | |||
insert_list = [] | |||
fields = ", ".join(["`"+field+"`" for field in fields]) | |||
for idx, value in enumerate(values): | |||
insert_list.append(tuple(value)) | |||
if idx and (idx%10000 == 0 or idx < len(values)-1): | |||
self.sql("""INSERT INTO `tab{doctype}` ({fields}) VALUES {values}""".format( | |||
doctype=doctype, | |||
fields=fields, | |||
values=", ".join(['%s'] * len(insert_list)) | |||
), tuple(insert_list)) | |||
insert_list = [] | |||
def enqueue_jobs_after_commit(): | |||
if frappe.flags.enqueue_after_commit and len(frappe.flags.enqueue_after_commit) > 0: | |||
@@ -0,0 +1,8 @@ | |||
// Copyright (c) 2019, Frappe Technologies and contributors | |||
// For license information, please see license.txt | |||
frappe.ui.form.on('Tag', { | |||
// refresh: function(frm) { | |||
// } | |||
}); |
@@ -0,0 +1,49 @@ | |||
{ | |||
"autoname": "Prompt", | |||
"creation": "2016-05-25 09:43:44.767581", | |||
"doctype": "DocType", | |||
"engine": "InnoDB", | |||
"field_order": [ | |||
"description" | |||
], | |||
"fields": [ | |||
{ | |||
"fieldname": "description", | |||
"fieldtype": "Small Text", | |||
"in_list_view": 1, | |||
"label": "Description" | |||
} | |||
], | |||
"modified": "2019-09-25 17:47:41.712237", | |||
"modified_by": "Administrator", | |||
"module": "Desk", | |||
"name": "Tag", | |||
"owner": "Administrator", | |||
"permissions": [ | |||
{ | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"export": 1, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"role": "System Manager", | |||
"share": 1, | |||
"write": 1 | |||
}, | |||
{ | |||
"create": 1, | |||
"email": 1, | |||
"export": 1, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"role": "All", | |||
"share": 1, | |||
"write": 1 | |||
} | |||
], | |||
"sort_field": "modified", | |||
"sort_order": "DESC" | |||
} |
@@ -0,0 +1,179 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright (c) 2019, Frappe Technologies and contributors | |||
# For license information, please see license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
from frappe.model.document import Document | |||
class Tag(Document): | |||
pass | |||
def check_user_tags(dt): | |||
"if the user does not have a tags column, then it creates one" | |||
try: | |||
frappe.db.sql("select `_user_tags` from `tab%s` limit 1" % dt) | |||
except Exception as e: | |||
if frappe.db.is_column_missing(e): | |||
DocTags(dt).setup() | |||
@frappe.whitelist() | |||
def add_tag(tag, dt, dn, color=None): | |||
"adds a new tag to a record, and creates the Tag master" | |||
DocTags(dt).add(dn, tag) | |||
return tag | |||
@frappe.whitelist() | |||
def remove_tag(tag, dt, dn): | |||
"removes tag from the record" | |||
DocTags(dt).remove(dn, tag) | |||
@frappe.whitelist() | |||
def get_tagged_docs(doctype, tag): | |||
frappe.has_permission(doctype, throw=True) | |||
return frappe.db.sql("""SELECT name | |||
FROM `tab{0}` | |||
WHERE _user_tags LIKE '%{1}%'""".format(doctype, tag)) | |||
@frappe.whitelist() | |||
def get_tags(doctype, txt): | |||
tag = frappe.get_list("Tag", filters=[["name", "like", "%{}%".format(txt)]]) | |||
tags = [t.name for t in tag] | |||
return sorted(filter(lambda t: t and txt.lower() in t.lower(), list(set(tags)))) | |||
class DocTags: | |||
"""Tags for a particular doctype""" | |||
def __init__(self, dt): | |||
self.dt = dt | |||
def get_tag_fields(self): | |||
"""returns tag_fields property""" | |||
return frappe.db.get_value('DocType', self.dt, 'tag_fields') | |||
def get_tags(self, dn): | |||
"""returns tag for a particular item""" | |||
return (frappe.db.get_value(self.dt, dn, '_user_tags', ignore=1) or '').strip() | |||
def add(self, dn, tag): | |||
"""add a new user tag""" | |||
tl = self.get_tags(dn).split(',') | |||
if not tag in tl: | |||
tl.append(tag) | |||
if not frappe.db.exists("Tag", tag): | |||
frappe.get_doc({"doctype": "Tag", "name": tag}).insert(ignore_permissions=True) | |||
self.update(dn, tl) | |||
def remove(self, dn, tag): | |||
"""remove a user tag""" | |||
tl = self.get_tags(dn).split(',') | |||
self.update(dn, filter(lambda x:x.lower()!=tag.lower(), tl)) | |||
def remove_all(self, dn): | |||
"""remove all user tags (call before delete)""" | |||
self.update(dn, []) | |||
def update(self, dn, tl): | |||
"""updates the _user_tag column in the table""" | |||
if not tl: | |||
tags = '' | |||
else: | |||
tl = list(set(filter(lambda x: x, tl))) | |||
tags = ',' + ','.join(tl) | |||
try: | |||
frappe.db.sql("update `tab%s` set _user_tags=%s where name=%s" % \ | |||
(self.dt,'%s','%s'), (tags , dn)) | |||
doc= frappe.get_doc(self.dt, dn) | |||
update_tags(doc, tags) | |||
except Exception as e: | |||
if frappe.db.is_column_missing(e): | |||
if not tags: | |||
# no tags, nothing to do | |||
return | |||
self.setup() | |||
self.update(dn, tl) | |||
else: raise | |||
def setup(self): | |||
"""adds the _user_tags column if not exists""" | |||
from frappe.database.schema import add_column | |||
add_column(self.dt, "_user_tags", "Data") | |||
def delete_tags_for_document(doc): | |||
""" | |||
Delete the Tag Link entry of a document that has | |||
been deleted | |||
:param doc: Deleted document | |||
""" | |||
if not frappe.db.table_exists("Tag Link"): | |||
return | |||
frappe.db.sql("""DELETE FROM `tabTag Link` WHERE `document_type`=%s AND `document_name`=%s""", (doc.doctype, doc.name)) | |||
def update_tags(doc, tags): | |||
""" | |||
Adds tags for documents | |||
:param doc: Document to be added to global tags | |||
""" | |||
new_tags = list(set([tag.strip() for tag in tags.split(",") if tag])) | |||
for tag in new_tags: | |||
if not frappe.db.exists("Tag Link", {"parenttype": doc.doctype, "parent": doc.name, "tag": tag}): | |||
frappe.get_doc({ | |||
"doctype": "Tag Link", | |||
"document_type": doc.doctype, | |||
"document_name": doc.name, | |||
"parenttype": doc.doctype, | |||
"parent": doc.name, | |||
"title": doc.get_title() or '', | |||
"tag": tag | |||
}).insert(ignore_permissions=True) | |||
existing_tags = [tag.tag for tag in frappe.get_list("Tag Link", filters={ | |||
"document_type": doc.doctype, | |||
"document_name": doc.name | |||
}, fields=["tag"])] | |||
deleted_tags = get_deleted_tags(new_tags, existing_tags) | |||
if deleted_tags: | |||
for tag in deleted_tags: | |||
delete_tag_for_document(doc.doctype, doc.name, tag) | |||
def get_deleted_tags(new_tags, existing_tags): | |||
return list(set(existing_tags) - set(new_tags)) | |||
def delete_tag_for_document(dt, dn, tag): | |||
frappe.db.sql("""DELETE FROM `tabTag Link` WHERE `document_type`=%s AND `document_name`=%s AND tag=%s""", (dt, dn, tag)) | |||
@frappe.whitelist() | |||
def get_documents_for_tag(tag): | |||
""" | |||
Search for given text in Tag Link | |||
:param tag: tag to be searched | |||
""" | |||
# remove hastag `#` from tag | |||
tag = tag[1:] | |||
results = [] | |||
result = frappe.get_list("Tag Link", filters={"tag": tag}, fields=["document_type", "document_name", "title", "tag"]) | |||
for res in result: | |||
results.append({ | |||
"doctype": res.document_type, | |||
"name": res.document_name, | |||
"content": res.title | |||
}) | |||
print(results) | |||
return results | |||
@frappe.whitelist() | |||
def get_tags_list_for_awesomebar(): | |||
return [t.name for t in frappe.get_list("Tag")] |
@@ -0,0 +1,10 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright (c) 2019, Frappe Technologies and Contributors | |||
# See license.txt | |||
from __future__ import unicode_literals | |||
# import frappe | |||
import unittest | |||
class TestTag(unittest.TestCase): | |||
pass |
@@ -0,0 +1,8 @@ | |||
// Copyright (c) 2019, Frappe Technologies and contributors | |||
// For license information, please see license.txt | |||
frappe.ui.form.on('Tag Link', { | |||
// refresh: function(frm) { | |||
// } | |||
}); |
@@ -0,0 +1,70 @@ | |||
{ | |||
"creation": "2019-09-24 13:25:36.435685", | |||
"doctype": "DocType", | |||
"editable_grid": 1, | |||
"engine": "InnoDB", | |||
"field_order": [ | |||
"document_type", | |||
"document_name", | |||
"tag", | |||
"title" | |||
], | |||
"fields": [ | |||
{ | |||
"fieldname": "title", | |||
"fieldtype": "Data", | |||
"label": "Document Title", | |||
"read_only": 1 | |||
}, | |||
{ | |||
"fieldname": "tag", | |||
"fieldtype": "Link", | |||
"in_list_view": 1, | |||
"in_standard_filter": 1, | |||
"label": "Document Tag", | |||
"options": "Tag", | |||
"read_only": 1 | |||
}, | |||
{ | |||
"fieldname": "document_type", | |||
"fieldtype": "Link", | |||
"in_list_view": 1, | |||
"in_standard_filter": 1, | |||
"label": "Document Type", | |||
"options": "DocType", | |||
"read_only": 1 | |||
}, | |||
{ | |||
"fieldname": "document_name", | |||
"fieldtype": "Dynamic Link", | |||
"in_list_view": 1, | |||
"in_standard_filter": 1, | |||
"label": "Document Name", | |||
"options": "document_type", | |||
"read_only": 1 | |||
} | |||
], | |||
"modified": "2019-10-03 16:42:35.932409", | |||
"modified_by": "Administrator", | |||
"module": "Desk", | |||
"name": "Tag Link", | |||
"owner": "Administrator", | |||
"permissions": [ | |||
{ | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"export": 1, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"role": "System Manager", | |||
"share": 1, | |||
"write": 1 | |||
} | |||
], | |||
"read_only": 1, | |||
"sort_field": "modified", | |||
"sort_order": "DESC", | |||
"track_changes": 1 | |||
} |
@@ -1,10 +1,10 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright (c) 2015, Frappe Technologies and contributors | |||
# Copyright (c) 2019, Frappe Technologies and contributors | |||
# For license information, please see license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
# import frappe | |||
from frappe.model.document import Document | |||
class TagCategory(Document): | |||
class TagLink(Document): | |||
pass |
@@ -0,0 +1,10 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright (c) 2019, Frappe Technologies and Contributors | |||
# See license.txt | |||
from __future__ import unicode_literals | |||
# import frappe | |||
import unittest | |||
class TestTagLink(unittest.TestCase): | |||
pass |
@@ -100,7 +100,8 @@ def get_docinfo(doc=None, doctype=None, name=None): | |||
"views": get_view_logs(doc.doctype, doc.name), | |||
"energy_point_logs": get_point_logs(doc.doctype, doc.name), | |||
"milestones": get_milestones(doc.doctype, doc.name), | |||
"is_document_followed": is_document_followed(doc.doctype, doc.name, frappe.session.user) | |||
"is_document_followed": is_document_followed(doc.doctype, doc.name, frappe.session.user), | |||
"tags": get_tags(doc.doctype, doc.name) | |||
} | |||
def get_milestones(doctype, name): | |||
@@ -255,3 +256,11 @@ def get_view_logs(doctype, docname): | |||
if view_logs: | |||
logs = view_logs | |||
return logs | |||
def get_tags(doctype, name): | |||
tags = [tag.tag for tag in frappe.get_all("Tag Link", filters={ | |||
"document_type": doctype, | |||
"document_name": name | |||
}, fields=["tag"])] | |||
return ",".join([tag for tag in tags]) |
@@ -261,13 +261,17 @@ def delete_bulk(doctype, items): | |||
@frappe.whitelist() | |||
@frappe.read_only() | |||
def get_sidebar_stats(stats, doctype, filters=[]): | |||
cat_tags = frappe.db.sql("""select `tag`.parent as `category`, `tag`.tag_name as `tag` | |||
from `tabTag Doc Category` as `docCat` | |||
INNER JOIN `tabTag` as `tag` on `tag`.parent = `docCat`.parent | |||
where `docCat`.tagdoc=%s | |||
ORDER BY `tag`.parent asc, `tag`.idx""", doctype, as_dict=1) | |||
return {"defined_cat":cat_tags, "stats":get_stats(stats, doctype, filters)} | |||
if not frappe.cache().hget("tags_count", doctype): | |||
tags = [tag.name for tag in frappe.get_list("Tag")] | |||
_user_tags = [] | |||
for tag in tags: | |||
count = frappe.db.count("Tag Link", filters={"document_type": doctype, "tag": tag}) | |||
if count > 0: | |||
_user_tags.append([tag, count]) | |||
frappe.cache().hset("tags_count", doctype, _user_tags) | |||
return {"stats": {"_user_tags": frappe.cache().hget("tags_count", doctype)}} | |||
@frappe.whitelist() | |||
@frappe.read_only() | |||
@@ -1,127 +0,0 @@ | |||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals, print_function | |||
import json | |||
""" | |||
Server side functions for tagging. | |||
- Tags can be added to any record (doctype, name) in the system. | |||
- Items are filtered by tags | |||
- Top tags are shown in the sidebar (?) | |||
- Tags are also identified by the tag_fields property of the DocType | |||
Discussion: | |||
Tags are shown in the docbrowser and ideally where-ever items are searched. | |||
There should also be statistics available for tags (like top tags etc) | |||
Design: | |||
- free tags (user_tags) are stored in __user_tags | |||
- doctype tags are set in tag_fields property of the doctype | |||
- top tags merges the tags from both the lists (only refreshes once an hour (max)) | |||
""" | |||
import frappe | |||
from frappe.utils.global_search import update_global_search | |||
def check_user_tags(dt): | |||
"if the user does not have a tags column, then it creates one" | |||
try: | |||
frappe.db.sql("select `_user_tags` from `tab%s` limit 1" % dt) | |||
except Exception as e: | |||
if frappe.db.is_column_missing(e): | |||
DocTags(dt).setup() | |||
@frappe.whitelist() | |||
def add_tag(tag, dt, dn, color=None): | |||
"adds a new tag to a record, and creates the Tag master" | |||
DocTags(dt).add(dn, tag) | |||
return tag | |||
@frappe.whitelist() | |||
def remove_tag(tag, dt, dn): | |||
"removes tag from the record" | |||
DocTags(dt).remove(dn, tag) | |||
@frappe.whitelist() | |||
def get_tagged_docs(doctype, tag): | |||
frappe.has_permission(doctype, throw=True) | |||
return frappe.db.sql("""SELECT name | |||
FROM `tab{0}` | |||
WHERE _user_tags LIKE '%{1}%'""".format(doctype, tag)) | |||
@frappe.whitelist() | |||
def get_tags(doctype, txt, cat_tags): | |||
tags = json.loads(cat_tags) | |||
try: | |||
for _user_tags in frappe.db.sql_list("""select DISTINCT `_user_tags` | |||
from `tab{0}` | |||
where _user_tags like {1} | |||
limit 50""".format(doctype, frappe.db.escape('%' + txt + '%'))): | |||
tags.extend(_user_tags[1:].split(",")) | |||
except Exception as e: | |||
if not frappe.db.is_column_missing(e): raise | |||
return sorted(filter(lambda t: t and txt.lower() in t.lower(), list(set(tags)))) | |||
class DocTags: | |||
"""Tags for a particular doctype""" | |||
def __init__(self, dt): | |||
self.dt = dt | |||
def get_tag_fields(self): | |||
"""returns tag_fields property""" | |||
return frappe.db.get_value('DocType', self.dt, 'tag_fields') | |||
def get_tags(self, dn): | |||
"""returns tag for a particular item""" | |||
return (frappe.db.get_value(self.dt, dn, '_user_tags', ignore=1) or '').strip() | |||
def add(self, dn, tag): | |||
"""add a new user tag""" | |||
tl = self.get_tags(dn).split(',') | |||
if not tag in tl: | |||
tl.append(tag) | |||
self.update(dn, tl) | |||
def remove(self, dn, tag): | |||
"""remove a user tag""" | |||
tl = self.get_tags(dn).split(',') | |||
self.update(dn, filter(lambda x:x.lower()!=tag.lower(), tl)) | |||
def remove_all(self, dn): | |||
"""remove all user tags (call before delete)""" | |||
self.update(dn, []) | |||
def update(self, dn, tl): | |||
"""updates the _user_tag column in the table""" | |||
if not tl: | |||
tags = '' | |||
else: | |||
tl = list(set(filter(lambda x: x, tl))) | |||
tags = ',' + ','.join(tl) | |||
try: | |||
frappe.db.sql("update `tab%s` set _user_tags=%s where name=%s" % \ | |||
(self.dt,'%s','%s'), (tags , dn)) | |||
doc= frappe.get_doc(self.dt, dn) | |||
update_global_search(doc) | |||
except Exception as e: | |||
if frappe.db.is_column_missing(e): | |||
if not tags: | |||
# no tags, nothing to do | |||
return | |||
self.setup() | |||
self.update(dn, tl) | |||
else: raise | |||
def setup(self): | |||
"""adds the _user_tags column if not exists""" | |||
from frappe.database.schema import add_column | |||
add_column(self.dt, "_user_tags", "Data") |
@@ -16,10 +16,11 @@ from frappe.core.doctype.file.file import remove_all | |||
from frappe.utils.password import delete_all_passwords_for | |||
from frappe.model.naming import revert_series_if_last | |||
from frappe.utils.global_search import delete_for_document | |||
from frappe.desk.doctype.tag.tag import delete_tags_for_document | |||
from frappe.exceptions import FileNotFoundError | |||
doctypes_to_skip = ("Communication", "ToDo", "DocShare", "Email Unsubscribe", "Activity Log", "File", "Version", "Document Follow", "Comment" , "View Log") | |||
doctypes_to_skip = ("Communication", "ToDo", "DocShare", "Email Unsubscribe", "Activity Log", "File", "Version", "Document Follow", "Comment" , "View Log", "Tag Link") | |||
def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False, | |||
ignore_permissions=False, flags=None, ignore_on_trash=False, ignore_missing=True): | |||
@@ -116,6 +117,8 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa | |||
# delete global search entry | |||
delete_for_document(doc) | |||
# delete tag link entry | |||
delete_tags_for_document(doc) | |||
if doc and not for_reload: | |||
add_to_deleted_document(doc) | |||
@@ -252,3 +252,4 @@ frappe.patches.v12_0.move_email_and_phone_to_child_table | |||
frappe.patches.v12_0.delete_duplicate_indexes | |||
frappe.patches.v12_0.set_default_incoming_email_port | |||
frappe.patches.v12_0.update_global_search | |||
frappe.patches.v12_0.setup_tags |
@@ -0,0 +1,31 @@ | |||
import frappe | |||
def execute(): | |||
frappe.delete_doc_if_exists("DocType", "Tag Category") | |||
frappe.delete_doc_if_exists("DocType", "Tag Doc Category") | |||
frappe.reload_doc("desk", "doctype", "tag") | |||
frappe.reload_doc("desk", "doctype", "tag_link") | |||
tag_list = [] | |||
tag_links = [] | |||
time = frappe.utils.get_datetime() | |||
for doctype in frappe.get_list("DocType", filters={"istable": 0, "issingle": 0}): | |||
for dt_tags in frappe.db.sql("select `name`, `_user_tags` from `tab{0}`".format(doctype.name), as_dict=True): | |||
tags = dt_tags.get("_user_tags").split(",") if dt_tags.get("_user_tags") else None | |||
if not tags: | |||
continue | |||
for tag in tags: | |||
if not tag: | |||
continue | |||
escaped_tag = frappe.db.escape(tag.strip()) | |||
tag_list.append((escaped_tag, time, time, 'Administrator')) | |||
tag_link_name = frappe.generate_hash(dt_tags.name + escaped_tag, 10), | |||
tag_links.append((tag_link_name, doctype.name, dt_tags.name, escaped_tag, time, time, 'Administrator')) | |||
frappe.db.bulk_insert("Tag", fields=["name", "creation", "modified", "modified_by"], values=tag_list) | |||
frappe.db.bulk_insert("Tag Link", fields=["name", "document_type", "document_name", "tag", "creation", "modified", "modified_by"], values=tag_links) |
@@ -186,6 +186,7 @@ | |||
"public/js/frappe/ui/toolbar/awesome_bar.js", | |||
"public/js/frappe/ui/toolbar/energy_points_notifications.js", | |||
"public/js/frappe/ui/toolbar/search.js", | |||
"public/js/frappe/ui/toolbar/tag_utils.js", | |||
"public/js/frappe/ui/toolbar/search.html", | |||
"public/js/frappe/ui/toolbar/search_header.html", | |||
"public/js/frappe/ui/toolbar/search_utils.js", | |||
@@ -147,6 +147,8 @@ frappe.Application = Class.extend({ | |||
}); | |||
}, 300000); // check every 5 minutes | |||
} | |||
this.fetch_tags(); | |||
}, | |||
setup_frappe_vue() { | |||
@@ -599,6 +601,10 @@ frappe.Application = Class.extend({ | |||
frappe.show_alert(message); | |||
}); | |||
}, | |||
fetch_tags() { | |||
frappe.tags.utils.fetch_tags(); | |||
} | |||
}); | |||
frappe.get_module = function(m, default_module) { | |||
@@ -76,7 +76,7 @@ frappe.ui.form.Sidebar = Class.extend({ | |||
this.frm.shared.refresh(); | |||
this.frm.follow.refresh(); | |||
this.frm.viewers.refresh(); | |||
this.frm.tags && this.frm.tags.refresh(this.frm.doc._user_tags); | |||
this.frm.tags && this.frm.tags.refresh(this.frm.get_docinfo().tags); | |||
this.sidebar.find(".modified-by").html(__("{0} edited this {1}", | |||
["<strong>" + frappe.user.full_name(this.frm.doc.modified_by) + "</strong>", | |||
"<br>" + comment_when(this.frm.doc.modified)])); | |||
@@ -119,7 +119,6 @@ frappe.ui.form.Sidebar = Class.extend({ | |||
}, | |||
make_tags: function() { | |||
var me = this; | |||
if (this.frm.meta.issingle) { | |||
this.sidebar.find(".form-tags").toggle(false); | |||
return; | |||
@@ -129,7 +128,7 @@ frappe.ui.form.Sidebar = Class.extend({ | |||
parent: this.sidebar.find(".tag-area"), | |||
frm: this.frm, | |||
on_change: function(user_tags) { | |||
me.frm.doc._user_tags = user_tags; | |||
this.frm.tags && this.frm.tags.refresh(user_tags); | |||
} | |||
}); | |||
}, | |||
@@ -288,29 +288,7 @@ frappe.views.ListSidebar = class ListSidebar { | |||
filters: me.default_filters || [] | |||
}, | |||
callback: function(r) { | |||
me.defined_category = r.message; | |||
if (r.message.defined_cat) { | |||
me.defined_category = r.message.defined_cat; | |||
me.cats = {}; | |||
//structure the tag categories | |||
for (var i in me.defined_category) { | |||
if (me.cats[me.defined_category[i].category] === undefined) { | |||
me.cats[me.defined_category[i].category] = [me.defined_category[i].tag]; | |||
} else { | |||
me.cats[me.defined_category[i].category].push(me.defined_category[i].tag); | |||
} | |||
me.cat_tags[i] = me.defined_category[i].tag; | |||
} | |||
me.tempstats = r.message.stats; | |||
$.each(me.cats, function(i, v) { | |||
me.render_stat(i, (me.tempstats || {})["_user_tags"], v); | |||
}); | |||
me.render_stat("_user_tags", (me.tempstats || {})["_user_tags"]); | |||
} else { | |||
//render normal stats | |||
me.render_stat("_user_tags", (r.message.stats || {})["_user_tags"]); | |||
} | |||
me.render_stat("_user_tags", (r.message.stats || {})["_user_tags"]); | |||
let stats_dropdown = me.sidebar.find('.list-stats-dropdown'); | |||
me.setup_dropdown_search(stats_dropdown,'.stat-label'); | |||
} | |||
@@ -355,13 +333,14 @@ frappe.views.ListSidebar = class ListSidebar { | |||
field: field, | |||
stat: stats, | |||
sum: sum, | |||
label: field === '_user_tags' ? (tags ? __(label) : __("Tags")) : __(label), | |||
label: field === '_user_tags' ? (tags ? __(label) : __("Tag")) : __(label), | |||
}; | |||
$(frappe.render_template("list_sidebar_stat", context)) | |||
.on("click", ".stat-link", function() { | |||
var doctype = "Tag Link"; | |||
var fieldname = $(this).attr('data-field'); | |||
var label = $(this).attr('data-label'); | |||
var condition = "like"; | |||
var condition = "="; | |||
var existing = me.list_view.filter_area.filter_list.get_filter(fieldname); | |||
if(existing) { | |||
existing.remove(); | |||
@@ -370,7 +349,7 @@ frappe.views.ListSidebar = class ListSidebar { | |||
label = "%,%"; | |||
condition = "not like"; | |||
} | |||
me.list_view.filter_area.filter_list.add_filter(me.list_view.doctype, fieldname, condition, label) | |||
me.list_view.filter_area.filter_list.add_filter(doctype, fieldname, condition, label) | |||
.then(function() { | |||
me.list_view.refresh(); | |||
}); | |||
@@ -432,7 +432,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { | |||
tag_editor.wrapper.on('click', '.tagit-label', (e) => { | |||
const $this = $(e.currentTarget); | |||
this.filter_area.add(this.doctype, '_user_tags', '=', $this.text()); | |||
this.filter_area.add('Tag Link', 'tag', '=', $this.text()); | |||
}); | |||
}); | |||
} | |||
@@ -173,6 +173,13 @@ frappe.ui.Filter = class { | |||
if(this.field) for(let k in this.field.df) cur[k] = this.field.df[k]; | |||
let original_docfield = (this.fieldselect.fields_by_name[doctype] || {})[fieldname]; | |||
if (doctype === "Tag Link" || fieldname === "_user_tags") { | |||
original_docfield = {fieldname: "tag", fieldtype: "Data", label: "Tags", parent: "Tag Link"}; | |||
doctype = "Tag Link"; | |||
condition = "="; | |||
} | |||
if(!original_docfield) { | |||
console.warn(`Field ${fieldname} is not selectable.`); | |||
this.remove(); | |||
@@ -35,13 +35,14 @@ frappe.ui.TagEditor = Class.extend({ | |||
onTagAdd: (tag) => { | |||
if(me.initialized && !me.refreshing) { | |||
return frappe.call({ | |||
method: 'frappe.desk.tags.add_tag', | |||
method: "frappe.desk.doctype.tag.tag.add_tag", | |||
args: me.get_args(tag), | |||
callback: function(r) { | |||
var user_tags = me.user_tags ? me.user_tags.split(",") : []; | |||
user_tags.push(tag) | |||
me.user_tags = user_tags.join(","); | |||
me.on_change && me.on_change(me.user_tags); | |||
frappe.tags.utils.fetch_tags(); | |||
} | |||
}); | |||
} | |||
@@ -49,13 +50,14 @@ frappe.ui.TagEditor = Class.extend({ | |||
onTagRemove: (tag) => { | |||
if(!me.refreshing) { | |||
return frappe.call({ | |||
method: 'frappe.desk.tags.remove_tag', | |||
method: "frappe.desk.doctype.tag.tag.remove_tag", | |||
args: me.get_args(tag), | |||
callback: function(r) { | |||
var user_tags = me.user_tags.split(","); | |||
user_tags.splice(user_tags.indexOf(tag), 1); | |||
me.user_tags = user_tags.join(","); | |||
me.on_change && me.on_change(me.user_tags); | |||
frappe.tags.utils.fetch_tags(); | |||
} | |||
}); | |||
} | |||
@@ -82,12 +84,10 @@ frappe.ui.TagEditor = Class.extend({ | |||
$input.on("input", function(e) { | |||
var value = e.target.value; | |||
frappe.call({ | |||
method:"frappe.desk.tags.get_tags", | |||
method: "frappe.desk.doctype.tag.tag.get_tags", | |||
args:{ | |||
doctype: me.frm.doctype, | |||
txt: value.toLowerCase(), | |||
cat_tags: me.list_sidebar ? | |||
JSON.stringify(me.list_sidebar.get_cat_tags()) : '[]' | |||
}, | |||
callback: function(r) { | |||
me.awesomplete.list = r.message; | |||
@@ -67,7 +67,6 @@ frappe.ui.Tags = class { | |||
} | |||
addTag(label) { | |||
label = toTitle(label); | |||
if(label && label!== '' && !this.tagsList.includes(label)) { | |||
let $tag = this.getTag(label); | |||
this.getListElement($tag).insertBefore(this.$inputWrapper); | |||
@@ -1,6 +1,7 @@ | |||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||
// MIT License. See license.txt | |||
frappe.provide('frappe.search'); | |||
frappe.provide('frappe.tags'); | |||
frappe.search.AwesomeBar = Class.extend({ | |||
setup: function(element) { | |||
@@ -140,6 +141,8 @@ frappe.search.AwesomeBar = Class.extend({ | |||
__("document type..., e.g. customer")+'</td></tr>\ | |||
<tr><td>'+__("Search in a document type")+'</td><td>'+ | |||
__("text in document type")+'</td></tr>\ | |||
<tr><td>'+__("Tags")+'</td><td>'+ | |||
__("tag name..., e.g. #tag")+'</td></tr>\ | |||
<tr><td>'+__("Open a module or tool")+'</td><td>'+ | |||
__("module name...")+'</td></tr>\ | |||
<tr><td>'+__("Calculate")+'</td><td>'+ | |||
@@ -177,6 +180,9 @@ frappe.search.AwesomeBar = Class.extend({ | |||
frappe.search.utils.get_recent_pages(txt || ""), | |||
frappe.search.utils.get_executables(txt) | |||
); | |||
if (txt.charAt(0) === "#") { | |||
options = frappe.tags.utils.get_tags(txt); | |||
} | |||
var out = this.deduplicate(options); | |||
return out.sort(function(a, b) { | |||
return b.index - a.index; | |||
@@ -215,6 +221,11 @@ frappe.search.AwesomeBar = Class.extend({ | |||
make_global_search: function(txt) { | |||
var me = this; | |||
if (txt.charAt(0) === "#") { | |||
return; | |||
} | |||
this.options.push({ | |||
label: __("Search for '{0}'", [txt.bold()]), | |||
value: __("Search for '{0}'", [txt]), | |||
@@ -124,6 +124,11 @@ frappe.search.SearchDialog = Class.extend({ | |||
// Help results | |||
// this.$modal_body.on('click', 'a[data-path]', frappe.help.show_results); | |||
this.bind_keyboard_events(); | |||
// Setup Minimizable functionality | |||
this.search_dialog.minimizable = true; | |||
this.search_dialog.is_minimized = false; | |||
this.search_dialog.$wrapper.find('.btn-modal-minimize').click(() => this.toggle_minimize()); | |||
}, | |||
bind_keyboard_events: function() { | |||
@@ -178,7 +183,17 @@ frappe.search.SearchDialog = Class.extend({ | |||
} else { | |||
this.$search_modal.find('.loading-state').removeClass('hide'); | |||
} | |||
if (this.current_keyword.charAt(0) === "#") { | |||
this.search = this.searches["tags"]; | |||
} else { | |||
this.search = this.searches["global_search"]; | |||
} | |||
this.search.get_results(keywords, this.parse_results.bind(this)); | |||
if (this.search_dialog.is_minimized) { | |||
this.toggle_minimize(); | |||
} | |||
}, | |||
parse_results: function(result_sets, keyword) { | |||
@@ -308,7 +323,7 @@ frappe.search.SearchDialog = Class.extend({ | |||
frappe.route_options = result.route_options; | |||
} | |||
$result.on('click', (e) => { | |||
this.search_dialog.hide(); | |||
this.toggle_minimize(); | |||
if(result.onclick) { | |||
result.onclick(result.match); | |||
} else { | |||
@@ -353,12 +368,25 @@ frappe.search.SearchDialog = Class.extend({ | |||
this.$modal_body.find('.more-results.last').slideDown(200, function() {}); | |||
}, | |||
get_minimize_btn: function() { | |||
return this.search_dialog.$wrapper.find(".modal-header .btn-modal-minimize"); | |||
}, | |||
toggle_minimize: function() { | |||
let modal = this.search_dialog.$wrapper.closest('.modal').toggleClass('modal-minimize'); | |||
modal.attr('tabindex') ? modal.removeAttr('tabindex') : modal.attr('tabindex', -1); | |||
this.get_minimize_btn().find('i').toggleClass('octicon-chevron-down').toggleClass('octicon-chevron-up'); | |||
this.search_dialog.is_minimized = !this.search_dialog.is_minimized; | |||
this.on_minimize_toggle && this.on_minimize_toggle(this.search_dialog.is_minimized); | |||
this.search_dialog.header.find('.modal-title').toggleClass('cursor-pointer'); | |||
}, | |||
// Search objects | |||
searches: { | |||
global_search: { | |||
input_placeholder: __("Global Search"), | |||
input_placeholder: __("Search"), | |||
empty_state_text: __("Search for anything"), | |||
no_results_status: (keyword) => __("<p>No results found for '" + keyword + "' in Global Search</p>"), | |||
no_results_status: (keyword) => "<p>" + __("No results found for {0} in Global Search", [keyword]) + "</p>", | |||
get_results: function(keywords, callback) { | |||
var start = 0, limit = 1000; | |||
@@ -372,6 +400,22 @@ frappe.search.SearchDialog = Class.extend({ | |||
}); | |||
} | |||
}, | |||
tags: { | |||
input_placeholder: __("Search"), | |||
empty_state_text: __("Search for anything"), | |||
no_results_status: (keyword) => "<p>" + __("No documents found tagged with {0}", [keyword]) + "</p>", | |||
get_results: function(keywords, callback) { | |||
var results = frappe.search.utils.get_nav_results(keywords); | |||
frappe.tags.utils.get_tag_results(keywords) | |||
.then(function(global_results) { | |||
results = results.concat(global_results); | |||
callback(results, keywords); | |||
}, function (err) { | |||
console.error(err); | |||
}); | |||
} | |||
}, | |||
}, | |||
}); |
@@ -1,6 +1,12 @@ | |||
<div class="search-header"> | |||
<i class="octicon octicon-search"></i> | |||
<input type="text" class="form-control search-input" style="padding-left: 15px"> | |||
<p class="loading-state hide" style="margin: 0px 20px; color:#d4d9dd">{%= __("Searching")%} ...</p> | |||
<a type="button" class="close" data-dismiss="modal" aria-hidden="true">×</a> | |||
<i class="octicon octicon-search"></i> | |||
<input type="text" class="form-control search-input" style="padding-left: 15px"> | |||
<p class="loading-state hide" style="margin: 0px 20px; color:#d4d9dd">{%= __("Searching")%} ...</p> | |||
<a type="button" class="btn btn-default btn-sm btn-modal-minimize" style="margin-right: 2px;"> | |||
<i class="octicon octicon-chevron-down" style="padding: 1px 0px;"></i> | |||
</a> | |||
<a type="button" class="btn btn-default btn-sm btn-modal-close" data-dismiss="modal" aria-hidden="true"> | |||
<i class="octicon octicon-x visible-xs" style="padding: 1px 0px;"></i> | |||
<span class="hidden-xs">Close</span> | |||
</a> | |||
</div> |
@@ -0,0 +1,108 @@ | |||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors | |||
// MIT License. See license.txt | |||
frappe.provide("frappe.tags"); | |||
frappe.tags.utils = { | |||
get_tags: function(txt) { | |||
txt = txt.slice(1); | |||
let out = []; | |||
for (let i in frappe.tags.tags) { | |||
let tag = frappe.tags.tags[i]; | |||
let level = frappe.search.utils.fuzzy_search(txt, tag); | |||
if (level) { | |||
out.push({ | |||
type: "Tag", | |||
label: __("#{0}", [frappe.search.utils.bolden_match_part(__(tag), txt)]), | |||
value: __("#{0}", [__(tag)]), | |||
index: 1 + level, | |||
match: tag, | |||
onclick() { | |||
// Use Global Search Dialog for tag search too. | |||
frappe.searchdialog.search.init_search("#".concat(tag), "tags"); | |||
} | |||
}); | |||
} | |||
} | |||
return out; | |||
}, | |||
fetch_tags() { | |||
frappe.call({ | |||
method: "frappe.desk.doctype.tag.tag.get_tags_list_for_awesomebar", | |||
callback: function(r) { | |||
if (r && r.message) { | |||
frappe.tags.tags = $.extend([], r.message); | |||
} | |||
} | |||
}); | |||
}, | |||
get_tag_results: function(tag) { | |||
function get_results_sets(data) { | |||
var results_sets = [], result, set; | |||
function get_existing_set(doctype) { | |||
return results_sets.find(function(set) { | |||
return set.title === doctype; | |||
}); | |||
} | |||
function make_description(content) { | |||
var field_length = 110; | |||
var field_value = null; | |||
if (content.length > field_length) { | |||
field_value = content.slice(0, field_length) + "..."; | |||
} else { | |||
var length = content.length; | |||
field_value = content.slice(0, length) + "..."; | |||
} | |||
return field_value; | |||
} | |||
data.forEach(function(d) { | |||
// more properties | |||
var description = ""; | |||
if (d.content) { | |||
description = make_description(d.content); | |||
} | |||
result = { | |||
label: d.name, | |||
value: d.name, | |||
description: description, | |||
route: ['Form', d.doctype, d.name], | |||
}; | |||
set = get_existing_set(d.doctype); | |||
if (set) { | |||
set.results.push(result); | |||
} else { | |||
set = { | |||
title: d.doctype, | |||
results: [result], | |||
fetch_type: "Global" | |||
}; | |||
results_sets.push(set); | |||
} | |||
}); | |||
return results_sets; | |||
} | |||
return new Promise(function(resolve) { | |||
frappe.call({ | |||
method: "frappe.desk.doctype.tag.tag.get_documents_for_tag", | |||
args: { | |||
tag: tag | |||
}, | |||
callback: function(r) { | |||
if (r.message) { | |||
resolve(get_results_sets(r.message)); | |||
} else { | |||
resolve([]); | |||
} | |||
} | |||
}); | |||
}); | |||
}, | |||
}; |
@@ -242,10 +242,6 @@ def update_global_search(doc): | |||
if doc.get(field.fieldname) and field.fieldtype not in frappe.model.table_fields: | |||
content.append(get_formatted_value(doc.get(field.fieldname), field)) | |||
tags = (doc.get('_user_tags') or '').strip() | |||
if tags: | |||
content.extend(list(filter(lambda x: x, tags.split(',')))) | |||
# Get children | |||
for child in doc.meta.get_table_fields(): | |||
for d in doc.get(child.fieldname): | |||