diff --git a/frappe/core/doctype/communication/communication.json b/frappe/core/doctype/communication/communication.json index 95ac938120..96e8ce0bc8 100644 --- a/frappe/core/doctype/communication/communication.json +++ b/frappe/core/doctype/communication/communication.json @@ -633,6 +633,33 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "uid", + "fieldtype": "Data", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "UID", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -1428,5 +1455,5 @@ "sort_order": "DESC", "title_field": "subject", "track_changes": 1, - "track_seen": 0 + "track_seen": 1 } \ No newline at end of file diff --git a/frappe/email/doctype/email_account/email_account.json b/frappe/email/doctype/email_account/email_account.json index 4960a6f569..05e2b0378d 100644 --- a/frappe/email/doctype/email_account/email_account.json +++ b/frappe/email/doctype/email_account/email_account.json @@ -530,6 +530,65 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "UNSEEN", + "depends_on": "eval: doc.enable_incoming", + "fieldname": "email_sync_option", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Email Sync Option", + "length": 0, + "no_copy": 0, + "options": "ALL\nUNSEEN", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "description": "Total number of emails to sync in initial sync process ", + "fieldname": "initial_sync_count", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Initial Sync Count", + "length": 0, + "no_copy": 0, + "options": "100\n250\n500", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -1138,7 +1197,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "uid_validity", + "fieldname": "uidvalidity", "fieldtype": "Data", "hidden": 1, "ignore_user_permissions": 0, @@ -1147,7 +1206,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "uid validity", + "label": "UIDVALIDITY", "length": 0, "no_copy": 1, "permlevel": 0, @@ -1176,7 +1235,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "uidnext", + "label": "UIDNEXT", "length": 0, "no_copy": 1, "permlevel": 0, diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 8b14cabe62..14f686ec9c 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -75,8 +75,6 @@ class EmailAccount(Document): if self.append_to not in valid_doctypes: frappe.throw(_("Append To can be one of {0}").format(comma_or(valid_doctypes))) - - def on_update(self): """Check there is only one default of each type.""" self.there_must_be_only_one_default() @@ -131,7 +129,7 @@ class EmailAccount(Document): server.password = self.get_password() server.sess - def get_incoming_server(self, in_receive=False): + def get_incoming_server(self, in_receive=False, email_sync_rule="UNSEEN"): """Returns logged in POP3/IMAP connection object.""" if frappe.cache().get_value("workers:no-internet") == True: return None @@ -142,6 +140,7 @@ class EmailAccount(Document): "use_ssl": self.use_ssl, "username": getattr(self, "login_id", None) or self.email_id, "use_imap": self.use_imap, + "email_sync_rule": email_sync_rule }) if self.password: args.password = self.get_password() @@ -221,17 +220,24 @@ class EmailAccount(Document): def receive(self, test_mails=None): """Called by scheduler to receive emails from this EMail account using POP3/IMAP.""" if self.enable_incoming: + uid_list = [] exceptions = [] + if frappe.local.flags.in_test: incoming_mails = test_mails else: - email_server = self.get_incoming_server(in_receive=True) - incoming_mails = email_server.get_messages() + email_sync_rule = self.build_email_sync_rule() - for msg in incoming_mails: + email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule) + emails = email_server.get_messages() + + incoming_mails = emails.get("latest_messages") + uid_list = emails.get("uid_list", []) + + for idx, msg in enumerate(incoming_mails): try: - - communication = self.insert_communication(msg) + uid = None if not uid_list else uid_list[idx] + communication = self.insert_communication(msg, uid) #self.notify_update() except SentEmailInInbox: @@ -277,12 +283,15 @@ class EmailAccount(Document): unhandled_email.save() frappe.db.commit() - def insert_communication(self, msg): + def insert_communication(self, msg, _uid=None): if isinstance(msg,list): raw, uid, seen = msg else: raw = msg seen = uid = None + + if _uid: uid = _uid + email = Email(raw) if email.from_email == self.email_id and not email.mail.get("Reply-To"): @@ -511,6 +520,17 @@ class EmailAccount(Document): def after_rename(self, old, new, merge=False): frappe.db.set_value("Email Account", new, "email_account_name", new) + def build_email_sync_rule(self): + if not self.use_imap: + return "UNSEEN" + + if self.email_sync_option == "ALL": + max_uid = get_max_email_uid(self.name) + last_uid = max_uid + int(self.initial_sync_count or 100) if max_uid == 1 else "*" + return "UID {}:{}".format(max_uid, last_uid) + else: + return self.email_sync_option + @frappe.whitelist() def get_append_to(doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None): if not txt: txt = "" @@ -584,3 +604,19 @@ def pull_from_email_account(email_account): '''Runs within a worker process''' email_account = frappe.get_doc("Email Account", email_account) email_account.receive() + +def get_max_email_uid(email_account): + # get maximum uid of emails + max_uid = 1 + + result = frappe.db.get_all("Communication", filters={ + "communication_medium": "Email", + "sent_or_received": "Received", + "email_account": email_account + }, fields=["ifnull(max(uid), 0) as uid"]) + + if not result: + return 1 + else: + max_uid = int(result[0].get("uid", 0)) + 1 + return max_uid \ No newline at end of file diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 8f16b4cc9d..30d3a242f7 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -101,12 +101,14 @@ class EmailServer: if not self.connect(): return [] + uid_list = [] + try: # track if errors arised self.errors = False self.latest_messages = [] - email_list = self.get_new_mails() + uid_list = email_list = self.get_new_mails() num = num_copy = len(email_list) # WARNING: Hard coded max no. of messages to be popped @@ -149,13 +151,16 @@ class EmailServer: else: self.pop.quit() - return self.latest_messages + out = { "latest_messages": self.latest_messages } + if self.settings.use_imap: out.update({ "uid_list": uid_list }) + + return out def get_new_mails(self): """Return list of new mails""" if cint(self.settings.use_imap): self.imap.select("Inbox") - response, message = self.imap.uid('search', None, "UNSEEN") + response, message = self.imap.uid('search', None, self.settings.email_sync_rule) email_list = message[0].split() else: email_list = self.pop.list()[1]