Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
cubicweb
cubes
email
Commits
c8a5ac67d5a1
Commit
c17935f1
authored
Jul 12, 2022
by
Laurent Peuch
Browse files
fix: backport missing work from pypi
parent
e05d6bc70adc
Pipeline
#140437
failed with stages
in 35 seconds
Changes
33
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
MANIFEST.in
View file @
c8a5ac67
include *.py
include views/*.py
include bin/*
include cubicweb-email.spec
recursive-include data external_resources *.gif
recursive-include i18n *.pot *.po
recursive-include migration *.py
recursive-include views *.py
recursive-include doc *.txt *.html
exclude test/data/database/*
include requirements-test.txt
include tox.ini
include README.rst
exclude .cube-doctor.yml
exclude .gitlab-ci.yml
exclude .gitlab-ci-extended.yml
exclude .yamllint
include */*.py
include *requirements.txt
recursive-include cubicweb_email *.py
recursive-include cubicweb_email/data *.gif *.png *.ico *.css *.js
recursive-include cubicweb_email/i18n *.po
recursive-include cubicweb_email/wdoc *
recursive-include test/data bootstrap_cubes *.py
recursive-include test/data/maildir/cur *,S
include test/data/*mbox
include *.ini
prune cubicweb-email.spec
prune debian
PKG-INFO
0 → 100644
View file @
c8a5ac67
Metadata-Version: 1.1
Name: cubicweb-email
Version: 1.13.1
Summary: email component for the CubicWeb framework
Home-page: http://www.cubicweb.org/project/cubicweb-email
Author: Logilab
Author-email: contact@logilab.fr
License: LGPL
Description: This cube models multipart email messages (`Emails` and `EmailPart`) and
provides tools to import your mail box into a cubicweb instance.
Email are automatically stored into`EmailThreads`.
It also comes with a command line tool to import emails (mbox) files
in a CubicWeb instance. This tool is based on cwclientlib_, so
connection credentials to the cubicweb instance should be stored in
your ~/.config/cwclientlibrc file.
.. _cwclientlib: https://www.cubicweb.org/project/cwclientlib
Platform: UNKNOWN
Classifier: Environment :: Web Environment
Classifier: Framework :: CubicWeb
Classifier: Programming Language :: Python
Classifier: Programming Language :: JavaScript
README
.rst
→
README
View file @
c8a5ac67
File moved
__pkginfo__.py
deleted
100644 → 0
View file @
e05d6bc7
# pylint: disable-msg=W0622
"""cubicweb-email packaging information"""
modname
=
'email'
distname
=
"cubicweb-%s"
%
modname
numversion
=
(
1
,
12
,
0
)
version
=
'.'
.
join
(
str
(
num
)
for
num
in
numversion
)
license
=
'LGPL'
author
=
"Logilab"
author_email
=
"contact@logilab.fr"
web
=
'https://forge.extranet.logilab.fr/cubicweb/cubes/%s'
%
distname
description
=
"email component for the CubicWeb framework"
classifiers
=
[
'Environment :: Web Environment'
,
'Framework :: CubicWeb'
,
'Programming Language :: Python'
,
'Programming Language :: JavaScript'
,
]
# used packages
__depends__
=
{
'cubicweb'
:
">= 3.20.0, < 3.32.0"
,
'cubicweb-file'
:
'>= 1.9.0'
,
'logilab-common'
:
'>= 0.58.3'
,
'cwclientlib'
:
'>= 0.3.1'
,
}
__recommends__
=
{
'cubicweb-comment'
:
None
}
# packaging ###
from
os
import
listdir
as
_listdir
from
os.path
import
join
,
isdir
from
glob
import
glob
THIS_CUBE_DIR
=
join
(
'share'
,
'cubicweb'
,
'cubes'
,
modname
)
def
listdir
(
dirpath
):
return
[
join
(
dirpath
,
fname
)
for
fname
in
_listdir
(
dirpath
)
if
fname
[
0
]
!=
'.'
and
not
fname
.
endswith
(
'.pyc'
)
and
not
fname
.
endswith
(
'~'
)
and
not
isdir
(
join
(
dirpath
,
fname
))]
data_files
=
[
# common files
[
THIS_CUBE_DIR
,
[
fname
for
fname
in
glob
(
'*.py'
)
if
fname
!=
'setup.py'
]],
]
# check for possible extended cube layout
for
dirname
in
(
'entities'
,
'views'
,
'sobjects'
,
'hooks'
,
'schema'
,
'data'
,
'i18n'
,
'migration'
,
'wdoc'
):
if
isdir
(
dirname
):
data_files
.
append
([
join
(
THIS_CUBE_DIR
,
dirname
),
listdir
(
dirname
)])
# Note: here, you'll need to add subdirectories if you want
# them to be included in the debian package
scripts
=
[
'bin/cw-mboximport'
]
packages
=
[
'cwemail'
]
bin/cw-mboximport
View file @
c8a5ac67
...
...
@@ -2,4 +2,3 @@
from
cwemail.mboximport
import
main
main
()
__init__.py
→
cubicweb_email/
__init__.py
View file @
c8a5ac67
"""cubicweb-email"""
cubicweb_email/__pkginfo__.py
0 → 100644
View file @
c8a5ac67
# pylint: disable=W0622
"""cubicweb-email packaging information"""
modname
=
'email'
distname
=
"cubicweb-%s"
%
modname
numversion
=
(
1
,
13
,
1
)
version
=
'.'
.
join
(
str
(
num
)
for
num
in
numversion
)
license
=
'LGPL'
author
=
"Logilab"
author_email
=
"contact@logilab.fr"
web
=
'http://www.cubicweb.org/project/%s'
%
distname
description
=
"email component for the CubicWeb framework"
classifiers
=
[
'Environment :: Web Environment'
,
'Framework :: CubicWeb'
,
'Programming Language :: Python'
,
'Programming Language :: JavaScript'
,
]
__depends__
=
{
'cubicweb'
:
'>= 3.24'
,
'cubicweb-file'
:
'>= 1.9.0'
,
'cwclientlib'
:
'>= 0.3.1'
,
}
emailcites.py
→
cubicweb_email/
emailcites.py
View file @
c8a5ac67
...
...
@@ -21,31 +21,34 @@ def parse_body(body, quote='>'):
newlevel
+=
1
line
=
line
[
1
:].
lstrip
()
if
line
.
strip
()
==
'--'
:
break
# ignore following signature
break
# ignore following signature
if
newlevel
!=
level
:
# detect lines such as
# On Wed, Jan 23, 2008 at 06:48:22PM +0100, machin wrote:
# preceding citation
if
not
newlevel
in
seenlevels
and
res
and
res
[
-
1
][
1
]
and
\
res
[
-
1
][
1
][
-
1
].
rstrip
().
endswith
(
':'
):
if
(
newlevel
not
in
seenlevels
and
res
and
res
[
-
1
][
1
]
and
res
[
-
1
][
1
][
-
1
].
rstrip
().
endswith
(
':'
)
):
current
.
insert
(
0
,
res
[
-
1
][
1
].
pop
())
seenlevels
.
add
(
newlevel
)
res
.
append
(
(
level
,
current
)
)
res
.
append
((
level
,
current
))
current
=
[]
level
=
newlevel
current
.
append
(
line
)
res
.
append
(
(
level
,
current
)
)
res
.
append
((
level
,
current
))
pmsg
=
ParsedMessage
()
for
level
,
lines
in
res
:
para
=
''
.
join
(
lines
).
strip
()
if
not
para
:
continue
if
level
>
0
:
if
level
>
0
:
pmsg
.
cites
.
append
(
para
)
else
:
pmsg
.
content
.
append
(
para
)
return
pmsg
class
ParsedMessage
(
object
):
def
__init__
(
self
):
self
.
content
=
[]
...
...
@@ -54,6 +57,7 @@ class ParsedMessage(object):
@
property
def
actual_content
(
self
):
return
'
\n
'
.
join
(
self
.
content
)
@
property
def
cited_content
(
self
):
return
'
\n
'
.
join
(
self
.
cites
)
...
...
entities.py
→
cubicweb_email/
entities.py
View file @
c8a5ac67
...
...
@@ -13,7 +13,7 @@ from logilab.common import umessage
from
cubicweb.entities
import
AnyEntity
,
fetch_config
,
adapters
from
cubicweb.predicates
import
is_instance
from
cub
es.
email.emailcites
import
parse_body
from
cub
icweb_
email.emailcites
import
parse_body
class
Email
(
AnyEntity
):
...
...
@@ -77,9 +77,9 @@ class Email(AnyEntity):
result
.
update
(
refs
.
split
())
return
result
lines_rgx
=
re
.
compile
(
'^Lines:\s*\d+\s*
\n
'
,
re
.
I
|
re
.
U
|
re
.
M
)
clength_rgx
=
re
.
compile
(
'^Content-Length:\s*\d+\s*
\n
'
,
re
.
I
|
re
.
U
|
re
.
M
)
ctype_rgx
=
re
.
compile
(
'^Content-Type:[^:]'
,
re
.
I
|
re
.
U
|
re
.
M
)
lines_rgx
=
re
.
compile
(
r
'^Lines:\s*\d+\s*\n'
,
re
.
I
|
re
.
U
|
re
.
M
)
clength_rgx
=
re
.
compile
(
r
'^Content-Length:\s*\d+\s*\n'
,
re
.
I
|
re
.
U
|
re
.
M
)
ctype_rgx
=
re
.
compile
(
r
'^Content-Type:[^:]'
,
re
.
I
|
re
.
U
|
re
.
M
)
def
umessage_headers
(
self
):
if
not
self
.
headers
:
...
...
@@ -87,8 +87,7 @@ class Email(AnyEntity):
headers
=
self
.
lines_rgx
.
sub
(
''
,
self
.
headers
)
headers
=
self
.
clength_rgx
.
sub
(
''
,
headers
)
headers
=
self
.
ctype_rgx
.
sub
(
'Content-type: text/plain; charset=utf8'
,
headers
)
return
umessage
.
message_from_string
(
headers
+
u
'
\n\n
'
)
return
umessage
.
message_from_string
(
headers
+
'
\n\n
'
)
class
EmailPart
(
AnyEntity
):
...
...
@@ -141,4 +140,3 @@ class EmailPartIFTIAdapter(adapters.IFTIndexableAdapter):
return
[]
except
AttributeError
:
return
super
(
EmailPartIFTIAdapter
,
self
).
get_words
()
hooks.py
→
cubicweb_email/
hooks.py
View file @
c8a5ac67
...
...
@@ -27,9 +27,9 @@ def fix_ownership(cnx, eid, email):
if
sender
and
sender
.
e_schema
==
'CWUser'
and
sender
.
eid
!=
cnx
.
user
.
eid
:
# match a user which is not the connection's user, set owned_by / created_by
cnx
.
execute
(
'SET X owned_by U WHERE X eid %(x)s, U eid %(u)s'
,
{
'x'
:
eid
,
'u'
:
sender
.
eid
})
{
'x'
:
eid
,
'u'
:
sender
.
eid
})
cnx
.
execute
(
'SET X created_by U WHERE X eid %(x)s, U eid %(u)s'
,
{
'x'
:
eid
,
'u'
:
sender
.
eid
})
{
'x'
:
eid
,
'u'
:
sender
.
eid
})
class
ExtractEmailInformation
(
hook
.
Operation
):
...
...
@@ -54,7 +54,7 @@ class ExtractEmailInformation(hook.Operation):
else
:
try
:
self
.
insert_comment
(
origeid
,
part
)
except
:
except
Exception
:
self
.
exception
(
'while generating comment on %s from email %s'
,
origeid
,
email
)
...
...
@@ -107,7 +107,9 @@ class AddEmailCommentHook(hook.Hook):
AnalyzeEmailText
(
self
.
_cw
,
email
=
self
.
entity
)
CLEANUP_RGX
=
re
.
compile
(
r
'\bre\s*:'
,
re
.
I
|
re
.
U
)
CLEANUP_RGX
=
re
.
compile
(
r
'\bre\s*:'
,
re
.
I
|
re
.
U
)
def
cleanup_subject
(
string
):
return
CLEANUP_RGX
.
sub
(
''
,
string
).
strip
()
...
...
@@ -148,6 +150,8 @@ class AddEmailPreHook(hook.Hook):
message
=
UMessage
(
email
.
message_from_string
(
msg
.
headers
))
except
Exception
:
self
.
exception
(
'bad message headers'
)
if
self
.
_cw
.
repo
.
config
.
mode
==
'test'
:
raise
return
# XXX why limit to a single sender?
if
'messageid'
not
in
msg
.
cw_edited
:
...
...
@@ -157,9 +161,13 @@ class AddEmailPreHook(hook.Hook):
if
'date'
not
in
msg
.
cw_edited
:
msg
.
cw_edited
[
'date'
]
=
message
.
date
()
if
'sender'
not
in
msg
.
cw_edited
:
sender
=
message
.
multi_addrs
(
'from'
)[
0
]
sendereid
=
self
.
address_eid
(
sender
[
1
],
sender
[
0
])
msg
.
cw_edited
[
'sender'
]
=
sendereid
try
:
sender
=
message
.
multi_addrs
(
'from'
)[
0
]
except
IndexError
:
pass
else
:
sendereid
=
self
.
address_eid
(
sender
[
1
],
sender
[
0
])
msg
.
cw_edited
[
'sender'
]
=
sendereid
replyto
=
message
.
get
(
'in-reply-to'
)
if
replyto
and
'reply_to'
not
in
msg
.
cw_edited
:
rset
=
self
.
_cw
.
find
(
'Email'
,
messageid
=
replyto
)
...
...
i18n/en.po
→
cubicweb_email/
i18n/en.po
View file @
c8a5ac67
File moved
i18n/fr.po
→
cubicweb_email/
i18n/fr.po
View file @
c8a5ac67
File moved
migration/1.1.0_Any.py
→
cubicweb_email/
migration/1.1.0_Any.py
View file @
c8a5ac67
File moved
migration/1.1.1_Any.py
→
cubicweb_email/
migration/1.1.1_Any.py
View file @
c8a5ac67
File moved
migration/1.11.0_Any.py
→
cubicweb_email/
migration/1.11.0_Any.py
View file @
c8a5ac67
File moved
migration/1.2.2_Any.py
→
cubicweb_email/
migration/1.2.2_Any.py
View file @
c8a5ac67
File moved
cubicweb_email/migration/1.4.1_Any.py
0 → 100644
View file @
c8a5ac67
change_relation_props
(
'Email'
,
'sender'
,
'EmailAddress'
,
commit
=
True
,
cardinality
=
'?*'
)
migration/postcreate.py
→
cubicweb_email/
migration/postcreate.py
View file @
c8a5ac67
# postcreate script. You could setup a workflow here for example
schema.py
→
cubicweb_email/
schema.py
View file @
c8a5ac67
...
...
@@ -6,10 +6,8 @@
"""
__docformat__
=
"restructuredtext en"
try
:
from
cubicweb
import
_
except
ImportError
:
_
=
unicode
from
cubicweb
import
_
# pylint: disable-msg=E0611,F0401
from
yams.buildobjs
import
(
SubjectRelation
,
RelationType
,
EntityType
,
...
...
@@ -21,34 +19,34 @@ from cubicweb.schema import ERQLExpression
class
Email
(
EntityType
):
"""electronic mail"""
subject
=
String
(
fulltextindexed
=
True
)
date
=
Datetime
(
description
=
_
(
'UTC time on which the mail was sent'
))
subject
=
String
(
fulltextindexed
=
True
)
date
=
Datetime
(
description
=
_
(
'UTC time on which the mail was sent'
))
messageid
=
String
(
required
=
True
,
indexed
=
True
,
unique
=
True
)
headers
=
String
(
description
=
_
(
'raw headers'
))
headers
=
String
(
description
=
_
(
'raw headers'
))
sender
=
SubjectRelation
(
'EmailAddress'
,
cardinality
=
'?*'
)
sender
=
SubjectRelation
(
'EmailAddress'
,
cardinality
=
'?*'
)
# an email with only Bcc is acceptable, don't require any recipients
recipients
=
SubjectRelation
(
'EmailAddress'
)
cc
=
SubjectRelation
(
'EmailAddress'
)
cc
=
SubjectRelation
(
'EmailAddress'
)
parts
=
SubjectRelation
(
'EmailPart'
,
cardinality
=
'*1'
,
composite
=
'subject'
)
attachment
=
SubjectRelation
(
'File'
)
parts
=
SubjectRelation
(
'EmailPart'
,
cardinality
=
'*1'
,
composite
=
'subject'
)
attachment
=
SubjectRelation
(
'File'
)
reply_to
=
SubjectRelation
(
'Email'
,
cardinality
=
'?*'
)
cites
=
SubjectRelation
(
'Email'
)
in_thread
=
SubjectRelation
(
'EmailThread'
,
cardinality
=
'?*'
)
reply_to
=
SubjectRelation
(
'Email'
,
cardinality
=
'?*'
)
cites
=
SubjectRelation
(
'Email'
)
in_thread
=
SubjectRelation
(
'EmailThread'
,
cardinality
=
'?*'
)
class
EmailPart
(
EntityType
):
"""an email attachment"""
__permissions__
=
{
'read'
:
(
'managers'
,
'users'
,
'guests'
,),
# XXX if E parts X, U has_read_permission E
'add'
:
(
'managers'
,
ERQLExpression
(
'E parts X, U has_update_permission E'
),),
'read'
:
(
'managers'
,
'users'
,
'guests'
,),
# XXX if E parts X, U has_read_permission E
'add'
:
(
'managers'
,
ERQLExpression
(
'E parts X, U has_update_permission E'
),),
'delete'
:
(
'managers'
,
ERQLExpression
(
'E parts X, U has_update_permission E'
)),
'update'
:
(
'managers'
,
'owners'
,),
}
}
content
=
String
(
fulltextindexed
=
True
)
content
=
String
(
fulltextindexed
=
True
)
content_format
=
String
(
required
=
True
,
maxsize
=
50
)
ordernum
=
Int
(
required
=
True
)
alternative
=
SubjectRelation
(
'EmailPart'
,
symmetric
=
True
)
...
...
@@ -60,33 +58,37 @@ class EmailThread(EntityType):
see_also
=
SubjectRelation
(
'EmailThread'
)
forked_from
=
SubjectRelation
(
'EmailThread'
,
cardinality
=
'?*'
)
class
parts
(
RelationType
):
""" """
fulltext_container
=
'subject'
class
sender
(
RelationType
):
""" """
inlined
=
True
class
in_thread
(
RelationType
):
""" """
inlined
=
True
class
reply_to
(
RelationType
):
""" """
inlined
=
True
class
generated_by
(
RelationType
):
"""mark an entity as generated from an email"""
cardinality
=
'?*'
subject
=
(
'TrInfo'
,)
object
=
'Email'
# if comment is installed
if
'Comment'
in
context
.
defined
:
class
comment_generated_by
(
RelationDefinition
):
subject
=
'Comment'
name
=
'generated_by'
object
=
'Email'
testutils.py
→
cubicweb_email/
testutils.py
View file @
c8a5ac67
...
...
@@ -37,5 +37,6 @@ class EmailValueGenerator(ValueGenerator):
This is required since the 'headers' attribute should be plain
ascii only
"""
def
generate_Email_headers
(
self
,
entity
,
index
):
return
headers
Prev
1
2
Next
Laurent Peuch
@bram
mentioned in commit
c2d72a00470a
·
Jul 12, 2022
mentioned in commit
c2d72a00470a
mentioned in commit b71a6fda4b8a6ecdd98b411deae764f7bf41abf8
Toggle commit list
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment