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 @@
@@ -615,6 +618,7 @@
+
diff --git a/addons/website_slides/views/website_slides_templates_homepage.xml b/addons/website_slides/views/website_slides_templates_homepage.xml
index e6abd4bed9c..8eaac02db8a 100644
--- a/addons/website_slides/views/website_slides_templates_homepage.xml
+++ b/addons/website_slides/views/website_slides_templates_homepage.xml
@@ -149,7 +149,9 @@
@@ -165,7 +167,9 @@
@@ -180,7 +184,9 @@
@@ -296,7 +302,9 @@
@@ -310,6 +318,7 @@