diff --git a/addons/mass_mailing_slides/views/slide_channel_views.xml b/addons/mass_mailing_slides/views/slide_channel_views.xml index ddc7c38af22..9b7f35b202c 100644 --- a/addons/mass_mailing_slides/views/slide_channel_views.xml +++ b/addons/mass_mailing_slides/views/slide_channel_views.xml @@ -5,7 +5,7 @@ slide.channel - + diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index 1c8f7b43485..626edb66c31 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -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') diff --git a/addons/website_slides/models/slide_slide.py b/addons/website_slides/models/slide_slide.py index e9ebe039886..4292665d870 100644 --- a/addons/website_slides/models/slide_slide.py +++ b/addons/website_slides/models/slide_slide.py @@ -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 diff --git a/addons/website_slides/static/src/js/rating_field_backend.js b/addons/website_slides/static/src/js/rating_field_backend.js new file mode 100644 index 00000000000..2c6d8ca38dd --- /dev/null +++ b/addons/website_slides/static/src/js/rating_field_backend.js @@ -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, +}; + +}); diff --git a/addons/website_slides/static/src/js/slide_category_one2many.js b/addons/website_slides/static/src/js/slide_category_one2many.js new file mode 100644 index 00000000000..ed270c840dc --- /dev/null +++ b/addons/website_slides/static/src/js/slide_category_one2many.js @@ -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); +}); \ No newline at end of file diff --git a/addons/website_slides/static/src/scss/category_backend.scss b/addons/website_slides/static/src/scss/category_backend.scss new file mode 100644 index 00000000000..99b017738ca --- /dev/null +++ b/addons/website_slides/static/src/scss/category_backend.scss @@ -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; +} \ No newline at end of file diff --git a/addons/website_slides/views/assets.xml b/addons/website_slides/views/assets.xml index 62129e58e6b..1ae8232624b 100644 --- a/addons/website_slides/views/assets.xml +++ b/addons/website_slides/views/assets.xml @@ -5,6 +5,9 @@ + +