diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py
index b847cd5a9dd..e3fb18813f1 100644
--- a/addons/website_sale/models/sale_order.py
+++ b/addons/website_sale/models/sale_order.py
@@ -133,6 +133,32 @@ class sale_order_line(osv.Model):
'discounted_price': fields.function(_fnct_get_discounted_price, string='Discounted price', type='float', digits_compute=dp.get_precision('Product Price')),
}
+class sale_order_line(osv.Model):
+ _inherit = "sale.order.line"
+
+ def _fnct_get_discounted_price(self, cr, uid, ids, field_name, args, context=None):
+ res = dict.fromkeys(ids, False)
+ for line in self.browse(cr, uid, ids, context=context):
+ res[line.id] = (line.price_unit * (1.0 - (line.discount or 0.0) / 100.0))
+ return res
+
+ _columns = {
+ 'discounted_price': fields.function(_fnct_get_discounted_price, string='Discounted price', type='float', digits_compute=dp.get_precision('Product Price')),
+ }
+
+class sale_order_line(osv.Model):
+ _inherit = "sale.order.line"
+
+ def _fnct_get_discounted_price(self, cr, uid, ids, field_name, args, context=None):
+ res = dict.fromkeys(ids, False)
+ for line in self.browse(cr, uid, ids, context=context):
+ res[line.id] = (line.price_unit * (1.0 - (line.discount or 0.0) / 100.0))
+ return res
+
+ _columns = {
+ 'discounted_price': fields.function(_fnct_get_discounted_price, string='Discounted price', type='float', digits_compute=dp.get_precision('Product Price')),
+ }
+
class website(orm.Model):
_inherit = 'website'
diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js
index 18b47dfe285..0149232558d 100644
--- a/addons/website_sale/static/src/js/website_sale.js
+++ b/addons/website_sale/static/src/js/website_sale.js
@@ -101,7 +101,7 @@ $('.oe_website_sale').each(function () {
$(oe_website_sale).on("change", 'input[name="add_qty"]', function (event) {
var product_ids = [];
- var product_dom = $(".js_add_cart_variants[data-attribute_value_ids]").last();
+ var product_dom = $(".js_product .js_add_cart_variants[data-attribute_value_ids]").last();
if (!product_dom.length) {
return;
}
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index b00e2c893e3..6784fb9eeca 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -1478,10 +1478,13 @@
-
-
-
+
+
+
+
+
+
diff --git a/addons/website_sale_delivery/__openerp__.py b/addons/website_sale_delivery/__openerp__.py
index c05689e506f..b03a495ccaf 100644
--- a/addons/website_sale_delivery/__openerp__.py
+++ b/addons/website_sale_delivery/__openerp__.py
@@ -12,7 +12,6 @@ Delivery Costs
'data': [
'views/website_sale_delivery.xml',
'views/website_sale_delivery_view.xml',
- 'security/ir.model.access.csv',
'demo/website_sale_delivery_data.xml'
],
'demo': [
diff --git a/addons/website_sale_delivery/models/sale_order.py b/addons/website_sale_delivery/models/sale_order.py
index b5d25bd8a1e..f5d49849783 100644
--- a/addons/website_sale_delivery/models/sale_order.py
+++ b/addons/website_sale_delivery/models/sale_order.py
@@ -109,21 +109,24 @@ class SaleOrder(orm.Model):
def _get_delivery_methods(self, cr, uid, order, context=None):
carrier_obj = self.pool.get('delivery.carrier')
- delivery_ids = carrier_obj.search(cr, uid, [('website_published', '=', True)], context=context)
+ carrier_ids = carrier_obj.search(cr, SUPERUSER_ID, [('website_published', '=', True)], context=context)
available_carrier_ids = []
# Following loop is done to avoid displaying delivery methods who are not available for this order
# This can surely be done in a more efficient way, but at the moment, it mimics the way it's
# done in delivery_set method of sale.py, from delivery module
- for carrier in carrier_obj.browse(cr, SUPERUSER_ID, delivery_ids, context=dict(context, order_id=order.id)):
+
+ new_context = dict(context, order_id=order.id)
+ for carrier in carrier_ids:
try:
- _logger.debug("Checking availability of %s" % carrier.name)
- if carrier.available:
- available_carrier_ids.append(carrier.id)
+ _logger.debug("Checking availability of carrier #%s" % carrier)
+ available = carrier_obj.read(cr, SUPERUSER_ID, [carrier], fields=['available'], context=new_context)[0]['available']
+ if available:
+ available_carrier_ids = available_carrier_ids + [carrier]
except ValidationError as e:
# RIM: hack to remove in master, because available field should not depend on a SOAP call to external shipping provider
# The validation error is used in backend to display errors in fedex config, but should fail silently in frontend
- _logger.debug("%s removed from e-commerce carrier list. %s" % (carrier.name, e))
+ _logger.debug("Carrier #%s removed from e-commerce carrier list. %s" % (carrier, e))
return available_carrier_ids
diff --git a/addons/website_sale_delivery/security/ir.model.access.csv b/addons/website_sale_delivery/security/ir.model.access.csv
deleted file mode 100644
index 69e804cfd28..00000000000
--- a/addons/website_sale_delivery/security/ir.model.access.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-access_delivery_carrier_public,delivery.carrier.public,delivery.model_delivery_carrier,,1,0,0,0
diff --git a/addons/website_slides/static/src/less/website_slides.less b/addons/website_slides/static/src/less/website_slides.less
index fc4cb43e9f7..12469b90f46 100644
--- a/addons/website_slides/static/src/less/website_slides.less
+++ b/addons/website_slides/static/src/less/website_slides.less
@@ -12,6 +12,11 @@
@MAX-Z-INDEX : 2147483647;
} .define-color-if-not-defined();
+.top_covered_image {
+ object-fit: cover;
+ object-position: top;
+}
+
.oe_slides_panel_footer {
background-color: @navbar-default-bg;
}
@@ -45,6 +50,7 @@ section.oe_slides_promote_box {
}
img.oe_slides_channel_thumbnail {
+ .top_covered_image();
height: 250px;
width: 100%
}
@@ -54,6 +60,7 @@ img.oe_slides_opacity {
}
img.oe_slides_promote_image {
+ .top_covered_image();
max-height: 300px;
width: 100%;
overflow: hidden;
@@ -65,6 +72,7 @@ img.oe_slides_promote_image {
}
.oe_slides_apart_small {
+ .top_covered_image();
height: 60px;
width: 90px;
}
@@ -97,6 +105,7 @@ textarea.o_slides_comment {
}
.oe_slides_grid_thumbnail {
+ .top_covered_image();
height: 200px;
width: 100%;
overflow: hidden;
diff --git a/doc/cla/corporate/abstract.md b/doc/cla/corporate/abstract.md
new file mode 100644
index 00000000000..7c1f520bfd8
--- /dev/null
+++ b/doc/cla/corporate/abstract.md
@@ -0,0 +1,18 @@
+Italy, 2015-11-05
+
+Abstract Srl agrees to the terms of the Odoo Corporate Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Davide Corio davide.corio@abstract.it https://github.com/dcorio
+
+
+List of contributors:
+
+Davide Corio davide.corio@abstract.it https://github.com/dcorio
+Simone Orsi simone.orsi@abstract.it https://github.com/simahawk
+Giacomo Spettoli giacomo.spettoli@abstract.it https://github.com/giacomos
diff --git a/doc/cla/corporate/brain-tec.md b/doc/cla/corporate/brain-tec.md
new file mode 100644
index 00000000000..f598f64eb1b
--- /dev/null
+++ b/doc/cla/corporate/brain-tec.md
@@ -0,0 +1,40 @@
+Switzerland, 2015-11-09
+
+brain-tec AG agrees to the terms of the Odoo Corporate Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Pascal Zenklusen pascal.zenklusen@braintec-group.com https://github.com/BT-pzenklusen
+
+List of contributors:
+
+Álvaro Estébanez López alvaro.estebanez@braintec-group.com https://github.com/BT-aestebanez
+Andreas Stauder andreas.stauder@braintec-group.com https://github.com/BT-astauder
+Carlos Millán de Silva carlos.millan@braintec-group.com https://github.com/BT-cmillan
+Cesar Andres Sanchez cesar-andres.sanchez@braintec-group.com https://github.com/BT-csanchez
+Carlos Serra Toro carlos.serra@braintec-group.com https://github.com/BT-cserra
+Dominik Schleich dominik.schleich@braintec-group.com https://github.com/BT-dschleich
+Frédéric Aebi frederic.aebi@braintec-group.com https://github.com/BT-faebi
+Fréderic Garbely frederic.garbely@braintec-group.com https://github.com/BT-fgarbely
+Florian Wonneberger florian.wonneberger@braintec-group.com https://github.com/BT-fwonneberger
+Jorge Asensio jorge.asensio@braintec-group.com https://github.com/BT-jasensio
+Jose J. Duran-Martin jose-javier.duran@braintec-group.com https://github.com/BT-jduran
+Federico Javier Mesa Hidalgo javier.mesa@braintec-group.com https://github.com/BT-jmesa
+Joël Michaud joel.michaud@braintec-group.com https://github.com/BT-jmichaud
+Jose Montero jose.montero@braintec-group.com https://github.com/BT-jmontero
+José Luis Vallejo Diaz jose.vallejo@braintec-group.com https://github.com/BT-jvallejo
+Kumar Aberer kumar.aberer@braintec-group.com https://github.com/BT-kaberer
+Matt Fasola matt.fasola@braintec-group.com https://github.com/BT-mfasola
+Miguel Tallon Benitez miguel.tallon@braintec-group.com https://github.com/BT-mtallon
+Nicolas Frei nicolas.frei@braintec-group.com https://github.com/BT-nfrei
+Nathanael Leutenegger nathanael.leutenegger@braintec-group.com https://github.com/BT-nleutenegger
+Olivier Jossen olivier.jossen@braintec-group.com https://github.com/BT-ojossen
+Philipp Fux philipp.fux@braintec-group.com https://github.com/BT-pfux
+Raúl Martín raul.martin@braintec-group.com https://github.com/BT-rmartin
+Silvan Wyden silvan.wyden@braintec-group.com https://github.com/BT-swyden
+Timka Piric Muratovic timka.piric@braintec-group.com https://github.com/BT-tpiric
+Pascal Zenklusen pascal.zenklusen@braintec-group.com https://github.com/BT-pzenklusen
diff --git a/doc/cla/corporate/elico-corporation.md b/doc/cla/corporate/elico-corporation.md
new file mode 100644
index 00000000000..aa5e28564bc
--- /dev/null
+++ b/doc/cla/corporate/elico-corporation.md
@@ -0,0 +1,30 @@
+China, 2015-12-10
+
+Elico Corporation agrees to the terms of the Odoo Corporate Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Eric Caudal eric.caudal@elico-corp.com https://github.com/elicoidal
+
+List of contributors:
+Eric Caudal eric.caudal@elico-corp.com https://github.com/elicoidal
+Sébastien Maillard sebastien.maillard@elico-corp.com https://github.com/seb-elico
+Siyuan Gu siyuancn@hotmail.com https://github.com/siyuancn
+Wang Liping lynn.config@gmail.com https://github.com/lynndotconfig
+Luke Zheng cialuo@126.com https://github.com/cialuo
+Gary Wei gary.wei@elico-corp.com https://github.com/Garywei1314
+Yaxi Zheng zheng.yaxi@elico-corp.com https://github.com/Elico-Yaxi
+Faust Huang faust_huang@hotmail.com https://github.com/fausthuang
+Yu Lin yu_lin@me.com https://github.com/uynil
+Xie Xiaopeng hellomyjob@163.com https://github.com/xie8899
+Connie Xiao connie.xiao@elico-corp.com https://github.com/ConnieXiao
+Alex Duan alex.duan@elico-corp.com https://github.com/duanyp1991
+Liu Lixia liulixia19901988@163.com https://github.com/liulixia1990
+Liliane Li liliane.li@elico-corp.com http://github.com/lilianeandbrody
+Rona Lin ssauapw@qq.com https://github.com/Rona111
+Bob Luo luo.yumeng0831@gmail.com https://github.com/m6519578
+Waller Zhang waller.zhang@elico-corp.com https://github.com/wallerzhang
diff --git a/doc/cla/corporate/kmee.md b/doc/cla/corporate/kmee.md
new file mode 100644
index 00000000000..e29245abf56
--- /dev/null
+++ b/doc/cla/corporate/kmee.md
@@ -0,0 +1,21 @@
+KMEE, Brazil, 2015-12-04
+
+KMEE INFORMATICA LTDA agrees to the terms of the Odoo Corporate
+Contributor License Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Luis Felipe Miléo mileo@kmee.com.br https://github.com/kmee
+
+List of contributors:
+
+Luis Felipe Miléo mileo@kmee.com.br https://github.com/mileo
+Ananias Pereira Batista Filho ananias@kmee.com.br https://github.com/ananiasfilho
+Michell Stuttigart michell.stuttigart@kmee.com.br https://github.com/mstuttgart
+Fernando Marcato Rodrigues fernando.marcato@kmee.com.br https://github.com/fernandomr
+Daniel Sadamo daniel.sadamo@kmee.com.br https://github.com/sadamo
+Bianca Tella bianca.tella@kmee.com.br https://github.com/biancatella
+Luiz Felipe do Divino luiz.divino@kmee.com.br https://github.com/lfdivino
\ No newline at end of file
diff --git a/doc/cla/corporate/sodexis.md b/doc/cla/corporate/sodexis.md
index 2a642474497..3b7544acd9c 100644
--- a/doc/cla/corporate/sodexis.md
+++ b/doc/cla/corporate/sodexis.md
@@ -17,3 +17,4 @@ Suganthi Karunanithi ksuganthi@sodexis.com https://github.com/suganthikarunanith
Xavier Dass xavier@sodexis.com https://github.com/xavier-dass
Atchuthan atchuthan@sodexis.com https://github.com/atchuthan
Dhinesh dhinesh@sodexis.com https://github.com/dvdhinesh
+SodexisTeam dev@sodexis.com https://github.com/SodexisTeam
diff --git a/doc/cla/corporate/sudokeys.md b/doc/cla/corporate/sudokeys.md
new file mode 100644
index 00000000000..9d93b7182e5
--- /dev/null
+++ b/doc/cla/corporate/sudokeys.md
@@ -0,0 +1,16 @@
+FRANCE, 2016-01-06
+
+Sudokeys agrees to the terms of the Odoo Corporate Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+David GUESSAB david@sudokeys.com https://github.com/chouab
+
+List of contributors:
+
+David GUESSAB david@sudokeys.com https://github.com/chouab
+Nicolas JEUDY nicolas@sudokeys.com https://github.com/njeudy
diff --git a/doc/cla/individual/The-Compiler.md b/doc/cla/individual/The-Compiler.md
new file mode 100644
index 00000000000..45d0819ba67
--- /dev/null
+++ b/doc/cla/individual/The-Compiler.md
@@ -0,0 +1,12 @@
+Switzerland, 2015-12-11
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Florian Bruhin git@the-compiler.org https://github.com/The-Compiler
+Florian Bruhin me@the-compiler.org https://github.com/The-Compiler
diff --git a/doc/cla/individual/jcarlosmontoya.md b/doc/cla/individual/jcarlosmontoya.md
new file mode 100644
index 00000000000..e10276c9f3c
--- /dev/null
+++ b/doc/cla/individual/jcarlosmontoya.md
@@ -0,0 +1,11 @@
+Spain, 2015-12-15
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Juan Carlos Montoya juancarlos.montoya.chamba@gmail.com https://github.com/jcarlosmontoya
diff --git a/doc/howtos/themes.rst b/doc/howtos/themes.rst
index 49d5126c8e5..6e73d2420d0 100644
--- a/doc/howtos/themes.rst
+++ b/doc/howtos/themes.rst
@@ -13,10 +13,6 @@ Ready to create your own theme? Great. Here are some things you should know befo
.. image:: theme_tutorial_assets/img/Intro.jpg
-.. warning::
-
- This tutorial requires having installed Odoo v9.0 and the Theme Support Engine (website_less) module. https://www.odoo.com/apps/9.0/website_less/
-
An introduction for web designers
=================================
@@ -250,7 +246,7 @@ Open the ``__openerp__.py`` you created and copy/paste the following:
'data': [
],
'category': 'Theme/Creative',
- 'depends': ['website', 'website_less'],
+ 'depends': ['website'],
}
Replace the first four property’s values with anything you like.
@@ -263,7 +259,7 @@ The ``data`` property will contain the xml files list. Right now it’s empty, b
``category`` defines your module category (always “Theme”) and, after a slash, the subcategory. You can use one subcategory from the Odoo Apps categories list. (https://www.odoo.com/apps/themes)
-``depends`` specifies the modules needed by our theme to work properly. For our tutorial theme, we only need website and website_less. If you need blogging or eCommerce features as well, you have to add those modules too.
+``depends`` specifies the modules needed by our theme to work properly. For our tutorial theme, we only need website. If you need blogging or eCommerce features as well, you have to add those modules too.
.. code-block:: python
@@ -691,7 +687,7 @@ We start by adding a new file in our views folder - name it **options.xml** and
.. code-block:: xml
-
+
@@ -832,7 +828,7 @@ This time we have to inherit ``assets_editor`` instead of ``assets_frontend``.
.. code-block:: xml
-
+
diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py
index f257c5705b4..ed4afe211cf 100644
--- a/openerp/addons/base/ir/ir_model.py
+++ b/openerp/addons/base/ir/ir_model.py
@@ -1103,10 +1103,31 @@ class ir_model_data(osv.osv):
},context=context)
else:
if mode=='init' or (mode=='update' and xml_id):
+ inherit_xml_ids = []
+ for table, field_name in model_obj._inherits.items():
+ xml_ids = self.pool['ir.model.data'].search(cr, uid, [
+ ('module', '=', module),
+ ('name', '=', xml_id + '_' + table.replace('.', '_')),
+ ], context=context)
+ # XML ID found in the database, try to recover an existing record
+ if xml_ids:
+ found_xml_id = self.pool['ir.model.data'].browse(cr, uid, xml_ids[0], context=context)
+ record = self.pool[found_xml_id.model].browse(cr, uid, [found_xml_id.res_id], context=context)[0]
+ # The record exists, store the id and don't recreate the XML ID
+ if record.exists():
+ inherit_xml_ids.append(found_xml_id.model)
+ values[field_name] = found_xml_id.res_id
+ # Orphan XML ID, delete it
+ else:
+ found_xml_id.unlink()
+
res_id = model_obj.create(cr, uid, values, context=context)
if xml_id:
if model_obj._inherits:
for table in model_obj._inherits:
+ if table in inherit_xml_ids:
+ continue
+
inherit_id = model_obj.browse(cr, uid,
res_id,context=context)[model_obj._inherits[table]]
self.create(cr, SUPERUSER_ID, {
diff --git a/openerp/addons/base/ir/ir_translation.py b/openerp/addons/base/ir/ir_translation.py
index 92bfd049b81..a474935e34c 100644
--- a/openerp/addons/base/ir/ir_translation.py
+++ b/openerp/addons/base/ir/ir_translation.py
@@ -675,7 +675,7 @@ class ir_translation(osv.osv):
return ir_translation_import_cursor(cr, uid, self, context=context)
def load_module_terms(self, cr, modules, langs, context=None):
- context = dict(context or {}) # local copy
+ context_template = dict(context or {}) # local copy
# make sure the given languages are active
lang_obj = self.pool['res.lang']
for lang in langs:
@@ -686,6 +686,7 @@ class ir_translation(osv.osv):
if not modpath:
continue
for lang in langs:
+ context = dict(context_template)
lang_code = tools.get_iso_codes(lang)
base_lang_code = None
if '_' in lang_code:
diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py
index 2e8f71f234f..4e73c00f852 100644
--- a/openerp/addons/base/ir/ir_ui_view.py
+++ b/openerp/addons/base/ir/ir_ui_view.py
@@ -619,10 +619,15 @@ class view(osv.osv):
requested (similar to ``id``)
"""
if context is None: context = {}
+ context = context.copy()
# if view_id is not a root view, climb back to the top.
base = v = self.browse(cr, uid, view_id, context=context)
+ check_view_ids = context.setdefault('check_view_ids', [])
while v.mode != 'primary':
+ # Add inherited views to the list of loading forced views
+ # Otherwise, inherited views could not find elements created in their direct parents if that parent is defined in the same module
+ check_view_ids.append(v.id)
v = v.inherit_id
root_id = v.id
diff --git a/openerp/http.py b/openerp/http.py
index 2d68fd216ff..e1a70fa72c6 100644
--- a/openerp/http.py
+++ b/openerp/http.py
@@ -433,8 +433,8 @@ def route(route=None, **kw):
* ``user``: The user must be authenticated and the current request
will perform using the rights of the user.
- * ``admin``: The user may not be authenticated and the current request
- will perform using the admin user.
+ * ``public``: The user may or may not be authenticated. If she isn't,
+ the current request will perform using the shared Public user.
* ``none``: The method is always active, even if there is no
database. Mainly used by the framework and authentication
modules. There request code will not have any facilities to access
diff --git a/openerp/models.py b/openerp/models.py
index 08b36b673a2..9b144be4438 100644
--- a/openerp/models.py
+++ b/openerp/models.py
@@ -1382,6 +1382,7 @@ class BaseModel(object):
from openerp.http import request
Users = self.pool['res.users']
for group_ext_id in groups.split(','):
+ group_ext_id = group_ext_id.strip()
if group_ext_id == 'base.group_no_one':
# check: the group_no_one is effective in debug mode only
if Users.has_group(cr, uid, group_ext_id) and request and request.debug:
diff --git a/openerp/tools/mail.py b/openerp/tools/mail.py
index cd18ebd8fa1..2eb6a6cf55a 100644
--- a/openerp/tools/mail.py
+++ b/openerp/tools/mail.py
@@ -11,7 +11,7 @@ import re
import socket
import threading
import time
-from email.utils import getaddresses
+from email.utils import getaddresses, formataddr
import openerp
from openerp.loglevels import ustr
@@ -632,10 +632,10 @@ def append_content_to_html(html, content, plaintext=True, preserve=False, contai
#----------------------------------------------------------
# matches any email in a body of text
-email_re = re.compile(r"""([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})""", re.VERBOSE)
+email_re = re.compile(r"""([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63})""", re.VERBOSE)
# matches a string containing only one email
-single_email_re = re.compile(r"""^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$""", re.VERBOSE)
+single_email_re = re.compile(r"""^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$""", re.VERBOSE)
res_re = re.compile(r"\[([0-9]+)\]", re.UNICODE)
command_re = re.compile("^Set-([a-z]+) *: *(.+)$", re.I + re.UNICODE)
@@ -709,3 +709,15 @@ def email_split(text):
# is strictly required in RFC2822's `addr-spec`.
if addr[1]
if '@' in addr[1]]
+
+def email_split_and_format(text):
+ """ Return a list of email addresses found in ``text``, formatted using
+ formataddr. """
+ if not text:
+ return []
+ return [formataddr((addr[0], addr[1])) for addr in getaddresses([text])
+ # getaddresses() returns '' when email parsing fails, and
+ # sometimes returns emails without at least '@'. The '@'
+ # is strictly required in RFC2822's `addr-spec`.
+ if addr[1]
+ if '@' in addr[1]]