changeset 8:a794183069ad 5.6 tip

Update to 5.6 series
author Cédric Krier <ced@b2ck.com>
date Fri, 17 Jul 2020 13:40:05 +0200
parents a80770f82311
children
files .drone.yml .flake8 doc/index.rst icons/tryton-marketing-mail.svg marketing.py marketing.xml message.xml setup.py tests/__init__.py tests/test_marketing_email.py tox.ini tryton.cfg view/email_list.xml view/email_list_form.xml view/email_list_list.xml view/email_message_form.xml
diffstat 16 files changed, 108 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/.drone.yml	Tue Nov 12 11:17:47 2019 +0100
+++ b/.drone.yml	Fri Jul 17 13:40:05 2020 +0200
@@ -1,6 +1,10 @@
 clone:
     hg:
         image: plugins/hg
+        environment:
+            - HG_SHARE_POOL=/root/.cache/hg
+        volumes:
+            - cache:/root/.cache
 
 pipeline:
     tox:
@@ -11,6 +15,10 @@
             - TOX_TESTENV_PASSENV=CFLAGS DB_CACHE
             - POSTGRESQL_URI=postgresql://postgres@postgresql:5432/
         commands:
+            - echo "[extensions]" >> /root/.hgrc
+            - echo "hgext.share =" >> /root/.hgrc
+            - echo "[share]" >> /root/.hgrc
+            - echo "pool = /root/.cache/hg" >> /root/.hgrc
             - pip install tox
             - tox -e "${TOXENV}-${DATABASE}"
         volumes:
@@ -19,6 +27,9 @@
 services:
     postgresql:
         image: postgres
+        environment:
+            - POSTGRES_HOST_AUTH_METHOD=trust
+        command: "-c fsync=off -c synchronous_commit=off -c full_page_writes=off"
         when:
             matrix:
                 DATABASE: postgresql
@@ -43,3 +54,9 @@
         - IMAGE: python:3.7
           TOXENV: py37
           DATABASE: postgresql
+        - IMAGE: python:3.8
+          TOXENV: py38
+          DATABASE: sqlite
+        - IMAGE: python:3.8
+          TOXENV: py38
+          DATABASE: postgresql
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.flake8	Fri Jul 17 13:40:05 2020 +0200
@@ -0,0 +1,2 @@
+[flake8]
+ignore=E123,E124,E126,E128,W503
--- a/doc/index.rst	Tue Nov 12 11:17:47 2019 +0100
+++ b/doc/index.rst	Fri Jul 17 13:40:05 2020 +0200
@@ -16,19 +16,20 @@
 
 Two actions are available:
 
-- *Request Subscribe* which sent an e-mail to confirm the subscription to a
+- *Request Subscribe* which sends an e-mail to confirm the subscription to a
   list.
 
-- *Request Unsubscribe* which sent an e-mail to confirm the unsubscription an
-  email of a list.
+- *Request Unsubscribe* which sends an e-mail to confirm the unsubscription of
+  an email address from the list.
 
 Message
 *******
 
-It stores a message to sent to all e-mails of a list. A message is defined by:
+It stores a message to send to all e-mails addresses on a list. A message is
+defined by:
 
     * From: the address from which the message is sent.
-    * List: the list of addresses to sent the message.
+    * List: the list of addresses to send the message to.
     * Title
     * Content
     * State:
@@ -37,8 +38,8 @@
         * Sending
         * Sent
 
-A wizard is available to send the message to a unique e-mail of the list for
-test purpose.
+A wizard is available that sends a message to a unique e-mail address from the
+list for test purposes.
 
 Configuration
 *************
@@ -47,10 +48,12 @@
 
 - `[marketing]`:
 
-    - `email_from`: The default `From` for the email sent.
+    - `email_from`: The default `From` for the e-mails that get sent.
 
     - `email_subscribe_url`: the URL to confirm the subscription to which the
       parameter `token` will be added.
 
-    - `email_unsubscribe_url`: the URL to unsubscribe an email to which the
-      parameter `token` will be added.
+    - `email_unsubscribe_url`: the URL to unsubscribe an e-mail address to
+      which the parameter `token` will be added.
+
+    - `email_spy_pixel`: A boolean to activate spy pixel. Disable by default.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/icons/tryton-marketing-mail.svg	Fri Jul 17 13:40:05 2020 +0200
@@ -0,0 +1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
\ No newline at end of file
--- a/marketing.py	Tue Nov 12 11:17:47 2019 +0100
+++ b/marketing.py	Fri Jul 17 13:40:05 2020 +0200
@@ -17,7 +17,6 @@
 except ImportError:
     html2text = None
 
