diff --git a/addons/website_sale_slides/static/src/xml/website_slides_unsubscribe.xml b/addons/website_sale_slides/static/src/xml/website_slides_unsubscribe.xml
index d09d78dce08..1cadfbbf67d 100644
--- a/addons/website_sale_slides/static/src/xml/website_slides_unsubscribe.xml
+++ b/addons/website_sale_slides/static/src/xml/website_slides_unsubscribe.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/addons/website_slides/__manifest__.py b/addons/website_slides/__manifest__.py
index 85a3b2d160c..fdb2c9e482c 100644
--- a/addons/website_slides/__manifest__.py
+++ b/addons/website_slides/__manifest__.py
@@ -48,6 +48,7 @@ Featuring
'wizard/slide_channel_invite_views.xml',
'data/gamification_data.xml',
'data/mail_data.xml',
+ 'data/mail_activity_data.xml',
'data/slide_data.xml',
'data/website_data.xml',
],
@@ -58,6 +59,9 @@ Featuring
'data/slide_slide_demo.xml',
'data/slide_user_demo.xml',
],
+ 'qweb': [
+ 'static/src/xml/activity.xml',
+ ],
'installable': True,
'application': True,
}
diff --git a/addons/website_slides/data/mail_activity_data.xml b/addons/website_slides/data/mail_activity_data.xml
new file mode 100644
index 00000000000..df1563f489e
--- /dev/null
+++ b/addons/website_slides/data/mail_activity_data.xml
@@ -0,0 +1,11 @@
+
+
+
+
+ Access Request
+ fa-check-circle
+ 50
+
+
+
+
diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py
index 7a950e7d1b6..e410091b109 100644
--- a/addons/website_slides/models/slide_channel.py
+++ b/addons/website_slides/models/slide_channel.py
@@ -78,6 +78,7 @@ class Channel(models.Model):
_description = 'Course'
_inherit = [
'mail.thread', 'rating.mixin',
+ 'mail.activity.mixin',
'image.mixin',
'website.seo.metadata', 'website.published.multi.mixin']
_order = 'sequence, id'
@@ -159,6 +160,7 @@ class Channel(models.Model):
string='Members', help="All members of the channel.", context={'active_test': False}, copy=False, depends=['channel_partner_ids'])
members_count = fields.Integer('Attendees count', compute='_compute_members_count')
members_done_count = fields.Integer('Attendees Done Count', compute='_compute_members_done_count')
+ has_requested_access = fields.Boolean(string='Access Requested', compute='_compute_has_requested_access', compute_sudo=False)
is_member = fields.Boolean(string='Is Member', compute='_compute_is_member', compute_sudo=False)
channel_partner_ids = fields.One2many('slide.channel.partner', 'channel_id', string='Members Information', groups='website_slides.group_website_slides_officer', depends=['partner_ids'])
upload_group_ids = fields.Many2many(
@@ -200,6 +202,17 @@ class Channel(models.Model):
for channel in self:
channel.members_done_count = data.get(channel.id, 0)
+ @api.depends('activity_ids.request_partner_id')
+ @api.depends_context('uid')
+ @api.model
+ def _compute_has_requested_access(self):
+ requested_cids = self.sudo().activity_search(
+ ['website_slides.mail_activity_data_access_request'],
+ additional_domain=[('request_partner_id', '=', self.env.user.partner_id.id)]
+ ).mapped('res_id')
+ for channel in self:
+ channel.has_requested_access = channel.id in requested_cids
+
@api.depends('channel_partner_ids.partner_id')
@api.depends_context('uid')
@api.model
@@ -394,6 +407,7 @@ class Channel(models.Model):
if vals.get('user_id'):
self._action_add_members(self.env['res.users'].sudo().browse(vals['user_id']).partner_id)
+ self.activity_reschedule(['website_slides.mail_activity_data_access_request'], new_user_id=vals.get('user_id'))
if 'enroll_group_ids' in vals:
self._add_groups_members()
@@ -590,8 +604,41 @@ class Channel(models.Model):
action['domain'] = [('res_id', 'in', self.ids)]
return action
+ def action_request_access(self):
+ """ Request access to the channel. Returns a dict with keys being either 'error'
+ (specific error raised) or 'done' (request done or not). """
+ if self.env.user.has_group('base.group_public'):
+ return {'error': _('You have to sign in before')}
+ if not self.is_published:
+ return {'error': _('Course not published yet')}
+ if self.is_member:
+ return {'error': _('Already member')}
+ if self.enroll == 'invite':
+ activities = self.sudo()._action_request_access(self.env.user.partner_id)
+ if activities:
+ return {'done': True}
+ return {'error': _('Already Requested')}
+ return {'done': False}
+
+ def action_grant_access(self, partner_id):
+ partner = self.env['res.partner'].browse(partner_id).exists()
+ if partner:
+ if self._action_add_members(partner):
+ self.activity_search(
+ ['website_slides.mail_activity_data_access_request'],
+ user_id=self.user_id.id, additional_domain=[('request_partner_id', '=', partner.id)]
+ ).action_feedback(feedback=_('Access Granted'))
+
+ def action_refuse_access(self, partner_id):
+ partner = self.env['res.partner'].browse(partner_id).exists()
+ if partner:
+ self.activity_search(
+ ['website_slides.mail_activity_data_access_request'],
+ user_id=self.user_id.id, additional_domain=[('request_partner_id', '=', partner.id)]
+ ).action_feedback(feedback=_('Access Refused'))
+
# ---------------------------------------------------------
- # Rating Mixin API
+ # Mailing Mixin API
# ---------------------------------------------------------
def _rating_domain(self):
@@ -599,6 +646,22 @@ class Channel(models.Model):
domain = super(Channel, self)._rating_domain()
return expression.AND([domain, [('is_internal', '=', False)]])
+ def _action_request_access(self, partner):
+ activities = self.env['mail.activity']
+ requested_cids = self.sudo().activity_search(
+ ['website_slides.mail_activity_data_access_request'],
+ additional_domain=[('request_partner_id', '=', partner.id)]
+ ).mapped('res_id')
+ for channel in self:
+ if channel.id not in requested_cids:
+ activities += channel.activity_schedule(
+ 'website_slides.mail_activity_data_access_request',
+ note=_('%s is requesting access to this course.') % partner.name,
+ user_id=channel.user_id.id,
+ request_partner_id=partner.id
+ )
+ return activities
+
# ---------------------------------------------------------
# Data / Misc
# ---------------------------------------------------------
diff --git a/addons/website_slides/static/src/js/activity.js b/addons/website_slides/static/src/js/activity.js
new file mode 100644
index 00000000000..bc207acbd06
--- /dev/null
+++ b/addons/website_slides/static/src/js/activity.js
@@ -0,0 +1,47 @@
+odoo.define('website_slides.Activity', function (require) {
+"use strict";
+
+var field_registry = require('web.field_registry');
+
+require('mail.Activity');
+
+var KanbanActivity = field_registry.get('kanban_activity');
+var MailActivity = field_registry.get('mail_activity');
+
+function applyInclude(Activity) {
+ Activity.include({
+ events: _.extend({}, Activity.prototype.events, {
+ 'click .o_activity_action_grant_access': '_onGrantAccess',
+ 'click .o_activity_action_refuse_access': '_onRefuseAccess',
+ }),
+
+ _onGrantAccess: function (event) {
+ var self = this;
+ var partnerId = $(event.currentTarget).data('partner-id');
+ this._rpc({
+ model: 'slide.channel',
+ method: 'action_grant_access',
+ args: [this.res_id, partnerId],
+ }).then(function (result) {
+ self.trigger_up('reload');
+ });
+ },
+
+ _onRefuseAccess: function (event) {
+ var self = this;
+ var partnerId = $(event.currentTarget).data('partner-id');
+ this._rpc({
+ model: 'slide.channel',
+ method: 'action_refuse_access',
+ args: [this.res_id, partnerId],
+ }).then(function () {
+ self.trigger_up('reload');
+ });
+ },
+ });
+}
+
+applyInclude(KanbanActivity);
+applyInclude(MailActivity);
+
+});
\ No newline at end of file
diff --git a/addons/website_slides/static/src/js/slides_course_enroll_email.js b/addons/website_slides/static/src/js/slides_course_enroll_email.js
new file mode 100644
index 00000000000..a9f5f799a79
--- /dev/null
+++ b/addons/website_slides/static/src/js/slides_course_enroll_email.js
@@ -0,0 +1,83 @@
+odoo.define('website_slides.course.enroll', function (require) {
+'use strict';
+
+var core = require('web.core');
+var Dialog = require('web.Dialog');
+var publicWidget = require('web.public.widget');
+var _t = core._t;
+
+var SlideEnrollDialog = Dialog.extend({
+ template: 'slide.course.join.request',
+
+ init: function (parent, options, modalOptions) {
+ modalOptions = _.defaults(modalOptions || {}, {
+ title: _t('Request Access.'),
+ size: 'medium',
+ buttons: [{
+ text: _t('Yes'),
+ classes: 'btn-primary',
+ click: this._onSendRequest.bind(this)
+ }, {
+ text: _t('Cancel'),
+ close: true
+ }]
+ });
+ this.$element = options.$element;
+ this.channelId = options.channelId;
+ this._super(parent, modalOptions);
+ },
+
+ _onSendRequest: function () {
+ var self = this;
+ this._rpc({
+ model: 'slide.channel',
+ method: 'action_request_access',
+ args: [self.channelId]
+ }).then(function (result) {
+ if (result.error) {
+ self.$element.replaceWith('
' + result.error + '
');
+ } else if (result.done) {
+ self.$element.replaceWith('' + _t('Request sent !') + '
');
+ } else {
+ self.$element.replaceWith('' + _t('Unknown error, try again.') + '
');
+ }
+ self.close();
+ });
+ }
+
+});
+
+publicWidget.registry.websiteSlidesEnroll = publicWidget.Widget.extend({
+ selector: '.o_wslides_js_channel_enroll',
+ xmlDependencies: ['/website_slides/static/src/xml/slide_course_join.xml'],
+ events: {
+ 'click': '_onSendRequestClick',
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ _openDialog: function (channelId) {
+ new SlideEnrollDialog(this, {
+ channelId: channelId,
+ $element: this.$el
+ }).open();
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ _onSendRequestClick: function (ev) {
+ ev.preventDefault();
+ this._openDialog($(ev.currentTarget).data('channelId'));
+ }
+});
+
+return {
+ slideEnrollDialog: SlideEnrollDialog,
+ websiteSlidesEnroll: publicWidget.registry.websiteSlidesEnroll
+};
+
+});
diff --git a/addons/website_slides/static/src/js/slides_course_quiz.js b/addons/website_slides/static/src/js/slides_course_quiz.js
index 50ac2a3cef1..d07e68f2344 100644
--- a/addons/website_slides/static/src/js/slides_course_quiz.js
+++ b/addons/website_slides/static/src/js/slides_course_quiz.js
@@ -10,6 +10,8 @@ odoo.define('website_slides.quiz', function (require) {
var QuestionFormWidget = require('website_slides.quiz.question.form');
var SlideQuizFinishModal = require('website_slides.quiz.finish');
+ var SlideEnrollDialog = require('website_slides.course.enroll').slideEnrollDialog;
+
var QWeb = core.qweb;
var _t = core._t;
@@ -26,7 +28,10 @@ odoo.define('website_slides.quiz', function (require) {
*/
var Quiz = publicWidget.Widget.extend({
template: 'slide.slide.quiz',
- xmlDependencies: ['/website_slides/static/src/xml/slide_quiz.xml'],
+ xmlDependencies: [
+ '/website_slides/static/src/xml/slide_quiz.xml',
+ '/website_slides/static/src/xml/slide_course_join.xml'
+ ],
events: {
"click .o_wslides_quiz_answer": '_onAnswerClick',
"click .o_wslides_js_lesson_quiz_submit": '_submitQuiz',
@@ -36,6 +41,7 @@ odoo.define('website_slides.quiz', function (require) {
'click .o_wslides_js_quiz_add': '_onCreateQuizClick',
'click .o_wslides_js_quiz_edit_question': '_onEditQuestionClick',
'click .o_wslides_js_quiz_delete_question': '_onDeleteQuestionClick',
+ 'click .o_wslides_js_channel_enroll': '_onSendRequestToResponsibleClick',
},
custom_events: {
@@ -531,6 +537,20 @@ odoo.define('website_slides.quiz', function (require) {
}).open();
},
+ /**
+ * Handler for the contact responsible link below a Quiz
+ * @param ev
+ * @private
+ */
+ _onSendRequestToResponsibleClick: function(ev) {
+ ev.preventDefault();
+ var channelId = $(ev.currentTarget).data('channelId');
+ new SlideEnrollDialog(this, {
+ channelId: channelId,
+ $element: $(ev.currentTarget).closest('.alert.alert-info')
+ }).open();
+ },
+
/**
* Displays the created Question at the correct place (after the last question or
* at the first place if there is no questions yet) It also displays the 'Add Question'
@@ -718,6 +738,7 @@ odoo.define('website_slides.quiz', function (require) {
return {
channelId: slideData.channelId,
channelEnroll: slideData.channelEnroll,
+ channelRequestedAccess: slideData.channelRequestedAccess || false,
signupAllowed: slideData.signupAllowed
};
},
diff --git a/addons/website_slides/static/src/scss/website_slides.scss b/addons/website_slides/static/src/scss/website_slides.scss
index 815ae9aa050..1f4175d1e99 100644
--- a/addons/website_slides/static/src/scss/website_slides.scss
+++ b/addons/website_slides/static/src/scss/website_slides.scss
@@ -237,6 +237,37 @@ $o-wslides-fs-side-width: 300px;
@include media-breakpoint-up(md) {
border-top-width: 0;
}
+
+ .o_wslides_js_channel_unsubscribe {
+ > .fa-times {
+ display: none;
+ }
+
+ &:hover {
+ > .fa-check {
+ display: none;
+ }
+
+ > .fa-times {
+ display: inline-block;
+ }
+ }
+ }
+
+ .o_wslides_js_channel_enroll {
+ cursor: pointer;
+
+ &:hover, &:hover .o_wslides_enroll_msg small {
+ font-weight: bold;
+ }
+ }
+
+ .o_wslides_enroll_msg {
+ p {
+ display: inline-block;
+ margin-bottom: 0px;
+ }
+ }
}
@mixin o-wslides-tabs($tab-active-color: $o-wslides-color-bg) {
diff --git a/addons/website_slides/static/src/xml/activity.xml b/addons/website_slides/static/src/xml/activity.xml
new file mode 100644
index 00000000000..20c683ab3c4
--- /dev/null
+++ b/addons/website_slides/static/src/xml/activity.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+ Grant Access
+
+
+ Refuse Access
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/website_slides/static/src/xml/slide_course_join.xml b/addons/website_slides/static/src/xml/slide_course_join.xml
index 5c890cd027d..0241915c190 100644
--- a/addons/website_slides/static/src/xml/slide_course_join.xml
+++ b/addons/website_slides/static/src/xml/slide_course_join.xml
@@ -15,4 +15,10 @@
+
+
+
+
Do you want to request access to this course ?
+
+
diff --git a/addons/website_slides/static/src/xml/slide_quiz.xml b/addons/website_slides/static/src/xml/slide_quiz.xml
index e64bc96604e..aa7bcc3f835 100644
--- a/addons/website_slides/static/src/xml/slide_quiz.xml
+++ b/addons/website_slides/static/src/xml/slide_quiz.xml
@@ -47,8 +47,23 @@
-
- This course is private. Contact the website administrator to enroll.
+ This course is private.
+
+ Please
+
+ sign in
+
+ to enroll.
+
+
+
+ Responsible already contacted.
+
+
+ Contact the responsible to enroll.
+
+
diff --git a/addons/website_slides/views/assets.xml b/addons/website_slides/views/assets.xml
index 3d896e8473e..78a54ef2f68 100644
--- a/addons/website_slides/views/assets.xml
+++ b/addons/website_slides/views/assets.xml
@@ -7,6 +7,7 @@
+
@@ -27,6 +28,7 @@
+
diff --git a/addons/website_slides/views/slide_channel_views.xml b/addons/website_slides/views/slide_channel_views.xml
index 656a8a15fe6..4a74978ef37 100644
--- a/addons/website_slides/views/slide_channel_views.xml
+++ b/addons/website_slides/views/slide_channel_views.xml
@@ -148,6 +148,7 @@
+
diff --git a/addons/website_slides/views/website_slides_templates_course.xml b/addons/website_slides/views/website_slides_templates_course.xml
index dd44da2ac59..8ddb367c90a 100644
--- a/addons/website_slides/views/website_slides_templates_course.xml
+++ b/addons/website_slides/views/website_slides_templates_course.xml
@@ -294,9 +294,25 @@
Join Course
+
-
Private Course
-
+
+ Private Course
+
+
+ Please sign in to contact responsible.
+
+
+
+
+ Request already sent
+
+
+
+
+
+
@@ -330,6 +347,10 @@