diff --git a/addons/website_slides/controllers/main.py b/addons/website_slides/controllers/main.py index aa52dff794e..c1a67026b70 100644 --- a/addons/website_slides/controllers/main.py +++ b/addons/website_slides/controllers/main.py @@ -240,7 +240,8 @@ class WebsiteSlides(WebsiteProfile): domain = request.website.website_domain() channels_all = request.env['slide.channel'].search(domain) if not request.env.user._is_public(): - channels_my = channels_all.filtered(lambda channel: channel.is_member).sorted('completion', reverse=True)[:3] + #If a course is completed, we don't want to see it in first position but in last + channels_my = channels_all.filtered(lambda channel: channel.is_member).sorted(lambda channel: 0 if channel.completed else channel.completion, reverse=True)[:3] else: channels_my = request.env['slide.channel'] channels_popular = channels_all.sorted('total_votes', reverse=True)[:3] diff --git a/addons/website_slides/data/slide_slide_demo.xml b/addons/website_slides/data/slide_slide_demo.xml index 233d6b7ddfb..6e00c21e9aa 100644 --- a/addons/website_slides/data/slide_slide_demo.xml +++ b/addons/website_slides/data/slide_slide_demo.xml @@ -11,6 +11,7 @@ presentation + 10 2.5 @@ -24,6 +25,7 @@ infographic + 5 1 @@ -37,6 +39,7 @@ infographic + 2 2 @@ -51,6 +54,7 @@ document + 0 1 @@ -64,6 +68,7 @@ quiz + 0 1 @@ -129,6 +134,7 @@ infographic + 5 0.5 @@ -142,6 +148,7 @@ infographic + 5 1.5 @@ -155,6 +162,7 @@ infographic + 10 1 @@ -180,6 +188,7 @@ video + 0 1 @@ -228,6 +237,7 @@ + 5 1 @@ -285,6 +295,7 @@ presentation + 10 2.5 @@ -300,6 +311,7 @@ video + 0 1 @@ -334,6 +346,7 @@ presentation + 0 1 @@ -431,6 +444,7 @@ + 0 2 @@ -446,6 +460,7 @@ video + 0 1 @@ -461,6 +476,7 @@ presentation + 0 2 @@ -492,6 +508,7 @@ infographic + 10 12 @@ -507,6 +524,7 @@ video + 10 3 @@ -558,6 +576,7 @@ presentation + 10 2.5 @@ -583,6 +602,7 @@ video + 0 3 @@ -597,6 +617,7 @@ video + 5 3 @@ -609,6 +630,7 @@ quiz + 0 0.5 diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index 0917d2b9629..27399364992 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -3,6 +3,8 @@ import uuid +from dateutil.relativedelta import relativedelta + from odoo import api, fields, models, tools, _ from odoo.addons.http_routing.models.ir_http import slug from odoo.exceptions import UserError, AccessError @@ -161,6 +163,7 @@ class Channel(models.Model): completed = fields.Boolean('Done', compute='_compute_user_statistics', compute_sudo=False) completion = fields.Integer('Completion', compute='_compute_user_statistics', compute_sudo=False) can_upload = fields.Boolean('Can Upload', compute='_compute_can_upload', compute_sudo=False) + partner_has_new_content = fields.Boolean(compute='_compute_partner_has_new_content', compute_sudo=False) # karma generation karma_gen_slide_vote = fields.Integer(string='Lesson voted', default=1) karma_gen_channel_rank = fields.Integer(string='Course ranked', default=5) @@ -297,6 +300,25 @@ class Channel(models.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.depends('slide_partner_ids') + @api.depends_context('uid') + def _compute_partner_has_new_content(self): + new_published_slides = self.env['slide.slide'].sudo().search([ + ('is_published', '=', True), + ('date_published', '>', fields.Datetime.now() - relativedelta(days=7)), + ('channel_id', 'in', self.ids), + ('is_category', '=', False) + ]) + slide_partner_completed = self.env['slide.slide.partner'].sudo().search([ + ('channel_id', 'in', self.ids), + ('partner_id', '=', self.env.user.partner_id.id), + ('slide_id', 'in', new_published_slides.ids), + ('completed', '=', True) + ]).mapped('slide_id') + for channel in self: + new_slides = new_published_slides.filtered(lambda slide: slide.channel_id == channel) + channel.partner_has_new_content = any(slide not in slide_partner_completed for slide in new_slides) + @api.depends('name', 'website_id.domain') def _compute_website_url(self): super(Channel, self)._compute_website_url() diff --git a/addons/website_slides/models/slide_slide.py b/addons/website_slides/models/slide_slide.py index 955b24a4a4b..d9fac8c5264 100644 --- a/addons/website_slides/models/slide_slide.py +++ b/addons/website_slides/models/slide_slide.py @@ -8,6 +8,7 @@ import re import requests import PyPDF2 +from dateutil.relativedelta import relativedelta from PIL import Image from werkzeug import urls @@ -123,6 +124,7 @@ class Slide(models.Model): channel_id = fields.Many2one('slide.channel', string="Course", required=True) tag_ids = fields.Many2many('slide.tag', 'rel_slide_tag', 'slide_id', 'tag_id', string='Tags') is_preview = fields.Boolean('Allow 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.") + is_new_slide = fields.Boolean('Is New Slide', compute='_compute_is_new_slide') completion_time = fields.Float('Duration', digits=(10, 4), help="The estimated completion time for this slide") # Categories is_category = fields.Boolean('Is a category', default=False) @@ -189,6 +191,11 @@ class Slide(models.Model): ('exclusion_html_content_and_url', "CHECK(html_content IS NULL OR url IS NULL)", "A slide is either filled with a document url or HTML content. Not both.") ] + @api.depends('date_published', 'is_published') + def _compute_is_new_slide(self): + for slide in self: + slide.is_new_slide = slide.date_published > fields.Datetime.now() - relativedelta(days=7) if slide.is_published else False + @api.depends('channel_id.slide_ids.is_category', 'channel_id.slide_ids.sequence') def _compute_category_id(self): """ Will take all the slides of the channel for which the index is higher diff --git a/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js b/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js index 604170b646a..1a2d217a727 100644 --- a/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js +++ b/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js @@ -19,9 +19,9 @@ odoo.define('website_slides.slide.preview', function (require) { }).then(function (isPreview) { if (isPreview) { $slideTarget.removeClass('badge-light badge-hide border'); - $slideTarget.addClass('badge-success'); + $slideTarget.addClass('badge-success py-1'); } else { - $slideTarget.removeClass('badge-success'); + $slideTarget.removeClass('badge-success py-1'); $slideTarget.addClass('badge-light badge-hide border'); } }); diff --git a/addons/website_slides/static/src/scss/website_slides.scss b/addons/website_slides/static/src/scss/website_slides.scss index 8e539e62787..cd02d6eaedd 100644 --- a/addons/website_slides/static/src/scss/website_slides.scss +++ b/addons/website_slides/static/src/scss/website_slides.scss @@ -29,6 +29,35 @@ $o-wslides-fs-side-width: 300px; } } +.o_wslides_arrow { + position: absolute; + height: 24px; + margin-left: -5px; + margin-top: 10px; + line-height: 1.8em; + padding-left: 8px; + padding-right: 5px; + background: #17A2B8; + color: white; + box-shadow: 0px 0px 3px gray; + z-index: 1; + + text-decoration: none; + + &:after { + // the triangle + content: ""; + position: absolute; + height: 0px; + width: 0px; + right: 0; + margin-right: -15px; + border-left: 15px solid #17A2B8; + border-bottom: 12px solid transparent; + border-top: 12px solid transparent; + } +} + .o_wslides_body { background-color: $o-wslides-color-bg; diff --git a/addons/website_slides/tests/test_statistics.py b/addons/website_slides/tests/test_statistics.py index a57f76d2f72..3d94a8ed596 100644 --- a/addons/website_slides/tests/test_statistics.py +++ b/addons/website_slides/tests/test_statistics.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import base64 import math +from dateutil.relativedelta import relativedelta + +from odoo import fields from odoo.addons.website_slides.tests import common -from odoo.exceptions import AccessError, UserError +from odoo.exceptions import UserError from odoo.tests import tagged from odoo.tests.common import users from odoo.tools import mute_logger, float_compare @@ -14,6 +16,26 @@ from odoo.tools import mute_logger, float_compare @tagged('functional') class TestChannelStatistics(common.SlidesCase): + @mute_logger('odoo.models') + def test_channel_new_content(self): + (self.slide | self.slide_2).write({'date_published': fields.Datetime.now() + relativedelta(days=-6)}) + self.slide_3.write({'date_published': fields.Datetime.now() + relativedelta(days=-8)}) + self.assertTrue(all(slide.is_new_slide for slide in (self.slide | self.slide_2))) + self.assertFalse(self.slide_3.is_new_slide) + + channel_aspublisher = self.channel.with_user(self.user_publisher) + self.assertTrue(channel_aspublisher.partner_has_new_content) + (self.slide | self.slide_2).with_user(self.user_publisher).action_set_completed() + self.assertFalse(channel_aspublisher.partner_has_new_content) + + channel_aspublisher._action_add_members(self.user_portal.partner_id) + channel_asportal = self.channel.with_user(self.user_portal) + self.assertTrue(channel_asportal.partner_has_new_content) + + (self.slide | self.slide_2).write({'date_published': fields.Datetime.now() + relativedelta(days=-8)}) + channel_asportal.invalidate_cache(['partner_has_new_content']) + self.assertFalse(channel_asportal.partner_has_new_content) + @mute_logger('odoo.models') def test_channel_statistics(self): channel_publisher = self.channel.with_user(self.user_publisher) diff --git a/addons/website_slides/views/slide_channel_views.xml b/addons/website_slides/views/slide_channel_views.xml index 2d5e08adcdd..6b7a2a53998 100644 --- a/addons/website_slides/views/slide_channel_views.xml +++ b/addons/website_slides/views/slide_channel_views.xml @@ -100,7 +100,8 @@ - +
diff --git a/addons/website_slides/views/website_slides_templates_course.xml b/addons/website_slides/views/website_slides_templates_course.xml index bc40fd88779..38d6c513f40 100644 --- a/addons/website_slides/views/website_slides_templates_course.xml +++ b/addons/website_slides/views/website_slides_templates_course.xml @@ -420,15 +420,18 @@
+ + Free preview + + Free preview + + New + xp Unpublished - - Free preview - - Free preview
@@ -615,6 +618,7 @@ + + + +