diff options
Diffstat (limited to 'addons/website')
| -rw-r--r-- | addons/website/controllers/backend.py | 2 | ||||
| -rw-r--r-- | addons/website/data/website_data.xml | 2 | ||||
| -rw-r--r-- | addons/website/models/ir_actions.py | 2 | ||||
| -rw-r--r-- | addons/website/models/ir_ui_view.py | 9 | ||||
| -rw-r--r-- | addons/website/static/src/js/backend/dashboard.js | 3 | ||||
| -rw-r--r-- | addons/website/static/src/js/content/menu.js | 12 | ||||
| -rw-r--r-- | addons/website/static/src/js/menu/customize.js | 9 | ||||
| -rw-r--r-- | addons/website/static/src/scss/compatibility/bs3_for_12_0.scss | 300 | ||||
| -rw-r--r-- | addons/website/static/src/scss/website.ui.scss | 1 | ||||
| -rw-r--r-- | addons/website/tests/test_views.py | 53 | ||||
| -rw-r--r-- | addons/website/views/snippets.xml | 6 | ||||
| -rw-r--r-- | addons/website/views/website_templates.xml | 6 |
12 files changed, 390 insertions, 15 deletions
diff --git a/addons/website/controllers/backend.py b/addons/website/controllers/backend.py index da8a9118a4a..b558620a0f5 100644 --- a/addons/website/controllers/backend.py +++ b/addons/website/controllers/backend.py @@ -61,6 +61,6 @@ class WebsiteBackend(http.Controller): request.env['res.config.settings'].create({ 'google_management_client_id': ga_client_id, 'google_analytics_key': ga_analytics_key, - 'website_id': current_website, + 'website_id': current_website.id, }).execute() return True diff --git a/addons/website/data/website_data.xml b/addons/website/data/website_data.xml index 28c0a0f8e85..3a54d5ce29c 100644 --- a/addons/website/data/website_data.xml +++ b/addons/website/data/website_data.xml @@ -78,7 +78,7 @@ </div> </section> </div> - <div class="text-center mt-3" name="mail_button"> + <div class="text-center my-3" name="mail_button"> <a role="button" t-attf-href="mailto:{{ res_company.email }}" class="btn btn-primary" id="o_contact_mail">Send us an email</a> </div> </div> diff --git a/addons/website/models/ir_actions.py b/addons/website/models/ir_actions.py index f26d6263004..4add5587d7f 100644 --- a/addons/website/models/ir_actions.py +++ b/addons/website/models/ir_actions.py @@ -38,7 +38,7 @@ class ServerAction(models.Model): def _get_website_url(self): for action in self: if action.state == 'code' and action.website_published: - action.website_url = self._compute_website_url(action.website_path, action.xml_id) + action.website_url = action._compute_website_url(action.website_path, action.xml_id) @api.model def _get_eval_context(self, action): diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index 84089d5c0c6..75b1a8d4fe4 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -45,17 +45,20 @@ class View(models.Model): view.with_context(no_cow=True).key = 'website.key_%s' % str(uuid.uuid4())[:6] if not view.website_id and current_website_id and not self._context.get('no_cow'): # If already a specific view for this generic view, write on it - website_specific_view = self.env['ir.ui.view'].search([ + website_specific_view = view.search([ ('key', '=', view.key), ('website_id', '=', current_website_id) ], limit=1) if not website_specific_view: # Set key to avoid copy() to generate an unique key as we want the specific view to have the same key - website_specific_view = view.copy({'website_id': current_website_id, 'key': view.key}) + copy_vals = {'website_id': current_website_id, 'key': view.key} + if vals.get('inherit_id'): + copy_vals['inherit_id'] = vals['inherit_id'] + website_specific_view = view.copy(copy_vals) view._create_website_specific_pages_for_view(website_specific_view, view.env['website'].browse(current_website_id)) - for inherit_child in view.inherit_children_ids: + for inherit_child in view.inherit_children_ids.filter_duplicate(): # COW won't be triggered if there is already a website_id on the view, we should copy the view ourself if inherit_child.website_id.id == current_website_id: inherit_child.copy({'inherit_id': website_specific_view.id, 'key': inherit_child.key}) diff --git a/addons/website/static/src/js/backend/dashboard.js b/addons/website/static/src/js/backend/dashboard.js index b4fe22ba84e..1a83e8dc39a 100644 --- a/addons/website/static/src/js/backend/dashboard.js +++ b/addons/website/static/src/js/backend/dashboard.js @@ -44,6 +44,9 @@ var Dashboard = AbstractAction.extend(ControlPanelMixin, { var self = this; return $.when(ajax.loadLibs(this), this._super()).then(function() { return self.fetch_data(); + }).then(function(){ + var website = _.findWhere(self.websites, {selected: true}); + self.website_id = website ? website.id : false; }); }, diff --git a/addons/website/static/src/js/content/menu.js b/addons/website/static/src/js/content/menu.js index c5371e3c5fb..42a12414557 100644 --- a/addons/website/static/src/js/content/menu.js +++ b/addons/website/static/src/js/content/menu.js @@ -98,6 +98,17 @@ sAnimation.registry.autohideMenu = sAnimation.Class.extend({ def.resolve(); }); }); + + // The previous code will make sure we wait for images to be fully + // loaded before initializing the auto more menu. But in some cases, + // it is not enough, we also have to wait for fonts or even extra + // scripts. Those will have no impact on the feature in most cases + // though, so we will only update the auto more menu at that time, + // no wait for it to initialize the feature. + var $window = $(window); + $window.on('load.autohideMenu', function () { + $window.trigger('resize'); + }); } return $.when.apply($, defs).then(function () { if (!self.noAutohide) { @@ -112,6 +123,7 @@ sAnimation.registry.autohideMenu = sAnimation.Class.extend({ destroy: function () { this._super.apply(this, arguments); if (!this.noAutohide) { + $(window).off('.autohideMenu'); dom.destroyAutoMoreMenu(this.$el); } }, diff --git a/addons/website/static/src/js/menu/customize.js b/addons/website/static/src/js/menu/customize.js index d8ce436248e..b931e938cc6 100644 --- a/addons/website/static/src/js/menu/customize.js +++ b/addons/website/static/src/js/menu/customize.js @@ -18,8 +18,14 @@ var CustomizeMenu = Widget.extend({ /** * @override */ - start: function () { + willStart: function () { this.viewName = $(document.documentElement).data('view-xmlid'); + return this._super.apply(this, arguments); + }, + /** + * @override + */ + start: function () { if (!this.viewName) { _.defer(this.destroy.bind(this)); } @@ -27,6 +33,7 @@ var CustomizeMenu = Widget.extend({ if (this.$el.is('.show')) { this._loadCustomizeOptions(); } + return this._super.apply(this, arguments); }, //-------------------------------------------------------------------------- diff --git a/addons/website/static/src/scss/compatibility/bs3_for_12_0.scss b/addons/website/static/src/scss/compatibility/bs3_for_12_0.scss new file mode 100644 index 00000000000..82beeec2cbf --- /dev/null +++ b/addons/website/static/src/scss/compatibility/bs3_for_12_0.scss @@ -0,0 +1,300 @@ +/** + * This file's purpose is to *ease* migration from 11.0. + */ + +// Restore gray utilities +$-compat-gray-map: ( + 'gray-darker': '900', + 'gray-dark': '900', + 'gray': '700', + 'gray-light': '600', + 'gray-lighter': '200', +); +@each $old, $new in $-compat-gray-map { + @include bg-variant(".bg-#{$old}", gray($new)); + @include text-emphasis-variant(".text-#{$old}", gray($new)); +} + +// Restore media ? + +// Restore progress bars +@each $color, $value in $theme-colors { + @include bg-variant(".progress-bar-#{$color}", $value); +} + +// Adapt radio ? + +// Adapt labels +.label { + @extend .badge; +} +@each $color, $value in $theme-colors { + .label-#{$color} { + @include badge-variant($value); + } +} +.label-default { + @include badge-variant(theme-color('secondary')); +} + +// Adapt center-block +.center-block { + display: block; + margin: auto; +} + +// Adapt pull-* classes +.pull-left { + float: left; +} +.pull-right { + float: right; +} + +// Adapt pagination +.pagination > li { + @extend .page-item; + > a { + @extend .page-link; + } +} + +// Adapt carousel +.carousel .item { + @extend .carousel-item; +} + +// Adapt checkboxes ? + +// Adapt tables +.table-condensed { + @extend .table-sm; +} + +// Adapt forms +.control-label { + @extend .col-form-label; +} +.help-block { + @extend .form-text; +} +.has-error .form-control { + @extend .is-invalid; +} +.has-success .form-control { + @extend .is-valid; +} +.form-horizontal .form-group { + @extend .row; +} + +// Adapt list-inline +.list-inline > li { + @extend .list-inline-item; +} + +// Adapt list-group +.panel .list-group { + @extend .list-group-flush; +} + +// Adapt image utilies +.img-rounded { + @extend .rounded; +} +.img-circle { + @extend .rounded-circle; +} + +// Adapt input group +.input-group { + .input-group-btn:first-child { + @extend .input-group-prepend; + } + .form-control ~ .input-group-btn { + @extend .input-group-append; + } + .input-group-addon { + @extend .input-group-append; + @extend .input-group-text; + } +} + +// Adapt panels +.panel { + @extend .card; +} +@each $color, $value in $theme-colors { + @include bg-variant(".panel-#{$color}", $value); +} +@include bg-variant(".panel-default", $white); +.panel-heading { + @extend .card-header; +} +.panel-body { + @extend .card-body; +} +.panel-footer { + @extend .card-footer; +} +.well { + @extend .card; + @extend .card-body; +} + +// Adapt grid (push-pull, xs ?) +@each $breakpoint in map-keys($grid-breakpoints) { + $infix: breakpoint-infix($breakpoint, $grid-breakpoints); + $infix: if($infix != "", $infix, "-xs"); + + @include media-breakpoint-up($breakpoint, $grid-breakpoints) { + // `$grid-columns - 1` because offsetting by the width of an entire row isn't possible + @for $i from 0 through ($grid-columns - 1) { + .col#{$infix}-offset-#{$i} { + @include make-col-offset($i, $grid-columns); + } + } + } +} + +// Adapt breadcrumb +.breadcrumb > li { + @extend .breadcrumb-item; +} + +// Adapt nav +.nav > li { + @extend .nav-item; + > a { + @extend .nav-link; + } +} +.nav-stacked { + flex-direction: column; +} +@include bg-variant(".navbar-default", $light); + +// Adapt img-responsive +.img-responsive { + @extend .img-fluid; +} + +// Adapt dropdowns +.dropdown-menu { + a { + @extend .dropdown-item; + } + .divider { + @extend .dropdown-divider; + } +} +.dropdown-toggle .caret { + display: none; +} + +// Adapt buttons +.btn-default { + @include button-variant(theme-color('secondary'), theme-color('secondary')); +} + +// Adapt display classes +.hidden, .hide { + display: none !important; +} +.hidden { + &-xs { + @include media-breakpoint-down(sm) { + display: none; + } + } + &-sm { + @include media-breakpoint-only(md) { + display: none; + } + } + &-md { + @include media-breakpoint-only(lg) { + display: none; + } + } + &-lg { + @include media-breakpoint-only(xl) { + display: none; + } + } + &-print { + @media print { + display: none; + } + } +} +.show { + display: block !important; +} +.visible { + &-xs { + &-block, &-inline-block { + display: none; + } + @include media-breakpoint-down(sm) { + &-block { + display: block; + } + &-inline-block { + display: inline-block; + } + } + } + &-sm { + &-block, &-inline-block { + display: none; + } + @include media-breakpoint-only(md) { + &-block { + display: block; + } + &-inline-block { + display: inline-block; + } + } + } + &-md { + &-block, &-inline-block { + display: none; + } + @include media-breakpoint-only(lg) { + &-block { + display: block; + } + &-inline-block { + display: inline-block; + } + } + } + &-lg { + &-block, &-inline-block { + display: none; + } + @include media-breakpoint-only(xl) { + &-block { + display: block; + } + &-inline-block { + display: inline-block; + } + } + } + &-print { + &-block, &-inline-block { + display: none; + } + @media print { + &-block { + display: block; + } + &-inline-block { + display: inline-block; + } + } + } +} diff --git a/addons/website/static/src/scss/website.ui.scss b/addons/website/static/src/scss/website.ui.scss index 1d1c42203b4..d9554a20a1c 100644 --- a/addons/website/static/src/scss/website.ui.scss +++ b/addons/website/static/src/scss/website.ui.scss @@ -298,6 +298,7 @@ body .modal { } #o_new_content_menu_choices { + @include o-w-preserve-base; @include o-position-absolute($o-navbar-height, 0, 0, 0); position: fixed; display: flex; diff --git a/addons/website/tests/test_views.py b/addons/website/tests/test_views.py index b16740ad619..49a76cf78ab 100644 --- a/addons/website/tests/test_views.py +++ b/addons/website/tests/test_views.py @@ -421,10 +421,13 @@ class TestCowViewSaving(common.TransactionCase): 'arch': '<div>content</div>', }) - total_views = View.search_count([]) + total_views = View.with_context(active_test=False).search_count([]) base_view.with_context(website_id=1).write({'name': 'New Name'}) # This will not write on `base_view` but will copy it to a specific view on which the `name` change will be applied + specific_view = View.search([['name', '=', 'New Name'], ['website_id', '=', 1]]) base_view.with_context(website_id=1).write({'name': 'Another New Name'}) - self.assertEqual(total_views + 1, View.search_count([]), "Second write should have wrote on the view copied during first write") + specific_view.active = False + base_view.with_context(website_id=1).write({'name': 'Yet Another New Name'}) + self.assertEqual(total_views + 1, View.with_context(active_test=False).search_count([]), "Subsequent writes should have written on the view copied during first write") # 2. Test with calling save() from ir.ui.view view_arch = '''<t name="Second View" t-name="website.second_view"> @@ -444,12 +447,12 @@ class TestCowViewSaving(common.TransactionCase): 'arch': view_arch, }) - total_views = View.search_count([]) + total_views = View.with_context(active_test=False).search_count([]) second_view.with_context(website_id=1).save('<div class="editable_part" data-oe-id="%s" data-oe-xpath="/t[1]/t[1]/div[1]/div[1]" data-oe-field="arch" data-oe-model="ir.ui.view">First editable_part</div>' % second_view.id, "/t[1]/t[1]/div[1]/div[1]") second_view.with_context(website_id=1).save('<div class="editable_part" data-oe-id="%s" data-oe-xpath="/t[1]/t[1]/div[1]/div[3]" data-oe-field="arch" data-oe-model="ir.ui.view">Second editable_part</div>' % second_view.id, "/t[1]/t[1]/div[1]/div[3]") - self.assertEqual(total_views + 1, View.search_count([]), "Second save should have wrote on the view copied during first save") + self.assertEqual(total_views + 1, View.with_context(active_test=False).search_count([]), "Second save should have written on the view copied during first save") - total_specific_view = View.search_count([('arch_db', 'like', 'First editable_part'), ('arch_db', 'like', 'Second editable_part')]) + total_specific_view = View.with_context(active_test=False).search_count([('arch_db', 'like', 'First editable_part'), ('arch_db', 'like', 'Second editable_part')]) self.assertEqual(total_specific_view, 1, "both editable_part should have been replaced on a created specific view") def test_cow_complete_flow(self): @@ -520,3 +523,43 @@ class TestCowViewSaving(common.TransactionCase): base_arch_w1 = self.base_view.with_context(website_id=1).read_combined(['arch'])['arch'] self.assertEqual('Hello World' in base_arch, True) self.assertEqual(base_arch, base_arch_w1, "Reading a top level view with or without a website_id in the context should render that exact view..") # ..even if there is a specific view for that one, as read_combined is supposed to render specific inherited view over generic but not specific top level instead of generic top level + + def test_cow_cross_inherit(self): + View = self.env['ir.ui.view'] + total_views = View.search_count([]) + + main_view = View.create({ + 'name': 'Main View', + 'type': 'qweb', + 'arch': '<body>GENERIC<div>A</div></body>', + 'key': 'website.main_view', + }).with_context(load_all_views=True) + + View.create({ + 'name': 'Child View', + 'mode': 'extension', + 'inherit_id': main_view.id, + 'arch': '<xpath expr="//div" position="replace"><div>VIEW<p>B</p></div></xpath>', + 'key': 'website.child_view', + }) + + child_view_2 = View.with_context(load_all_views=True).create({ + 'name': 'Child View 2', + 'mode': 'extension', + 'inherit_id': main_view.id, + 'arch': '<xpath expr="//p" position="replace"><span>C</span></xpath>', + 'key': 'website.child_view_2', + }) + + # These line doing `write()` are the real tests, it should not be changed and should not crash on xpath. + child_view_2.with_context(website_id=1).write({'arch': '<xpath expr="//p" position="replace"><span>D</span></xpath>'}) + self.assertEqual(total_views + 3 + 1, View.search_count([]), "It should have created the 3 initial generic views and created a child_view_2 specific view") + main_view.with_context(website_id=1).write({'arch': '<body>SPECIFIC<div>Z</div></body>'}) + self.assertEqual(total_views + 3 + 3, View.search_count([]), "It should have duplicated the Main View tree as a specific tree and then removed the specific view from the generic tree as no more needed") + + generic_view = View.with_context(website_id=None).get_view_id('website.main_view') + specific_view = View.with_context(website_id=1).get_view_id('website.main_view') + generic_view_arch = View.browse(generic_view).with_context(load_all_views=True).read_combined(['arch'])['arch'] + specific_view_arch = View.browse(specific_view).with_context(load_all_views=True, website_id=1).read_combined(['arch'])['arch'] + self.assertEqual(generic_view_arch == '<body>GENERIC<div>VIEW<span>C</span></div></body>', True) + self.assertEqual(specific_view_arch == '<body>SPECIFIC<div>VIEW<span>D</span></div></body>', True, "Writing on top level view hierarchy with a website in context should write on the view and clone it's inherited views") diff --git a/addons/website/views/snippets.xml b/addons/website/views/snippets.xml index d2b3cffbf60..c9a76741fad 100644 --- a/addons/website/views/snippets.xml +++ b/addons/website/views/snippets.xml @@ -567,7 +567,7 @@ <div class="col-lg-12 pt16 pb16"> <div id="myCollapse" class="accordion" role="tablist"> <div class="card bg-white"> - <a href="#" role="tab" data-toggle="collapse" aria-expanded="true" class="card-header text-primary">Terms of service</a> + <a href="#" role="tab" data-toggle="collapse" aria-expanded="true" class="card-header">Terms of service</a> <div class="collapse show" role="tabpanel"> <div class="card-body"> <p class="card-text">These terms of service ("Terms", "Agreement") are an agreement between the website ("Website operator", "us", "we" or "our") and you ("User", "you" or "your"). This Agreement sets forth the general terms and conditions of your use of this website and any of its products or services (collectively, "Website" or "Services").</p> @@ -575,7 +575,7 @@ </div> </div> <div class="card bg-white"> - <a href="#" role="tab" data-toggle="collapse" aria-expanded="false" class="collapsed card-header text-primary">Links to other Websites</a> + <a href="#" role="tab" data-toggle="collapse" aria-expanded="false" class="collapsed card-header">Links to other Websites</a> <div class="collapse" role="tabpanel"> <div class="card-body"> <p class="card-text">Although this Website may be linked to other websites, we are not, directly or indirectly, implying any approval, association, sponsorship, endorsement, or affiliation with any linked website, unless specifically stated herein.</p> @@ -584,7 +584,7 @@ </div> </div> <div class="card bg-white"> - <a href="#" role="tab" data-toggle="collapse" aria-expanded="false" class="collapsed card-header text-primary">Use of Cookies</a> + <a href="#" role="tab" data-toggle="collapse" aria-expanded="false" class="collapsed card-header">Use of Cookies</a> <div class="collapse" role="tabpanel"> <div class="card-body"> <p class="card-text">Website may use cookies to personalize and facilitate maximum navigation of the User by this site. The User may configure his / her browser to notify and reject the installation of the cookies sent by us.</p> diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 0b52b91bdb1..81cbff1d0cd 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -60,6 +60,12 @@ </xpath> </template> +<template id="assets_frontend_compatibility_for_12_0" inherit_id="website.assets_frontend" active="False"> + <xpath expr="//link[last()]" position="after"> + <link rel="stylesheet" type="text/scss" href="/website/static/src/scss/compatibility/bs3_for_12_0.scss"/> + </xpath> +</template> + <template id="website.assets_editor" name="Website Editor Assets (used in website editor)"> <t t-call="web._assets_helpers"/> |
