[REF] website_slides: revamp channel form view
PURPOSE
Like already done for sale order, invoice of survey, purpose of this commit
is to remove category model and replace by a flagged line (slide). It allows
to easily reorder slides in an embedded list view.
SPECIFICATIONS
This commit completely reworks the slide.channel form view
* improve the content management from the form itself by adding a tree view
with the possibility of drag and dropping slides from category to category.
This tree view has buttons to quickly add content or certification. It
also allow to add a category (slide with flag is_category) without opening
a modal;
* reorder tabs and fields, improve some labels;
* add a custom float field widget to display ratings as stars;
* add stat button for partners who completed/certified ;the course
* add stat button that redirects to the forum posts;
* add stat button that redirects to the channel reviews;
LINKS
Task 1978731
PR #33255
This commit is contained in:
committed by
Thibault Delavallée
parent
b180c66a80
commit
340613d068
@@ -5,7 +5,7 @@
|
||||
<field name="model">slide.channel</field>
|
||||
<field name="inherit_id" ref="website_slides.view_slide_channel_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='action_redirect_to_members']" position="before">
|
||||
<xpath expr="//button[@name='action_redirect_to_done_members']" position="before">
|
||||
<button class="oe_stat_button" type="object"
|
||||
name="action_mass_mailing_attendees"
|
||||
icon="fa-envelope-o"
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
type="object"
|
||||
icon="fa-signal"
|
||||
class="oe_stat_button"
|
||||
attrs="{'invisible': [('enroll', '!=', 'payment')]}"
|
||||
groups="sales_team.group_sale_salesman">
|
||||
<field name="product_sale_revenues" string="Sales" widget="statinfo"/>
|
||||
</button>
|
||||
|
||||
@@ -113,16 +113,16 @@ class Channel(models.Model):
|
||||
('most_viewed', 'Most Viewed')],
|
||||
string="Featuring Policy", default='latest', required=True)
|
||||
access_token = fields.Char("Security Token", copy=False, default=_default_access_token)
|
||||
nbr_presentation = fields.Integer('Number of Presentations', compute='_compute_slides_statistics', store=True)
|
||||
nbr_document = fields.Integer('Number of Documents', compute='_compute_slides_statistics', store=True)
|
||||
nbr_video = fields.Integer('Number of Videos', compute='_compute_slides_statistics', store=True)
|
||||
nbr_infographic = fields.Integer('Number of Infographics', compute='_compute_slides_statistics', store=True)
|
||||
nbr_webpage = fields.Integer("Number of Webpages", compute='_compute_slides_statistics', store=True)
|
||||
nbr_presentation = fields.Integer('Presentations', compute='_compute_slides_statistics', store=True)
|
||||
nbr_document = fields.Integer('Documents', compute='_compute_slides_statistics', store=True)
|
||||
nbr_video = fields.Integer('Videos', compute='_compute_slides_statistics', store=True)
|
||||
nbr_infographic = fields.Integer('Infographics', compute='_compute_slides_statistics', store=True)
|
||||
nbr_webpage = fields.Integer("Webpages", compute='_compute_slides_statistics', store=True)
|
||||
nbr_quiz = fields.Integer("Number of Quizs", compute='_compute_slides_statistics', store=True)
|
||||
total_slides = fields.Integer('# Slides', compute='_compute_slides_statistics', store=True)
|
||||
total_views = fields.Integer('# Views', compute='_compute_slides_statistics', store=True)
|
||||
total_votes = fields.Integer('# Votes', compute='_compute_slides_statistics', store=True)
|
||||
total_time = fields.Float('# Hours', compute='_compute_slides_statistics', digits=(10, 4), store=True)
|
||||
total_slides = fields.Integer('Content', compute='_compute_slides_statistics', store=True)
|
||||
total_views = fields.Integer('Visits', compute='_compute_slides_statistics', store=True)
|
||||
total_votes = fields.Integer('Votes', compute='_compute_slides_statistics', store=True)
|
||||
total_time = fields.Float('Duration', compute='_compute_slides_statistics', digits=(10, 2), store=True)
|
||||
rating_avg_stars = fields.Float("Rating Average (Stars)", compute='_compute_rating_stats', digits=(16, 1))
|
||||
# configuration
|
||||
allow_comment = fields.Boolean(
|
||||
@@ -131,7 +131,7 @@ class Channel(models.Model):
|
||||
" * like content and post comments on documentation course;\n"
|
||||
" * post comment and review on training course;")
|
||||
publish_template_id = fields.Many2one(
|
||||
'mail.template', string='Published Template',
|
||||
'mail.template', string='New Content Email',
|
||||
help="Email template to send slide publication through email",
|
||||
default=lambda self: self.env['ir.model.data'].xmlid_to_res_id('website_slides.slide_template_published'))
|
||||
share_template_id = fields.Many2one(
|
||||
@@ -169,9 +169,9 @@ class Channel(models.Model):
|
||||
karma_gen_channel_rank = fields.Integer(string='Course ranked', default=5)
|
||||
karma_gen_channel_finish = fields.Integer(string='Course finished', default=10)
|
||||
# Karma based actions
|
||||
karma_review = fields.Integer('Add a review', default=10, help="Karma needed to add a review on the course")
|
||||
karma_slide_comment = fields.Integer('Add a comment', default=3, help="Karma needed to add a comment on a slide of this course")
|
||||
karma_slide_vote = fields.Integer('Vote on slide', default=3, help="Karma needed to like/dislike a slide of this course.")
|
||||
karma_review = fields.Integer('Add Review', default=10, help="Karma needed to add a review on the course")
|
||||
karma_slide_comment = fields.Integer('Add Comment', default=3, help="Karma needed to add a comment on a slide of this course")
|
||||
karma_slide_vote = fields.Integer('Vote', default=3, help="Karma needed to like/dislike a slide of this course.")
|
||||
can_review = fields.Boolean('Can Review', compute='_compute_action_rights')
|
||||
can_comment = fields.Boolean('Can Comment', compute='_compute_action_rights')
|
||||
can_vote = fields.Boolean('Can Vote', compute='_compute_action_rights')
|
||||
|
||||
@@ -105,7 +105,7 @@ class Slide(models.Model):
|
||||
channel_id = fields.Many2one('slide.channel', string="Channel", required=True)
|
||||
tag_ids = fields.Many2many('slide.tag', 'rel_slide_tag', 'slide_id', 'tag_id', string='Tags')
|
||||
is_preview = fields.Boolean('Is Preview', default=False, help="The course is accessible by anyone : the users don't need to join the channel to access the content of the course.")
|
||||
completion_time = fields.Float('# Hours', default=1, digits=(10, 4))
|
||||
completion_time = fields.Float('Duration', default=1, digits=(10, 2))
|
||||
# Categories
|
||||
is_category = fields.Boolean('Is a category', default=False)
|
||||
category_id = fields.Many2one('slide.slide', string="Category", compute="_compute_category_id", store=True)
|
||||
@@ -152,7 +152,7 @@ class Slide(models.Model):
|
||||
embedcount_ids = fields.One2many('slide.embed', 'slide_id', string="Embed Count")
|
||||
slide_views = fields.Integer('# of Website Views', store=True, compute="_compute_slide_views")
|
||||
public_views = fields.Integer('# of Public Views')
|
||||
total_views = fields.Integer("Total # Views", default="0", compute='_compute_total', store=True)
|
||||
total_views = fields.Integer("Views", default="0", compute='_compute_total', store=True)
|
||||
# comments
|
||||
comments_count = fields.Integer('Number of comments', compute="_compute_comments_count")
|
||||
# channel
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
odoo.define('website_slides.ratingField', function (require) {
|
||||
"use strict";
|
||||
|
||||
var basicFields = require('web.basic_fields');
|
||||
var fieldRegistry = require('web.field_registry');
|
||||
|
||||
var core = require('web.core');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
|
||||
var FieldFloatRating = basicFields.FieldFloat.extend({
|
||||
xmlDependencies: !basicFields.FieldFloat.prototype.xmlDependencies ?
|
||||
['/website_rating/static/src/xml/website_mail.xml'] : basicFields.FieldFloat.prototype.xmlDependencies.concat(
|
||||
['/website_rating/static/src/xml/website_mail.xml']
|
||||
),
|
||||
description: "",
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_render: function () {
|
||||
var self = this;
|
||||
|
||||
return Promise.resolve(this._super()).then(function () {
|
||||
self.$el.html(QWeb.render('website_rating.rating_stars_static', {
|
||||
'val': self.value / 2,
|
||||
'inline_mode': true
|
||||
}));
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
fieldRegistry.add('field_float_rating', FieldFloatRating);
|
||||
|
||||
return {
|
||||
FieldFloatRating: FieldFloatRating,
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,180 @@
|
||||
odoo.define('survey.slide_category_one2many', function (require){
|
||||
"use strict";
|
||||
|
||||
var Context = require('web.Context');
|
||||
var FieldOne2Many = require('web.relational_fields').FieldOne2Many;
|
||||
var FieldRegistry = require('web.field_registry');
|
||||
var ListRenderer = require('web.ListRenderer');
|
||||
|
||||
var SectionListRenderer = ListRenderer.extend({
|
||||
init: function (parent, state, params) {
|
||||
this.sectionFieldName = "is_category";
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
_checkIfRecordIsSection: function (id){
|
||||
var record = this._findRecordById(id);
|
||||
return record && record.data[this.sectionFieldName];
|
||||
},
|
||||
_findRecordById: function (id){
|
||||
return _.find(this.state.data, function (record){
|
||||
return record.id === id;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Allows to hide specific field in case the record is a section
|
||||
* and, in this case, makes the 'title' field take the space of all the other
|
||||
* fields
|
||||
* @private
|
||||
* @override
|
||||
* @param {*} record
|
||||
* @param {*} node
|
||||
* @param {*} index
|
||||
* @param {*} options
|
||||
*/
|
||||
_renderBodyCell: function (record, node, index, options){
|
||||
var $cell = this._super.apply(this, arguments);
|
||||
|
||||
var isSection = record.data[this.sectionFieldName];
|
||||
|
||||
if (isSection){
|
||||
if (node.attrs.widget === "handle"){
|
||||
return $cell;
|
||||
} else if (node.attrs.name === "name"){
|
||||
var nbrColumns = this._getNumberOfCols();
|
||||
if (this.handleField){
|
||||
nbrColumns--;
|
||||
}
|
||||
if (this.addTrashIcon){
|
||||
nbrColumns--;
|
||||
}
|
||||
$cell.attr('colspan', nbrColumns);
|
||||
} else {
|
||||
$cell.removeClass('o_invisible_modifier');
|
||||
return $cell.addClass('o_hidden');
|
||||
}
|
||||
}
|
||||
return $cell;
|
||||
},
|
||||
/**
|
||||
* Adds specific classes to rows that are sections
|
||||
* to apply custom css on them
|
||||
* @private
|
||||
* @override
|
||||
* @param {*} record
|
||||
* @param {*} index
|
||||
*/
|
||||
_renderRow: function (record, index){
|
||||
var $row = this._super.apply(this, arguments);
|
||||
if (record.data[this.sectionFieldName]) {
|
||||
$row.addClass("o_is_section");
|
||||
}
|
||||
return $row;
|
||||
},
|
||||
/**
|
||||
* Adding this class after the view is rendered allows
|
||||
* us to limit the custom css scope to this particular case
|
||||
* and no other
|
||||
* @private
|
||||
* @override
|
||||
*/
|
||||
_renderView: function (){
|
||||
var def = this._super.apply(this, arguments);
|
||||
var self = this;
|
||||
return def.then(function () {
|
||||
self.$('table.o_list_table').addClass('o_section_list_view');
|
||||
});
|
||||
},
|
||||
// Handlers
|
||||
/**
|
||||
* Overriden to allow different behaviours depending on
|
||||
* the row the user clicked on.
|
||||
* If the row is a section: edit inline
|
||||
* else use a normal modal
|
||||
* @private
|
||||
* @override
|
||||
* @param {*} ev
|
||||
*/
|
||||
_onRowClicked: function (ev){
|
||||
var parent = this.getParent();
|
||||
var recordId = $(ev.currentTarget).data('id');
|
||||
var is_section = this._checkIfRecordIsSection(recordId);
|
||||
if (is_section && parent.mode === "edit"){
|
||||
this.editable = "bottom";
|
||||
} else {
|
||||
this.editable = null;
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
* Overriden to allow different behaviours depending on
|
||||
* the cell the user clicked on.
|
||||
* If the cell is part of a section: edit inline
|
||||
* else use a normal edit modal
|
||||
* @private
|
||||
* @override
|
||||
* @param {*} ev
|
||||
*/
|
||||
_onCellClick: function (ev){
|
||||
var parent = this.getParent();
|
||||
var recordId = $(ev.currentTarget.parentElement).data('id');
|
||||
var is_section = this._checkIfRecordIsSection(recordId);
|
||||
if (is_section && parent.mode === "edit"){
|
||||
this.editable = "bottom";
|
||||
} else {
|
||||
this.editable = null;
|
||||
this.unselectRow();
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
* In this case, navigating in the list caused issues.
|
||||
* For example, editing a section then pressing enter would trigger
|
||||
* the inline edition of the next element in the list. Which is not desired
|
||||
* if the next element ends up being a question and not a section
|
||||
* @override
|
||||
* @param {*} ev
|
||||
*/
|
||||
_onNavigationMove: function (ev){
|
||||
this.unselectRow();
|
||||
},
|
||||
});
|
||||
|
||||
var SectionFieldOne2Many = FieldOne2Many.extend({
|
||||
init: function (parent, name, record, options){
|
||||
this._super.apply(this, arguments);
|
||||
this.sectionFieldName = "is_category";
|
||||
this.rendered = false;
|
||||
},
|
||||
/**
|
||||
* Overriden to use our custom renderer
|
||||
* @private
|
||||
* @override
|
||||
*/
|
||||
_getRenderer: function (){
|
||||
if (this.view.arch.tag === 'tree'){
|
||||
return SectionListRenderer;
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
* Overriden to allow different behaviours depending on
|
||||
* the object we want to add. Adding a section would be done inline
|
||||
* while adding a question would render a modal.
|
||||
* @private
|
||||
* @override
|
||||
* @param {*} ev
|
||||
*/
|
||||
_onAddRecord: function (ev) {
|
||||
var context_str = ev.data.context && ev.data.context[0];
|
||||
var context = new Context(context_str).eval();
|
||||
if (context['default_' + this.sectionFieldName]){
|
||||
this.editable = "bottom";
|
||||
} else {
|
||||
this.editable = null;
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
FieldRegistry.add('slide_category_one2many', SectionFieldOne2Many);
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
table.o_section_list_view tr.o_data_row.o_is_section {
|
||||
font-weight: bold;
|
||||
background-color: #DDD;
|
||||
border-top: 1px solid #BBB;
|
||||
border-bottom: 1px solid #BBB;
|
||||
}
|
||||
@@ -5,6 +5,9 @@
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/scss" href="/website_slides/static/src/scss/rating_rating_views.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/website_slides/static/src/scss/slide_channel_views.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/website_slides/static/src/scss/category_backend.scss" t-ignore="true"/>
|
||||
<script type="text/javascript" src="/website_slides/static/src/js/slide_category_one2many.js"/>
|
||||
<script type="text/javascript" src="/website_slides/static/src/js/rating_field_backend.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -14,12 +14,30 @@
|
||||
<sheet>
|
||||
<widget name="web_ribbon" text="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="action_redirect_to_done_members"
|
||||
type="object"
|
||||
icon="fa-trophy"
|
||||
class="oe_stat_button"
|
||||
groups="website.group_website_publisher">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value"><field name="members_done_count" nolabel="1"/></span>
|
||||
<span name="stat_label" class="o_stat_text">Finished</span>
|
||||
</div>
|
||||
</button>
|
||||
<button name="action_redirect_to_members"
|
||||
type="object"
|
||||
icon="fa-users"
|
||||
class="oe_stat_button"
|
||||
groups="website.group_website_publisher">
|
||||
<field name="members_count" string="Attendees" widget="statinfo"/>
|
||||
</button>
|
||||
<button name="action_view_ratings"
|
||||
type="object"
|
||||
icon="fa-star"
|
||||
class="oe_stat_button"
|
||||
groups="website.group_website_publisher"
|
||||
attrs="{'invisible': [('allow_comment', '=', False)]}">
|
||||
<field name="rating_count" string="Reviews" widget="statinfo"/>
|
||||
</button>
|
||||
<button class="oe_stat_button" name="website_publish_button"
|
||||
type="object" icon="fa-globe">
|
||||
@@ -28,74 +46,94 @@
|
||||
</div>
|
||||
<field name="image_1920" widget="image" class="oe_avatar" options="{'preview_image': 'image_128'}"/>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only" string="Name"/>
|
||||
<h1><field name="name" default_focus="1" placeholder="Name"/></h1>
|
||||
<label for="name" class="oe_edit_only" string="Course Title"/>
|
||||
<h1><field name="name" default_focus="1" placeholder="Course Title"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="enroll" widget="radio" options="{'horizontal': true}"/>
|
||||
<field name="tag_ids" widget="many2many_tags" placeholder="Tags"/>
|
||||
<field name="user_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Content">
|
||||
<field name="slide_ids" colspan="4" nolabel="1" widget="slide_category_one2many" context="{'default_channel_id': active_id}">
|
||||
<tree decoration-bf="is_category" editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="slide_type" attrs="{'invisible': [('slide_type', '=', 'category')]}"/>
|
||||
<field name="completion_time" attrs="{'invisible': [('slide_type', '=', 'category')]}"/>
|
||||
<field name="total_views" attrs="{'invisible': [('slide_type', '=', 'category')]}"/>
|
||||
<field name="is_category" invisible="1"/>
|
||||
|
||||
<control>
|
||||
<create name="add_slide_section" string="Add Section" context="{'default_is_category': True}"/>
|
||||
<create name="add_slide_lesson" string="Add Content"/>
|
||||
</control>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Description">
|
||||
<field name="description" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
<page string="Options">
|
||||
<group>
|
||||
<group>
|
||||
<field name="channel_type" widget="radio"/>
|
||||
<group string="Course">
|
||||
<field string="Type" name="channel_type" widget="radio"/>
|
||||
<field name="user_id"/>
|
||||
<field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
|
||||
<field name="promote_strategy" widget="radio"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="upload_group_ids" widget="many2many_tags"/>
|
||||
<group string="Access Rights">
|
||||
<field name="enroll" widget="radio" options="{'horizontal': true}"/>
|
||||
<field name="upload_group_ids" string="New Content" widget="many2many_tags"/>
|
||||
<field name="enroll_group_ids" widget="many2many_tags" groups="base.group_no_one"/>
|
||||
<field name="visibility" widget="radio" groups="base.group_no_one"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Communication">
|
||||
<field string="Allow Rating" name="allow_comment"/>
|
||||
<field name="publish_template_id" domain="[('model','=','slide.slide')]"/>
|
||||
<field name="share_template_id" domain="[('model','=','slide.slide')]"/>
|
||||
</group>
|
||||
<group string="Display">
|
||||
<field name="visibility" widget="radio"/>
|
||||
<field name="promote_strategy" widget="radio"/>
|
||||
</group>
|
||||
</group>
|
||||
<group colspan="4">
|
||||
<div attrs="{'invisible': [('enroll', '!=', 'invite')]}">
|
||||
<label for="enroll_msg"/>
|
||||
<field name="enroll_msg" colspan="4" nolabel="1"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Communication">
|
||||
<group>
|
||||
<field name="allow_comment"/>
|
||||
</group>
|
||||
</div>
|
||||
</page>
|
||||
<page string="Karma Rules" name="karma_rules">
|
||||
<group>
|
||||
<group string="Karma generation">
|
||||
<field name="karma_gen_slide_vote"/>
|
||||
<field name="karma_gen_channel_rank"/>
|
||||
<field name="karma_gen_channel_finish"/>
|
||||
</group>
|
||||
<group string="Karma based actions">
|
||||
<field name="karma_review"/>
|
||||
<field name="karma_slide_comment"/>
|
||||
<field name="karma_slide_vote"/>
|
||||
</group>
|
||||
<field name="karma_gen_channel_rank" string="Rank Course"/>
|
||||
<field name="karma_gen_channel_finish" string="Finish Course"/>
|
||||
<field name="karma_review" attrs="{'invisible': [('allow_comment', '!=', True)]}"/>
|
||||
<field name="karma_slide_comment" attrs="{'invisible': [('allow_comment', '!=', True)]}"/>
|
||||
<field name="karma_slide_vote" attrs="{'invisible': [('allow_comment', '!=', True)]}"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Statistics" groups="base.group_no_one">
|
||||
<page string="Statistics">
|
||||
<group>
|
||||
<group>
|
||||
<group class="col-4">
|
||||
<field name="nbr_presentation"/>
|
||||
<field name="nbr_document"/>
|
||||
<field name="nbr_video"/>
|
||||
<field name="nbr_infographic"/>
|
||||
<field name="nbr_quiz" string="Quizzes"/>
|
||||
<field name="nbr_webpage"/>
|
||||
<separator/>
|
||||
<field name="total_slides"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="total_views"/>
|
||||
<field name="total_votes"/>
|
||||
<field name="total_time"/>
|
||||
<field name="total_views" string="Visits"/>
|
||||
<field string="Rating" name="rating_avg" widget="field_float_rating"/>
|
||||
<label for="total_time" string="Watch Time"/>
|
||||
<div>
|
||||
<field name="total_time"/>
|
||||
<span class="ml-2">Hours</span>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
@@ -51,8 +51,8 @@
|
||||
attrs="{'readonly': [('slide_type', 'in', ('document', 'presentation', 'video'))], 'invisible': [('is_category', '=', True)]}"
|
||||
/>
|
||||
<div class="oe_title">
|
||||
<div class="oe_edit_only">
|
||||
<label for="name" string="Content Title"/>
|
||||
<div>
|
||||
<label for="name" string="Name"/>
|
||||
</div>
|
||||
<h1>
|
||||
<field name="name" default_focus="1" placeholder="Title"/>
|
||||
@@ -90,7 +90,7 @@
|
||||
<page string="External Links" name="external_links" >
|
||||
<group>
|
||||
<field name="link_ids" widget="one2many" nolabel="1">
|
||||
<tree editable="top">
|
||||
<tree editable="top">
|
||||
<field name="name"/>
|
||||
<field name="link" widget="url"/>
|
||||
</tree>
|
||||
|
||||
@@ -8,7 +8,19 @@ class Channel(models.Model):
|
||||
_inherit = 'slide.channel'
|
||||
|
||||
forum_id = fields.Many2one('forum.forum', 'Course Forum')
|
||||
forum_total_posts = fields.Integer('Number of active forum posts', related="forum_id.total_posts")
|
||||
|
||||
_sql_constraints = [
|
||||
('forum_uniq', 'unique (forum_id)', "Only one forum per slide channel!"),
|
||||
]
|
||||
|
||||
def action_redirect_to_forum(self):
|
||||
self.ensure_one()
|
||||
action = self.env.ref('website_forum.action_forum_post').read()[0]
|
||||
action['view_mode'] = 'tree'
|
||||
action['context'] = {
|
||||
'create': False
|
||||
}
|
||||
action['domain'] = [('forum_id', '=', self.forum_id.id)]
|
||||
|
||||
return action
|
||||
|
||||
@@ -5,8 +5,17 @@
|
||||
<field name="model">slide.channel</field>
|
||||
<field name="inherit_id" ref="website_slides.view_slide_channel_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='action_view_ratings']" position="after">
|
||||
<button name="action_redirect_to_forum"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
attrs="{'invisible': [('forum_id', '=', False)]}"
|
||||
icon="fa-comment">
|
||||
<field string="Forum Posts" name="forum_total_posts" widget="statinfo"/>
|
||||
</button>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='allow_comment']" position="after">
|
||||
<field name="forum_id"/>
|
||||
<field string="Forum" name="forum_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -8,5 +8,3 @@ class Channel(models.Model):
|
||||
_inherit = 'slide.channel'
|
||||
|
||||
nbr_certification = fields.Integer("Number of Certifications", compute='_compute_slides_statistics', store=True)
|
||||
|
||||
|
||||
|
||||
@@ -45,6 +45,18 @@ class Slide(models.Model):
|
||||
('check_certification_preview', "CHECK(slide_type != 'certification' OR is_preview = False)", "A slide of type certification cannot be previewed."),
|
||||
]
|
||||
|
||||
@api.onchange('survey_id')
|
||||
def _on_change_survey_id(self):
|
||||
if self.survey_id:
|
||||
self.slide_type = 'certification'
|
||||
|
||||
@api.model
|
||||
def create(self, values):
|
||||
rec = super(Slide, self).create(values)
|
||||
if rec.survey_id:
|
||||
rec.slide_type = 'certification'
|
||||
return rec
|
||||
|
||||
def _generate_certification_url(self):
|
||||
""" get a map of certification url for certification slide from `self`. The url will come from the survey user input:
|
||||
1/ existing and not done user_input for member of the course
|
||||
|
||||
@@ -5,8 +5,18 @@
|
||||
<field name="model">slide.channel</field>
|
||||
<field name="inherit_id" ref="website_slides.view_slide_channel_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='total_slides']" position="before">
|
||||
<field name="nbr_certification"/>
|
||||
<xpath expr="//span[@name='stat_label']" position="replace">
|
||||
<span class="o_stat_text" attrs="{'invisible': [('nbr_certification', '>', 0)]}">Finished</span>
|
||||
<span class="o_stat_text" attrs="{'invisible': [('nbr_certification', '=', 0)]}">Certified</span>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='nbr_webpage']" position="after">
|
||||
<field string="Certifications" name="nbr_certification"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='slide_type']" position="after">
|
||||
<field name="survey_id"/>
|
||||
</xpath>
|
||||
<xpath expr="//create[@name='add_slide_lesson']" position="after">
|
||||
<create name="add_slide_certificate" string="Add Certification" groups="survey.group_survey_user" context="{'default_slide_type': 'certification'}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
Reference in New Issue
Block a user