-from trytond import backend
 from trytond.config import config
 from trytond.i18n import gettext
 from trytond.ir.session import token_hex
@@ -30,7 +29,7 @@
 from trytond.sendmail import sendmail_transactional, SMTPDataManager
 from trytond.tools import grouped_slice
 from trytond.transaction import Transaction
-from trytond.url import HOSTNAME
+from trytond.url import http_host
 from trytond.wizard import Wizard, StateView, StateTransition, Button
 
 from .exceptions import TemplateError
@@ -41,10 +40,7 @@
         'html', 'plugins-marketing.email.message-content',
         'fullpage')
 
-USE_SSL = bool(config.get('ssl', 'certificate'))
-URL_BASE = config.get('marketing', 'email_base',
-    default=urlunsplit(
-        ('http' + ('s' if USE_SSL else ''), HOSTNAME, '', '', '')))
+URL_BASE = config.get('marketing', 'email_base', default=http_host())
 URL_OPEN = urljoin(URL_BASE, '/m/empty.gif')
 
 
@@ -79,7 +75,7 @@
     web_user = fields.Function(
         fields.Many2One('web.user', "Web User"), 'get_web_user')
     party = fields.Function(
-        fields.Many2One('party.party', "Party"), 'get_party')
+        fields.Many2One('party.party', "Party"), 'get_web_user')
 
     @classmethod
     def __setup__(cls):
@@ -93,36 +89,42 @@
 
     @classmethod
     def __register__(cls, module_name):
-        TableHandler = backend.get('TableHandler')
-
         super().__register__(module_name)
 
-        table = TableHandler(cls, module_name)
-        table.index_action(['email', 'list_'], action='add')
-        table.index_action(['list_', 'active'], action='add')
+        table_h = cls.__table_handler__(module_name)
+        table_h.index_action(['email', 'list_'], action='add')
+        table_h.index_action(['list_', 'active'], action='add')
 
     @classmethod
     def default_email_token(cls, nbytes=None):
         return token_hex(nbytes)
 
     @classmethod
-    def get_web_user(cls, records, name):
+    def get_web_user(cls, records, names):
         pool = Pool()
         WebUser = pool.get('web.user')
-        result = dict.fromkeys(list(map(int, records)))
+        result = {}
+        web_user = 'web_user' in names
+        if web_user:
+            web_users = dict.fromkeys(list(map(int, records)))
+            result['web_user'] = web_users
+        party = 'party' in names
+        if party:
+            parties = dict.fromkeys(list(map(int, records)))
+            result['party'] = parties
         for sub_records in grouped_slice(records):
             email2id = {r.email: r.id for r in sub_records}
-            web_users = WebUser.search([
+            users = WebUser.search([
                     ('email', 'in', list(email2id.keys())),
                     ])
-            result.update((email2id[u.email], u.id) for u in web_users)
+            if web_user:
+                web_users.update((email2id[u.email], u.id) for u in users)
+            if party:
+                parties.update(
+                    (email2id[u.email], u.party.id)
+                    for u in users if u.party)
         return result
 
-    def get_party(self, name):
-        if self.web_user and self.web_user.party:
-            return self.web_user.party.id
-        return None
-
     @classmethod
     def _format_email(cls, records):
         for record in records:
@@ -193,7 +195,7 @@
         parts = urlsplit(url)
         tokens = filter(
             None, parse_qs(parts.query).get('token', [None]))
-        return cls.unsubscribe(list(tokens))
+        cls.unsubscribe(list(tokens))
 
     @classmethod
     def unsubscribe(cls, tokens):
@@ -334,6 +336,8 @@
             'readonly': _states['readonly'],
             },
         depends=['state'] + _depends)
+    urls = fields.One2Many(
+        'web.shortened_url', 'record', "URLs", readonly=True)
     state = fields.Selection([
             ('draft', "Draft"),
             ('sending', "Sending"),
@@ -384,7 +388,7 @@
                 gettext('marketing_email'
                     '.msg_message_invalid_content',
                     message=self.rec_name,
-                  exception=exception)) from exception
+                    exception=exception)) from exception
 
     @classmethod
     @ModelView.button
@@ -412,6 +416,8 @@
     def process(cls, messages=None, emails=None, smtpd_datamanager=None):
         pool = Pool()
         WebShortener = pool.get('web.shortened_url')
+        spy_pixel = config.getboolean(
+            'marketing', 'email_spy_pixel', default=False)
 
         @lru_cache(None)
         def short(url, record):
