[IMP] website_forum: Add privacy

Add privacy on forum: public or private. If private, we must select
one group that has access.

For forum linked to eLearning course: the privacy of forum is the same
as the course. If the course is not yet published, the forum will be
invisible (unless we have the group group_website_slides_officer).

If a forum linked to a course is unlinked or removed, the forum will
become private and his group will be set to group_website_slides_officer.

All posts and tags have the same access rules as their linked forum.

closes odoo/odoo#43486

Taskid: 2169965
Related: odoo/upgrade#758
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
jbm-odoo
2020-02-28 08:58:43 +00:00
committed by Yannick Tivisse
parent ee519c222f
commit 49d64a8fb1
13 changed files with 295 additions and 29 deletions
+1
View File
@@ -24,6 +24,7 @@ Ask questions, get answers, no distractions
'views/website_forum_profile.xml',
'views/ir_qweb.xml',
'security/ir.model.access.csv',
'security/website_forum_security.xml',
'data/badges_question.xml',
'data/badges_answer.xml',
'data/badges_participation.xml',
+11 -4
View File
@@ -48,19 +48,26 @@ class WebsiteForum(WebsiteProfile):
return request.render("website_forum.forum_all", {'forums': forums})
@http.route('/forum/new', type='json', auth="user", methods=['POST'], website=True)
def forum_create(self, forum_name="New Forum", forum_mode="questions", add_menu=False):
forum_id = request.env['forum.forum'].create({
def forum_create(self, forum_name="New Forum", forum_mode="questions", forum_privacy="public", forum_privacy_group=False, add_menu=False):
forum = {
'name': forum_name,
'mode': forum_mode,
'privacy': forum_privacy,
'website_id': request.website.id,
})
}
if forum_privacy == 'private' and forum_privacy_group:
forum['authorized_group_id'] = forum_privacy_group
forum_id = request.env['forum.forum'].create(forum)
if add_menu:
request.env['website.menu'].create({
group = [int(forum_privacy_group)] if forum_privacy == 'private' else [request.env.ref('base.group_portal').id, request.env.ref('base.group_user').id]
menu_id = request.env['website.menu'].create({
'name': forum_name,
'url': "/forum/%s" % slug(forum_id),
'parent_id': request.website.menu_id.id,
'website_id': request.website.id,
'group_ids': [(6, 0, group)]
})
forum_id.menu_id = menu_id
return "/forum/%s" % slug(forum_id)
def sitemap_forum(env, rule, qs):
+22
View File
@@ -34,6 +34,14 @@ class Forum(models.Model):
('discussions', 'Discussions')],
string='Forum Mode', required=True, default='questions',
help='Questions mode: only one answer allowed\n Discussions mode: multiple answers allowed')
privacy = fields.Selection([
('public', 'Public'),
('connected', 'Signed In'),
('private', 'Some users')],
help="Public: Forum is pubic\nSigned In: Forum is visible for signed in users\nSome users: Forum and their content are hidden for non members of selected group",
default='public')
authorized_group_id = fields.Many2one('res.groups', 'Authorized Group')
menu_id = fields.Many2one('website.menu', 'Menu', copy=False)
active = fields.Boolean(default=True)
faq = fields.Html('Guidelines', default=_get_default_faq, translate=html_translate, sanitize=False)
description = fields.Text('Description', translate=True)
@@ -153,6 +161,20 @@ class Forum(models.Model):
return super(Forum, self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True)).create(values)
def write(self, vals):
if 'privacy' in vals:
if not vals['privacy']:
# The forum is neither public, neither private, remove menu to avoid conflict
self.menu_id.unlink()
elif vals['privacy'] == 'public':
# The forum is public, the menu must be also public
vals['authorized_group_id'] = False
self.menu_id.write({'group_ids': [(5, 0, 0)]})
elif vals['privacy'] == 'connected':
vals['authorized_group_id'] = False
self.menu_id.write({'group_ids': [(6, 0, [self.env.ref('base.group_portal').id, self.env.ref('base.group_user').id])]})
if 'authorized_group_id' in vals and vals['authorized_group_id']:
self.menu_id.write({'group_ids': [(6, 0, [vals['authorized_group_id']])]})
res = super(Forum, self).write(vals)
if 'active' in vals:
# archiving/unarchiving a forum does it on its posts, too
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="website_forum_public" model="ir.rule">
<field name="name">Website forum: Public user can only access to public forum</field>
<field name="model_id" ref="model_forum_forum"/>
<field name="domain_force">[('privacy', '=', 'public')]</field>
<field name="groups" eval="[(4, ref('base.group_public'))]"/>
</record>
<record id="website_forum_connected" model="ir.rule">
<field name="name">Website forum: User can only access to public (or authorized) forum</field>
<field name="model_id" ref="model_forum_forum"/>
<field name="domain_force">[
'|',
('privacy', 'in', ['public', 'connected']),
'&amp;',
('privacy', '=', 'private'),
('authorized_group_id', 'in', user.groups_id.ids)]</field>
<field name="groups" eval="[(4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
</record>
<record id="website_forum_create_website_designer" model="ir.rule">
<field name="name">Website forum: Website designer can create private forum</field>
<field name="model_id" ref="model_forum_forum"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('website.group_website_designer'))]"/>
<field name="perm_unlink" eval="0"/>
<field name="perm_write" eval="0"/>
<field name="perm_read" eval="0"/>
<field name="perm_create" eval="1"/>
</record>
<record id="website_forum_private" model="ir.rule">
<field name="name">Website forum: All access for manager</field>
<field name="model_id" ref="model_forum_forum"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('base.group_erp_manager'))]"/>
</record>
<record id="website_forum_public_post" model="ir.rule">
<field name="name">Website forum post: Public user can only access to public post</field>
<field name="model_id" ref="model_forum_post"/>
<field name="domain_force">[('forum_id.privacy', '=', 'public')]</field>
<field name="groups" eval="[(4, ref('base.group_public'))]"/>
</record>
<record id="website_forum_connected_post" model="ir.rule">
<field name="name">Website forum post: User can only access to public (or authorized) post</field>
<field name="model_id" ref="model_forum_post"/>
<field name="domain_force">['|', ('forum_id.privacy', 'in', ['public', 'connected']), '&amp;', ('forum_id.privacy', '=', 'private'), ('forum_id.authorized_group_id', 'in', user.groups_id.ids)]</field>
<field name="groups" eval="[(4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
</record>
<record id="website_forum_private_post" model="ir.rule">
<field name="name">Website forum post : All access for manager</field>
<field name="model_id" ref="model_forum_post"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('base.group_erp_manager'))]"/>
</record>
<record id="website_forum_public_tag" model="ir.rule">
<field name="name">Website forum tag: Public user can only access to tag linked to public forum</field>
<field name="model_id" ref="model_forum_tag"/>
<field name="domain_force">[('forum_id.privacy', '=', 'public')]</field>
<field name="groups" eval="[(4, ref('base.group_public'))]"/>
</record>
<record id="website_forum_connected_tag" model="ir.rule">
<field name="name">Website forum tag: User can only access to tag linked to public (or authorized) forum</field>
<field name="model_id" ref="model_forum_tag"/>
<field name="domain_force">['|', ('forum_id.privacy', 'in', ['public', 'connected']), '&amp;', ('forum_id.privacy', '=', 'private'), ('forum_id.authorized_group_id', 'in', user.groups_id.ids)]</field>
<field name="groups" eval="[(4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
</record>
<record id="website_forum_private_tag" model="ir.rule">
<field name="name">Website forum tag : Manager user can access to all tags</field>
<field name="model_id" ref="model_forum_tag"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('base.group_erp_manager'))]"/>
</record>
</odoo>
@@ -12,6 +12,9 @@ var ForumCreateDialog = Dialog.extend({
['/website_forum/static/src/xml/website_forum_templates.xml']
),
template: 'website_forum.add_new_forum',
events: _.extend({}, Dialog.prototype.events, {
'change input[name="privacy"]': '_onPrivacyChanged',
}),
/**
* @override
@@ -36,19 +39,67 @@ var ForumCreateDialog = Dialog.extend({
});
this._super(parent, options);
},
start: function () {
var self = this;
return this._super.apply(this, arguments).then(function () {
var $input = self.$('#group_id');
$input.select2({
width: '100%',
allowClear: true,
formatNoMatches: false,
multiple: false,
selection_data: false,
fill_data: function (query, data) {
var that = this;
var tags = {results: []};
_.each(data, function (obj) {
if (that.matcher(query.term, obj.display_name)) {
tags.results.push({id: obj.id, text: obj.display_name});
}
});
query.callback(tags);
},
query: function (query) {
var that = this;
// fetch data only once and store it
if (!this.selection_data) {
self._rpc({
model: 'res.groups',
method: 'search_read',
args: [[], ['display_name']],
}).then(function (data) {
that.fill_data(query, data);
that.selection_data = data;
});
} else {
this.fill_data(query, this.selection_data);
}
}
});
});
},
onCreateClick: function () {
var $dialog = this.$el;
var forumName = $dialog.find('input[name=forum_name]').val();;
if (!forumName) {
var $forumName = $dialog.find('input[name=forum_name]');
if (!$forumName.val()) {
$forumName.addClass('border-danger');
return;
}
var $forumPrivacyGroup = $dialog.find('input[name=group_id]');
var forumPrivacy = $dialog.find('input:radio[name=privacy]:checked').val();
if (forumPrivacy === 'private' && !$forumPrivacyGroup.val()) {
this.$("#group-required").removeClass('d-none');
return;
}
var addMenu = ($dialog.find('input[type="checkbox"]').is(':checked'));
var forumMode = $dialog.find('input[type="radio"]:checked').val();
var forumMode = $dialog.find('input:radio[name=mode]:checked').val();
return this._rpc({
route: '/forum/new',
params: {
forum_name: forumName,
forum_name: $forumName.val(),
forum_mode: forumMode,
forum_privacy: forumPrivacy,
forum_privacy_group: $forumPrivacyGroup.val(),
add_menu: addMenu || "",
},
}).then(function (url) {
@@ -56,6 +107,12 @@ var ForumCreateDialog = Dialog.extend({
return new Promise(function () {});
});
},
/**
* @private
*/
_onPrivacyChanged: function (ev) {
this.$('.show_visibility_group').toggleClass('d-none', ev.target.value !== 'private');
},
});
WebsiteNewMenu.include({
@@ -3,7 +3,7 @@
<t t-name="website_forum.add_new_forum">
<form id="editor_new_forum">
<div class="form-group row">
<label for="page-name" class="col-md-4 col-form-label">Forum Name:</label>
<label for="page-name" class="col-md-4 col-form-label">Forum Name</label>
<div class="col-md-8">
<input type="text" name="forum_name" class="form-control" required="required"/>
<div class="custom-control custom-checkbox mt-2">
@@ -13,7 +13,8 @@
</div>
</div>
<div class="form-group row mt-2">
<label class="col-md-4 col-form-label">Forum Mode:</label>
<label class="col-md-4 col-form-label" data-toggle="tooltip" data-placement="bottom"
title="Questions and Answers mode: only one answer allowed\n Discussions mode: multiple answers allowed">Forum Mode</label>
<div class="col-md-8">
<div class="custom-control custom-radio">
<input type="radio" id="questions" name="mode" class="custom-control-input" value="questions" checked="checked"/>
@@ -25,6 +26,28 @@
</div>
</div>
</div>
<div class="form-group row mt-2">
<label class="col-md-4 col-form-label" data-toggle="tooltip" data-placement="bottom"
title="Public: Forum is pubic\nSigned In: Forum is visible for signed in users\nSome users: Forum and their content are hidden for non members of selected group">Privacy</label>
<div class="col-md-8">
<div class="custom-control custom-radio">
<input type="radio" id="public" name="privacy" class="custom-control-input" value="public" checked="checked"/>
<label class="custom-control-label" for="public">Public</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" id="connected" name="privacy" class="custom-control-input" value="connected"/>
<label class="custom-control-label" for="connected">Signed In</label>
</div>
<div class="custom-control custom-radio">
<input type="radio" id="private" name="privacy" class="custom-control-input" value="private"/>
<label class="custom-control-label" for="private">Some Users</label>
</div>
<div class="form-group show_visibility_group d-none">
<input type="text" class="form-control" name="group_id" id="group_id" placeholder="Select Authorized Group"/>
<p id="group-required" class="text-danger mt-1 mb-0 d-none">Please fill in this field</p>
</div>
</div>
</div>
</form>
</t>
</templates>
+21 -15
View File
@@ -42,31 +42,40 @@
</h1>
</div>
<group>
<field name="mode" widget="radio" required="True"/>
<field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
<group>
<field name="mode" widget="radio" required="True"/>
<field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
</group>
</group>
<notebook>
<page name="options" string="Options">
<group>
<group string="Order" name="group_order">
<field name="default_order"/>
<group string="Order and Visibility" name="group_order">
<field name="default_order" string="Default Sort"/>
<field name="privacy" widget="radio" attrs="{'required': True}"/>
<field name="authorized_group_id" options="{'no_create': True}" attrs="{'invisible': [('privacy', '!=', 'private')], 'required': [('privacy', '=', 'private')]}"/>
<label for="relevancy_post_vote" string="Relevance Computation" groups="base.group_no_one" attrs="{'invisible':[('default_order','!=','relevancy desc')]}"/>
<div groups="base.group_no_one" class="o_row" attrs="{'invisible':[('default_order','!=','relevancy desc')]}">
(votes - 1) ** <field name="relevancy_post_vote"/> / (days + 2) ** <field name="relevancy_time_decay"/>
</div>
</group>
</group>
<group>
<field name="description" nolabel="1" placeholder="Description visible on website"/>
</group>
</page>
<page name="karma_gains" string="Karma Gains">
<group name="karma_gain_details">
<field name="karma_gen_question_new"/>
<field name="karma_gen_question_upvote"/>
<field name="karma_gen_question_downvote"/>
<field name="karma_gen_answer_upvote"/>
<field name="karma_gen_answer_downvote"/>
<field name="karma_gen_answer_accept"/>
<field name="karma_gen_answer_accepted"/>
<field name="karma_gen_answer_flagged"/>
<group>
<field name="karma_gen_question_new"/>
<field name="karma_gen_question_upvote"/>
<field name="karma_gen_question_downvote"/>
<field name="karma_gen_answer_upvote"/>
<field name="karma_gen_answer_downvote"/>
<field name="karma_gen_answer_accept"/>
<field name="karma_gen_answer_accepted"/>
<field name="karma_gen_answer_flagged"/>
</group>
</group>
</page>
<page name="karma_rights" string="Karma Related Rights">
@@ -103,9 +112,6 @@
</group>
</page>
</notebook>
<group>
<field name="description"/>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
+5 -1
View File
@@ -1608,7 +1608,11 @@
<!-- User Navbar -->
<template id="user_navbar_inherit_website_forum" inherit_id="website.user_navbar">
<xpath expr="//div[@id='o_new_content_menu_choices']//div[@name='module_website_forum']" position="replace">
<xpath expr="//div[@id='o_new_content_menu_choices']//div[@name='module_website_forum']" position="attributes">
<attribute name="name"/>
<attribute name="t-att-data-module-id"/>
<attribute name="t-att-data-module-shortdesc"/>
<attribute name="groups">website.group_website_designer</attribute>
</xpath>
</template>
@@ -13,6 +13,7 @@
],
'data': [
'security/ir.model.access.csv',
'security/website_slides_forum_security.xml',
'views/forum_views.xml',
'views/slide_channel_views.xml',
'views/website_slides_menu_views.xml',
+3 -2
View File
@@ -7,8 +7,9 @@ from odoo import api, fields, models
class Forum(models.Model):
_inherit = 'forum.forum'
slide_channel_ids = fields.One2many('slide.channel', 'forum_id', 'Courses')
slide_channel_id = fields.Many2one('slide.channel', 'Course', compute='_compute_slide_channel_id')
slide_channel_ids = fields.One2many('slide.channel', 'forum_id', 'Courses', help="Edit the course linked to this forum on the course form.")
slide_channel_id = fields.Many2one('slide.channel', 'Course', compute='_compute_slide_channel_id', store=True)
visibility = fields.Selection(related='slide_channel_id.visibility', help="Forum linked to a Course, the visibility is the one applied on the course.")
@api.depends('slide_channel_ids')
def _compute_slide_channel_id(self):
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
from odoo import api, fields, models
class Channel(models.Model):
@@ -24,3 +24,23 @@ class Channel(models.Model):
action['domain'] = [('forum_id', '=', self.forum_id.id)]
return action
@api.model
def create(self, vals):
channel = super(Channel, self.with_context(mail_create_nosubscribe=True)).create(vals)
if channel.forum_id:
channel.forum_id.privacy = False
return channel
def write(self, vals):
old_forum = self.forum_id
res = super(Channel, self).write(vals)
if 'forum_id' in vals:
self.forum_id.privacy = False
if old_forum != self.forum_id:
old_forum.write({
'privacy': 'private',
'authorized_group_id': self.env.ref('website_slides.group_website_slides_officer').id,
})
return res
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="website_slides_forum_public" model="ir.rule">
<field name="name">Website forum: User can only access to forum related to followed courses</field>
<field name="model_id" ref="website_forum.model_forum_forum"/>
<field name="domain_force">['&amp;', ('slide_channel_ids.website_published', '=', True), '|', ('slide_channel_ids.visibility', '=', 'public'), ('slide_channel_ids.partner_ids', 'in', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_public')), (4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
</record>
<record id="website_slides_forum_website_slides_officer" model="ir.rule">
<field name="name">Website forum: website slides officer can access all forum</field>
<field name="model_id" ref="website_forum.model_forum_forum"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('website_slides.group_website_slides_officer'))]"/>
</record>
<record id="website_slides_forum_public_post" model="ir.rule">
<field name="name">Website forum post: User can only access to post linked to forum related to followed courses</field>
<field name="model_id" ref="website_forum.model_forum_post"/>
<field name="domain_force">['&amp;', ('forum_id.slide_channel_ids.website_published', '=', True), '|', ('forum_id.slide_channel_ids.visibility', '=', 'public'), ('forum_id.slide_channel_ids.partner_ids', 'in', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_public')), (4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
</record>
<record id="website_slides_forum_website_slides_officer_post" model="ir.rule">
<field name="name">Website forum post: website slides officer can access all post</field>
<field name="model_id" ref="website_forum.model_forum_post"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('website_slides.group_website_slides_officer'))]"/>
</record>
<record id="website_slides_forum_public_tag" model="ir.rule">
<field name="name">Website slides forum tag: User can only access to tag linked to forum related to followed courses</field>
<field name="model_id" ref="website_forum.model_forum_tag"/>
<field name="domain_force">['&amp;', ('forum_id.slide_channel_ids.website_published', '=', True), '|', ('forum_id.slide_channel_ids.visibility', '=', 'public'), ('forum_id.slide_channel_ids.partner_ids', 'in', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_public')), (4, ref('base.group_portal')), (4, ref('base.group_user'))]"/>
</record>
<record id="website_slides_forum_website_slides_officer_tag" model="ir.rule">
<field name="name">Website slides forum tag: website slides officer can access all tag</field>
<field name="model_id" ref="website_forum.model_forum_tag"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('website_slides.group_website_slides_officer'))]"/>
</record>
</odoo>
@@ -10,6 +10,15 @@
<field name="slide_channel_id" readonly="True"/>
</group>
</xpath>
<xpath expr="//field[@name='privacy']" position="attributes">
<attribute name="attrs">{'invisible': [('slide_channel_id', '!=', False)], 'required': [('slide_channel_id', '!=', 'False')]}</attribute>
</xpath>
<xpath expr="//field[@name='authorized_group_id']" position="attributes">
<attribute name="attrs">{'invisible': ['|', ('privacy', '!=', 'private'), ('slide_channel_id', '!=', False)], 'required': [('privacy', '=', 'private')]}</attribute>
</xpath>
<xpath expr="//field[@name='privacy']" position="before">
<field name="visibility" attrs="{'invisible': [('slide_channel_id', '=', False)]}"/>
</xpath>
</field>
</record>