[MERGE] website_slides: allow to contact responsible and request access to course
PURPOSE Before this merge, a user could not contact a course responsible to ask him to join. With this merge he is be able to do it. SPECIFICATIONS: ACTIVITY Add activity_search method in mail.activity.mixin to ease searching on automated activities (code refactoring mainly). Add possibility to choose a source / request partner on an activity. It allows to know if an activity is linked to a customer or a requesting partner. Com SPECIFICATIONS: SLIDES Change the message 'Contact website administrator' to 'Contact Responsible'. Add the name of the course responsible in the course description panel. When clicking on its name it opens a modal to request access to the course. It creates an activity linked to the customer and assigned to the responsible. When hovering the 'You're enrolled' button the fa-check icon changes to a fa-times icon to show that you can unsubscribe when clicking. LINKS Task ID : 2127364 Community PR odoo/odoo#40708 Upgrade PR odoo/upgrade#1101 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import logging
|
||||
import pytz
|
||||
|
||||
from odoo import api, exceptions, fields, models, _
|
||||
from odoo.osv import expression
|
||||
|
||||
from odoo.tools.misc import clean_context
|
||||
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
|
||||
@@ -162,6 +163,7 @@ class MailActivity(models.Model):
|
||||
'res.users', 'Assigned to',
|
||||
default=lambda self: self.env.user,
|
||||
index=True, required=True)
|
||||
request_partner_id = fields.Many2one('res.partner', string='Requesting Partner')
|
||||
state = fields.Selection([
|
||||
('overdue', 'Overdue'),
|
||||
('today', 'Today'),
|
||||
@@ -781,6 +783,38 @@ class MailActivityMixin(models.AbstractModel):
|
||||
)
|
||||
return True
|
||||
|
||||
def activity_search(self, act_type_xmlids='', user_id=None, additional_domain=None):
|
||||
""" Search automated activities on current record set, given a list of activity
|
||||
types xml IDs. It is useful when dealing with specific types involved in automatic
|
||||
activities management.
|
||||
|
||||
:param act_type_xmlids: list of activity types xml IDs
|
||||
:param user_id: if set, restrict to activities of that user_id;
|
||||
:param additional_domain: if set, filter on that domain;
|
||||
"""
|
||||
if self.env.context.get('mail_activity_automation_skip'):
|
||||
return False
|
||||
|
||||
Data = self.env['ir.model.data'].sudo()
|
||||
activity_types_ids = [type_id for type_id in (Data.xmlid_to_res_id(xmlid, raise_if_not_found=False) for xmlid in act_type_xmlids) if type_id]
|
||||
if not any(activity_types_ids):
|
||||
return False
|
||||
|
||||
domain = [
|
||||
'&', '&', '&',
|
||||
('res_model', '=', self._name),
|
||||
('res_id', 'in', self.ids),
|
||||
('automated', '=', True),
|
||||
('activity_type_id', 'in', activity_types_ids)
|
||||
]
|
||||
|
||||
if user_id:
|
||||
domain = expression.AND([domain, [('user_id', '=', user_id)]])
|
||||
if additional_domain:
|
||||
domain = expression.AND([domain, additional_domain])
|
||||
|
||||
return self.env['mail.activity'].search(domain)
|
||||
|
||||
def activity_schedule(self, act_type_xmlid='', date_deadline=None, summary='', note='', **act_values):
|
||||
""" Schedule an activity on each record of the current record set.
|
||||
This method allow to provide as parameter act_type_xmlid. This is an
|
||||
@@ -864,16 +898,7 @@ class MailActivityMixin(models.AbstractModel):
|
||||
activity_types_ids = [act_type_id for act_type_id in activity_types_ids if act_type_id]
|
||||
if not any(activity_types_ids):
|
||||
return False
|
||||
domain = [
|
||||
'&', '&', '&',
|
||||
('res_model', '=', self._name),
|
||||
('res_id', 'in', self.ids),
|
||||
('automated', '=', True),
|
||||
('activity_type_id', 'in', activity_types_ids)
|
||||
]
|
||||
if user_id:
|
||||
domain = ['&'] + domain + [('user_id', '=', user_id)]
|
||||
activities = self.env['mail.activity'].search(domain)
|
||||
activities = self.activity_search(act_type_xmlids, user_id=user_id)
|
||||
if activities:
|
||||
write_vals = {}
|
||||
if date_deadline:
|
||||
@@ -894,16 +919,7 @@ class MailActivityMixin(models.AbstractModel):
|
||||
activity_types_ids = [act_type_id for act_type_id in activity_types_ids if act_type_id]
|
||||
if not any(activity_types_ids):
|
||||
return False
|
||||
domain = [
|
||||
'&', '&', '&',
|
||||
('res_model', '=', self._name),
|
||||
('res_id', 'in', self.ids),
|
||||
('automated', '=', True),
|
||||
('activity_type_id', 'in', activity_types_ids)
|
||||
]
|
||||
if user_id:
|
||||
domain = ['&'] + domain + [('user_id', '=', user_id)]
|
||||
activities = self.env['mail.activity'].search(domain)
|
||||
activities = self.activity_search(act_type_xmlids, user_id=user_id)
|
||||
if activities:
|
||||
activities.action_feedback(feedback=feedback)
|
||||
return True
|
||||
@@ -919,14 +935,5 @@ class MailActivityMixin(models.AbstractModel):
|
||||
activity_types_ids = [act_type_id for act_type_id in activity_types_ids if act_type_id]
|
||||
if not any(activity_types_ids):
|
||||
return False
|
||||
domain = [
|
||||
'&', '&', '&',
|
||||
('res_model', '=', self._name),
|
||||
('res_id', 'in', self.ids),
|
||||
('automated', '=', True),
|
||||
('activity_type_id', 'in', activity_types_ids)
|
||||
]
|
||||
if user_id:
|
||||
domain = ['&'] + domain + [('user_id', '=', user_id)]
|
||||
self.env['mail.activity'].search(domain).unlink()
|
||||
self.activity_search(act_type_xmlids, user_id=user_id).unlink()
|
||||
return True
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-extend="slides.course.unsubscribe.modal.leave">
|
||||
<t t-jquery="p" t-operation="after">
|
||||
<t t-jquery="p:last" t-operation="after">
|
||||
<t t-if="widget.enroll === 'payment'">
|
||||
<p class="alert alert-warning">
|
||||
<i class="fa fa-exclamation-triangle fa-3x float-left mr-3"></i>
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="mail_activity_data_access_request" model="mail.activity.type">
|
||||
<field name="name">Access Request</field>
|
||||
<field name="icon">fa-check-circle</field>
|
||||
<field name="sequence">50</field>
|
||||
<field name="res_model_id" ref="website_slides.model_slide_channel"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -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'
|
||||
@@ -85,6 +86,9 @@ class Channel(models.Model):
|
||||
def _default_access_token(self):
|
||||
return str(uuid.uuid4())
|
||||
|
||||
def _get_default_enroll_msg(self):
|
||||
return _('Contact Responsible')
|
||||
|
||||
# description
|
||||
name = fields.Char('Name', translate=True, required=True)
|
||||
active = fields.Boolean(default=True, tracking=100)
|
||||
@@ -145,7 +149,7 @@ class Channel(models.Model):
|
||||
help='Condition to enroll: everyone, on invite, on payment (sale bridge).')
|
||||
enroll_msg = fields.Html(
|
||||
'Enroll Message', help="Message explaining the enroll process",
|
||||
default=False, translate=tools.html_translate, sanitize_attributes=False)
|
||||
default=_get_default_enroll_msg, translate=tools.html_translate, sanitize_attributes=False)
|
||||
enroll_group_ids = fields.Many2many('res.groups', string='Auto Enroll Groups', help="Members of those groups are automatically added as members of the channel.")
|
||||
visibility = fields.Selection([
|
||||
('public', 'Public'), ('members', 'Members Only')],
|
||||
@@ -156,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(
|
||||
@@ -197,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
|
||||
@@ -391,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()
|
||||
|
||||
@@ -587,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):
|
||||
@@ -596,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=_('<b>%s</b> is requesting access to this course.') % partner.name,
|
||||
user_id=channel.user_id.id,
|
||||
request_partner_id=partner.id
|
||||
)
|
||||
return activities
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Data / Misc
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -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);
|
||||
|
||||
});
|
||||
@@ -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('<div class="alert alert-danger" role="alert"><strong>' + result.error + '</strong></div>');
|
||||
} else if (result.done) {
|
||||
self.$element.replaceWith('<div class="alert alert-success" role="alert"><strong>' + _t('Request sent !') + '</strong></div>');
|
||||
} else {
|
||||
self.$element.replaceWith('<div class="alert alert-danger" role="alert"><strong>' + _t('Unknown error, try again.') + '</strong></div>');
|
||||
}
|
||||
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
|
||||
};
|
||||
|
||||
});
|
||||
@@ -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
|
||||
};
|
||||
},
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-extend="mail.activity_items">
|
||||
<t t-jquery=".o_thread_message .o_thread_message_core .o_thread_message_tools" t-operation="replace">
|
||||
<t t-if="activity.activity_type_id[1] == 'Access Request'">
|
||||
<t t-if="activity.user_id[0] === uid">
|
||||
<a role="button" class="btn btn-link btn-success text-muted text-success o_activity_link o_activity_action_grant_access" t-att-data-partner-id="activity.request_partner_id[0]">
|
||||
<i class="fa fa-check"/> Grant Access
|
||||
</a>
|
||||
<a role="button" class="btn btn-link btn-danger text-muted text-danger o_activity_link o_activity_action_refuse_access" t-att-data-partner-id="activity.request_partner_id[0]">
|
||||
<i class="fa fa-times"/> Refuse Access
|
||||
</a>
|
||||
</t>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="o_thread_message_tools btn-group">
|
||||
<t t-call="mail.activity_thread_message_tools"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -15,4 +15,10 @@
|
||||
</a>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="slide.course.join.request">
|
||||
<div>
|
||||
<p>Do you want to request access to this course ?</p>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
@@ -47,8 +47,23 @@
|
||||
<div t-if="!widget.isMember">
|
||||
<div class="o_wslides_join_course alert alert-info d-flex align-items-center justify-content-between">
|
||||
<div t-if="widget.channel.channelEnroll == 'invite'">
|
||||
<b class="h5 mb-0">
|
||||
<b>This course is private. <a href="/contactus" class="font-weight-bold">Contact the website administrator</a> to enroll.</b>
|
||||
<b>This course is private.
|
||||
<span t-if="widget.publicUser">
|
||||
Please
|
||||
<a t-att-href="'/web/login?redirect=' + widget.redirectURL" class="font-weight-bold">
|
||||
sign in
|
||||
</a>
|
||||
to enroll.
|
||||
</span>
|
||||
<a t-else="" href="#" class="font-weight-bold o_wslides_js_channel_enroll"
|
||||
t-att-data-channel-id="widget.channel.channelId">
|
||||
<span t-if="widget.channel.channelRequestedAccess" class="text-success">
|
||||
Responsible already contacted.
|
||||
</span>
|
||||
<span t-else="">
|
||||
Contact the responsible to enroll.
|
||||
</span>
|
||||
</a>
|
||||
</b>
|
||||
<span class="my-0 h4">
|
||||
<span title="Succeed and gain karma" aria-label="Succeed and gain karma" class="badge badge-pill badge-warning text-white font-weight-bold ml-3 px-2 py-1">
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<link rel="stylesheet" type="text/scss" href="/website_slides/static/src/scss/slide_views.scss"/>
|
||||
<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"/>
|
||||
<script type="text/javascript" src="/website_slides/static/src/js/activity.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -27,6 +28,7 @@
|
||||
<script type="text/javascript" src="/website_slides/static/src/js/slides_course_slides_list.js"/>
|
||||
<script type="text/javascript" src="/website_slides/static/src/js/slides_course_fullscreen_player.js"/>
|
||||
<script type="text/javascript" src="/website_slides/static/src/js/slides_course_join.js"/>
|
||||
<script type="text/javascript" src="/website_slides/static/src/js/slides_course_enroll_email.js"/>
|
||||
<script type="text/javascript" src="/website_slides/static/src/js/slides_course_quiz.js"/>
|
||||
<script type="text/javascript" src="/website_slides/static/src/js/slides_course_quiz_question_form.js"/>
|
||||
<script type="text/javascript" src="/website_slides/static/src/js/slides_course_quiz_finish.js"/>
|
||||
|
||||
@@ -148,6 +148,7 @@
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -294,10 +294,25 @@
|
||||
<t t-else="">Join Course</t>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<div t-if="not channel.is_member and channel.enroll == 'invite'" class="text-center">
|
||||
<div class="alert my-0 bg-100 p-2">Private Course</div>
|
||||
<div t-if="channel.enroll_msg" t-field="channel.enroll_msg"/>
|
||||
<small t-if="not channel.enroll_msg">Contact website administrator.</small>
|
||||
<div t-attf-class="alert my-0 bg-100 p-2 #{'o_wslides_js_channel_enroll' if not is_public_user else ''}"
|
||||
t-att-data-channel-id="channel.id">
|
||||
Private Course
|
||||
<div t-if="is_public_user">
|
||||
<small>
|
||||
Please <a t-att-href="'/web/login?redirect=/slides/%s' % (slug(channel))">sign in</a> to contact responsible.
|
||||
</small>
|
||||
</div>
|
||||
<div t-elif="channel.has_requested_access">
|
||||
<small class="text-success">
|
||||
Request already sent
|
||||
</small>
|
||||
</div>
|
||||
<div t-else="" class="o_wslides_enroll_msg">
|
||||
<small t-field="channel.enroll_msg"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<t t-if="channel.is_member">
|
||||
<button class="d-flex align-items-center alert my-0 px-2 px-xl-3 bg-100 w-100 o_wslides_js_channel_unsubscribe"
|
||||
@@ -310,6 +325,7 @@
|
||||
</t>
|
||||
<h6 class="d-flex flex-grow-1 my-0">You're enrolled</h6>
|
||||
<i class="fa fa-check"/>
|
||||
<i class="fa fa-times"/>
|
||||
</button>
|
||||
<div class="d-flex align-items-center pt-3">
|
||||
<t t-if="channel.completed">
|
||||
@@ -331,6 +347,10 @@
|
||||
|
||||
<div id="o_wslides_sidebar_collapse" class="collapse d-md-block">
|
||||
<table class="table table-sm mt-3">
|
||||
<tr>
|
||||
<th class="border-top-0">Responsible</th>
|
||||
<td class="border-top-0"><span t-field="channel.user_id"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="border-top-0">Last Update</th>
|
||||
<td class="border-top-0"><t t-esc="channel.slide_last_update" t-options="{'widget': 'date'}"/></td>
|
||||
|
||||
@@ -452,6 +452,7 @@
|
||||
t-att-data-next-slide-url="'/slides/slide/%s' % (slug(next_slide)) if next_slide else None"
|
||||
t-att-data-channel-id="slide.channel_id.id"
|
||||
t-att-data-channel-enroll="slide.channel_id.enroll"
|
||||
t-att-data-channel-requested-access="slide.channel_id.has_requested_access"
|
||||
t-att-data-channel-can-upload="slide.channel_id.can_upload"
|
||||
t-att-data-signup-allowed="signup_allowed"
|
||||
t-att-data-session-answers="session_answers">
|
||||
|
||||
Reference in New Issue
Block a user