[IMP] website_[blog|event|forum|sale|slide]: make website specific

Set up env & tools for module website multiwebsite (blog, event, sale..)
website_id in modelConverter and ir_rule

add _compute_domain_keys to have a different cache per website
  or rules would not be correctly website_dependant
  eg: blog 1 on website 1, blog 2 on website 2
      access blog 1 from website 1 => can access -> normal
      access blog 1 from website 2 => can access -> should crash because (ir rule)

split mixing website.published.mixin and website.published.multi.mixin to have
website_id only on last one.
 multi mixing will:
     - override website_published compute to take current_website into account (not in backend)
     - force website when clicking on published in backend

- website blog, website_sale, website_event, website_forum, website_slides are now multi website

Co-authored-by: Derie Romain <rde@odoo.com>
Co-authored-by: Kersten Jérémy <jke@odoo.com>
This commit is contained in:
Jeremy Kersten
2018-08-13 20:16:34 +02:00
co-authored by Derie Romain
parent 8300155cd6
commit 066cfc9598
39 changed files with 319 additions and 122 deletions
+6 -1
View File
@@ -31,6 +31,7 @@ class WebsiteBackend(http.Controller):
if has_group_designer:
config = request.env['res.config.settings'].sudo().create({})
config.website_id = current_website
if config.has_google_analytics_dashboard:
dashboard_data['dashboards']['visits'] = dict(
ga_client_id=config.google_management_client_id or '', # void string instead of stringified False
@@ -39,7 +40,7 @@ class WebsiteBackend(http.Controller):
return dashboard_data
@http.route('/website/dashboard/set_ga_data', type='json', auth='user')
def website_set_ga_data(self, ga_client_id, ga_analytics_key):
def website_set_ga_data(self, website_id, ga_client_id, ga_analytics_key):
if not request.env.user.has_group('base.group_system'):
return {
'error': {
@@ -54,10 +55,14 @@ class WebsiteBackend(http.Controller):
'message': 'The Google Analytics Client ID or Key you entered seems incorrect.',
}
}
Website = request.env['website']
current_website = website_id and Website.browse(website_id) or Website.get_current_website()
request.env['res.config.settings'].create({
'has_google_analytics': True,
'has_google_analytics_dashboard': True,
'google_management_client_id': ga_client_id,
'google_analytics_key': ga_analytics_key,
'website_id': current_website,
}).execute()
return True
+1
View File
@@ -7,6 +7,7 @@ from . import ir_http
from . import ir_qweb
from . import ir_qweb_fields
from . import website
from . import ir_rule
from . import ir_ui_view
from . import res_company
from . import res_partner
+2
View File
@@ -267,6 +267,8 @@ class ModelConverter(ModelConverter):
def generate(self, uid, dom=None, args=None):
Model = request.env[self.model].sudo(uid)
# Allow to current_website_id directly in route domain
args.update(current_website_id=request.env['website'].get_current_website().id)
domain = safe_eval(self.domain, (args or {}).copy())
if dom:
domain += dom
+5 -1
View File
@@ -8,5 +8,9 @@ class IrRule(models.Model):
@api.model
def _eval_context(self):
res = super(IrRule, self)._eval_context()
res['website_id'] = self._context.get('website_id')
res['website_id'] = self.env['website'].get_current_website().id
return res
def _compute_domain_keys(self):
""" Return the list of context keys to use for caching ``_compute_domain``. """
return super(IrRule, self)._compute_domain_keys() + ['website_id']
+3
View File
@@ -297,3 +297,6 @@ class View(models.Model):
'url': '/website/pages',
'target': 'self',
}
def _read_template_keys(self):
return super(View, self)._read_template_keys() + ['website_id']
+51 -28
View File
@@ -694,18 +694,63 @@ class SeoMetadata(models.AbstractModel):
website_meta_keywords = fields.Char("Website meta keywords", translate=True)
class WebsiteMultiMixin(models.AbstractModel):
_name = 'website.multi.mixin'
website_id = fields.Many2one('website', string='Website', help='Restrict publishing to this website.')
@api.multi
def can_access_from_current_website(self, website_id=False):
can_access = True
for record in self:
if (website_id or record.website_id.id) not in (False, request.website.id):
can_access = False
continue
return can_access
class WebsitePublishedMixin(models.AbstractModel):
_name = "website.published.mixin"
website_published = fields.Boolean('Visible on current website',
compute='_compute_website_published',
inverse='_inverse_website_published',
search='_search_website_published') # todo jov evaluate places where this is used, we can probably replace some with is_published
website_published = fields.Boolean('Visible on current website', related='is_published')
is_published = fields.Boolean('Is published')
website_id = fields.Many2one('website', string='Website', help='Restrict publishing to this website.')
website_url = fields.Char('Website URL', compute='_compute_website_url', help='The full URL to access the document through the website.')
@api.multi
def _compute_website_url(self):
for record in self:
record.website_url = '#'
@api.multi
def website_publish_button(self):
self.ensure_one()
if self.env.user.has_group('website.group_website_publisher') and self.website_url != '#':
# Force website to land on record's website to publish/unpublish it
if 'website_id' in self and self.env.user.has_group('website.group_multi_website'):
self.website_id._force()
return self.open_website_url()
return self.write({'website_published': not self.website_published})
def open_website_url(self):
return {
'type': 'ir.actions.act_url',
'url': self.website_url,
'target': 'self',
}
class WebsitePublishedMultiMixin(WebsitePublishedMixin):
_name = 'website.published.multi.mixin'
_inherit = ['website.published.mixin', 'website.multi.mixin']
website_published = fields.Boolean(compute='_compute_website_published',
inverse='_inverse_website_published',
search='_search_website_published',
related=False)
@api.multi
@api.depends('is_published', 'website_id')
def _compute_website_published(self):
@@ -718,11 +763,8 @@ class WebsitePublishedMixin(models.AbstractModel):
@api.multi
def _inverse_website_published(self):
current_website_id = self._context.get('website_id')
for record in self:
record.is_published = record.website_published
if record.website_published and current_website_id:
record.write({'website_id': current_website_id})
def _search_website_published(self, operator, value):
if not isinstance(value, bool) or operator not in ('=', '!='):
@@ -740,30 +782,11 @@ class WebsitePublishedMixin(models.AbstractModel):
else: # should be in the backend, return things that are published anywhere
return is_published
@api.multi
def _compute_website_url(self):
for record in self:
record.website_url = '#'
@api.multi
def website_publish_button(self):
self.ensure_one()
if self.env.user.has_group('website.group_website_publisher') and self.website_url != '#':
return self.open_website_url()
return self.write({'website_published': not self.website_published})
def open_website_url(self):
return {
'type': 'ir.actions.act_url',
'url': self.website_url,
'target': 'self',
}
class Page(models.Model):
_name = 'website.page'
_inherits = {'ir.ui.view': 'view_id'}
_inherit = 'website.published.mixin'
_inherit = 'website.published.multi.mixin'
_description = 'Page'
_order = 'website_id'
@@ -112,6 +112,7 @@ var Dashboard = AbstractAction.extend(ControlPanelMixin, {
return this._rpc({
route: '/website/dashboard/set_ga_data',
params: {
'website_id': self.website_id,
'ga_client_id': ga_client_id,
'ga_analytics_key': ga_analytics_key,
},
+1 -1
View File
@@ -75,7 +75,7 @@
<field name="view_id" ref="view_website_tree"/>
<field name="target">current</field>
</record>
<!-- website.redirect views -->
<record id="website_redirect_form_view" model="ir.ui.view">
<field name="name">website.redirect.form</field>
+25 -12
View File
@@ -48,13 +48,14 @@ class WebsiteBlog(http.Controller):
'/blog/page/<int:page>',
], type='http', auth="public", website=True)
def blogs(self, page=1, **post):
domain = request.website.website_domain()
Blog = request.env['blog.blog']
blogs = Blog.search([], limit=2)
blogs = Blog.search(domain, limit=2)
if len(blogs) == 1:
return werkzeug.utils.redirect('/blog/%s' % slug(blogs[0]), code=302)
BlogPost = request.env['blog.post']
total = BlogPost.search([], count=True)
total = BlogPost.search_count(domain)
pager = request.website.pager(
url='/blog',
@@ -62,7 +63,7 @@ class WebsiteBlog(http.Controller):
page=page,
step=self._blog_post_per_page,
)
posts = BlogPost.search([], offset=(page - 1) * self._blog_post_per_page, limit=self._blog_post_per_page)
posts = BlogPost.search(domain, offset=(page - 1) * self._blog_post_per_page, limit=self._blog_post_per_page)
blog_url = QueryURL('', ['blog', 'tag'])
return request.render("website_blog.latest_blogs", {
'posts': posts,
@@ -71,10 +72,10 @@ class WebsiteBlog(http.Controller):
})
@http.route([
'/blog/<model("blog.blog"):blog>',
'/blog/<model("blog.blog"):blog>/page/<int:page>',
'/blog/<model("blog.blog"):blog>/tag/<string:tag>',
'/blog/<model("blog.blog"):blog>/tag/<string:tag>/page/<int:page>',
'''/blog/<model("blog.blog", "[('website_id', 'in', (False, current_website_id))]"):blog>''',
'''/blog/<model("blog.blog"):blog>/page/<int:page>''',
'''/blog/<model("blog.blog"):blog>/tag/<string:tag>''',
'''/blog/<model("blog.blog"):blog>/tag/<string:tag>/page/<int:page>''',
], type='http', auth="public", website=True)
def blog(self, blog=None, tag=None, page=1, **opt):
""" Prepare all values to display the blog.
@@ -92,16 +93,19 @@ class WebsiteBlog(http.Controller):
- 'date': date_begin optional parameter, used in archives navigation
- 'blog_url': help object to create URLs
"""
if not blog.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
date_begin, date_end, state = opt.get('date_begin'), opt.get('date_end'), opt.get('state')
published_count, unpublished_count = 0, 0
domain = request.website.website_domain()
BlogPost = request.env['blog.post']
Blog = request.env['blog.blog']
blogs = Blog.search([], order="create_date asc")
blogs = Blog.search(domain, order="create_date asc")
# build the domain for blog post to display
domain = []
# retrocompatibility to accept tag as slug
active_tag_ids = tag and [int(unslug(t)[1]) for t in tag.split(',')] or []
if active_tag_ids:
@@ -172,7 +176,7 @@ class WebsiteBlog(http.Controller):
response = request.render("website_blog.blog_post_short", values)
return response
@http.route(['/blog/<model("blog.blog"):blog>/feed'], type='http', auth="public")
@http.route(['''/blog/<model("blog.blog", "[('website_id', 'in', (False, current_website_id))]"):blog>/feed'''], type='http', auth="public")
def blog_feed(self, blog, limit='15'):
v = {}
v['blog'] = blog
@@ -185,7 +189,7 @@ class WebsiteBlog(http.Controller):
return r
@http.route([
'''/blog/<model("blog.blog"):blog>/post/<model("blog.post", "[('blog_id','=',blog[0])]"):blog_post>''',
'''/blog/<model("blog.blog", "[('website_id', 'in', (False, current_website_id))]"):blog>/post/<model("blog.post", "[('blog_id','=',blog[0])]"):blog_post>''',
], type='http', auth="public", website=True)
def blog_post(self, blog, blog_post, tag_id=None, page=1, enable_editor=None, **post):
""" Prepare all values to display the blog.
@@ -201,6 +205,9 @@ class WebsiteBlog(http.Controller):
- 'nav_list': a dict [year][month] for archives navigation
- 'next_post': next blog post, to direct the user towards the next interesting post
"""
if not blog.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
BlogPost = request.env['blog.post']
date_begin, date_end = post.get('date_begin'), post.get('date_end')
@@ -271,6 +278,12 @@ class WebsiteBlog(http.Controller):
@http.route('/blog/<int:blog_id>/post/new', type='http', auth="public", website=True)
def blog_post_create(self, blog_id, **post):
# Use sudo so this line prevents both editor and admin to access blog from another website
# as browse() will return the record even if forbidden by security rules but editor won't
# be able to access it
if not request.env['blog.blog'].browse(blog_id).sudo().can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
new_blog_post = request.env['blog.post'].create({
'blog_id': blog_id,
'website_published': False,
+4 -2
View File
@@ -15,7 +15,7 @@ from odoo.tools import html2plaintext
class Blog(models.Model):
_name = 'blog.blog'
_description = 'Blogs'
_inherit = ['mail.thread', 'website.seo.metadata']
_inherit = ['mail.thread', 'website.seo.metadata', 'website.multi.mixin']
_order = 'name'
name = fields.Char('Blog Name', required=True, translate=True)
@@ -108,7 +108,7 @@ class BlogTag(models.Model):
class BlogPost(models.Model):
_name = "blog.post"
_description = "Blog Post"
_inherit = ['mail.thread', 'website.seo.metadata', 'website.published.mixin']
_inherit = ['mail.thread', 'website.seo.metadata', 'website.published.multi.mixin']
_order = 'id DESC'
_mail_post_access = 'read'
@@ -168,6 +168,8 @@ class BlogPost(models.Model):
visits = fields.Integer('No of Views', copy=False)
ranking = fields.Float(compute='_compute_ranking', string='Ranking')
website_id = fields.Many2one(related='blog_id.website_id', readonly=True)
@api.multi
@api.depends('content', 'teaser_manual')
def _compute_teaser(self):
@@ -64,26 +64,26 @@
</span>
</h4>
<div class="text-muted">
<span class="fa fa-calendar mr8" role="img" aria-label="Post date" title="Post date"> <span t-field="post.post_date"/></span>
<span class="fa fa-folder-open"> In
<a class="mr8" t-attf-href="#{blog_url(blog=post.blog_id)}">
<span t-field="post.blog_id"/>
</a>
</span>
<span class="fa fa-calendar mr8" role="img" aria-label="Post date" title="Post date" />
<span t-field="post.post_date"/>
<span class="fa fa-folder-open" /> In
<a class="mr8" t-attf-href="#{blog_url(blog=post.blog_id)}">
<span t-field="post.blog_id"/>
</a>
</div>
<div class="text-muted fa fa-tags">
<div class="text-muted">
<span class="fa fa-tags" role="img" aria-label="Tags" title="Tags"/>
<span t-field="post.website_meta_keywords"/>
<span t-if="editable and not post.website_meta_keywords" class="badge badge-danger">
No keywords defined!
</span>
</div>
<div class="text-muted" t-if="len(post.message_ids) &gt; 0">
<span class="fa fa-comment-o">
<a t-attf-href="#{blog_url('', ['blogpost'], blogpost=post)}#comments">
<t t-if="len(post.message_ids) &lt;= 1" ><t t-esc="len(post.message_ids)"/> comment</t>
<t t-if="len(post.message_ids) > 1"><t t-esc="len(post.message_ids)"/> comments</t>
</a>
</span>
<span class="fa fa-comment-o"/>
<a t-attf-href="#{blog_url('', ['blogpost'], blogpost=post)}#comments">
<t t-if="len(post.message_ids) &lt;= 1" ><t t-esc="len(post.message_ids)"/> comment</t>
<t t-if="len(post.message_ids) > 1"><t t-esc="len(post.message_ids)"/> comments</t>
</a>
</div>
<div class="text-muted mb16" t-field="post.website_meta_description"/>
</div>
@@ -8,6 +8,7 @@
<field name="arch" type="xml">
<tree string="Blogs">
<field name="name"/>
<field name="website_id" groups="website.group_multi_website"/>
<field name="active" invisible="1"/>
</tree>
</field>
@@ -26,6 +27,7 @@
<group>
<field name="name"/>
<field name="subtitle"/>
<field name="website_id" groups="website.group_multi_website"/>
</group>
</sheet>
<div class="oe_chatter">
@@ -45,6 +47,7 @@
<field name="name"/>
<field name="active" invisible="1"/>
<field name="blog_id"/>
<field name="website_id"/>
<field name="visits"/>
<field name="create_uid" invisible="1"/>
<field name="write_uid"/>
+22 -8
View File
@@ -28,7 +28,6 @@ class WebsiteEventController(http.Controller):
searches.setdefault('type', 'all')
searches.setdefault('country', 'all')
domain_search = {}
def sdn(date):
return fields.Datetime.to_string(date.replace(hour=23, minute=59, second=59))
@@ -64,6 +63,7 @@ class WebsiteEventController(http.Controller):
]
# search domains
domain_search = {'website_specific': request.website.website_domain()}
current_date = None
current_type = None
current_country = None
@@ -141,8 +141,11 @@ class WebsiteEventController(http.Controller):
return request.render("website_event.index", values)
@http.route(['/event/<model("event.event"):event>/page/<path:page>'], type='http', auth="public", website=True, sitemap=False)
@http.route(['''/event/<model("event.event", "[('website_id', 'in', (False, current_website_id))]"):event>/page/<path:page>'''], type='http', auth="public", website=True, sitemap=False)
def event_page(self, event, page, **post):
if not event.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
values = {
'event': event,
'main_object': event
@@ -161,8 +164,11 @@ class WebsiteEventController(http.Controller):
return request.render(page, values)
@http.route(['/event/<model("event.event"):event>'], type='http', auth="public", website=True)
@http.route(['''/event/<model("event.event", "[('website_id', 'in', (False, current_website_id))]"):event>'''], type='http', auth="public", website=True)
def event(self, event, **post):
if not event.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
if event.menu_id and event.menu_id.child_id:
target_url = event.menu_id.child_id[0].url
else:
@@ -171,8 +177,11 @@ class WebsiteEventController(http.Controller):
target_url += '?enable_editor=1'
return request.redirect(target_url)
@http.route(['/event/<model("event.event"):event>/register'], type='http', auth="public", website=True, sitemap=False)
@http.route(['''/event/<model("event.event", "[('website_id', 'in', (False, current_website_id))]"):event>/register'''], type='http', auth="public", website=True, sitemap=False)
def event_register(self, event, **post):
if not event.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
values = {
'event': event,
'main_object': event,
@@ -195,6 +204,7 @@ class WebsiteEventController(http.Controller):
'date_begin': fields.Date.to_string(date_begin),
'date_end': fields.Date.to_string((date_begin + timedelta(days=(1)))),
'seats_available': 1000,
'website_id': request.website.id,
}
return request.env['event.event'].with_context(context or {}).create(vals)
@@ -210,11 +220,12 @@ class WebsiteEventController(http.Controller):
country_code = request.session['geoip'].get('country_code')
result = {'events': [], 'country': False}
events = None
domain = request.website.website_domain()
if country_code:
country = request.env['res.country'].search([('code', '=', country_code)], limit=1)
events = Event.search(['|', ('address_id', '=', None), ('country_id.code', '=', country_code), ('date_begin', '>=', '%s 00:00:00' % fields.Date.today()), ('state', '=', 'confirm')], order="date_begin")
events = Event.search(domain + ['|', ('address_id', '=', None), ('country_id.code', '=', country_code), ('date_begin', '>=', '%s 00:00:00' % fields.Date.today()), ('state', '=', 'confirm')], order="date_begin")
if not events:
events = Event.search([('date_begin', '>=', '%s 00:00:00' % fields.Date.today()), ('state', '=', 'confirm')], order="date_begin")
events = Event.search(domain + [('date_begin', '>=', '%s 00:00:00' % fields.Date.today()), ('state', '=', 'confirm')], order="date_begin")
for event in events:
if country_code and event.country_id.code == country_code:
result['country'] = country
@@ -252,8 +263,11 @@ class WebsiteEventController(http.Controller):
registration[key] = value
return list(registrations.values())
@http.route(['/event/<model("event.event"):event>/registration/confirm'], type='http', auth="public", methods=['POST'], website=True)
@http.route(['''/event/<model("event.event", "[('website_id', 'in', (False, current_website_id))]"):event>/registration/confirm'''], type='http', auth="public", methods=['POST'], website=True)
def registration_confirm(self, event, **post):
if not event.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
Attendees = request.env['event.registration']
registrations = self._process_registration_details(post)
@@ -270,7 +284,7 @@ class WebsiteEventController(http.Controller):
'iCal_url': urls.get('iCal_url')
})
@http.route(['/event/<model("event.event"):event>/ics'], type='http', auth="public", website=True)
@http.route(['''/event/<model("event.event", "[('website_id', 'in', (False, current_website_id))]"):event>/ics'''], type='http', auth="public", website=True)
def make_event_ics_file(self, event, **kwargs):
if not event or not event.registration_ids:
return request.not_found()
+5 -5
View File
@@ -32,9 +32,9 @@ class EventType(models.Model):
class Event(models.Model):
_name = 'event.event'
_inherit = ['event.event', 'website.seo.metadata', 'website.published.mixin']
_inherit = ['event.event', 'website.seo.metadata', 'website.published.multi.mixin']
website_published = fields.Boolean(track_visibility='onchange')
is_published = fields.Boolean(track_visibility='onchange')
is_participating = fields.Boolean("Is Participating", compute="_compute_is_participating")
@@ -45,7 +45,7 @@ class Event(models.Model):
def _compute_is_participating(self):
# we don't allow public user to see participating label
if self.env.user != self.env.ref('base.public_user'):
if self.env.user != self.env['website'].get_current_website().user_id:
email = self.env.user.partner_id.email
for event in self:
domain = ['&', '|', ('email', '=', email), ('partner_id', '=', self.env.user.partner_id.id), ('event_id', '=', event.id)]
@@ -120,9 +120,9 @@ class Event(models.Model):
@api.multi
def _track_subtype(self, init_values):
self.ensure_one()
if 'website_published' in init_values and self.website_published:
if 'is_published' in init_values and self.is_published:
return 'website_event.mt_event_published'
elif 'website_published' in init_values and not self.website_published:
elif 'is_published' in init_values and not self.is_published:
return 'website_event.mt_event_unpublished'
return super(Event, self)._track_subtype(init_values)
@@ -30,6 +30,9 @@
<field name="model">event.event</field>
<field name="inherit_id" ref="event.view_event_form"/>
<field name="arch" type="xml">
<field name="organizer_id" position="before">
<field name="website_id" groups="website.group_multi_website"/>
</field>
<button name="toggle_active" position="before">
<field name="website_url" invisible="1"/>
<button name="website_publish_button"
@@ -57,4 +60,15 @@
</field>
</record>
<record id="event_event_view_list_inherit_website" model="ir.ui.view">
<field name="name">event.event.view.list.inherit.website</field>
<field name="model">event.event</field>
<field name="inherit_id" ref="event.view_event_tree"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="website_id" groups="website.group_multi_website"/>
</field>
</field>
</record>
</odoo>
@@ -8,9 +8,9 @@ from odoo.http import request
class WebsiteEventSaleController(WebsiteEventController):
@http.route(['/event/<model("event.event"):event>/register'], type='http', auth="public", website=True)
@http.route()
def event_register(self, event, **post):
event = event.with_context(pricelist=request.website.get_current_pricelist().id)
event = event.with_context(pricelist=request.website.id)
return super(WebsiteEventSaleController, self).event_register(event, **post)
def _process_tickets_details(self, data):
@@ -25,7 +25,7 @@ class WebsiteEventSaleController(WebsiteEventController):
tickets = request.env['event.event.ticket'].browse(tuple(ticket_post))
return [{'id': ticket.id, 'name': ticket.name, 'quantity': ticket_post[ticket.id], 'price': ticket.price} for ticket in tickets if ticket_post[ticket.id]]
@http.route(['/event/<model("event.event"):event>/registration/confirm'], type='http', auth="public", methods=['POST'], website=True)
@http.route()
def registration_confirm(self, event, **post):
order = request.website.sale_get_order(force_create=1)
attendee_ids = set()
+22 -6
View File
@@ -5,6 +5,7 @@ import babel
import collections
import datetime
import pytz
from werkzeug.exceptions import NotFound
from odoo import fields, http
from odoo.http import request
@@ -13,8 +14,11 @@ from odoo.tools import html_escape as escape, html2plaintext
class WebsiteEventTrackController(http.Controller):
@http.route(['''/event/<model("event.event"):event>/track/<model("event.track", "[('event_id','=',event[0])]"):track>'''], type='http', auth="public", website=True)
@http.route(['''/event/<model("event.event", "[('website_id', 'in', (False, current_website_id))]"):event>/track/<model("event.track", "[('event_id','=',event[0])]"):track>'''], type='http', auth="public", website=True)
def event_track_view(self, event, track, **post):
if not event.can_access_from_current_website():
raise NotFound()
track = track.sudo().with_context(tz=event.date_tz or 'UTC')
values = {'track': track, 'event': track.event_id, 'main_object': track}
return request.render("website_event_track.track_view", values)
@@ -65,8 +69,11 @@ class WebsiteEventTrackController(http.Controller):
'dates': dates
}
@http.route(['''/event/<model("event.event"):event>/agenda'''], type='http', auth="public", website=True, sitemap=False)
@http.route(['''/event/<model("event.event", "[('website_id', 'in', (False, current_website_id))]"):event>/agenda'''], type='http', auth="public", website=True, sitemap=False)
def event_agenda(self, event, tag=None, **post):
if not event.can_access_from_current_website():
raise NotFound()
event = event.with_context(tz=event.date_tz or 'UTC')
local_tz = pytz.timezone(event.date_tz or 'UTC')
days_tracks = collections.defaultdict(lambda: [])
@@ -90,10 +97,13 @@ class WebsiteEventTrackController(http.Controller):
})
@http.route([
'''/event/<model("event.event"):event>/track''',
'''/event/<model("event.event"):event>/track/tag/<model("event.track.tag"):tag>'''
'''/event/<model("event.event", "[('website_id', 'in', (False, current_website_id))]"):event>/track''',
'''/event/<model("event.event", "[('website_id', 'in', (False, current_website_id))]"):event>/track/tag/<model("event.track.tag"):tag>'''
], type='http', auth="public", website=True, sitemap=False)
def event_tracks(self, event, tag=None, **post):
if not event.can_access_from_current_website():
raise NotFound()
event = event.with_context(tz=event.date_tz or 'UTC')
searches = {}
if tag:
@@ -112,12 +122,18 @@ class WebsiteEventTrackController(http.Controller):
}
return request.render("website_event_track.tracks", values)
@http.route(['''/event/<model("event.event"):event>/track_proposal'''], type='http', auth="public", website=True, sitemap=False)
@http.route(['''/event/<model("event.event", "[('website_id', 'in', (False, current_website_id))]"):event>/track_proposal'''], type='http', auth="public", website=True, sitemap=False)
def event_track_proposal(self, event, **post):
if not event.can_access_from_current_website():
raise NotFound()
return request.render("website_event_track.event_track_proposal", {'event': event})
@http.route(['/event/<model("event.event"):event>/track_proposal/post'], type='http', auth="public", methods=['POST'], website=True)
@http.route(['''/event/<model("event.event", "[('website_id', 'in', (False, current_website_id))]"):event>/track_proposal/post'''], type='http', auth="public", methods=['POST'], website=True)
def event_track_proposal_post(self, event, **post):
if not event.can_access_from_current_website():
raise NotFound()
tags = []
for tag in event.allowed_track_tag_ids:
if post.get('tag_' + str(tag.id)):
+11 -6
View File
@@ -81,7 +81,8 @@ class WebsiteForum(http.Controller):
@http.route(['/forum'], type='http', auth="public", website=True)
def forum(self, **kwargs):
forums = request.env['forum.forum'].search([])
domain = request.website.get_current_website().website_domain()
forums = request.env['forum.forum'].search(domain)
return request.render("website_forum.forum_all", {'forums': forums})
@http.route('/forum/new', type='json', auth="user", methods=['POST'], website=True)
@@ -104,6 +105,7 @@ class WebsiteForum(http.Controller):
def sitemap_forum(env, rule, qs):
Forum = env['forum.forum']
dom = sitemap_qs2dom(qs, '/forum', Forum._rec_name)
dom += env['website'].get_current_website().website_domain()
for f in Forum.search(dom):
loc = '/forum/%s' % slug(f)
if not qs or qs.lower() in loc:
@@ -115,6 +117,9 @@ class WebsiteForum(http.Controller):
'''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag"):tag>/questions/page/<int:page>''',
], type='http', auth="public", website=True, sitemap=sitemap_forum)
def questions(self, forum, tag=None, page=1, filters='all', sorting=None, search='', post_type=None, **post):
if not forum.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
Post = request.env['forum.post']
domain = [('forum_id', '=', forum.id), ('parent_id', '=', False), ('state', '=', 'active')]
@@ -174,7 +179,7 @@ class WebsiteForum(http.Controller):
})
return request.render("website_forum.forum_index", values)
@http.route(['/forum/<model("forum.forum"):forum>/faq'], type='http', auth="public", website=True)
@http.route(['''/forum/<model("forum.forum", "[('website_id', 'in', (False, current_website_id))]"):forum>/faq'''], type='http', auth="public", website=True)
def forum_faq(self, forum, **post):
values = self._prepare_forum_values(forum=forum, searches=dict(), header={'is_guidelines': True}, **post)
return request.render("website_forum.faq", values)
@@ -232,7 +237,7 @@ class WebsiteForum(http.Controller):
except IOError:
return False
@http.route(['''/forum/<model("forum.forum"):forum>/question/<model("forum.post", "[('forum_id','=',forum[0]),('parent_id','=',False),('can_view', '=', True)]"):question>'''], type='http', auth="public", website=True)
@http.route(['''/forum/<model("forum.forum", "[('website_id', 'in', (False, current_website_id))]"):forum>/question/<model("forum.post", "[('forum_id','=',forum[0]),('parent_id','=',False),('can_view', '=', True)]"):question>'''], type='http', auth="public", website=True)
def question(self, forum, question, **post):
if not forum.active:
return request.render("website_forum.header", {'forum': forum})
@@ -548,8 +553,8 @@ class WebsiteForum(http.Controller):
# User
# --------------------------------------------------
@http.route(['/forum/<model("forum.forum"):forum>/users',
'/forum/<model("forum.forum"):forum>/users/page/<int:page>'],
@http.route(['''/forum/<model("forum.forum", "[('website_id', 'in', (False, current_website_id))]"):forum>/users''',
'''/forum/<model("forum.forum"):forum>/users/page/<int:page>'''],
type='http', auth="public", website=True)
def users(self, forum, page=1, **searches):
User = request.env['res.users']
@@ -733,7 +738,7 @@ class WebsiteForum(http.Controller):
# Badges
# --------------------------------------------------
@http.route('/forum/<model("forum.forum"):forum>/badge', type='http', auth="public", website=True)
@http.route('''/forum/<model("forum.forum", "[('website_id', 'in', (False, current_website_id))]"):forum>/badge''', type='http', auth="public", website=True)
def badges(self, forum, **searches):
Badge = request.env['gamification.badge']
badges = Badge.sudo().search([('challenge_ids.category', '=', 'forum')])
+2 -1
View File
@@ -25,7 +25,7 @@ class KarmaError(Forbidden):
class Forum(models.Model):
_name = 'forum.forum'
_description = 'Forum'
_inherit = ['mail.thread', 'website.seo.metadata']
_inherit = ['mail.thread', 'website.seo.metadata', 'website.multi.mixin']
@api.model_cr
def init(self):
@@ -208,6 +208,7 @@ class Post(models.Model):
('discussion', 'Discussion')],
string='Type', default='question', required=True)
website_message_ids = fields.One2many(domain=lambda self: [('model', '=', self._name), ('message_type', 'in', ['email', 'comment'])])
website_id = fields.Many2one(related='forum_id.website_id', readonly=True)
# history
create_date = fields.Datetime('Asked on', index=True, readonly=True)
+6
View File
@@ -15,6 +15,7 @@
<field name="arch" type="xml">
<tree string="Forums">
<field name="name"/>
<field name="website_id" groups="website.group_multi_website"/>
<field name="active" invisible="1"/>
<field name="allow_question"/>
<field name="allow_link"/>
@@ -39,6 +40,9 @@
<field name="name"/>
</h1>
<field name="description"/>
<group>
<field name="website_id" groups="website.group_multi_website"/>
</group>
<notebook>
<page string="Options">
<group string="Post Types">
@@ -138,6 +142,7 @@
<field name="name"/>
<field name="active" invisible="1"/>
<field name="forum_id"/>
<field name="website_id" groups="website.group_multi_website"/>
</tree>
</field>
</record>
@@ -160,6 +165,7 @@
<group>
<group>
<field name="forum_id"/>
<field name="website_id" groups="website.group_multi_website"/>
<field name="parent_id"/>
</group><group>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
+1 -1
View File
@@ -5,7 +5,7 @@ from odoo import api, fields, models
class HrEmployee(models.Model):
_name = 'hr.employee'
_inherit = ['hr.employee', 'website.published.mixin']
_inherit = ['hr.employee', 'website.published.multi.mixin']
public_info = fields.Char(string='Public Info')
@@ -39,7 +39,7 @@ class Applicant(models.Model):
class Job(models.Model):
_name = 'hr.job'
_inherit = ['hr.job', 'website.seo.metadata', 'website.published.mixin']
_inherit = ['hr.job', 'website.seo.metadata', 'website.published.multi.mixin']
def _get_default_website_description(self):
default_description = self.env["ir.model.data"].xmlid_to_object("website_hr_recruitment.default_website_description")
+12 -6
View File
@@ -206,12 +206,14 @@ class WebsiteSale(http.Controller):
return domain
@http.route([
'/shop',
'/shop/page/<int:page>',
'/shop/category/<model("product.public.category"):category>',
'/shop/category/<model("product.public.category"):category>/page/<int:page>'
'''/shop''',
'''/shop/page/<int:page>''',
'''/shop/category/<model("product.public.category", "[('website_id', 'in', (False, current_website_id))]"):category>''',
'''/shop/category/<model("product.public.category", "[('website_id', 'in', (False, current_website_id))]"):category>/page/<int:page>'''
], type='http', auth="public", website=True)
def shop(self, page=0, category=None, search='', ppg=False, **post):
if category and not category.can_access_from_current_website():
raise NotFound()
if ppg:
try:
ppg = int(ppg)
@@ -245,7 +247,7 @@ class WebsiteSale(http.Controller):
if attrib_list:
post['attrib'] = attrib_list
categs = request.env['product.public.category'].search([('parent_id', '=', False), ('website_published', '=', True)])
categs = request.env['product.public.category'].search([('parent_id', '=', False)] + request.website.website_domain())
Product = request.env['product.template']
parent_category_ids = []
@@ -292,6 +294,9 @@ class WebsiteSale(http.Controller):
@http.route(['/shop/product/<model("product.template"):product>'], type='http', auth="public", website=True)
def product(self, product, category='', search='', **kwargs):
if not product.can_access_from_current_website():
raise NotFound()
product_context = dict(request.env.context,
active_id=product.id,
partner=request.env.user.partner_id)
@@ -993,7 +998,8 @@ class WebsiteSale(http.Controller):
def add_product(self, name=None, category=0, **post):
product = request.env['product.product'].create({
'name': name or _("New Product"),
'public_categ_ids': category
'public_categ_ids': category,
'website_id': request.website.id,
})
return "/shop/product/%s?enable_editor=1" % slug(product.product_tmpl_id)
+4 -2
View File
@@ -53,7 +53,7 @@ class ProductPricelist(models.Model):
class ProductPublicCategory(models.Model):
_name = "product.public.category"
_inherit = ["website.seo.metadata", "website.published.mixin"]
_inherit = ["website.seo.metadata", "website.published.multi.mixin"]
_description = "Website Product Category"
_order = "sequence, name"
@@ -105,7 +105,7 @@ class ProductPublicCategory(models.Model):
class ProductTemplate(models.Model):
_inherit = ["product.template", "website.seo.metadata", 'website.published.mixin', 'rating.mixin']
_inherit = ["product.template", "website.seo.metadata", 'website.published.multi.mixin', 'rating.mixin']
_order = 'website_published desc, website_sequence desc, name'
_name = 'product.template'
_mail_post_access = 'read'
@@ -180,6 +180,8 @@ class ProductTemplate(models.Model):
class Product(models.Model):
_inherit = "product.product"
website_id = fields.Many2one(related='product_tmpl_id.website_id')
website_price = fields.Float('Website price', compute='_website_price', digits=dp.get_precision('Product Price'))
website_public_price = fields.Float('Website public price', compute='_website_price', digits=dp.get_precision('Product Price'))
website_price_difference = fields.Boolean('Website price difference', compute='_website_price')
+9 -2
View File
@@ -1,10 +1,17 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, fields
from odoo import api, fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
last_website_so_id = fields.Many2one('sale.order', string='Last Online Sales Order', copy=False)
last_website_so_id = fields.Many2one('sale.order', compute='_compute_last_website_so_id', string='Last Online Sales Order')
@api.multi
def _compute_last_website_so_id(self):
SaleOrder = self.env['sale.order']
for partner in self:
current_website_so = SaleOrder.search([('partner_id', '=', partner.id)]).filtered(lambda so: so.order_line.mapped('product_id.product_tmpl_id').can_access_from_current_website())
partner.last_website_so_id = current_website_so and current_website_so[0]
+1 -1
View File
@@ -145,7 +145,7 @@ class Website(models.Model):
@api.multi
def sale_product_domain(self):
return [("sale_ok", "=", True)]
return [("sale_ok", "=", True)] + self.get_current_website().website_domain()
@api.model
def sale_get_payment_term(self, partner):
@@ -12,6 +12,13 @@
<field name="perm_unlink" eval="False"/>
</record>
<record id="product_public_category_public" model="ir.rule">
<field name="name">Public product category public</field>
<field name="model_id" ref="model_product_public_category"/>
<field name="domain_force">[('website_published', '=', True)]</field>
<field name="groups" eval="[(4, ref('base.group_public')), (4, ref('base.group_portal'))]"/>
</record>
<record id="product_supplierinfo_public" model="ir.rule">
<field name="name">Public product supplierinfo</field>
<field name="model_id" ref="product.model_product_supplierinfo"/>
@@ -68,6 +68,28 @@
</field>
</record>
<record model="ir.ui.view" id="product_product_website_tree_view">
<field name="name">product.product.website.tree</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_product_tree_view"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="website_id" groups="website.group_multi_website"/>
</field>
</field>
</record>
<record model="ir.ui.view" id="product_template_website_tree_view">
<field name="name">product.template.website.tree</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_tree_view"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="website_id" groups="website.group_multi_website"/>
</field>
</field>
</record>
<record model="ir.ui.view" id="product_template_form_view">
<field name="name">product.template.product.website.form</field>
<field name="model">product.template</field>
@@ -85,6 +107,7 @@
<group name="shop">
<group string="Shop">
<field name="website_url" invisible="1"/>
<field name="website_id" groups="website.group_multi_website"/>
<field name="public_categ_ids" widget="many2many_tags" string="Categories"/>
<field name="alternative_product_ids" widget="many2many_tags"/>
<field name="accessory_product_ids" widget="many2many_tags"/>
@@ -182,6 +205,7 @@
<group>
<field name="name"/>
<field name="parent_id"/>
<field name="website_id" groups="website.group_multi_website"/>
<field name="sequence"/>
</group>
</div>
@@ -197,6 +221,7 @@
<tree string="Product Public Categories">
<field name="sequence" widget="handle"/>
<field name="display_name"/>
<field name="website_id" groups="website.group_multi_website"/>
</tree>
</field>
</record>
+3 -1
View File
@@ -271,7 +271,9 @@
<!-- Add to cart button-->
<template id="categories_recursive" name="Category list">
<li class="nav-item">
<a t-att-href="keep('/shop/category/' + slug(c), category=0)" t-field="c.name" t-attf-class="nav-link#{' active' if c.id == int(category or 0) else ''}"></a>
<a t-att-href="keep('/shop/category/' + slug(c), category=0)" t-attf-class="nav-link#{' active' if c.id == int(category or 0) else ''}">
<span t-field="c.name"/> <span t-if="not c.website_published" class="text-danger">(Unpublished)</span>
</a>
<ul t-if="c.child_id" class="nav nav-pills flex-column nav-hierarchy">
<t t-foreach="c.child_id" t-as="c">
<t t-call="website_sale.categories_recursive" />
@@ -6,7 +6,7 @@ from odoo import fields, models
class DeliveryCarrier(models.Model):
_name = 'delivery.carrier'
_inherit = ['delivery.carrier', 'website.published.mixin']
_inherit = ['delivery.carrier', 'website.published.multi.mixin']
website_description = fields.Text(related='product_id.description_sale', string='Description for Online Quotations')
website_published = fields.Boolean(default=False)
+10 -2
View File
@@ -57,7 +57,8 @@ class WebsiteSlides(http.Controller):
""" Returns a list of available channels: if only one is available,
redirects directly to its slides
"""
channels = request.env['slide.channel'].search([], order='sequence, id')
domain = request.website.website_domain()
channels = request.env['slide.channel'].search(domain, order='sequence, id')
if not channels:
return request.render("website_slides.channel_not_found")
elif len(channels) == 1:
@@ -71,6 +72,7 @@ class WebsiteSlides(http.Controller):
def sitemap_slide(env, rule, qs):
Channel = env['slide.channel']
dom = sitemap_qs2dom(qs=qs, route='/slides/', field=Channel._rec_name)
dom += env['website'].get_current_website().website_domain()
for channel in Channel.search(dom):
loc = '/slides/%s' % slug(channel)
if not qs or qs.lower() in loc:
@@ -93,6 +95,9 @@ class WebsiteSlides(http.Controller):
'''/slides/<model("slide.channel"):channel>/category/<model("slide.category"):category>/<string:slide_type>/page/<int:page>'''],
type='http', auth="public", website=True, sitemap=sitemap_slide)
def channel(self, channel, category=None, tag=None, page=1, slide_type=None, sorting='creation', search=None, **kw):
if not channel.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
user = request.env.user
Slide = request.env['slide.slide']
domain = [('channel_id', '=', channel.id)]
@@ -164,8 +169,11 @@ class WebsiteSlides(http.Controller):
# SLIDE.SLIDE CONTOLLERS
# --------------------------------------------------
@http.route('''/slides/slide/<model("slide.slide", "[('channel_id.can_see', '=', True)]"):slide>''', type='http', auth="public", website=True)
@http.route('''/slides/slide/<model("slide.slide", "[('channel_id.can_see', '=', True), ('website_id', 'in', (False, current_website_id))]"):slide>''', type='http', auth="public", website=True)
def slide_view(self, slide, **kwargs):
if not slide.channel_id.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
values = self._get_slide_detail(slide)
if not values.get('private'):
self._set_viewed_slide(slide, 'slide')
@@ -2,9 +2,8 @@
<odoo>
<data noupdate="1">
<record model="ir.config_parameter" id="google_app_key">
<field name="key">website_slides.google_app_key</field>
<field name="value">AIzaSyDOWlmDW-7DbLmOR9ZsT5AOEXf4n6TFwQA</field>
<record id="website.default_website" model="website">
<field name="website_slide_google_app_key">AIzaSyDOWlmDW-7DbLmOR9ZsT5AOEXf4n6TFwQA</field>
</record>
<record id="website_menu_slides" model="website.menu">
+1
View File
@@ -2,3 +2,4 @@
from . import slides
from . import res_config_settings
from . import website
@@ -7,4 +7,4 @@ from odoo import api, fields, models
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
website_slide_google_app_key = fields.Char(string='Google Doc Key', config_parameter='website_slides.google_app_key')
website_slide_google_app_key = fields.Char(related='website_id.website_slide_google_app_key')
+9 -5
View File
@@ -26,7 +26,7 @@ class Channel(models.Model):
channels. """
_name = 'slide.channel'
_description = 'Channel for Slides'
_inherit = ['mail.thread', 'website.seo.metadata', 'website.published.mixin']
_inherit = ['mail.thread', 'website.seo.metadata', 'website.published.multi.mixin']
_order = 'sequence, id'
_order_by_strategy = {
'most_viewed': 'total_views desc',
@@ -330,6 +330,7 @@ class Slide(models.Model):
self[key] = value
# website
website_id = fields.Many2one(related='channel_id.website_id', readonly=True)
date_published = fields.Datetime('Publish Date')
likes = fields.Integer('Likes')
dislikes = fields.Integer('Dislikes')
@@ -469,14 +470,17 @@ class Slide(models.Model):
return groups
def get_related_slides(self, limit=20):
domain = [('website_published', '=', True), ('channel_id.visibility', '!=', 'private'), ('id', '!=', self.id)]
domain = request.website.website_domain()
domain += [('website_published', '=', True), ('channel_id.visibility', '!=', 'private'), ('id', '!=', self.id)]
if self.category_id:
domain += [('category_id', '=', self.category_id.id)]
for record in self.search(domain, limit=limit):
yield record
def get_most_viewed_slides(self, limit=20):
for record in self.search([('website_published', '=', True), ('channel_id.visibility', '!=', 'private'), ('id', '!=', self.id)], limit=limit, order='total_views desc'):
domain = request.website.website_domain()
domain += [('website_published', '=', True), ('channel_id.visibility', '!=', 'private'), ('id', '!=', self.id)]
for record in self.search(domain, limit=limit, order='total_views desc'):
yield record
def _post_publication(self):
@@ -542,7 +546,7 @@ class Slide(models.Model):
return {'error': _('Unknown document')}
def _parse_youtube_document(self, document_id, only_preview_fields):
key = self.env['ir.config_parameter'].sudo().get_param('website_slides.google_app_key')
key = self.env['website'].get_current_website().website_slide_google_app_key
fetch_res = self._fetch_data('https://www.googleapis.com/youtube/v3/videos', {'id': document_id, 'key': key, 'part': 'snippet', 'fields': 'items(id,snippet)'}, 'json')
if fetch_res.get('error'):
return fetch_res
@@ -592,7 +596,7 @@ class Slide(models.Model):
if access_token:
params['access_token'] = access_token
if not params.get('access_token'):
params['key'] = self.env['ir.config_parameter'].sudo().get_param('website_slides.google_app_key')
params['key'] = self.env['website'].get_current_website().website_slide_google_app_key
fetch_res = self._fetch_data('https://www.googleapis.com/drive/v2/files/%s' % document_id, params, "json")
if fetch_res.get('error'):
+10
View File
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class Website(models.Model):
_inherit = "website"
website_slide_google_app_key = fields.Char('Google Doc Key')
@@ -60,6 +60,7 @@
<group>
<field name="channel_id"/>
<field name="category_id" context="{'default_channel_id': channel_id}"/>
<field name="website_id" groups="website.group_multi_website"/>
<field name="website_url"/>
</group>
<notebook>
@@ -108,6 +109,7 @@
<field name="arch" type="xml">
<tree string="Slides">
<field name="name"/>
<field name="website_id" groups="website.group_multi_website"/>
<field name="active" invisible="1"/>
<field name="slide_type"/>
<field name="channel_id"/>
@@ -198,7 +200,7 @@
</p>
</field>
</record>
<!-- SLIDE.CHANNEL VIEWS -->
<record model="ir.ui.view" id="view_slide_channel_form">
<field name="name">slide.channel.form</field>
@@ -229,6 +231,7 @@
<page string="General">
<group>
<group>
<field name="website_id" groups="website.group_multi_website"/>
<field name="promote_strategy" widget="radio"/>
<field name="custom_slide_id" class="oe_edit_only" attrs="{'invisible': [('promote_strategy', '!=', 'custom')]}" domain="[('channel_id','=',id), ('website_published','=',True)]"/>
<field name="promoted_slide_id" attrs="{'invisible': [('promote_strategy', '!=', 'custom')]}"/>
@@ -269,6 +272,7 @@
<tree string="Channel">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="website_id" groups="website.group_multi_website"/>
<field name="active" invisible="1"/>
<field name="visibility"/>
<field name="promote_strategy"/>
@@ -297,7 +301,7 @@
</p>
</field>
</record>
<menuitem name="Slides"
id="menu_website_slides_root"
sequence="32"
@@ -310,13 +314,13 @@
sequence="140"
parent="website.menu_website_global_configuration"
groups="website.group_website_designer"/>
<menuitem action="action_slide_channels" id="menu_action_slide_channels_global"
<menuitem action="action_slide_channels" id="menu_action_slide_channels_global"
parent="menu_website_slides_root_global" sequence="1"/>
<menuitem action="action_ir_slide_category" id="menu_action_ir_slide_category_global" parent="menu_website_slides_root_global" sequence="2"/>
<menuitem action="action_slide_tag" id="menu_slide_tag" parent="menu_website_slides_root_global" sequence="10" groups="base.group_no_one"/>
</data>
</odoo>
+10 -1
View File
@@ -6,6 +6,7 @@ from collections import defaultdict
from odoo import api, fields, models, tools, SUPERUSER_ID, _
from odoo.exceptions import ValidationError
from odoo.osv import expression
from odoo.tools import config
from odoo.tools.safe_eval import safe_eval
@@ -60,8 +61,16 @@ class IrRule(models.Model):
if any(rule.model_id.model == self._name for rule in self):
raise ValidationError(_('Rules can not be applied on the Record Rules model.'))
def _compute_domain_keys(self):
""" Return the list of context keys to use for caching ``_compute_domain``. """
return []
@api.model
@tools.ormcache('self._uid', 'model_name', 'mode')
@tools.conditional(
'xml' not in config['dev_mode'],
tools.ormcache('self._uid', 'model_name', 'mode',
'tuple(self._context.get(k) for k in self._compute_domain_keys())'),
)
def _compute_domain(self, model_name, mode="read"):
if mode not in self._MODES:
raise ValueError('Invalid mode: %r' % (mode,))
+1 -1
View File
@@ -1095,7 +1095,7 @@ actual arch.
def _read_template_keys(self):
""" Return the list of context keys to use for caching ``_read_template``. """
return ['lang', 'inherit_branding', 'editable', 'translatable', 'edit_translations', 'website_id']
return ['lang', 'inherit_branding', 'editable', 'translatable', 'edit_translations']
# apply ormcache_context decorator unless in dev mode...
@api.model