[IMP] fetchmail_.._folder: optionally archive messages automatically

pull/3071/head
Ronald Portier 2024-04-25 13:00:31 +02:00 committed by Jose Zambudio
parent b6098fcd36
commit 2b44724d82
5 changed files with 519 additions and 1 deletions

View File

@ -7,7 +7,7 @@ Email gateway - folders
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:95f6645118da34dd962fa794f5fca9c8797579a9a585d80228407f4997e9ba91 !! source digest: sha256:907101d997473fcc276317ee6d3bf95f2f005eab22b585dd59644a757959ca10
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
@ -91,6 +91,10 @@ Odoo and attached to the partner in question. After some testing, you
might want to check ``Delete matches`` in your folder configuration so might want to check ``Delete matches`` in your folder configuration so
that this folder doesn't grow indefinitely. that this folder doesn't grow indefinitely.
Another way to prevent having to process ever more messages from the
folder to read is to automatically move all processed messages to an
archive folder that can be specified.
Bug Tracker Bug Tracker
=========== ===========

View File

@ -31,6 +31,9 @@ class FetchmailServerFolder(models.Model):
help="The path to your mail folder." help="The path to your mail folder."
" Typically would be something like 'INBOX.myfolder'", " Typically would be something like 'INBOX.myfolder'",
) )
archive_path = fields.Char(
help="The path where successfully retrieved messages will be stored.",
)
model_id = fields.Many2one( model_id = fields.Many2one(
comodel_name="ir.model", comodel_name="ir.model",
required=True, required=True,
@ -123,6 +126,7 @@ class FetchmailServerFolder(models.Model):
try: try:
# New connection per folder # New connection per folder
connection = this.server_id.connect() connection = this.server_id.connect()
this.check_imap_archive_folder(connection)
this.retrieve_imap_folder(connection) this.retrieve_imap_folder(connection)
connection.close() connection.close()
except Exception: except Exception:
@ -141,6 +145,20 @@ class FetchmailServerFolder(models.Model):
if connection: if connection:
connection.logout() connection.logout()
def check_imap_archive_folder(self, connection):
"""If archive folder specified, check existance and create when needed."""
self.ensure_one()
server = self.server_id
if not self.archive_path:
return
if connection.select(self.archive_path)[0] != "OK":
connection.create(self.archive_path)
if connection.select(self.archive_path)[0] != "OK":
raise UserError(
_("Could not create archive folder %(folder)s on server %(server)s")
% {"folder": self.archive_path, "server": server.name}
)
def retrieve_imap_folder(self, connection): def retrieve_imap_folder(self, connection):
"""Retrieve all mails for one IMAP folder.""" """Retrieve all mails for one IMAP folder."""
self.ensure_one() self.ensure_one()
@ -204,6 +222,8 @@ class FetchmailServerFolder(models.Model):
) )
matched = True if thread_id else False matched = True if thread_id else False
self.update_msg(connection, msgid, matched=matched) self.update_msg(connection, msgid, matched=matched)
if self.archive_path:
self._archive_msg(connection, msgid)
def fetch_msg(self, connection, msgid): def fetch_msg(self, connection, msgid):
"""Select a single message from a folder.""" """Select a single message from a folder."""
@ -227,3 +247,10 @@ class FetchmailServerFolder(models.Model):
else: else:
if self.flag_nonmatching: if self.flag_nonmatching:
connection.store(msgid, "+FLAGS", "\\FLAGGED") connection.store(msgid, "+FLAGS", "\\FLAGGED")
def _archive_msg(self, connection, msgid):
"""Archive message. Folder should already have been created."""
self.ensure_one()
connection.copy(msgid, self.archive_path)
connection.store(msgid, "+FLAGS", "\\Deleted")
connection.expunge()

View File

@ -10,3 +10,7 @@ Now when your users drop mails into this folder, they will be fetched by Odoo
and attached to the partner in question. After some testing, you might want to and attached to the partner in question. After some testing, you might want to
check `Delete matches` in your folder configuration so that this folder doesn't check `Delete matches` in your folder configuration so that this folder doesn't
grow indefinitely. grow indefinitely.
Another way to prevent having to process ever more messages from the folder
to read is to automatically move all processed messages to an archive folder
that can be specified.

View File