@@ -432,7 +438,7 @@
                             href = short(href, str(message))
                             attrs |= [(QName('href'), href)]
                             data = tag, attrs
-                    elif kind is END and data == 'body':
+                    elif kind is END and data == 'body' and spy_pixel:
                         yield START, (QName('img'), Attrs([
                                     (QName('src'), short(
                                             URL_OPEN, str(message))),
--- a/marketing.xml	Tue Nov 12 11:17:47 2019 +0100
+++ b/marketing.xml	Fri Jul 17 13:40:05 2020 +0200
@@ -3,6 +3,11 @@
 this repository contains the full copyright notices and license terms. -->
 <tryton>
     <data>
+        <record model="ir.ui.icon" id="mail_icon">
+            <field name="name">tryton-marketing-mail</field>
+            <field name="path">icons/tryton-marketing-mail.svg</field>
+        </record>
+
         <record model="ir.ui.view" id="email_view_form">
             <field name="model">marketing.email</field>
             <field name="type">form</field>
--- a/message.xml	Tue Nov 12 11:17:47 2019 +0100
+++ b/message.xml	Fri Jul 17 13:40:05 2020 +0200
@@ -4,7 +4,7 @@
 <tryton>
     <data grouped="1">
         <record model="ir.message" id="msg_email_list_unique">
-            <field name="text">Email address can only be subscribed once on each list.</field>
+            <field name="text">Email addresses can only be subscribed once to each list.</field>
         </record>
 
         <record model="ir.message" id="msg_message_invalid_content">
--- a/setup.py	Tue Nov 12 11:17:47 2019 +0100
+++ b/setup.py	Fri Jul 17 13:40:05 2020 +0200
@@ -73,8 +73,8 @@
     keywords='tryton marketing email list',
     package_dir={'trytond.modules.marketing_email': '.'},
     packages=(
-        ['trytond.modules.marketing_email'] +
-        ['trytond.modules.marketing_email.%s' % p for p in find_packages()]
+        ['trytond.modules.marketing_email']
+        + ['trytond.modules.marketing_email.%s' % p for p in find_packages()]
         ),
     package_data={
         'trytond.modules.marketing_email': (info.get('xml', [])
@@ -88,7 +88,8 @@
         'Intended Audience :: Developers',
         'Intended Audience :: Financial and Insurance Industry',
         'Intended Audience :: Legal Industry',
-        'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
+        'License :: OSI Approved :: '
+        'GNU General Public License v3 or later (GPLv3+)',
         'Natural Language :: Bulgarian',
         'Natural Language :: Catalan',
         'Natural Language :: Chinese (Simplified)',
--- a/tests/__init__.py	Tue Nov 12 11:17:47 2019 +0100
+++ b/tests/__init__.py	Fri Jul 17 13:40:05 2020 +0200
@@ -2,7 +2,7 @@
 # this repository contains the full copyright notices and license terms.
 
 try:
-    from trytond.modules.marketing_email.tests.test_marketing_email import suite
+    from trytond.modules.marketing_email.tests.test_marketing_email import suite  # noqa: E501
 except ImportError:
     from .test_marketing_email import suite
 
--- a/tests/test_marketing_email.py	Tue Nov 12 11:17:47 2019 +0100
+++ b/tests/test_marketing_email.py	Fri Jul 17 13:40:05 2020 +0200
@@ -25,17 +25,23 @@
         super().setUp()
         if not config.has_section('marketing'):
             config.add_section('marketing')
-        subscribe_url = config.get('marketing', 'email_subscribe_url')
+        subscribe_url = config.get(
+            'marketing', 'email_subscribe_url', default='')
         config.set('marketing', 'email_subscribe_url', SUBSCRIBE_URL)
         self.addCleanup(
             lambda: config.set(
                 'marketing', 'email_subscribe_url', subscribe_url))
-        unsubscribe_url = config.get('marketing', 'email_unsubscribe_url')
+        unsubscribe_url = config.get(
+            'marketing', 'email_unsubscribe_url', default='')
         config.set('marketing', 'email_unsubscribe_url', UNSUBSCRIBE_URL)
         self.addCleanup(
             lambda: config.set(
                 'marketing', 'email_unsubscribe_url', unsubscribe_url))
-        from_ = config.get('email', 'from')
+        spy_pixel = config.get('marketing', 'email_spy_pixel', default='')
+        config.set('marketing', 'email_spy_pixel', 'true')
+        self.addCleanup(
+            lambda: config.set('marketing', 'email_spy_pixel', spy_pixel))
+        from_ = config.get('email', 'from', default='')
         config.set('email', 'from', FROM)
         self.addCleanup(lambda: config.set('email', 'from', from_))
 
@@ -111,7 +117,7 @@
         self.assertFalse(email.active)
 
     @with_transaction()
-    def test_sent_messages(self):
+    def test_send_messages(self):
         "Test messages are sent to the list"
         pool = Pool()
         Email = pool.get('marketing.email')
--- a/tox.ini	Tue Nov 12 11:17:47 2019 +0100
+++ b/tox.ini	Fri Jul 17 13:40:05 2020 +0200
@@ -1,15 +1,15 @@
 [tox]
-envlist = {py34,py35,py36,py37}-{sqlite,postgresql},pypy3-{sqlite,postgresql}
+envlist = {py35,py36,py37,py38}-{sqlite,postgresql},pypy3-{sqlite,postgresql}
 
 [testenv]
 commands = {envpython} setup.py test
 deps =
-    {py34,py35,py36,py37}-postgresql: psycopg2 >= 2.5
+    {py35,py36,py37,py38}-postgresql: psycopg2 >= 2.5
     pypy3-postgresql: psycopg2cffi >= 2.5
-    {py34,py35,py36}-sqlite: sqlitebck
+    {py35,py36}-sqlite: sqlitebck
 setenv =
     sqlite: TRYTOND_DATABASE_URI={env:SQLITE_URI:sqlite://}
     postgresql: TRYTOND_DATABASE_URI={env:POSTGRESQL_URI:postgresql://}
     sqlite: DB_NAME={env:SQLITE_NAME::memory:}
     postgresql: DB_NAME={env:POSTGRESQL_NAME:test}
-install_command = pip install --pre --process-dependency-links {opts} {packages}
+install_command = pip install --pre --find-links https://trydevpi.tryton.org/ {opts} {packages}
--- a/tryton.cfg	Tue Nov 12 11:17:47 2019 +0100
+++ b/tryton.cfg	Fri Jul 17 13:40:05 2020 +0200
@@ -1,5 +1,5 @@
 [tryton]
-version=5.2.0
+version=5.6.0
 depends:
     ir
     marketing
--- a/view/email_list.xml	Tue Nov 12 11:17:47 2019 +0100
+++ b/view/email_list.xml	Fri Jul 17 13:40:05 2020 +0200
@@ -2,8 +2,8 @@
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
 <tree>
-    <field name="list_" expand="1"/>
-    <field name="email" expand="1"/>
+    <field name="list_" expand="2"/>
+    <field name="email" expand="2"/>
     <field name="party" expand="1"/>
     <field name="web_user" expand="1"/>
 </tree>
--- a/view/email_list_form.xml	Tue Nov 12 11:17:47 2019 +0100
+++ b/view/email_list_form.xml	Fri Jul 17 13:40:05 2020 +0200
@@ -8,4 +8,7 @@
     <field name="active"/>
     <label name="language"/>
     <field name="language"/>
+    <group id="links" col="-1" colspan="4">
+        <link icon="tryton-marketing-mail" name="marketing_email.act_email_message_form"/>
+    </group>
 </form>
--- a/view/email_list_list.xml	Tue Nov 12 11:17:47 2019 +0100
+++ b/view/email_list_list.xml	Fri Jul 17 13:40:05 2020 +0200
@@ -2,6 +2,6 @@
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
 <tree>
-    <field name="name"/>
-    <field name="language"/>
+    <field name="name" expand="2"/>
+    <field name="language" expand="1"/>
 </tree>
--- a/view/email_message_form.xml	Tue Nov 12 11:17:47 2019 +0100
+++ b/view/email_message_form.xml	Fri Jul 17 13:40:05 2020 +0200
@@ -8,11 +8,18 @@
     <field name="list_" colspan="3"/>
     <label name="title"/>
     <field name="title" colspan="3"/>
-    <label name="content" yfill="1" yalign="0"/>
-    <group col="-1" id="content" colspan="3" yexpand="1">
-        <field name="content"/>
-        <field name="content" string="Edit" widget="html" xexpand="0"/>
-    </group>
+    <notebook colspan="4">
+        <page name="content">
+            <label name="content" yfill="1" yalign="0"/>
+            <group col="-1" id="content" colspan="3" yexpand="1">
+                <field name="content"/>
+                <field name="content" string="Edit" widget="html" xexpand="0"/>
+            </group>
+        </page>
+        <page name="urls">
+            <field name="urls" colspan="4"/>
+        </page>
+    </notebook>
     <label name="state"/>
     <field name="state"/>
     <group col="-1" colspan="2" id="buttons">