diff --git a/cypress/integration/list_view_settings.js b/cypress/integration/list_view_settings.js index 0c564d4aac..84131386f6 100644 --- a/cypress/integration/list_view_settings.js +++ b/cypress/integration/list_view_settings.js @@ -6,7 +6,7 @@ context('List View Settings', () => { it('Default settings', () => { cy.visit('/desk#List/DocType/List'); cy.get('.list-count').should('contain', "20 of"); - cy.get('.sidebar-stat').should('contain', "No Tags"); + cy.get('.sidebar-stat').should('contain', "Tags"); }); it('disable count and sidebar stats then verify', () => { cy.visit('/desk#List/DocType/List'); diff --git a/frappe/desk/doctype/auto_repeat/__init__.py b/frappe/automation/doctype/auto_repeat/__init__.py similarity index 100% rename from frappe/desk/doctype/auto_repeat/__init__.py rename to frappe/automation/doctype/auto_repeat/__init__.py diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.js b/frappe/automation/doctype/auto_repeat/auto_repeat.js new file mode 100644 index 0000000000..a11de1d881 --- /dev/null +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.js @@ -0,0 +1,104 @@ +// Copyright (c) 2018, Frappe Technologies and contributors +// For license information, please see license.txt +frappe.provide("frappe.auto_repeat"); + +frappe.ui.form.on('Auto Repeat', { + setup: function(frm) { + frm.fields_dict['reference_doctype'].get_query = function() { + return { + query: "frappe.automation.doctype.auto_repeat.auto_repeat.get_auto_repeat_doctypes" + }; + }; + + frm.fields_dict['reference_document'].get_query = function() { + return { + filters: { + "auto_repeat": '' + } + }; + }; + + frm.fields_dict['print_format'].get_query = function() { + return { + filters: { + "doc_type": frm.doc.reference_doctype + } + }; + }; + }, + + refresh: function(frm) { + // auto repeat message + if (frm.is_new()) { + let customize_form_link = `${__('Customize Form')}`; + frm.dashboard.set_headline(__('To configure Auto Repeat, enable "Allow Auto Repeat" from {0}.', [customize_form_link])); + } + + // view document button + if (!frm.is_dirty()) { + let label = __('View {0}', [__(frm.doc.reference_doctype)]); + frm.add_custom_button(label, () => + frappe.set_route("List", frm.doc.reference_doctype, { auto_repeat: frm.doc.name }) + ); + } + + // auto repeat schedule + frappe.auto_repeat.render_schedule(frm); + }, + + template: function(frm) { + if (frm.doc.template) { + frappe.model.with_doc("Email Template", frm.doc.template, () => { + let email_template = frappe.get_doc("Email Template", frm.doc.template); + frm.set_value("subject", email_template.subject); + frm.set_value("message", email_template.response); + frm.refresh_field("subject"); + frm.refresh_field("message"); + }); + } + }, + + get_contacts: function(frm) { + frm.call('fetch_linked_contacts'); + }, + + preview_message: function(frm) { + if (frm.doc.message) { + frappe.call({ + method: "frappe.automation.doctype.auto_repeat.auto_repeat.generate_message_preview", + args: { + reference_dt: frm.doc.reference_doctype, + reference_doc: frm.doc.reference_document, + subject: frm.doc.subject, + message: frm.doc.message + }, + callback: function(r) { + if (r.message) { + frappe.msgprint(r.message.message, r.message.subject) + } + } + }); + } else { + frappe.msgprint(__("Please setup a message first"), __("Message not setup")) + } + } +}); + +frappe.auto_repeat.render_schedule = function(frm) { + if (!frm.is_dirty() && frm.doc.status !== 'Disabled') { + frappe.call({ + method: "get_auto_repeat_schedule", + doc: frm.doc + }).done((r) => { + frm.dashboard.wrapper.empty(); + frm.dashboard.add_section( + frappe.render_template("auto_repeat_schedule", { + schedule_details : r.message || [] + }) + ); + frm.dashboard.show(); + }); + } else { + frm.dashboard.hide(); + } +}; diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.json b/frappe/automation/doctype/auto_repeat/auto_repeat.json new file mode 100644 index 0000000000..8ee6ca1d45 --- /dev/null +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.json @@ -0,0 +1,239 @@ +{ + "allow_import": 1, + "allow_rename": 1, + "autoname": "format:AUT-AR-{#####}", + "creation": "2018-03-09 11:22:31.192349", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "section_break_1", + "disabled", + "section_break_3", + "reference_doctype", + "reference_document", + "column_break_5", + "start_date", + "end_date", + "section_break_10", + "frequency", + "repeat_on_day", + "repeat_on_last_day", + "column_break_12", + "next_schedule_date", + "notification", + "notify_by_email", + "recipients", + "get_contacts", + "template", + "subject", + "message", + "preview_message", + "print_format", + "status" + ], + "fields": [ + { + "fieldname": "section_break_1", + "fieldtype": "Section Break" + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reference Document Type", + "options": "DocType", + "reqd": 1 + }, + { + "fieldname": "reference_document", + "fieldtype": "Dynamic Link", + "label": "Reference Document", + "no_copy": 1, + "options": "reference_doctype", + "reqd": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "start_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Start Date", + "reqd": 1 + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "End Date" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled", + "no_copy": 1 + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "fieldname": "frequency", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Frequency", + "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nHalf-yearly\nYearly", + "reqd": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval: in_list([\"Monthly\", \"Quarterly\", \"Half-yearly\", \"Yearly\"], doc.frequency) && !doc.repeat_on_last_day\n", + "fieldname": "repeat_on_day", + "fieldtype": "Int", + "label": "Repeat on Day" + }, + { + "fieldname": "next_schedule_date", + "fieldtype": "Date", + "label": "Next Schedule Date", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "collapsible": 1, + "fieldname": "notification", + "fieldtype": "Section Break", + "label": "Notification" + }, + { + "default": "0", + "fieldname": "notify_by_email", + "fieldtype": "Check", + "label": "Notify by Email" + }, + { + "depends_on": "notify_by_email", + "fieldname": "recipients", + "fieldtype": "Small Text", + "label": "Recipients" + }, + { + "depends_on": "eval: doc.notify_by_email && doc.reference_doctype && doc.reference_document", + "fieldname": "get_contacts", + "fieldtype": "Button", + "label": "Get Contacts" + }, + { + "depends_on": "eval: doc.notify_by_email", + "fieldname": "template", + "fieldtype": "Link", + "label": "Template", + "options": "Email Template" + }, + { + "depends_on": "eval: doc.notify_by_email", + "description": "To add dynamic subject, use jinja tags like\n\n
New {{ doc.doctype }} #{{ doc.name }}
", + "fieldname": "subject", + "fieldtype": "Data", + "label": "Subject" + }, + { + "default": "Please find attached {{ doc.doctype }} #{{ doc.name }}", + "depends_on": "eval: doc.notify_by_email", + "fieldname": "message", + "fieldtype": "Text", + "label": "Message" + }, + { + "depends_on": "eval: doc.notify_by_email && doc.reference_doctype && doc.reference_document", + "fieldname": "preview_message", + "fieldtype": "Button", + "label": "Preview Message" + }, + { + "depends_on": "notify_by_email", + "fieldname": "print_format", + "fieldtype": "Link", + "label": "Print Format", + "options": "Print Format" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "in_list_view": 1, + "label": "Status", + "options": "\nActive\nDisabled\nCompleted", + "read_only": 1 + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break" + }, + { + "default": "0", + "depends_on": "eval:doc.frequency === 'Monthly'", + "fieldname": "repeat_on_last_day", + "fieldtype": "Check", + "label": "Repeat on Last Day of the Month" + } + ], + "modified": "2019-07-17 11:30:51.412317", + "modified_by": "Administrator", + "module": "Automation", + "name": "Auto Repeat", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + } + ], + "search_fields": "reference_document", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "reference_document", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py new file mode 100644 index 0000000000..8a429b93f9 --- /dev/null +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -0,0 +1,374 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.desk.form import assign_to +from frappe.utils.jinja import validate_template +from dateutil.relativedelta import relativedelta +from frappe.utils.user import get_system_managers +from frappe.utils import cstr, getdate, split_emails, add_days, today, get_last_day, get_first_day +from frappe.model.document import Document +from frappe.core.doctype.communication.email import make +from frappe.utils.background_jobs import get_jobs + +month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12} + + +class AutoRepeat(Document): + def validate(self): + self.update_status() + self.validate_reference_doctype() + self.validate_dates() + self.validate_email_id() + self.set_dates() + self.update_auto_repeat_id() + self.unlink_if_applicable() + + validate_template(self.subject or "") + validate_template(self.message or "") + + def before_insert(self): + if not frappe.flags.in_test: + start_date = self.start_date + today_date = today() + if start_date <= today_date: + start_date = today_date + + def after_save(self): + frappe.get_doc(self.reference_doctype, self.reference_document).notify_update() + + def on_trash(self): + frappe.db.set_value(self.reference_doctype, self.reference_document, { + 'auto_repeat': self.name + }, 'auto_repeat', '') + + def set_dates(self): + if self.disabled: + self.next_schedule_date = None + else: + self.next_schedule_date = get_next_schedule_date(self.start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, self.end_date) + + def unlink_if_applicable(self): + if self.status == 'Completed' or self.disabled: + frappe.db.set_value(self.reference_doctype, self.reference_document, 'auto_repeat', '') + + def validate_reference_doctype(self): + if not frappe.flags.in_test: + if not frappe.get_meta(self.reference_doctype).allow_auto_repeat: + frappe.throw(_("Enable Allow Auto Repeat for the doctype {0} in Customize Form").format(self.reference_doctype)) + + def validate_dates(self): + self.validate_from_to_dates('start_date', 'end_date') + + if self.end_date == self.start_date: + frappe.throw(_('{0} should not be same as {1}').format(frappe.bold('End Date'), frappe.bold('Start Date'))) + + def validate_email_id(self): + if self.notify_by_email: + if self.recipients: + email_list = split_emails(self.recipients.replace("\n", "")) + from frappe.utils import validate_email_address + + for email in email_list: + if not validate_email_address(email): + frappe.throw(_("{0} is an invalid email address in 'Recipients'").format(email)) + else: + frappe.throw(_("'Recipients' not specified")) + + def update_auto_repeat_id(self): + #check if document is already on auto repeat + auto_repeat = frappe.db.get_value(self.reference_doctype, self.reference_document, "auto_repeat") + if auto_repeat and auto_repeat != self.name: + frappe.throw(_("The {0} is already on auto repeat {1}").format(self.reference_document, auto_repeat)) + else: + frappe.db.set_value(self.reference_doctype, self.reference_document, "auto_repeat", self.name) + + def update_status(self): + if self.disabled: + self.status = "Disabled" + elif self.is_completed(): + self.status = "Completed" + else: + self.status = "Active" + + def is_completed(self): + return self.end_date and getdate(self.end_date) < getdate(today()) + + def get_auto_repeat_schedule(self): + schedule_details = [] + start_date = getdate(self.start_date) + end_date = getdate(self.end_date) + today = frappe.utils.datetime.date.today() + + if start_date < today: + start_date = today + + if not self.end_date: + start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day) + row = { + "reference_document": self.reference_document, + "frequency": self.frequency, + "next_scheduled_date": start_date + } + schedule_details.append(row) + start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day) + + if self.end_date: + start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day) + while (getdate(start_date) < getdate(end_date)): + row = { + "reference_document" : self.reference_document, + "frequency" : self.frequency, + "next_scheduled_date" : start_date + } + schedule_details.append(row) + start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, end_date) + + + return schedule_details + + def create_documents(self): + try: + new_doc = self.make_new_document() + if self.notify_by_email and self.recipients: + self.send_notification(new_doc) + except Exception: + error_log = frappe.log_error(frappe.get_traceback(), _("Auto Repeat Document Creation Failure")) + + self.disable_auto_repeat() + + if self.reference_document and not frappe.flags.in_test: + self.notify_error_to_user(error_log) + + def make_new_document(self): + reference_doc = frappe.get_doc(self.reference_doctype, self.reference_document) + new_doc = frappe.copy_doc(reference_doc, ignore_no_copy = False) + self.update_doc(new_doc, reference_doc) + new_doc.insert(ignore_permissions = True) + + return new_doc + + def update_doc(self, new_doc, reference_doc): + new_doc.docstatus = 0 + if new_doc.meta.get_field('set_posting_time'): + new_doc.set('set_posting_time', 1) + + if new_doc.meta.get_field('auto_repeat'): + new_doc.set('auto_repeat', self.name) + + for fieldname in ['naming_series', 'ignore_pricing_rule', 'posting_time', 'select_print_heading', 'remarks', 'owner']: + if new_doc.meta.get_field(fieldname): + new_doc.set(fieldname, reference_doc.get(fieldname)) + + for data in new_doc.meta.fields: + if data.fieldtype == 'Date' and data.reqd: + new_doc.set(data.fieldname, self.next_schedule_date) + + self.set_auto_repeat_period(new_doc) + + auto_repeat_doc = frappe.get_doc('Auto Repeat', self.name) + + #for any action that needs to take place after the recurring document creation + #on recurring method of that doctype is triggered + new_doc.run_method('on_recurring', reference_doc = reference_doc, auto_repeat_doc = auto_repeat_doc) + + def set_auto_repeat_period(self, new_doc): + mcount = month_map.get(self.frequency) + if mcount and new_doc.meta.get_field('from_date') and new_doc.meta.get_field('to_date'): + last_ref_doc = frappe.db.get_all(doctype = self.reference_doctype, + fields = ['name', 'from_date', 'to_date'], + filters = [ + ['auto_repeat', '=', self.name], + ['docstatus', '<', 2], + ], + order_by = 'creation desc', + limit = 1) + + if not last_ref_doc: + return + + from_date = get_next_date(last_ref_doc[0].from_date, mcount) + + if (cstr(get_first_day(last_ref_doc[0].from_date)) == cstr(last_ref_doc[0].from_date)) and \ + (cstr(get_last_day(last_ref_doc[0].to_date)) == cstr(last_ref_doc[0].to_date)): + to_date = get_last_day(get_next_date(last_ref_doc[0].to_date, mcount)) + else: + to_date = get_next_date(last_ref_doc[0].to_date, mcount) + + new_doc.set('from_date', from_date) + new_doc.set('to_date', to_date) + + def send_notification(self, new_doc): + """Notify concerned people about recurring document generation""" + subject = self.subject or '' + message = self.message or '' + + if not self.subject: + subject = _("New {0}: {1}").format(new_doc.doctype, new_doc.name) + elif "{" in self.subject: + subject = frappe.render_template(self.subject, {'doc': new_doc}) + + if not self.message: + message = _("Please find attached {0}: {1}").format(new_doc.doctype, new_doc.name) + elif "{" in self.message: + message = frappe.render_template(self.message, {'doc': new_doc}) + + print_format = self.print_format or 'Standard' + + attachments = [frappe.attach_print(new_doc.doctype, new_doc.name, + file_name=new_doc.name, print_format=print_format)] + + make(doctype=new_doc.doctype, name=new_doc.name, recipients=self.recipients, + subject=subject, content=message, attachments=attachments, send_email=1) + + def fetch_linked_contacts(self): + if self.reference_doctype and self.reference_document: + res = frappe.db.get_all('Contact', + fields=['email_id'], + filters=[ + ['Dynamic Link', 'link_doctype', '=', self.reference_doctype], + ['Dynamic Link', 'link_name', '=', self.reference_document] + ]) + + email_ids = list(set([d.email_id for d in res])) + if not email_ids: + frappe.msgprint(_('No contacts linked to document'), alert=True) + else: + self.recipients = ', '.join(email_ids) + + def disable_auto_repeat(self): + frappe.db.set_value('Auto Repeat', self.name, 'disabled', 1) + + def notify_error_to_user(self, error_log): + recipients = get_system_managers(only_name=True) + self.owner + subject = _("Auto Repeat Document Creation Failed") + + form_link = frappe.utils.get_link_to_form(self.reference_doctype, self.reference_document) + auto_repeat_failed_for = _('Auto Repeat failed for {0}').format(form_link) + + error_log_link =frappe.utils.get_link_to_form(error_log.reference_doctype, error_log.reference_document) + error_log_message = _('Check the Error Log for more information: {0}').format(error_log_link) + + frappe.sendmail( + recipients=recipients, + subject=subject, + template="auto_repeat_fail", + args={ + 'auto_repeat_failed_for': auto_repeat_failed_for, + 'error_log_message': error_log_message + }, + header=[subject, 'red'] + ) + + +def get_next_schedule_date(start_date, frequency, repeat_on_day, repeat_on_last_day = False, end_date = None): + month_count = month_map.get(frequency) + if month_count and repeat_on_last_day: + next_date = get_next_date(start_date, month_count, 31) + elif month_count and repeat_on_day: + next_date = get_next_date(start_date, month_count, repeat_on_day) + elif month_count: + next_date = get_next_date(start_date, month_count) + else: + days = 7 if frequency == 'Weekly' else 1 + next_date = add_days(start_date, days) + + return next_date + +def get_next_date(dt, mcount, day=None): + dt = getdate(dt) + dt += relativedelta(months=mcount, day=day) + return dt + +#called through hooks +def make_auto_repeat_entry(): + enqueued_method = 'frappe.automation.doctype.auto_repeat.auto_repeat.create_repeated_entries' + jobs = get_jobs() + + if not jobs or enqueued_method not in jobs[frappe.local.site]: + date = getdate(today()) + data = get_auto_repeat_entries(date) + frappe.enqueue(enqueued_method, data=data) + +def create_repeated_entries(data): + for d in data: + doc = frappe.get_doc('Auto Repeat', d.name) + + current_date = getdate(today()) + schedule_date = getdate(doc.next_schedule_date) + + while schedule_date <= current_date and not doc.disabled: + doc.create_documents() + schedule_date = get_next_schedule_date(schedule_date, doc.frequency, doc.repeat_on_day, doc.repeat_on_last_day, doc.end_date) + + if schedule_date and not doc.disabled: + frappe.db.set_value('Auto Repeat', doc.name, 'next_schedule_date', schedule_date) + +def get_auto_repeat_entries(date=None): + if not date: + date = getdate(today()) + return frappe.db.get_all('Auto Repeat', filters=[ + ['next_schedule_date', '<=', date], + ['status', '=', 'Active'] + ]) + +#called through hooks +def set_auto_repeat_as_completed(): + auto_repeat = frappe.get_all("Auto Repeat", filters = {'status': ['!=', 'Disabled']}) + for entry in auto_repeat: + doc = frappe.get_doc("Auto Repeat", entry.name) + if doc.is_completed(): + doc.status = 'Completed' + doc.save() + +@frappe.whitelist() +def make_auto_repeat(doctype, docname, frequency, start_date, end_date = None): + doc = frappe.new_doc('Auto Repeat') + doc.reference_doctype = doctype + doc.reference_document = docname + doc.frequency = frequency + doc.start_date = start_date + if end_date: + doc.end_date = end_date + doc.save() + return doc + +#method for reference_doctype filter +def get_auto_repeat_doctypes(doctype, txt, searchfield, start, page_len, filters): + res = frappe.db.get_all('Property Setter', { + 'property': 'allow_auto_repeat', + 'value': '1', + }, ['doc_type']) + docs = [r.doc_type for r in res] + + res = frappe.db.get_all('DocType', { + 'allow_auto_repeat': 1, + }, ['name']) + docs += [r.name for r in res] + docs = set(list(docs)) + + return [[d] for d in docs] + +@frappe.whitelist() +def update_reference(docname, reference): + result = "" + try: + frappe.db.set_value("Auto Repeat", docname, "reference_document", reference) + result = "success" + except Exception as e: + result = "error" + raise e + return result + +@frappe.whitelist() +def generate_message_preview(reference_dt, reference_doc, message=None, subject=None): + doc = frappe.get_doc(reference_dt, reference_doc) + subject_preview = _("Please add a subject to your email") + msg_preview = frappe.render_template(message, {'doc': doc}) + if subject: + subject_preview = frappe.render_template(subject, {'doc': doc}) + + return {'message': msg_preview, 'subject': subject_preview} diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat_list.js b/frappe/automation/doctype/auto_repeat/auto_repeat_list.js new file mode 100644 index 0000000000..f906580f7e --- /dev/null +++ b/frappe/automation/doctype/auto_repeat/auto_repeat_list.js @@ -0,0 +1,11 @@ +frappe.listview_settings['Auto Repeat'] = { + add_fields: ["next_schedule_date"], + get_indicator: function(doc) { + var colors = { + "Active": "green", + "Disabled": "red", + "Completed": "blue", + }; + return [__(doc.status), colors[doc.status], "status,=," + doc.status]; + } +}; diff --git a/frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html b/frappe/automation/doctype/auto_repeat/auto_repeat_schedule.html similarity index 74% rename from frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html rename to frappe/automation/doctype/auto_repeat/auto_repeat_schedule.html index 7e579821c5..fb194f44a4 100644 --- a/frappe/desk/doctype/auto_repeat/auto_repeat_schedule.html +++ b/frappe/automation/doctype/auto_repeat/auto_repeat_schedule.html @@ -11,10 +11,9 @@ {% for(var i=0; i < schedule_details.length; i++) { %} {{ schedule_details[i].reference_document }} - {{ schedule_details[i].frequency }} - {{ schedule_details[i].next_scheduled_date }} + {{ __(schedule_details[i].frequency) }} + {{ frappe.datetime.str_to_user(schedule_details[i].next_scheduled_date) }} {% } %} - diff --git a/frappe/desk/doctype/auto_repeat/test_auto_repeat.js b/frappe/automation/doctype/auto_repeat/test_auto_repeat.js similarity index 100% rename from frappe/desk/doctype/auto_repeat/test_auto_repeat.js rename to frappe/automation/doctype/auto_repeat/test_auto_repeat.js diff --git a/frappe/desk/doctype/auto_repeat/test_auto_repeat.py b/frappe/automation/doctype/auto_repeat/test_auto_repeat.py similarity index 77% rename from frappe/desk/doctype/auto_repeat/test_auto_repeat.py rename to frappe/automation/doctype/auto_repeat/test_auto_repeat.py index f0cf9643ce..cb98d4a8fd 100644 --- a/frappe/desk/doctype/auto_repeat/test_auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/test_auto_repeat.py @@ -7,20 +7,19 @@ import unittest import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_field -from frappe.desk.doctype.auto_repeat.auto_repeat import get_auto_repeat_entries, create_repeated_entries, disable_auto_repeat +from frappe.automation.doctype.auto_repeat.auto_repeat import get_auto_repeat_entries, create_repeated_entries from frappe.utils import today, add_days, getdate, add_months def add_custom_fields(): df = dict( fieldname='auto_repeat', label='Auto Repeat', fieldtype='Link', insert_after='sender', - options='Auto Repeat') + options='Auto Repeat', hidden=1, print_hide=1, read_only=1) create_custom_field('ToDo', df) - class TestAutoRepeat(unittest.TestCase): def setUp(self): - if not frappe.db.sql("SELECT `name` FROM `tabCustom Field` WHERE `name`='auto_repeat'"): + if not frappe.db.sql("SELECT `fieldname` FROM `tabCustom Field` WHERE `fieldname`='auto_repeat' and `dt`=%s", "Todo"): add_custom_fields() def test_daily_auto_repeat(self): @@ -29,8 +28,8 @@ class TestAutoRepeat(unittest.TestCase): doc = make_auto_repeat(reference_document=todo.name) self.assertEqual(doc.next_schedule_date, today()) - for data in get_auto_repeat_entries(today()): - create_repeated_entries(data) + data = get_auto_repeat_entries(getdate(today())) + create_repeated_entries(data) frappe.db.commit() todo = frappe.get_doc(doc.reference_doctype, doc.reference_document) @@ -51,8 +50,11 @@ class TestAutoRepeat(unittest.TestCase): dict(doctype='ToDo', description='test recurring todo', assigned_by='Administrator')).insert() self.monthly_auto_repeat('ToDo', todo.name, start_date, end_date) + #test without end_date + todo = frappe.get_doc(dict(doctype='ToDo', description='test recurring todo without end_date', assigned_by='Administrator')).insert() + self.monthly_auto_repeat('ToDo', todo.name, start_date) - def monthly_auto_repeat(self, doctype, docname, start_date, end_date): + def monthly_auto_repeat(self, doctype, docname, start_date, end_date = None): def get_months(start, end): diff = (12 * end.year + end.month) - (12 * start.year + start.month) return diff + 1 @@ -61,10 +63,10 @@ class TestAutoRepeat(unittest.TestCase): reference_doctype=doctype, frequency='Monthly', reference_document=docname, start_date=start_date, end_date=end_date) - disable_auto_repeat(doc) + doc.disable_auto_repeat() - for data in get_auto_repeat_entries(today()): - create_repeated_entries(data) + data = get_auto_repeat_entries(getdate(today())) + create_repeated_entries(data) docnames = frappe.get_all(doc.reference_doctype, {'auto_repeat': doc.name}) self.assertEqual(len(docnames), 1) @@ -72,8 +74,8 @@ class TestAutoRepeat(unittest.TestCase): doc.db_set('disabled', 0) months = get_months(getdate(start_date), getdate(today())) - for data in get_auto_repeat_entries(today()): - create_repeated_entries(data) + data = get_auto_repeat_entries(getdate(today())) + create_repeated_entries(data) docnames = frappe.get_all(doc.reference_doctype, {'auto_repeat': doc.name}) self.assertEqual(len(docnames), months) @@ -84,8 +86,8 @@ class TestAutoRepeat(unittest.TestCase): doc = make_auto_repeat(reference_document=todo.name, notify=1, recipients="test@domain.com", subject="New ToDo", message="A new ToDo has just been created for you") - for data in get_auto_repeat_entries(today()): - create_repeated_entries(data) + data = get_auto_repeat_entries(getdate(today())) + create_repeated_entries(data) frappe.db.commit() new_todo = frappe.db.get_value('ToDo', @@ -100,18 +102,14 @@ def make_auto_repeat(**args): doc = frappe.get_doc({ 'doctype': 'Auto Repeat', 'reference_doctype': args.reference_doctype or 'ToDo', - 'reference_document': args.reference_document or frappe.db.get_value('ToDo', {'docstatus': 1}, 'name'), + 'reference_document': args.reference_document or frappe.db.get_value('ToDo', 'name'), 'frequency': args.frequency or 'Daily', 'start_date': args.start_date or add_days(today(), -1), - 'end_date': args.end_date or add_days(today(), 2), - 'submit_on_creation': args.submit_on_creation or 0, + 'end_date': args.end_date or "", 'notify_by_email': args.notify or 0, 'recipients': args.recipients or "", 'subject': args.subject or "", 'message': args.message or "" }).insert(ignore_permissions=True) - if not args.do_not_submit: - doc.submit() - return doc diff --git a/frappe/config/integrations.py b/frappe/config/integrations.py index ce71051c1d..224fa65d13 100644 --- a/frappe/config/integrations.py +++ b/frappe/config/integrations.py @@ -92,11 +92,6 @@ def get_data(): "name": "Google Settings", "description": _("Google API Settings."), }, - { - "type": "doctype", - "name": "Google Maps Settings", - "description": _("Google Maps integration"), - }, { "type": "doctype", "name": "GCalendar Settings", diff --git a/frappe/config/settings.py b/frappe/config/settings.py index 9577879fc0..2422f2fae2 100644 --- a/frappe/config/settings.py +++ b/frappe/config/settings.py @@ -169,11 +169,27 @@ def get_data(): "name": "Workflow Action", "description": _("Actions for workflow (e.g. Approve, Cancel).") }, + ] + }, + { + "label": _("Automation"), + "icon": "fa fa-random", + "items": [ { "type": "doctype", "name": "Assignment Rule", "description": _("Set up rules for user assignments.") - } + }, + { + "type": "doctype", + "name": "Milestone", + "description": _("Tracks milestones on the lifecycle of a document if it undergoes multiple stages.") + }, + { + "type": "doctype", + "name": "Auto Repeat", + "description": _("Automatically generates recurring documents.") + }, ] }, ] diff --git a/frappe/contacts/doctype/contact/contact.py b/frappe/contacts/doctype/contact/contact.py index 0e0d9aeabc..2018f5469e 100644 --- a/frappe/contacts/doctype/contact/contact.py +++ b/frappe/contacts/doctype/contact/contact.py @@ -161,3 +161,16 @@ def contact_query(doctype, txt, searchfield, start, page_len, filters): 'link_name': link_name, 'link_doctype': link_doctype }) + + +def get_contact_with_phone_number(number): + if not number: return + + contacts = frappe.get_all('Contact', or_filters={ + 'phone': ['like', '%{}'.format(number)], + 'mobile_no': ['like', '%{}'.format(number)] + }, limit=1) + + contact = contacts[0].name if contacts else None + + return contact diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 36377a90f7..9f711c1fd9 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -529,7 +529,7 @@ def update_mins_to_first_communication(parent, communication): if frappe.db.get_all('User', filters={'email': communication.sender, 'user_type': 'System User', 'enabled': 1}, limit=1): first_responded_on = communication.creation - if parent.meta.has_field('first_responded_on'): + if parent.meta.has_field('first_responded_on') and communication.sent_or_received == "Sent": parent.db_set('first_responded_on', first_responded_on) parent.db_set('mins_to_first_response', round(time_diff_in_seconds(first_responded_on, parent.creation) / 60), 2) diff --git a/frappe/core/doctype/doctype/doctype.js b/frappe/core/doctype/doctype/doctype.js index b369aa1b77..9a19185cfc 100644 --- a/frappe/core/doctype/doctype/doctype.js +++ b/frappe/core/doctype/doctype/doctype.js @@ -22,9 +22,15 @@ frappe.ui.form.on('DocType', { } if (!frm.is_new() && !frm.doc.istable) { - frm.add_custom_button(__('Go to {0} List', [frm.doc.name]), () => { - frappe.set_route('List', frm.doc.name, 'List'); - }); + if (frm.doc.issingle) { + frm.add_custom_button(__('Go to {0}', [frm.doc.name]), () => { + frappe.set_route('Form', frm.doc.name); + }); + } else { + frm.add_custom_button(__('Go to {0} List', [frm.doc.name]), () => { + frappe.set_route('List', frm.doc.name, 'List'); + }); + } } if(!frappe.boot.developer_mode && !frm.doc.custom) { diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 24a7a4c287..a2e5c21202 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -37,6 +37,7 @@ "allow_rename", "allow_import", "allow_events_in_timeline", + "allow_auto_repeat", "view_settings", "title_field", "search_fields", @@ -81,6 +82,7 @@ "search_index": 1 }, { + "default": "0", "depends_on": "eval:!doc.istable", "description": "Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended.", "fieldname": "is_submittable", @@ -88,6 +90,7 @@ "label": "Is Submittable" }, { + "default": "0", "description": "Child Tables are shown as a Grid in other DocTypes", "fieldname": "istable", "fieldtype": "Check", @@ -97,6 +100,7 @@ "oldfieldtype": "Check" }, { + "default": "0", "depends_on": "eval:!doc.istable", "description": "Single Types have only one record no tables associated. Values are stored in tabSingles", "fieldname": "issingle", @@ -135,6 +139,7 @@ "label": "Track Changes" }, { + "default": "0", "depends_on": "eval:!doc.istable", "description": "If enabled, the document is marked as seen, the first time a user opens it", "fieldname": "track_seen", @@ -150,11 +155,13 @@ "label": "Track Views" }, { + "default": "0", "fieldname": "custom", "fieldtype": "Check", "label": "Custom?" }, { + "default": "0", "fieldname": "beta", "fieldtype": "Check", "label": "Beta" @@ -236,6 +243,7 @@ "fieldtype": "Column Break" }, { + "default": "0", "fieldname": "hide_toolbar", "fieldtype": "Check", "label": "Hide Sidebar and Menu", @@ -243,6 +251,7 @@ "oldfieldtype": "Check" }, { + "default": "0", "fieldname": "allow_copy", "fieldtype": "Check", "label": "Hide Copy", @@ -250,6 +259,7 @@ "oldfieldtype": "Check" }, { + "default": "0", "fieldname": "allow_rename", "fieldtype": "Check", "label": "Allow Rename", @@ -257,15 +267,23 @@ "oldfieldtype": "Check" }, { + "default": "0", "fieldname": "allow_import", "fieldtype": "Check", "label": "Allow Import (via Data Import Tool)" }, { + "default": "0", "fieldname": "allow_events_in_timeline", "fieldtype": "Check", "label": "Allow events in timeline" }, + { + "default": "0", + "fieldname": "allow_auto_repeat", + "fieldtype": "Check", + "label": "Allow Auto Repeat" + }, { "collapsible": 1, "fieldname": "view_settings", @@ -329,6 +347,13 @@ "label": "Color" }, { + "default": "0", + "fieldname": "show_preview_popup", + "fieldtype": "Check", + "label": "Show Preview Popup" + }, + { + "default": "0", "fieldname": "show_name_in_global_search", "fieldtype": "Check", "label": "Make \"name\" searchable in Global Search" @@ -354,6 +379,7 @@ "options": "Domain" }, { + "default": "0", "fieldname": "read_only", "fieldtype": "Check", "label": "User Cannot Search", @@ -361,6 +387,7 @@ "oldfieldtype": "Check" }, { + "default": "0", "fieldname": "in_create", "fieldtype": "Check", "label": "User Cannot Create", @@ -411,17 +438,11 @@ "fieldtype": "Select", "label": "Database Engine", "options": "InnoDB\nMyISAM" - }, - { - "default": "0", - "fieldname": "show_preview_popup", - "fieldtype": "Check", - "label": "Show Preview Popup" } ], "icon": "fa fa-bolt", "idx": 6, - "modified": "2019-05-16 14:58:33.405381", + "modified": "2019-07-04 23:23:17.174960", "modified_by": "Administrator", "module": "Core", "name": "DocType", @@ -454,4 +475,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index bd528bb4d7..5ec8478d60 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -13,6 +13,7 @@ from frappe.utils import now, cint from frappe.model import no_value_fields, default_fields, data_fieldtypes, table_fields from frappe.model.document import Document from frappe.custom.doctype.property_setter.property_setter import make_property_setter +from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.desk.notifications import delete_notification_count_for from frappe.modules import make_boilerplate, get_doc_path from frappe.database.schema import validate_column_name, validate_column_length @@ -47,7 +48,8 @@ class DocType(Document): - Validate series - Check fieldnames (duplication etc) - Clear permission table for child tables - - Add `amended_from` and `amended_by` if Amendable""" + - Add `amended_from` and `amended_by` if Amendable + - Add custom field `auto_repeat` if Repeatable""" self.check_developer_mode() @@ -76,6 +78,7 @@ class DocType(Document): validate_permissions(self) self.make_amendable() + self.make_repeatable() self.validate_website() if not self.is_new(): @@ -526,6 +529,14 @@ class DocType(Document): "no_copy": 1 }) + def make_repeatable(self): + """If allow_auto_repeat is set, add auto_repeat custom field.""" + if self.allow_auto_repeat: + if not frappe.db.exists('Custom Field', {'fieldname': 'auto_repeat', 'dt': self.name}): + insert_after = self.fields[len(self.fields) - 1].fieldname + df = dict(fieldname='auto_repeat', label='Auto Repeat', fieldtype='Link', options='Auto Repeat', insert_after=insert_after, read_only=1, no_copy=1, print_hide=1) + create_custom_field(self.name, df) + def get_max_idx(self): """Returns the highest `idx`""" max_idx = frappe.db.sql("""select max(idx) from `tabDocField` where parent = %s""", diff --git a/frappe/core/doctype/language/language.json b/frappe/core/doctype/language/language.json index 1907253f81..099b383980 100644 --- a/frappe/core/doctype/language/language.json +++ b/frappe/core/doctype/language/language.json @@ -1,173 +1,72 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 1, - "autoname": "field:language_code", - "beta": 0, - "creation": "2014-08-22 16:12:17.249590", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "language_code", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Language Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "language_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Language Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "flag", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Flag", - "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 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "based_on", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Based On", - "length": 0, - "no_copy": 0, - "options": "Language", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-globe", - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2016-12-29 14:40:33.210645", - "modified_by": "Administrator", - "module": "Core", - "name": "Language", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "language_name", - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "language_name", - "track_changes": 1, - "track_seen": 0 - } \ No newline at end of file + "allow_rename": 1, + "autoname": "field:language_code", + "creation": "2014-08-22 16:12:17.249590", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "language_code", + "language_name", + "flag", + "based_on" + ], + "fields": [ + { + "fieldname": "language_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Language Code", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "language_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Language Name", + "reqd": 1 + }, + { + "fieldname": "flag", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Flag" + }, + { + "fieldname": "based_on", + "fieldtype": "Link", + "label": "Based On", + "options": "Language" + } + ], + "icon": "fa fa-globe", + "modified": "2019-07-19 16:32:12.652550", + "modified_by": "Administrator", + "module": "Core", + "name": "Language", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "read": 1, + "role": "System Manager", + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Guest", + "share": 1 + } + ], + "search_fields": "language_name", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "language_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/core/doctype/report/report.py b/frappe/core/doctype/report/report.py index 1b6957d057..df62f94bcb 100644 --- a/frappe/core/doctype/report/report.py +++ b/frappe/core/doctype/report/report.py @@ -12,6 +12,7 @@ from frappe.modules.export_file import export_to_files from frappe.modules import make_boilerplate from frappe.core.doctype.page.page import delete_custom_role from frappe.core.doctype.custom_role.custom_role import get_custom_allowed_roles +from frappe.desk.reportview import append_totals_row from six import iteritems @@ -76,11 +77,6 @@ class Report(Document): if not self.json: self.json = '{}' - if self.json: - data = json.loads(self.json) - data["add_total_row"] = self.add_total_row - self.json = json.dumps(data) - def export_doc(self): if frappe.flags.in_import: return @@ -178,6 +174,9 @@ class Report(Document): out = out + [list(d) for d in result] + if params.get('add_totals_row'): + out = append_totals_row(out) + if as_dict: data = [] for row in out: diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index 318b31d4a2..ca94afd17d 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -1,2528 +1,651 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, - "beta": 0, "creation": "2014-03-11 14:55:00", - "custom": 0, "description": "Represents a User in the system.", - "docstatus": 0, "doctype": "DocType", - "document_type": "", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "sb0_5", + "enabled", + "section_break_3", + "email", + "first_name", + "middle_name", + "last_name", + "full_name", + "send_welcome_email", + "unsubscribed", + "column_break0", + "username", + "language", + "time_zone", + "user_image", + "sb1", + "role_profile_name", + "roles_html", + "roles", + "short_bio", + "gender", + "phone", + "mobile_no", + "birth_date", + "location", + "banner_image", + "column_break_22", + "interest", + "bio", + "mute_sounds", + "change_password", + "new_password", + "send_password_update_notification", + "logout_all_sessions", + "reset_password_key", + "last_password_reset_date", + "redirect_url", + "document_follow_notifications_section", + "document_follow_notify", + "document_follow_frequency", + "email_settings", + "thread_notify", + "send_me_a_copy", + "allowed_in_mentions", + "email_signature", + "email_inbox", + "user_emails", + "background", + "background_image", + "background_style", + "sb_allow_modules", + "modules_html", + "block_modules", + "home_settings", + "sb2", + "defaults", + "sb3", + "simultaneous_sessions", + "user_type", + "login_after", + "login_before", + "restrict_ip", + "bypass_restrict_ip_check_if_2fa_enabled", + "column_break1", + "last_login", + "last_ip", + "last_active", + "last_known_versions", + "third_party_authentication", + "social_logins", + "api_access", + "api_key", + "generate_keys", + "column_break_65", + "api_secret" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sb0_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, - "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, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "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, "oldfieldname": "enabled", "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "enabled", "fieldname": "section_break_3", - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "email", "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", - "length": 0, "no_copy": 1, "oldfieldname": "email", "oldfieldtype": "Data", "options": "Email", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "first_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "First Name", - "length": 0, - "no_copy": 0, "oldfieldname": "first_name", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "middle_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Middle Name (Optional)", - "length": 0, - "no_copy": 0, "oldfieldname": "middle_name", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Data" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "fieldname": "last_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Last Name", - "length": 0, - "no_copy": 0, "oldfieldname": "last_name", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Data" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "full_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, "in_standard_filter": 1, "label": "Full Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "default": "1", "depends_on": "eval:doc.__islocal", "fieldname": "send_welcome_email", "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": "Send Welcome Email", - "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 + "label": "Send Welcome Email" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "unsubscribed", "fieldtype": "Check", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Unsubscribed", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "no_copy": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break0", "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, "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50%" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "username", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, "in_standard_filter": 1, "label": "Username", - "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": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "language", "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": "Language", - "length": 0, - "no_copy": 0, - "options": "Language", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Language" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "time_zone", "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": "Timezone", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Timezone" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Get your globally recognized avatar from Gravatar.com", "fieldname": "user_image", "fieldtype": "Attach Image", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "User Image", - "length": 0, "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "enabled", - "description": "", "fieldname": "sb1", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Roles", - "length": 0, - "no_copy": 0, "permlevel": 1, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "role_profile_name", "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": "Role Profile", - "length": 0, - "no_copy": 0, - "options": "Role Profile", - "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 + "options": "Role Profile" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "roles_html", "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": "Roles HTML", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "roles", "fieldtype": "Table", "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": "Roles Assigned", - "length": 0, - "no_copy": 0, "options": "Has Role", "permlevel": 1, - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "depends_on": "enabled", "fieldname": "short_bio", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "More Information" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "gender", "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": "Gender", - "length": 0, - "no_copy": 0, "oldfieldname": "gender", "oldfieldtype": "Select", - "options": "Gender", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Gender" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "phone", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Phone", - "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 + "label": "Phone" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "mobile_no", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Mobile No", - "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": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "birth_date", "fieldtype": "Date", - "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": "Birth Date", - "length": 0, "no_copy": 1, "oldfieldname": "birth_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "location", "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": "Location", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "no_copy": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "banner_image", "fieldtype": "Attach Image", - "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": "Banner Image", - "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 + "label": "Banner Image" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_22", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "interest", "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": "Interest", - "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 + "label": "Interests" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "bio", "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": "Bio", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "no_copy": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "mute_sounds", "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": "Mute Sounds", - "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 + "label": "Mute Sounds" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "depends_on": "eval:doc.enabled && (!doc.__islocal || !cint(doc.send_welcome_email))", "fieldname": "change_password", "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": "Change Password", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Change Password" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "new_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": "Set New Password", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "no_copy": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:!doc.__islocal", "fieldname": "send_password_update_notification", "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": "Send Password Update Notification", - "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 + "label": "Send Password Update Notification" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "fieldname": "logout_all_sessions", "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": "Logout from all devices while changing 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 + "label": "Logout from all devices while changing Password" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "reset_password_key", "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": "Reset Password Key", - "length": 0, "no_copy": 1, - "permlevel": 0, "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 + "read_only": 1 }, { - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "redirect_url", "fieldtype": "Small Text", "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": "Redirect 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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Redirect URL" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, - "depends_on": "", "fieldname": "document_follow_notifications_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Document Follow", - "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 + "label": "Document Follow" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "document_follow_notify", "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": "Send Notifications for documents followed by me", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Send Notifications for documents followed by me" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Daily", "depends_on": "eval:(doc.document_follow_notify== 1)", "fieldname": "document_follow_frequency", "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": "Frequency", - "length": 0, - "no_copy": 0, - "options": "Hourly\nDaily\nWeekly", - "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 + "options": "Hourly\nDaily\nWeekly" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "depends_on": "enabled", "fieldname": "email_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": "Email Settings", - "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 + "label": "Email Settings" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "fieldname": "thread_notify", "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": "Send Notifications for Email threads", - "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 + "label": "Send Notifications for Email threads" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "send_me_a_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": "Send Me A Copy of Outgoing Emails", - "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 + "label": "Send Me A Copy of Outgoing Emails" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "fieldname": "allowed_in_mentions", "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": "Allowed In Mentions", - "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 + "label": "Allowed In Mentions" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "email_signature", "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": "Email Signature", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "no_copy": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "email_inbox", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Email Inbox", - "length": 0, - "no_copy": 0, - "permlevel": 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 + "label": "Email Inbox" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "user_emails", "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": "User Emails", - "length": 0, - "no_copy": 0, "options": "User Email", - "permlevel": 1, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "permlevel": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "depends_on": "enabled", "fieldname": "background", "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": "Desktop Background", - "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 + "label": "Desktop Background" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "background_image", "fieldtype": "Attach", - "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": "Background Image", - "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 + "label": "Background Image" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "background_style", "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": "Background Style", - "length": 0, - "no_copy": 0, - "options": "Fill Screen\nTile", - "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 + "options": "Fill Screen\nTile" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, - "default": "", - "description": "", "fieldname": "sb_allow_modules", "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": "Allow Modules", - "length": 0, - "no_copy": 0, - "permlevel": 1, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "permlevel": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "modules_html", "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": "Modules HTML", - "length": 0, - "no_copy": 0, - "permlevel": 1, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "permlevel": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "block_modules", "fieldtype": "Table", "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": "Block Modules", - "length": 0, - "no_copy": 0, "options": "Block Module", - "permlevel": 1, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "permlevel": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", "fieldname": "home_settings", "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": "Home Settings", - "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 + "label": "Home Settings" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "These values will be automatically updated in transactions and also will be useful to restrict permissions for this user on transactions containing these values.", "fieldname": "sb2", "fieldtype": "Section Break", "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": "Defaults", - "length": 0, - "no_copy": 0, "oldfieldtype": "Column Break", "permlevel": 1, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50%", "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50%" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Enter default value fields (keys) and values. If you add multiple values for a field, the first one will be picked. These defaults are also used to set \"match\" permission rules. To see list of fields, go to \"Customize Form\".", "fieldname": "defaults", "fieldtype": "Table", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "User Defaults", - "length": 0, "no_copy": 1, - "options": "DefaultValue", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "DefaultValue" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "depends_on": "enabled", "fieldname": "sb3", "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": "Security Settings", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "fieldname": "simultaneous_sessions", "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": "Simultaneous Sessions", - "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 + "label": "Simultaneous Sessions" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "System User", "description": "If the user has any role checked, then the user becomes a \"System User\". \"System User\" has access to the desktop", "fieldname": "user_type", "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": "User Type", - "length": 0, - "no_copy": 0, "oldfieldname": "user_type", "oldfieldtype": "Select", "options": "System User\nWebsite User", "permlevel": 1, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Allow user to login only after this hour (0-24)", "fieldname": "login_after", "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": "Login After", - "length": 0, - "no_copy": 0, - "permlevel": 1, - "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 + "permlevel": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Allow user to login only before this hour (0-24)", "fieldname": "login_before", "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": "Login Before", - "length": 0, - "no_copy": 0, - "permlevel": 1, - "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 + "permlevel": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Restrict user from this IP address only. Multiple IP addresses can be added by separating with commas. Also accepts partial IP addresses like (111.111.111)", "fieldname": "restrict_ip", "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": "Restrict IP", - "length": 0, - "no_copy": 0, - "permlevel": 1, - "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 + "permlevel": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "eval:doc.restrict_ip && doc.restrict_ip.length", "description": "If enabled, user can login from any IP Address using Two Factor Auth, this can also be set for all users in System Settings", "fieldname": "bypass_restrict_ip_check_if_2fa_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": "Bypass restricted IP Address check If Two Factor Auth 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 + "label": "Bypass restricted IP Address check If Two Factor Auth Enabled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break1", "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, "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50%" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "last_login", "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Last Login", - "length": 0, "no_copy": 1, "oldfieldname": "last_login", "oldfieldtype": "Read Only", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "last_ip", "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Last IP", - "length": 0, "no_copy": 1, "oldfieldname": "last_ip", "oldfieldtype": "Read Only", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "last_active", "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Last Active", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Stores the JSON of last known versions of various installed apps. It is used to show release notes.", "fieldname": "last_known_versions", "fieldtype": "Text", "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 Known Versions", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "depends_on": "enabled", "fieldname": "third_party_authentication", "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": "Third Party Authentication", - "length": 0, - "no_copy": 0, - "permlevel": 1, - "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 + "permlevel": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "social_logins", "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": "Social Logins", - "length": 0, - "no_copy": 0, - "options": "User Social Login", - "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 + "options": "User Social Login" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "api_access", "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": "Api Access", - "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 + "label": "Api Access" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "API Key cannot be regenerated", "fieldname": "api_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": "API Key", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "generate_keys", "fieldtype": "Button", - "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": "Generate Keys", - "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 + "label": "Generate Keys" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_65", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "api_secret", "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": "API Secret", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-user", "idx": 413, "image_field": "user_image", - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, "max_attachments": 5, - "modified": "2019-04-15 20:25:02.022893", - "modified_by": "Administrator", + "modified": "2019-07-12 11:35:17.469656", + "modified_by": "prasadsherlock@gmail.com", "module": "Core", "name": "User", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, "write": 1 } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "search_fields": "full_name", "show_name_in_global_search": 1, + "sort_field": "modified", "sort_order": "DESC", "title_field": "full_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json index 5f288fe99c..d4b6bc6352 100644 --- a/frappe/custom/doctype/customize_form/customize_form.json +++ b/frappe/custom/doctype/customize_form/customize_form.json @@ -4,6 +4,7 @@ "doctype": "DocType", "document_type": "Document", "editable_grid": 1, + "engine": "InnoDB", "field_order": [ "doc_type", "properties", @@ -16,6 +17,7 @@ "quick_entry", "track_changes", "track_views", + "allow_auto_repeat", "image_view", "column_break_5", "title_field", @@ -59,17 +61,20 @@ "label": "Max Attachments" }, { + "default": "0", "fieldname": "allow_copy", "fieldtype": "Check", "label": "Hide Copy" }, { + "default": "0", "fieldname": "istable", "fieldtype": "Check", "label": "Is Table", "read_only": 1 }, { + "default": "0", "depends_on": "istable", "fieldname": "editable_grid", "fieldtype": "Check", @@ -82,11 +87,13 @@ "label": "Quick Entry" }, { + "default": "0", "fieldname": "track_changes", "fieldtype": "Check", "label": "Track Changes" }, { + "default": "0", "depends_on": "eval: doc.image_field", "fieldname": "image_view", "fieldtype": "Check", @@ -150,16 +157,23 @@ "options": "Customize Form Field" }, { + "default": "0", "fieldname": "track_views", "fieldtype": "Check", "label": "Track Views" + }, + { + "default": "0", + "fieldname": "allow_auto_repeat", + "fieldtype": "Check", + "label": "Allow Auto Repeat" } ], "hide_toolbar": 1, "icon": "fa fa-glass", "idx": 1, "issingle": 1, - "modified": "2019-05-13 18:54:40.610862", + "modified": "2019-07-01 22:50:50.372465", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form", @@ -177,6 +191,7 @@ ], "quick_entry": 1, "search_fields": "doc_type", + "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 } \ No newline at end of file diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index 78ee82efcb..425191c4eb 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -13,6 +13,7 @@ from frappe.utils import cint from frappe.model.document import Document from frappe.model import no_value_fields, core_doctypes_list from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype +from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.model.docfield import supports_translation doctype_properties = { @@ -29,6 +30,7 @@ doctype_properties = { 'max_attachments': 'Int', 'track_changes': 'Check', 'track_views': 'Check', + 'allow_auto_repeat': 'Check' } docfield_properties = { @@ -65,6 +67,7 @@ docfield_properties = { 'columns': 'Int', 'remember_last_selected_value': 'Check', 'allow_bulk_edit': 'Check', + 'auto_repeat': 'Link' } allowed_fieldtype_change = (('Currency', 'Float', 'Percent'), ('Small Text', 'Data'), @@ -108,6 +111,13 @@ class CustomizeForm(Document): translation = self.get_name_translation() self.label = translation.target_name if translation else '' + #If allow_auto_repeat is set, add auto_repeat custom field. + if self.allow_auto_repeat: + if not frappe.db.exists('Custom Field', {'fieldname': 'auto_repeat', 'dt': self.doc_type}): + insert_after = self.fields[len(self.fields) - 1].fieldname + df = dict(fieldname='auto_repeat', label='Auto Repeat', fieldtype='Link', options='Auto Repeat', insert_after=insert_after, read_only=1, no_copy=1, print_hide=1) + create_custom_field(self.doc_type, df) + # NOTE doc is sent to clientside by run_method def get_name_translation(self): diff --git a/frappe/database/database.py b/frappe/database/database.py index 7650af43f9..6702ce2a5e 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -553,6 +553,10 @@ class Database(object): val = val[0][0] if val else None df = frappe.get_meta(doctype).get_field(fieldname) + + if not df: + frappe.throw(_('Invalid field name: {0}').format(frappe.bold(fieldname)), self.InvalidColumnName) + if df.fieldtype in frappe.model.numeric_fieldtypes: val = cint(val) diff --git a/frappe/database/db_manager.py b/frappe/database/db_manager.py index 0954657b28..0447f97273 100644 --- a/frappe/database/db_manager.py +++ b/frappe/database/db_manager.py @@ -48,7 +48,10 @@ class DbManager: if not host: host = self.get_current_host() - self.db.sql("GRANT ALL PRIVILEGES ON `%s`.* TO '%s'@'%s';" % (target, user, host)) + if frappe.conf.get('rds_db', 0) == 1: + self.db.sql("GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES, CREATE VIEW, EVENT, TRIGGER, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EXECUTE ON `%s`.* TO '%s'@'%s';" % (target, user, host)) + else: + self.db.sql("GRANT ALL PRIVILEGES ON `%s`.* TO '%s'@'%s';" % (target, user, host)) def flush_privileges(self): self.db.sql("FLUSH PRIVILEGES") diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index 2e3938859c..610f15e011 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -111,13 +111,10 @@ class PostgresDatabase(Database): def format_date(self, date): if not date: - return '0001-01-01::DATE' + return '0001-01-01' - if isinstance(date, frappe.string_types): - if ':' not in date: - date = date + '::DATE' - else: - date = date.strftime('%Y-%m-%d') + '::DATE' + if not isinstance(date, frappe.string_types): + date = date.strftime('%Y-%m-%d') return date diff --git a/frappe/desk/doctype/auto_repeat/auto_repeat.js b/frappe/desk/doctype/auto_repeat/auto_repeat.js deleted file mode 100644 index ca96cb2b45..0000000000 --- a/frappe/desk/doctype/auto_repeat/auto_repeat.js +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies and contributors -// For license information, please see license.txt -frappe.provide("frappe.auto_repeat"); - -frappe.ui.form.on('Auto Repeat', { - setup: function(frm) { - frm.fields_dict['reference_doctype'].get_query = function() { - return { - query: "frappe.desk.doctype.auto_repeat.auto_repeat.auto_repeat_doctype_query" - }; - }; - - frm.fields_dict['reference_document'].get_query = function() { - return { - filters: { - "docstatus": 1, - "auto_repeat": '' - } - }; - }; - - frm.fields_dict['print_format'].get_query = function() { - return { - filters: { - "doc_type": frm.doc.reference_doctype - } - }; - }; - }, - - refresh: function(frm) { - - if(frm.doc.docstatus == 1) { - - let label = __('View {0}', [__(frm.doc.reference_doctype)]); - frm.add_custom_button(__(label), - function() { - frappe.route_options = { - "auto_repeat": frm.doc.name, - }; - frappe.set_route("List", frm.doc.reference_doctype); - } - ); - - if(frm.doc.status != 'Stopped') { - frm.add_custom_button(__("Stop"), - function() { - frm.events.stop_resume_auto_repeat(frm, "Stopped"); - } - ); - } - - if(frm.doc.status == 'Stopped') { - frm.add_custom_button(__("Restart"), - function() { - frm.events.stop_resume_auto_repeat(frm, "Resumed"); - } - ); - } - } - - frm.toggle_display('auto_repeat_schedule', !in_list(['Stopped', 'Cancelled'], frm.doc.status)); - if(frm.doc.start_date && !in_list(['Stopped', 'Cancelled'], frm.doc.status)){ - frappe.auto_repeat.render_schedule(frm); - } - - }, - - stop_resume_auto_repeat: function(frm, status) { - frappe.call({ - method: "frappe.desk.doctype.auto_repeat.auto_repeat.stop_resume_auto_repeat", - args: { - auto_repeat: frm.doc.name, - status: status - }, - callback: function(r) { - if(r.message) { - frm.set_value("status", r.message); - frm.reload_doc(); - } - } - }); - }, - - template: function(frm) { - if (frm.doc.template) { - frappe.model.with_doc("Email Template", frm.doc.template, () => { - let email_template = frappe.get_doc("Email Template", frm.doc.template); - frm.set_value("subject", email_template.subject); - frm.set_value("message", email_template.response); - frm.refresh_field("subject"); - frm.refresh_field("message"); - }); - } - }, - - get_contacts: function(frm) { - frappe.call({ - method: "frappe.desk.doctype.auto_repeat.auto_repeat.get_contacts", - args: { - reference_doctype: frm.doc.reference_doctype, - reference_name: frm.doc.reference_document - }, - callback: function(r) { - if(r.message) { - frm.set_value("recipients", r.message.join()); - frm.refresh_field("recipients"); - } - } - }); - }, - - preview_message: function(frm) { - if (frm.doc.message) { - frappe.call({ - method: "frappe.desk.doctype.auto_repeat.auto_repeat.generate_message_preview", - args: { - reference_dt: frm.doc.reference_doctype, - reference_doc: frm.doc.reference_document, - subject: frm.doc.subject, - message: frm.doc.message - }, - callback: function(r) { - if(r.message) { - frappe.msgprint(r.message.message, r.message.subject) - } - } - }); - } else { - frappe.msgprint(__("Please setup a message first"), __("Message not setup")) - } - } -}); - -frappe.auto_repeat.render_schedule = function(frm) { - frappe.call({ - method: "get_auto_repeat_schedule", - doc: frm.doc - }).done((r) => { - var wrapper = $(frm.fields_dict["auto_repeat_schedule"].wrapper); - wrapper.html(frappe.render_template ("auto_repeat_schedule", {"schedule_details" : r.message || []} )); - frm.refresh_fields(); - }); -}; \ No newline at end of file diff --git a/frappe/desk/doctype/auto_repeat/auto_repeat.json b/frappe/desk/doctype/auto_repeat/auto_repeat.json deleted file mode 100644 index 701f692672..0000000000 --- a/frappe/desk/doctype/auto_repeat/auto_repeat.json +++ /dev/null @@ -1,1081 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "naming_series:", - "beta": 0, - "creation": "2018-03-09 11:22:31.192349", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "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, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "naming_series", - "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": "Series", - "length": 0, - "no_copy": 0, - "options": "SUB-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_doctype", - "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": "Reference Doctype", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 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, - "fieldname": "reference_document", - "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 Document", - "length": 0, - "no_copy": 1, - "options": "reference_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 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, - "fieldname": "reference_party_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 Party Doctype", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_party", - "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 Party", - "length": 0, - "no_copy": 0, - "options": "reference_party_doctype", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "start_date", - "fieldtype": "Date", - "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": "Start Date", - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "end_date", - "fieldtype": "Date", - "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": "End Date", - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "submit_on_creation", - "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": "Submit on Creation", - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "disabled", - "fieldtype": "Check", - "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": "Disabled", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_10", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "frequency", - "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": 0, - "label": "Frequency", - "length": 0, - "no_copy": 0, - "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nHalf-yearly\nYearly", - "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, - "fieldname": "column_break_12", - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: in_list([\"Monthly\", \"Quarterly\", \"Yearly\"], doc.frequency)", - "fieldname": "repeat_on_day", - "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": "Repeat on Day", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "next_schedule_date", - "fieldtype": "Date", - "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": "Next Schedule 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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "section_break_13", - "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": "Auto Repeat Schedule", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: !cur_frm.doc.__islocal", - "fieldname": "auto_repeat_schedule", - "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": "Auto Repeat Schedule", - "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": 1, - "columns": 0, - "fieldname": "notification", - "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": "Notification", - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "notify_by_email", - "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": "Notify by Email", - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "notify_by_email", - "fieldname": "recipients", - "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": "Recipients", - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.notify_by_email", - "fieldname": "get_contacts", - "fieldtype": "Button", - "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": "Get Contacts", - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.notify_by_email", - "fieldname": "template", - "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": "Template", - "length": 0, - "no_copy": 0, - "options": "Email Template", - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.notify_by_email", - "description": "To add dynamic subject, use jinja tags like\n\n
New {{ doc.doctype }} #{{ doc.name }}
", - "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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Please find attached {{ doc.doctype }} #{{ doc.name }}", - "depends_on": "eval: doc.notify_by_email", - "fieldname": "message", - "fieldtype": "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": "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.notify_by_email", - "fieldname": "preview_message", - "fieldtype": "Button", - "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": "Preview 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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "notify_by_email", - "fieldname": "print_format", - "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": "Print Format", - "length": 0, - "no_copy": 0, - "options": "Print Format", - "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, - "depends_on": "eval: !doc.__islocal", - "fieldname": "section_break_16", - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 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": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "\nDraft\nStopped\nSubmitted\nCancelled\nCompleted", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "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": "Amended From ", - "length": 0, - "no_copy": 1, - "options": "Auto Repeat", - "permlevel": 0, - "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 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-10-12 18:59:22.463211", - "modified_by": "Administrator", - "module": "Desk", - "name": "Auto Repeat", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 1, - "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": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "reference_document", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "reference_document", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/frappe/desk/doctype/auto_repeat/auto_repeat.py b/frappe/desk/doctype/auto_repeat/auto_repeat.py deleted file mode 100644 index 0caaf69c3a..0000000000 --- a/frappe/desk/doctype/auto_repeat/auto_repeat.py +++ /dev/null @@ -1,418 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -import calendar -from frappe import _ -from frappe.desk.form import assign_to -from frappe.utils.jinja import validate_template -from dateutil.relativedelta import relativedelta -from frappe.utils.user import get_system_managers -from frappe.utils import cstr, getdate, split_emails, add_days, today, get_last_day, get_first_day -from frappe.model.document import Document -from frappe.core.doctype.communication.email import make -from frappe.utils.background_jobs import get_jobs - -month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12} - - -class AutoRepeat(Document): - def validate(self): - self.update_status() - self.validate_reference_doctype() - self.validate_dates() - self.validate_next_schedule_date() - self.validate_email_id() - self.link_party() - - validate_template(self.subject or "") - validate_template(self.message or "") - - def before_submit(self): - start_date_copy = self.start_date - today_copy = add_days(today(), -1) - - if start_date_copy <= today_copy: - start_date_copy = today_copy - - if not self.next_schedule_date: - self.next_schedule_date = get_next_schedule_date( - start_date_copy, self.frequency, self.repeat_on_day) - - def on_submit(self): - self.update_auto_repeat_id() - - def on_cancel(self): - self.update_status() - - def on_update_after_submit(self): - self.validate_dates() - self.set_next_schedule_date() - - def before_cancel(self): - self.unlink_auto_repeat_id() - self.next_schedule_date = None - - def unlink_auto_repeat_id(self): - frappe.db.sql( - "update `tab{0}` set auto_repeat = null where auto_repeat=%s".format(self.reference_doctype), self.name) - - def validate_reference_doctype(self): - if not frappe.get_meta(self.reference_doctype).has_field('auto_repeat'): - frappe.throw(_("Add custom field Auto Repeat in the doctype {0}").format(self.reference_doctype)) - - def validate_dates(self): - if self.end_date and getdate(self.start_date) > getdate(self.end_date): - frappe.throw(_("End date must be greater than start date")) - - def validate_next_schedule_date(self): - if self.repeat_on_day and self.next_schedule_date: - next_date = getdate(self.next_schedule_date) - if next_date.day != self.repeat_on_day: - # if the repeat day is the last day of the month (31) - # and the current month does not have as many days, - # then the last day of the current month is a valid date - lastday = calendar.monthrange(next_date.year, next_date.month)[1] - if self.repeat_on_day < lastday: - # the specified day of the month is not same as the day specified - # or the last day of the month - frappe.throw(_("Next Date's day and Repeat on Day of Month must be equal")) - - def validate_email_id(self): - if self.notify_by_email: - if self.recipients: - email_list = split_emails(self.recipients.replace("\n", "")) - - from frappe.utils import validate_email_address - for email in email_list: - if not validate_email_address(email): - frappe.throw(_("{0} is an invalid email address in 'Recipients'").format(email)) - else: - frappe.throw(_("'Recipients' not specified")) - - def set_next_schedule_date(self): - if self.repeat_on_day: - self.next_schedule_date = get_next_date(self.next_schedule_date, 0, self.repeat_on_day) - - def update_auto_repeat_id(self): - frappe.db.set_value(self.reference_doctype, self.reference_document, "auto_repeat", self.name) - - def update_status(self, status=None): - self.status = { - '0': 'Draft', - '1': 'Submitted', - '2': 'Cancelled' - }[cstr(self.docstatus or 0)] - - if status and status != 'Resumed': - self.status = status - - if self.docstatus == 2: - self.db_set("status", self.status) - - def get_auto_repeat_schedule(self): - schedule_details = [] - start_date_copy = getdate(self.start_date) - end_date_copy = getdate(self.end_date) - today_copy = frappe.utils.datetime.date.today() - - if start_date_copy < today_copy: - start_date_copy = today_copy - - if not self.end_date: - days = 60 if self.frequency in ['Daily', 'Weekly'] else 365 - end_date_copy = add_days(today_copy, days) - - start_date_copy = get_next_schedule_date(start_date_copy, self.frequency, self.repeat_on_day) - while (getdate(start_date_copy) < getdate(end_date_copy)): - row = { - "reference_document" : self.reference_document, - "frequency" : self.frequency, - "next_scheduled_date" : start_date_copy - } - schedule_details.append(row) - start_date_copy = get_next_schedule_date(start_date_copy, self.frequency, self.repeat_on_day) - - return schedule_details - - def link_party(self): - reference = frappe.get_meta(self.reference_doctype) - for field in reference.fields: - if field.options in ['Customer', 'Supplier', 'Employee']: - self.reference_party_doctype = field.options - self.reference_party = frappe.db.get_value(self.reference_doctype, self.reference_document, field.fieldname) - break - -def get_next_schedule_date(start_date, frequency, repeat_on_day): - mcount = month_map.get(frequency) - if mcount: - next_date = get_next_date(start_date, mcount, repeat_on_day) - else: - days = 7 if frequency == 'Weekly' else 1 - next_date = add_days(start_date, days) - return next_date - -def make_auto_repeat_entry(date=None): - enqueued_method = 'frappe.desk.doctype.auto_repeat.auto_repeat.create_repeated_entries' - jobs = get_jobs() - - if not jobs or enqueued_method not in jobs[frappe.local.site]: - date = date or today() - for data in get_auto_repeat_entries(date): - frappe.enqueue(enqueued_method, data=data) - -def create_repeated_entries(data): - schedule_date = getdate(data.next_schedule_date) - while schedule_date <= getdate(today()) and not frappe.db.get_value('Auto Repeat', data.name, 'disabled'): - create_documents(data, schedule_date) - schedule_date = get_next_schedule_date(schedule_date, data.frequency, data.repeat_on_day) - - if schedule_date and not frappe.db.get_value('Auto Repeat', data.name, 'disabled'): - frappe.db.set_value('Auto Repeat', data.name, 'next_schedule_date', schedule_date) - frappe.db.commit() - -def get_auto_repeat_entries(date): - return frappe.db.sql(""" select * from `tabAuto Repeat` - where docstatus = 1 and next_schedule_date <=%s - and reference_document is not null and reference_document != '' - and next_schedule_date <= ifnull(end_date, '2199-12-31') - and disabled = 0 and status != 'Stopped' """, (date), as_dict=1) - -def create_documents(data, schedule_date): - try: - doc = make_new_document(data, schedule_date) - if data.notify_by_email and data.recipients: - print_format = data.print_format or "Standard" - send_notification(doc, data, print_format=print_format) - - frappe.db.commit() - except Exception: - frappe.db.rollback() - frappe.db.begin() - frappe.log_error(frappe.get_traceback(), _("Recurring document creation failure")) - disable_auto_repeat(data) - frappe.db.commit() - if data.reference_document and not frappe.flags.in_test: - notify_error_to_user(data) - -def disable_auto_repeat(data): - auto_repeat = frappe.get_doc('Auto Repeat', data.name) - auto_repeat.db_set('disabled', 1) - -def notify_error_to_user(data): - party = '' - party_type = '' - - if data.reference_doctype in ['Sales Order', 'Sales Invoice', 'Delivery Note']: - party_type = 'customer' - elif data.reference_doctype in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: - party_type = 'supplier' - - if party_type: - party = frappe.db.get_value(data.reference_doctype, data.reference_document, party_type) - - notify_errors(data.reference_document, data.reference_doctype, party, data.owner, data.name) - -def make_new_document(args, schedule_date): - doc = frappe.get_doc(args.reference_doctype, args.reference_document) - new_doc = frappe.copy_doc(doc, ignore_no_copy=False) - update_doc(new_doc, doc, args, schedule_date) - new_doc.insert(ignore_permissions=True) - - if args.submit_on_creation: - new_doc.submit() - - return new_doc - -def update_doc(new_document, reference_doc, args, schedule_date): - new_document.docstatus = 0 - if new_document.meta.get_field('set_posting_time'): - new_document.set('set_posting_time', 1) - - mcount = month_map.get(args.frequency) - - if new_document.meta.get_field('auto_repeat'): - new_document.set('auto_repeat', args.name) - - for fieldname in ['naming_series', 'ignore_pricing_rule', 'posting_time', - 'select_print_heading', 'remarks', 'owner']: - if new_document.meta.get_field(fieldname): - new_document.set(fieldname, reference_doc.get(fieldname)) - - # copy item fields - if new_document.meta.get_field('items'): - for i, item in enumerate(new_document.items): - for fieldname in ("page_break",): - item.set(fieldname, reference_doc.items[i].get(fieldname)) - - for data in new_document.meta.fields: - if data.fieldtype == 'Date' and data.reqd: - new_document.set(data.fieldname, schedule_date) - - set_auto_repeat_period(args, mcount, new_document) - - new_document.run_method("on_recurring", reference_doc=reference_doc, auto_repeat_doc=args) - -def set_auto_repeat_period(args, mcount, new_document): - if mcount and new_document.meta.get_field('from_date') and new_document.meta.get_field('to_date'): - last_ref_doc = frappe.db.sql(""" - select name, from_date, to_date - from `tab{0}` - where auto_repeat=%s and docstatus < 2 - order by creation desc - limit 1 - """.format(args.reference_doctype), args.name, as_dict=1) - - if not last_ref_doc: - return - - from_date = get_next_date(last_ref_doc[0].from_date, mcount) - - if (cstr(get_first_day(last_ref_doc[0].from_date)) == cstr(last_ref_doc[0].from_date)) and \ - (cstr(get_last_day(last_ref_doc[0].to_date)) == cstr(last_ref_doc[0].to_date)): - to_date = get_last_day(get_next_date(last_ref_doc[0].to_date, mcount)) - else: - to_date = get_next_date(last_ref_doc[0].to_date, mcount) - - new_document.set('from_date', from_date) - new_document.set('to_date', to_date) - -def get_next_date(dt, mcount, day=None): - dt = getdate(dt) - dt += relativedelta(months=mcount, day=day) - - return dt - -def send_notification(new_rv, auto_repeat_doc, print_format='Standard'): - """Notify concerned persons about recurring document generation""" - print_format = print_format - subject = auto_repeat_doc.subject or '' - message = auto_repeat_doc.message or '' - - if not auto_repeat_doc.subject: - subject = _("New {0}: #{1}").format(new_rv.doctype, new_rv.name) - elif "{" in auto_repeat_doc.subject: - subject = frappe.render_template(auto_repeat_doc.subject, {'doc': new_rv}) - - if not auto_repeat_doc.message: - message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name) - elif "{" in auto_repeat_doc.message: - message = frappe.render_template(auto_repeat_doc.message, {'doc': new_rv}) - - attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, - file_name=new_rv.name, print_format=print_format)] - - make(doctype=new_rv.doctype, name=new_rv.name, recipients=auto_repeat_doc.recipients, - subject=subject, content=message, attachments=attachments, send_email=1) - -def notify_errors(doc, doctype, party, owner, name): - recipients = get_system_managers(only_name=True) - frappe.sendmail(recipients + [frappe.db.get_value("User", owner, "email")], - subject=_("[Urgent] Error while creating recurring %s for %s" % (doctype, doc)), - message=frappe.get_template("templates/emails/recurring_document_failed.html").render({ - "type": _(doctype), - "name": doc, - "party": party or "", - "auto_repeat": name - })) - try: - assign_task_to_owner(name, _("Recurring Documents Failed"), recipients) - except Exception: - frappe.log_error(frappe.get_traceback(), _("Recurring Documents Failed")) - -def assign_task_to_owner(name, msg, users): - for d in users: - args = { - 'doctype': 'Auto Repeat', - 'assign_to': d, - 'name': name, - 'description': msg, - 'priority': 'High' - } - assign_to.add(args) - -@frappe.whitelist() -def make_auto_repeat(doctype, docname): - doc = frappe.new_doc('Auto Repeat') - - reference_doc = frappe.get_doc(doctype, docname) - doc.reference_doctype = doctype - doc.reference_document = docname - doc.start_date = reference_doc.get('posting_date') or reference_doc.get('transaction_date') - return doc - -@frappe.whitelist() -def stop_resume_auto_repeat(auto_repeat, status): - doc = frappe.get_doc('Auto Repeat', auto_repeat) - frappe.msgprint(_("Auto Repeat has been {0}").format(status)) - if status == 'Resumed': - doc.next_schedule_date = get_next_schedule_date(today(), - doc.frequency, doc.repeat_on_day) - - doc.update_status(status) - doc.save() - - return doc.status - -def auto_repeat_doctype_query(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.sql("""select parent from `tabDocField` - where fieldname = 'auto_repeat' - and parent like %(txt)s - order by - if(locate(%(_txt)s, parent), locate(%(_txt)s, parent), 99999), - parent - limit %(start)s, %(page_len)s""".format(**{ - 'key': searchfield, - }), { - 'txt': "%%%s%%" % txt, - '_txt': txt.replace("%", ""), - 'start': start, - 'page_len': page_len - }) - -@frappe.whitelist() -def get_contacts(reference_doctype, reference_name): - docfields = frappe.get_meta(reference_doctype).fields - - contact_fields = [] - for field in docfields: - if field.fieldtype == "Link" and field.options == "Contact": - contact_fields.append(field.fieldname) - - if contact_fields: - contacts = [] - for contact_field in contact_fields: - contacts.append(frappe.db.get_value(reference_doctype, reference_name, contact_field)) - else: - return [] - - if contacts: - emails = [] - for contact in contacts: - emails.append(frappe.db.get_value("Contact", contact, "email_id")) - - return emails - else: - return [] - - -@frappe.whitelist() -def update_reference(docname, reference): - try: - frappe.db.set_value("Auto Repeat", docname, "reference_document", reference) - return "success" - except Exception as e: - raise e - return "error" - -@frappe.whitelist() -def generate_message_preview(reference_dt, reference_doc, message=None, subject=None): - doc = frappe.get_doc(reference_dt, reference_doc) - subject_preview = _("Please add a subject to your email") - msg_preview = frappe.render_template(message, {'doc': doc}) - if subject: - subject_preview = frappe.render_template(subject, {'doc': doc}) - - return {'message': msg_preview, 'subject': subject_preview} diff --git a/frappe/desk/doctype/auto_repeat/auto_repeat_list.js b/frappe/desk/doctype/auto_repeat/auto_repeat_list.js deleted file mode 100644 index 295d9391b0..0000000000 --- a/frappe/desk/doctype/auto_repeat/auto_repeat_list.js +++ /dev/null @@ -1,16 +0,0 @@ -frappe.listview_settings['Auto Repeat'] = { - add_fields: ["next_schedule_date"], - get_indicator: function(doc) { - if(doc.disabled) { - return [__("Disabled"), "red"]; - } else if(doc.next_schedule_date >= frappe.datetime.get_today() && doc.status != 'Stopped') { - return [__("Active"), "green"]; - } else if(doc.docstatus === 0) { - return [__("Draft"), "red", "docstatus,=,0"]; - } else if(doc.status === 'Stopped') { - return [__("Stopped"), "red"]; - } else { - return [__("Expired"), "darkgrey"]; - } - } -}; \ No newline at end of file diff --git a/frappe/desk/doctype/todo/todo.json b/frappe/desk/doctype/todo/todo.json index 4b70086648..a0343e9f69 100644 --- a/frappe/desk/doctype/todo/todo.json +++ b/frappe/desk/doctype/todo/todo.json @@ -684,4 +684,4 @@ "track_changes": 1, "track_seen": 1, "track_views": 0 -} \ No newline at end of file +} diff --git a/frappe/desk/listview.py b/frappe/desk/listview.py index f31aae401e..f4927dd098 100644 --- a/frappe/desk/listview.py +++ b/frappe/desk/listview.py @@ -3,8 +3,6 @@ from __future__ import unicode_literals import frappe -import json - @frappe.whitelist() def get_list_settings(doctype): @@ -22,31 +20,37 @@ def set_list_settings(doctype, values): doc = frappe.new_doc("List View Setting") doc.name = doctype frappe.clear_messages() - doc.update(json.loads(values)) + doc.update(frappe.parse_json(values)) doc.save() + @frappe.whitelist() -def get_user_assignments_and_count(doctype, current_filters): - +def get_group_by_count(doctype, current_filters, field): + current_filters = frappe.parse_json(current_filters) subquery_condition = '' - if current_filters: - # get the subquery - subquery = frappe.get_all(doctype, - filters=current_filters, return_query = True) + + subquery = frappe.get_all(doctype, filters=current_filters, return_query = True) + if field == 'assigned_to': subquery_condition = ' and `tabToDo`.reference_name in ({subquery})'.format(subquery = subquery) + return frappe.db.sql("""select `tabToDo`.owner as name, count(*) as count + from + `tabToDo`, `tabUser` + where + `tabToDo`.status='Open' and + `tabToDo`.owner = `tabUser`.name and + `tabUser`.user_type = 'System User' + {subquery_condition} + group by + `tabToDo`.owner + order by + count desc + limit 50""".format(subquery_condition = subquery_condition), as_dict=True) + else : + return frappe.db.get_list(doctype, + filters=current_filters, + group_by=field, + fields=['count(*) as count', field + ' as name'], + order_by='count desc', + limit=50, + ) - todo_list = frappe.db.sql("""select `tabToDo`.owner as name, count(*) as count - from - `tabToDo`, `tabUser` - where - `tabToDo`.status='Open' and - `tabToDo`.owner = `tabUser`.name and - `tabUser`.user_type = 'System User' - {subquery_condition} - group by - `tabToDo`.owner - order by - count desc - limit 50""".format(subquery_condition = subquery_condition), as_dict=True) - - return todo_list \ No newline at end of file diff --git a/frappe/desk/moduleview.py b/frappe/desk/moduleview.py index ccde3aad40..ca3eef5c52 100644 --- a/frappe/desk/moduleview.py +++ b/frappe/desk/moduleview.py @@ -287,44 +287,141 @@ def get_onboard_items(app, module): return onboard_items or fallback_items - @frappe.whitelist() +def get_links_for_module(app, module): + return [l.get('label') for l in get_links(app, module)] + def get_links(app, module): try: sections = get_config(app, frappe.scrub(module)) except ImportError: return [] - link_names = [] - + links = [] for section in sections: - for item in section["items"]: - link_names.append(item.get("label")) - return link_names + for item in section['items']: + links.append(item) + return links @frappe.whitelist() -def hide_modules_from_desktop(modules): +def get_desktop_settings(): + from frappe.config import get_modules_from_all_apps_for_user + all_modules = get_modules_from_all_apps_for_user() + home_settings = get_home_settings() + + modules_by_name = {} + for m in all_modules: + modules_by_name[m['module_name']] = m + + module_categories = ['Modules', 'Domains', 'Places', 'Administration'] + user_modules_by_category = {} + + user_saved_modules_by_category = home_settings.modules_by_category or {} + user_saved_links_by_module = home_settings.links_by_module or {} + + def apply_user_saved_links(module): + module = frappe._dict(module) + all_links = get_links(module.app, module.module_name) + module_links_by_label = {} + for link in all_links: + module_links_by_label[link['label']] = link + + if module.module_name in user_saved_links_by_module: + user_links = frappe.parse_json(user_saved_links_by_module[module.module_name]) + module.links = [module_links_by_label[l] for l in user_links if l in module_links_by_label] + + return module + + for category in module_categories: + if category in user_saved_modules_by_category: + user_modules = user_saved_modules_by_category[category] + user_modules_by_category[category] = [apply_user_saved_links(modules_by_name[m]) \ + for m in user_modules] + else: + user_modules_by_category[category] = [apply_user_saved_links(m) \ + for m in all_modules if m.get('category') == category] + + # filter out hidden modules + if home_settings.hidden_modules: + for category in user_modules_by_category: + hidden_modules = home_settings.hidden_modules or [] + modules = user_modules_by_category[category] + user_modules_by_category[category] = [module for module in modules if module.module_name not in hidden_modules] + + return user_modules_by_category + +@frappe.whitelist() +def update_hidden_modules(category_map): + category_map = frappe.parse_json(category_map) + home_settings = get_home_settings() + + saved_hidden_modules = home_settings.hidden_modules or [] + + for category in category_map: + config = frappe._dict(category_map[category]) + saved_hidden_modules += config.removed or [] + saved_hidden_modules = [d for d in saved_hidden_modules if d not in (config.added or [])] + + home_settings.hidden_modules = saved_hidden_modules + set_home_settings(home_settings) + + return get_desktop_settings() + + +@frappe.whitelist() +def update_modules_order(module_category, modules): modules = frappe.parse_json(modules) - home_settings = frappe.db.get_value("User", frappe.session.user, 'home_settings') - home_settings = frappe.parse_json(home_settings or '{}') - - home_settings['hidden_modules'] = modules - frappe.db.set_value('User', frappe.session.user, 'home_settings', json.dumps(home_settings)) - - return home_settings + home_settings = get_home_settings() + home_settings.modules_by_category = home_settings.modules_by_category or {} + home_settings.modules_by_category[module_category] = modules + set_home_settings(home_settings) @frappe.whitelist() def update_links_for_module(module_name, links): - home_settings = frappe.db.get_value("User", frappe.session.user, 'home_settings') - home_settings = frappe.parse_json(home_settings or '{}') + links = frappe.parse_json(links) + home_settings = get_home_settings() - home_settings.setdefault('links', {}) - home_settings['links'].setdefault(module_name, None) - home_settings['links'][module_name] = links + home_settings.setdefault('links_by_module', {}) + home_settings['links_by_module'].setdefault(module_name, None) + home_settings['links_by_module'][module_name] = links + + set_home_settings(home_settings) + + return get_desktop_settings() + +@frappe.whitelist() +def get_options_for_show_hide_cards(): + from frappe.config import get_modules_from_all_apps_for_user + all_modules = get_modules_from_all_apps_for_user() + home_settings = get_home_settings() + + hidden_modules = home_settings.hidden_modules or [] + + options = [] + for module in all_modules: + module = frappe._dict(module) + options.append({ + 'category': module.category, + 'label': module.label, + 'value': module.module_name, + 'checked': module.module_name not in hidden_modules + }) + + return options + +def set_home_settings(home_settings): + frappe.cache().hset('home_settings', frappe.session.user, home_settings) frappe.db.set_value('User', frappe.session.user, 'home_settings', json.dumps(home_settings)) +@frappe.whitelist() +def get_home_settings(): + def get_from_db(): + settings = frappe.db.get_value("User", frappe.session.user, 'home_settings') + return frappe.parse_json(settings or '{}') + + home_settings = frappe.cache().hget('home_settings', frappe.session.user, get_from_db) return home_settings diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index caf6a7d4f0..9d6f3561cb 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -307,6 +307,7 @@ def export_query(): if isinstance(data.get("file_format_type"), string_types): file_format_type = data["file_format_type"] + include_indentation = data["include_indentation"] if isinstance(data.get("visible_idx"), string_types): visible_idx = json.loads(data.get("visible_idx")) else: @@ -318,7 +319,7 @@ def export_query(): columns = get_columns_dict(data.columns) from frappe.utils.xlsxutils import make_xlsx - xlsx_data = build_xlsx_data(columns, data, visible_idx) + xlsx_data = build_xlsx_data(columns, data, visible_idx, include_indentation) xlsx_file = make_xlsx(xlsx_data, "Query Report") frappe.response['filename'] = report_name + '.xlsx' @@ -326,7 +327,7 @@ def export_query(): frappe.response['type'] = 'binary' -def build_xlsx_data(columns, data, visible_idx): +def build_xlsx_data(columns, data, visible_idx,include_indentation): result = [[]] # add column headings @@ -344,7 +345,7 @@ def build_xlsx_data(columns, data, visible_idx): label = columns[idx]["label"] fieldname = columns[idx]["fieldname"] cell_value = row.get(fieldname, row.get(label, "")) - if 'indent' in row and idx == 0: + if cint(include_indentation) and 'indent' in row and idx == 0: cell_value = (' ' * cint(row['indent'])) + cell_value row_data.append(cell_value) else: diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 5ed4dc730b..9654e14687 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -185,6 +185,10 @@ def append_totals_row(data): for i in range(len(row)): if isinstance(row[i], (float, int)): totals[i] = (totals[i] or 0) + row[i] + + if not isinstance(totals[0], (int, float)): + totals[0] = 'Total' + data.append(totals) return data diff --git a/frappe/email/doctype/notification/notification.json b/frappe/email/doctype/notification/notification.json index b57a77a953..14eff2251a 100644 --- a/frappe/email/doctype/notification/notification.json +++ b/frappe/email/doctype/notification/notification.json @@ -1,1163 +1,287 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, "allow_rename": 1, "autoname": "Prompt", - "beta": 0, "creation": "2014-07-11 17:18:09.923399", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "System", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "enabled", + "column_break_2", + "channel", + "slack_webhook_url", + "filters", + "subject", + "document_type", + "is_standard", + "module", + "col_break_1", + "event", + "method", + "date_changed", + "days_in_advance", + "value_changed", + "sender", + "sender_email", + "section_break_9", + "condition", + "column_break_6", + "html_7", + "property_section", + "set_property_after_alert", + "property_value", + "column_break_5", + "recipients", + "message_sb", + "message", + "message_examples", + "slack_message_examples", + "view_properties", + "column_break_25", + "attach_print", + "print_format" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "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, - "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 + "label": "Enabled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 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, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Email", "fieldname": "channel", "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": "Channel", - "length": 0, - "no_copy": 0, "options": "Email\nSlack", - "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": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.channel=='Slack'", "fieldname": "slack_webhook_url", "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": "Slack Channel", - "length": 0, - "no_copy": 0, - "options": "Slack Webhook URL", - "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 + "options": "Slack Webhook URL" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "filters", "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": "Filters", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Filters" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "To add dynamic subject, use jinja tags like\n\n
{{ doc.name }} Delivered
", "fieldname": "subject", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Subject", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 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": 1, "in_standard_filter": 1, "label": "Document 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": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "is_standard", "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 Standard", - "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 + "label": "Is Standard" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "is_standard", "fieldname": "module", "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": 1, "label": "Module", - "length": 0, - "no_copy": 0, - "options": "Module Def", - "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 + "options": "Module Def" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "col_break_1", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "event", "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": 0, "label": "Send Alert On", - "length": 0, - "no_copy": 0, "options": "\nNew\nSave\nSubmit\nCancel\nDays After\nDays Before\nValue Change\nMethod\nCustom", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.event=='Method'", "description": "Trigger on valid methods like \"before_insert\", \"after_update\", etc (will depend on the DocType selected)", "fieldname": "method", "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": "Trigger Method", - "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 + "label": "Trigger Method" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.event==\"Days After\" || doc.event==\"Days Before\"", "description": "Send alert if date matches this field's value", "fieldname": "date_changed", "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": "Reference Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Reference Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "depends_on": "eval:doc.event==\"Days After\" || doc.event==\"Days Before\"", "description": "Send days before or after the reference date", "fieldname": "days_in_advance", "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": "Days Before or After", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Days Before or After" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.event==\"Value Change\"", "description": "Send alert if this field's value changes", "fieldname": "value_changed", "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": "Value Changed", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Value Changed" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sender", "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": "Sender", - "length": 0, - "no_copy": 0, - "options": "Email Account", - "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 + "options": "Email Account" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sender_email", "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": "Sender Email", - "length": 0, - "no_copy": 0, "options": "Email", - "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 + "read_only": 1 }, { - "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, - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", "description": "Optional: The alert will be sent if this expression is true", "fieldname": "condition", "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Condition", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Condition" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "html_7", "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "options": "

Condition Examples:

\n
doc.status==\"Open\"
doc.due_date==nowdate()
doc.total > 40000\n
\n

Hints:

\n
    \n
  1. To check for an event every day, select \"Date Change\" in Event
  2. \n
  3. To send an alert if a particular value changes, select \"Value Change\"
  4. \n
", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "

Condition Examples:

\n
doc.status==\"Open\"
doc.due_date==nowdate()
doc.total > 40000\n
\n" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "property_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Set Property After Alert", - "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 + "label": "Set Property After Alert" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "set_property_after_alert", "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": "Set Property After Alert", - "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 + "label": "Set Property After Alert" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "property_value", "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": "Value To Be Set", - "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 + "label": "Value To Be Set" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.channel=='Email'", "fieldname": "column_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, - "label": "Recipients", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Recipients" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "recipients", "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": "Recipients", - "length": 0, - "no_copy": 0, - "options": "Notification Recipient", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Notification Recipient" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "message_sb", "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, - "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 + "label": "Message" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Add your message here", - "depends_on": "", "fieldname": "message", "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Message" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.channel=='Email'", "fieldname": "message_examples", "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": "Message Examples", - "length": 0, - "no_copy": 0, - "options": "
Message Example
\n\n
<h3>Order Overdue</h3>\n\n<p>Transaction {{ doc.name }} has exceeded Due Date. Please take necessary action.</p>\n\n<!-- show last comment -->\n{% if comments %}\nLast comment: {{ comments[-1].comment }} by {{ comments[-1].by }}\n{% endif %}\n\n<h4>Details</h4>\n\n<ul>\n<li>Customer: {{ doc.customer }}\n<li>Amount: {{ doc.grand_total }}\n</ul>\n
", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "
Message Example
\n\n
<h3>Order Overdue</h3>\n\n<p>Transaction {{ doc.name }} has exceeded Due Date. Please take necessary action.</p>\n\n<!-- show last comment -->\n{% if comments %}\nLast comment: {{ comments[-1].comment }} by {{ comments[-1].by }}\n{% endif %}\n\n<h4>Details</h4>\n\n<ul>\n<li>Customer: {{ doc.customer }}\n<li>Amount: {{ doc.grand_total }}\n</ul>\n
" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.channel=='Slack'", "fieldname": "slack_message_examples", "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": "Message Examples", - "length": 0, - "no_copy": 0, - "options": "
Message Example
\n\n
*Order Overdue*\n\nTransaction {{ doc.name }} has exceeded Due Date. Please take necessary action.\n\n\n{% if comments %}\nLast comment: {{ comments[-1].comment }} by {{ comments[-1].by }}\n{% endif %}\n\n*Details*\n\n\u2022 Customer: {{ doc.customer }}\n\u2022 Amount: {{ doc.grand_total }}\n
", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "
Message Example
\n\n
*Order Overdue*\n\nTransaction {{ doc.name }} has exceeded Due Date. Please take necessary action.\n\n\n{% if comments %}\nLast comment: {{ comments[-1].comment }} by {{ comments[-1].by }}\n{% endif %}\n\n*Details*\n\n\u2022 Customer: {{ doc.customer }}\n\u2022 Amount: {{ doc.grand_total }}\n
" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "view_properties", "fieldtype": "Button", - "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": "View Properties (via Customize Form)", - "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 + "label": "View Properties (via Customize Form)" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, "collapsible_depends_on": "attach_print", - "columns": 0, "fieldname": "column_break_25", "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": "Print Settings", - "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 + "label": "Print Settings" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "attach_print", "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": "Attach Print", - "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 + "label": "Attach Print" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "attach_print", "fieldname": "print_format", "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": "Print Format", - "length": 0, - "no_copy": 0, - "options": "Print Format", - "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 + "options": "Print Format" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-envelope", - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2019-06-05 14:32:02.179599", + "modified": "2019-07-15 13:17:02.585013", "modified_by": "Administrator", "module": "Email", "name": "Notification", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, - "email": 0, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 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": "subject", - "track_changes": 1, - "track_seen": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/frappe/hooks.py b/frappe/hooks.py index db5984ddb3..6b9caec22a 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -161,7 +161,6 @@ scheduler_events = { "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", ], @@ -181,6 +180,8 @@ scheduler_events = { "frappe.desk.form.document_follow.send_daily_updates", "frappe.social.doctype.energy_point_settings.energy_point_settings.allocate_review_points", "frappe.integrations.doctype.google_contacts.google_contacts.sync", + "frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat_entry", + "frappe.automation.doctype.auto_repeat.auto_repeat.set_auto_repeat_as_completed" ], "daily_long": [ "frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_daily", @@ -236,6 +237,7 @@ setup_wizard_exception = "frappe.desk.page.setup_wizard.setup_wizard.email_setup before_write_file = "frappe.limits.validate_space_limit" before_migrate = ['frappe.patches.v11_0.sync_user_permission_doctype_before_migrate.execute'] +after_migrate = ['frappe.website.doctype.website_theme.website_theme.generate_theme_files_if_not_exist'] otp_methods = ['OTP App','Email','SMS'] user_privacy_documents = [ diff --git a/frappe/integrations/doctype/google_maps_settings/google_maps_settings.js b/frappe/integrations/doctype/google_maps_settings/google_maps_settings.js deleted file mode 100644 index ef98ce2c47..0000000000 --- a/frappe/integrations/doctype/google_maps_settings/google_maps_settings.js +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) 2017, Frappe Technologies and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Google Maps Settings', { -}); diff --git a/frappe/integrations/doctype/google_maps_settings/google_maps_settings.json b/frappe/integrations/doctype/google_maps_settings/google_maps_settings.json deleted file mode 100644 index 7a0695906e..0000000000 --- a/frappe/integrations/doctype/google_maps_settings/google_maps_settings.json +++ /dev/null @@ -1,159 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-10-16 17:13:05.684227", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "client_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": "Client 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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "home_address", - "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": "Home Address", - "length": 0, - "no_copy": 0, - "options": "Address", - "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": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:53:09.170463", - "modified_by": "Administrator", - "module": "Integrations", - "name": "Google Maps Settings", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "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, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "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 -} \ No newline at end of file diff --git a/frappe/integrations/doctype/google_maps_settings/google_maps_settings.py b/frappe/integrations/doctype/google_maps_settings/google_maps_settings.py deleted file mode 100644 index 71aafd3558..0000000000 --- a/frappe/integrations/doctype/google_maps_settings/google_maps_settings.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, Frappe Technologies and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals - -import frappe -from frappe import _ -from frappe.model.document import Document - - -class GoogleMapsSettings(Document): - def validate(self): - if self.enabled: - if not self.client_key: - frappe.throw(_("Client key is required")) - if not self.home_address: - frappe.throw(_("Home Address is required")) - - def get_client(self): - if not self.enabled: - frappe.throw(_("Google Maps integration is not enabled")) - - import googlemaps - - try: - client = googlemaps.Client(key=self.client_key) - except Exception as e: - frappe.throw(e.message) - - return client diff --git a/frappe/integrations/doctype/google_maps_settings/test_google_maps_settings.js b/frappe/integrations/doctype/google_maps_settings/test_google_maps_settings.js deleted file mode 100644 index a3c72bc42a..0000000000 --- a/frappe/integrations/doctype/google_maps_settings/test_google_maps_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Google Maps Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Google Maps - () => frappe.tests.make('Google Maps Settings', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/frappe/integrations/doctype/google_maps_settings/test_google_maps_settings.py b/frappe/integrations/doctype/google_maps_settings/test_google_maps_settings.py deleted file mode 100644 index f5717e19e0..0000000000 --- a/frappe/integrations/doctype/google_maps_settings/test_google_maps_settings.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, Frappe Technologies and Contributors -# See license.txt -from __future__ import unicode_literals -import unittest - -class TestGoogleMapsSettings(unittest.TestCase): - pass diff --git a/frappe/integrations/doctype/google_settings/google_settings.json b/frappe/integrations/doctype/google_settings/google_settings.json index c61d6606b5..8a316fd67e 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.json +++ b/frappe/integrations/doctype/google_settings/google_settings.json @@ -6,7 +6,8 @@ "enable", "google_credentials", "client_id", - "client_secret" + "client_secret", + "api_key" ], "fields": [ { @@ -32,10 +33,15 @@ "fieldtype": "Password", "in_list_view": 1, "label": "Client Secret" + }, + { + "fieldname": "api_key", + "fieldtype": "Data", + "label": "API Key" } ], "issingle": 1, - "modified": "2019-06-19 15:28:05.957380", + "modified": "2019-06-29 13:26:33.201060", "modified_by": "Administrator", "module": "Integrations", "name": "Google Settings", diff --git a/frappe/integrations/doctype/google_maps_settings/__init__.py b/frappe/integrations/doctype/ldap_group_mapping/__init__.py similarity index 100% rename from frappe/integrations/doctype/google_maps_settings/__init__.py rename to frappe/integrations/doctype/ldap_group_mapping/__init__.py diff --git a/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.json b/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.json new file mode 100644 index 0000000000..92db68e962 --- /dev/null +++ b/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.json @@ -0,0 +1,38 @@ +{ + "creation": "2019-05-29 01:24:29.585060", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "ldap_group", + "erpnext_role" + ], + "fields": [ + { + "fieldname": "ldap_group", + "fieldtype": "Data", + "in_list_view": 1, + "label": "LDAP Group", + "reqd": 1 + }, + { + "fieldname": "erpnext_role", + "fieldtype": "Link", + "in_list_view": 1, + "label": "ERPNext Role", + "options": "Role", + "reqd": 1 + } + ], + "istable": 1, + "modified": "2019-07-15 06:46:38.050408", + "modified_by": "Administrator", + "module": "Integrations", + "name": "LDAP Group Mapping", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py b/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py new file mode 100644 index 0000000000..f9f2adeed0 --- /dev/null +++ b/frappe/integrations/doctype/ldap_group_mapping/ldap_group_mapping.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class LDAPGroupMapping(Document): + pass diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.json b/frappe/integrations/doctype/ldap_settings/ldap_settings.json index aa43b2e9d0..5d30a873fb 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.json +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.json @@ -1,594 +1,215 @@ { - "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, + "engine": "InnoDB", + "field_order": [ + "enabled", + "ldap_server_url", + "column_break_4", + "base_dn", + "password", + "section_break_5", + "organizational_unit", + "default_role", + "ldap_search_string", + "ldap_email_field", + "ldap_username_field", + "column_break_11", + "ldap_first_name_field", + "ldap_middle_name_field", + "ldap_last_name_field", + "ldap_phone_field", + "ldap_mobile_field", + "ldap_security", + "ssl_tls_mode", + "require_trusted_certificate", + "column_break_17", + "local_private_key_file", + "local_server_certificate_file", + "local_ca_certs_file", + "ldap_group_mappings_section", + "ldap_group_field", + "ldap_groups" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "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 + "label": "Enabled" }, { - "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 + "reqd": 1 }, { - "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 + "fieldname": "column_break_4", + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "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 + "reqd": 1 }, { - "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 + "reqd": 1 }, { - "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 + "label": "LDAP User Creation and Mapping" + }, + { + "fieldname": "organizational_unit", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Organizational Unit for Users", + "reqd": 1 + }, + { + "fieldname": "default_role", + "fieldtype": "Link", + "label": "Default Role on Creation", + "options": "Role", + "reqd": 1 }, { - "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 + "reqd": 1 }, { - "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 + "reqd": 1 }, { - "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 + "reqd": 1 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "ldap_first_name_field", + "fieldtype": "Data", + "label": "LDAP First Name Field", + "reqd": 1 + }, + { + "fieldname": "ldap_middle_name_field", + "fieldtype": "Data", + "label": "LDAP Middle Name Field" + }, + { + "fieldname": "ldap_last_name_field", + "fieldtype": "Data", + "label": "LDAP Last Name Field" + }, + { + "fieldname": "ldap_phone_field", + "fieldtype": "Data", + "label": "LDAP Phone Field" + }, + { + "fieldname": "ldap_mobile_field", + "fieldtype": "Data", + "label": "LDAP Mobile Field" }, { - "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, - "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 + "label": "LDAP Security" }, { - "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, - "options": "Off\nStartTLS", - "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 + "options": "Off\nStartTLS" }, { - "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, "options": "No\nYes", - "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 + "reqd": 1 + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" }, { - "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 + "label": "Path to private Key File" }, { - "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 + "label": "Path to Server Certificate" }, { - "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 + "label": "Path to CA Certs File" + }, + { + "fieldname": "ldap_group_mappings_section", + "fieldtype": "Section Break", + "label": "LDAP Group Mappings" + }, + { + "fieldname": "ldap_group_field", + "fieldtype": "Data", + "label": "LDAP Group Field" + }, + { + "fieldname": "ldap_groups", + "fieldtype": "Table", + "label": "LDAP Group Mappings", + "options": "LDAP Group Mapping" } ], - "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": "2019-07-15 06:48:16.562109", "modified_by": "Administrator", "module": "Integrations", "name": "LDAP Settings", - "name_case": "", "owner": "Administrator", "permissions": [ { - "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, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/frappe/integrations/doctype/ldap_settings/ldap_settings.py b/frappe/integrations/doctype/ldap_settings/ldap_settings.py index 5fa54e9628..8cc2489b72 100644 --- a/frappe/integrations/doctype/ldap_settings/ldap_settings.py +++ b/frappe/integrations/doctype/ldap_settings/ldap_settings.py @@ -15,155 +15,195 @@ class LDAPSettings(Document): if not self.flags.ignore_mandatory: if self.ldap_search_string and self.ldap_search_string.endswith("={0}"): - 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 - ) + self.connect_to_ldap(base_dn=self.base_dn, password=self.get_password(raise_exception=False)) else: frappe.throw(_("LDAP Search String needs to end with a placeholder, eg sAMAccountName={0}")) + def connect_to_ldap(self, base_dn, password): + try: + import ldap3 + import ssl -def get_ldap_client_settings(): - #return the settings to be used on the client side. - result = { - "enabled": False - } - settings = frappe.get_doc("LDAP Settings") + if self.require_trusted_certificate == '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 settings and settings.enabled: - result["enabled"] = True - result["method"] = "frappe.integrations.doctype.ldap_settings.ldap_settings.login" - return result + if self.local_private_key_file: + tls_configuration.private_key_file = self.local_private_key_file + if self.local_server_certificate_file: + tls_configuration.certificate_file = self.local_server_certificate_file + if self.local_ca_certs_file: + tls_configuration.ca_certs_file = self.local_ca_certs_file + server = ldap3.Server(host=self.ldap_server_url, tls=tls_configuration) + bind_type = ldap3.AUTO_BIND_TLS_BEFORE_BIND if self.ssl_tls_mode == "StartTLS" else True -def connect_to_ldap(server_url, - base_dn, - password, - ssl_tls_mode, - trusted_cert, - private_key_file, - server_cert_file, - ca_certs_file): - try: - import ldap3 - import ssl + conn = ldap3.Connection( + server=server, + user=base_dn, + password=password, + auto_bind=bind_type, + read_only=True, + raise_exceptions=True) - if trusted_cert == 'Yes': - tls_configuration = ldap3.Tls(validate=ssl.CERT_REQUIRED, - version=ssl.PROTOCOL_TLSv1) + 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 username or password")) + except Exception as ex: + frappe.throw(_(str(ex))) + + @staticmethod + def get_ldap_client_settings(): + # return the settings to be used on the client side. + result = { + "enabled": False + } + ldap = frappe.get_doc("LDAP Settings") + if ldap.enabled: + result["enabled"] = True + result["method"] = "frappe.integrations.doctype.ldap_settings.ldap_settings.login" + return result + + @classmethod + def update_user_fields(cls, user, user_data): + + updatable_data = {key: value for key, value in user_data.items() if key != 'email'} + + for key, value in updatable_data.items(): + setattr(user, key, value) + user.save(ignore_permissions=True) + + def sync_roles(self, user, additional_groups=None): + + current_roles = set([d.role for d in user.get("roles")]) + + needed_roles = set() + needed_roles.add(self.default_role) + + lower_groups = [g.lower() for g in additional_groups or []] + + all_mapped_roles = {r.erpnext_role for r in self.ldap_groups} + matched_roles = {r.erpnext_role for r in self.ldap_groups if r.ldap_group.lower() in lower_groups} + unmatched_roles = all_mapped_roles.difference(matched_roles) + needed_roles.update(matched_roles) + roles_to_remove = current_roles.intersection(unmatched_roles) + + if not needed_roles.issubset(current_roles): + missing_roles = needed_roles.difference(current_roles) + user.add_roles(*missing_roles) + + user.remove_roles(*roles_to_remove) + + def create_or_update_user(self, user_data, groups=None): + user = None + if frappe.db.exists("User", user_data['email']): + user = frappe.get_doc("User", user_data['email']) + LDAPSettings.update_user_fields(user=user, user_data=user_data) else: - tls_configuration = ldap3.Tls(validate=ssl.CERT_NONE, - version=ssl.PROTOCOL_TLSv1) + doc = user_data + doc.update({ + "doctype": "User", + "send_welcome_email": 0, + "language": "", + "user_type": "System User", + # "roles": [{ + # "role": self.default_role + # }] + }) + user = frappe.get_doc(doc) + user.insert(ignore_permissions=True) + # always add default role. + user.add_roles(self.default_role) + if self.ldap_group_field: + self.sync_roles(user, groups) + return user - 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 + def get_ldap_attributes(self): + ldap_attributes = [self.ldap_email_field, self.ldap_username_field, self.ldap_first_name_field] - server = ldap3.Server(host=server_url, - tls=tls_configuration) - bind_type = ldap3.AUTO_BIND_TLS_BEFORE_BIND if ssl_tls_mode == "StartTLS" else True + if self.ldap_group_field: + ldap_attributes.append(self.ldap_group_field) - conn = ldap3.Connection(server=server, - user=base_dn, - password=password, - auto_bind=bind_type, - read_only=True, - raise_exceptions=True) + if self.ldap_middle_name_field: + ldap_attributes.append(self.ldap_middle_name_field) - return conn + if self.ldap_last_name_field: + ldap_attributes.append(self.ldap_last_name_field) - 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))) + if self.ldap_phone_field: + ldap_attributes.append(self.ldap_phone_field) + + if self.ldap_mobile_field: + ldap_attributes.append(self.ldap_mobile_field) + + return ldap_attributes + + def authenticate(self, username, password): + + if not self.enabled: + frappe.throw(_("LDAP is not enabled.")) + + user_filter = self.ldap_search_string.format(username) + ldap_attributes = self.get_ldap_attributes() + + conn = self.connect_to_ldap(self.base_dn, self.get_password(raise_exception=False)) + + conn.search( + search_base=self.organizational_unit, + search_filter="({0})".format(user_filter), + attributes=ldap_attributes) + + if len(conn.entries) == 1 and conn.entries[0]: + user = conn.entries[0] + # only try and connect as the user, once we have their fqdn entry. + self.connect_to_ldap(base_dn=user.entry_dn, password=password) + + groups = None + if self.ldap_group_field: + groups = getattr(user, self.ldap_group_field).values + return self.create_or_update_user(self.convert_ldap_entry_to_dict(user), groups=groups) + else: + frappe.throw(_("Invalid username or password")) + + def convert_ldap_entry_to_dict(self, user_entry): + data = { + 'username': user_entry[self.ldap_username_field].value, + 'email': user_entry[self.ldap_email_field].value, + 'first_name': user_entry[self.ldap_first_name_field].value + } + + # optional fields + + if self.ldap_middle_name_field: + data['middle_name'] = user_entry[self.ldap_middle_name_field].value + + if self.ldap_last_name_field: + data['last_name'] = user_entry[self.ldap_last_name_field].value + + if self.ldap_phone_field: + data['phone'] = user_entry[self.ldap_phone_field].value + + if self.ldap_mobile_field: + data['mobile_no'] = user_entry[self.ldap_mobile_field].value + + return data @frappe.whitelist(allow_guest=True) def login(): # LDAP LOGIN LOGIC args = frappe.form_dict - user = authenticate_ldap_user(frappe.as_unicode(args.usr), frappe.as_unicode(args.pwd)) + ldap = frappe.get_doc("LDAP Settings") + + user = ldap.authenticate(frappe.as_unicode(args.usr), frappe.as_unicode(args.pwd)) frappe.local.login_manager.user = user.name frappe.local.login_manager.post_login() # because of a GET request! frappe.db.commit() - - -def authenticate_ldap_user(user=None, - password=None): - - params = {} - 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) - - 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]) - - 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.")) - - -def create_user(params): - if frappe.db.exists("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({ - "doctype": "User", - "send_welcome_email": 0, - "language": "", - "user_type": "System User", - "roles": [{ - "role": _("Customer") - }] - }) - - user = frappe.get_doc(params).insert(ignore_permissions=True) - - return user diff --git a/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py b/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py new file mode 100644 index 0000000000..e6cf4eef3a --- /dev/null +++ b/frappe/integrations/doctype/ldap_settings/test_ldap_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestLDAPSettings(unittest.TestCase): + pass diff --git a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py index a021cddbe3..d00883f5a3 100755 --- a/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py +++ b/frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py @@ -66,15 +66,26 @@ def take_backups_if(freq): @frappe.whitelist() -def take_backups_s3(): +def take_backups_s3(retry_count=0): try: backup_to_s3() send_email(True, "S3 Backup Settings") + except JobTimeoutException: + if retry_count < 2: + args = { + "retry_count" :retry_count + 1 + } + enqueue("frappe.integrations.doctype.s3_backup_settings.s3_backup_settings.take_backups_s3", + queue='long', timeout=1500, **args) + else: + notify() except Exception: - error_message = frappe.get_traceback() - frappe.errprint(error_message) - send_email(False, "S3 Backup Settings", error_message) + notify() +def notify(): + error_message = frappe.get_traceback() + frappe.errprint(error_message) + send_email(False, "S3 Backup Settings", error_message) def send_email(success, service_name, error_status=None): if success: @@ -134,6 +145,7 @@ def upload_file_to_s3(filename, folder, conn, bucket): conn.upload_file(filename, bucket, destpath) except Exception as e: + frappe.log_error() print("Error uploading: %s" % (e)) diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 115d175379..96814984f8 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -477,7 +477,7 @@ def get_field_currency(df, doc=None): if ":" in cstr(df.get("options")): split_opts = df.get("options").split(":") - if len(split_opts)==3: + if len(split_opts)==3 and doc.get(split_opts[1]): currency = frappe.get_cached_value(split_opts[0], doc.get(split_opts[1]), split_opts[2]) else: currency = doc.get(df.get("options")) diff --git a/frappe/patches.txt b/frappe/patches.txt index ac3b3501f5..0a43328a12 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -237,7 +237,7 @@ frappe.patches.v12_0.set_primary_key_in_series 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 -frappe.patches.v12_0.init_desk_settings #11-03-2019 +frappe.patches.v12_0.init_desk_settings #16-05-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 @@ -246,3 +246,4 @@ frappe.patches.v11_0.apply_customization_to_custom_doctype 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 +frappe.patches.v12_0.delete_feedback_request_if_exists #1 diff --git a/frappe/patches/v12_0/delete_feedback_request_if_exists.py b/frappe/patches/v12_0/delete_feedback_request_if_exists.py new file mode 100644 index 0000000000..fdbcecfc5a --- /dev/null +++ b/frappe/patches/v12_0/delete_feedback_request_if_exists.py @@ -0,0 +1,8 @@ + +import frappe + +def execute(): + frappe.db.sql(''' + DELETE from `tabDocType` + WHERE name = 'Feedback Request' + ''') \ No newline at end of file diff --git a/frappe/patches/v12_0/init_desk_settings.py b/frappe/patches/v12_0/init_desk_settings.py index 31c6cf9207..ecd9c94d5b 100644 --- a/frappe/patches/v12_0/init_desk_settings.py +++ b/frappe/patches/v12_0/init_desk_settings.py @@ -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 = ''""") diff --git a/frappe/patches/v12_0/website_meta_tag_parent.py b/frappe/patches/v12_0/website_meta_tag_parent.py new file mode 100644 index 0000000000..7cc84b1283 --- /dev/null +++ b/frappe/patches/v12_0/website_meta_tag_parent.py @@ -0,0 +1,9 @@ +import frappe + +def execute(): + # convert all /path to path + frappe.db.sql(''' + UPDATE `tabWebsite Meta Tag` + SET parent = SUBSTR(parent, 2) + WHERE parent like '/%' + ''') diff --git a/frappe/patches/v5_0/style_settings_to_website_theme.py b/frappe/patches/v5_0/style_settings_to_website_theme.py index b2f21e780e..40414d4e20 100644 --- a/frappe/patches/v5_0/style_settings_to_website_theme.py +++ b/frappe/patches/v5_0/style_settings_to_website_theme.py @@ -25,7 +25,7 @@ def migrate_style_settings(): website_theme.no_sidebar = cint(frappe.db.get_single_value("Website Settings", "no_sidebar")) website_theme.save() - website_theme.use_theme() + website_theme.set_as_default() def map_color_fields(style_settings, website_theme): color_fields_map = { diff --git a/frappe/public/build.json b/frappe/public/build.json index 203a642195..2cc19dec4a 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -36,7 +36,7 @@ "public/js/frappe/ui/messages.js", "public/js/frappe/translate.js", "public/js/frappe/utils/pretty_date.js", - "public/js/lib/microtemplate.js", + "public/js/frappe/microtemplate.js", "public/js/frappe/query_string.js", "public/js/frappe/ui/dropzone.js", @@ -145,7 +145,7 @@ "public/js/frappe/router_history.js", "public/js/frappe/defaults.js", "public/js/frappe/roles_editor.js", - "public/js/lib/microtemplate.js", + "public/js/frappe/microtemplate.js", "public/js/legacy/handler.js", @@ -276,6 +276,7 @@ "public/js/frappe/list/list_sidebar.js", "public/js/frappe/list/list_sidebar.html", "public/js/frappe/list/list_sidebar_stat.html", + "public/js/frappe/list/list_sidebar_group_by.js", "public/js/frappe/list/list_view_permission_restrictions.html", "public/js/frappe/views/gantt/gantt_view.js", diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 4dc3b4b0f8..3f4e391461 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -464,6 +464,11 @@ frappe.Application = Class.extend({ return frappe.call('frappe.client.get_hooks', { hook: 'app_logo_url' }) .then(r => { frappe.app.logo_url = (r.message || []).slice(-1)[0]; + if (window.cordova) { + let host = frappe.request.url; + host = host.slice(0, host.length - 1); + frappe.app.logo_url = host + frappe.app.logo_url; + } }); }, diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 838f4f129b..68136eba44 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -309,9 +309,8 @@ frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ function get_filter_description(filter) { let doctype = filter[0]; let fieldname = filter[1]; - let label = meta - ? frappe.meta.get_docfield(doctype, fieldname).label - : frappe.model.unscrub(fieldname); + let docfield = frappe.meta.get_docfield(doctype, fieldname); + let label = docfield ? docfield.label : frappe.model.unscrub(fieldname); let value = filter[3] == null || filter[3] === '' ? __('empty') diff --git a/frappe/public/js/frappe/form/controls/multiselect_list.js b/frappe/public/js/frappe/form/controls/multiselect_list.js index e244c3c12e..7592b80a62 100644 --- a/frappe/public/js/frappe/form/controls/multiselect_list.js +++ b/frappe/public/js/frappe/form/controls/multiselect_list.js @@ -69,6 +69,8 @@ frappe.ui.form.ControlMultiSelectList = frappe.ui.form.ControlData.extend({ this.set_input_attributes(); this.values = []; + this._options = []; + this._selected_values = []; this.highlighted = -1; }, @@ -104,6 +106,20 @@ frappe.ui.form.ControlMultiSelectList = frappe.ui.form.ControlData.extend({ this.update_status(); }, + set_value(value) { + if (!value) return Promise.resolve(); + if (typeof value === 'string') { + value = [value]; + } + this.values = value; + this.values.forEach(value => { + this.update_selected_values(value); + }); + this.parse_validate_and_set_in_model(''); + this.update_status(); + return Promise.resolve(); + }, + update_selected_values(value) { this._selected_values = this._selected_values || []; let option = this._options.find(opt => opt.value === value); @@ -122,7 +138,8 @@ frappe.ui.form.ControlMultiSelectList = frappe.ui.form.ControlData.extend({ text = this.get_placeholder_text(); } else if (this.values.length === 1) { let val = this.values[0]; - text = this._options.find(opt => opt.value === val).label; + let option = this._options.find(opt => opt.value === val); + text = option ? option.label : val; } else { text = __('{0} values selected', [this.values.length]); } diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 79797f5560..24b9aed646 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -120,14 +120,16 @@ frappe.ui.form.Form = class FrappeForm { shortcut: 'shift+>', action: () => this.navigate_records(0), page: this.page, - description: __('Go to next record') + description: __('Go to next record'), + condition: () => !this.is_new() }); frappe.ui.keys.add_shortcut({ shortcut: 'shift+<', action: () => this.navigate_records(1), page: this.page, - description: __('Go to previous record') + description: __('Go to previous record'), + condition: () => !this.is_new() }); } @@ -540,7 +542,9 @@ frappe.ui.form.Form = class FrappeForm { me.script_manager.trigger("after_save"); // submit comment if entered - me.timeline.comment_area.submit(); + if (me.timeline) { + me.timeline.comment_area.submit(); + } me.refresh(); } else { if(on_error) { diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index 92f34aa623..0f3f01ac61 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -606,7 +606,7 @@ export default class GridRow { } } - get_visible_columns(blacklist) { + get_visible_columns(blacklist=[]) { var me = this; var visible_columns = $.map(this.docfields, function(df) { var visible = !df.hidden && df.in_list_view && me.grid.frm.get_perm(df.permlevel, "read") diff --git a/frappe/public/js/frappe/form/multi_select_dialog.js b/frappe/public/js/frappe/form/multi_select_dialog.js index 56dca993f2..0d8d2caca1 100644 --- a/frappe/public/js/frappe/form/multi_select_dialog.js +++ b/frappe/public/js/frappe/form/multi_select_dialog.js @@ -35,6 +35,20 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ if(!this.date_field) { this.date_field = "transaction_date"; } + + // setters can be defined as a dict or a list of fields + // setters define the additional filters that get applied + // for selection + + // CASE 1: DocType name and fieldname is the same, example "customer" and "customer" + // setters define the filters applied in the modal + // if the fieldnames and doctypes are consistently named, + // pass a dict with the setter key and value, for example + // {customer: [customer_name]} + + // CASE 2: if the fieldname of the target is different, + // then pass a list of fields with appropriate fieldname + if($.isArray(this.setters)) { for (let df of this.setters) { fields.push(df, {fieldtype: "Column Break"}); @@ -142,6 +156,7 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ clearTimeout($this.data('timeout')); $this.data('timeout', setTimeout(function() { frappe.flags.auto_scroll = false; + me.empty_list(); me.get_results(); }, 300)); }); @@ -198,16 +213,15 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ render_result_list: function(results, more = 0) { var me = this; - var more_btn = me.dialog.fields_dict.more_btn.$wrapper; // Make empty result set if filter is set if (!frappe.flags.auto_scroll) { - this.$results.splice(1, this.$results.length); + this.empty_list(); } if(results.length === 0) { - this.$results.empty(); + this.empty_list(); more_btn.hide(); return; } else if(more) { @@ -223,6 +237,10 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ } }, + empty_list: function() { + this.$results.find('.list-item-container').remove(); + }, + get_results: function() { let me = this; diff --git a/frappe/public/js/frappe/form/print.js b/frappe/public/js/frappe/form/print.js index 60a21e3cff..ec78b30c8a 100644 --- a/frappe/public/js/frappe/form/print.js +++ b/frappe/public/js/frappe/form/print.js @@ -484,7 +484,7 @@ frappe.ui.get_print_settings = function (pdf, callback, letter_head) { default: "Landscape" }]; - frappe.prompt(columns, function (data) { + return frappe.prompt(columns, function (data) { var data = $.extend(print_settings, data); if (!data.with_letter_head) { data.letter_head = null; diff --git a/frappe/public/js/frappe/form/quick_entry.js b/frappe/public/js/frappe/form/quick_entry.js index 53dc40925c..38250f2ad8 100644 --- a/frappe/public/js/frappe/form/quick_entry.js +++ b/frappe/public/js/frappe/form/quick_entry.js @@ -159,17 +159,29 @@ frappe.ui.form.QuickEntryForm = Class.extend({ doc: me.dialog.doc }, callback: function(r) { - me.dialog.hide(); - // delete the old doc - frappe.model.clear_doc(me.dialog.doc.doctype, me.dialog.doc.name); - me.dialog.doc = r.message; - if(frappe._from_link) { - frappe.ui.form.update_calling_link(me.dialog.doc); + + if (frappe.model.is_submittable(me.doctype)) { + frappe.run_serially([ + () => me.dialog.working = true, + () => { + me.dialog.set_primary_action(__('Submit'), function() { + me.submit(r.message); + }); + } + ]); } else { - if(me.after_insert) { - me.after_insert(me.dialog.doc); + me.dialog.hide(); + // delete the old doc + frappe.model.clear_doc(me.dialog.doc.doctype, me.dialog.doc.name); + me.dialog.doc = r.message; + if(frappe._from_link) { + frappe.ui.form.update_calling_link(me.dialog.doc); } else { - me.open_form_if_not_list(); + if(me.after_insert) { + me.after_insert(me.dialog.doc); + } else { + me.open_form_if_not_list(); + } } } }, @@ -185,6 +197,26 @@ frappe.ui.form.QuickEntryForm = Class.extend({ }); }, + submit: function(doc) { + var me = this; + frappe.call({ + method: "frappe.client.submit", + args : { + doc: doc + }, + callback: function(r) { + me.dialog.hide(); + // delete the old doc + frappe.model.clear_doc(me.dialog.doc.doctype, me.dialog.doc.name); + me.dialog.doc = r.message; + if (frappe._from_link) { + frappe.ui.form.update_calling_link(me.dialog.doc); + } + cur_frm.reload_doc(); + } + }); + }, + open_form_if_not_list: function() { let route = frappe.get_route(); let doc = this.dialog.doc; diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index 4b38a2ed20..a78623d566 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -142,13 +142,16 @@ frappe.ui.form.save = function (frm, action, callback, btn) { } }); + if (frm.is_new() && frm.meta.autoname === 'Prompt' && !frm.doc.__newname) { + error_fields = [__('Name'), ...error_fields]; + } + if (error_fields.length) { if (doc.parenttype) { var message = __('Mandatory fields required in table {0}, Row {1}', [__(frappe.meta.docfield_map[doc.parenttype][doc.parentfield].label).bold(), doc.idx]); } else { var message = __('Mandatory fields required in {0}', [__(doc.doctype)]); - } message = message + '

"; frappe.msgprint({ diff --git a/frappe/public/js/frappe/form/script_manager.js b/frappe/public/js/frappe/form/script_manager.js index c3739b0daa..fd5b8d3856 100644 --- a/frappe/public/js/frappe/form/script_manager.js +++ b/frappe/public/js/frappe/form/script_manager.js @@ -169,7 +169,7 @@ frappe.ui.form.ScriptManager = Class.extend({ function setup_add_fetch(df) { if((['Data', 'Read Only', 'Text', 'Small Text', 'Currency', - 'Text Editor', 'Code', 'Link', 'Float', 'Int', 'Date'].includes(df.fieldtype) || df.read_only==1) + 'Text Editor', 'Code', 'Link', 'Float', 'Int', 'Date', 'Select'].includes(df.fieldtype) || df.read_only==1) && df.fetch_from && df.fetch_from.indexOf(".")!=-1) { var parts = df.fetch_from.split("."); me.frm.add_fetch(parts[0], parts[1], df.fieldname); diff --git a/frappe/public/js/frappe/form/sidebar/form_sidebar.js b/frappe/public/js/frappe/form/sidebar/form_sidebar.js index b766d66a5f..5798d3361e 100644 --- a/frappe/public/js/frappe/form/sidebar/form_sidebar.js +++ b/frappe/public/js/frappe/form/sidebar/form_sidebar.js @@ -36,6 +36,7 @@ frappe.ui.form.Sidebar = Class.extend({ this.bind_events(); this.setup_keyboard_shortcuts(); + this.show_auto_repeat_status(); frappe.ui.form.setup_user_image_event(this.frm); this.refresh(); @@ -88,6 +89,28 @@ frappe.ui.form.Sidebar = Class.extend({ } }, + show_auto_repeat_status: function() { + if (this.frm.meta.allow_auto_repeat && this.frm.doc.auto_repeat) { + const me = this; + frappe.call({ + method: "frappe.client.get_value", + args:{ + doctype: "Auto Repeat", + filters: { + name: this.frm.doc.auto_repeat + }, + fieldname: ["frequency"] + }, + callback: function(res) { + me.sidebar.find(".auto-repeat-status").html(__("Repeats {0}", [res.message.frequency])); + me.sidebar.find(".auto-repeat-status").on("click", function(){ + frappe.set_route("Form", "Auto Repeat", me.frm.doc.auto_repeat); + }); + } + }); + } + }, + refresh_comments: function() { $.map(this.frm.timeline.get_communications(), function(c) { return (c.communication_type==="Communication" || (c.communication_type=="Comment" && c.comment_type==="Comment")) ? c : null; diff --git a/frappe/public/js/frappe/form/sidebar/user_image.js b/frappe/public/js/frappe/form/sidebar/user_image.js index 8be760179a..6c8099db89 100644 --- a/frappe/public/js/frappe/form/sidebar/user_image.js +++ b/frappe/public/js/frappe/form/sidebar/user_image.js @@ -4,6 +4,7 @@ frappe.ui.form.set_user_image = function(frm) { var image_field = frm.meta.image_field; var image = frm.doc[image_field]; var title_image = frm.page.$title_area.find('.title-image'); + var image_actions = frm.sidebar.image_wrapper.find('.sidebar-image-actions'); image_section.toggleClass('hide', image_field ? false : true); @@ -32,6 +33,8 @@ frappe.ui.form.set_user_image = function(frm) { .css("background-image", 'url("' + image + '")') .html(''); + image_actions.find('.sidebar-image-change, .sidebar-image-remove').show(); + } else { image_section .find(".sidebar-image") @@ -51,6 +54,8 @@ frappe.ui.form.set_user_image = function(frm) { .css({'background-color': frappe.get_palette(title)}) .html(frappe.get_abbr(title)); + image_actions.find('.sidebar-image-change').show(); + image_actions.find('.sidebar-image-remove').hide(); } } @@ -63,12 +68,27 @@ frappe.ui.form.setup_user_image_event = function(frm) { }); } - // bind click on image_wrapper - frm.sidebar.image_wrapper.on('click', function() { - var field = frm.get_field(frm.meta.image_field); - if(!field.$input) { - field.make_input(); + frm.sidebar.image_wrapper.on('click', ':not(.sidebar-image-actions)', (e) => { + let $target = $(e.currentTarget); + if ($target.is('a.dropdown-toggle, .dropdown')) { + return; + } + let dropdown = frm.sidebar.image_wrapper.find('.sidebar-image-actions .dropdown'); + dropdown.toggleClass('open'); + e.stopPropagation(); + }); + + // bind click on image_wrapper + frm.sidebar.image_wrapper.on('click', '.sidebar-image-change, .sidebar-image-remove', function(e) { + let $target = $(e.currentTarget); + var field = frm.get_field(frm.meta.image_field); + if ($target.is('.sidebar-image-change')) { + if(!field.$input) { + field.make_input(); + } + field.$input.trigger('click'); + } else { + field.set_value('').then(() => frm.save()); } - field.$input.trigger('click'); }); } \ No newline at end of file diff --git a/frappe/public/js/frappe/form/templates/form_sidebar.html b/frappe/public/js/frappe/form/templates/form_sidebar.html index 34f9b57ef2..b611557c43 100644 --- a/frappe/public/js/frappe/form/templates/form_sidebar.html +++ b/frappe/public/js/frappe/form/templates/form_sidebar.html @@ -12,6 +12,15 @@ + {% if frm.meta.beta %} @@ -72,6 +81,9 @@
  • {%= __("Currently Viewing") %}
  • + diff --git a/frappe/public/js/frappe/form/toolbar.js b/frappe/public/js/frappe/form/toolbar.js index fa22d488fb..43411bd6de 100644 --- a/frappe/public/js/frappe/form/toolbar.js +++ b/frappe/public/js/frappe/form/toolbar.js @@ -129,7 +129,10 @@ frappe.ui.form.Toolbar = Class.extend({ if(frappe.model.can_email(null, me.frm) && me.frm.doc.docstatus < 2) { this.page.add_menu_item(__("Email"), function() { me.frm.email_doc(); - }, true, 'Ctrl+E'); + }, true, { + shortcut: 'Ctrl+E', + condition: () => !this.frm.is_new() + }); } // go to field modal @@ -168,7 +171,10 @@ frappe.ui.form.Toolbar = Class.extend({ && frappe.model.can_delete(me.frm.doctype)) { this.page.add_menu_item(__("Delete"), function() { me.frm.savetrash(); - }, true, 'Shift+Ctrl+D'); + }, true, { + shortcut: 'Shift+Ctrl+D', + condition: () => !this.frm.is_new() + }); } if(frappe.user_roles.includes("System Manager") && me.frm.meta.issingle === 0) { @@ -178,7 +184,7 @@ frappe.ui.form.Toolbar = Class.extend({ }) }, true); - if (frappe.boot.developer_mode===1 && me.frm.meta.issingle) { + if (frappe.boot.developer_mode===1) { // edit doctype this.page.add_menu_item(__("Edit DocType"), function() { frappe.set_route('Form', 'DocType', me.frm.doctype); @@ -186,11 +192,21 @@ frappe.ui.form.Toolbar = Class.extend({ } } + // Auto Repeat + if(this.can_repeat()) { + this.page.add_menu_item(__("Repeat"), function(){ + frappe.utils.new_auto_repeat_prompt(me.frm); + }, true); + } + // New if(p[CREATE] && !this.frm.meta.issingle) { this.page.add_menu_item(__("New {0}", [__(me.frm.doctype)]), function() { frappe.new_doc(me.frm.doctype, true); - }, true, 'Ctrl+B'); + }, true, { + shortcut: 'Ctrl+B', + condition: () => !this.frm.is_new() + }); } // Navigate @@ -203,6 +219,11 @@ frappe.ui.form.Toolbar = Class.extend({ }); } }, + can_repeat: function() { + return this.frm.meta.allow_auto_repeat + && !this.frm.is_new() + && !this.frm.doc.auto_repeat; + }, can_save: function() { return this.get_docstatus()===0; }, diff --git a/frappe/public/js/frappe/form/workflow.js b/frappe/public/js/frappe/form/workflow.js index 115ad5e702..464e5bf05c 100644 --- a/frappe/public/js/frappe/form/workflow.js +++ b/frappe/public/js/frappe/form/workflow.js @@ -49,7 +49,6 @@ frappe.ui.form.States = Class.extend({ }, refresh: function() { - const me = this; // hide if its not yet saved if(this.frm.doc.__islocal) { this.set_default_state(); @@ -59,8 +58,6 @@ frappe.ui.form.States = Class.extend({ // state text const state = this.get_state(); - let doctype = this.frm.doctype; - if(state) { // show actions from that state this.show_actions(state); @@ -71,8 +68,6 @@ frappe.ui.form.States = Class.extend({ var added = false; var me = this; - this.frm.page.clear_actions_menu(); - // if the loaded doc is dirty, don't show workflow buttons if (this.frm.doc.__unsaved===1) { return; @@ -90,7 +85,8 @@ frappe.ui.form.States = Class.extend({ } frappe.workflow.get_transitions(this.frm.doc).then(transitions => { - $.each(transitions, function(i, d) { + this.frm.page.clear_actions_menu(); + transitions.forEach(d => { if(frappe.user_roles.includes(d.allowed) && has_approval_access(d)) { added = true; me.frm.page.add_action_item(__(d.action), function() { diff --git a/frappe/public/js/frappe/list/list_sidebar.html b/frappe/public/js/frappe/list/list_sidebar.html index c8c2239b03..885b5f2dae 100644 --- a/frappe/public/js/frappe/list/list_sidebar.html +++ b/frappe/public/js/frappe/list/list_sidebar.html @@ -52,21 +52,31 @@ - {% if(frappe.help.has_help(doctype)) { %}
  • {{ __("Help") }}
  • {% } %} + + +