diff options
| author | Christophe Simonis <chs@odoo.com> | 2019-06-06 11:54:35 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2019-06-06 11:54:35 +0200 |
| commit | 71a50a2214748fec413c5a0f042d0e7172ae6ea9 (patch) | |
| tree | 2e9ee11551350e23923390d9c53baf630b6ed834 /addons/website | |
| parent | a7621137cb0d75ec545152d5e1d413c9d3548e45 (diff) | |
| parent | 409679866b2ee8352a8cb27460e7332563526924 (diff) | |
[MERGE] forward port branch saas-12.3 up to 409679866b2
Diffstat (limited to 'addons/website')
| -rw-r--r-- | addons/website/models/res_users.py | 9 | ||||
| -rw-r--r-- | addons/website/models/website.py | 11 | ||||
| -rw-r--r-- | addons/website/static/src/js/content/snippets.animation.js | 4 | ||||
| -rw-r--r-- | addons/website/static/src/js/content/website_root.js | 31 | ||||
| -rw-r--r-- | addons/website/static/src/js/menu/seo.js | 62 | ||||
| -rw-r--r-- | addons/website/static/tests/tours/rte.js | 1 | ||||
| -rw-r--r-- | addons/website/tests/test_get_current_website.py | 7 | ||||
| -rw-r--r-- | addons/website/views/website_templates.xml | 23 |
8 files changed, 102 insertions, 46 deletions
diff --git a/addons/website/models/res_users.py b/addons/website/models/res_users.py index a84eaaeb3c6..8902e4c34b6 100644 --- a/addons/website/models/res_users.py +++ b/addons/website/models/res_users.py @@ -2,8 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import logging -from odoo import api, fields, models, tools, _ -from odoo.exceptions import ValidationError +from odoo import api, fields, models, tools from odoo.http import request _logger = logging.getLogger(__name__) @@ -12,8 +11,8 @@ _logger = logging.getLogger(__name__) class ResUsers(models.Model): _inherit = 'res.users' - website_id = fields.Many2one('website', related='partner_id.website_id', store=True, related_sudo=False, readonly=True) - + website_id = fields.Many2one('website', related='partner_id.website_id', store=True, related_sudo=False, readonly=False) + _sql_constraints = [ # Partial constraint, complemented by unique index (see below). Still # useful to keep because it provides a proper error message when a @@ -51,7 +50,7 @@ class ResUsers(models.Model): @api.model_cr_context def _auto_init(self): result = super(ResUsers, self)._auto_init() - # Use unique index to implement unique constraint per website, even if website_id is null + # Use unique index to implement unique constraint per website, even if website_id is null # (not possible using a constraint) tools.create_unique_index(self._cr, 'res_users_login_key_unique_website_index', self._table, ['login', 'COALESCE(website_id,-1)']) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index bbc8489cf47..5202798fb97 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -149,14 +149,17 @@ class Website(models.Model): @api.multi def write(self, values): + public_user_to_change_websites = self.env['website'] self._handle_favicon(values) self._get_languages.clear_cache(self) if 'company_id' in values and 'user_id' not in values: - company = self.env['res.company'].browse(values['company_id']) - values['user_id'] = company._get_public_user().id + public_user_to_change_websites = self.filtered(lambda w: w.user_id.company_id.id != values['company_id']) + if public_user_to_change_websites: + company = self.env['res.company'].browse(values['company_id']) + super(Website, public_user_to_change_websites).write(dict(values, user_id=company._get_public_user().id)) - result = super(Website, self).write(values) + result = super(Website, self - public_user_to_change_websites).write(values) if 'cdn_activated' in values or 'cdn_url' in values or 'cdn_filters' in values: # invalidate the caches from static node at compile time self.env['ir.qweb'].clear_caches() @@ -185,6 +188,8 @@ class Website(models.Model): self.homepage_id = self.env['website.page'].search([('website_id', '=', self.id), ('key', '=', standard_homepage.key)]) + # prevent /-1 as homepage URL + self.homepage_id.url = '/' # Bootstrap default menu hierarchy, create a new minimalist one if no default default_menu = self.env.ref('website.main_menu') diff --git a/addons/website/static/src/js/content/snippets.animation.js b/addons/website/static/src/js/content/snippets.animation.js index 7f57eb81f2c..33e1e01ebcc 100644 --- a/addons/website/static/src/js/content/snippets.animation.js +++ b/addons/website/static/src/js/content/snippets.animation.js @@ -860,6 +860,10 @@ registry.gallerySlider = publicWidget.Widget.extend({ destroy: function () { this._super.apply(this, arguments); + if (!this.$indicator) { + return; + } + this.$prev.prependTo(this.$indicator); this.$next.appendTo(this.$indicator); this.$carousel.off('.gallery_slider'); diff --git a/addons/website/static/src/js/content/website_root.js b/addons/website/static/src/js/content/website_root.js index 22c71890643..612265124cd 100644 --- a/addons/website/static/src/js/content/website_root.js +++ b/addons/website/static/src/js/content/website_root.js @@ -19,6 +19,7 @@ var WebsiteRoot = publicRootData.PublicRoot.extend({ }), custom_events: _.extend({}, publicRootData.PublicRoot.prototype.custom_events || {}, { 'ready_to_clean_for_save': '_onWidgetsStopRequest', + seo_object_request: '_onSeoObjectRequest', }), /** @@ -111,6 +112,36 @@ var WebsiteRoot = publicRootData.PublicRoot.extend({ window.location.href = _.str.sprintf("/website/lang/%(lang)s?r=%(url)s%(hash)s", redirect); }, /** + /** + * Checks information about the page SEO object. + * + * @private + * @param {OdooEvent} ev + */ + _onSeoObjectRequest: function (ev) { + var res = this._unslugHtmlDataObject('seo-object'); + ev.data.callback(res); + }, + /** + * Returns a model/id object constructed from html data attribute. + * + * @private + * @param {string} dataAttr + * @returns {Object} an object with 2 keys: model and id, or null + * if not found + */ + _unslugHtmlDataObject: function (dataAttr) { + var repr = $('html').data(dataAttr); + var match = repr && repr.match(/(.+)\((\d+),(.*)\)/); + if (!match) { + return null; + } + return { + model: match[1], + id: match[2] | 0, + }; + }, + /** * @todo review * @private */ diff --git a/addons/website/static/src/js/menu/seo.js b/addons/website/static/src/js/menu/seo.js index 332fde71a4f..b65137ecc8f 100644 --- a/addons/website/static/src/js/menu/seo.js +++ b/addons/website/static/src/js/menu/seo.js @@ -129,17 +129,17 @@ var Keyword = Widget.extend({ _updateTitle: function () { var $title = this.$('.js_seo_keyword_title'); if (this.htmlPage.isInTitle(this.keyword)) { - $title.css('visibility','visible'); + $title.css('visibility', 'visible'); } else { - $title.css('visibility','hidden'); + $title.css('visibility', 'hidden'); } }, _updateDescription: function () { var $description = this.$('.js_seo_keyword_description'); if (this.htmlPage.isInDescription(this.keyword)) { - $description.css('visibility','visible'); + $description.css('visibility', 'visible'); } else { - $description.css('visibility','hidden'); + $description.css('visibility', 'hidden'); } }, }); @@ -210,7 +210,7 @@ var Preview = Widget.extend({ this.url = options.url; this.description = options.description || _t("The description will be generated by search engines based on page content unless you specify one."); if (this.description.length > 160) { - this.description = this.description.substring(0,159) + '…'; + this.description = this.description.substring(0, 159) + '…'; } this._super(parent); }, @@ -289,19 +289,19 @@ var HtmlPage = Class.extend(mixins.PropertiesMixin, { return $('body').children().not('.oe_seo_configuration').find('h2').text(); }, isInBody: function (text) { - return new RegExp(WORD_SEPARATORS_REGEX+text+WORD_SEPARATORS_REGEX, 'gi').test(this.bodyText()); + return new RegExp(WORD_SEPARATORS_REGEX + text + WORD_SEPARATORS_REGEX, 'gi').test(this.bodyText()); }, isInTitle: function (text) { - return new RegExp(WORD_SEPARATORS_REGEX+text+WORD_SEPARATORS_REGEX, 'gi').test(this.title()); + return new RegExp(WORD_SEPARATORS_REGEX + text + WORD_SEPARATORS_REGEX, 'gi').test(this.title()); }, isInDescription: function (text) { - return new RegExp(WORD_SEPARATORS_REGEX+text+WORD_SEPARATORS_REGEX, 'gi').test(this.description()); + return new RegExp(WORD_SEPARATORS_REGEX + text + WORD_SEPARATORS_REGEX, 'gi').test(this.description()); }, isInHeading1: function (text) { - return new RegExp(WORD_SEPARATORS_REGEX+text+WORD_SEPARATORS_REGEX, 'gi').test(this.heading1()); + return new RegExp(WORD_SEPARATORS_REGEX + text + WORD_SEPARATORS_REGEX, 'gi').test(this.heading1()); }, isInHeading2: function (text) { - return new RegExp(WORD_SEPARATORS_REGEX+text+WORD_SEPARATORS_REGEX, 'gi').test(this.heading2()); + return new RegExp(WORD_SEPARATORS_REGEX + text + WORD_SEPARATORS_REGEX, 'gi').test(this.heading2()); }, }); @@ -415,7 +415,7 @@ var MetaTitleDescription = Widget.extend({ _renderPreview: function () { var indexed = this.isIndexed; var preview = ""; - if (indexed){ + if (indexed) { preview = new Preview(this, { title: this.getTitle(), description: this.getDescription(), @@ -447,7 +447,7 @@ var MetaKeywords = Widget.extend({ start: function () { var self = this; this.$input = this.$('input[name=website_meta_keywords]'); - this.keywordList = new KeywordList(this, { htmlPage: this.htmlPage }); + this.keywordList = new KeywordList(this, {htmlPage: this.htmlPage}); this.keywordList.on('list-full', this, function () { self.$input.attr({ readonly: 'readonly', @@ -494,7 +494,7 @@ var MetaKeywords = Widget.extend({ model: 'website', method: 'get_languages', args: [[context.website_id]], - }).then( function (data) { + }).then(function (data) { self.$('#language-box').html(core.qweb.render('Configurator.language_promote', { 'language': data, 'def_lang': context.lang @@ -508,7 +508,7 @@ var MetaKeywords = Widget.extend({ * @param {boolean} removed: a keyword is about to be removed, * we need to exclude it from the count */ - _updateTable : function (removed) { + _updateTable: function (removed) { var min = removed ? 1 : 0; if (this.keywordList.keywords().length > min) { this.$('table').show(); @@ -562,7 +562,7 @@ var MetaImageSelector = Widget.extend({ _setDescription: function (description) { this.metaDescription = description || _t("The description will be generated by social media based on page content unless you specify one."); if (this.metaDescription.length > 160) { - this.metaDescription = this.metaDescription.substring(0,159) + '…'; + this.metaDescription = this.metaDescription.substring(0, 159) + '…'; } }, @@ -731,19 +731,25 @@ var SeoConfigurator = Dialog.extend({ }); }, getMainObject: function () { - var repr = $('html').data('main-object'); - var m = repr.match(/(.+)\((\d+),(.*)\)/); - if (!m) { - return null; - } else { - return { - model: m[1], - id: m[2]|0 - }; - } + var mainObject; + this.trigger_up('main_object_request', { + callback: function (value) { + mainObject = value; + }, + }); + return mainObject; + }, + getSeoObject: function () { + var seoObject; + this.trigger_up('seo_object_request', { + callback: function (value) { + seoObject = value; + }, + }); + return seoObject; }, loadMetaData: function () { - var obj = this.getMainObject(); + var obj = this.getSeoObject() || this.getMainObject(); return new Promise(function (resolve, reject) { if (!obj) { // return Promise.reject(new Error("No main_object was found.")); @@ -751,7 +757,7 @@ var SeoConfigurator = Dialog.extend({ } else { var fields = ['website_meta_title', 'website_meta_description', 'website_meta_keywords' ,'website_meta_og_img']; - if (obj.model === 'website.page'){ + if (obj.model === 'website.page') { fields.push('website_indexed'); fields.push('website_id'); } @@ -772,7 +778,7 @@ var SeoConfigurator = Dialog.extend({ }); }, saveMetaData: function (data) { - var obj = this.getMainObject(); + var obj = this.getSeoObject() || this.getMainObject(); if (!obj) { return Promise.reject(); } else { diff --git a/addons/website/static/tests/tours/rte.js b/addons/website/static/tests/tours/rte.js index e29f40e45ed..716562389e7 100644 --- a/addons/website/static/tests/tours/rte.js +++ b/addons/website/static/tests/tours/rte.js @@ -13,6 +13,7 @@ var ready = Promise.all([domReady, session.is_bound, ajax.loadXML()]); tour.register('rte_translator', { test: true, + url: '/', wait_for: ready, url: '/', }, [{ diff --git a/addons/website/tests/test_get_current_website.py b/addons/website/tests/test_get_current_website.py index a71126cb484..57d85887b4d 100644 --- a/addons/website/tests/test_get_current_website.py +++ b/addons/website/tests/test_get_current_website.py @@ -112,3 +112,10 @@ class TestGetCurrentWebsite(TransactionCase): self.assertEqual(Website._get_current_website_id('site-1.com:81', False), website2.id) self.assertEqual(Website._get_current_website_id('site-1.com:82', False), website1.id) self.assertEqual(Website._get_current_website_id('site-1.com', False), website1.id) + + def test_02_signup_user_website_id(self): + website = self.env['website'].browse(1) + website.specific_user_account = True + + user = self.env['res.users'].create({'website_id': website.id, 'login': 'sad@mail.com', 'name': 'Hope Fully'}) + self.assertTrue(user.website_id == user.partner_id.website_id == website) diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 583e019e20e..4dd19528c0d 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -169,7 +169,8 @@ 'data-view-xmlid': xmlid if editable or translatable else None, 'data-viewid': viewid if editable or translatable else None, 'data-main-object': repr(main_object) if editable or translatable else None, - 'data-oe-company-name': res_company.name + 'data-seo-object': repr(seo_object) if seo_object else None, + 'data-oe-company-name': res_company.name, }"/> </xpath> <xpath expr="//head" position="before"> @@ -178,8 +179,9 @@ <t t-set="additional_title" t-value="main_object.name"/> </t> <t t-set="default_title"> <t t-if="additional_title"><t t-raw="additional_title"/> | </t><t t-raw="(website or res_company).name"/> </t> - <t t-if="main_object and 'website_meta_title' in main_object and main_object.website_meta_title"> - <t t-set="title" t-value="main_object.website_meta_title"/> + <t t-set="seo_object" t-value="seo_object or main_object"/> + <t t-if="seo_object and 'website_meta_title' in seo_object and seo_object.website_meta_title"> + <t t-set="title" t-value="seo_object.website_meta_title"/> </t> <t t-else=""> <t t-set="title" t-value="default_title"></t> @@ -189,17 +191,18 @@ </xpath> <xpath expr="//head/meta[last()]" position="after"> <meta name="generator" content="Odoo"/> - <t t-set="website_meta" t-value="main_object and main_object.get_website_meta() or {}"/> + <t t-set="website_meta" t-value="seo_object and seo_object.get_website_meta() or {}"/> <meta name="default_title" t-att-content="default_title" groups="website.group_website_designer"/> <meta t-if="main_object and 'website_indexed' in main_object and not main_object.website_indexed" name="robots" content="noindex"/> - <t t-set="meta_description" t-value="main_object and 'website_meta_description' in main_object - and main_object.website_meta_description or website_meta_description or website_meta.get('meta_description', '')"/> - <t t-set="meta_keywords" t-value="main_object and 'website_meta_keywords' in main_object - and main_object.website_meta_keywords or website_meta_keywords"/> + <t t-set="seo_object" t-value="seo_object or main_object"/> + <t t-set="meta_description" t-value="seo_object and 'website_meta_description' in seo_object + and seo_object.website_meta_description or website_meta_description or website_meta.get('meta_description', '')"/> + <t t-set="meta_keywords" t-value="seo_object and 'website_meta_keywords' in seo_object + and seo_object.website_meta_keywords or website_meta_keywords"/> <meta t-if="meta_description or editable" name="description" t-att-content="meta_description"/> <meta t-if="meta_keywords or editable" name="keywords" t-att-content="meta_keywords"/> - <t t-if="main_object"> + <t t-if="seo_object"> <meta name="default_description" t-att-content="website_meta_description or website_meta.get('meta_description')" groups="website.group_website_designer"/> <!-- OpenGraph tags for Facebook sharing --> <t t-set="opengraph_meta" t-value="website_meta.get('opengraph_meta')"/> @@ -399,7 +402,7 @@ </div> </li> <li groups="website.group_website_publisher" class="list-inline-item"> - <t t-set="url_return" t-value="url_for('', '[lang]') + '?' + keep_query()"/> + <t t-set="url_return" t-value="quote_plus(url_for('', '[lang]') + '?' + keep_query())"/> <a class="d-none d-sm-block" t-attf-href="/web#action=base.action_view_base_language_install&website_id=#{website.id if website else ''}&url_return=#{url_return}"> <i class="fa fa-plus-circle"/> Add a language... |
