diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 282481888e..23bbc8af24 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -39,7 +39,7 @@ Cypress.Commands.add('fill_field', (fieldname, value, fieldtype='Data') => { let selector = `.form-control[data-fieldname="${fieldname}"]`; if (fieldtype === 'Text Editor') { - selector = `[data-fieldname="${fieldname}"] .ql-editor`; + selector = `[data-fieldname="${fieldname}"] .ql-editor[contenteditable=true]`; } if (fieldtype === 'Code') { selector = `[data-fieldname="${fieldname}"] .ace_text-input`; diff --git a/frappe/__init__.py b/frappe/__init__.py index 0242843cee..cd662cd6cb 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -16,7 +16,6 @@ from faker import Faker # public from .exceptions import * from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader) -from .utils.error import get_frame_locals # Hamless for Python 3 # For Python 2 set default encoding to utf-8 @@ -188,15 +187,20 @@ def connect(site=None, db_name=None): local.db = get_db(user=db_name or local.conf.db_name) set_user("Administrator") -def connect_read_only(): +def connect_replica(): from frappe.database import get_db + user = local.conf.db_name + password = local.conf.db_password - local.read_only_db = get_db(host=local.conf.slave_host, user=local.conf.slave_db_name, - password=local.conf.slave_db_password) + if local.conf.different_credentials_for_replica: + user = local.conf.replica_db_name + password = local.conf.replica_db_password + + local.replica_db = get_db(host=local.conf.replica_host, user=user, password=password) # swap db connections - local.master_db = local.db - local.db = local.read_only_db + local.primary_db = local.db + local.db = local.replica_db def get_site_config(sites_path=None, site_path=None): """Returns `site_config.json` combined with `sites/common_site_config.json`. @@ -274,7 +278,7 @@ def errprint(msg): if not request or (not "cmd" in local.form_dict) or conf.developer_mode: print(msg) - error_log.append({"exc": msg, "locals": get_frame_locals()}) + error_log.append({"exc": msg}) def log(msg): """Add to `debug_log`. @@ -496,16 +500,17 @@ def whitelist(allow_guest=False, xss_safe=False): def read_only(): def innfn(fn): def wrapper_fn(*args, **kwargs): - if conf.use_slave_for_read_only: - connect_read_only() + if conf.read_from_replica: + connect_replica() + try: retval = fn(*args, **get_newargs(fn, kwargs)) except: raise finally: - if local and hasattr(local, 'master_db'): + if local and hasattr(local, 'primary_db'): local.db.close() - local.db = local.master_db + local.db = local.primary_db return retval return wrapper_fn @@ -1329,14 +1334,15 @@ def format(*args, **kwargs): import frappe.utils.formatters return frappe.utils.formatters.format_value(*args, **kwargs) -def get_print(doctype=None, name=None, print_format=None, style=None, html=None, as_pdf=False, doc=None, output = None, no_letterhead = 0): +def get_print(doctype=None, name=None, print_format=None, style=None, html=None, as_pdf=False, doc=None, output = None, no_letterhead = 0, password=None): """Get Print Format for given document. :param doctype: DocType of document. :param name: Name of document. :param print_format: Print Format name. Default 'Standard', :param style: Print Format style. - :param as_pdf: Return as PDF. Default False.""" + :param as_pdf: Return as PDF. Default False. + :param password: Password to encrypt the pdf with. Default None""" from frappe.website.render import build_page from frappe.utils.pdf import get_pdf @@ -1347,15 +1353,19 @@ def get_print(doctype=None, name=None, print_format=None, style=None, html=None, local.form_dict.doc = doc local.form_dict.no_letterhead = no_letterhead + options = None + if password: + options = {'password': password} + if not html: html = build_page("printview") if as_pdf: - return get_pdf(html, output = output) + return get_pdf(html, output = output, options = options) else: return html -def attach_print(doctype, name, file_name=None, print_format=None, style=None, html=None, doc=None, lang=None, print_letterhead=True): +def attach_print(doctype, name, file_name=None, print_format=None, style=None, html=None, doc=None, lang=None, print_letterhead=True, password=None): from frappe.utils import scrub_urls if not file_name: file_name = name @@ -1374,12 +1384,12 @@ def attach_print(doctype, name, file_name=None, print_format=None, style=None, h if int(print_settings.send_print_as_pdf or 0): out = { "fname": file_name + ".pdf", - "fcontent": get_print(doctype, name, print_format=print_format, style=style, html=html, as_pdf=True, doc=doc, no_letterhead=no_letterhead) + "fcontent": get_print(doctype, name, print_format=print_format, style=style, html=html, as_pdf=True, doc=doc, no_letterhead=no_letterhead, password=password) } else: out = { "fname": file_name + ".html", - "fcontent": scrub_urls(get_print(doctype, name, print_format=print_format, style=style, html=html, doc=doc, no_letterhead=no_letterhead)).encode("utf-8") + "fcontent": scrub_urls(get_print(doctype, name, print_format=print_format, style=style, html=html, doc=doc, no_letterhead=no_letterhead, password=password)).encode("utf-8") } local.flags.ignore_print_permissions = False diff --git a/frappe/auth.py b/frappe/auth.py index 37a79bd4a6..3a58330e11 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -8,7 +8,7 @@ from frappe import _ import frappe import frappe.database import frappe.utils -from frappe.utils import cint, flt, get_datetime, datetime +from frappe.utils import cint, flt, get_datetime, datetime, date_diff, today import frappe.utils.user from frappe import conf from frappe.sessions import Session, clear_sessions, delete_session @@ -124,6 +124,12 @@ class LoginManager: frappe.clear_cache(user = frappe.form_dict.get('usr')) user, pwd = get_cached_user_pass() self.authenticate(user=user, pwd=pwd) + if self.force_user_to_reset_password(): + doc = frappe.get_doc("User", self.user) + frappe.local.response["redirect_to"] = doc.reset_password(send_email=False, password_expired=True) + frappe.local.response["message"] = "Password Reset" + return False + if should_run_2fa(self.user): authenticate_for_2factor(self.user) if not confirm_otp_token(self): @@ -209,6 +215,22 @@ class LoginManager: self.check_if_enabled(user) self.user = self.check_password(user, pwd) + def force_user_to_reset_password(self): + if not self.user: + return + + reset_pwd_after_days = cint(frappe.db.get_single_value("System Settings", + "force_user_to_reset_password")) + + if reset_pwd_after_days: + last_password_reset_date = frappe.db.get_value("User", + self.user, "last_password_reset_date") or today() + + last_pwd_reset_days = date_diff(today(), last_password_reset_date) + + if last_pwd_reset_days > reset_pwd_after_days: + return True + def check_if_enabled(self, user): """raise exception if user not enabled""" doc = frappe.get_doc("System Settings") diff --git a/frappe/boot.py b/frappe/boot.py index 0583b31181..4acb7ee3c1 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -16,7 +16,6 @@ from frappe.desk.form.load import get_meta_bundle from frappe.utils.change_log import get_versions from frappe.translate import get_lang_dict from frappe.email.inbox import get_email_accounts -from frappe.core.doctype.feedback_trigger.feedback_trigger import get_enabled_feedback_trigger from frappe.social.doctype.energy_point_settings.energy_point_settings import is_energy_point_enabled from frappe.social.doctype.energy_point_log.energy_point_log import get_energy_points from frappe.social.doctype.post.post import frequently_visited_links @@ -77,7 +76,6 @@ def get_bootinfo(): bootinfo.calendars = sorted(frappe.get_hooks("calendars")) bootinfo.treeviews = frappe.get_hooks("treeviews") or [] bootinfo.lang_dict = get_lang_dict() - bootinfo.feedback_triggers = get_enabled_feedback_trigger() bootinfo.gsuite_enabled = get_gsuite_status() bootinfo.success_action = get_success_action() bootinfo.update(get_email_accounts(user=frappe.session.user)) diff --git a/frappe/client.py b/frappe/client.py index fb2d47925b..3f7491ade5 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -371,4 +371,5 @@ def check_parent_permission(parent, child_doctype): if frappe.permissions.has_permission(parent): return # Either parent not passed or the user doesn't have permission on parent doctype of child table! - raise frappe.PermissionError \ No newline at end of file + raise frappe.PermissionError + diff --git a/frappe/commands/__init__.py b/frappe/commands/__init__.py index 993f1f7d11..84e5f4937f 100644 --- a/frappe/commands/__init__.py +++ b/frappe/commands/__init__.py @@ -72,12 +72,11 @@ def call_command(cmd, context): def get_commands(): # prevent circular imports - from .docs import commands as doc_commands from .scheduler import commands as scheduler_commands from .site import commands as site_commands from .translate import commands as translate_commands from .utils import commands as utils_commands - return list(set(doc_commands + scheduler_commands + site_commands + translate_commands + utils_commands)) + return list(set(scheduler_commands + site_commands + translate_commands + utils_commands)) commands = get_commands() diff --git a/frappe/commands/docs.py b/frappe/commands/docs.py deleted file mode 100644 index 6c73739478..0000000000 --- a/frappe/commands/docs.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import unicode_literals, absolute_import -import click -import os, shutil -import frappe -from frappe.commands import pass_context - -@click.command('build-docs') -@pass_context -@click.argument('app') -@click.option('--docs-version', default='current') -@click.option('--target', default=None) -@click.option('--local', default=False, is_flag=True, help='Run app locally') -@click.option('--watch', default=False, is_flag=True, help='Watch for changes and rewrite') -def build_docs(context, app, docs_version="current", target=None, local=False, watch=False): - "Setup docs in target folder of target app" - from frappe.utils import watch as start_watch - from frappe.utils.setup_docs import add_breadcrumbs_tag - - for site in context.sites: - _build_docs_once(site, app, docs_version, target, local) - - if watch: - def trigger_make(source_path, event_type): - if "/docs/user/" in source_path: - # user file - target_path = frappe.get_app_path(target, 'www', 'docs', 'user', - os.path.relpath(source_path, start=frappe.get_app_path(app, 'docs', 'user'))) - shutil.copy(source_path, target_path) - add_breadcrumbs_tag(target_path) - - if source_path.endswith('/docs/index.md'): - target_path = frappe.get_app_path(target, 'www', 'docs', 'index.md') - shutil.copy(source_path, target_path) - - apps_path = frappe.get_app_path(app) - start_watch(apps_path, handler=trigger_make) - -def _build_docs_once(site, app, docs_version, target, local, only_content_updated=False): - from frappe.utils.setup_docs import setup_docs - - try: - - frappe.init(site=site) - frappe.connect() - make = setup_docs(app, target) - - if not only_content_updated: - make.build(docs_version) - - #make.make_docs(target, local) - - finally: - frappe.destroy() - -commands = [ - build_docs, -] diff --git a/frappe/commands/site.py b/frappe/commands/site.py index 3798c07e91..ee8131c1dc 100755 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -157,16 +157,17 @@ def _reinstall(site, admin_password=None, mariadb_root_username=None, mariadb_ro admin_password=admin_password) @click.command('install-app') -@click.argument('app') +@click.argument('apps', nargs=-1) @pass_context -def install_app(context, app): - "Install a new app to site" +def install_app(context, apps): + "Install a new app to site, supports multiple apps" from frappe.installer import install_app as _install_app for site in context.sites: frappe.init(site=site) frappe.connect() try: - _install_app(app, verbose=context.verbose) + for app in apps: + _install_app(app, verbose=context.verbose) finally: frappe.destroy() diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index ab8597832d..219a99070b 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -625,19 +625,30 @@ def setup_help(context): print_in_app_help_deprecation() @click.command('rebuild-global-search') +@click.option('--static-pages', is_flag=True, default=False, help='Rebuild global search for static pages') @pass_context -def rebuild_global_search(context): +def rebuild_global_search(context, static_pages=False): '''Setup help table in the current site (called after migrate)''' - from frappe.utils.global_search import (get_doctypes_with_global_search, rebuild_for_doctype) + from frappe.utils.global_search import (get_doctypes_with_global_search, rebuild_for_doctype, + get_routes_to_index, add_route_to_global_search, sync_global_search) for site in context.sites: try: frappe.init(site) frappe.connect() - doctypes = get_doctypes_with_global_search() - for i, doctype in enumerate(doctypes): - rebuild_for_doctype(doctype) - update_progress_bar('Rebuilding Global Search', i, len(doctypes)) + + if static_pages: + routes = get_routes_to_index() + for i, route in enumerate(routes): + add_route_to_global_search(route) + frappe.local.request = None + update_progress_bar('Rebuilding Global Search', i, len(routes)) + sync_global_search() + else: + doctypes = get_doctypes_with_global_search() + for i, doctype in enumerate(doctypes): + rebuild_for_doctype(doctype) + update_progress_bar('Rebuilding Global Search', i, len(doctypes)) finally: frappe.destroy() diff --git a/frappe/config/__init__.py b/frappe/config/__init__.py index 1fe06f9094..b2c1f41d78 100644 --- a/frappe/config/__init__.py +++ b/frappe/config/__init__.py @@ -78,15 +78,22 @@ def get_modules_from_app(app): return active_modules_list def get_all_empty_tables_by_module(): - results = frappe.db.sql(""" - SELECT - name, module - FROM information_schema.tables as i - JOIN tabDocType as d - ON i.table_name = CONCAT('tab', d.name) - WHERE table_rows = 0; - - """) + results = frappe.db.multisql({ + 'mariadb': ''' + SELECT `name`, `module` + FROM information_schema.tables AS i + JOIN `tabDocType` AS d + ON i.table_name = CONCAT('tab', d.name) + WHERE `table_rows` = 0; + ''', + 'postgres': ''' + SELECT "name", "module" + FROM "pg_stat_all_tables" AS i + JOIN "tabDocType" AS d + ON i.relname = CONCAT('tab', d.name) + WHERE n_tup_ins = 0; + ''' + }) empty_tables_by_module = {} @@ -95,7 +102,6 @@ def get_all_empty_tables_by_module(): empty_tables_by_module[module].append(doctype) else: empty_tables_by_module[module] = [doctype] - return empty_tables_by_module def is_domain(module): diff --git a/frappe/contacts/address_and_contact.py b/frappe/contacts/address_and_contact.py index 38d292e9b4..6523b0b1e5 100644 --- a/frappe/contacts/address_and_contact.py +++ b/frappe/contacts/address_and_contact.py @@ -152,3 +152,11 @@ def filter_dynamic_link_doctypes(doctype, txt, searchfield, start, page_len, fil valid_doctypes = [[doctype] for doctype in valid_doctypes] return valid_doctypes + +def set_link_title(doc): + if not doc.links: + return + for link in doc.links: + if not link.link_title: + linked_doc = frappe.get_doc(link.link_doctype, link.link_name) + link.link_title = linked_doc.get("title_field") or linked_doc.get("name") diff --git a/frappe/contacts/doctype/address/address.py b/frappe/contacts/doctype/address/address.py index c705f972d3..57ed4e937f 100644 --- a/frappe/contacts/doctype/address/address.py +++ b/frappe/contacts/doctype/address/address.py @@ -15,6 +15,7 @@ from frappe.model.naming import make_autoname from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links from six import iteritems, string_types from past.builtins import cmp +from frappe.contacts.address_and_contact import set_link_title import functools @@ -39,6 +40,7 @@ class Address(Document): def validate(self): self.link_address() self.validate_reference() + set_link_title(self) deduplicate_dynamic_links(self) def link_address(self): diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index 987fad3829..0e0d9aeabc 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -10,6 +10,7 @@ from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_li from six import iteritems from past.builtins import cmp from frappe.model.naming import append_number_if_name_exists +from frappe.contacts.address_and_contact import set_link_title import functools @@ -31,6 +32,7 @@ class Contact(Document): if self.email_id: self.email_id = self.email_id.strip() self.set_user() + set_link_title(self) if self.email_id and not self.image: self.image = has_gravatar(self.email_id) diff --git a/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py b/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py index 0907476b30..f73858c8ab 100644 --- a/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py +++ b/frappe/contacts/report/addresses_and_contacts/addresses_and_contacts.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from six import iteritems import frappe - +from frappe import _ field_map = { "Contact": [ "first_name", "last_name", "phone", "mobile_no", "email_id", "is_primary_contact" ], @@ -94,6 +94,9 @@ def get_reference_details(reference_doctype, doctype, reference_list, reference_ for d in records: temp_records.append(d[1:]) + if not reference_list: + frappe.throw(_("No records present in {0}".format(reference_doctype))) + reference_details[reference_list[0]][frappe.scrub(doctype)] = temp_records return reference_details diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index 7badf737e4..8b7941c086 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals from frappe import _ from frappe.utils import get_fullname, now from frappe.model.document import Document -from frappe.core.utils import get_parent_doc, set_timeline_doc +from frappe.core.utils import set_timeline_doc import frappe class ActivityLog(Document): diff --git a/frappe/core/doctype/activity_log/feed.py b/frappe/core/doctype/activity_log/feed.py index e09436196e..8892038681 100644 --- a/frappe/core/doctype/activity_log/feed.py +++ b/frappe/core/doctype/activity_log/feed.py @@ -67,7 +67,7 @@ def get_feed_match_conditions(user=None, doctype='Comment'): user_permissions = frappe.permissions.get_user_permissions(user) can_read = frappe.get_user().get_can_read() - can_read_doctypes = ['"{}"'.format(doctype) for doctype in + can_read_doctypes = ["'{}'".format(doctype) for doctype in list(set(can_read) - set(list(user_permissions)))] if can_read_doctypes: diff --git a/frappe/core/doctype/comment/test_comment.py b/frappe/core/doctype/comment/test_comment.py index 2f6583b7ba..2adc5eb899 100644 --- a/frappe/core/doctype/comment/test_comment.py +++ b/frappe/core/doctype/comment/test_comment.py @@ -48,10 +48,10 @@ class TestComment(unittest.TestCase): add_comment('pleez vizits my site http://mysite.com', 'test@test.com', 'bad commentor', 'Blog Post', test_blog.name, test_blog.route) - self.assertEqual(frappe.get_all('Comment', fields = ['*'], filters = dict( + self.assertEqual(len(frappe.get_all('Comment', fields = ['*'], filters = dict( reference_doctype = test_blog.doctype, reference_name = test_blog.name - ))[0].published, 0) + ))), 0) diff --git a/frappe/core/doctype/communication/communication.js b/frappe/core/doctype/communication/communication.js index 8e35c388a5..924c29bee2 100644 --- a/frappe/core/doctype/communication/communication.js +++ b/frappe/core/doctype/communication/communication.js @@ -31,13 +31,6 @@ frappe.ui.form.on("Communication", { } } - if(frm.doc.communication_type == "Feedback") { - frm.add_custom_button(__("Resend"), function() { - var feedback = new frappe.utils.Feedback(); - feedback.resend_feedback_request(frm.doc); - }); - } - if(frm.doc.status==="Open") { frm.add_custom_button(__("Close"), function() { frm.set_value("status", "Closed"); @@ -54,7 +47,7 @@ frappe.ui.form.on("Communication", { frm.trigger('show_relink_dialog'); }); - if(frm.doc.communication_type=="Communication" + if(frm.doc.communication_type=="Communication" && frm.doc.communication_medium == "Email" && frm.doc.sent_or_received == "Received") { @@ -90,7 +83,7 @@ frappe.ui.form.on("Communication", { } } - if(frm.doc.communication_type=="Communication" + if(frm.doc.communication_type=="Communication" && frm.doc.communication_medium == "Phone" && frm.doc.sent_or_received == "Received"){ @@ -185,7 +178,7 @@ frappe.ui.form.on("Communication", { forward_mail: function(frm) { var args = frm.events.get_mail_args(frm) - $.extend(args, { + $.extend(args, { forward: true, subject: __("Fw: {0}", [frm.doc.subject]), }) diff --git a/frappe/core/doctype/communication/communication.json b/frappe/core/doctype/communication/communication.json index ea0e429e0f..34d8d29bdb 100644 --- a/frappe/core/doctype/communication/communication.json +++ b/frappe/core/doctype/communication/communication.json @@ -1,1728 +1,438 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2013-01-29 10:47:14", - "custom": 0, - "description": "Keep a track of all communications", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "engine": "InnoDB", + "allow_import": 1, + "creation": "2013-01-29 10:47:14", + "description": "Keeps track of all communications", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "subject", + "section_break_10", + "communication_medium", + "sender", + "column_break_4", + "recipients", + "cc", + "bcc", + "phone_no", + "delivery_status", + "section_break_8", + "content", + "status_section", + "text_content", + "communication_type", + "comment_type", + "column_break_5", + "status", + "sent_or_received", + "additional_info", + "communication_date", + "read_receipt", + "column_break_14", + "sender_full_name", + "read_by_recipient", + "read_by_recipient_on", + "reference_section", + "reference_doctype", + "reference_name", + "reference_owner", + "email_account", + "in_reply_to", + "user", + "column_break_27", + "email_template", + "unread_notification_sent", + "seen", + "_user_tags", + "timeline_links_sections", + "timeline_links", + "email_inbox", + "message_id", + "uid", + "email_status", + "has_attachment", + "feedback_section", + "rating", + "feedback_request" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subject", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subject", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "subject", + "fieldtype": "Small Text", + "in_global_search": 1, + "label": "Subject", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "", - "columns": 0, - "fieldname": "section_break_10", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "To and CC", - "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, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "section_break_10", + "fieldtype": "Section Break", + "label": "To and CC" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "eval:doc.communication_type===\"Communication\"", - "fieldname": "communication_medium", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Type", - "length": 0, - "no_copy": 0, - "options": "\nEmail\nChat\nPhone\nSMS\nEvent\nMeeting\nVisit\nOther", - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_type===\"Communication\"", + "fieldname": "communication_medium", + "fieldtype": "Select", + "label": "Type", + "options": "\nEmail\nChat\nPhone\nSMS\nEvent\nMeeting\nVisit\nOther" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_medium===\"Email\"", - "fieldname": "sender", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "From", - "length": 255, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "sender", + "fieldtype": "Data", + "in_global_search": 1, + "label": "From", + "length": 255, + "options": "Email" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "recipients", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "To", - "length": 0, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "recipients", + "fieldtype": "Code", + "label": "To", + "options": "Email" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_medium===\"Email\"", - "fieldname": "cc", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "CC", - "length": 0, - "no_copy": 0, - "options": "Email", - "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, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "cc", + "fieldtype": "Code", + "label": "CC", + "options": "Email" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_medium===\"Email\"", - "fieldname": "bcc", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "BCC", - "length": 0, - "no_copy": 0, - "options": "Email", - "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, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "bcc", + "fieldtype": "Code", + "label": "BCC", + "options": "Email" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:in_list([\"Phone\",\"SMS\"],doc.communication_medium)", - "fieldname": "phone_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Phone No.", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:in_list([\"Phone\",\"SMS\"],doc.communication_medium)", + "fieldname": "phone_no", + "fieldtype": "Data", + "label": "Phone No." + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Integrations can use this field to set email delivery status", - "fieldname": "delivery_status", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Delivery Status", - "length": 0, - "no_copy": 0, - "options": "\nSent\nBounced\nOpened\nMarked As Spam\nRejected\nDelayed\nSoft-Bounced\nClicked\nRecipient Unsubscribed\nError\nExpired\nSending\nRead", - "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, - "translatable": 0, - "unique": 0 - }, + "description": "Integrations can use this field to set email delivery status", + "fieldname": "delivery_status", + "fieldtype": "Select", + "hidden": 1, + "label": "Delivery Status", + "options": "\nSent\nBounced\nOpened\nMarked As Spam\nRejected\nDelayed\nSoft-Bounced\nClicked\nRecipient Unsubscribed\nError\nExpired\nSending\nRead" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "content", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0, + "fieldname": "content", + "fieldtype": "Text Editor", + "label": "Message", "width": "400" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "status_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "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, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "text_content", - "fieldtype": "Code", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Text Content", - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "text_content", + "fieldtype": "Code", + "hidden": 1, + "label": "Text Content" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Communication", - "fieldname": "communication_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Communication Type", - "length": 0, - "no_copy": 0, - "options": "Communication\nComment\nChat\nBot\nNotification\nFeedback", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Communication", + "fieldname": "communication_type", + "fieldtype": "Select", + "label": "Communication Type", + "options": "Communication\nComment\nChat\nBot\nNotification\nFeedback", + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "comment_type", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Comment Type", - "length": 0, - "no_copy": 0, - "options": "\nComment\nLike\nInfo\nLabel\nWorkflow\nCreated\nSubmitted\nCancelled\nUpdated\nDeleted\nAssigned\nAssignment Completed\nAttachment\nAttachment Removed\nShared\nUnshared\nBot\nRelinked", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "comment_type", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Comment Type", + "options": "\nComment\nLike\nInfo\nLabel\nWorkflow\nCreated\nSubmitted\nCancelled\nUpdated\nDeleted\nAssigned\nAssignment Completed\nAttachment\nAttachment Removed\nShared\nUnshared\nBot\nRelinked", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_type===\"Communication\"", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Open\nReplied\nClosed\nLinked", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_type===\"Communication\"", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "options": "Open\nReplied\nClosed\nLinked", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_type===\"Communication\"", - "fieldname": "sent_or_received", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Sent or Received", - "length": 0, - "no_copy": 0, - "options": "Sent\nReceived", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_type===\"Communication\"", + "fieldname": "sent_or_received", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Sent or Received", + "options": "Sent\nReceived", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "additional_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "additional_info", + "fieldtype": "Section Break", + "label": "More Information" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Now", - "fieldname": "communication_date", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, + "default": "Now", + "fieldname": "communication_date", + "fieldtype": "Datetime", + "label": "Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "read_receipt", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sent Read Receipt", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "read_receipt", + "fieldtype": "Check", + "label": "Sent Read Receipt", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sender_full_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "From Full Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sender_full_name", + "fieldtype": "Data", + "label": "From Full Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "read_by_recipient", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Read by Recipient", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "read_by_recipient", + "fieldtype": "Check", + "label": "Read by Recipient", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "read_by_recipient_on", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Read by Recipient On", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "read_by_recipient_on", + "fieldtype": "Datetime", + "label": "Read by Recipient On", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "reference_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference", - "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, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "reference_section", + "fieldtype": "Section Break", + "label": "Reference" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_doctype", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference DocType", - "length": 0, - "no_copy": 0, - "options": "DocType", - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_doctype", + "fieldtype": "Link", + "label": "Reference DocType", + "options": "DocType" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Name", - "length": 0, - "no_copy": 0, - "options": "reference_doctype", - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "label": "Reference Name", + "options": "reference_doctype" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "reference_name.owner", - "fieldname": "reference_owner", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Owner", - "length": 0, - "no_copy": 0, - "options": "", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "reference_name.owner", + "fieldname": "reference_owner", + "fieldtype": "Read Only", + "label": "Reference Owner", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.communication_medium===\"Email\"", - "fieldname": "email_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Email Account", - "length": 0, - "no_copy": 0, - "options": "Email Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.communication_medium===\"Email\"", + "fieldname": "email_account", + "fieldtype": "Link", + "label": "Email Account", + "options": "Email Account", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "in_reply_to", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Reply To", - "length": 0, - "no_copy": 0, - "options": "Communication", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "in_reply_to", + "fieldtype": "Link", + "label": "In Reply To", + "options": "Communication", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "__user", - "fieldname": "user", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "__user", + "fieldname": "user", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "User", + "options": "User", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_27", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_27", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "link_doctype", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Link DocType", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "unread_notification_sent", + "fieldtype": "Check", + "label": "Unread Notification Sent", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "link_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Link Name", - "length": 0, - "no_copy": 0, - "options": "link_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "seen", + "fieldtype": "Check", + "label": "Seen", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "timeline_doctype", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Timeline DocType", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "_user_tags", + "fieldtype": "Data", + "hidden": 1, + "label": "User Tags", + "no_copy": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "timeline_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Timeline Name", - "length": 0, - "no_copy": 0, - "options": "timeline_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "email_inbox", + "fieldtype": "Section Break", + "label": "Email Inbox", + "permlevel": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "timeline_label", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Timeline field Name", - "length": 0, - "no_copy": 0, - "options": "", - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "message_id", + "fieldtype": "Data", + "ignore_xss_filter": 1, + "label": "Message ID", + "length": 995, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "unread_notification_sent", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Unread Notification Sent", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "uid", + "fieldtype": "Int", + "hidden": 1, + "label": "UID", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "seen", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Seen", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "email_status", + "fieldtype": "Select", + "label": "Email Status", + "options": "Open\nSpam\nTrash" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "_user_tags", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User Tags", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "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, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "has_attachment", + "fieldtype": "Check", + "hidden": 1, + "label": "Has Attachment" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "email_inbox", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Email Inbox", - "length": 0, - "no_copy": 0, - "permlevel": 1, - "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, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "depends_on": "eval: doc.rating > 0", + "fieldname": "feedback_section", + "fieldtype": "Section Break", + "label": "Feedback" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "message_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Message ID", - "length": 995, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "rating", + "fieldtype": "Int", + "label": "Rating", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "uid", - "fieldtype": "Int", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "UID", - "length": 0, - "no_copy": 1, - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "feedback_request", + "fieldtype": "Data", + "label": "Feedback Request", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "email_status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Email Status", - "length": 0, - "no_copy": 0, - "options": "Open\nSpam\nTrash", - "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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "email_template", + "fieldtype": "Link", + "label": "Email Template", + "options": "Email Template", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "has_attachment", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Has Attachment", - "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, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "timeline_links_sections", + "fieldtype": "Section Break", + "label": "Timeline Links" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "depends_on": "eval: doc.rating > 0", - "fieldname": "feedback_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Feedback", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rating", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rating", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "feedback_request", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Feedback Request", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "timeline_links", + "fieldtype": "Table", + "label": "Timeline Links", + "options": "Communication Link", + "permlevel": 2 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-comment", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-09-24 13:53:52.389244", - "modified_by": "chdecultot@dokos.io", - "module": "Core", - "name": "Communication", - "owner": "Administrator", + ], + "icon": "fa fa-comment", + "idx": 1, + "modified": "2019-05-21 09:48:24.892143", + "modified_by": "Administrator", + "module": "Core", + "name": "Communication", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - }, + "create": 1, + "delete": 1, + "email": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 - }, + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1 + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 1, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "All", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 2, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "if_owner": 1, + "read": 1, + "role": "All" } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "subject", - "show_name_in_global_search": 0, - "sort_order": "DESC", - "title_field": "subject", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 + ], + "search_fields": "subject", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "subject", + "track_changes": 1, + "track_seen": 1 } \ No newline at end of file diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 77ccefba71..7ed4aea4b5 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -8,11 +8,11 @@ from frappe.model.document import Document from frappe.utils import validate_email_address, get_fullname, strip_html, cstr from frappe.core.doctype.communication.email import (validate_email, notify, _notify, update_parent_mins_to_first_response) -from frappe.core.utils import get_parent_doc, set_timeline_doc +from frappe.core.utils import get_parent_doc from frappe.utils.bot import BotReply from frappe.utils import parse_addr from frappe.core.doctype.comment.comment import update_comment_in_doc - +from email.utils import parseaddr from collections import Counter exclude_from_linked_with = True @@ -58,7 +58,10 @@ class Communication(Document): self.set_sender_full_name() validate_email(self) - set_timeline_doc(self) + + if self.communication_medium == "Email": + self.set_timeline_links() + self.deduplicate_timeline_links() def validate_reference(self): if self.reference_doctype and self.reference_name: @@ -79,6 +82,7 @@ class Communication(Document): circular_linking = True break doc = get_parent_doc(doc) + if circular_linking: frappe.throw(_("Please make sure the Reference Communication Docs are not circularly linked."), frappe.CircularLinkingError) @@ -231,26 +235,67 @@ class Communication(Document): if commit: frappe.db.commit() + # Timeline Links + def set_timeline_links(self): + contacts = get_contacts([self.sender, self.recipients, self.cc, self.bcc]) + for contact_name in contacts: + self.add_link('Contact', contact_name) + + #link contact's dynamic links to communication + add_contact_links_to_communication(self, contact_name) + + def deduplicate_timeline_links(self): + if self.timeline_links: + links, duplicate = [], False + + for l in self.timeline_links: + t = (l.link_doctype, l.link_name) + if not t in links: + links.append(t) + else: + duplicate = True + + if duplicate: + del self.timeline_links[:] # make it python 2 compatible as list.clear() is python 3 only + for l in links: + self.add_link(link_doctype=l[0], link_name=l[1]) + + def add_link(self, link_doctype, link_name, autosave=False): + self.append("timeline_links", + { + "link_doctype": link_doctype, + "link_name": link_name + } + ) + + if autosave: + self.save(ignore_permissions=True) + + def get_links(self): + return self.timeline_links + + def remove_link(self, link_doctype, link_name, autosave=False, ignore_permissions=True): + for l in self.timeline_links: + if l.link_doctype == link_doctype and l.link_name == link_name: + self.timeline_links.remove(l) + + if autosave: + self.save(ignore_permissions=ignore_permissions) def on_doctype_update(): """Add indexes in `tabCommunication`""" frappe.db.add_index("Communication", ["reference_doctype", "reference_name"]) - frappe.db.add_index("Communication", ["timeline_doctype", "timeline_name"]) - frappe.db.add_index("Communication", ["link_doctype", "link_name"]) frappe.db.add_index("Communication", ["status", "communication_type"]) + frappe.db.add_index("Communication Link", ["link_doctype", "link_name"]) def has_permission(doc, ptype, user): if ptype=="read": - if (doc.reference_doctype == "Communication" and doc.reference_name == doc.name) \ - or (doc.timeline_doctype == "Communication" and doc.timeline_name == doc.name): - return + if doc.reference_doctype == "Communication" and doc.reference_name == doc.name: + return if doc.reference_doctype and doc.reference_name: if frappe.has_permission(doc.reference_doctype, ptype="read", doc=doc.reference_name): return True - if doc.timeline_doctype and doc.timeline_name: - if frappe.has_permission(doc.timeline_doctype, ptype="read", doc=doc.timeline_name): - return True def get_permission_query_conditions_for_communication(user): if not user: user = frappe.session.user @@ -265,8 +310,44 @@ def get_permission_query_conditions_for_communication(user): distinct=True, order_by="idx") if not accounts: - return """tabCommunication.communication_medium!='Email'""" + return """`tabCommunication`.communication_medium!='Email'""" email_accounts = [ '"%s"'%account.get("email_account") for account in accounts ] - return """tabCommunication.email_account in ({email_accounts})"""\ + return """`tabCommunication`.email_account in ({email_accounts})"""\ .format(email_accounts=','.join(email_accounts)) + +def get_contacts(email_strings): + email_addrs = [] + + for email_string in email_strings: + if email_string: + for email in email_string.split(","): + parsed_email = parseaddr(email)[1] + if parsed_email: + email_addrs.append(parsed_email) + + contacts = [] + for email in email_addrs: + contact_name = frappe.db.get_value('Contact', {'email_id': email}) + + if not contact_name: + contact = frappe.get_doc({ + "doctype": "Contact", + "first_name": frappe.unscrub(email.split("@")[0]), + "email_id": email + }).insert(ignore_permissions=True) + contact_name = contact.name + + contacts.append(contact_name) + + return contacts + +def add_contact_links_to_communication(communication, contact_name): + contact_links = frappe.get_list("Dynamic Link", filters={ + "parenttype": "Contact", + "parent": contact_name + }, fields=["link_doctype", "link_name"]) + + if contact_links: + for contact_link in contact_links: + communication.add_link(contact_link.link_doctype, contact_link.link_name) \ No newline at end of file diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 0048668ee2..36377a90f7 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -22,7 +22,7 @@ from frappe.utils.background_jobs import enqueue def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent", sender=None, sender_full_name=None, recipients=None, communication_medium="Email", send_email=False, print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, bcc=None, - flags=None, read_receipt=None, print_letterhead=True): + flags=None, read_receipt=None, print_letterhead=True, email_template=None): """Make a new communication. :param doctype: Reference DocType. @@ -38,6 +38,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = :param print_format: Print Format name of parent document to be sent as attachment. :param attachments: List of attachments as list of files or JSON string. :param send_me_a_copy: Send a copy to the sender (default **False**). + :param email_template: Template which is used to compose mail . """ is_error_report = (doctype=="User" and name==frappe.session.user and subject=="Error Report") @@ -66,15 +67,13 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "sent_or_received": sent_or_received, "reference_doctype": doctype, "reference_name": name, + "email_template": email_template, "message_id":get_message_id().strip(" <>"), "read_receipt":read_receipt, "has_attachment": 1 if attachments else 0 - }) - comm.insert(ignore_permissions=True) + }).insert(ignore_permissions=True) - if not doctype: - # if no reference given, then send it against the communication - comm.db_set(dict(reference_doctype='Communication', reference_name=comm.name)) + comm.save(ignore_permissions=True) if isinstance(attachments, string_types): attachments = json.loads(attachments) @@ -555,5 +554,4 @@ def mark_email_as_seen(name=None): frappe.response["type"] = 'binary' frappe.response["filename"] = "imaginary_pixel.png" - frappe.response["filecontent"] = buffered_obj.getvalue() - + frappe.response["filecontent"] = buffered_obj.getvalue() \ No newline at end of file diff --git a/frappe/core/doctype/communication/test_communication.py b/frappe/core/doctype/communication/test_communication.py index 1941ff31cc..21756280a9 100644 --- a/frappe/core/doctype/communication/test_communication.py +++ b/frappe/core/doctype/communication/test_communication.py @@ -44,28 +44,130 @@ class TestCommunication(unittest.TestCase): self.assertFalse(frappe.utils.parse_addr(x)[0]) def test_circular_linking(self): - content = "This was created to test circular linking" a = frappe.get_doc({ "doctype": "Communication", "communication_type": "Communication", - "content": content, - }).insert() + "content": "This was created to test circular linking: Communication A", + }).insert(ignore_permissions=True) + b = frappe.get_doc({ "doctype": "Communication", "communication_type": "Communication", - "content": content, + "content": "This was created to test circular linking: Communication B", "reference_doctype": "Communication", "reference_name": a.name - }).insert() + }).insert(ignore_permissions=True) + c = frappe.get_doc({ "doctype": "Communication", "communication_type": "Communication", - "content": content, + "content": "This was created to test circular linking: Communication C", "reference_doctype": "Communication", "reference_name": b.name - }).insert() + }).insert(ignore_permissions=True) + a = frappe.get_doc("Communication", a.name) a.reference_doctype = "Communication" a.reference_name = c.name + self.assertRaises(frappe.CircularLinkingError, a.save) + def test_deduplication_timeline_links(self): + frappe.delete_doc_if_exists("Note", "deduplication timeline links") + + note = frappe.get_doc({ + "doctype": "Note", + "title": "deduplication timeline links", + "content": "deduplication timeline links" + }).insert(ignore_permissions=True) + + comm = frappe.get_doc({ + "doctype": "Communication", + "communication_type": "Communication", + "content": "Deduplication of Links", + "communication_medium": "Email" + }).insert(ignore_permissions=True) + + #adding same link twice + comm.add_link(link_doctype="Note", link_name=note.name, autosave=True) + comm.add_link(link_doctype="Note", link_name=note.name, autosave=True) + + comm = frappe.get_doc("Communication", comm.name) + + self.assertNotEqual(2, len(comm.timeline_links)) + + def test_contacts_attached(self): + contact_sender = frappe.get_doc({ + "doctype": "Contact", + "first_name": frappe.generate_hash(length=10), + "email_id": "comm_sender@example.com" + }).insert(ignore_permissions=True) + + contact_recipient = frappe.get_doc({ + "doctype": "Contact", + "first_name": frappe.generate_hash(length=10), + "email_id": "comm_recipient@example.com" + }).insert(ignore_permissions=True) + + contact_cc = frappe.get_doc({ + "doctype": "Contact", + "first_name": frappe.generate_hash(length=10), + "email_id": "comm_cc@example.com" + }).insert(ignore_permissions=True) + + comm = frappe.get_doc({ + "doctype": "Communication", + "communication_medium": "Email", + "subject": "Contacts Attached Test", + "sender": "comm_sender@example.com", + "recipients": "comm_recipient@example.com", + "cc": "comm_cc@example.com" + }).insert(ignore_permissions=True) + + comm = frappe.get_doc("Communication", comm.name) + + contact_links = [] + for timeline_link in comm.timeline_links: + contact_links.append(timeline_link.link_name) + + self.assertIn(contact_sender.name, contact_links) + self.assertIn(contact_recipient.name, contact_links) + self.assertIn(contact_cc.name, contact_links) + + def test_get_communication_data(self): + from frappe.desk.form.load import get_communication_data + + frappe.delete_doc_if_exists("Note", "get communication data") + + note = frappe.get_doc({ + "doctype": "Note", + "title": "get communication data", + "content": "get communication data" + }).insert(ignore_permissions=True) + + comm_note_1 = frappe.get_doc({ + "doctype": "Communication", + "communication_type": "Communication", + "content": "Test Get Communication Data 1", + "communication_medium": "Email" + }).insert(ignore_permissions=True) + + comm_note_1.add_link(link_doctype="Note", link_name=note.name, autosave=True) + + comm_note_2 = frappe.get_doc({ + "doctype": "Communication", + "communication_type": "Communication", + "content": "Test Get Communication Data 2", + "communication_medium": "Email" + }).insert(ignore_permissions=True) + + comm_note_2.add_link(link_doctype="Note", link_name=note.name, autosave=True) + + comms = get_communication_data("Note", note.name, as_dict=True) + + data = [] + for comm in comms: + data.append(comm.name) + + self.assertIn(comm_note_1.name, data) + self.assertIn(comm_note_2.name, data) \ No newline at end of file diff --git a/frappe/core/doctype/feedback_request/__init__.py b/frappe/core/doctype/communication_link/__init__.py similarity index 100% rename from frappe/core/doctype/feedback_request/__init__.py rename to frappe/core/doctype/communication_link/__init__.py diff --git a/frappe/core/doctype/communication_link/communication_link.json b/frappe/core/doctype/communication_link/communication_link.json new file mode 100644 index 0000000000..1dd051bed2 --- /dev/null +++ b/frappe/core/doctype/communication_link/communication_link.json @@ -0,0 +1,47 @@ +{ + "creation": "2019-05-21 09:47:23.043960", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "link_doctype", + "link_name", + "link_title" + ], + "fields": [ + { + "fieldname": "link_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Link DocType", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "link_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Link Name", + "options": "link_doctype", + "reqd": 1 + }, + { + "fieldname": "link_title", + "fieldtype": "Read Only", + "in_list_view": 1, + "label": "Link Title", + "read_only": 1 + } + ], + "istable": 1, + "modified": "2019-05-21 09:47:23.043960", + "modified_by": "Administrator", + "module": "Core", + "name": "Communication Link", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/core/doctype/communication_link/communication_link.py b/frappe/core/doctype/communication_link/communication_link.py new file mode 100644 index 0000000000..0328f2b86a --- /dev/null +++ b/frappe/core/doctype/communication_link/communication_link.py @@ -0,0 +1,10 @@ +# -*- 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 CommunicationLink(Document): + pass diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index b295a9be66..c78495898e 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -30,6 +30,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "", "length": 0, @@ -62,6 +63,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, + "in_preview": 0, "in_standard_filter": 0, "label": "Label", "length": 0, @@ -99,13 +101,14 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, + "in_preview": 0, "in_standard_filter": 0, "label": "Type", "length": 0, "no_copy": 0, "oldfieldname": "fieldtype", "oldfieldtype": "Select", - "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature", + "options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -134,6 +137,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, + "in_preview": 0, "in_standard_filter": 0, "label": "Name", "length": 0, @@ -168,6 +172,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, + "in_preview": 0, "in_standard_filter": 0, "label": "Mandatory", "length": 0, @@ -206,6 +211,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Precision", "length": 0, @@ -240,6 +246,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Length", "length": 0, @@ -273,6 +280,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Index", "length": 0, @@ -309,6 +317,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "In List View", "length": 0, @@ -343,6 +352,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "In Standard Filter", "length": 0, @@ -377,6 +387,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "In Global Search", "length": 0, @@ -394,6 +405,40 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "in_preview", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "label": "In Preview", + "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, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -410,6 +455,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Allow in Quick Entry", "length": 0, @@ -443,6 +489,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Bold", "length": 0, @@ -478,6 +525,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Translatable", "length": 0, @@ -512,6 +560,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Collapsible", "length": 255, @@ -546,6 +595,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Collapsible Depends On", "length": 0, @@ -580,6 +630,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -612,6 +663,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, + "in_preview": 0, "in_standard_filter": 0, "label": "Options", "length": 0, @@ -646,6 +698,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Default", "length": 0, @@ -680,6 +733,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Fetch From", "length": 0, @@ -714,6 +768,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Fetch If Empty", "length": 0, @@ -747,6 +802,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Permissions", "length": 0, @@ -779,6 +835,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Display Depends On", "length": 255, @@ -814,6 +871,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Hidden", "length": 0, @@ -850,6 +908,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Read Only", "length": 0, @@ -884,6 +943,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Unique", "length": 0, @@ -918,6 +978,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Set Only Once", "length": 0, @@ -951,6 +1012,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Allow Bulk Edit", "length": 0, @@ -984,6 +1046,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1016,6 +1079,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Perm Level", "length": 0, @@ -1053,6 +1117,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Ignore User Permissions", "length": 0, @@ -1086,6 +1151,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Allow on Submit", "length": 0, @@ -1122,6 +1188,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Report Hide", "length": 0, @@ -1159,6 +1226,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Remember Last Selected Value", "length": 0, @@ -1193,6 +1261,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Ignore XSS Filter", "length": 0, @@ -1226,6 +1295,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Display", "length": 0, @@ -1258,6 +1328,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "In Filter", "length": 0, @@ -1294,6 +1365,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "No Copy", "length": 0, @@ -1330,6 +1402,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Print Hide", "length": 0, @@ -1367,6 +1440,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Print Hide If No Value", "length": 0, @@ -1400,6 +1474,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Print Width", "length": 0, @@ -1432,6 +1507,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Width", "length": 0, @@ -1470,6 +1546,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "label": "Columns", "length": 0, @@ -1503,6 +1580,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1534,6 +1612,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 1, + "in_preview": 0, "in_standard_filter": 0, "label": "Description", "length": 0, @@ -1570,6 +1649,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1603,6 +1683,7 @@ "in_filter": 0, "in_global_search": 0, "in_list_view": 0, + "in_preview": 0, "in_standard_filter": 0, "length": 0, "no_copy": 0, @@ -1622,16 +1703,14 @@ } ], "has_web_view": 0, - "hide_heading": 0, "hide_toolbar": 0, "idx": 1, - "image_view": 0, "in_create": 0, "is_submittable": 0, "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2019-03-18 17:59:57.873790", + "modified": "2019-04-08 12:19:53.415372", "modified_by": "Administrator", "module": "Core", "name": "DocField", @@ -1639,7 +1718,6 @@ "permissions": [], "quick_entry": 0, "read_only": 0, - "read_only_onload": 0, "show_name_in_global_search": 0, "sort_order": "ASC", "track_changes": 0, diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 599427f740..24a7a4c287 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -1,1889 +1,457 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "autoname": "Prompt", - "beta": 0, - "creation": "2013-02-18 13:36:19", - "custom": 0, - "description": "DocType is a Table / Form in the application.", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "sb0", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "module", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Module", - "length": 0, - "no_copy": 0, - "oldfieldname": "module", - "oldfieldtype": "Link", - "options": "Module Def", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.istable", - "description": "Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended.", - "fetch_if_empty": 0, - "fieldname": "is_submittable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Submittable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Child Tables are shown as a Grid in other DocTypes", - "fetch_if_empty": 0, - "fieldname": "istable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Is Child Table", - "length": 0, - "no_copy": 0, - "oldfieldname": "istable", - "oldfieldtype": "Check", - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.istable", - "description": "Single Types have only one record no tables associated. Values are stored in tabSingles", - "fetch_if_empty": 0, - "fieldname": "issingle", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Is Single", - "length": 0, - "no_copy": 0, - "oldfieldname": "issingle", - "oldfieldtype": "Check", - "permlevel": 0, - "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": 1, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "depends_on": "istable", - "fetch_if_empty": 0, - "fieldname": "editable_grid", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Editable Grid", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "depends_on": "eval:!doc.istable && !doc.issingle", - "description": "Open a dialog with mandatory fields to create a new record quickly", - "fetch_if_empty": 0, - "fieldname": "quick_entry", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Quick Entry", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "cb01", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "depends_on": "eval:!doc.istable", - "description": "If enabled, changes to the document are tracked and shown in timeline", - "fetch_if_empty": 0, - "fieldname": "track_changes", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Track Changes", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.istable", - "description": "If enabled, the document is marked as seen, the first time a user opens it", - "fetch_if_empty": 0, - "fieldname": "track_seen", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Track Seen", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "eval:!doc.istable", - "description": "If enabled, document views are tracked, this can happen multiple times", - "fetch_if_empty": 0, - "fieldname": "track_views", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Track Views", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "custom", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Custom?", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "beta", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Beta", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "fields_section_break", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Fields", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "fields", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Fields", - "length": 0, - "no_copy": 0, - "oldfieldname": "fields", - "oldfieldtype": "Table", - "options": "DocField", - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "sb1", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Naming", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Naming Options:\n
{{ doc.name }} Delivered",
- "fieldname": "subject",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Subject",
- "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": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_5",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "1",
- "description": "Send Feedback Request only if there is at least one communication is available for the document.",
- "fieldname": "check_communication",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Check Communication",
- "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_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Optional: The alert will be sent if this expression is true",
- "fieldname": "condition",
- "fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Condition",
- "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_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_7",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "html_8",
- "fieldtype": "HTML",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "options": "Condition Examples:
\ndoc.status==\"Closed\"\ndoc.due_date==nowdate()\ndoc.total > 40000\n", - "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_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Message", - "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_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "message", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Message", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "example", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Example", - "length": 0, - "no_copy": 0, - "options": "
<h3>Issue Resolved</h3>\n\n<p>Issue {{ doc.name }} Is resolved. Please check and confirm the same.</p>\n\n<p> Your Feedback is important for us. Please give us your Feedback for {{ doc.name }}</p>\n\n<h4>Details</h4>",
- "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
- }
- ],
- "has_web_view": 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": "2017-05-29 16:36:04.178592",
- "modified_by": "Administrator",
- "module": "Core",
- "name": "Feedback Trigger",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "document_type",
- "track_changes": 1,
- "track_seen": 0
-}
diff --git a/frappe/core/doctype/feedback_trigger/feedback_trigger.py b/frappe/core/doctype/feedback_trigger/feedback_trigger.py
deleted file mode 100644
index e2265913ad..0000000000
--- a/frappe/core/doctype/feedback_trigger/feedback_trigger.py
+++ /dev/null
@@ -1,215 +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 json
-import frappe
-from frappe import _
-from frappe.utils import get_url
-from frappe.model.document import Document
-from frappe.utils.jinja import validate_template
-
-class FeedbackTrigger(Document):
- def validate(self):
- frappe.cache().delete_value('feedback_triggers')
- validate_template(self.subject)
- validate_template(self.message)
- self.validate_condition()
-
- def on_trash(self):
- frappe.cache().delete_value('feedback_triggers')
-
- def validate_condition(self):
- temp_doc = frappe.new_doc(self.document_type)
- if self.condition:
- try:
- frappe.safe_eval(self.condition, None, get_context(temp_doc))
- except:
- frappe.throw(_("The condition '{0}' is invalid").format(self.condition))
-
-def trigger_feedback_request(doc, method):
- """Trigger the feedback alert, or delete feedback requests on delete"""
-
- def _get():
- triggers = {}
- if not (frappe.flags.in_migrate or frappe.flags.in_install):
- for d in frappe.get_all('Feedback Trigger', dict(enabled=1), ['name', 'document_type']):
- triggers[d.document_type] = d.name
-
- return triggers
-
- feedback_triggers = frappe.cache().get_value('feedback_triggers', _get)
- if doc.doctype in feedback_triggers:
- if doc.flags.in_delete:
- frappe.enqueue('frappe.core.doctype.feedback_trigger.feedback_trigger.delete_feedback_request_and_feedback',
- reference_doctype=doc.doctype, reference_name=doc.name, now=frappe.flags.in_test)
- else:
- frappe.enqueue('frappe.core.doctype.feedback_trigger.feedback_trigger.send_feedback_request',
- trigger=feedback_triggers[doc.doctype], reference_doctype=doc.doctype,
- reference_name=doc.name, now=frappe.flags.in_test)
-
-@frappe.whitelist()
-def send_feedback_request(reference_doctype, reference_name, trigger="Manual", details=None, is_manual=False):
- """ send feedback alert """
-
- if is_feedback_request_already_sent(reference_doctype, reference_name, is_manual=is_manual):
- frappe.msgprint(_("Feedback Request is already sent to user"))
- return None
-
- details = json.loads(details) if details else \
- get_feedback_request_details(reference_doctype, reference_name, trigger=trigger)
-
- if not details:
- return None
-
- feedback_request, url = get_feedback_request_url(reference_doctype,
- reference_name, details.get("recipients"), trigger)
-
- feedback_msg = frappe.render_template("templates/emails/feedback_request_url.html", { "url": url })
-
- # appending feedback url to message body
- message = "{message}{feedback_msg}".format(
- message=details.get("message"),
- feedback_msg=feedback_msg
- )
- details.update({
- "message": message,
- "header": [details.get('subject'), 'blue']
- })
-
- if details:
- frappe.sendmail(**details)
- frappe.db.set_value("Feedback Request", feedback_request, "is_sent", 1)
-
-
-@frappe.whitelist()
-def get_feedback_request_details(reference_doctype, reference_name, trigger="Manual", request=None):
- if not frappe.db.get_value(reference_doctype, reference_name):
- # reference document is either deleted or renamed
- return
- elif not trigger and not request and not frappe.db.get_value("Feedback Trigger", { "document_type": reference_doctype }):
- return
- elif not trigger and request:
- trigger = frappe.db.get_value("Feedback Request", request, "feedback_trigger")
- else:
- trigger = frappe.db.get_value("Feedback Trigger", { "document_type": reference_doctype })
-
- if not trigger:
- return
-
- feedback_trigger = frappe.get_doc("Feedback Trigger", trigger)
-
- doc = frappe.get_doc(reference_doctype, reference_name)
- context = get_context(doc)
-
- recipients = doc.get(feedback_trigger.email_fieldname, None)
- if feedback_trigger.check_communication:
- communications = frappe.get_all("Communication", filters={
- "reference_doctype": reference_doctype,
- "reference_name": reference_name,
- "communication_type": "Communication",
- "sent_or_received": "Sent"
- }, fields=["name"])
-
- if len(communications) < 1:
- frappe.msgprint(_("At least one reply is mandatory before requesting feedback"))
- return None
-
- if recipients and (not feedback_trigger.condition or \
- frappe.safe_eval(feedback_trigger.condition, None, context)):
- subject = feedback_trigger.subject
- context.update({ "feedback_trigger": feedback_trigger })
-
- if "{" in subject:
- subject = frappe.render_template(feedback_trigger.subject, context)
-
- feedback_request_message = frappe.render_template(feedback_trigger.message, context)
-
- return {
- "subject": subject,
- "recipients": recipients,
- "reference_name":doc.name,
- "reference_doctype":doc.doctype,
- "message": feedback_request_message,
- }
- else:
- frappe.msgprint(_("Feedback conditions do not match"))
- return None
-
-def get_feedback_request_url(reference_doctype, reference_name, recipients, trigger="Manual"):
- """ prepare the feedback request url """
- is_manual = 1 if trigger == "Manual" else 0
- feedback_request = frappe.get_doc({
- "is_manual": is_manual,
- "feedback_trigger": trigger,
- "doctype": "Feedback Request",
- "reference_name": reference_name,
- "reference_doctype": reference_doctype,
- }).insert(ignore_permissions=True)
-
- feedback_url = "{base_url}/feedback?reference_doctype={doctype}&reference_name={docname}&email={email_id}&key={nonce}".format(
- base_url=get_url(),
- doctype=reference_doctype,
- docname=reference_name,
- email_id=recipients,
- nonce=feedback_request.key
- )
-
- return [ feedback_request.name, feedback_url ]
-
-def is_feedback_request_already_sent(reference_doctype, reference_name, is_manual=False):
- """
- check if feedback request mail is already sent but feedback is not submitted
- to avoid sending multiple feedback request mail
- """
- is_request_sent = False
- filters = {
- "is_sent": 1,
- "reference_name": reference_name,
- "is_manual": 1 if is_manual else 0,
- "reference_doctype": reference_doctype
- }
-
- if is_manual:
- filters.update({ "is_feedback_submitted": 0 })
-
- feedback_request = frappe.get_all("Feedback Request", filters=filters, fields=["name"])
-
- if feedback_request: is_request_sent = True
- return is_request_sent
-
-def get_enabled_feedback_trigger():
- """ get mapper of all the enable feedback trigger """
-
- triggers = frappe.get_all("Feedback Trigger", filters={"enabled": 1},
- fields=["document_type", "name"], as_list=True)
-
- triggers = { dt[0]: dt[1] for dt in triggers }
- return triggers
-
-def get_context(doc):
- return { "doc": doc }
-
-def delete_feedback_request_and_feedback(reference_doctype, reference_name):
- """ delete all the feedback request and feedback communication """
- if not all([reference_doctype, reference_name]):
- return
-
- feedback_requests = frappe.get_all("Feedback Request", filters={
- "is_feedback_submitted": 0,
- "reference_doctype": reference_doctype,
- "reference_name": reference_name
- })
-
- communications = frappe.get_all("Communication", {
- "communication_type": "Feedback",
- "reference_doctype": reference_doctype,
- "reference_name": reference_name
- })
-
- for request in feedback_requests:
- frappe.delete_doc("Feedback Request", request.get("name"), ignore_permissions=True)
-
- for communication in communications:
- frappe.delete_doc("Communication", communication.get("name"), ignore_permissions=True)
diff --git a/frappe/core/doctype/feedback_trigger/test_feedback_trigger.py b/frappe/core/doctype/feedback_trigger/test_feedback_trigger.py
deleted file mode 100644
index f44d1f96ae..0000000000
--- a/frappe/core/doctype/feedback_trigger/test_feedback_trigger.py
+++ /dev/null
@@ -1,137 +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('Feedback Trigger')
-def get_feedback_request(todo, feedback_trigger):
- return frappe.db.get_value("Feedback Request", {
- "is_sent": 1,
- "is_feedback_submitted": 0,
- "reference_doctype": "ToDo",
- "reference_name": todo,
- "feedback_trigger": feedback_trigger
- }, ["name", "key"])
-
-class TestFeedbackTrigger(unittest.TestCase):
- def setUp(self):
- new_user = frappe.get_doc(dict(doctype='User', email='test-feedback@example.com',
- first_name='Tester')).insert(ignore_permissions=True)
- new_user.add_roles("System Manager")
-
- def tearDown(self):
- frappe.db.sql("delete from tabContact where email_id='test-feedback@example.com'")
- frappe.delete_doc("User", "test-feedback@example.com")
- frappe.delete_doc("Feedback Trigger", "ToDo")
- frappe.db.sql('delete from `tabEmail Queue`')
- frappe.db.sql('delete from `tabFeedback Request`')
-
- def test_feedback_trigger(self):
- """ Test feedback trigger """
- from frappe.www.feedback import accept
-
- frappe.delete_doc("Feedback Trigger", "ToDo")
- frappe.db.sql('delete from `tabEmail Queue`')
- frappe.db.sql('delete from `tabFeedback Request`')
-
- feedback_trigger = frappe.get_doc({
- "enabled": 1,
- "doctype": "Feedback Trigger",
- "document_type": "ToDo",
- "email_field": "assigned_by",
- "email_fieldname": "assigned_by",
- "subject": "{{ doc.name }} Task Completed",
- "condition": "doc.status == 'Closed'",
- "message": """Task {{ doc.name }} is Completed by {{ doc.owner }}.
- regarding the Task {{ doc.name }}"""
- }).insert(ignore_permissions=True)
-
- # create a todo
- todo = frappe.get_doc({
- "doctype": "ToDo",
- "owner": "test-feedback@example.com",
- "assigned_by": "test-feedback@example.com",
- "description": "Unable To Submit Sales Order #SO-00001"
- }).insert(ignore_permissions=True)
-
- # feedback alert mail should be sent only on 'Closed' status
- email_queue = frappe.db.sql("""select name from `tabEmail Queue` where
- reference_doctype='ToDo' and reference_name='{0}'""".format(todo.name))
- self.assertFalse(email_queue)
-
- # add a communication
- frappe.get_doc({
- "reference_doctype": "ToDo",
- "reference_name": todo.name,
- "communication_type": "Communication",
- "content": "Test Communication",
- "subject": "Test Communication",
- "doctype": "Communication"
- }).insert(ignore_permissions=True)
-
- # check if feedback mail alert is triggered
- todo.reload()
- todo.status = "Closed"
- todo.save(ignore_permissions=True)
-
- email_queue = frappe.db.sql("""select name from `tabEmail Queue` where
- reference_doctype='ToDo' and reference_name='{0}'""".format(todo.name))
- self.assertTrue(email_queue)
-
- # test if feedback is submitted for the todo
- feedback_request, request_key = get_feedback_request(todo.name, feedback_trigger.name)
- self.assertTrue(feedback_request)
-
- # test if mail alerts are triggered multiple times for same document
- todo.save(ignore_permissions=True)
- email_queue = frappe.db.sql("""select name from `tabEmail Queue` where
- reference_doctype='ToDo' and reference_name='{0}'""".format(todo.name))
- self.assertTrue(len(email_queue) == 1)
- frappe.db.sql('delete from `tabEmail Queue`')
-
-
- # Test if feedback is submitted sucessfully
- result = accept(request_key, "test-feedback@example.com", "ToDo", todo.name, "Great Work !!", 4, fullname="Test User")
- self.assertTrue(result)
-
- # test if feedback is saved in Communication
- docname = frappe.db.get_value("Communication", {
- "reference_doctype": "ToDo",
- "reference_name": todo.name,
- "communication_type": "Feedback",
- "feedback_request": feedback_request
- })
-
- communication = frappe.get_doc("Communication", docname)
- self.assertEqual(communication.rating, 4)
- self.assertEqual(communication.content, "Great Work !!")
-
- # test if link expired after feedback submission
- self.assertRaises(Exception, accept, key=request_key, sender="test-feedback@example.com",
- reference_doctype="ToDo", reference_name=todo.name, feedback="Thank You !!", rating=4, fullname="Test User")
-
- # auto feedback request should trigger only once
- todo.reload()
- todo.save(ignore_permissions=True)
- email_queue = frappe.db.sql("""select name from `tabEmail Queue` where
- reference_doctype='ToDo' and reference_name='{0}'""".format(todo.name))
- self.assertFalse(email_queue)
- frappe.delete_doc("ToDo", todo.name)
-
- # test if feedback requests and feedback communications are deleted?
- communications = frappe.get_all("Communication", {
- "reference_doctype": "ToDo",
- "reference_name": todo.name,
- "communication_type": "Feedback"
- })
- self.assertFalse(communications)
-
- feedback_requests = frappe.get_all("Feedback Request", {
- "reference_doctype": "ToDo",
- "reference_name": todo.name,
- "is_feedback_submitted": 0
- })
- self.assertFalse(feedback_requests)
diff --git a/frappe/core/doctype/page/page.js b/frappe/core/doctype/page/page.js
index 86fbbedaf3..d1d9600e59 100644
--- a/frappe/core/doctype/page/page.js
+++ b/frappe/core/doctype/page/page.js
@@ -3,9 +3,14 @@
frappe.ui.form.on('Page', {
refresh: function(frm) {
- if(!frappe.boot.developer_mode && user != 'Administrator') {
+ if (!frappe.boot.developer_mode && frappe.session.user != 'Administrator') {
// make the document read-only
frm.set_read_only();
}
+ if (!frm.is_new() && !frm.doc.istable) {
+ frm.add_custom_button(__('Go to {0} Page', [frm.doc.title || frm.doc.name]), () => {
+ frappe.set_route(frm.doc.name);
+ });
+ }
}
});
diff --git a/frappe/core/doctype/prepared_report/prepared_report.py b/frappe/core/doctype/prepared_report/prepared_report.py
index 012c313dda..29c069515f 100644
--- a/frappe/core/doctype/prepared_report/prepared_report.py
+++ b/frappe/core/doctype/prepared_report/prepared_report.py
@@ -25,7 +25,7 @@ class PreparedReport(Document):
self.status = "Queued"
self.report_start_time = frappe.utils.now()
- def after_insert(self):
+ def enqueue_report(self):
enqueue(
run_background,
prepared_report=self.name, timeout=6000
@@ -54,14 +54,14 @@ def run_background(prepared_report):
instance.status = "Completed"
instance.columns = json.dumps(result["columns"])
instance.report_end_time = frappe.utils.now()
- instance.save()
+ instance.save(ignore_permissions=True)
except Exception:
frappe.log_error(frappe.get_traceback())
instance = frappe.get_doc("Prepared Report", prepared_report)
instance.status = "Error"
instance.error_message = frappe.get_traceback()
- instance.save()
+ instance.save(ignore_permissions=True)
frappe.publish_realtime(
'report_generated',
diff --git a/frappe/core/doctype/report/report.py b/frappe/core/doctype/report/report.py
index 6b40cd7d33..1b6957d057 100644
--- a/frappe/core/doctype/report/report.py
+++ b/frappe/core/doctype/report/report.py
@@ -100,7 +100,7 @@ class Report(Document):
columns = []
out = []
- if self.report_type in ('Query Report', 'Script Report'):
+ if self.report_type in ('Query Report', 'Script Report', 'Custom Report'):
# query and script reports
data = frappe.desk.query_report.run(self.name, filters=filters, user=user)
for d in data.get('columns'):
diff --git a/frappe/core/doctype/system_settings/system_settings.json b/frappe/core/doctype/system_settings/system_settings.json
index cf317edc42..161f0401e0 100644
--- a/frappe/core/doctype/system_settings/system_settings.json
+++ b/frappe/core/doctype/system_settings/system_settings.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@@ -18,6 +19,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "localization",
"fieldtype": "Section Break",
"hidden": 0,
@@ -49,6 +51,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "country",
"fieldtype": "Link",
"hidden": 0,
@@ -82,6 +85,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "language",
"fieldtype": "Link",
"hidden": 0,
@@ -114,6 +118,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
@@ -145,6 +150,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "time_zone",
"fieldtype": "Select",
"hidden": 0,
@@ -176,6 +182,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "is_first_startup",
"fieldtype": "Check",
"hidden": 1,
@@ -208,6 +215,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "setup_complete",
"fieldtype": "Check",
"hidden": 1,
@@ -240,6 +248,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "date_and_number_format",
"fieldtype": "Section Break",
"hidden": 0,
@@ -271,6 +280,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "date_format",
"fieldtype": "Select",
"hidden": 0,
@@ -303,6 +313,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "column_break_7",
"fieldtype": "Column Break",
"hidden": 0,
@@ -334,6 +345,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "number_format",
"fieldtype": "Select",
"hidden": 0,
@@ -366,6 +378,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "float_precision",
"fieldtype": "Select",
"hidden": 0,
@@ -399,6 +412,7 @@
"collapsible": 0,
"columns": 0,
"description": "If not set, the currency precision will depend on number format",
+ "fetch_if_empty": 0,
"fieldname": "currency_precision",
"fieldtype": "Select",
"hidden": 0,
@@ -432,6 +446,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "sec_backup_limit",
"fieldtype": "Section Break",
"hidden": 0,
@@ -466,6 +481,7 @@
"columns": 0,
"default": "3",
"description": "Older backups will be automatically deleted",
+ "fetch_if_empty": 0,
"fieldname": "backup_limit",
"fieldtype": "Int",
"hidden": 0,
@@ -498,6 +514,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "background_workers",
"fieldtype": "Section Break",
"hidden": 0,
@@ -531,6 +548,7 @@
"collapsible": 0,
"columns": 0,
"description": "Run scheduled jobs only if checked",
+ "fetch_if_empty": 0,
"fieldname": "enable_scheduler",
"fieldtype": "Check",
"hidden": 1,
@@ -562,6 +580,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "scheduler_last_event",
"fieldtype": "Data",
"hidden": 1,
@@ -594,6 +613,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "permissions",
"fieldtype": "Section Break",
"hidden": 0,
@@ -628,6 +648,7 @@
"columns": 0,
"default": "0",
"description": "If Apply Strict User Permission is checked and User Permission is defined for a DocType for a User, then all the documents where value of the link is blank, will not be shown to that User",
+ "fetch_if_empty": 0,
"fieldname": "apply_strict_user_permissions",
"fieldtype": "Check",
"hidden": 0,
@@ -660,6 +681,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "security",
"fieldtype": "Section Break",
"hidden": 0,
@@ -693,6 +715,7 @@
"columns": 0,
"default": "06:00",
"description": "Session Expiry in Hours e.g. 06:00",
+ "fetch_if_empty": 0,
"fieldname": "session_expiry",
"fieldtype": "Data",
"hidden": 0,
@@ -727,6 +750,7 @@
"columns": 0,
"default": "720:00",
"description": "In Hours",
+ "fetch_if_empty": 0,
"fieldname": "session_expiry_mobile",
"fieldtype": "Data",
"hidden": 0,
@@ -759,75 +783,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "default": "1",
- "description": "If enabled, the password strength will be enforced based on the Minimum Password Score value. A value of 2 being medium strong and 4 being very strong.",
- "fieldname": "enable_password_policy",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Enable Password Policy",
- "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,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "2",
- "depends_on": "eval:doc.enable_password_policy==1",
- "fieldname": "minimum_password_score",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Minimum Password Score",
- "length": 0,
- "no_copy": 0,
- "options": "2\n3\n4",
- "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,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "column_break_13",
"fieldtype": "Column Break",
"hidden": 0,
@@ -860,6 +816,7 @@
"collapsible": 0,
"columns": 0,
"description": "Note: Multiple sessions will be allowed in case of mobile device",
+ "fetch_if_empty": 0,
"fieldname": "deny_multiple_sessions",
"fieldtype": "Check",
"hidden": 0,
@@ -894,6 +851,7 @@
"columns": 0,
"default": "0",
"description": "User can login using Email id or Mobile number",
+ "fetch_if_empty": 0,
"fieldname": "allow_login_using_mobile_number",
"fieldtype": "Check",
"hidden": 0,
@@ -928,6 +886,7 @@
"columns": 0,
"default": "0",
"description": "User can login using Email id or User Name",
+ "fetch_if_empty": 0,
"fieldname": "allow_login_using_user_name",
"fieldtype": "Check",
"hidden": 0,
@@ -962,6 +921,7 @@
"columns": 0,
"default": "1",
"description": "",
+ "fetch_if_empty": 0,
"fieldname": "allow_error_traceback",
"fieldtype": "Check",
"hidden": 0,
@@ -994,6 +954,177 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "password_settings",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Password",
+ "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,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "In Days",
+ "fetch_if_empty": 0,
+ "fieldname": "force_user_to_reset_password",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Force User to Reset Password",
+ "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,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "column_break_31",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "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,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "1",
+ "description": "If enabled, the password strength will be enforced based on the Minimum Password Score value. A value of 2 being medium strong and 4 being very strong.",
+ "fetch_if_empty": 0,
+ "fieldname": "enable_password_policy",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Enable Password Policy",
+ "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,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "2",
+ "depends_on": "eval:doc.enable_password_policy==1",
+ "fetch_if_empty": 0,
+ "fieldname": "minimum_password_score",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Minimum Password Score",
+ "length": 0,
+ "no_copy": 0,
+ "options": "2\n3\n4",
+ "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,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "brute_force_security",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1026,6 +1157,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "allow_consecutive_login_attempts",
"fieldtype": "Int",
"hidden": 0,
@@ -1058,6 +1190,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "column_break_34",
"fieldtype": "Column Break",
"hidden": 0,
@@ -1091,6 +1224,7 @@
"columns": 0,
"default": "60",
"description": "In seconds",
+ "fetch_if_empty": 0,
"fieldname": "allow_login_after_fail",
"fieldtype": "Int",
"hidden": 0,
@@ -1123,6 +1257,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "two_factor_authentication",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1155,6 +1290,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "enable_two_factor_auth",
"fieldtype": "Check",
"hidden": 0,
@@ -1190,6 +1326,7 @@
"default": "0",
"depends_on": "enable_two_factor_auth",
"description": "If enabled, users who login from Restricted IP Address, won't be prompted for Two Factor Auth",
+ "fetch_if_empty": 0,
"fieldname": "bypass_2fa_for_retricted_ip_users",
"fieldtype": "Check",
"hidden": 0,
@@ -1224,6 +1361,7 @@
"columns": 0,
"depends_on": "enable_two_factor_auth",
"description": "If enabled, all users can login from any IP Address using Two Factor Auth. This can also be set only for specific user(s) in User Page",
+ "fetch_if_empty": 0,
"fieldname": "bypass_restrict_ip_check_if_2fa_enabled",
"fieldtype": "Check",
"hidden": 0,
@@ -1259,6 +1397,7 @@
"default": "OTP App",
"depends_on": "",
"description": "Choose authentication method to be used by all users",
+ "fetch_if_empty": 0,
"fieldname": "two_factor_method",
"fieldtype": "Select",
"hidden": 0,
@@ -1294,6 +1433,7 @@
"columns": 0,
"depends_on": "eval:doc.two_factor_method == \"OTP App\"",
"description": "Time in seconds to retain QR code image on server. Min:240",
+ "fetch_if_empty": 0,
"fieldname": "lifespan_qrcode_image",
"fieldtype": "Int",
"hidden": 0,
@@ -1328,6 +1468,7 @@
"columns": 0,
"default": "Frappe Framework",
"depends_on": "enable_two_factor_auth",
+ "fetch_if_empty": 0,
"fieldname": "otp_issuer_name",
"fieldtype": "Data",
"hidden": 0,
@@ -1361,6 +1502,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "email",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1394,6 +1536,7 @@
"collapsible": 0,
"columns": 0,
"description": "Your organization name and address for the email footer.",
+ "fetch_if_empty": 0,
"fieldname": "email_footer_address",
"fieldtype": "Small Text",
"hidden": 0,
@@ -1426,6 +1569,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "column_break_18",
"fieldtype": "Column Break",
"hidden": 0,
@@ -1457,6 +1601,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "disable_standard_email_footer",
"fieldtype": "Check",
"hidden": 0,
@@ -1489,6 +1634,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "hide_footer_in_auto_email_reports",
"fieldtype": "Check",
"hidden": 0,
@@ -1521,6 +1667,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "chat",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1554,6 +1701,7 @@
"collapsible": 0,
"columns": 0,
"default": "1",
+ "fetch_if_empty": 0,
"fieldname": "enable_chat",
"fieldtype": "Check",
"hidden": 0,
@@ -1587,6 +1735,7 @@
"collapsible": 0,
"columns": 0,
"default": "1",
+ "fetch_if_empty": 0,
"fieldname": "use_socketio_to_upload_file",
"fieldtype": "Check",
"hidden": 0,
@@ -1624,7 +1773,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2019-01-30 11:02:41.011412",
+ "modified": "2019-04-16 13:26:09.247487",
"modified_by": "Administrator",
"module": "Core",
"name": "System Settings",
diff --git a/frappe/core/doctype/system_settings/system_settings.py b/frappe/core/doctype/system_settings/system_settings.py
index 47a9226582..59c8275319 100644
--- a/frappe/core/doctype/system_settings/system_settings.py
+++ b/frappe/core/doctype/system_settings/system_settings.py
@@ -7,7 +7,7 @@ from frappe import _
from frappe.model.document import Document
from frappe.model import no_value_fields
from frappe.translate import set_default_language
-from frappe.utils import cint
+from frappe.utils import cint, today
from frappe.utils.momentjs import get_all_timezones
from frappe.twofactor import toggle_two_factor_auth
@@ -35,6 +35,11 @@ class SystemSettings(Document):
self.bypass_2fa_for_retricted_ip_users = 0
self.bypass_restrict_ip_check_if_2fa_enabled = 0
+ frappe.flags.update_last_reset_password_date = False
+ if (self.force_user_to_reset_password and
+ not cint(frappe.db.get_single_value("System Settings", "force_user_to_reset_password"))):
+ frappe.flags.update_last_reset_password_date = True
+
def on_update(self):
for df in self.meta.get("fields"):
if df.fieldtype not in no_value_fields:
@@ -47,6 +52,16 @@ class SystemSettings(Document):
frappe.cache().delete_value('time_zone')
frappe.local.system_settings = {}
+ if frappe.flags.update_last_reset_password_date:
+ update_last_reset_password_date()
+
+def update_last_reset_password_date():
+ frappe.db.sql(""" UPDATE `tabUser`
+ SET
+ last_password_reset_date = %s
+ WHERE
+ last_password_reset_date is null or last_password_reset_date = ''""", today())
+
@frappe.whitelist()
def load():
if not "System Manager" in frappe.get_roles():
diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json
index e96cdf6f36..318b31d4a2 100644
--- a/frappe/core/doctype/user/user.json
+++ b/frappe/core/doctype/user/user.json
@@ -1144,6 +1144,39 @@
"translatable": 0,
"unique": 0
},
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fetch_if_empty": 0,
+ "fieldname": "last_password_reset_date",
+ "fieldtype": "Date",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Last Password Reset Date",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@@ -2437,7 +2470,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 5,
- "modified": "2019-03-03 11:10:06.162541",
+ "modified": "2019-04-15 20:25:02.022893",
"modified_by": "Administrator",
"module": "Core",
"name": "User",
diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py
index adc054197a..ebd312fc07 100644
--- a/frappe/core/doctype/user/user.py
+++ b/frappe/core/doctype/user/user.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals, print_function
import frappe
from frappe.model.document import Document
-from frappe.utils import cint, has_gravatar, format_datetime, now_datetime, get_formatted_email
+from frappe.utils import cint, has_gravatar, format_datetime, now_datetime, get_formatted_email, today
from frappe import throw, msgprint, _
from frappe.utils.password import update_password as _update_password
from frappe.desk.notifications import clear_notifications
@@ -218,13 +218,17 @@ class User(Document):
def validate_reset_password(self):
pass
- def reset_password(self, send_email=False):
+ def reset_password(self, send_email=False, password_expired=False):
from frappe.utils import random_string, get_url
key = random_string(32)
self.db_set("reset_password_key", key)
- link = get_url("/update-password?key=" + key)
+ url = "/update-password?key=" + key
+ if password_expired:
+ url = "/update-password?key=" + key + '&password_expired=true'
+
+ link = get_url(url)
if send_email:
self.password_reset_mail(link)
@@ -591,6 +595,9 @@ def update_password(new_password, logout_all_sessions=0, key=None, old_password=
frappe.local.login_manager.login_as(user)
+ frappe.db.set_value("User", user,
+ 'last_password_reset_date', today())
+
if user_doc.user_type == "System User":
return "/desk"
else:
diff --git a/frappe/core/doctype/user_permission/user_permission.py b/frappe/core/doctype/user_permission/user_permission.py
index af197dcf24..af2815a9ac 100644
--- a/frappe/core/doctype/user_permission/user_permission.py
+++ b/frappe/core/doctype/user_permission/user_permission.py
@@ -53,7 +53,7 @@ class UserPermission(Document):
}, limit=1)
if overlap_exists:
ref_link = frappe.get_desk_link(self.doctype, overlap_exists[0].name)
- frappe.throw(_("{0} has already assigned default vaue for {1}.".format(ref_link, self.allow)))
+ frappe.throw(_("{0} has already assigned default value for {1}.".format(ref_link, self.allow)))
@frappe.whitelist()
def get_user_permissions(user=None):
@@ -236,4 +236,4 @@ def update_applicable(already_applied, to_apply, user, doctype, docname):
AND `applicable_for`=%s
AND `allow`=%s
AND `for_value`=%s
- """,(user, applied, doctype, docname))
\ No newline at end of file
+ """,(user, applied, doctype, docname))
diff --git a/frappe/core/page/dashboard/dashboard.js b/frappe/core/page/dashboard/dashboard.js
index b4cb4d7222..c8b8fc94dc 100644
--- a/frappe/core/page/dashboard/dashboard.js
+++ b/frappe/core/page/dashboard/dashboard.js
@@ -60,7 +60,12 @@ class Dashboard {
show_dashboard(current_dashboard_name) {
if(this.dashboard_name !== current_dashboard_name) {
this.dashboard_name = current_dashboard_name;
- this.page.set_title(this.dashboard_name);
+ let title = this.dashboard_name;
+ if (!this.dashboard_name.toLowerCase().includes(__('dashboard'))) {
+ // ensure dashboard title has "dashboard"
+ title = __('{0} Dashboard', [title]);
+ }
+ this.page.set_title(title);
this.set_dropdown();
this.container.empty();
this.refresh();
diff --git a/frappe/core/report/feedback_ratings/__init__.py b/frappe/core/report/feedback_ratings/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/core/report/feedback_ratings/feedback_ratings.js b/frappe/core/report/feedback_ratings/feedback_ratings.js
deleted file mode 100644
index 7d42069e49..0000000000
--- a/frappe/core/report/feedback_ratings/feedback_ratings.js
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.query_reports["Feedback Ratings"] = {
- "filters": [
- {
- "fieldname": "document_type",
- "label": __("Document Type"),
- "fieldtype": "Link",
- "options": "DocType",
- "reqd": 1,
- "default": "Issue",
- "get_query": function() {
- return {
- "query": "frappe.core.report.feedback_ratings.feedback_ratings.get_document_type"
- }
- }
- },
- {
- "fieldname": "document_id",
- "label": __("Document ID"),
- "fieldtype": "Dynamic Link",
- "get_options": function() {
- var document_type = frappe.query_report.get_filter_value('document_type');
- if(!document_type) {
- frappe.throw(__("Please select Document Type first"));
- }
- return document_type;
- }
- },
- {
- "fieldname":"from_date",
- "label": __("From Date"),
- "fieldtype": "Date",
- 'reqd': 1,
- "default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
- },
- {
- "fieldname":"to_date",
- "label": __("To Date"),
- "fieldtype": "Date",
- 'reqd': 1,
- "default":frappe.datetime.nowdate()
- }
- ],
-
- get_chart_data: function(columns, result) {
- return {
- data: {
- x: 'Date',
- columns: [
- ['Date'].concat($.map(result, function(d) { return d[0]; })),
- ['Average Feedback'].concat($.map(result, function(d) { return d[1]; }))
- ]
- },
- chart_type: 'line',
-
- }
- }
-}
diff --git a/frappe/core/report/feedback_ratings/feedback_ratings.json b/frappe/core/report/feedback_ratings/feedback_ratings.json
deleted file mode 100644
index 48c10c2e6c..0000000000
--- a/frappe/core/report/feedback_ratings/feedback_ratings.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "add_total_row": 0,
- "apply_user_permissions": 1,
- "creation": "2017-02-05 20:38:21.890174",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 2,
- "is_standard": "Yes",
- "modified": "2017-02-24 19:56:51.141147",
- "modified_by": "Administrator",
- "module": "Core",
- "name": "Feedback Ratings",
- "owner": "Administrator",
- "ref_doctype": "Feedback Trigger",
- "report_name": "Feedback Ratings",
- "report_type": "Script Report",
- "roles": [
- {
- "role": "System Manager"
- }
- ]
-}
\ No newline at end of file
diff --git a/frappe/core/report/feedback_ratings/feedback_ratings.py b/frappe/core/report/feedback_ratings/feedback_ratings.py
deleted file mode 100644
index dff832bfba..0000000000
--- a/frappe/core/report/feedback_ratings/feedback_ratings.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute(filters=None):
- columns, data = get_columns(filters), get_data(filters)
- return columns, data
-
-def get_columns(filters):
- return [
- "Date:Date",
- "Average Rating",
- ]
-
-def get_data(filters):
- data = []
- document_type = filters.get("document_type")
- party = filters.get("document_id")
- filters = {
- "reference_doctype": document_type,
- "communication_type": "Feedback",
- "creation": ["Between", [filters.get("from_date"), filters.get("to_date")]]
- }
- fields = ["DATE_FORMAT(DATE(creation),'%m-%d-%Y')", "avg(rating) as rating"]
-
- if not document_type:
- return []
-
- if party:
- filters.update({ "reference_name": party })
-
- party_details = frappe.get_list("Communication", filters=filters, fields=fields,
- order_by="creation", group_by="DATE_FORMAT(DATE(creation),'%m-%d-%Y')", as_list=True)
-
- return party_details or []
-
-@frappe.whitelist()
-def get_document_type(doctype, txt, searchfield, start, page_len, filters):
- """ get the document type """
-
- document_type = []
- txt = "%%%s%%" % txt
-
- document_type = frappe.get_all("Feedback Trigger", filters={ "enabled": 1, "document_type": ("like", txt) },
- fields=["document_type"], as_list=True)
-
- document_type = map(list, document_type)
- to_ignore = [ doc[0] for doc in document_type ]
-
- documents = frappe.get_all("Feedback Request", filters={ "reference_doctype": ["not in", to_ignore] },
- fields=["reference_doctype"], distinct=True, as_list=True)
-
- if documents:
- document_type.extend(documents)
-
- return document_type
diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json
index 462f1be56c..5f288fe99c 100644
--- a/frappe/custom/doctype/customize_form/customize_form.json
+++ b/frappe/custom/doctype/customize_form/customize_form.json
@@ -1,694 +1,182 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "DL.####",
- "beta": 0,
- "creation": "2013-01-29 17:55:08",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 1,
+ "autoname": "DL.####",
+ "creation": "2013-01-29 17:55:08",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "field_order": [
+ "doc_type",
+ "properties",
+ "label",
+ "default_print_format",
+ "max_attachments",
+ "allow_copy",
+ "istable",
+ "editable_grid",
+ "quick_entry",
+ "track_changes",
+ "track_views",
+ "image_view",
+ "column_break_5",
+ "title_field",
+ "image_field",
+ "search_fields",
+ "section_break_8",
+ "sort_field",
+ "column_break_10",
+ "sort_order",
+ "fields_section_break",
+ "fields"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "doc_type",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Enter Form Type",
- "length": 0,
- "no_copy": 0,
- "options": "DocType",
- "permlevel": 0,
- "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
- },
+ "fieldname": "doc_type",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Enter Form Type",
+ "options": "DocType"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "doc_type",
- "fieldname": "properties",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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
- },
+ "depends_on": "doc_type",
+ "fieldname": "properties",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "label",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Change Label (via Custom Translation)",
- "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
- },
+ "fieldname": "label",
+ "fieldtype": "Data",
+ "label": "Change Label (via Custom Translation)"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "default_print_format",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Default Print Format",
- "length": 0,
- "no_copy": 0,
- "options": "Print Format",
- "permlevel": 0,
- "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
- },
+ "fieldname": "default_print_format",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Default Print Format",
+ "options": "Print Format"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "max_attachments",
- "fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Max Attachments",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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
- },
+ "fieldname": "max_attachments",
+ "fieldtype": "Int",
+ "label": "Max Attachments"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "allow_copy",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Hide Copy",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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
- },
+ "fieldname": "allow_copy",
+ "fieldtype": "Check",
+ "label": "Hide Copy"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "istable",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Is Table",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "istable",
+ "fieldtype": "Check",
+ "label": "Is Table",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "istable",
- "fieldname": "editable_grid",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Editable Grid",
- "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
- },
+ "depends_on": "istable",
+ "fieldname": "editable_grid",
+ "fieldtype": "Check",
+ "label": "Editable Grid"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "1",
- "fieldname": "quick_entry",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Quick Entry",
- "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
- },
+ "default": "1",
+ "fieldname": "quick_entry",
+ "fieldtype": "Check",
+ "label": "Quick Entry"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "track_changes",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Track Changes",
- "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
- },
+ "fieldname": "track_changes",
+ "fieldtype": "Check",
+ "label": "Track Changes"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval: doc.image_field",
- "fieldname": "image_view",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Image View",
- "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
- },
+ "depends_on": "eval: doc.image_field",
+ "fieldname": "image_view",
+ "fieldtype": "Check",
+ "label": "Image View"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_5",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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
- },
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Use this fieldname to generate title",
- "fieldname": "title_field",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Title Field",
- "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
- },
+ "description": "Use this fieldname to generate title",
+ "fieldname": "title_field",
+ "fieldtype": "Data",
+ "label": "Title Field"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Must be of type \"Attach Image\"",
- "fieldname": "image_field",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Image Field",
- "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
- },
+ "description": "Must be of type \"Attach Image\"",
+ "fieldname": "image_field",
+ "fieldtype": "Data",
+ "label": "Image Field"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Fields separated by comma (,) will be included in the \"Search By\" list of Search dialog box",
- "fieldname": "search_fields",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Search Fields",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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
- },
+ "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"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "doc_type",
- "fieldname": "section_break_8",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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
- },
+ "depends_on": "doc_type",
+ "fieldname": "section_break_8",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sort_field",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Sort Field",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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
- },
+ "fieldname": "sort_field",
+ "fieldtype": "Select",
+ "label": "Sort Field"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_10",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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
- },
+ "fieldname": "column_break_10",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sort_order",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Sort Order",
- "length": 0,
- "no_copy": 0,
- "options": "ASC\nDESC",
- "permlevel": 0,
- "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
- },
+ "fieldname": "sort_order",
+ "fieldtype": "Select",
+ "label": "Sort Order",
+ "options": "ASC\nDESC"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "doc_type",
- "description": "Customize Label, Print Hide, Default etc.",
- "fieldname": "fields_section_break",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Fields",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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
- },
+ "depends_on": "doc_type",
+ "description": "Customize Label, Print Hide, Default etc.",
+ "fieldname": "fields_section_break",
+ "fieldtype": "Section Break",
+ "label": "Fields"
+ },
{
- "allow_bulk_edit": 1,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "fields",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Fields",
- "length": 0,
- "no_copy": 0,
- "options": "Customize Form Field",
- "permlevel": 0,
- "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_bulk_edit": 1,
+ "fieldname": "fields",
+ "fieldtype": "Table",
+ "label": "Fields",
+ "options": "Customize Form Field"
+ },
+ {
+ "fieldname": "track_views",
+ "fieldtype": "Check",
+ "label": "Track Views"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 1,
- "icon": "fa fa-glass",
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2017-04-21 16:59:12.752428",
- "modified_by": "Administrator",
- "module": "Custom",
- "name": "Customize Form",
- "owner": "Administrator",
+ ],
+ "hide_toolbar": 1,
+ "icon": "fa fa-glass",
+ "idx": 1,
+ "issingle": 1,
+ "modified": "2019-05-13 18:54:40.610862",
+ "modified_by": "Administrator",
+ "module": "Custom",
+ "name": "Customize Form",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 0,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 0,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "System Manager",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "search_fields": "doc_type",
- "show_name_in_global_search": 0,
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "quick_entry": 1,
+ "search_fields": "doc_type",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py
index 9be66c2583..800ce0d462 100644
--- a/frappe/custom/doctype/customize_form/customize_form.py
+++ b/frappe/custom/doctype/customize_form/customize_form.py
@@ -28,6 +28,7 @@ doctype_properties = {
'editable_grid': 'Check',
'max_attachments': 'Int',
'track_changes': 'Check',
+ 'track_views': 'Check',
}
docfield_properties = {
@@ -87,6 +88,9 @@ class CustomizeForm(Document):
if self.doc_type in core_doctypes_list:
return frappe.msgprint(_("Core DocTypes cannot be customized."))
+ if meta.issingle:
+ return frappe.msgprint(_("Single DocTypes cannot be customized."))
+
if meta.custom:
return frappe.msgprint(_("Only standard DocTypes are allowed to be customized from Customize Form."))
diff --git a/frappe/database/mariadb/framework_mariadb.sql b/frappe/database/mariadb/framework_mariadb.sql
index eae479df05..ad4cd7d5ef 100644
--- a/frappe/database/mariadb/framework_mariadb.sql
+++ b/frappe/database/mariadb/framework_mariadb.sql
@@ -37,6 +37,7 @@ CREATE TABLE `tabDocField` (
`unique` int(1) NOT NULL DEFAULT 0,
`no_copy` int(1) NOT NULL DEFAULT 0,
`allow_on_submit` int(1) NOT NULL DEFAULT 0,
+ `show_preview_popup` int(1) NOT NULL DEFAULT 0,
`trigger` varchar(255) DEFAULT NULL,
`collapsible_depends_on` text,
`depends_on` text,
@@ -49,6 +50,7 @@ CREATE TABLE `tabDocField` (
`description` text,
`in_list_view` int(1) NOT NULL DEFAULT 0,
`in_standard_filter` int(1) NOT NULL DEFAULT 0,
+ `in_preview` int(1) NOT NULL DEFAULT 0,
`read_only` int(1) NOT NULL DEFAULT 0,
`precision` varchar(255) DEFAULT NULL,
`length` int(11) NOT NULL DEFAULT 0,
diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py
index 036ee3f34a..b219aac13a 100644
--- a/frappe/database/postgres/database.py
+++ b/frappe/database/postgres/database.py
@@ -64,10 +64,10 @@ class PostgresDatabase(Database):
def get_connection(self):
# warnings.filterwarnings('ignore', category=psycopg2.Warning)
- conn = psycopg2.connect('host={} dbname={} port={}'.format(self.host, self.user, self.port))
+ conn = psycopg2.connect('host={} dbname={} user={} password={} port={}'.format(
+ self.host, self.user, self.user, self.password, self.port
+ ))
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) # TODO: Remove this
- # conn = psycopg2.connect('host={} dbname={} user={} password={}'.format(self.host,
- # self.user, self.user, self.password))
return conn
@@ -310,4 +310,4 @@ def replace_locate_with_strpos(query):
# strpos is the locate equivalent in postgres
if re.search(r'locate\(', query, flags=re.IGNORECASE):
query = re.sub(r'locate\(([^,]+),([^)]+)\)', r'strpos(\2, \1)', query, flags=re.IGNORECASE)
- return query
\ No newline at end of file
+ return query
diff --git a/frappe/database/postgres/framework_postgres.sql b/frappe/database/postgres/framework_postgres.sql
index eb82f3ed97..756917ca97 100644
--- a/frappe/database/postgres/framework_postgres.sql
+++ b/frappe/database/postgres/framework_postgres.sql
@@ -37,6 +37,7 @@ CREATE TABLE "tabDocField" (
"unique" smallint NOT NULL DEFAULT 0,
"no_copy" smallint NOT NULL DEFAULT 0,
"allow_on_submit" smallint NOT NULL DEFAULT 0,
+ "show_preview_popup" smallint NOT NULL DEFAULT 0,
"trigger" varchar(255) DEFAULT NULL,
"collapsible_depends_on" text,
"depends_on" text,
@@ -49,6 +50,7 @@ CREATE TABLE "tabDocField" (
"description" text,
"in_list_view" smallint NOT NULL DEFAULT 0,
"in_standard_filter" smallint NOT NULL DEFAULT 0,
+ "in_preview" smallint NOT NULL DEFAULT 0,
"read_only" smallint NOT NULL DEFAULT 0,
"precision" varchar(255) DEFAULT NULL,
"length" bigint NOT NULL DEFAULT 0,
diff --git a/frappe/database/postgres/schema.py b/frappe/database/postgres/schema.py
index 05b6b19a9a..b5129b60bb 100644
--- a/frappe/database/postgres/schema.py
+++ b/frappe/database/postgres/schema.py
@@ -40,7 +40,20 @@ class PostgresTable(DBTable):
query.append("ADD COLUMN `{}` {}".format(col.fieldname, col.get_definition()))
for col in self.change_type:
- query.append("ALTER COLUMN `{}` TYPE {}".format(col.fieldname, get_definition(col.fieldtype, precision=col.precision, length=col.length)))
+ using_clause = ""
+ if col.fieldtype in ("Datetime"):
+ # The USING option of SET DATA TYPE can actually specify any expression
+ # involving the old values of the row
+ # read more https://www.postgresql.org/docs/9.1/sql-altertable.html
+ using_clause = "USING {}::timestamp without time zone".format(col.fieldname)
+ elif col.fieldtype in ("Check"):
+ using_clause = "USING {}::smallint".format(col.fieldname)
+
+ query.append("ALTER COLUMN {0} TYPE {1} {2}".format(
+ col.fieldname,
+ get_definition(col.fieldtype, precision=col.precision, length=col.length),
+ using_clause)
+ )
for col in self.set_default:
if col.fieldname=="name":
@@ -93,4 +106,4 @@ class PostgresTable(DBTable):
fieldname, self.table_name)))
raise e
else:
- raise e
\ No newline at end of file
+ raise e
diff --git a/frappe/database/postgres/setup_db.py b/frappe/database/postgres/setup_db.py
index 0209d65839..7ccd86a26e 100644
--- a/frappe/database/postgres/setup_db.py
+++ b/frappe/database/postgres/setup_db.py
@@ -1,4 +1,5 @@
import frappe, subprocess, os
+from six.moves import input
def setup_database(force, source_sql, verbose):
root_conn = get_root_connection()
@@ -10,9 +11,16 @@ def setup_database(force, source_sql, verbose):
frappe.conf.db_password))
root_conn.sql("GRANT ALL PRIVILEGES ON DATABASE `{0}` TO {0}".format(frappe.conf.db_name))
+ # we can't pass psql password in arguments in postgresql as mysql. So
+ # set password connection parameter in environment variable
+ subprocess_env = os.environ.copy()
+ subprocess_env['PGPASSWORD'] = str(frappe.conf.db_password)
# bootstrap db
- subprocess.check_output(['psql', frappe.conf.db_name, '-qf',
- os.path.join(os.path.dirname(__file__), 'framework_postgres.sql')])
+ subprocess.check_output([
+ 'psql', frappe.conf.db_name, '-h', 'localhost', '-U',
+ frappe.conf.db_name, '-f',
+ os.path.join(os.path.dirname(__file__), 'framework_postgres.sql')
+ ], env=subprocess_env)
frappe.connect()
@@ -24,17 +32,20 @@ def setup_help_database(help_db_name):
root_conn.sql("CREATE user {0} password '{1}'".format(help_db_name, help_db_name))
root_conn.sql("GRANT ALL PRIVILEGES ON DATABASE `{0}` TO {0}".format(help_db_name))
-def get_root_connection(root_login='postgres', root_password=None):
+def get_root_connection(root_login=None, root_password=None):
import getpass
if not frappe.local.flags.root_connection:
if not root_login:
- root_login = 'root'
+ root_login = frappe.conf.get("root_login") or None
+
+ if not root_login:
+ root_login = input("Enter postgres super user: ")
if not root_password:
root_password = frappe.conf.get("root_password") or None
if not root_password:
- root_password = getpass.getpass("Postgres root password: ")
+ root_password = getpass.getpass("Postgres super user password: ")
frappe.local.flags.root_connection = frappe.database.get_db(user=root_login, password=root_password)
diff --git a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
index c178bcfc79..ba00d81143 100644
--- a/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
+++ b/frappe/desk/doctype/dashboard_chart/dashboard_chart.js
@@ -100,6 +100,9 @@ frappe.ui.form.on('Dashboard Chart', {
// nothing is mandatory
_df.reqd = 0;
_df.default = null;
+ _df.read_only = 0;
+ _df.permlevel = 1;
+ _df.hidden = 0;
frm.chart_filters.push(_df);
}
diff --git a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py
index ca55d9254a..970f937020 100644
--- a/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py
+++ b/frappe/desk/doctype/dashboard_chart/test_dashboard_chart.py
@@ -8,6 +8,10 @@ from frappe.utils import getdate
from frappe.desk.doctype.dashboard_chart.dashboard_chart import (get,
get_period_ending)
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
+import calendar
+
class TestDashboardChart(unittest.TestCase):
def test_period_ending(self):
self.assertEqual(get_period_ending('2019-04-10', 'Daily'),
@@ -48,21 +52,13 @@ class TestDashboardChart(unittest.TestCase):
timeseries = 1
)).insert()
- result = get(chart_name ='Test Dashboard Chart',
- to_date = '2019-04-11', refresh = 1)
- self.assertEqual(result.get('labels')[0], '2018-04-30')
- self.assertEqual(result.get('labels')[1], '2018-05-31')
- self.assertEqual(result.get('labels')[2], '2018-06-30')
- self.assertEqual(result.get('labels')[3], '2018-07-31')
- self.assertEqual(result.get('labels')[4], '2018-08-31')
- self.assertEqual(result.get('labels')[5], '2018-09-30')
- self.assertEqual(result.get('labels')[6], '2018-10-31')
- self.assertEqual(result.get('labels')[7], '2018-11-30')
- self.assertEqual(result.get('labels')[8], '2018-12-31')
- self.assertEqual(result.get('labels')[9], '2019-01-31')
- self.assertEqual(result.get('labels')[10], '2019-02-28')
- self.assertEqual(result.get('labels')[11], '2019-03-31')
- self.assertEqual(result.get('labels')[12], '2019-04-30')
+ cur_date = datetime.now() - relativedelta(years=1)
+
+ result = get(chart_name ='Test Dashboard Chart', refresh = 1)
+ for idx in range(13):
+ month = str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])
+ self.assertEqual(result.get('labels')[idx], month)
+ cur_date += relativedelta(months=1)
# self.assertEqual(result.get('datasets')[0].get('values')[:-1],
# [44, 28, 8, 11, 2, 6, 18, 6, 4, 5, 15, 13])
@@ -87,21 +83,13 @@ class TestDashboardChart(unittest.TestCase):
timeseries = 1
)).insert()
- result = get(chart_name ='Test Empty Dashboard Chart',
- to_date = '2019-04-11', refresh = 1)
- self.assertEqual(result.get('labels')[0], '2018-04-30')
- self.assertEqual(result.get('labels')[1], '2018-05-31')
- self.assertEqual(result.get('labels')[2], '2018-06-30')
- self.assertEqual(result.get('labels')[3], '2018-07-31')
- self.assertEqual(result.get('labels')[4], '2018-08-31')
- self.assertEqual(result.get('labels')[5], '2018-09-30')
- self.assertEqual(result.get('labels')[6], '2018-10-31')
- self.assertEqual(result.get('labels')[7], '2018-11-30')
- self.assertEqual(result.get('labels')[8], '2018-12-31')
- self.assertEqual(result.get('labels')[9], '2019-01-31')
- self.assertEqual(result.get('labels')[10], '2019-02-28')
- self.assertEqual(result.get('labels')[11], '2019-03-31')
- self.assertEqual(result.get('labels')[12], '2019-04-30')
+ cur_date = datetime.now() - relativedelta(years=1)
+
+ result = get(chart_name ='Test Empty Dashboard Chart', refresh = 1)
+ for idx in range(13):
+ month = str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])
+ self.assertEqual(result.get('labels')[idx], month)
+ cur_date += relativedelta(months=1)
frappe.db.rollback()
@@ -126,24 +114,16 @@ class TestDashboardChart(unittest.TestCase):
timeseries = 1
)).insert()
- result = get(chart_name ='Test Empty Dashboard Chart 2',
- to_date = '2019-04-11', refresh = 1)
- self.assertEqual(result.get('labels')[0], '2018-04-30')
- self.assertEqual(result.get('labels')[1], '2018-05-31')
- self.assertEqual(result.get('labels')[2], '2018-06-30')
- self.assertEqual(result.get('labels')[3], '2018-07-31')
- self.assertEqual(result.get('labels')[4], '2018-08-31')
- self.assertEqual(result.get('labels')[5], '2018-09-30')
- self.assertEqual(result.get('labels')[6], '2018-10-31')
- self.assertEqual(result.get('labels')[7], '2018-11-30')
- self.assertEqual(result.get('labels')[8], '2018-12-31')
- self.assertEqual(result.get('labels')[9], '2019-01-31')
- self.assertEqual(result.get('labels')[10], '2019-02-28')
- self.assertEqual(result.get('labels')[11], '2019-03-31')
- self.assertEqual(result.get('labels')[12], '2019-04-30')
+ cur_date = datetime.now() - relativedelta(years=1)
+
+ result = get(chart_name ='Test Empty Dashboard Chart 2', refresh = 1)
+ for idx in range(13):
+ month = str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])
+ self.assertEqual(result.get('labels')[idx], month)
+ cur_date += relativedelta(months=1)
# only 1 data point with value
- self.assertEqual(result.get('datasets')[0].get('values')[2], 1)
+ self.assertEqual(result.get('datasets')[0].get('values')[2], 0)
frappe.db.rollback()
diff --git a/frappe/desk/doctype/event/event.py b/frappe/desk/doctype/event/event.py
index 04f7455e2d..1ad57246a6 100644
--- a/frappe/desk/doctype/event/event.py
+++ b/frappe/desk/doctype/event/event.py
@@ -43,10 +43,17 @@ class Event(Document):
def sync_communication(self):
if self.event_participants:
for participant in self.event_participants:
- communication_name = frappe.db.get_value("Communication", dict(reference_doctype=self.doctype, reference_name=self.name, timeline_doctype=participant.reference_doctype, timeline_name=participant.reference_docname), "name")
- if communication_name:
- communication = frappe.get_doc("Communication", communication_name)
- self.update_communication(participant, communication)
+ comms = frappe.get_list("Communication", filters=[
+ ["Communication", "reference_doctype", "=", self.doctype],
+ ["Communication", "reference_name", "=", self.name],
+ ["Communication Link", "link_doctype", "=", participant.reference_doctype],
+ ["Communication Link", "link_name", "=", participant.reference_docname]
+ ], fields=["name"])
+
+ if comms:
+ for comm in comms:
+ communication = frappe.get_doc("Communication", comm.name)
+ self.update_communication(participant, communication)
else:
meta = frappe.get_meta(participant.reference_doctype)
if hasattr(meta, "allow_events_in_timeline") and meta.allow_events_in_timeline==1:
@@ -62,12 +69,11 @@ class Event(Document):
communication.subject = self.subject
communication.content = self.description if self.description else self.subject
communication.communication_date = self.starts_on
- communication.timeline_doctype = participant.reference_doctype
- communication.timeline_name = participant.reference_docname
communication.reference_doctype = self.doctype
communication.reference_name = self.name
communication.communication_medium = communication_mapping[self.event_category] if self.event_category else ""
communication.status = "Linked"
+ communication.add_link(participant.reference_doctype, participant.reference_docname)
communication.save(ignore_permissions=True)
@frappe.whitelist()
@@ -76,9 +82,18 @@ def delete_communication(event, reference_doctype, reference_docname):
if isinstance(event, string_types):
event = json.loads(event)
- communication_name = frappe.db.get_value("Communication", dict(reference_doctype=event["doctype"], reference_name=event["name"], timeline_doctype=deleted_participant.reference_doctype, timeline_name=deleted_participant.reference_docname), "name")
- if communication_name:
- deletion = frappe.get_doc("Communication", communication_name).delete()
+ comms = frappe.get_list("Communication", filters=[
+ ["Communication", "reference_doctype", "=", event.get("doctype")],
+ ["Communication", "reference_name", "=", event.get("name")],
+ ["Communication Link", "link_doctype", "=", deleted_participant.reference_doctype],
+ ["Communication Link", "link_name", "=", deleted_participant.reference_docname]
+ ], fields=["name"])
+
+ if comms:
+ deletion = []
+ for comm in comms:
+ delete = frappe.get_doc("Communication", comm.name).delete()
+ deletion.append(delete)
return deletion
diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py
index 45c3874806..5d04f412c0 100644
--- a/frappe/desk/doctype/todo/todo.py
+++ b/frappe/desk/doctype/todo/todo.py
@@ -43,8 +43,11 @@ class ToDo(Document):
def on_trash(self):
# unlink todo from linked comments
- frappe.db.sql("""update `tabCommunication` set link_doctype=null, link_name=null
- where link_doctype=%(doctype)s and link_name=%(name)s""", {"doctype": self.doctype, "name": self.name})
+ frappe.db.sql("""
+ delete from `tabCommunication Link`
+ where link_doctype=%(doctype)s and link_name=%(name)s""", {
+ "doctype": self.doctype, "name": self.name
+ })
self.update_in_reference()
@@ -94,7 +97,7 @@ def get_permission_query_conditions(user):
if "System Manager" in frappe.get_roles(user):
return None
else:
- return """(tabToDo.owner = {user} or tabToDo.assigned_by = {user})"""\
+ return """(`tabToDo`.owner = {user} or `tabToDo`.assigned_by = {user})"""\
.format(user=frappe.db.escape(user))
def has_permission(doc, user):
@@ -108,4 +111,4 @@ def new_todo(description):
frappe.get_doc({
'doctype': 'ToDo',
'description': description
- }).insert()
\ No newline at end of file
+ }).insert()
diff --git a/frappe/desk/form/document_follow.py b/frappe/desk/form/document_follow.py
index de04ef35af..333e9e1333 100644
--- a/frappe/desk/form/document_follow.py
+++ b/frappe/desk/form/document_follow.py
@@ -54,8 +54,8 @@ def unfollow_document(doctype, doc_name, user):
return 1
return 0
-def get_message(doc_name, doctype, frequency):
- activity_list = get_version(doctype, doc_name, frequency) + get_comments(doctype, doc_name, frequency)
+def get_message(doc_name, doctype, frequency, user):
+ activity_list = get_version(doctype, doc_name, frequency, user) + get_comments(doctype, doc_name, frequency, user)
return sorted(activity_list, key=lambda k: k["time"], reverse=True)
def send_email_alert(receiver, docinfo, timeline):
@@ -98,7 +98,7 @@ def send_document_follow_mails(frequency):
valid_document_follows = []
if user_frequency == frequency:
for d in grouped_by_user[user]:
- content = get_message(d.ref_docname, d.ref_doctype, frequency)
+ content = get_message(d.ref_docname, d.ref_doctype, frequency, user)
if content:
message = message + content
valid_document_follows.append({
@@ -107,13 +107,13 @@ def send_document_follow_mails(frequency):
"reference_url": get_url_to_form(d.ref_doctype, d.ref_docname)
})
- if message:
+ if message and frappe.db.get_value("User", user, "document_follow_notify", ignore=True):
send_email_alert(user, valid_document_follows, message)
-def get_version(doctype, doc_name, frequency):
+def get_version(doctype, doc_name, frequency, user):
timeline = []
- filters = get_filters("docname", doc_name, frequency)
+ filters = get_filters("docname", doc_name, frequency, user)
version = frappe.get_all("Version",
filters=filters,
fields=["ref_doctype", "data", "modified", "modified", "modified_by"]
@@ -134,9 +134,9 @@ def get_version(doctype, doc_name, frequency):
return timeline
-def get_comments(doctype, doc_name, frequency):
+def get_comments(doctype, doc_name, frequency, user):
timeline = []
- filters = get_filters("reference_name", doc_name, frequency)
+ filters = get_filters("reference_name", doc_name, frequency, user)
comments = frappe.get_all("Comment",
filters=filters,
fields=["content", "modified", "modified_by", "comment_type"]
@@ -255,26 +255,29 @@ def send_daily_updates():
def send_weekly_updates():
send_document_follow_mails("Weekly")
-def get_filters(search_by, name, frequency):
+def get_filters(search_by, name, frequency, user):
filters = []
if frequency == "Weekly":
filters = [
[search_by, "=", name],
["modified", ">", frappe.utils.add_days(frappe.utils.nowdate(),-7)],
- ["modified", "<", frappe.utils.nowdate()]
+ ["modified", "<", frappe.utils.nowdate()],
+ ["modified_by", "!=", user]
]
elif frequency == "Daily":
filters = [
[search_by, "=", name],
["modified", ">", frappe.utils.add_days(frappe.utils.nowdate(),-1)],
- ["modified", "<", frappe.utils.nowdate()]
+ ["modified", "<", frappe.utils.nowdate()],
+ ["modified_by", "!=", user]
]
elif frequency == "Hourly":
filters = [
[search_by, "=", name],
- ["modified", ">", frappe.utils.add_to_date(frappe.utils.now_datetime(), 0, 0, 0, -1)],
- ["modified", "<", frappe.utils.now_datetime()]
+ ["modified", ">", frappe.utils.add_to_date(frappe.utils.now_datetime(), hours=-1)],
+ ["modified", "<", frappe.utils.now_datetime()],
+ ["modified_by", "!=", user]
]
return filters
diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py
index 00f29b6ffe..3b67b78144 100644
--- a/frappe/desk/form/load.py
+++ b/frappe/desk/form/load.py
@@ -47,9 +47,6 @@ def getdoc(doctype, name, user=None):
frappe.errprint(frappe.utils.get_traceback())
raise
- if doc and not name.startswith('_'):
- frappe.get_user().update_recent(doctype, name)
-
doc.add_seen()
frappe.response.docs.append(doc)
@@ -100,7 +97,6 @@ def get_docinfo(doc=None, doctype=None, name=None):
"assignments": get_assignments(doc.doctype, doc.name),
"permissions": get_doc_permissions(doc),
"shared": frappe.share.get_users(doc.doctype, doc.name),
- "rating": get_feedback_rating(doc.doctype, doc.name),
"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),
@@ -164,36 +160,59 @@ def get_communication_data(doctype, name, start=0, limit=20, after=None, fields=
group_by=None, as_dict=True):
'''Returns list of communications for a given document'''
if not fields:
- fields = '''`name`, `communication_type`,`communication_medium`, `comment_type`,
- `communication_date`, `content`, `sender`, `sender_full_name`, `cc`, `bcc`,
- `creation`, `subject`, `delivery_status`, `_liked_by`,
- `timeline_doctype`, `timeline_name`, `reference_doctype`, `reference_name`,
- `link_doctype`, `link_name`, `read_by_recipient`, `rating`, 'Communication' AS `doctype`'''
-
- conditions = '''communication_type in ('Communication', 'Feedback')
- and (
- (reference_doctype=%(doctype)s and reference_name=%(name)s)
- or (
- (timeline_doctype=%(doctype)s and timeline_name=%(name)s)
- and (communication_type='Communication')
- )
- )'''
-
+ fields = '''
+ C.name, C.communication_type, C.communication_medium,
+ C.comment_type, C.communication_date, C.content,
+ C.sender, C.sender_full_name, C.cc, C.bcc,
+ C.creation AS creation, C.subject, C.delivery_status,
+ C._liked_by, C.reference_doctype, C.reference_name,
+ C.read_by_recipient, C.rating
+ '''
+ conditions = ''
if after:
# find after a particular date
- conditions+= ' and creation > {0}'.format(after)
+ conditions += '''
+ AND C.creation > {0}
+ '''.format(after)
if doctype=='User':
- conditions+= " and not (reference_doctype='User' and communication_type='Communication')"
+ conditions += '''
+ AND NOT (C.reference_doctype='User' AND C.communication_type='Communication')
+ '''
- communications = frappe.db.sql("""select {fields}
- from `tabCommunication`
- where {conditions} {group_by}
- order by creation desc LIMIT %(limit)s OFFSET %(start)s""".format(
- fields = fields, conditions=conditions, group_by=group_by or ""),
- { "doctype": doctype, "name": name, "start": frappe.utils.cint(start), "limit": limit },
- as_dict=as_dict)
+ # communications linked to reference_doctype
+ part1 = '''
+ SELECT {fields}
+ FROM `tabCommunication` as C
+ WHERE C.communication_type IN ('Communication', 'Feedback')
+ AND (C.reference_doctype = %(doctype)s AND C.reference_name = %(name)s)
+ {conditions}
+ '''.format(fields=fields, conditions=conditions)
+
+ # communications linked in Timeline Links
+ part2 = '''
+ SELECT {fields}
+ FROM `tabCommunication` as C
+ INNER JOIN `tabCommunication Link` ON C.name=`tabCommunication Link`.parent
+ WHERE C.communication_type IN ('Communication', 'Feedback')
+ AND `tabCommunication Link`.link_doctype = %(doctype)s AND `tabCommunication Link`.link_name = %(name)s
+ {conditions}
+ '''.format(fields=fields, conditions=conditions)
+
+ communications = frappe.db.sql('''
+ SELECT *
+ FROM (({part1}) UNION ({part2})) AS combined
+ {group_by}
+ ORDER BY creation DESC
+ LIMIT %(limit)s
+ OFFSET %(start)s
+ '''.format(part1=part1, part2=part2, group_by=(group_by or '')), dict(
+ doctype=doctype,
+ name=name,
+ start=frappe.utils.cint(start),
+ limit=limit
+ ), as_dict=as_dict)
return communications
@@ -222,21 +241,6 @@ def run_onload(doc):
doc.set("__onload", frappe._dict())
doc.run_method("onload")
-def get_feedback_rating(doctype, docname):
- """ get and return the latest feedback rating if available """
-
- rating= frappe.get_all("Communication", filters={
- "reference_doctype": doctype,
- "reference_name": docname,
- "communication_type": "Feedback"
- }, fields=["rating"], order_by="creation desc", as_list=True)
-
- if not rating:
- return 0
- else:
- return rating[0][0]
-
-
def get_view_logs(doctype, docname):
""" get and return the latest view logs if available """
logs = []
@@ -248,4 +252,4 @@ def get_view_logs(doctype, docname):
if view_logs:
logs = view_logs
- return logs
+ return logs
\ No newline at end of file
diff --git a/frappe/desk/form/save.py b/frappe/desk/form/save.py
index 1e07f10ba7..694b44b907 100644
--- a/frappe/desk/form/save.py
+++ b/frappe/desk/form/save.py
@@ -27,11 +27,8 @@ def savedocs(doc, action):
# update recent documents
run_onload(doc)
- frappe.get_user().update_recent(doc.doctype, doc.name)
send_updated_docs(doc)
except Exception:
- if not frappe.local.message_log:
- frappe.msgprint(frappe._('Did not save'))
frappe.errprint(frappe.utils.get_traceback())
raise
diff --git a/frappe/desk/link_preview.py b/frappe/desk/link_preview.py
new file mode 100644
index 0000000000..f8252f20bc
--- /dev/null
+++ b/frappe/desk/link_preview.py
@@ -0,0 +1,28 @@
+import frappe
+from frappe.model import no_value_fields
+import json
+
+@frappe.whitelist()
+def get_preview_data(doctype, docname, fields):
+ fields = json.loads(fields)
+ preview_fields = [field['name'] for field in fields if field['type'] not in no_value_fields]
+ preview_fields.append(frappe.get_meta(doctype).get_title_field())
+ if 'name' not in fields:
+ preview_fields.append('name')
+ preview_fields.append(frappe.get_meta(doctype).image_field)
+
+ preview_data = frappe.get_list(doctype, filters={
+ 'name': docname
+ }, fields=preview_fields, limit=1)
+ if preview_data:
+ preview_data = preview_data[0]
+
+ preview_data = {k: v for k, v in preview_data.items() if v is not None}
+ for k,v in preview_data.items():
+ if frappe.get_meta(doctype).has_field(k):
+ preview_data[k] = frappe.format(v,frappe.get_meta(doctype).get_field(k).fieldtype)
+
+ if not preview_data:
+ return None
+ return preview_data
+
diff --git a/frappe/desk/moduleview.py b/frappe/desk/moduleview.py
index a9a460b124..ccde3aad40 100644
--- a/frappe/desk/moduleview.py
+++ b/frappe/desk/moduleview.py
@@ -383,7 +383,7 @@ def get_report_list(module, is_standard="No"):
out.append({
"type": "report",
"doctype": r.ref_doctype,
- "is_query_report": 1 if r.report_type in ("Query Report", "Script Report") else 0,
+ "is_query_report": 1 if r.report_type in ("Query Report", "Script Report", "Custom Report") else 0,
"label": _(r.name),
"name": r.name
})
diff --git a/frappe/desk/page/activity/activity.py b/frappe/desk/page/activity/activity.py
index e25c94d0e5..31ade42e7c 100644
--- a/frappe/desk/page/activity/activity.py
+++ b/frappe/desk/page/activity/activity.py
@@ -38,7 +38,8 @@ def get_feed(start, page_length):
{match_conditions_comment}
) X
order by X.creation DESC
- limit %(start)s, %(page_length)s"""
+ LIMIT %(page_length)s
+ OFFSET %(start)s"""
.format(match_conditions_comment = match_conditions_comment,
match_conditions_communication = match_conditions_communication), {
"user": frappe.session.user,
@@ -55,4 +56,4 @@ def get_heatmap_data():
where
date(creation) > subdate(curdate(), interval 1 year)
group by date(creation)
- order by creation asc"""))
\ No newline at end of file
+ order by creation asc"""))
diff --git a/frappe/desk/page/setup_wizard/setup_wizard.py b/frappe/desk/page/setup_wizard/setup_wizard.py
index cf8f5f502b..a18d4df9c4 100755
--- a/frappe/desk/page/setup_wizard/setup_wizard.py
+++ b/frappe/desk/page/setup_wizard/setup_wizard.py
@@ -389,7 +389,7 @@ def make_records(records, debug=False):
# pass DuplicateEntryError and continue
if e.args and e.args[0]==doc.doctype and e.args[1]==doc.name:
# make sure DuplicateEntryError is for the exact same doc and not a related doc
- pass
+ frappe.clear_messages()
else:
raise
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
index f1c3535824..0890e2ad7a 100644
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -132,6 +132,8 @@ def background_enqueue_run(report_name, filters=None, user=None):
})
track_instance.insert(ignore_permissions=True)
frappe.db.commit()
+ track_instance.enqueue_report()
+
return {
"name": track_instance.name,
"redirect_url": get_url_to_form("Prepared Report", track_instance.name)
@@ -280,6 +282,10 @@ def export_query():
filters = json.loads(data["filters"])
if isinstance(data.get("report_name"), string_types):
report_name = data["report_name"]
+ frappe.permissions.can_export(
+ frappe.get_cached_value('Report', report_name, 'ref_doctype'),
+ raise_exception=True
+ )
if isinstance(data.get("file_format_type"), string_types):
file_format_type = data["file_format_type"]
diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py
index 42409867fb..5ed4dc730b 100644
--- a/frappe/desk/reportview.py
+++ b/frappe/desk/reportview.py
@@ -108,7 +108,7 @@ def save_report():
d.report_type = "Report Builder"
d.json = data['json']
frappe.get_doc(d).save()
- frappe.msgprint(_("{0} is saved").format(d.name))
+ frappe.msgprint(_("{0} is saved").format(d.name), alert=True)
return d.name
@frappe.whitelist()
diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.js b/frappe/email/doctype/auto_email_report/auto_email_report.js
index c4f5397ca0..fab5bd3a8a 100644
--- a/frappe/email/doctype/auto_email_report/auto_email_report.js
+++ b/frappe/email/doctype/auto_email_report/auto_email_report.js
@@ -54,9 +54,9 @@ frappe.ui.form.on('Auto Email Report', {
show_filters: function(frm) {
var wrapper = $(frm.get_field('filters_display').wrapper);
wrapper.empty();
- if(frm.doc.report_type !== 'Report Builder'
+ if(frm.doc.report_type === 'Custom Report' || (frm.doc.report_type !== 'Report Builder'
&& frappe.query_reports[frm.doc.report]
- && frappe.query_reports[frm.doc.report].filters) {
+ && frappe.query_reports[frm.doc.report].filters)) {
// make a table to show filters
var table = $('| %(set_user_permissions)s | \ ', perm)); } - + me.perm_dialog.set_title(role); me.perm_dialog.show(); } }); @@ -191,7 +191,7 @@ frappe.RoleEditor = Class.extend({ }, make_perm_dialog: function() { this.perm_dialog = new frappe.ui.Dialog({ - title:__('Role Permissions') + title: __('Role Permissions') }); this.perm_dialog.$wrapper.find('.modal-dialog').css("width", "800px"); diff --git a/frappe/public/js/frappe/router_history.js b/frappe/public/js/frappe/router_history.js index 93d4904c0b..fb3d09fe0b 100644 --- a/frappe/public/js/frappe/router_history.js +++ b/frappe/public/js/frappe/router_history.js @@ -1,6 +1,6 @@ frappe.provide('frappe.route'); frappe.route_history_queue = []; -const routes_to_skip = ['Form', 'social']; +const routes_to_skip = ['Form', 'social', 'setup-wizard']; const save_routes = frappe.utils.debounce(() => { const routes = frappe.route_history_queue; diff --git a/frappe/public/js/frappe/ui/group_by/group_by.html b/frappe/public/js/frappe/ui/group_by/group_by.html index 172d955807..dd8d3dd281 100644 --- a/frappe/public/js/frappe/ui/group_by/group_by.html +++ b/frappe/public/js/frappe/ui/group_by/group_by.html @@ -22,10 +22,6 @@