[FIX] website_slides: hide publish widget if user cannot publish

Purpose
=======

The "publish/unpublish" widget is always shown on slide.channel/slide.slide for
a website_publisher user even if he's not allowed to manage the publishing of
the channel (see slide.channel.can_publish field).

This commit will hide the widget on both the slide.channel and the slide.slide frontend
pages if the user doesn't have the right to publish/unpublish. A generic solution
implemented directly in the publish mixin has been chosen to ease understanding
and allow easy reuse in other applications.

Commit linked to task ID 1902304 (main elearning task).

closes odoo/odoo#31187
This commit is contained in:
Robot Odoo
2019-02-19 10:20:00 +01:00
committed by GitHub
5 changed files with 68 additions and 12 deletions
+34 -1
View File
@@ -4,9 +4,10 @@
import logging
from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.http import request
from odoo.osv import expression
from odoo.exceptions import AccessError
logger = logging.getLogger(__name__)
@@ -120,6 +121,7 @@ class WebsitePublishedMixin(models.AbstractModel):
website_published = fields.Boolean('Visible on current website', related='is_published', readonly=False)
is_published = fields.Boolean('Is published', copy=False)
can_publish = fields.Boolean('Can publish', compute='_compute_can_publish')
website_url = fields.Char('Website URL', compute='_compute_website_url', help='The full URL to access the document through the website.')
@api.multi
@@ -144,9 +146,40 @@ class WebsitePublishedMixin(models.AbstractModel):
'target': 'self',
}
@api.model_create_multi
def create(self, vals_list):
records = super(WebsitePublishedMixin, self).create(vals_list)
is_publish_modified = any('website_published' in values for values in vals_list)
if is_publish_modified and not all(record.can_publish for record in records):
raise AccessError(self._get_can_publish_error_message())
return records
@api.multi
def write(self, values):
if 'website_published' in values and not all(record.can_publish for record in self):
raise AccessError(self._get_can_publish_error_message())
return super(WebsitePublishedMixin, self).write(values)
def create_and_get_website_url(self, **kwargs):
return self.create(kwargs).website_url
@api.multi
def _compute_can_publish(self):
""" This method can be overridden if you need more complex rights management than just 'website_publisher'
The publish widget will be hidden and the user won't be able to change the 'website_published' value
if this method sets can_publish False """
for record in self:
record.can_publish = True
@api.model
def _get_can_publish_error_message(self):
""" Override this method to customize the error message shown when the user doesn't
have the rights to publish/unpublish. """
return _("You do not have the rights to publish/unpublish")
class WebsitePublishedMultiMixin(WebsitePublishedMixin):
@@ -64,7 +64,7 @@
</ul>
<ul class="o_menu_systray d-none d-md-block" groups="website.group_website_publisher">
<li t-if="'website_published' in main_object.fields_get()" t-attf-class="js_publish_management #{main_object.website_published and 'css_published' or 'css_unpublished'}" t-att-data-id="main_object.id" t-att-data-object="main_object._name" t-att-data-controller="publish_controller">
<li t-if="'website_published' in main_object.fields_get() and ('can_publish' not in main_object.fields_get() or main_object.can_publish)" t-attf-class="js_publish_management #{main_object.website_published and 'css_published' or 'css_unpublished'}" t-att-data-id="main_object.id" t-att-data-object="main_object._name" t-att-data-controller="publish_controller">
<label class="o_switch o_switch_danger js_publish_btn" for="id">
<input type="checkbox" t-att-checked="main_object.website_published" id="id"/>
<span/>
+18 -6
View File
@@ -136,8 +136,7 @@ class Channel(models.Model):
# not stored access fields, depending on each user
completed = fields.Boolean('Done', compute='_compute_user_statistics')
completion = fields.Integer('Completion', compute='_compute_user_statistics')
can_upload = fields.Boolean('Can Upload', compute='_compute_access')
can_publish = fields.Boolean('Can Publish', compute='_compute_access')
can_upload = fields.Boolean('Can Upload', compute='_compute_can_upload')
# karma generation
karma_gen_slide_vote = fields.Integer(string='Lesson voted', default=1)
karma_gen_channel_share = fields.Integer(string='Course shared', default=2)
@@ -215,12 +214,25 @@ class Channel(models.Model):
for record in self:
record.completed, record.completion = mapped_data.get(record.id, (False, 0))
@api.depends('channel_type', 'partner_ids', 'upload_group_ids')
def _compute_access(self):
@api.depends('upload_group_ids')
def _compute_can_upload(self):
for record in self:
record.can_upload = not self.env.user.share and (not record.upload_group_ids or bool(record.upload_group_ids & self.env.user.groups_id))
@api.depends('channel_type', 'user_id', 'can_upload')
def _compute_can_publish(self):
""" For channels of type 'training', only the responsible (see user_id field) can publish slides.
The 'sudo' user needs to be handled because he's the one used for uploads done on the front-end when the
logged in user is not publisher but fulfills the upload_group_ids condition. """
for record in self:
is_doc = record.channel_type == 'documentation'
record.can_upload = not self.env.user.share and (not record.upload_group_ids or bool(record.upload_group_ids & self.env.user.groups_id))
record.can_publish = record.can_upload and self.env.user.has_group('website.group_website_publisher') and (is_doc or record.user_id == self.env.user)
record.can_publish = record.can_upload \
and self.env.user.has_group('website.group_website_publisher') \
and (is_doc or record.user_id == self.env.user or self.env.user._is_superuser())
@api.model
def _get_can_publish_error_message(self):
return _("Publishing is restricted to the responsible of training courses or members of the publisher group for documentation courses")
@api.multi
@api.depends('name')
+10 -4
View File
@@ -254,6 +254,15 @@ class Slide(models.Model):
url = '%s/slides/slide/%s' % (base_url, slug(slide))
slide.website_url = url
@api.depends('channel_id.can_publish')
def _compute_can_publish(self):
for record in self:
record.can_publish = record.channel_id.can_publish
@api.model
def _get_can_publish_error_message(self):
return _("Publishing is restricted to the responsible of training courses or members of the publisher group for documentation courses")
# ---------------------------------------------------------
# ORM Overrides
# ---------------------------------------------------------
@@ -263,7 +272,7 @@ class Slide(models.Model):
# Do not publish slide if user has not publisher rights
channel = self.env['slide.channel'].browse(values['channel_id'])
if not channel.can_publish:
values['website_published'] = False
# 'website_published' is handled by mixin
values['date_published'] = False
if not values.get('index_content'):
@@ -288,9 +297,6 @@ class Slide(models.Model):
@api.multi
def write(self, values):
if 'website_published' in values and any(not slide.channel_id.can_publish for slide in self):
values.pop('website_published')
if values.get('url') and values['url'] != self.url:
doc_data = self._parse_document_url(values['url']).get('values', dict())
for key, value in doc_data.items():
@@ -154,3 +154,8 @@ class TestAccess(common.SlidesCase):
# standard people should not be responsible as they still cannot publish
self.assertTrue(channel_emp.can_upload)
self.assertFalse(channel_emp.can_publish)
# superuser should always be able to publish even if he's not the responsible
channel_superuser = self.channel.sudo()
self.assertTrue(channel_superuser.can_upload)
self.assertTrue(channel_superuser.can_publish)