diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 80baa60d343..3b25ca5fcf4 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -154,6 +154,19 @@ class Website(models.Model): self.env['ir.qweb'].clear_caches() return result + @api.multi + def unlink(self): + # Do not delete invoices, delete what's strictly necessary + attachments_to_unlink = self.env['ir.attachment'].search([ + ('website_id', 'in', self.ids), + '|', '|', + ('key', '!=', False), # theme attachment + ('url', 'ilike', '.custom.'), # customized theme attachment + ('url', 'ilike', '.assets\\_'), + ]) + attachments_to_unlink.unlink() + return super(Website, self).unlink() + # ---------------------------------------------------------- # Page Management # ---------------------------------------------------------- @@ -896,7 +909,7 @@ class WebsiteMultiMixin(models.AbstractModel): _name = 'website.multi.mixin' _description = 'Multi Website Mixin' - website_id = fields.Many2one('website', string='Website', help='Restrict publishing to this website.') + website_id = fields.Many2one('website', string='Website', ondelete='restrict', help='Restrict publishing to this website.') @api.multi def can_access_from_current_website(self, website_id=False): @@ -1183,7 +1196,7 @@ class Menu(models.Model): page_id = fields.Many2one('website.page', 'Related Page', ondelete='cascade') new_window = fields.Boolean('New Window') sequence = fields.Integer(default=_default_sequence) - website_id = fields.Many2one('website', 'Website') + website_id = fields.Many2one('website', 'Website', ondelete='cascade') parent_id = fields.Many2one('website.menu', 'Parent Menu', index=True, ondelete="cascade") child_id = fields.One2many('website.menu', 'parent_id', string='Child Menus') parent_path = fields.Char(index=True) diff --git a/addons/website/tests/test_ui.py b/addons/website/tests/test_ui.py index a9e9d502dbf..a38b69b7ee0 100644 --- a/addons/website/tests/test_ui.py +++ b/addons/website/tests/test_ui.py @@ -5,6 +5,58 @@ import odoo import odoo.tests +@odoo.tests.tagged('-at_install', 'post_install') +class TestUiCustomizeTheme(odoo.tests.HttpCase): + def test_01_attachment_website_unlink(self): + ''' Some ir.attachment needs to be unlinked when a website is unlink, + otherwise some flows will just crash. That's the case when 2 website + have their theme color customized. Removing a website will make its + customized attachment generic, thus having 2 attachments with the + same URL available for other websites, leading to singleton errors + (among other). + + But no all attachment should be deleted, eg we don't want to delete + a SO or invoice PDF coming from an ecommerce order. + ''' + Website = self.env['website'] + Page = self.env['website.page'] + Attachment = self.env['ir.attachment'] + + website_default = Website.browse(1) + website_test = Website.create({'name': 'Website Test'}) + + # simulate attachment state when editing 2 theme through customize + custom_url = '/TEST/website/static/src/scss/options/colors/user_theme_color_palette.custom.web.assets_common.scss' + scss_attachment = Attachment.create({ + 'name': custom_url, + 'type': 'binary', + 'mimetype': 'text/scss', + 'datas': '', + 'datas_fname': custom_url, + 'url': custom_url, + 'website_id': website_default.id + }) + scss_attachment.copy({'website_id': website_test.id}) + + # simulate PDF from ecommerce order + # Note: it will only have its website_id flag if the website has a domain + # equal to the current URL (fallback or get_current_website()) + so_attachment = Attachment.create({ + 'name': 'SO036.pdf', + 'type': 'binary', + 'mimetype': 'application/pdf', + 'datas': '', + 'website_id': website_test.id + }) + + # avoid sql error on page website_id restrict + Page.search([('website_id', '=', website_test.id)]).unlink() + website_test.unlink() + self.assertEqual(Attachment.search_count([('url', '=', custom_url)]), 1, 'Should not left duplicates when deleting a website') + self.assertTrue(so_attachment.exists(), 'Most attachment should not be deleted') + self.assertFalse(so_attachment.website_id, 'Website should be removed') + + @odoo.tests.tagged('-at_install', 'post_install') class TestUiHtmlEditor(odoo.tests.HttpCase): def test_html_editor_multiple_templates(self): diff --git a/addons/website_payment/models/payment_acquirer.py b/addons/website_payment/models/payment_acquirer.py index fe0c00f9a26..2bd0dca903c 100644 --- a/addons/website_payment/models/payment_acquirer.py +++ b/addons/website_payment/models/payment_acquirer.py @@ -6,4 +6,4 @@ from odoo import fields, models class PaymentAcquirer(models.Model): _inherit = 'payment.acquirer' - website_id = fields.Many2one('website') + website_id = fields.Many2one('website', ondelete='restrict') diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py index ea29837836c..e9e1df9cf3c 100644 --- a/addons/website_sale/models/product.py +++ b/addons/website_sale/models/product.py @@ -23,7 +23,7 @@ class ProductPricelist(models.Model): domain = [('company_id', '=', company_id)] return self.env['website'].search(domain, limit=1) - website_id = fields.Many2one('website', string="Website", default=_default_website) + website_id = fields.Many2one('website', string="Website", ondelete='restrict', default=_default_website) code = fields.Char(string='E-commerce Promotional Code', groups="base.group_user") selectable = fields.Boolean(help="Allow the end user to choose this price list") diff --git a/addons/website_sale_wishlist/models/product_wishlist.py b/addons/website_sale_wishlist/models/product_wishlist.py index 0a67caa597c..d456cecd8fb 100644 --- a/addons/website_sale_wishlist/models/product_wishlist.py +++ b/addons/website_sale_wishlist/models/product_wishlist.py @@ -18,7 +18,7 @@ class ProductWishlist(models.Model): currency_id = fields.Many2one('res.currency', related='pricelist_id.currency_id', readonly=True) pricelist_id = fields.Many2one('product.pricelist', string='Pricelist', help='Pricelist when added') price = fields.Monetary(digits=0, currency_field='currency_id', string='Price', help='Price of the product when it has been added in the wishlist') - website_id = fields.Many2one('website', required=True) + website_id = fields.Many2one('website', ondelete='cascade', required=True) active = fields.Boolean(default=True, required=True) @api.model diff --git a/addons/website_twitter/models/website_twitter_tweet.py b/addons/website_twitter/models/website_twitter_tweet.py index 4e80e439296..6fa6fb1564c 100644 --- a/addons/website_twitter/models/website_twitter_tweet.py +++ b/addons/website_twitter/models/website_twitter_tweet.py @@ -8,7 +8,7 @@ class WebsiteTwitterTweet(models.Model): _name = 'website.twitter.tweet' _description = 'Website Twitter' - website_id = fields.Many2one('website', string='Website') + website_id = fields.Many2one('website', string='Website', ondelete='cascade') screen_name = fields.Char(string='Screen Name') tweet = fields.Text(string='Tweets')