Merge branch 'develop' of https://github.com/frappe/frappe into web-form-refactor
This commit is contained in:
commit
c85b76aeaa
199 changed files with 4413 additions and 12903 deletions
|
|
@ -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`;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
raise frappe.PermissionError
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
]
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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]),
|
||||
})
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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)
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
}
|
||||
10
frappe/core/doctype/communication_link/communication_link.py
Normal file
10
frappe/core/doctype/communication_link/communication_link.py
Normal file
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -23,6 +23,14 @@ import frappe.website.render
|
|||
import json
|
||||
|
||||
class InvalidFieldNameError(frappe.ValidationError): pass
|
||||
class UniqueFieldnameError(frappe.ValidationError): pass
|
||||
class IllegalMandatoryError(frappe.ValidationError): pass
|
||||
class DoctypeLinkError(frappe.ValidationError): pass
|
||||
class WrongOptionsDoctypeLinkError(frappe.ValidationError): pass
|
||||
class HiddenAndMandatoryWithoutDefaultError(frappe.ValidationError): pass
|
||||
class NonUniqueError(frappe.ValidationError): pass
|
||||
class CannotIndexedError(frappe.ValidationError): pass
|
||||
class CannotCreateStandardDoctypeError(frappe.ValidationError): pass
|
||||
|
||||
form_grid_templates = {
|
||||
"fields": "templates/form_grid/fields.html"
|
||||
|
|
@ -101,7 +109,7 @@ class DocType(Document):
|
|||
return
|
||||
|
||||
if not frappe.conf.get("developer_mode") and not self.custom:
|
||||
frappe.throw(_("Not in Developer Mode! Set in site_config.json or make 'Custom' DocType."))
|
||||
frappe.throw(_("Not in Developer Mode! Set in site_config.json or make 'Custom' DocType."), CannotCreateStandardDoctypeError)
|
||||
|
||||
def setup_fields_to_fetch(self):
|
||||
'''Setup query to update values for newly set fetch values'''
|
||||
|
|
@ -542,7 +550,6 @@ def validate_fields_for_doctype(doctype):
|
|||
# this is separate because it is also called via custom field
|
||||
def validate_fields(meta):
|
||||
"""Validate doctype fields. Checks
|
||||
|
||||
1. There are no illegal characters in fieldnames
|
||||
2. If fieldnames are unique.
|
||||
3. Validate column length.
|
||||
|
|
@ -562,38 +569,38 @@ def validate_fields(meta):
|
|||
def check_illegal_characters(fieldname):
|
||||
validate_column_name(fieldname)
|
||||
|
||||
def check_unique_fieldname(fieldname):
|
||||
def check_unique_fieldname(docname, fieldname):
|
||||
duplicates = list(filter(None, map(lambda df: df.fieldname==fieldname and str(df.idx) or None, fields)))
|
||||
if len(duplicates) > 1:
|
||||
frappe.throw(_("Fieldname {0} appears multiple times in rows {1}").format(fieldname, ", ".join(duplicates)))
|
||||
frappe.throw(_("{0}: Fieldname {1} appears multiple times in rows {2}").format(docname, fieldname, ", ".join(duplicates)), UniqueFieldnameError)
|
||||
|
||||
def check_fieldname_length(fieldname):
|
||||
validate_column_length(fieldname)
|
||||
|
||||
def check_illegal_mandatory(d):
|
||||
def check_illegal_mandatory(docname, d):
|
||||
if (d.fieldtype in no_value_fields) and d.fieldtype not in table_fields and d.reqd:
|
||||
frappe.throw(_("Field {0} of type {1} cannot be mandatory").format(d.label, d.fieldtype))
|
||||
frappe.throw(_("{0}: Field {1} of type {2} cannot be mandatory").format(docname, d.label, d.fieldtype), IllegalMandatoryError)
|
||||
|
||||
def check_link_table_options(d):
|
||||
def check_link_table_options(docname, d):
|
||||
if d.fieldtype in ("Link",) + table_fields:
|
||||
if not d.options:
|
||||
frappe.throw(_("Options required for Link or Table type field {0} in row {1}").format(d.label, d.idx))
|
||||
frappe.throw(_("{0}: Options required for Link or Table type field {1} in row {2}").format(docname, d.label, d.idx), DoctypeLinkError)
|
||||
if d.options=="[Select]" or d.options==d.parent:
|
||||
return
|
||||
if d.options != d.parent:
|
||||
options = frappe.db.get_value("DocType", d.options, "name")
|
||||
if not options:
|
||||
frappe.throw(_("Options must be a valid DocType for field {0} in row {1}").format(d.label, d.idx))
|
||||
frappe.throw(_("{0}: Options must be a valid DocType for field {1} in row {2}").format(docname, d.label, d.idx), WrongOptionsDoctypeLinkError)
|
||||
elif not (options == d.options):
|
||||
frappe.throw(_("Options {0} must be the same as doctype name {1} for the field {2}")
|
||||
.format(d.options, options, d.label))
|
||||
frappe.throw(_("{0}: Options {1} must be the same as doctype name {2} for the field {3}", DoctypeLinkError)
|
||||
.format(docname, d.options, options, d.label))
|
||||
else:
|
||||
# fix case
|
||||
d.options = options
|
||||
|
||||
def check_hidden_and_mandatory(d):
|
||||
def check_hidden_and_mandatory(docname, d):
|
||||
if d.hidden and d.reqd and not d.default:
|
||||
frappe.throw(_("Field {0} in row {1} cannot be hidden and mandatory without default").format(d.label, d.idx))
|
||||
frappe.throw(_("{0}: Field {1} in row {2} cannot be hidden and mandatory without default").format(docname, d.label, d.idx), HiddenAndMandatoryWithoutDefaultError)
|
||||
|
||||
def check_width(d):
|
||||
if d.fieldtype == "Currency" and cint(d.width) < 100:
|
||||
|
|
@ -616,7 +623,9 @@ def validate_fields(meta):
|
|||
frappe.throw(_("Options 'Dynamic Link' type of field must point to another Link Field with options as 'DocType'"))
|
||||
|
||||
def check_illegal_default(d):
|
||||
if d.fieldtype == "Check" and d.default and d.default not in ('0', '1'):
|
||||
if d.fieldtype == "Check" and not d.default:
|
||||
d.default = '0'
|
||||
if d.fieldtype == "Check" and d.default not in ('0', '1'):
|
||||
frappe.throw(_("Default for 'Check' type of field must be either '0' or '1'"))
|
||||
if d.fieldtype == "Select" and d.default and (d.default not in d.options.split("\n")):
|
||||
frappe.throw(_("Default for {0} must be an option").format(d.fieldname))
|
||||
|
|
@ -625,14 +634,14 @@ def validate_fields(meta):
|
|||
if d.fieldtype in ("Currency", "Float", "Percent") and d.precision is not None and not (1 <= cint(d.precision) <= 6):
|
||||
frappe.throw(_("Precision should be between 1 and 6"))
|
||||
|
||||
def check_unique_and_text(d):
|
||||
def check_unique_and_text(docname, d):
|
||||
if meta.issingle:
|
||||
d.unique = 0
|
||||
d.search_index = 0
|
||||
|
||||
if getattr(d, "unique", False):
|
||||
if d.fieldtype not in ("Data", "Link", "Read Only"):
|
||||
frappe.throw(_("Fieldtype {0} for {1} cannot be unique").format(d.fieldtype, d.label))
|
||||
frappe.throw(_("{0}: Fieldtype {1} for {2} cannot be unique").format(docname, d.fieldtype, d.label), NonUniqueError)
|
||||
|
||||
if not d.get("__islocal") and frappe.db.has_column(d.parent, d.fieldname):
|
||||
has_non_unique_values = frappe.db.sql("""select `{fieldname}`, count(*)
|
||||
|
|
@ -641,10 +650,10 @@ def validate_fields(meta):
|
|||
doctype=d.parent, fieldname=d.fieldname))
|
||||
|
||||
if has_non_unique_values and has_non_unique_values[0][0]:
|
||||
frappe.throw(_("Field '{0}' cannot be set as Unique as it has non-unique values").format(d.label))
|
||||
frappe.throw(_("{0}: Field '{1}' cannot be set as Unique as it has non-unique values").format(docname, d.label), NonUniqueError)
|
||||
|
||||
if d.search_index and d.fieldtype in ("Text", "Long Text", "Small Text", "Code", "Text Editor"):
|
||||
frappe.throw(_("Fieldtype {0} for {1} cannot be indexed").format(d.fieldtype, d.label))
|
||||
frappe.throw(_("{0}:Fieldtype {1} for {2} cannot be indexed").format(docname, d.fieldtype, d.label), CannotIndexedError)
|
||||
|
||||
def check_fold(fields):
|
||||
fold_exists = False
|
||||
|
|
@ -795,16 +804,16 @@ def validate_fields(meta):
|
|||
d.fieldname = d.fieldname.lower()
|
||||
|
||||
check_illegal_characters(d.fieldname)
|
||||
check_unique_fieldname(d.fieldname)
|
||||
check_unique_fieldname(meta.get("name"), d.fieldname)
|
||||
check_fieldname_length(d.fieldname)
|
||||
check_illegal_mandatory(d)
|
||||
check_link_table_options(d)
|
||||
check_illegal_mandatory(meta.get("name"), d)
|
||||
check_link_table_options(meta.get("name"), d)
|
||||
check_dynamic_link_options(d)
|
||||
check_hidden_and_mandatory(d)
|
||||
check_hidden_and_mandatory(meta.get("name"), d)
|
||||
check_in_list_view(d)
|
||||
check_in_global_search(d)
|
||||
check_illegal_default(d)
|
||||
check_unique_and_text(d)
|
||||
check_unique_and_text(meta.get("name"), d)
|
||||
check_illegal_depends_on_conditions(d)
|
||||
check_table_multiselect_option(d)
|
||||
scrub_options_in_select(d)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ from __future__ import unicode_literals
|
|||
|
||||
import frappe
|
||||
import unittest
|
||||
from frappe.core.doctype.doctype.doctype import UniqueFieldnameError, IllegalMandatoryError, DoctypeLinkError, WrongOptionsDoctypeLinkError,\
|
||||
HiddenAndMandatoryWithoutDefaultError, CannotIndexedError
|
||||
|
||||
# test_records = frappe.get_test_records('DocType')
|
||||
|
||||
|
|
@ -226,3 +228,55 @@ class TestDocType(unittest.TestCase):
|
|||
raise
|
||||
finally:
|
||||
frappe.flags.allow_doctype_export = 0
|
||||
|
||||
def test_unique_field_name_for_two_fields(self):
|
||||
doc = self.new_doctype('Test Unique Field')
|
||||
field_1 = doc.append('fields', {})
|
||||
field_1.fieldname = 'some_fieldname_1'
|
||||
field_1.fieldtype = 'Data'
|
||||
|
||||
field_2 = doc.append('fields', {})
|
||||
field_2.fieldname = 'some_fieldname_1'
|
||||
field_2.fieldtype = 'Data'
|
||||
|
||||
self.assertRaises(UniqueFieldnameError, doc.insert)
|
||||
|
||||
def test_illegal_mandatory_validation(self):
|
||||
doc = self.new_doctype('Test Illegal mandatory')
|
||||
field_1 = doc.append('fields', {})
|
||||
field_1.fieldname = 'some_fieldname_1'
|
||||
field_1.fieldtype = 'Section Break'
|
||||
field_1.reqd = 1
|
||||
|
||||
self.assertRaises(IllegalMandatoryError, doc.insert)
|
||||
|
||||
def test_link_with_wrong_and_no_options(self):
|
||||
doc = self.new_doctype('Test link')
|
||||
field_1 = doc.append('fields', {})
|
||||
field_1.fieldname = 'some_fieldname_1'
|
||||
field_1.fieldtype = 'Link'
|
||||
|
||||
self.assertRaises(DoctypeLinkError, doc.insert)
|
||||
|
||||
field_1.options = 'wrongdoctype'
|
||||
|
||||
self.assertRaises(WrongOptionsDoctypeLinkError, doc.insert)
|
||||
|
||||
def test_hidden_and_mandatory_without_default(self):
|
||||
doc = self.new_doctype('Test hidden and mandatory')
|
||||
field_1 = doc.append('fields', {})
|
||||
field_1.fieldname = 'some_fieldname_1'
|
||||
field_1.fieldtype = 'Data'
|
||||
field_1.reqd = 1
|
||||
field_1.hidden = 1
|
||||
|
||||
self.assertRaises(HiddenAndMandatoryWithoutDefaultError, doc.insert)
|
||||
|
||||
def test_field_can_not_be_indexed_validation(self):
|
||||
doc = self.new_doctype('Test index')
|
||||
field_1 = doc.append('fields', {})
|
||||
field_1.fieldname = 'some_fieldname_1'
|
||||
field_1.fieldtype = 'Long Text'
|
||||
field_1.search_index = 1
|
||||
|
||||
self.assertRaises(CannotIndexedError, doc.insert)
|
||||
|
|
|
|||
|
|
@ -1,125 +1,47 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2017-01-13 04:55:18.835023",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"creation": "2017-01-13 04:55:18.835023",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"link_doctype",
|
||||
"link_name",
|
||||
"link_title"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"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_list_view": 1,
|
||||
"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": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "link_doctype",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Link DocType",
|
||||
"options": "DocType",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"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_list_view": 1,
|
||||
"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": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "link_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Link Name",
|
||||
"options": "link_doctype",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "link_title",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Link Title",
|
||||
"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": "link_title",
|
||||
"fieldtype": "Read Only",
|
||||
"in_list_view": 1,
|
||||
"label": "Link Title",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-01-17 14:25:49.140730",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Dynamic Link",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2019-05-16 19:54:31.400026",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Dynamic Link",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) 2016, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Feedback Request', {
|
||||
refresh: function(frm) {
|
||||
var rating_icons = frappe.render_template("rating_icons", {rating: frm.doc.rating, show_label: false});
|
||||
$(frm.fields_dict.feedback_rating.wrapper).html(rating_icons);
|
||||
|
||||
if(frm.doc.reference_doctype && frm.doc.reference_name) {
|
||||
frm.add_custom_button(__(frm.doc.reference_name), function() {
|
||||
frappe.set_route("Form", frm.doc.reference_doctype, frm.doc.reference_name);
|
||||
});
|
||||
}
|
||||
|
||||
if(frm.doc.reference_communication){
|
||||
frm.add_custom_button(__("Communication"), function() {
|
||||
frappe.set_route("Form", "Communication", frm.doc.reference_communication);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -1,470 +0,0 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "",
|
||||
"beta": 0,
|
||||
"creation": "2017-01-27 15:43:33.780808",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "is_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": "Is Sent",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "is_feedback_submitted",
|
||||
"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": "Feedback Submitted",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"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_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Is Feedback request triggered manually ?",
|
||||
"fieldname": "is_manual",
|
||||
"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 Manual",
|
||||
"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": 1,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "key",
|
||||
"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": "Key",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reference",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "reference_doctype",
|
||||
"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": "Reference DocType",
|
||||
"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_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "reference_name",
|
||||
"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": "Reference Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 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
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "feedback_trigger",
|
||||
"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 Trigger",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Feedback Trigger",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"collapsible_depends_on": "eval: doc.rating",
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_1",
|
||||
"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 Rating",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "rating",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"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": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "feedback_rating",
|
||||
"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": "Feedback Rating",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reference_communication",
|
||||
"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": "Reference Communication",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 1,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-03-03 08:11:09.718589",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Feedback Request",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"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": 0
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "reference_name",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import get_datetime
|
||||
|
||||
class FeedbackRequest(Document):
|
||||
def autoname(self):
|
||||
""" feedback request name in the format Feedback for {doctype} {name} on {datetime}"""
|
||||
|
||||
self.name = "Feedback for {doctype} {docname} on {datetime}".format(
|
||||
doctype=self.reference_doctype,
|
||||
docname=self.reference_name,
|
||||
datetime=get_datetime()
|
||||
)
|
||||
|
||||
def before_insert(self):
|
||||
from frappe.utils import random_string
|
||||
|
||||
self.key = random_string(32)
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def is_valid_feedback_request(key=None):
|
||||
if not key:
|
||||
return False
|
||||
|
||||
is_feedback_submitted = frappe.db.get_value("Feedback Request", { "key": key }, "is_feedback_submitted")
|
||||
if is_feedback_submitted:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def delete_feedback_request():
|
||||
""" clear 100 days old feedback request """
|
||||
frappe.db.sql("""delete from `tabFeedback Request` where `creation` < (NOW() - INTERVAL '100' DAY)""")
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
frappe.listview_settings['Feedback Request'] = {
|
||||
colwidths: {
|
||||
subject: 2,
|
||||
},
|
||||
column_render: {
|
||||
rating: function(doc) {
|
||||
var html = ""
|
||||
for (var i = 0; i < 5; i++) {
|
||||
html += repl("<span class='indicator %(color)s'></span>",
|
||||
{color: i<doc.rating? "blue": "darkgrey"})
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Feedback Request')
|
||||
|
||||
class TestFeedbackRequest(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (c) 2016, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Feedback Trigger', {
|
||||
onload: function(frm) {
|
||||
frm.set_query("document_type", function() {
|
||||
return {
|
||||
"filters": {
|
||||
"istable": 0
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.events.setup_field_options(frm);
|
||||
},
|
||||
|
||||
document_type: function(frm) {
|
||||
frm.set_value('email_field', '');
|
||||
frm.set_value('email_fieldname');
|
||||
|
||||
frm.events.setup_field_options(frm);
|
||||
},
|
||||
|
||||
email_field: function(frm) {
|
||||
frm.set_value('email_fieldname', frm.fieldname_mapper[frm.doc.email_field]);
|
||||
},
|
||||
|
||||
setup_field_options: function(frm) {
|
||||
frm.fieldname_mapper = {};
|
||||
frm.options = [];
|
||||
|
||||
if(!frm.doc.document_type)
|
||||
return
|
||||
|
||||
frappe.model.with_doctype(frm.doc.document_type, function() {
|
||||
var fields = frappe.get_doc("DocType", frm.doc.document_type).fields;
|
||||
$.each(fields, function(idx, field) {
|
||||
if(!in_list(frappe.model.no_value_type, field.fieldtype) && field.options == "Email") {
|
||||
frm.options.push(field.label);
|
||||
frm.fieldname_mapper[field.label] = field.fieldname;
|
||||
}
|
||||
})
|
||||
|
||||
frm.set_df_property("email_field", "options", [""].concat(frm.options));
|
||||
frm.refresh_fields();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -1,517 +0,0 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:document_type",
|
||||
"beta": 0,
|
||||
"creation": "2017-01-24 15:46:38.366213",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "enabled",
|
||||
"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": "Enabled",
|
||||
"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": "section_break_2",
|
||||
"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,
|
||||
"fieldname": "document_type",
|
||||
"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": "Document Type",
|
||||
"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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval: doc.document_type",
|
||||
"fieldname": "email_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": "Email 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": 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": "email_fieldname",
|
||||
"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": "Email Fieldname",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "To add dynamic subject, use jinja tags like\n\n<div><pre><code>{{ doc.name }} Delivered</code></pre></div>",
|
||||
"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": "<p><strong>Condition Examples:</strong></p>\n<pre>doc.status==\"Closed\"\ndoc.due_date==nowdate()\ndoc.total > 40000\n</pre>",
|
||||
"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": "<h5>Message Example</h5>\n\n<pre><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></pre>",
|
||||
"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
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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'):
|
||||
|
|
|
|||
|
|
@ -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:<strong>240</strong>",
|
||||
"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",
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
""",(user, applied, doctype, docname))
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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."))
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
return query
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
raise e
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}).insert()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
28
frappe/desk/link_preview.py
Normal file
28
frappe/desk/link_preview.py
Normal file
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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"""))
|
||||
order by creation asc"""))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 = $('<table class="table table-bordered" style="cursor:pointer; margin:0px;"><thead>\
|
||||
|
|
@ -65,7 +65,17 @@ frappe.ui.form.on('Auto Email Report', {
|
|||
$('<p class="text-muted small">' + __("Click table to edit") + '</p>').appendTo(wrapper);
|
||||
|
||||
var filters = JSON.parse(frm.doc.filters || '{}');
|
||||
var report_filters = frappe.query_reports[frm.doc.report].filters;
|
||||
|
||||
let report_filters;
|
||||
|
||||
if (frm.doc.report_type === 'Custom Report'
|
||||
&& frappe.query_reports[frm.doc.reference_report]
|
||||
&& frappe.query_reports[frm.doc.reference_report].filters) {
|
||||
report_filters = frappe.query_reports[frm.doc.reference_report].filters;
|
||||
} else {
|
||||
report_filters = frappe.query_reports[frm.doc.report].filters;
|
||||
}
|
||||
|
||||
if(report_filters && report_filters.length > 0) {
|
||||
frm.set_value('filter_meta', JSON.stringify(report_filters));
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -387,7 +387,7 @@ class EmailAccount(Document):
|
|||
communication._seen = json.dumps(users)
|
||||
|
||||
communication.flags.in_receive = True
|
||||
communication.insert(ignore_permissions = 1)
|
||||
communication.insert(ignore_permissions=True)
|
||||
|
||||
# save attachments
|
||||
communication._attachments = email.save_attachments_in_doc(communication)
|
||||
|
|
@ -470,7 +470,7 @@ class EmailAccount(Document):
|
|||
parent = frappe.db.get_all(self.append_to, filters={
|
||||
self.sender_field: email.from_email,
|
||||
self.subject_field: ("like", "%{0}%".format(subject)),
|
||||
"creation": (">", (get_datetime() - relativedelta(days=10)).strftime(DATE_FORMAT))
|
||||
"creation": (">", (get_datetime() - relativedelta(days=60)).strftime(DATE_FORMAT))
|
||||
}, fields="name")
|
||||
|
||||
# match only subject field
|
||||
|
|
@ -479,7 +479,7 @@ class EmailAccount(Document):
|
|||
if not parent and len(subject) > 10 and is_system_user(email.from_email):
|
||||
parent = frappe.db.get_all(self.append_to, filters={
|
||||
self.subject_field: ("like", "%{0}%".format(subject)),
|
||||
"creation": (">", (get_datetime() - relativedelta(days=10)).strftime(DATE_FORMAT))
|
||||
"creation": (">", (get_datetime() - relativedelta(days=60)).strftime(DATE_FORMAT))
|
||||
}, fields="name")
|
||||
|
||||
if parent:
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class TestEmailAccount(unittest.TestCase):
|
|||
email_account.db_set("enable_incoming", 0)
|
||||
|
||||
def test_incoming(self):
|
||||
frappe.db.sql("delete from tabCommunication where sender='test_sender@example.com'")
|
||||
cleanup("test_sender@example.com")
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "test_mails", "incoming-1.raw"), "r") as f:
|
||||
test_mails = [f.read()]
|
||||
|
|
@ -52,7 +52,8 @@ class TestEmailAccount(unittest.TestCase):
|
|||
"reference_name": comm.reference_name, "status":"Not Sent"}))
|
||||
|
||||
def test_incoming_with_attach(self):
|
||||
frappe.db.sql("DELETE FROM `tabCommunication` WHERE sender='test_sender@example.com'")
|
||||
cleanup("test_sender@example.com")
|
||||
|
||||
existing_file = frappe.get_doc({'doctype': 'File', 'file_name': 'erpnext-conf-14.png'})
|
||||
frappe.delete_doc("File", existing_file.name)
|
||||
|
||||
|
|
@ -75,7 +76,7 @@ class TestEmailAccount(unittest.TestCase):
|
|||
|
||||
|
||||
def test_incoming_attached_email_from_outlook_plain_text_only(self):
|
||||
frappe.db.sql("delete from tabCommunication where sender='test_sender@example.com'")
|
||||
cleanup("test_sender@example.com")
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "test_mails", "incoming-3.raw"), "r") as f:
|
||||
test_mails = [f.read()]
|
||||
|
|
@ -88,7 +89,7 @@ class TestEmailAccount(unittest.TestCase):
|
|||
self.assertTrue("This is an e-mail message sent automatically by Microsoft Outlook while" in comm.content)
|
||||
|
||||
def test_incoming_attached_email_from_outlook_layers(self):
|
||||
frappe.db.sql("delete from tabCommunication where sender='test_sender@example.com'")
|
||||
cleanup("test_sender@example.com")
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "test_mails", "incoming-4.raw"), "r") as f:
|
||||
test_mails = [f.read()]
|
||||
|
|
@ -123,8 +124,7 @@ class TestEmailAccount(unittest.TestCase):
|
|||
self.assertTrue("test-mail-002" in sent_mail.get("Subject"))
|
||||
|
||||
def test_threading(self):
|
||||
frappe.db.sql("""delete from tabCommunication
|
||||
where sender in ('test_sender@example.com', 'test@example.com')""")
|
||||
cleanup(["in", ['test_sender@example.com', 'test@example.com']])
|
||||
|
||||
# send
|
||||
sent_name = make(subject = "Test", content="test content",
|
||||
|
|
@ -149,8 +149,7 @@ class TestEmailAccount(unittest.TestCase):
|
|||
self.assertEqual(comm.reference_name, sent.reference_name)
|
||||
|
||||
def test_threading_by_subject(self):
|
||||
frappe.db.sql("""delete from tabCommunication
|
||||
where sender in ('test_sender@example.com', 'test@example.com')""")
|
||||
cleanup(["in", ['test_sender@example.com', 'test@example.com']])
|
||||
|
||||
with open(os.path.join(os.path.dirname(__file__), "test_mails", "reply-2.raw"), "r") as f:
|
||||
test_mails = [f.read()]
|
||||
|
|
@ -170,7 +169,7 @@ class TestEmailAccount(unittest.TestCase):
|
|||
self.assertEqual(comm_list[0].reference_name, comm_list[1].reference_name)
|
||||
|
||||
def test_threading_by_message_id(self):
|
||||
frappe.db.sql("""delete from tabCommunication""")
|
||||
cleanup()
|
||||
frappe.db.sql("""delete from `tabEmail Queue`""")
|
||||
|
||||
# reference document for testing
|
||||
|
|
@ -196,3 +195,13 @@ class TestEmailAccount(unittest.TestCase):
|
|||
# check if threaded correctly
|
||||
self.assertEqual(comm_list[0].reference_doctype, event.doctype)
|
||||
self.assertEqual(comm_list[0].reference_name, event.name)
|
||||
|
||||
def cleanup(sender=None):
|
||||
filters = {}
|
||||
if sender:
|
||||
filters.update({"sender": sender})
|
||||
|
||||
names = frappe.get_list("Communication", filters=filters, fields=["name"])
|
||||
for name in names:
|
||||
frappe.delete_doc_if_exists("Communication", name.name)
|
||||
frappe.delete_doc_if_exists("Communication Link", {"parent": name.name})
|
||||
|
|
@ -126,9 +126,14 @@ def get_context(context):
|
|||
self.send_a_slack_msg(doc, context)
|
||||
|
||||
if self.set_property_after_alert:
|
||||
frappe.db.set_value(doc.doctype, doc.name, self.set_property_after_alert,
|
||||
self.property_value, update_modified = False)
|
||||
doc.set(self.set_property_after_alert, self.property_value)
|
||||
allow_update = True
|
||||
if doc.docstatus == 1 and not doc.meta.get_field(self.set_property_after_alert).allow_on_submit:
|
||||
allow_update = False
|
||||
|
||||
if allow_update:
|
||||
frappe.db.set_value(doc.doctype, doc.name, self.set_property_after_alert,
|
||||
self.property_value, update_modified = False)
|
||||
doc.set(self.set_property_after_alert, self.property_value)
|
||||
|
||||
def send_an_email(self, doc, context):
|
||||
from email.utils import formataddr
|
||||
|
|
@ -150,6 +155,7 @@ def get_context(context):
|
|||
reference_doctype = doc.doctype,
|
||||
reference_name = doc.name,
|
||||
attachments = attachments,
|
||||
expose_recipients="header",
|
||||
print_letterhead = ((attachments
|
||||
and attachments[0].get('print_letterhead')) or False))
|
||||
|
||||
|
|
|
|||
|
|
@ -2330,6 +2330,7 @@
|
|||
},
|
||||
"Suriname": {
|
||||
"code": "sr",
|
||||
"currency": "SRD",
|
||||
"currency_fraction": "Cent",
|
||||
"currency_fraction_units": 100,
|
||||
"currency_symbol": "$",
|
||||
|
|
|
|||
|
|
@ -118,7 +118,6 @@ doc_events = {
|
|||
"frappe.core.doctype.activity_log.feed.update_feed",
|
||||
"frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions",
|
||||
"frappe.automation.doctype.assignment_rule.assignment_rule.apply",
|
||||
"frappe.social.doctype.energy_point_rule.energy_point_rule.process_energy_points",
|
||||
"frappe.automation.doctype.milestone_tracker.milestone_tracker.evaluate_milestone"
|
||||
],
|
||||
"after_rename": "frappe.desk.notifications.clear_doctype_notifications",
|
||||
|
|
@ -131,8 +130,8 @@ doc_events = {
|
|||
"frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions"
|
||||
],
|
||||
"on_change": [
|
||||
"frappe.core.doctype.feedback_trigger.feedback_trigger.trigger_feedback_request"
|
||||
]
|
||||
"frappe.social.doctype.energy_point_rule.energy_point_rule.process_energy_points"
|
||||
],
|
||||
},
|
||||
"Email Group Member": {
|
||||
"validate": "frappe.email.doctype.email_group.email_group.restrict_email_group"
|
||||
|
|
@ -157,6 +156,7 @@ scheduler_events = {
|
|||
"frappe.utils.error.collect_error_snapshots",
|
||||
"frappe.desk.page.backups.backups.delete_downloadable_backups",
|
||||
"frappe.limits.update_space_usage",
|
||||
"frappe.limits.update_site_usage",
|
||||
"frappe.desk.doctype.auto_repeat.auto_repeat.make_auto_repeat_entry",
|
||||
"frappe.deferred_insert.save_to_db",
|
||||
"frappe.desk.form.document_follow.send_hourly_updates"
|
||||
|
|
@ -172,7 +172,6 @@ scheduler_events = {
|
|||
"frappe.utils.scheduler.disable_scheduler_on_expiry",
|
||||
"frappe.utils.scheduler.restrict_scheduler_events_if_dormant",
|
||||
"frappe.email.doctype.auto_email_report.auto_email_report.send_daily",
|
||||
"frappe.core.doctype.feedback_request.feedback_request.delete_feedback_request",
|
||||
"frappe.core.doctype.activity_log.activity_log.clear_authentication_logs",
|
||||
"frappe.website.doctype.personal_data_deletion_request.personal_data_deletion_request.remove_unverified_record",
|
||||
"frappe.desk.form.document_follow.send_daily_updates",
|
||||
|
|
@ -285,4 +284,4 @@ user_privacy_documents = [
|
|||
'applies_to_website_user': 1
|
||||
},
|
||||
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
|
|
@ -12,16 +14,20 @@
|
|||
"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": "integration_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": "Integration Type",
|
||||
|
|
@ -38,19 +44,24 @@
|
|||
"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": "integration_request_service",
|
||||
"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": "Integration Request Service",
|
||||
|
|
@ -67,26 +78,31 @@
|
|||
"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": "Queued",
|
||||
"fetch_if_empty": 0,
|
||||
"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": "\nQueued\nAuthorized\nCompleted\nCancelled\nFailed\n",
|
||||
"options": "\nQueued\nAuthorized\nCompleted\nCancelled\nFailed",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
|
|
@ -97,19 +113,24 @@
|
|||
"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": "data",
|
||||
"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": "Data",
|
||||
|
|
@ -125,19 +146,24 @@
|
|||
"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": "output",
|
||||
"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": "Output",
|
||||
|
|
@ -153,19 +179,24 @@
|
|||
"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": "error",
|
||||
"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": "Error",
|
||||
|
|
@ -181,19 +212,24 @@
|
|||
"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": "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",
|
||||
|
|
@ -210,19 +246,24 @@
|
|||
"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": "reference_docname",
|
||||
"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 Docname",
|
||||
|
|
@ -239,20 +280,21 @@
|
|||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 1,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-10-09 14:40:00.783063",
|
||||
"modified": "2019-04-25 16:38:21.084580",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Integrations",
|
||||
"name": "Integration Request",
|
||||
|
|
@ -261,7 +303,6 @@
|
|||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
|
|
@ -269,7 +310,6 @@
|
|||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"is_custom": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
|
|
@ -284,9 +324,11 @@
|
|||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "integration_request_service",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
|
|
@ -1,317 +1,363 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2016-09-22 04:16:48.829658",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "System",
|
||||
"editable_grid": 1,
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2016-09-22 04:16:48.829658",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "System",
|
||||
"editable_grid": 1,
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "enabled",
|
||||
"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": "Enabled",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "ldap_server_url",
|
||||
"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": "LDAP Server Url",
|
||||
"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_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "organizational_unit",
|
||||
"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": "Organizational Unit",
|
||||
"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_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "base_dn",
|
||||
"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": "Base Distinguished Name (DN)",
|
||||
"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_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "password",
|
||||
"fieldtype": "Password",
|
||||
"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 for Base DN",
|
||||
"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_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_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "ldap_search_string",
|
||||
"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": "LDAP Search String",
|
||||
"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_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "ldap_first_name_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": "LDAP First Name 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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "ldap_email_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": "LDAP Email 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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "ldap_username_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": "LDAP Username 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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 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": "enabled",
|
||||
"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": "Enabled",
|
||||
"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": "ldap_server_url",
|
||||
"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": "LDAP Server Url",
|
||||
"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,
|
||||
"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": "organizational_unit",
|
||||
"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": "Organizational Unit",
|
||||
"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,
|
||||
"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": "base_dn",
|
||||
"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": "Base Distinguished Name (DN)",
|
||||
"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,
|
||||
"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": "password",
|
||||
"fieldtype": "Password",
|
||||
"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": "Password for Base DN",
|
||||
"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,
|
||||
"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": "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,
|
||||
"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": "ldap_search_string",
|
||||
"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": "LDAP Search String",
|
||||
"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,
|
||||
"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": "ldap_first_name_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": "LDAP First Name 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": 1,
|
||||
"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": "ldap_email_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": "LDAP Email 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": 1,
|
||||
"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": "ldap_username_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": "LDAP Username 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": 1,
|
||||
"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": "ldap_security",
|
||||
"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": "LDAP Security",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
|
|
@ -325,22 +371,28 @@
|
|||
"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": "Off",
|
||||
"description": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "ssl_tls_mode",
|
||||
"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": "SSL/TLS Mode",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
|
|
@ -355,21 +407,27 @@
|
|||
"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": "No",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "require_trusted_certificate",
|
||||
"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": "Require Trusted Certificate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
|
|
@ -384,53 +442,153 @@
|
|||
"reqd": 1,
|
||||
"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": "local_private_key_file",
|
||||
"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": "Path to private Key File",
|
||||
"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": "local_server_certificate_file",
|
||||
"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": "Path to Server Certificate",
|
||||
"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": "local_ca_certs_file",
|
||||
"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": "Path to CA Certs File",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 1,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-01-30 11:02:41.011412",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Integrations",
|
||||
"name": "LDAP Settings",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 1,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-04-29 10:56:42.322696",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Integrations",
|
||||
"name": "LDAP Settings",
|
||||
"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": 0,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 1,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 1,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
|
|
@ -5,56 +5,93 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cstr
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class LDAPSettings(Document):
|
||||
def validate(self):
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
if not self.flags.ignore_mandatory:
|
||||
self.validate_ldap_credentails()
|
||||
if self.ldap_search_string.endswith("={0}"):
|
||||
if self.enabled:
|
||||
connect_to_ldap(server_url=self.ldap_server_url,
|
||||
base_dn=self.base_dn,
|
||||
password=self.get_password(raise_exception=False),
|
||||
ssl_tls_mode=self.ssl_tls_mode,
|
||||
trusted_cert=self.require_trusted_certificate,
|
||||
private_key_file=self.local_private_key_file,
|
||||
server_cert_file=self.local_server_certificate_file,
|
||||
ca_certs_file=self.local_ca_certs_file)
|
||||
else:
|
||||
frappe.throw(_("LDAP Search String needs to end with a placeholder, eg sAMAccountName={0}"))
|
||||
|
||||
def validate_ldap_credentails(self):
|
||||
try:
|
||||
import ldap
|
||||
conn = ldap.initialize(self.ldap_server_url)
|
||||
try:
|
||||
if self.ssl_tls_mode == 'StartTLS':
|
||||
conn.set_option(ldap.OPT_X_TLS_DEMAND, True)
|
||||
if self.require_trusted_certificate == 'Yes':
|
||||
conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
|
||||
conn.start_tls_s()
|
||||
except:
|
||||
frappe.throw(_("StartTLS is not supported"))
|
||||
|
||||
conn.simple_bind_s(self.base_dn, self.get_password(raise_exception=False))
|
||||
except ImportError:
|
||||
msg = """
|
||||
<div>
|
||||
{{_("Seems ldap is not installed on system.<br>Guidelines to install ldap dependancies and python package")}},
|
||||
<a href="https://discuss.erpnext.com/t/frappe-v-7-1-beta-ldap-dependancies/15841" target="_blank">{{_("Click here")}}</a>,
|
||||
</div>
|
||||
"""
|
||||
frappe.throw(msg, title=_("LDAP Not Installed"))
|
||||
def get_ldap_client_settings():
|
||||
#return the settings to be used on the client side.
|
||||
result = {
|
||||
"enabled": False
|
||||
}
|
||||
settings = frappe.get_doc("LDAP Settings")
|
||||
|
||||
except ldap.LDAPError:
|
||||
conn.unbind_s()
|
||||
frappe.throw(_("Incorrect UserId or Password"))
|
||||
if settings and settings.enabled:
|
||||
result["enabled"] = True
|
||||
result["method"] = "frappe.integrations.doctype.ldap_settings.ldap_settings.login"
|
||||
return result
|
||||
|
||||
def get_ldap_settings():
|
||||
|
||||
def connect_to_ldap(server_url,
|
||||
base_dn,
|
||||
password,
|
||||
ssl_tls_mode,
|
||||
trusted_cert,
|
||||
private_key_file,
|
||||
server_cert_file,
|
||||
ca_certs_file):
|
||||
try:
|
||||
settings = frappe.get_doc("LDAP Settings")
|
||||
import ldap3
|
||||
import ssl
|
||||
|
||||
if trusted_cert == 'Yes':
|
||||
tls_configuration = ldap3.Tls(validate=ssl.CERT_REQUIRED,
|
||||
version=ssl.PROTOCOL_TLSv1)
|
||||
else:
|
||||
tls_configuration = ldap3.Tls(validate=ssl.CERT_NONE,
|
||||
version=ssl.PROTOCOL_TLSv1)
|
||||
|
||||
if private_key_file:
|
||||
tls_configuration.private_key_file = private_key_file
|
||||
if server_cert_file:
|
||||
tls_configuration.certificate_file = server_cert_file
|
||||
if ca_certs_file:
|
||||
tls_configuration.ca_certs_file = ca_certs_file
|
||||
|
||||
server = ldap3.Server(host=server_url,
|
||||
tls=tls_configuration)
|
||||
bind_type = ldap3.AUTO_BIND_TLS_BEFORE_BIND if ssl_tls_mode == "StartTLS" else True
|
||||
|
||||
conn = ldap3.Connection(server=server,
|
||||
user=base_dn,
|
||||
password=password,
|
||||
auto_bind=bind_type,
|
||||
read_only=True,
|
||||
raise_exceptions=True)
|
||||
|
||||
return conn
|
||||
|
||||
except ImportError:
|
||||
msg = _("Please Install the ldap3 library via pip to use ldap functionality.")
|
||||
frappe.throw(msg, title=_("LDAP Not Installed"))
|
||||
except ldap3.core.exceptions.LDAPInvalidCredentialsResult:
|
||||
frappe.throw(_("Invalid Credentials"))
|
||||
except Exception as ex:
|
||||
frappe.throw(_(str(ex)))
|
||||
|
||||
settings.update({
|
||||
"method": "frappe.integrations.doctype.ldap_settings.ldap_settings.login"
|
||||
})
|
||||
return settings
|
||||
except Exception:
|
||||
# this will return blank settings
|
||||
return frappe._dict()
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def login():
|
||||
#### LDAP LOGIN LOGIC #####
|
||||
# LDAP LOGIN LOGIC
|
||||
args = frappe.form_dict
|
||||
user = authenticate_ldap_user(frappe.as_unicode(args.usr), frappe.as_unicode(args.pwd))
|
||||
|
||||
|
|
@ -64,64 +101,57 @@ def login():
|
|||
# because of a GET request!
|
||||
frappe.db.commit()
|
||||
|
||||
def authenticate_ldap_user(user=None, password=None):
|
||||
dn = None
|
||||
|
||||
def authenticate_ldap_user(user=None,
|
||||
password=None):
|
||||
|
||||
params = {}
|
||||
settings = get_ldap_settings()
|
||||
settings = frappe.get_doc("LDAP Settings")
|
||||
if settings and settings.enabled:
|
||||
conn = connect_to_ldap(server_url=settings.ldap_server_url,
|
||||
base_dn=settings.base_dn,
|
||||
password=settings.get_password(raise_exception=False),
|
||||
ssl_tls_mode=settings.ssl_tls_mode,
|
||||
trusted_cert=settings.require_trusted_certificate,
|
||||
private_key_file=settings.local_private_key_file,
|
||||
server_cert_file=settings.local_server_certificate_file,
|
||||
ca_certs_file=settings.local_ca_certs_file)
|
||||
|
||||
try:
|
||||
import ldap
|
||||
except:
|
||||
msg = """
|
||||
<div>
|
||||
{{_("Seems ldap is not installed on system.")}}<br>
|
||||
<a href"https://discuss.erpnext.com/t/frappe-v-7-1-beta-ldap-dependancies/15841">{{_("Click here")}}</a>,
|
||||
{{_("Guidelines to install ldap dependancies and python")}}
|
||||
</div>
|
||||
"""
|
||||
frappe.throw(msg, title=_("LDAP Not Installed"))
|
||||
user_filter = settings.ldap_search_string.format(user)
|
||||
conn.search(search_base=settings.organizational_unit,
|
||||
search_filter="({0})".format(user_filter),
|
||||
attributes=[settings.ldap_email_field,
|
||||
settings.ldap_username_field,
|
||||
settings.ldap_first_name_field])
|
||||
|
||||
conn = ldap.initialize(settings.ldap_server_url)
|
||||
|
||||
try:
|
||||
try:
|
||||
# set TLS settings for secure connection
|
||||
if settings.ssl_tls_mode == 'StartTLS':
|
||||
conn.set_option(ldap.OPT_X_TLS_DEMAND, True)
|
||||
if settings.require_trusted_certificate == 'Yes':
|
||||
conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
|
||||
conn.start_tls_s()
|
||||
except:
|
||||
frappe.throw(_("StartTLS is not supported"))
|
||||
|
||||
# simple_bind_s is synchronous binding to server, it takes two param DN and password
|
||||
conn.simple_bind_s(settings.base_dn, settings.get_password(raise_exception=False))
|
||||
|
||||
#search for surnames beginning with a
|
||||
#available options for how deep a search you want.
|
||||
#LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL,LDAP_SCOPE_SUBTREE,
|
||||
result = conn.search_s(settings.organizational_unit, ldap.SCOPE_SUBTREE,
|
||||
settings.ldap_search_string.format(user))
|
||||
|
||||
for dn, r in result:
|
||||
dn = cstr(dn)
|
||||
params["email"] = cstr(r[settings.ldap_email_field][0])
|
||||
params["username"] = cstr(r[settings.ldap_username_field][0])
|
||||
params["first_name"] = cstr(r[settings.ldap_first_name_field][0])
|
||||
|
||||
if dn:
|
||||
conn.simple_bind_s(dn, frappe.as_unicode(password))
|
||||
if len(conn.entries) > 0 and conn.entries[0]:
|
||||
user = conn.entries[0]
|
||||
params["email"] = str(user[settings.ldap_email_field])
|
||||
params["username"] = str(user[settings.ldap_username_field])
|
||||
params["first_name"] = str(user[settings.ldap_first_name_field])
|
||||
connect_to_ldap(server_url=settings.ldap_server_url,
|
||||
base_dn=user.entry_dn,
|
||||
password=frappe.as_unicode(password),
|
||||
ssl_tls_mode=settings.ssl_tls_mode,
|
||||
trusted_cert=settings.require_trusted_certificate,
|
||||
private_key_file=settings.local_private_key_file,
|
||||
server_cert_file=settings.local_server_certificate_file,
|
||||
ca_certs_file=settings.local_ca_certs_file
|
||||
)
|
||||
return create_user(params)
|
||||
else:
|
||||
frappe.throw(_("Not a valid LDAP user"))
|
||||
else:
|
||||
frappe.throw(_("LDAP is not enabled."))
|
||||
|
||||
except ldap.LDAPError:
|
||||
conn.unbind_s()
|
||||
frappe.throw(_("Incorrect UserId or Password"))
|
||||
|
||||
def create_user(params):
|
||||
if frappe.db.exists("User", params["email"]):
|
||||
return frappe.get_doc("User", params["email"])
|
||||
user = frappe.get_doc("User", params["email"])
|
||||
user.first_name = params["first_name"]
|
||||
user.username = params["username"]
|
||||
user.save(ignore_permissions=True)
|
||||
return user
|
||||
|
||||
else:
|
||||
params.update({
|
||||
|
|
@ -135,6 +165,5 @@ def create_user(params):
|
|||
})
|
||||
|
||||
user = frappe.get_doc(params).insert(ignore_permissions=True)
|
||||
frappe.db.commit()
|
||||
|
||||
return user
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ from frappe.utils import now_datetime, getdate, flt, cint, get_fullname
|
|||
from frappe.installer import update_site_config
|
||||
from frappe.utils.data import formatdate
|
||||
from frappe.utils.user import get_enabled_system_users, disable_users
|
||||
import os, subprocess
|
||||
from frappe.utils.__init__ import get_site_info
|
||||
import os, subprocess, json
|
||||
from six.moves.urllib.parse import parse_qsl, urlsplit, urlunsplit, urlencode
|
||||
from six import string_types
|
||||
|
||||
|
|
@ -225,3 +226,8 @@ def get_folder_size(path):
|
|||
if os.path.exists(path):
|
||||
return flt(subprocess.check_output(['du', '-ms', path]).split()[0], 2)
|
||||
|
||||
def update_site_usage():
|
||||
data = get_site_info()
|
||||
with open(os.path.join(frappe.get_site_path(), 'site_data.json'), 'w') as outfile:
|
||||
json.dump(data, outfile)
|
||||
outfile.close()
|
||||
|
|
|
|||
|
|
@ -76,27 +76,50 @@ def delete_fields(args_dict, delete=0):
|
|||
args_dict = { dt: [field names] }
|
||||
"""
|
||||
import frappe.utils
|
||||
for dt in list(args_dict):
|
||||
for dt in args_dict:
|
||||
fields = args_dict[dt]
|
||||
if not fields: continue
|
||||
if not fields:
|
||||
continue
|
||||
|
||||
frappe.db.sql("""\
|
||||
frappe.db.sql("""
|
||||
DELETE FROM `tabDocField`
|
||||
WHERE parent=%s AND fieldname IN (%s)
|
||||
""" % ('%s', ", ".join(['"' + f + '"' for f in fields])), dt)
|
||||
WHERE parent='%s' AND fieldname IN (%s)
|
||||
""" % (dt, ", ".join(["'{}'".format(f) for f in fields])))
|
||||
|
||||
# Delete the data / column only if delete is specified
|
||||
if not delete: continue
|
||||
# Delete the data/column only if delete is specified
|
||||
if not delete:
|
||||
continue
|
||||
|
||||
if frappe.db.get_value("DocType", dt, "issingle"):
|
||||
frappe.db.sql("""\
|
||||
frappe.db.sql("""
|
||||
DELETE FROM `tabSingles`
|
||||
WHERE doctype=%s AND field IN (%s)
|
||||
""" % ('%s', ", ".join(['"' + f + '"' for f in fields])), dt)
|
||||
WHERE doctype='%s' AND field IN (%s)
|
||||
""" % (dt, ", ".join(["'{}'".format(f) for f in fields])))
|
||||
else:
|
||||
existing_fields = frappe.db.sql("desc `tab%s`" % dt)
|
||||
existing_fields = frappe.db.multisql({
|
||||
"mariadb": "DESC `tab%s`" % dt,
|
||||
"postgres": """
|
||||
SELECT
|
||||
COLUMN_NAME
|
||||
FROM
|
||||
information_schema.COLUMNS
|
||||
WHERE
|
||||
TABLE_NAME = 'tab%s';
|
||||
""" % dt,
|
||||
})
|
||||
existing_fields = existing_fields and [e[0] for e in existing_fields] or []
|
||||
fields_need_to_delete = set(fields) & set(existing_fields)
|
||||
if not fields_need_to_delete:
|
||||
continue
|
||||
|
||||
if frappe.conf.db_type == 'mariadb':
|
||||
# mariadb implicitly commits before DDL, make it explicit
|
||||
frappe.db.commit()
|
||||
|
||||
query = "ALTER TABLE `tab%s` " % dt + \
|
||||
", ".join(["DROP COLUMN `%s`" % f for f in fields if f in existing_fields])
|
||||
frappe.db.commit()
|
||||
", ".join(["DROP COLUMN `%s`" % f for f in fields_need_to_delete])
|
||||
frappe.db.sql(query)
|
||||
|
||||
if frappe.conf.db_type == 'postgres':
|
||||
# commit the results to db
|
||||
frappe.db.commit()
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ class BaseDocument(object):
|
|||
|
||||
return value
|
||||
|
||||
def get_valid_dict(self, sanitize=True, convert_dates_to_str=False):
|
||||
def get_valid_dict(self, sanitize=True, convert_dates_to_str=False, ignore_nulls = False):
|
||||
d = frappe._dict()
|
||||
for fieldname in self.meta.get_valid_columns():
|
||||
d[fieldname] = self.get(fieldname)
|
||||
|
|
@ -234,6 +234,9 @@ class BaseDocument(object):
|
|||
if convert_dates_to_str and isinstance(d[fieldname], (datetime.datetime, datetime.time, datetime.timedelta)):
|
||||
d[fieldname] = str(d[fieldname])
|
||||
|
||||
if d[fieldname] == None and ignore_nulls:
|
||||
del d[fieldname]
|
||||
|
||||
return d
|
||||
|
||||
def init_valid_columns(self):
|
||||
|
|
@ -306,7 +309,8 @@ class BaseDocument(object):
|
|||
self.creation = self.modified = now()
|
||||
self.created_by = self.modified_by = frappe.session.user
|
||||
|
||||
d = self.get_valid_dict(convert_dates_to_str=True)
|
||||
# if doctype is "DocType", don't insert null values as we don't know who is valid yet
|
||||
d = self.get_valid_dict(convert_dates_to_str=True, ignore_nulls = self.doctype in ('DocType', 'DocField', 'DocPerm'))
|
||||
|
||||
columns = list(d)
|
||||
try:
|
||||
|
|
@ -341,7 +345,7 @@ class BaseDocument(object):
|
|||
self.db_insert()
|
||||
return
|
||||
|
||||
d = self.get_valid_dict(convert_dates_to_str=True)
|
||||
d = self.get_valid_dict(convert_dates_to_str=True, ignore_nulls = self.doctype in ('DocType', 'DocField', 'DocPerm'))
|
||||
|
||||
# don't update name, as case might've been changed
|
||||
name = d['name']
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa
|
|||
frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", name)
|
||||
frappe.db.sql("delete from `tabReport` where ref_doctype=%s", name)
|
||||
frappe.db.sql("delete from `tabCustom DocPerm` where parent=%s", name)
|
||||
frappe.db.sql("delete from `__global_search` where doctype=%s", name)
|
||||
|
||||
delete_from_table(doctype, name, ignore_doctypes, None)
|
||||
|
||||
|
|
@ -277,9 +278,8 @@ def delete_dynamic_links(doctype, name):
|
|||
delete_references('Document Follow', doctype, name, 'ref_doctype', 'ref_docname')
|
||||
|
||||
# unlink communications
|
||||
clear_timeline_references(doctype, name)
|
||||
clear_references('Communication', doctype, name)
|
||||
clear_references('Communication', doctype, name, 'link_doctype', 'link_name')
|
||||
clear_references('Communication', doctype, name, 'timeline_doctype', 'timeline_name')
|
||||
|
||||
clear_references('Activity Log', doctype, name)
|
||||
clear_references('Activity Log', doctype, name, 'timeline_doctype', 'timeline_name')
|
||||
|
|
@ -300,6 +300,9 @@ def clear_references(doctype, reference_doctype, reference_name,
|
|||
{1}=%s and {2}=%s'''.format(doctype, reference_doctype_field, reference_name_field), # nosec
|
||||
(reference_doctype, reference_name))
|
||||
|
||||
def clear_timeline_references(link_doctype, link_name):
|
||||
frappe.db.sql("""delete from `tabCommunication Link`
|
||||
where `tabCommunication Link`.link_doctype='{0}' and `tabCommunication Link`.link_name='{1}'""".format(link_doctype, link_name)) # nosec
|
||||
|
||||
def insert_feed(doc):
|
||||
from frappe.utils import get_fullname
|
||||
|
|
|
|||
|
|
@ -230,6 +230,9 @@ class Document(BaseDocument):
|
|||
self.set_docstatus()
|
||||
self.flags.in_insert = False
|
||||
|
||||
# follow document on document creation
|
||||
|
||||
|
||||
# run validate, on update etc.
|
||||
|
||||
# parent
|
||||
|
|
@ -259,6 +262,8 @@ class Document(BaseDocument):
|
|||
if hasattr(self, "__islocal"):
|
||||
delattr(self, "__islocal")
|
||||
|
||||
if not (frappe.flags.in_migrate or frappe.local.flags.in_install):
|
||||
follow_document(self.doctype, self.name, frappe.session.user)
|
||||
return self
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
|
@ -1190,18 +1195,11 @@ class Document(BaseDocument):
|
|||
return
|
||||
|
||||
# update timeline doc in communication if it is different than current timeline doc
|
||||
frappe.db.sql("""update `tabCommunication`
|
||||
set timeline_doctype=%(timeline_doctype)s, timeline_name=%(timeline_name)s
|
||||
where
|
||||
reference_doctype=%(doctype)s and reference_name=%(name)s
|
||||
and (timeline_doctype is null or timeline_doctype != %(timeline_doctype)s
|
||||
or timeline_name is null or timeline_name != %(timeline_name)s)""",
|
||||
{
|
||||
"doctype": self.doctype,
|
||||
"name": self.name,
|
||||
"timeline_doctype": timeline_doctype,
|
||||
"timeline_name": timeline_name
|
||||
})
|
||||
communications = frappe.get_list("Communication", filters={"reference_doctype": self.doctype, "reference_name": self.name})
|
||||
for communication in communications:
|
||||
communication = frappe.get_doc("Communication", communication.name)
|
||||
# duplicate entries will be handled by deduplicate links in communication
|
||||
communication.add_link(link_doctype=timeline_doctype, link_name=timeline_name, autosave=True)
|
||||
|
||||
def queue_action(self, action, **kwargs):
|
||||
'''Run an action in background. If the action has an inner function,
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ def validate_rename(doctype, new, meta, merge, force, ignore_permissions):
|
|||
if (not merge) and exists:
|
||||
frappe.msgprint(_("Another {0} with name {1} exists, select another name").format(doctype, new), raise_exception=1)
|
||||
|
||||
if not (ignore_permissions or frappe.has_permission(doctype, "write")):
|
||||
if not (ignore_permissions or frappe.permissions.has_permission(doctype, "write", raise_exception=False)):
|
||||
frappe.msgprint(_("You need write permission to rename"), raise_exception=1)
|
||||
|
||||
if not (force or ignore_permissions) and not meta.allow_rename:
|
||||
|
|
|
|||
|
|
@ -230,9 +230,10 @@ frappe.patches.v11_0.delete_all_prepared_reports
|
|||
frappe.patches.v11_0.fix_order_by_in_reports_json
|
||||
execute:frappe.delete_doc('Page', 'applications', ignore_missing=True)
|
||||
frappe.patches.v11_0.set_missing_creation_and_modified_value_for_user_permissions
|
||||
frappe.patches.v11_0.remove_doctype_user_permissions_for_page_and_report
|
||||
frappe.patches.v11_0.set_default_letter_head_source
|
||||
frappe.patches.v12_0.set_primary_key_in_series
|
||||
execute:frappe.reload_doc('core', 'doctype', 'communication_link')
|
||||
execute:frappe.reload_doc('core', 'doctype', 'communication')
|
||||
execute:frappe.delete_doc("Page", "modules", ignore_missing=True)
|
||||
frappe.patches.v11_0.set_default_letter_head_source
|
||||
frappe.patches.v12_0.setup_comments_from_communications
|
||||
|
|
@ -240,3 +241,7 @@ frappe.patches.v12_0.init_desk_settings #11-03-2019
|
|||
frappe.patches.v12_0.replace_null_values_in_tables
|
||||
frappe.patches.v12_0.reset_home_settings
|
||||
frappe.patches.v12_0.update_print_format_type
|
||||
frappe.patches.v11_0.remove_doctype_user_permissions_for_page_and_report #2019-05-01
|
||||
frappe.patches.v12_0.remove_feedback_rating
|
||||
frappe.patches.v12_0.move_form_attachments_to_attachments_folder
|
||||
frappe.patches.v12_0.move_timeline_links_to_dynamic_links
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import re
|
|||
def execute():
|
||||
""" Create Contact for each User if not present """
|
||||
frappe.reload_doc('contacts', 'doctype', 'contact')
|
||||
frappe.reload_doc('core', 'doctype', 'dynamic_link')
|
||||
|
||||
users = frappe.get_all('User', filters={"name": ('not in', 'Administrator, Guest')}, fields=["*"])
|
||||
for user in users:
|
||||
|
|
|
|||
|
|
@ -5,5 +5,4 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
|
||||
def execute():
|
||||
if frappe.db.table_exists('User Permission for Page and Report'):
|
||||
frappe.delete_doc("DocType", "User Permission for Page and Report")
|
||||
frappe.delete_doc_if_exists("DocType", "User Permission for Page and Report")
|
||||
|
|
@ -6,4 +6,4 @@ def execute():
|
|||
frappe.reload_doctype('Letter Head')
|
||||
|
||||
# source of all existing letter heads must be HTML
|
||||
frappe.db.sql('update `tabLetter Head` set source = "HTML"')
|
||||
frappe.db.sql("update `tabLetter Head` set source = 'HTML'")
|
||||
|
|
|
|||
|
|
@ -8,4 +8,4 @@ from frappe.desk.moduleview import get_onboard_items
|
|||
def execute():
|
||||
"""Reset the initial customizations for desk, with modules, indices and links."""
|
||||
frappe.reload_doc("core", "doctype", "user")
|
||||
frappe.db.sql("""update tabUser set home_settings = %s""", (''), debug=True)
|
||||
frappe.db.sql("""update `tabUser` set home_settings = %s""", (''), debug=True)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.db.sql('''
|
||||
UPDATE tabFile
|
||||
SET folder = 'Home/Attachments'
|
||||
WHERE ifnull(attached_to_doctype, '') != ''
|
||||
AND folder = 'Home'
|
||||
''')
|
||||
44
frappe/patches/v12_0/move_timeline_links_to_dynamic_links.py
Normal file
44
frappe/patches/v12_0/move_timeline_links_to_dynamic_links.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
communications = frappe.db.sql("""
|
||||
SELECT
|
||||
`tabCommunication`.name, `tabCommunication`.creation, `tabCommunication`.modified,
|
||||
`tabCommunication`.modified_by,`tabCommunication`.timeline_doctype, `tabCommunication`.timeline_name,
|
||||
`tabCommunication`.link_doctype, `tabCommunication`.link_name
|
||||
FROM `tabCommunication`
|
||||
WHERE `tabCommunication`.communication_medium='Email'
|
||||
""", as_dict=True)
|
||||
|
||||
name = 1000000000
|
||||
values = []
|
||||
|
||||
for count, communication in enumerate(communications):
|
||||
counter = 1
|
||||
if communication.timeline_doctype and communication.timeline_name:
|
||||
name += 1
|
||||
values.append("""({0}, "{1}", "timeline_links", "Communication", "{2}", "{3}", "{4}", "{5}", "{6}", "{7}")""".format(
|
||||
counter, str(name), communication.name, communication.timeline_doctype,
|
||||
communication.timeline_name, communication.creation, communication.modified, communication.modified_by
|
||||
))
|
||||
counter += 1
|
||||
if communication.link_doctype and communication.link_name:
|
||||
name += 1
|
||||
values.append("""({0}, "{1}", "timeline_links", "Communication", "{2}", "{3}", "{4}", "{5}", "{6}", "{7}")""".format(
|
||||
counter, str(name), communication.name, communication.link_doctype,
|
||||
communication.link_name, communication.creation, communication.modified, communication.modified_by
|
||||
))
|
||||
|
||||
if values and (count % 10000 == 0 or count == len(communications) - 1):
|
||||
frappe.db.sql("""
|
||||
INSERT INTO `tabCommunication Link`
|
||||
(`idx`, `name`, `parentfield`, `parenttype`, `parent`, `link_doctype`, `link_name`, `creation`,
|
||||
`modified`, `modified_by`)
|
||||
VALUES {0}
|
||||
""".format(", ".join([d for d in values])))
|
||||
|
||||
values = []
|
||||
|
||||
frappe.db.add_index("Communication Link", ["link_doctype", "link_name"])
|
||||
9
frappe/patches/v12_0/remove_feedback_rating.py
Normal file
9
frappe/patches/v12_0/remove_feedback_rating.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import frappe
|
||||
|
||||
def execute():
|
||||
'''
|
||||
Deprecate Feedback Trigger and Rating. This feature was not customizable.
|
||||
Now can be achieved via custom Web Forms
|
||||
'''
|
||||
frappe.delete_doc('DocType', 'Feedback Trigger')
|
||||
frappe.delete_doc('DocType', 'Feedback Rating')
|
||||
|
|
@ -25,4 +25,4 @@ def execute():
|
|||
new_comment.db_insert()
|
||||
|
||||
# clean up
|
||||
frappe.db.sql('delete from tabCommunication where communication_type = "Comment"')
|
||||
frappe.db.sql("delete from `tabCommunication` where communication_type = 'Comment'")
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import frappe
|
|||
def execute():
|
||||
frappe.db.sql('''
|
||||
UPDATE `tabPrint Format`
|
||||
SET `print_format_type` = "Jinja"
|
||||
WHERE `print_format_type` in ("Server", "Client")
|
||||
SET `print_format_type` = 'Jinja'
|
||||
WHERE `print_format_type` in ('Server', 'Client')
|
||||
''')
|
||||
frappe.db.sql('''
|
||||
UPDATE `tabPrint Format`
|
||||
SET `print_format_type` = "JS"
|
||||
WHERE `print_format_type` = "Js"
|
||||
SET `print_format_type` = 'JS'
|
||||
WHERE `print_format_type` = 'Js'
|
||||
''')
|
||||
|
|
|
|||
|
|
@ -7,18 +7,4 @@ def execute():
|
|||
remove Guest None from sender full name
|
||||
setup feedback request trigger's is_manual field
|
||||
"""
|
||||
frappe.reload_doc('core', 'doctype', 'dynamic_link')
|
||||
frappe.reload_doc('email', 'doctype', 'contact')
|
||||
|
||||
frappe.reload_doc("core", "doctype", "feedback_request")
|
||||
frappe.reload_doc("core", "doctype", "communication")
|
||||
|
||||
if frappe.db.has_column('Communication', 'feedback'):
|
||||
frappe.db.sql("""update tabCommunication set content=ifnull(feedback, "feedback details not provided")
|
||||
where communication_type="Feedback" and content is NULL""")
|
||||
|
||||
frappe.db.sql(""" update tabCommunication set sender_full_name="" where communication_type="Feedback"
|
||||
and sender_full_name='Guest None' """)
|
||||
|
||||
frappe.db.sql(""" update `tabFeedback Request` set is_manual=1, feedback_trigger="Manual"
|
||||
where ifnull(feedback_trigger, '')='' """)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -7,29 +7,4 @@ def execute():
|
|||
update the feedback request and save the rating and communication
|
||||
reference in Feedback Request document
|
||||
"""
|
||||
|
||||
frappe.reload_doc("core", "doctype", "feedback_request")
|
||||
feedback_requests = frappe.get_all("Feedback Request")
|
||||
for request in feedback_requests:
|
||||
communication, rating = frappe.db.get_value("Communication", { "feedback_request": request.get("name") },
|
||||
["name", "rating"]) or [None, 0]
|
||||
|
||||
if communication:
|
||||
frappe.db.sql("""update `tabFeedback Request` set reference_communication='{communication}',
|
||||
rating={rating} where name='{feedback_request}'""".format(
|
||||
communication=communication,
|
||||
rating=rating or 0,
|
||||
feedback_request=request.get("name")
|
||||
))
|
||||
|
||||
if "Feedback" not in request.get("name"):
|
||||
# rename the feedback request doc
|
||||
reference_name, creation = frappe.db.get_value("Feedback Request", request.get("name"), ["name", "creation"])
|
||||
oldname = request.get("name")
|
||||
newname = "Feedback for {doctype} {docname} on {datetime}".format(
|
||||
doctype="Feedback Request",
|
||||
docname=reference_name,
|
||||
datetime=creation
|
||||
)
|
||||
frappe.rename_doc("Feedback Request", oldname, newname, ignore_permissions=True)
|
||||
if communication: frappe.db.set_value("Communication", communication, "feedback_request", newname)
|
||||
return
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue