|
- # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
- # License: GNU General Public License v3. See license.txt
-
- from __future__ import unicode_literals
-
- import frappe
- import re
- from frappe.utils import cint, strip_html_tags
-
- def setup_global_search_table():
- '''Creates __global_seach table'''
- if not '__global_search' in frappe.db.get_tables():
- frappe.db.sql('''create table __global_search(
- doctype varchar(100),
- name varchar(140),
- title varchar(140),
- content text,
- fulltext(content),
- route varchar(140),
- published int(1) not null default 0,
- unique (doctype, name))
- COLLATE=utf8mb4_unicode_ci
- ENGINE=MyISAM
- CHARACTER SET=utf8mb4''')
-
- def reset():
- '''Deletes all data in __global_search'''
- frappe.db.sql('delete from __global_search')
-
- def get_doctypes_with_global_search():
- '''Return doctypes with global search fields'''
- def _get():
- global_search_doctypes = []
- for d in frappe.get_all('DocType', 'name, module'):
- meta = frappe.get_meta(d.name)
- if len(meta.get_global_search_fields()) > 0:
- global_search_doctypes.append(d)
-
- installed_apps = frappe.get_installed_apps()
-
- doctypes = [d.name for d in global_search_doctypes
- if frappe.local.module_app[frappe.scrub(d.module)] in installed_apps]
- return doctypes
-
- return frappe.cache().get_value('doctypes_with_global_search', _get)
-
- def update_global_search(doc):
- '''Add values marked with `in_global_search` to
- `frappe.flags.update_global_search` from given doc
- :param doc: Document to be added to global search'''
-
- if cint(doc.meta.istable) == 1 and frappe.db.exists("DocType", doc.parenttype):
- d = frappe.get_doc(doc.parenttype, doc.parent)
- update_global_search(d)
- return
-
- if doc.docstatus > 1:
- return
-
- if frappe.flags.update_global_search==None:
- frappe.flags.update_global_search = []
-
- content = []
- for field in doc.meta.get_global_search_fields():
- if doc.get(field.fieldname):
- if getattr(field, 'fieldtype', None) == "Table":
-
- # Get children
- for d in doc.get(field.fieldname):
- if d.parent == doc.name:
- for field in d.meta.get_global_search_fields():
- if d.get(field.fieldname):
- content.append(get_field_value(d, field))
- else:
- content.append(get_field_value(doc, field))
-
- if content:
- published = 0
- if hasattr(doc, 'is_website_published') and doc.meta.allow_guest_to_view:
- published = 1 if doc.is_website_published() else 0
-
- frappe.flags.update_global_search.append(
- dict(doctype=doc.doctype, name=doc.name, content=' ||| '.join(content or ''),
- published=published, title=doc.get_title(), route=doc.get('route')))
-
- def get_field_value(doc, field):
- '''Prepare field from raw data'''
-
- from HTMLParser import HTMLParser
-
- value = doc.get(field.fieldname)
- if(getattr(field, 'fieldtype', None) in ["Text", "Text Editor"]):
- h = HTMLParser()
- value = h.unescape(value)
- value = (re.subn(r'<[\s]*(script|style).*?</\1>(?s)', '', unicode(value))[0])
- value = ' '.join(value.split())
- return field.label + " : " + strip_html_tags(unicode(value))
-
- def sync_global_search():
- '''Add values from `frappe.flags.update_global_search` to __global_search.
- This is called internally at the end of the request.'''
-
- for value in frappe.flags.update_global_search:
- frappe.db.sql('''
- insert into __global_search
- (doctype, name, content, published, title, route)
- values
- (%(doctype)s, %(name)s, %(content)s, %(published)s, %(title)s, %(route)s)
- on duplicate key update
- content = %(content)s''', value)
-
- frappe.flags.update_global_search = []
-
- def rebuild_for_doctype(doctype):
- '''Rebuild entries of doctype's documents in __global_search on change of
- searchable fields
- :param doctype: Doctype '''
-
- frappe.flags.update_global_search = []
-
- frappe.db.sql('''
- delete
- from __global_search
- where
- doctype = %s''', doctype, as_dict=True)
-
- for d in frappe.get_all(doctype):
- update_global_search(frappe.get_doc(doctype, d.name))
- sync_global_search()
-
- def delete_for_document(doc):
- '''Delete the __global_search entry of a document that has
- been deleted
- :param doc: Deleted document'''
-
- frappe.db.sql('''
- delete
- from __global_search
- where
- doctype = %s and
- name = %s''', (doc.doctype, doc.name), as_dict=True)
-
- @frappe.whitelist()
- def search(text, start=0, limit=20, doctype=""):
- '''Search for given text in __global_search
- :param text: phrase to be searched
- :param start: start results at, default 0
- :param limit: number of results to return, default 20
- :return: Array of result objects'''
-
- text = "+" + text + "*"
- if not doctype:
- results = frappe.db.sql('''
- select
- doctype, name, content
- from
- __global_search
- where
- match(content) against (%s IN BOOLEAN MODE)
- limit {start}, {limit}'''.format(start=start, limit=limit), text+"*", as_dict=True)
- else:
- results = frappe.db.sql('''
- select
- doctype, name, content
- from
- __global_search
- where
- doctype = %s AND
- match(content) against (%s IN BOOLEAN MODE)
- limit {start}, {limit}'''.format(start=start, limit=limit), (doctype, text), as_dict=True)
-
- for r in results:
- if frappe.get_meta(r.doctype).image_field:
- doc = frappe.get_doc(r.doctype, r.name)
- r.image = doc.get(doc.meta.image_field)
-
- return results
-
- @frappe.whitelist(allow_guest=True)
- def web_search(text, start=0, limit=20):
- '''Search for given text in __global_search where published = 1
- :param text: phrase to be searched
- :param start: start results at, default 0
- :param limit: number of results to return, default 20
- :return: Array of result objects'''
-
- text = "+" + text + "*"
- results = frappe.db.sql('''
- select
- doctype, name, content, title, route
- from
- __global_search
- where
- published = 1 and
- match(content) against (%s IN BOOLEAN MODE)
- limit {start}, {limit}'''.format(start=start, limit=limit),
- text, as_dict=True)
- return results
|