@ -0,0 +1,481 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Email gateway - folders</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="email-gateway-folders">
<h1 class="title">Email gateway - folders</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:907101d997473fcc276317ee6d3bf95f2f005eab22b585dd59644a757959ca10
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/server-tools/tree/16.0/fetchmail_attach_from_folder"><img alt="OCA/server-tools" src="https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-tools-16-0/server-tools-16-0-fetchmail_attach_from_folder"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/server-tools&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>Adds the possibility to attach emails from a certain IMAP folder to
objects, ie partners. Matching is done via several algorithms, ie email
address, email addresss domain or the original Odoo algorithm.</p>
<p>This gives a simple possibility to archive emails in Odoo without a mail
client integration.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a><ul>
<li><a class="reference internal" href="#exact-mailaddress" id="toc-entry-2">Exact mailaddress</a></li>
<li><a class="reference internal" href="#domain-of-email-addresses" id="toc-entry-3">Domain of email addresses</a></li>
<li><a class="reference internal" href="#odoo-standard" id="toc-entry-4">Odoo standard</a></li>
</ul>
</li>
<li><a class="reference internal" href="#usage" id="toc-entry-5">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-6">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-7">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-8">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-9">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-10">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
<p>In your fetchmail configuration, youll find a new list field
<tt class="docutils literal">Folders to&nbsp; monitor</tt>. Add your folders here in IMAP notation (usually
something like <tt class="docutils literal">INBOX.your_folder_name.your_subfolder_name</tt>), choose a
model to attach mails to and a matching algorithm to use.</p>
<div class="section" id="exact-mailaddress">
<h2><a class="toc-backref" href="#toc-entry-2">Exact mailaddress</a></h2>
<p>Fill in a field to search for the email address in <tt class="docutils literal">Field (model)</tt>.
For partners, this would be <tt class="docutils literal">email</tt>. Also fill in the header field
from the email to look at in <tt class="docutils literal">Field (email)</tt>. If you want to match
incoming mails from your customers, this would be <tt class="docutils literal">from</tt>. You can also
list header fields, so to match partners receiving this email, you might
fill in <tt class="docutils literal">to,cc,bcc</tt>.</p>
</div>
<div class="section" id="domain-of-email-addresses">
<h2><a class="toc-backref" href="#toc-entry-3">Domain of email addresses</a></h2>
<p>Match the domain of the email address(es) found in <tt class="docutils literal">Field (email)</tt>.
This would attach a mail to <tt class="docutils literal">test1&#64;example.com</tt> to a record with
<tt class="docutils literal">Field (model)</tt> set to <tt class="docutils literal">test2&#64;example.com</tt>. Given that this is a
fuzzy match, you probably want to check <tt class="docutils literal">Use 1st match</tt>, because
otherwise nothing happens if multiple possible matches are found.</p>
</div>
<div class="section" id="odoo-standard">
<h2><a class="toc-backref" href="#toc-entry-4">Odoo standard</a></h2>
<p>This is stricly speaking no matching algorithm, but calls the models
standard action on new incoming mail, which is usually creating a new
record.</p>
</div>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-5">Usage</a></h1>
<p>A widespread configuration is to have a shared mailbox with several
folders, i.e. one where users drop mails they want to attach to
partners. Let this folder be called <tt class="docutils literal">From partners</tt>. Then create a
folder configuration for your server with path <tt class="docutils literal">&quot;INBOX.From partners&quot;</tt>
(note the quotes because of the space, this is server dependent). Choose
model <tt class="docutils literal">Partners</tt>, set <tt class="docutils literal">Field (model)</tt> to <tt class="docutils literal">email</tt> and
<tt class="docutils literal">Field (email)</tt> to <tt class="docutils literal">from</tt>. In <tt class="docutils literal">Domain</tt>, you could fill in
<tt class="docutils literal"><span class="pre">[('customer',</span> <span class="pre">'=',</span> True)]</tt> to be sure to only match customer records.</p>
<p>Now when your users drop mails into this folder, they will be fetched by
Odoo and attached to the partner in question. After some testing, you
might want to check <tt class="docutils literal">Delete matches</tt> in your folder configuration so
that this folder doesnt grow indefinitely.</p>
<p>Another way to prevent having to process ever more messages from the
folder to read is to automatically move all processed messages to an
archive folder that can be specified.</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-6">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-tools/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/server-tools/issues/new?body=module:%20fetchmail_attach_from_folder%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-7">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-8">Authors</a></h2>
<ul class="simple">
<li>Therp BV</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-9">Contributors</a></h2>
<ul class="simple">
<li>Holger Brunn <a class="reference external" href="mailto:hbrunn&#64;therp.nl">hbrunn&#64;therp.nl</a></li>
<li>Ronald Portier <a class="reference external" href="mailto:ronald&#64;therp.nl">ronald&#64;therp.nl</a></li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-10">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/server-tools/tree/16.0/fetchmail_attach_from_folder">OCA/server-tools</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@ -24,6 +24,7 @@
<field name="active" invisible="True" /> <field name="active" invisible="True" />
<field name="sequence" widget="handle" /> <field name="sequence" widget="handle" />
<field name="path" /> <field name="path" />
<field name="archive_path" />
<field name="model_id" /> <field name="model_id" />
<field name="match_algorithm" /> <field name="match_algorithm" />
<field name="model_field" /> <field name="model_field" />
@ -98,6 +99,7 @@
</group> </group>
<group> <group>
<field name="active" /> <field name="active" />
<field name="archive_path" />
<field name="delete_matching" /> <field name="delete_matching" />
<field name="msg_state" /> <field name="msg_state" />
</group> </group>