[MERGE] webside_slides: improve onboarding, eLearning experience and management

PURPOSE

Improve eLearning onboarding and usability by performing a back2basics on
both backend and frontend.

SPECIFICATIONS

Change display of portal rating button to open the composer from btn-primary to
btn-link.

Overall improve profile related parts of eLearning user experience: karma rank
display, forum information display in profile.

Improve onboarding and user experience of eLearning homepage: remove
banners, improve copy writing and overall display.

Improve onboarding and user experience and course frontend: improve default
images, display of badges and action buttons, display sample data when having
a void course, improve copy writing.

Move level related tags from demo to data. Those are standard enough to
fill up tags data.

Improve course management and creation, notably
  * the create 'new course' dialog has been improved with examples and
    improved placeholders;
  * allow to create tags directly from course creation modal. Using the
    add tag button after having created the course is not the only way of
    managing tags anymore;
  * the create 'new content' dialog has some explanations on the right side;

Improve course backend usability, notably
  * improve layout of email send when invite. Do like a SO, add the join button
    in the notification email and not in the composer itself;
  * improve channel form view;

LINKS

Task ID-2241513
PR #55698
Upgrade PR odoo/upgrade#1590

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Odoo's Mergebot
2020-08-11 15:57:42 +02:00
committed by GitHub
40 changed files with 685 additions and 276 deletions
@@ -12,7 +12,9 @@
<t t-set="inline_mode" t-value="true"/>
</t>
</div>
<button t-if="widget.options['display_composer']" type="button" class="btn btn-sm btn-primary mx-3" data-toggle="modal" data-target="#ratingpopupcomposer">
<button t-if="widget.options['display_composer']" type="button"
t-att-class="'btn btn-sm mx-3 ' + widget.options['link_btn_classes'] or 'btn-primary'"
data-toggle="modal" data-target="#ratingpopupcomposer">
<t t-if="widget.options['display_composer']">
<t t-if="widget.options['default_message_id']">
Modify your review
@@ -47,7 +47,8 @@
t-att-data-default_rating_value="default_rating_value"
t-att-data-default_attachment_ids="default_attachment_ids"
t-att-data-force_submit_url="force_submit_url"
t-att-data-disable_composer="disable_composer">
t-att-data-disable_composer="disable_composer"
t-att-data-link_btn_classes="_link_btn_classes">
</div>
</template>
</odoo>
@@ -55,13 +55,13 @@
<xpath expr="//div[@id='profile_extra_info_tabcontent']" position="inside">
<t t-if="forum">
<div role="tabpanel" class="o_wforum_profile_tab tab-pane" id="questions">
<div class="mb32">
<div class="mb-4">
<h5 class="border-bottom pb-1">
Questions
<t t-call="website_forum.forum_filter_tag"/>
</h5>
<t t-if="questions">
<div class="mb4" t-foreach="questions" t-as="question">
<div class="mb-1" t-foreach="questions" t-as="question">
<t t-call="website_forum.display_post"/>
</div>
</t>
@@ -70,9 +70,9 @@
</t>
</div>
<t t-if="favourite">
<div class="mb4">
<div class="mb-4">
<h5 class="border-bottom pb-1">Favourite Questions</h5>
<div t-foreach="favourite" t-as="question">
<div class="mb-1" t-foreach="favourite" t-as="question">
<t t-call="website_forum.display_post">
<t t-set="hide_fav_icon" t-value="True"/>
</t>
@@ -80,23 +80,23 @@
</div>
</t>
<t t-if="followed">
<div class="mb4">
<div class="mb-4">
<h5 class="border-bottom pb-1">Followed Questions</h5>
<div t-foreach="followed" t-as="question" class="mb16">
<div class="mb-1" t-foreach="followed" t-as="question">
<t t-call="website_forum.display_post"/>
</div>
</div>
</t>
</div>
<div role="tabpanel" class="o_wforum_profile_tab tab-pane" id="answers">
<div class="mb32">
<div class="mb-4">
<h5 class="border-bottom pb-1">
Answers
<t t-call="website_forum.forum_filter_tag"/>
</h5>
<t t-if="answers">
<div t-foreach="answers" t-as="answer">
<div class="mb-1" t-foreach="answers" t-as="answer">
<t t-call="website_forum.display_post_answer"/>
</div>
</t>
@@ -106,13 +106,13 @@
</div>
</div>
<div role="tabpanel" class="o_wforum_profile_tab tab-pane" id="votes" t-if="uid == user.id">
<div class="mb32">
<div class="mb-4">
<h5 class="border-bottom pb-1">Votes</h5>
<t t-call="website_forum.user_votes"/>
</div>
</div>
<div role="tabpanel" class="o_wforum_profile_tab tab-pane" id="activity" t-if="uid == user.id">
<div class="mb32">
<div class="mb-4">
<h5 class="border-bottom pb-1">
Activities
<t t-call="website_forum.forum_filter_tag"/>
@@ -344,13 +344,11 @@
t-att-alt="(next_rank_id.name if next_rank_id else user.rank_id.name) + ' badge'"
t-att-style="'max-width: ' + (img_max_width if img_max_width else '50%;')"/>
<h4 class=" mb-0">
"
<span t-if="next_rank_id" t-field="next_rank_id.name"/>
<span t-else="" t-field="user.rank_id.name"/>
"
</h4>
<small>
<span class="font-weight-bold text-primary" t-field="user.karma"/>/
<span class="font-weight-bold text-primary" t-field="user.karma"/> /
<span t-if="next_rank_id" class="font-weight-bold" t-field="next_rank_id.karma_min"/>
<span t-else="" class="font-weight-bold" t-field="user.rank_id.karma_min"/>
xp
@@ -597,10 +595,9 @@
<t t-if="user['karma_gain']">
<span class="badge badge-pill badge-success d-inline">+ <t t-esc="user['karma_gain']"/> XP</span>
<span class="text-muted pl-2 pr-3">
<t t-if="group_by == 'week'">Weekly </t>
<t t-elif="group_by == 'month'">Monthly </t>
<t t-if="group_by == 'week'">this week</t>
<t t-elif="group_by == 'month'">this month</t>
<t t-else="">All time </t>
gain
</span>
</t>
</td>
@@ -7,7 +7,8 @@
<field name="arch" type="xml">
<xpath expr="//field[@name='enroll']" position="after">
<field name="product_id"
attrs="{'invisible': [('enroll', '!=', 'payment')], 'required': [('enroll', '=', 'payment')]}" />
attrs="{'invisible': [('enroll', '!=', 'payment')], 'required': [('enroll', '=', 'payment')]}"
context="{'default_type': 'service', 'default_invoice_policy': 'order', 'default_purchase_ok': False, 'default_sale_ok': True, 'default_website_published': True}"/>
</xpath>
<xpath expr="//button[@name='action_redirect_to_members']" position="after">
<button name="action_view_sales"
@@ -32,6 +32,20 @@
</xpath>
</template>
<!-- Tweak "preview" badge: display Free Preview if payment-based course -->
<template id="course_slides_list_slide"
name="Slide template for a training channel (Sale)"
inherit_id="website_slides.course_slides_list_slide">
<xpath expr="//a[@t-if='channel.can_upload']/span/span" position="replace">
<span t-if="channel.enroll == 'payment'">Free Preview</span>
<span t-else="">Preview</span>
</xpath>
<xpath expr="//t[@t-elif='slide.is_preview and not channel.is_member']/span/span" position="replace">
<span t-if="channel.enroll == 'payment'">Free Preview</span>
<span t-else="">Preview</span>
</xpath>
</template>
<!-- FULLSCREEN -->
<template id="slide_fullscreen" inherit_id="website_slides.slide_fullscreen">
<xpath expr="//div[hasclass('o_wslides_fs_main')]" position="attributes">
+1 -1
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
{
'name': 'eLearning',
'version': '2.1',
'version': '2.2',
'sequence': 145,
'summary': 'Manage and publish an eLearning platform',
'website': 'https://www.odoo.com/page/slides',
+48 -24
View File
@@ -259,6 +259,37 @@ class WebsiteSlides(WebsiteProfile):
session_slide_answer_quiz.pop(str(slide['id']), None)
request.session['slide_answer_quiz'] = json.dumps(session_slide_answer_quiz)
# TAG UTILITIES
# --------------------------------------------------
def _create_or_get_channel_tag(self, tag_id, group_id):
if not tag_id:
return request.env['slide.channel.tag']
# handle creation of new channel tag
if tag_id[0] == 0:
group_id = self._create_or_get_channel_tag_group(group_id)
if not group_id:
return {'error': _('Missing "Tag Group" for creating a new "Tag".')}
new_tag = request.env['slide.channel.tag'].create({
'name': tag_id[1]['name'],
'group_id': group_id,
})
return new_tag
return request.env['slide.channel.tag'].browse(tag_id[0])
def _create_or_get_channel_tag_group(self, group_id):
if not group_id:
return False
# handle creation of new channel tag group
if group_id[0] == 0:
tag_group = request.env['slide.channel.tag.group'].create({
'name': group_id[1]['name'],
})
group_id = tag_group.id
# use existing channel tag group
return group_id[0]
# --------------------------------------------------
# SLIDE.CHANNEL MAIN / SEARCH
# --------------------------------------------------
@@ -406,7 +437,8 @@ class WebsiteSlides(WebsiteProfile):
# sorting criterion
if channel.channel_type == 'documentation':
actual_sorting = sorting if sorting and sorting in request.env['slide.slide']._order_by_strategy else channel.promote_strategy
default_sorting = 'latest' if channel.promote_strategy in ['specific', 'none', False] else channel.promote_strategy
actual_sorting = sorting if sorting and sorting in request.env['slide.slide']._order_by_strategy else default_sorting
else:
actual_sorting = 'sequence'
order = request.env['slide.slide']._order_by_strategy[actual_sorting]
@@ -489,7 +521,10 @@ class WebsiteSlides(WebsiteProfile):
# of them but unreachable ones won't be clickable (+ slide controller will crash anyway)
# documentation mode may display less slides than content by category but overhead of
# computation is reasonable
values['slide_promoted'] = request.env['slide.slide'].sudo().search(domain, limit=1, order=order)
if channel.promote_strategy == 'specific':
values['slide_promoted'] = channel.sudo().promoted_slide_id
else:
values['slide_promoted'] = request.env['slide.slide'].sudo().search(domain, limit=1, order=order)
limit_category_data = False
if channel.channel_type == 'documentation':
@@ -613,29 +648,9 @@ class WebsiteSlides(WebsiteProfile):
if not can_upload or not can_publish:
return {'error': _('You cannot add tags to this course.')}
if tag_id:
# handle creation of new channel tag
if tag_id[0] == 0:
if group_id:
# handle creation of new channel tag group
if group_id[0] == 0:
tag_group = request.env['slide.channel.tag.group'].create({
'name': group_id[1]['name'],
})
group_id = tag_group.id
# use existing channel tag group
else:
group_id = group_id[0]
else:
return {'error': _('Missing "Tag Group" for creating a new "Tag".')}
tag = self._create_or_get_channel_tag(tag_id, group_id)
tag.write({'channel_ids': [(4, channel.id, 0)]})
request.env['slide.channel.tag'].create({'name': tag_id[1]['name'],
'channel_ids': [channel.id],
'group_id': group_id,
})
else:
# use existing channel tag
request.env['slide.channel.tag'].browse(tag_id[0]).write({'channel_ids': [(4, channel.id, 0)]})
return {'url': "/slides/%s" % (slug(channel))}
@http.route(['/slides/channel/subscribe'], type='json', auth='user', website=True)
@@ -807,6 +822,15 @@ class WebsiteSlides(WebsiteProfile):
result = slide._send_share_email(email, fullscreen)
return result
# --------------------------------------------------
# TAGS SECTION
# --------------------------------------------------
@http.route('/slide_channel_tag/add', type='json', auth='user', methods=['POST'], website=True)
def slide_channel_tag_create_or_get(self, tag_id, group_id):
tag = self._create_or_get_channel_tag(tag_id, group_id)
return {'tag_id': tag.id}
# --------------------------------------------------
# QUIZ SECTION
# --------------------------------------------------
+73 -12
View File
@@ -80,24 +80,85 @@
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px; font-size: 13px;">
<p style="margin: 0px; padding: 0px; font-size: 13px;">
Dear ${object.partner_id.name or 'participant'}<br/><br/>
Hello<br/><br/>
You have been invited to join a new course: ${object.channel_id.name}.
<div style="margin: 16px 0px 16px 0px;">
<a href="${(object.channel_id.website_url) | safe}"
style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size:13px;">
Click here to start the course.
</a>
</div>
Enjoy this exclusive content !
% if user.signature
<br />
${user.signature | safe}
% endif
</p>
</div>
</field>
<field name="lang">${object.partner_id.lang}</field>
<field name="auto_delete" eval="True"/>
</record>
<!-- QWeb templates -->
<!-- Note: mail_notification_channel_invite: record should be a slide.channel.partner record -->
<template id="mail_notification_channel_invite">
<table border="0" cellpadding="0" cellspacing="0" style="padding-top: 16px; background-color: #F1F1F1; font-family:Verdana, Arial,sans-serif; color: #454748; width: 100%; border-collapse:separate;"><tr><td align="center">
<table border="0" cellpadding="0" cellspacing="0" width="590" style="padding: 24px; background-color: white; color: #454748; border-collapse:separate;">
<tbody>
<!-- HEADER -->
<tr>
<td align="center" style="min-width: 590px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: white; padding: 0; border-collapse:separate;">
<tr><td valign="middle">
<span style="font-size: 10px;">Your <t t-esc="model_description or 'document'"/></span><br/>
<span style="font-size: 20px; font-weight: bold;">
<t t-esc="message.record_name and message.record_name.replace('/','-') or ''"/>
</span>
</td><td valign="middle" align="right">
<img t-att-src="'/logo.png?company=%s' % (company.id or 0)" style="padding: 0px; margin: 0px; height: 48px;" t-att-alt="'%s' % company.name"/>
</td></tr>
<tr><td colspan="2" style="text-align:center;">
<hr width="100%" style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin:4px 0px 32px 0px;"/>
</td></tr>
</table>
</td>
</tr>
<!-- CONTENT -->
<tr>
<td style="min-width: 590px;">
<t t-raw="message.body"/>
<div style="margin: 32px 0px 32px 0px; text-align: center;">
<a t-att-href="record.channel_id.website_url"
style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size:13px;">
Click here to start the course
</a>
</div>
<div style="margin: 0px; padding: 0px; font-size:13px;">
Enjoy this exclusive content !
</div>
<div>&amp;nbsp;</div>
<div t-if="signature" style="font-size: 13px;">
<div t-raw="signature"/>
</div>
</td>
</tr>
<!-- FOOTER -->
<tr>
<td align="center" style="min-width: 590px; padding: 0 8px 0 8px; font-size:11px;">
<hr width="100%" style="background-color:rgb(204,204,204);border:medium none;clear:both;display:block;font-size:0px;min-height:1px;line-height:0; margin: 16px 0px 4px 0px;"/>
<b t-esc="company.name"/><br/>
<div style="color: #999999;">
<t t-esc="company.phone"/>
<t t-if="company.email"> |
<a t-att-href="'mailto:%s' % company.email" style="text-decoration:none; color: #999999;"><t t-esc="company.email"/></a>
</t>
<t t-if="company.website"> |
<a t-att-href="'%s' % company.website" style="text-decoration:none; color: #999999;">
<t t-esc="company.website"/>
</a>
</t>
</div>
</td>
</tr>
</tbody>
</table>
</td></tr>
<!-- POWERED BY -->
<tr><td align="center" style="min-width: 590px;">
Powered by <a target="_blank" href="https://www.odoo.com?utm_source=db&amp;utm_medium=email" style="color: #875A7B;">Odoo</a>
</td></tr>
</table>
</template>
</data>
</odoo>
@@ -1,25 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="1">
<record id="slide_channel_tag_group_level" model="slide.channel.tag.group">
<field name="name">Your Level</field>
<field name="is_published" eval="True"/>
</record>
<record id="slide_channel_tag_level_basic" model="slide.channel.tag">
<field name="name">Basic</field>
<field name="color">10</field>
<field name="group_id" ref="website_slides.slide_channel_tag_group_level"/>
</record>
<record id="slide_channel_tag_level_intermediate" model="slide.channel.tag">
<field name="name">Intermediate</field>
<field name="color">3</field>
<field name="group_id" ref="website_slides.slide_channel_tag_group_level"/>
</record>
<record id="slide_channel_tag_level_advanced" model="slide.channel.tag">
<field name="name">Advanced</field>
<field name="color">1</field>
<field name="group_id" ref="website_slides.slide_channel_tag_group_level"/>
</record>
<record id="slide_channel_tag_group_role" model="slide.channel.tag.group">
<field name="name">Your Role</field>
<field name="is_published" eval="True"/>
+19
View File
@@ -6,5 +6,24 @@
<field name="sequence">20</field>
<field name="is_published" eval="True"/>
</record>
<record id="slide_channel_tag_group_level" model="slide.channel.tag.group">
<field name="name">Your Level</field>
<field name="is_published" eval="True"/>
</record>
<record id="slide_channel_tag_level_basic" model="slide.channel.tag">
<field name="name">Basic</field>
<field name="color">10</field>
<field name="group_id" ref="website_slides.slide_channel_tag_group_level"/>
</record>
<record id="slide_channel_tag_level_intermediate" model="slide.channel.tag">
<field name="name">Intermediate</field>
<field name="color">3</field>
<field name="group_id" ref="website_slides.slide_channel_tag_group_level"/>
</record>
<record id="slide_channel_tag_level_advanced" model="slide.channel.tag">
<field name="name">Advanced</field>
<field name="color">1</field>
<field name="group_id" ref="website_slides.slide_channel_tag_group_level"/>
</record>
</data>
</odoo>
+16 -7
View File
@@ -8,7 +8,7 @@ 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
from odoo.exceptions import AccessError
from odoo.osv import expression
@@ -96,8 +96,8 @@ class Channel(models.Model):
description_short = fields.Text('Short Description', translate=True, help="The description that is displayed on the course card")
description_html = fields.Html('Detailed Description', translate=tools.html_translate, sanitize_attributes=False, sanitize_form=False)
channel_type = fields.Selection([
('documentation', 'Documentation'), ('training', 'Training')],
string="Course type", default="documentation", required=True)
('training', 'Training'), ('documentation', 'Documentation')],
string="Course type", default="training", required=True)
sequence = fields.Integer(default=10, help='Display order')
user_id = fields.Many2one('res.users', string='Responsible', default=lambda self: self.env.uid)
color = fields.Integer('Color Index', default=0, help='Used to decorate kanban view')
@@ -115,8 +115,17 @@ class Channel(models.Model):
promote_strategy = fields.Selection([
('latest', 'Latest Published'),
('most_voted', 'Most Voted'),
('most_viewed', 'Most Viewed')],
string="Featured Content", default='latest', required=True)
('most_viewed', 'Most Viewed'),
('specific', 'Specific'),
('none', 'None')],
string="Promoted Content", default='latest', required=False,
help='Depending the promote strategy, a slide will appear on the top of the course\'s page :\n'
' * Latest Published : the slide created last.\n'
' * Most Voted : the slide which have to most vote.\n'
' * Most Viewed ; the slide which have been viewed the most.\n'
' * Specific : You choose the slide to appear.\n'
' * None : There won\'t be any slide showing.\n')
promoted_slide_id = fields.Many2one('slide.slide', string='Promoted Slide')
access_token = fields.Char("Security Token", copy=False, default=_default_access_token)
nbr_presentation = fields.Integer('Presentations', compute='_compute_slides_statistics', store=True)
nbr_document = fields.Integer('Documents', compute='_compute_slides_statistics', store=True)
@@ -131,7 +140,7 @@ class Channel(models.Model):
rating_avg_stars = fields.Float("Rating Average (Stars)", compute='_compute_rating_stats', digits=(16, 1), compute_sudo=True)
# configuration
allow_comment = fields.Boolean(
"Allow rating on Course", default=False,
"Allow rating on Course", default=True,
help="If checked it allows members to either:\n"
" * like content and post comments on documentation course;\n"
" * post comment and review on training course;")
@@ -467,7 +476,7 @@ class Channel(models.Model):
default_channel_id=self.id,
default_use_template=bool(template),
default_template_id=template and template.id or False,
notif_layout='mail.mail_notification_light',
notif_layout='website_slides.mail_notification_channel_invite',
)
return {
'type': 'ir.actions.act_window',
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

@@ -567,11 +567,7 @@ odoo.define('website_slides.quiz', function (require) {
}
this.quiz.questionsCount++;
event.data.questionFormWidget.destroy();
if (event.data.save_and_new) {
this._onCreateQuizClick();
} else {
this.$('.o_wslides_js_quiz_add_question').removeClass('d-none');
}
this.$('.o_wslides_js_quiz_add_question').removeClass('d-none');
},
/**
@@ -24,7 +24,6 @@ var SlideQuizFinishModal = Dialog.extend({
options = _.defaults(options || {}, {
size: 'medium',
dialogClass: 'd-flex p-0',
$parentNode: parent.$el,
technical: false,
renderHeader: false,
renderFooter: false
@@ -116,14 +116,12 @@ var QuestionFormWidget = publicWidget.Widget.extend({
},
/**
* Handler when user click on 'Save & New', 'Save & Close'
* or 'Update' buttons.
* Handler when user click on 'Save' or 'Update' buttons.
* @param ev
* @private
*/
_validateQuestion: function (ev) {
this._createOrUpdateQuestion({
save_and_new: $(ev.currentTarget).hasClass('o_wslides_js_quiz_create_next'),
update: $(ev.currentTarget).hasClass('o_wslides_js_quiz_update'),
});
},
@@ -169,7 +167,6 @@ var QuestionFormWidget = publicWidget.Widget.extend({
} else {
self.trigger_up('display_created_question', {
newQuestionRenderedTemplate: renderedQuestion,
save_and_new: options.save_and_new,
questionFormWidget: self
});
}
@@ -29,12 +29,14 @@ var TagCourseDialog = Dialog.extend({
click: this._onClickFormSubmit.bind(this)
}, {
text: _t("Discard"),
close: true
click: this._onClickClose.bind(this)
}]
});
this.channelID = parseInt(options.channelId, 10);
this.tagIds = options.channelTagIds || [];
// Open with a tag name as default
this.defaultTag = options.defaultTag;
this._super(parent, options);
},
start: function () {
@@ -42,6 +44,9 @@ var TagCourseDialog = Dialog.extend({
return this._super.apply(this, arguments).then(function () {
self._bindSelect2Dropdown();
self._hideTagGroup();
if (self.defaultTag) {
self._setDefaultSelection();
}
});
},
@@ -151,6 +156,11 @@ var TagCourseDialog = Dialog.extend({
return values;
},
_setDefaultSelection: function () {
this.$('#tag_id').select2('data', {id: _.uniqueId('tag_'), text: this.defaultTag, create: true}, true);
this.$('#tag_id').select2('readonly', true);
},
/**
* Get value for tag_id and [when appropriate] tag_group_id to send to server
*
@@ -260,7 +270,16 @@ var TagCourseDialog = Dialog.extend({
//--------------------------------------------------------------------------
// Handler
//--------------------------------------------------------------------------
_onClickFormSubmit: function () {
if (this.defaultTag && !this.channelID) {
this._createNewTag();
} else {
this._addTagToChannel();
}
},
_addTagToChannel: function () {
var self = this;
var $form = this.$('#slides_channel_tag_add_form');
if (this._formValidate($form)) {
@@ -280,6 +299,34 @@ var TagCourseDialog = Dialog.extend({
}
},
_createNewTag: function () {
var self = this;
var $form = this.$('#slides_channel_tag_add_form');
this.$('#tag_id').select2('readonly', false);
var valid = this._formValidate($form);
this.$('#tag_id').select2('readonly', true);
if (valid) {
var values = this._getSelect2DropdownValues();
return this._rpc({
route: '/slide_channel_tag/add',
params: {
'tag_id': values.tag_id,
'group_id': values.group_id
},
}).then(function (data) {
self.trigger_up('tag_refresh', { tag_id: data.tag_id });
self.close();
});
}
},
_onClickClose: function () {
if (this.defaultTag && !this.channelID) {
this.trigger_up('tag_remove_new');
}
this.close();
},
_onChangeTag: function (ev) {
var self = this;
var tag = $(ev.currentTarget).select2('data');
@@ -13,7 +13,7 @@ var SlideUnsubscribeDialog = Dialog.extend({
template: 'slides.course.unsubscribe.modal',
_texts: {
titleSubscribe: _t("Subscribe"),
titleUnsubscribe: _t("Unsubscribe"),
titleUnsubscribe: _t("Notifications"),
titleLeaveCourse: _t("Leave the course")
},
@@ -60,7 +60,7 @@ var SlideUnsubscribeDialog = Dialog.extend({
if (state === '_subscription') {
btnList.push({text: _t("Save"), classes: "btn-primary", click: this._onClickSubscriptionSubmit.bind(this)});
btnList.push({text: _t("Discard"), close: true});
btnList.push({text: _t("Leave the course"), classes: "btn-danger ml-auto", click: this._onClickLeaveCourse.bind(this)});
btnList.push({text: _t("or Leave the course"), classes: "btn-danger ml-auto", click: this._onClickLeaveCourse.bind(this)});
} else if (state === '_leave') {
btnList.push({text: _t("Leave the course"), classes: "btn-danger", click: this._onClickLeaveCourseSubmit.bind(this)});
btnList.push({text: _t("Discard"), click: this._onClickLeaveCourseCancel.bind(this)});
@@ -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 py-1');
$slideTarget.addClass('badge-success');
} else {
$slideTarget.removeClass('badge-success py-1');
$slideTarget.removeClass('badge-success');
$slideTarget.addClass('badge-light badge-hide border');
}
});
@@ -67,11 +67,12 @@ var SlideUploadDialog = Dialog.extend({
this._alertRemove();
$('<div/>', {
"class": 'alert alert-warning',
id: 'upload-alert',
role: 'alert'
}).text(message).insertBefore(this.$('form'));
},
_alertRemove: function () {
this.$('.alert-warning').remove();
this.$('#upload-alert').remove();
},
/**
* Section and tags management from select2
@@ -188,17 +189,17 @@ var SlideUploadDialog = Dialog.extend({
if (! this.modulesToInstallStatus.installing) {
btnList.push({text: this.modulesToInstallStatus.failed ? _t("Retry") : _t("Install"), classes: 'btn-primary', click: this._onClickInstallModuleConfirm.bind(this)});
}
btnList.push({text: _t("Go Back"), classes: 'o_w_slide_go_back', click: this._onClickGoBack.bind(this)});
btnList.push({text: _t("Discard"), classes: 'o_w_slide_go_back', click: this._onClickGoBack.bind(this)});
} else if (state !== '_upload') { // no button when uploading
if (this.canUpload) {
if (this.canPublish) {
btnList.push({text: _t("Publish"), classes: 'btn-primary o_w_slide_upload o_w_slide_upload_published', click: this._onClickFormSubmit.bind(this)});
btnList.push({text: _t("Save as Draft"), classes: 'o_w_slide_upload', click: this._onClickFormSubmit.bind(this)});
btnList.push({text: _t("Save & Publish"), classes: 'btn-primary o_w_slide_upload o_w_slide_upload_published', click: this._onClickFormSubmit.bind(this)});
btnList.push({text: _t("Save"), classes: 'o_w_slide_upload', click: this._onClickFormSubmit.bind(this)});
} else {
btnList.push({text: _t("Save as Draft"), classes: 'btn-primary o_w_slide_upload', click: this._onClickFormSubmit.bind(this)});
btnList.push({text: _t("Save"), classes: 'btn-primary o_w_slide_upload', click: this._onClickFormSubmit.bind(this)});
}
}
btnList.push({text: _t("Go Back"), classes: 'o_w_slide_go_back', click: this._onClickGoBack.bind(this)});
btnList.push({text: _t("Discard"), classes: 'o_w_slide_go_back', click: this._onClickGoBack.bind(this)});
}
return btnList;
},
@@ -351,22 +352,20 @@ var SlideUploadDialog = Dialog.extend({
};
},
/**
* Show the preview/right column and resize the modal
* Show the preview
* @private
*/
_showPreviewColumn: function () {
this.$('#o_wslides_js_slide_upload_left_column').removeClass('col').addClass('col-md-6');
this.$('#o_wslides_js_slide_upload_preview_column').removeClass('d-none');
this.$modal.find('.modal-dialog').addClass('modal-lg');
this.$('.o_slide_tutorial').addClass('d-none');
this.$('.o_slide_preview').removeClass('d-none');
},
/**
* Hide the preview/right column and resize the modal
* Hide the preview
* @private
*/
_hidePreviewColumn: function () {
this.$('#o_wslides_js_slide_upload_left_column').addClass('col').removeClass('col-md-6');
this.$('#o_wslides_js_slide_upload_preview_column').addClass('d-none');
this.$modal.find('.modal-dialog').removeClass('modal-lg');
this.$('.o_slide_tutorial').removeClass('d-none');
this.$('.o_slide_preview').addClass('d-none');
},
/**
* @private
@@ -388,6 +387,7 @@ var SlideUploadDialog = Dialog.extend({
_onChangeType: function () {
var currentType = this.get('state');
var tmpl;
this.$modal.find('.modal-dialog').removeClass('modal-lg');
if (currentType === '_select') {
tmpl = 'website.slide.upload.modal.select';
} else if (currentType === '_upload') {
@@ -396,6 +396,7 @@ var SlideUploadDialog = Dialog.extend({
tmpl = 'website.slide.upload.modal.import';
} else {
tmpl = this.slide_type_data[currentType]['template'];
this.$modal.find('.modal-dialog').addClass('modal-lg');
}
this.$('.o_w_slide_upload_modal_container').empty();
this.$('.o_w_slide_upload_modal_container').append(QWeb.render(tmpl, {widget: this}));
@@ -407,8 +408,6 @@ var SlideUploadDialog = Dialog.extend({
} else {
this.set_title(_t("Upload a document"));
}
this.$modal.find('.modal-dialog').removeClass('modal-lg');
},
_onChangeCanSubmitForm: function (ev) {
if (this.get('can_submit_form')) {
@@ -0,0 +1,117 @@
odoo.define('website_slides.slides_tour', function (require) {
"use strict";
var core = require('web.core');
var _t = core._t;
var tour = require('web_tour.tour');
tour.register('slides_tour', {
url: '/slides',
}, [{
trigger: '#new-content-menu > a',
content: _t("Welcome on your course's home page. It's still empty for now. Click on \"<b>New</b>\" to write your first course."),
position: 'bottom',
}, {
trigger: 'a[data-action="new_slide_channel"]',
content: _t("Select <b>Course</b> to create it and manage it."),
position: 'bottom',
width: 210,
}, {
trigger: 'input[name="name"]',
content: _t("Give your course an engaging <b>Title</b>."),
position: 'bottom',
width: 280,
run: 'text My New Course',
}, {
trigger: 'textarea[name="description"]',
content: _t("Give your course a helpful <b>Description</b>."),
position: 'bottom',
width: 300,
run: 'text This course is for advanced users.',
}, {
trigger: 'button.btn-primary',
content: _t("Click on the <b>Create</b> button to create your first course."),
}, {
trigger: '.o_wslides_js_slide_section_add',
content: _t("Congratulation, your course is created, but there isn't any content yet. First, let's add a <b>Section</b> to give your course a structure."),
position: 'bottom',
}, {
trigger: 'input[name="name"]',
content: _t("A good course has structure and a table of content. Your first section will be the <b>Introduction</b>."),
position: 'bottom',
}, {
trigger: 'button.btn-primary',
content: _t("Click on <b>Save</b> to apply changes."),
position: 'bottom',
width: 260,
}, {
trigger: 'a.btn-primary.o_wslides_js_slide_upload',
content: _t("Your first section is created, now it's time to add lessons to your course. Click on <b>Add Content</b> to upload a document, create a web page or link a video."),
position: 'bottom',
}, {
trigger: 'a[data-slide-type="presentation"]',
content: _t("First, let's add a <b>Presentation</b>. It can be a .pdf or an image."),
position: 'bottom',
}, {
trigger: 'input#upload',
content: _t("Choose a <b>File</b> on your computer."),
}, {
trigger: 'input#name',
content: _t("The <b>Title</b> of your lesson is autocompleted but you can change it if you want.</br>A <b>Preview</b> of your file is available on the right side of the screen."),
}, {
trigger: 'input#duration',
content: _t("The <b>Duration</b> of the lesson is based on the number of pages of your document. You can change this number if your attendees will need more time to assimilate the content."),
}, {
trigger: 'button.o_w_slide_upload_published',
content: _t("<b>Save & Publish</b> your lesson to make it available to your attendees."),
position: 'bottom',
width: 285,
}, {
trigger: 'span.badge-info:contains("New")',
content: _t("Congratulation! Your first lesson is available. Let's see the options available here. The tag \"<b>New</b>\" indicate that this lesson has less than 7 days."),
position: 'bottom',
}, {
trigger: 'a[name="o_wslides_list_slide_add_quizz"]',
extra_trigger: '.o_wslides_slides_list_slide:hover',
content: _t("If you want to be sure that attendees have understood and memorized the content, you can add a Quiz on the lesson. Click on <b>Add Quiz</b>."),
}, {
trigger: 'input[name="question-name"]',
content: _t("Enter your <b>Question</b>. Be clear and concise."),
position: 'left',
width: 330,
}, {
trigger: 'input.o_wslides_js_quiz_answer_value',
content: _t("Enter at least two possible <b>Answers</b>."),
position: 'left',
width: 290,
}, {
trigger: 'a.o_wslides_js_quiz_is_correct',
content: _t("Mark the correct answer by checking the <b>correct</b> mark."),
position: 'right',
width: 230,
}, {
trigger: 'i.o_wslides_js_quiz_comment_answer:last',
content: _t("You can add <b>comments</b> on answers. This will be visible with the results if the user select this answer."),
position: 'right',
}, {
trigger: 'a.o_wslides_js_quiz_validate_question',
content: _t("<b>Save</b> your question."),
position: 'left',
width: 170,
}, {
trigger: 'li.breadcrumb-item:nth-child(2)',
content: _t("Click on your <b>Course</b> to go back to the table of content."),
position: 'top',
}, {
trigger: 'label.js_publish_btn',
content: _t("Once you're done, don't forget to <b>Publish</b> your course."),
position: 'bottom',
}, {
trigger: 'a.o_wslides_js_slides_list_slide_link',
content: _t("Congratulation you've created your first course.<br/>Click on the title of this content to see it in fullscreen mode."),
position: 'bottom',
}]);
});
@@ -5,6 +5,7 @@ var core = require('web.core');
var Dialog = require('web.Dialog');
var QWeb = core.qweb;
var WebsiteNewMenu = require('website.newMenu');
var TagCourseDialog = require('website_slides.channel_tag.add').TagCourseDialog;
var wUtils = require('website.utils');
var _t = core._t;
@@ -13,8 +14,16 @@ var _t = core._t;
var ChannelCreateDialog = Dialog.extend({
template: 'website.slide.channel.create',
xmlDependencies: Dialog.prototype.xmlDependencies.concat(
['/website_slides/static/src/xml/website_slides_channel.xml']
['/website_slides/static/src/xml/website_slides_channel.xml',
'/website_slides/static/src/xml/website_slides_channel_tag.xml']
),
events: _.extend({}, Dialog.prototype.events, {
'change input#tag_ids' : '_onChangeTag',
}),
custom_events: _.extend({}, Dialog.prototype.custom_events, {
'tag_refresh': '_onTagRefresh',
'tag_remove_new': '_onTagRemoveNew',
}),
/**
* @override
* @param {Object} parent
@@ -45,6 +54,29 @@ var ChannelCreateDialog = Dialog.extend({
formatNoMatches: false,
multiple: true,
selection_data: false,
formatSelection: function (data) {
if (data.tag) {
data.text = data.tag;
}
return data.text;
},
createSearchChoice: function(term, data) {
var addedTags = $(this.opts.element).select2('data');
if (_.filter(_.union(addedTags, data), function (tag) {
return tag.text.toLowerCase().localeCompare(term.toLowerCase()) === 0;
}).length === 0) {
if (this.opts.can_create) {
return {
id: _.uniqueId('tag_'),
create: true,
tag: term,
text: _.str.sprintf(_t("Create new Tag '%s'"), term),
};
} else {
return undefined;
}
}
},
fill_data: function (query, data) {
var that = this,
tags = {results: []};
@@ -87,6 +119,44 @@ var ChannelCreateDialog = Dialog.extend({
$form.submit();
}
},
_onChangeTag: function (ev) {
var self = this;
var tags = $(ev.currentTarget).select2('data');
tags.forEach(function (element) {
if (element.create) {
new TagCourseDialog(self, { defaultTag: element.text }).open();
}
});
},
/**
* Replace the new tag ID by its real ID
* @param ev
* @private
*/
_onTagRefresh: function (ev) {
var $tag_ids = $('#tag_ids');
var tags = $tag_ids.select2('data');
tags.forEach(function (element) {
if (element.create) {
element.id = ev.data.tag_id;
element.create = false;
}
});
$tag_ids.select2('data', tags);
// Set selection_data to false to force tag reload
$tag_ids.data('select2').opts.selection_data = false;
},
/**
* Remove the created tag if the user clicks on 'Discard' on the create tag Dialog
* @private
*/
_onTagRemoveNew: function () {
var tags = $('#tag_ids').select2('data');
tags = tags.filter(function (value) {
return !value.create;
});
$('#tag_ids').select2('data', tags);
},
});
WebsiteNewMenu.include({
@@ -119,6 +119,6 @@
.modal-open {
> .modal-backdrop {
z-index: $zindex-dropdown - 1;
z-index: $zindex-modal-backdrop;
}
}
@@ -554,6 +554,11 @@ $o-wslides-fs-side-width: 300px;
}
}
#slide_channel_add_form input[name="channel_type"]:checked + label {
border: 4px solid $primary;
box-shadow: 3px 3px 5px gray;
}
// Embed PDFViewer
// **************************************************
#PDFViewer.o_wslides_fs_pdf_viewer {
@@ -68,10 +68,7 @@
<t t-name="slide.quiz.create.buttons">
<div>
<a class="o_wslides_js_quiz_validate_question btn btn-primary text-white border" role="button">
<span>Save &amp; Close</span>
</a>
<a class="o_wslides_js_quiz_validate_question o_wslides_js_quiz_create_next btn btn-primary text-white border" role="button">
<span>Save &amp; New</span>
<span>Save</span>
</a>
<a class="o_wslides_js_quiz_cancel_question btn btn-light border" role="button">
<span>Cancel</span>
@@ -8,38 +8,44 @@
<input type="hidden" name="csrf_token" t-att-value="csrf_token"/>
<div class="form-group">
<label for="title" class="col-form-label">Title</label>
<input type="text" class="form-control" name="name" id="title" placeholder="Course Title" required="1"/>
<input type="text" class="form-control" name="name" id="title" placeholder="Computer Science for kids" required="1"/>
<p id="title-required" class="text-danger mt-1 mb-0 d-none">Please fill in this field</p>
</div>
<div class="form-group">
<label for="tag_ids" class="col-form-label">Tags</label>
<input type="text" class="form-control" name="tag_ids" id="tag_ids" placeholder="Tags"/>
</div>
<fieldset class="form-group">
<label for="channel_type">Type</label>
<div class="form-check">
<input class="form-check-input" type="radio" name="channel_type" id="channel_type1" value="training" checked="checked"/>
<span class="form-check-label" for="channel_type1">
Training (with progress)
</span>
<label for="channel_type">Choose a layout</label>
<div class="form-row">
<div class="form-group col-6">
<div class="form-check px-0">
<input class="form-check-input d-none" type="radio" name="channel_type" id="channel_type1" value="training" checked="checked"/>
<label for="channel_type1">
<img class="w-100" src="/website_slides/static/src/img/channel-training-layout.png" alt="Training Layout"/>
</label>
</div>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="channel_type" id="channel_type2" value="documentation"/>
<span class="form-check-label" for="channel_type2">
Documentation
</span>
<div class="form-group col-6">
<div class="form-check px-0">
<input class="form-check-input d-none" type="radio" name="channel_type" id="channel_type2" value="documentation"/>
<label for="channel_type2">
<img class="w-100" src="/website_slides/static/src/img/channel-documentation-layout.png" alt="Documentation Layout"/>
</label>
</div>
</div>
</fieldset>
</div>
<div class="form-group">
<label for="title">Description</label>
<input type="text" class="form-control" name="description" id="description" placeholder="Write here a short description of your first course" />
<textarea rows="2" class="form-control" name="description" id="description"
placeholder="Common tasks for a computer scientist is asking the right questions and answering
questions. In this course, you'll study those topics with activities about mathematics, science and logic." />
</div>
<div class="form-group">
<label id="communication-label">Review</label>
<div class="o_wslide_channel_communication_type">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="allow_comment" name="allow_comment"/>
<span class="form-check-label" for="allow_comment">Allow review on Course</span>
<input class="form-check-input" type="checkbox" id="allow_comment" name="allow_comment" checked="checked"/>
<span class="form-check-label" for="allow_comment">Allow students to review your course</span>
</div>
</div>
</div>
@@ -14,7 +14,7 @@
<div class="form-group row">
<div class="controls mt8 ml-3">
<input id="subscribed" name="subscribed" type="checkbox"/>
<label for="subscribed" class="col-form-label font-weight-normal">Subscribe to get notified when a new content is added.</label>
<label for="subscribed" class="col-form-label font-weight-normal">Be notified when a new content is added.</label>
</div>
</div>
</form>
@@ -39,7 +39,7 @@
Uploading template
-->
<t t-name="website.slide.upload.modal.uploading">
<div class="text-center" aria-hidden="true" role="status">
<div class="text-center" role="status">
<div class="fa-3x">
<i class="fa fa-spinner fa-pulse"></i>
</div>
@@ -88,17 +88,25 @@
<div>
<form class="clearfix">
<div class="row">
<div id="o_wslides_js_slide_upload_left_column" class="col">
<div id="o_wslides_js_slide_upload_left_column" class="col-md-6">
<div class="form-group">
<label for="upload" class="col-form-label">Choose a PDF or an Image</label>
<input id="upload" name="file" class="form-control" accept="image/*,application/pdf" type="file" required="required"/>
<input id="upload" name="file" class="form-control h-100" accept="image/*,application/pdf" type="file" required="required"/>
</div>
<canvas id="data_canvas" class="d-none"></canvas>
<t t-call="website.slide.upload.modal.common"/>
</div>
<div id="o_wslides_js_slide_upload_preview_column" class="col-md-6 d-none">
<div class="img-thumbnail">
<div class="o_slide_preview">
<div id="o_wslides_js_slide_upload_preview_column" class="col-md-6">
<div class="img-thumbnail h-100">
<div class="o_slide_tutorial p-3">
<div class="h5">How to upload your PowerPoint Presentations or Word Documents?</div>
<div class="mx-3 my-4">Save your presentations or documents as PDF files and upload them.</div>
<div class="alert alert-warning" role="alert">
<i class="fa fa-info-circle pr-2"/>
Only JPG, PNG, PDF, files types are supported
</div>
</div>
<div class="o_slide_preview d-none">
<img src="/website_slides/static/src/img/document.png" id="slide-image" title="Content Preview" alt="Content Preview" class="img-fluid"/>
</div>
</div>
@@ -112,18 +120,15 @@
<div>
<form class="clearfix">
<div class="row">
<div id="o_wslides_js_slide_upload_left_column" class="col">
<div class="form-group">
<label for="upload" class="col-form-label">Choose a Cover Image</label>
<input id="upload" name="file" class="form-control" accept="image/*" type="file" required="required"/>
</div>
<div id="o_wslides_js_slide_upload_left_column" class="col-md-6">
<canvas id="data_canvas" class="d-none"></canvas>
<t t-call="website.slide.upload.modal.common"/>
</div>
<div id="o_wslides_js_slide_upload_preview_column" class="col-md-6 d-none">
<div class="img-thumbnail">
<div class="o_slide_preview">
<img src="/website_slides/static/src/img/document.png" id="slide-image" title="Content Preview" alt="Content Preview" class="img-fluid"/>
<div id="o_wslides_js_slide_upload_preview_column" class="col-md-6">
<div class="img-thumbnail h-100">
<div class="o_slide_tutorial p-3">
<div class="h5">How to create a Lesson as a Web Page?</div>
<div class="mx-3 my-4">First, create your lesson, then edit it with the website builder. You'll be able to drop building blocks on your page and edit them.</div>
</div>
</div>
</div>
@@ -136,7 +141,7 @@
<div>
<form class="clearfix">
<div class="row">
<div id="o_wslides_js_slide_upload_left_column" class="col">
<div id="o_wslides_js_slide_upload_left_column" class="col-md-6">
<div class="form-group">
<label for="url" class="col-form-label">Youtube Link</label>
<input id="url" name="url" class="form-control" placeholder="Youtube Video URL" required="required"/>
@@ -144,9 +149,15 @@
<canvas id="data_canvas" class="d-none"></canvas>
<t t-call="website.slide.upload.modal.common"/>
</div>
<div id="o_wslides_js_slide_upload_preview_column" class="col-md-6 d-none">
<div class="img-thumbnail">
<div class="o_slide_preview">
<div id="o_wslides_js_slide_upload_preview_column" class="col-md-6">
<div class="img-thumbnail h-100">
<div class="o_slide_tutorial p-3">
<div class="h5">How to upload your videos ?</div>
<div class="mx-3 my-4">First, upload your videos on YouTube and mark them as <strong>unlisted</strong>. This way, they will be secured.</div>
<div class="mx-3 my-4">What does <strong>unlisted</strong> means? The YouTube unlisted meaning is a video which can be viewed only by the users having the link to it. Your video will never come up in the search results nor on your channel.</div>
<div class="mx-3 my-4"><a href="https://support.google.com/youtube/answer/157177" target="_blank" >Change video privacy settings</a></div>
</div>
<div class="o_slide_preview d-none">
<img src="/website_slides/static/src/img/document.png" id="slide-image" title="Content Preview" alt="Content Preview" class="img-fluid"/>
</div>
</div>
@@ -159,8 +170,21 @@
<t t-name="website.slide.upload.quiz">
<div>
<form class="clearfix">
<canvas id="data_canvas" class="d-none"></canvas>
<t t-call="website.slide.upload.modal.common"/>
<div class="row">
<div id="o_wslides_js_slide_upload_left_column" class="col-md-6">
<canvas id="data_canvas" class="d-none"></canvas>
<t t-call="website.slide.upload.modal.common"/>
</div>
<div id="o_wslides_js_slide_upload_preview_column" class="col-md-6">
<div class="img-thumbnail h-100">
<div class="o_slide_tutorial p-3">
<div class="h5">Test your students with small Quizzes</div>
<div class="mx-3 my-4">With Quizzes you can keep your students focused and motivated by answering some questions and gaining some karma points</div>
<img src="/website_slides/static/src/img/onboarding-quiz.png" title="Quiz Demo Data" class="img-fluid"/>
</div>
</div>
</div>
</div>
</form>
</div>
</t>
+1
View File
@@ -34,6 +34,7 @@
<script type="text/javascript" src="/website_slides/static/src/js/slides_course_quiz_finish.js"/>
<script type="text/javascript" src="/website_slides/static/src/js/slides_course_tag_add.js"/>
<script type="text/javascript" src="/website_slides/static/src/js/slides_course_unsubscribe.js"/>
<script type="text/javascript" src="/website_slides/static/src/js/tours/slides_tour.js"/>
</xpath>
</template>
@@ -18,8 +18,8 @@
class="oe_stat_button"
groups="website_slides.group_website_slides_officer">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value"><field name="total_slides" nolabel="1"/></span>
<span name="total_slides_label" class="o_stat_text">Contents</span>
<span class="o_stat_value"><field name="total_views" nolabel="1"/> Visits</span>
<span class="o_stat_value"><field name="total_slides" nolabel="1"/> Contents</span>
</div>
</button>
<button name="action_redirect_to_done_members"
@@ -45,7 +45,10 @@
class="oe_stat_button"
groups="website_slides.group_website_slides_officer"
attrs="{'invisible': [('allow_comment', '=', False)]}">
<field name="rating_count" string="Reviews" widget="statinfo"/>
<div class="o_field_widget o_stat_info">
<span class="o_stat_value"><field name="rating_avg_stars" nolabel="1"/>/5</span>
<span name="rating_count_label" class="o_stat_text"><field name="rating_count" nolabel="1"/> Reviews</span>
</div>
</button>
<field name="is_published" widget="website_redirect_button"/>
</div>
@@ -53,7 +56,7 @@
<field name="image_1920" widget="image" class="oe_avatar" options="{'preview_image': 'image_128'}"/>
<div class="oe_title">
<label for="name" class="oe_edit_only" string="Course Title"/>
<h1><field name="name" default_focus="1" placeholder="Course Title"/></h1>
<h1><field name="name" default_focus="1" placeholder="Computer Science for kids"/></h1>
</div>
<div>
<field name="active" invisible="1"/>
@@ -61,7 +64,7 @@
</div>
<notebook colspan="4">
<page name="content" string="Content">
<field name="slide_ids" colspan="4" nolabel="1" widget="slide_category_one2many" mode="tree,kanban" context="{'default_channel_id': active_id, 'form_view_ref' : 'website_slides.view_slide_slide_form_wo_channel_id'}">
<field name="slide_ids" string="Content" colspan="4" nolabel="1" widget="slide_category_one2many" mode="tree,kanban" context="{'default_channel_id': active_id, 'form_view_ref' : 'website_slides.view_slide_slide_form_wo_channel_id'}">
<tree decoration-bf="is_category" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="name"/>
@@ -80,8 +83,7 @@
</page>
<page name="description" string="Description">
<group>
<field name="description" colspan="4" />
<field name="description_short" colspan="4" />
<field name="description" colspan="4" placeholder="Common tasks for a computer scientist is asking the right questions and answering questions. In this course, you'll study those topics with activities about mathematics, science and logic."/>
</group>
</page>
<page name="options" string="Options">
@@ -107,6 +109,10 @@
<field name="visibility" widget="radio"/>
<field name="promote_strategy" widget="radio"
attrs="{'invisible': [('channel_type', '=', 'training')]}"/>
<field name="promoted_slide_id"
attrs="{'invisible': ['|', ('channel_type', '=', 'training'), ('promote_strategy', '!=', 'specific')],
'required': [('channel_type', '!=', 'training'), ('promote_strategy', '=', 'specific')]}"
domain="[('channel_id', '=', active_id), ('is_category', '=', False)]"/>
</group>
</group>
<div attrs="{'invisible': [('enroll', '!=', 'invite')]}">
@@ -114,33 +120,16 @@
<field name="enroll_msg" colspan="4" nolabel="1"/>
</div>
</page>
<page string="Karma Rules" name="karma_rules">
<page string="Karma" name="karma_rules">
<group>
<field name="karma_gen_channel_rank" string="Rank Course"/>
<field name="karma_gen_channel_finish" string="Finish Course"/>
<field name="karma_review" attrs="{'invisible': [('allow_comment', '!=', True)]}"/>
<field name="karma_slide_comment" attrs="{'invisible': [('allow_comment', '!=', True)]}"/>
<field name="karma_slide_vote" attrs="{'invisible': [('allow_comment', '!=', True)]}"/>
</group>
</page>
<page name="statistics" string="Statistics">
<group>
<group name="statistics_nbr" class="col-4">
<field name="nbr_presentation"/>
<field name="nbr_document"/>
<field name="nbr_video"/>
<field name="nbr_infographic"/>
<field name="nbr_quiz" string="Quizzes"/>
<field name="nbr_webpage"/>
<group string="Rewards">
<field name="karma_gen_channel_rank" string="Review Course"/>
<field name="karma_gen_channel_finish" string="Finish Course"/>
</group>
<group name="view_stats">
<field name="total_views" string="Visits"/>
<field string="Rating" name="rating_avg" widget="field_float_rating"/>
<label for="total_time" string="Watch Time"/>
<div>
<field name="total_time" widget="float_time" />
<span class="ml-2">Hours</span>
</div>
<group string="Access Rights" attrs="{'invisible': [('allow_comment', '!=', True)]}">
<field name="karma_review" attrs="{'invisible': [('allow_comment', '!=', True)]}"/>
<field name="karma_slide_comment" attrs="{'invisible': [('allow_comment', '!=', True)]}"/>
<field name="karma_slide_vote" attrs="{'invisible': [('allow_comment', '!=', True)]}"/>
</group>
</group>
</page>
@@ -181,6 +170,7 @@
<tree string="Courses" create="false" default_order="total_views desc" sample="1">
<field name="name"/>
<field name="total_views"/>
<field name="total_time" widget="float_time" />
<field name="members_count"/>
<field name="total_votes"/>
<field name="rating_avg_stars"/>
@@ -118,7 +118,11 @@
<!-- ==== Header Left ==== -->
<div class="col-12 col-md-4 col-lg-3">
<div class="d-flex align-items-end justify-content-around h-100">
<div t-field="channel.image_1920" t-options='{"widget": "image", "class": "o_wslides_course_pict d-inline-block mb-2 mt-3 my-md-0"}' class="h-100"/>
<div t-if="channel.image_1920" t-field="channel.image_1920" t-options='{"widget": "image", "class": "o_wslides_course_pict d-inline-block mb-2 mt-3 my-md-0"}' class="h-100"/>
<div t-else="" class="h-100">
<img t-att-src="'/website_slides/static/src/img/channel-%s-default.jpg' % ('training' if channel.channel_type == 'training' else 'documentation')"
class="o_wslides_course_pict d-inline-block mb-2 mt-3 my-md-0"/>
</div>
</div>
</div>
@@ -128,7 +132,7 @@
<h1 t-field="channel.name"/>
<p class="mb-0 mb-xl-3" t-field="channel.description"/>
<div t-if="channel.channel_type == 'documentation'" class="my-1">
<div t-if="channel.channel_type == 'documentation'" class="d-flex mb-md-5">
<button role="button" class="btn text-white pl-0" title="Share Channel"
aria-label="Share Channel"
data-toggle="modal" t-att-data-target="'#slideChannelShareModal_%s' % channel.id">
@@ -138,7 +142,6 @@
</div>
<div class="d-flex flex-column justify-content-center h5 flex-grow-1 mb-md-5" t-if="channel.allow_comment">
<t t-call="portal_rating.rating_stars_static_popup_composer">
<t t-set="btn_classes" t-value="'btn-warning'"/>
<t t-set="rating_avg" t-value="rating_avg"/>
<t t-set="rating_total" t-value="rating_count"/>
<t t-set="object" t-value="channel"/>
@@ -151,6 +154,7 @@
<t t-set="default_attachment_ids" t-value="last_message_attachment_ids"/>
<t t-set="force_submit_url" t-value="'/slides/mail/update_comment' if last_message_id else False"/>
<t t-set="disable_composer" t-value="not channel.can_review"/>
<t t-set="_link_btn_classes" t-value="'btn-link text-white'"/>
</t>
</div>
</div>
@@ -206,7 +210,7 @@
href="#"
t-att-data-channel-id="channel.id"
t-att-data-channel-tag-ids="channel.tag_ids.ids">
<i class="fa fa-plus mr-1"/><span>Add Tag</span>
<span>Add Tag</span>
</a>
</div>
<t t-if="channel.channel_type == 'training'" t-call="website_slides.course_slides_list"/>
@@ -386,12 +390,12 @@
<li t-if="category['total_slides'] or channel.can_publish" t-att-class="'o_wslides_slide_list_category o_wslides_js_list_item mb-2' if category_id else 'mt-4'" t-att-data-slide-id="category_id" t-att-data-category-id="category_id">
<div t-att-data-category-id="category_id"
t-att-class="'o_wslides_slide_list_category_header position-relative d-flex justify-content-between align-items-center mt8 %s' % ('o_wslides_js_category bg-white shadow-sm border-bottom-0' if category_id and channel.can_upload else 'border-0 py-1')">
t-att-class="'o_wslides_slide_list_category_header position-relative d-flex justify-content-between align-items-center mt8 %s %s' % ('bg-white shadow-sm border-bottom-0' if category_id else 'border-0', 'o_wslides_js_category py-0' if channel.can_upload else 'py-2')">
<div t-att-class="'d-flex align-items-center pl-3 %s' % ('o_wslides_slides_list_drag' if channel.can_publish else '')">
<div t-if="channel.can_publish and category_id" class="o_wslides_slides_list_drag py-2 pr-3">
<i class="fa fa-bars"/>
</div>
<span t-if="category_id" t-att-class="'text-muted' if not channel.can_upload else ''" t-field="category['category'].name"/>
<span t-if="category_id" t-field="category['category'].name"/>
<small t-if="not category['total_slides'] and category_id" class="ml-1 text-muted"><b>(empty)</b></small>
</div>
<div t-if="category_id" class="o_text_link d-flex border-left">
@@ -422,7 +426,7 @@
</t>
</ul>
<div t-if="channel.can_upload" class="o_wslides_content_actions btn-group">
<a class="o_wslides_js_slide_upload mr-1 border btn btn-light bg-white"
<a class="o_wslides_js_slide_upload mr-1 border btn btn-primary"
role="button"
aria-label="Upload Presentation"
href="#"
@@ -435,10 +439,83 @@
href="#" role="button"
groups="website_slides.group_website_slides_officer"><i class="fa fa-folder-o mr-1"/><span>Add Section</span></a>
</div>
<t t-if="not channel.slide_ids and channel.can_publish">
<t t-call="website_slides.course_slides_list_sample"/>
</t>
</div>
<div t-field="channel.description_html"/>
</template>
<template id="course_slides_list_sample" name="Course Sample Content">
<ul class="list-unstyled mt-3" style="opacity: 50%;">
<li class="o_wslides_slide_list_category mb-2">
<div class="o_wslides_slide_list_category_header position-relative d-flex justify-content-between align-items-center mt8 bg-white shadow-sm border-bottom-0 py-2">
<div class="d-flex align-items-center pl-3">
<span class="text-muted">Common tasks for a computer scientist</span>
</div>
</div>
<ul class="list-unstyled pb-1 border-top">
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
<i class="fa fa-file-text py-2 mx-2"/>
<div class="text-truncate mr-auto">
<span>Asking Question</span>
</div>
<div class="d-flex flex-row">
<span class="badge font-weight-bold px-2 py-1 m-1 badge-warning">
<i class="fa fa-fw fa-flag"/> 10 xp
</span>
</div>
</li>
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
<i class="fa fa-question-circle py-2 mx-2"/>
<div class="text-truncate mr-auto">
<span>Asking the right question</span>
</div>
</li>
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
<i class="fa fa-file-pdf-o py-2 mx-2"/>
<div class="text-truncate mr-auto">
<span>Answering Questions</span>
</div>
<div class="d-flex flex-row">
<span class="badge badge-info badge-arrow-right font-weight-normal px-2 py-1 m-1">New</span>
</div>
</li>
</ul>
</li>
<li class="o_wslides_slide_list_category mb-2">
<div class="o_wslides_slide_list_category_header position-relative d-flex justify-content-between align-items-center mt8 bg-white shadow-sm border-bottom-0 py-2">
<div class="d-flex align-items-center pl-3">
<span class="text-muted">Parts of computer science</span>
</div>
</div>
<ul class="list-unstyled pb-1 border-top">
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
<i class="fa fa-file-pdf-o py-2 mx-2"/>
<div class="text-truncate mr-auto">
<span>Mathematics</span>
</div>
</li>
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
<i class="fa fa-file-pdf-o py-2 mx-2"/>
<div class="text-truncate mr-auto">
<span>Science</span>
</div>
</li>
<li class="o_wslides_slides_list_slide bg-white-50 border-top-0 d-flex align-items-center pl-2 py-1 pr-2">
<i class="fa fa-play py-2 mx-2"/>
<div class="text-truncate mr-auto">
<span>Logic</span>
</div>
<div class="d-flex flex-row">
<span class="badge badge-success font-weight-normal px-2 py-1 m-1">Preview</span>
</div>
</li>
</ul>
</li>
</ul>
</template>
<template id="course_slides_list_slide" name="Slide template for a training channel">
<li t-att-index="j" t-att-data-slide-id="slide.id" t-att-data-category-id="category_id" t-attf-class="o_wslides_slides_list_slide o_wslides_js_list_item bg-white-50 border-top-0 d-flex align-items-center pl-2 #{'py-1 pr-2' if not channel.can_upload else ''}">
<div t-if="channel.can_publish" class=" o_wslides_slides_list_drag border-right p-2">
@@ -458,12 +535,14 @@
<div class="d-flex flex-row">
<a name="o_wslides_list_slide_add_quizz" t-if="channel.can_upload and not slide.question_ids" t-attf-href="/slides/slide/#{slug(slide)}?quiz_quick_create">
<span class="badge badge-light badge-hide border font-weight-normal px-2 py-1 m-1"><i class="fa fa-plus mr-1"/>Add Quiz</span>
<span class="badge badge-light badge-hide border font-weight-normal px-2 py-1 m-1">Add Quiz</span>
</a>
<a t-if="channel.can_upload" href="#">
<span t-att-data-slide-id="slide.id" t-attf-class="o_wslides_js_slide_toggle_is_preview badge #{'badge-success py-1' if slide.is_preview else 'badge-light badge-hide border'} font-weight-normal px-2 m-1">Free preview</span>
<span t-att-data-slide-id="slide.id" t-attf-class="o_wslides_js_slide_toggle_is_preview badge #{'badge-success' if slide.is_preview else 'badge-light badge-hide border'} font-weight-normal px-2 py-1 m-1"><span>Preview</span></span>
</a>
<span t-elif="slide.is_preview and not channel.is_member" class="badge badge-success font-weight-normal px-2 py-1 m-1">Free preview</span>
<t t-elif="slide.is_preview and not channel.is_member">
<span class="badge badge-success font-weight-normal px-2 py-1 m-1"><span>Preview</span></span>
</t>
<span t-if="slide.is_new_slide and not channel_progress[slide.id].get('completed')" class="badge badge-info badge-arrow-right font-weight-normal px-2 py-1 m-1">
New
</span>
@@ -481,7 +560,7 @@
</t>
<span t-if="channel.can_publish" class="d-none d-md-flex">
<a t-if="slide.slide_type == 'webpage'" class="px-2 o_text_link text-primary" target="_blank" t-attf-href="/slides/slide/#{slug(slide)}?enable_editor=1"><span class="fa fa-pencil"/></a>
<a t-else="" class="px-2 o_text_link text-primary" target="_blank" t-attf-href="/web#id=#{slide.id}&amp;action=#{slide_action}&amp;model=slide.slide&amp;view_type=form"><span class="fa fa-pencil"/></a>
<a t-else="" class="px-2 o_text_link text-primary" target="_blank" t-attf-href="/web#id=#{slide.id}&amp;action=#{slide_action}&amp;model=slide.slide&amp;view_type=form" title="Edit in backend"><span class="fa fa-pencil"/></a>
<a href="#" t-att-data-slide-id="slide.id" class="o_text_link text-danger px-2 o_wslides_js_slide_archive"><span class="fa fa-trash"/></a>
</span>
</div>
@@ -490,7 +569,7 @@
<!-- ======= Documentation Course content: cards / categories======= -->
<template id="course_promoted_slide" name="Documentation Course content: promoted slide">
<div class="o_wslides_promoted_slide d-none">
<div class="o_wslides_promoted_slide">
<div t-if="not search and not search_slide_type and slide_promoted" class="container py-1 mb-2">
<div class="card flex-column flex-lg-row">
<t t-set="image_url" t-value="website.image_url(slide_promoted, 'image_1024')"/>
@@ -647,7 +726,9 @@
</div>
</div>
<!-- Featured lesson -->
<t t-call="website_slides.course_promoted_slide"/>
<t t-if="channel.promote_strategy != 'none'">
<t t-call="website_slides.course_promoted_slide"/>
</t>
<div class="container py-2">
<t t-if="search">
@@ -708,13 +789,14 @@
</a>
<div t-else="" class="card-img-top border-bottom o_wslides_gradient" t-attf-style="padding-top: 50%;"/>
</t>
<i t-if="channel_progress[slide.id].get('completed')" class="position-absolute py-1 px-2 h5 fa fa-check-circle text-primary" style="right:0; top:0;"/>
<div class="card-body p-3">
<a t-if="can_access" class="card-title h5 mb-2o_wslides_desc_truncate_2" t-attf-href="/slides/slide/#{slug(slide)}#{query_string}" t-esc="slide.name"/>
<span t-else="" class="card-title h5 mb-2 o_wslides_desc_truncate_2 text-muted" t-esc="slide.name"/>
<div class="card-subtitle mb-2 text-muted" t-if="slide.is_preview or (not slide.is_published and user.has_group('website_slides.group_website_slides_officer'))">
<t t-if="slide.is_preview">
<span class="badge badge-info">Free preview</span>
<span class="badge badge-info">Preview</span>
</t>
<t t-if="not slide.is_published and channel.can_publish">
<span class="badge badge-danger">Unpublished</span>
@@ -761,10 +843,4 @@
<i t-if="slide.slide_type == 'quiz'" t-att-class="'fa fa-question-circle %s' % icon_class"></i>
</template>
<template name="Display Promoted Slide" id="display_promoted_slide" inherit_id="website_slides.course_promoted_slide" active="True" customize_show="True">
<xpath expr="//div[hasclass('o_wslides_promoted_slide')]" position="attributes">
<attribute name="class">o_wslides_promoted_slide</attribute>
</xpath>
</template>
</data></odoo>
@@ -1,29 +1,6 @@
<?xml version="1.0" ?>
<odoo><data>
<template id='courses_footer'>
<!-- 'users': 3 top users with picture (skip users without picture) -->
<section class="s_banner bg-900" style="background-image: url(/website_slides/static/src/img/banner_heroes_default.svg); background-size: cover; background-position: right bottom" data-snippet="s_banner">
<div class="container py-5">
<div class="row align-items-center">
<div t-attf-class="col-12 #{'col-md' if top3_users else 'text-center'}">
<h2 class="my-4 text-white">Meet The Heros Who Transformed Their Company!</h2>
<a href="/profile/users" class="btn btn-primary btn-lg">Go meet them</a>
</div>
<div class="col-12 col-md-5" t-if="top3_users">
<div class="ml-4 ml-md-2 mt-3">
<t t-foreach="top3_users" t-as="user">
<img class="rounded-circle img-fluid shadow ml-n4"
style="width: 140px; height: 140px; max-width: 40%; max-height: 40%; object-fit: cover; border: 6px solid white;"
t-attf-src="/web/image/res.users/#{user['id']}/image_256"/>
</t>
</div>
</div>
</div>
</div>
</section>
</template>
<!-- Channels home template -->
<template id='courses_home' name="Odoo Courses Homepage">
<t t-set="body_classname" t-value="'o_wslides_body'"/>
@@ -33,13 +10,10 @@
<div class="container align-items-center d-flex mb-5 mt-lg-5 pt-lg-4 pb-lg-1">
<div>
<h1 class="display-3 mb-0">Reach new heights</h1>
<h2 class="mb-4">Start your online course today !</h2>
<h2 class="mb-4">Start your online course today!</h2>
<div class="row mt-1 mb-3">
<div class="col-lg-6">
<p>Skill up and have an impact! Your business career starts here. Time to start a course.</p>
</div>
<div class="col-lg-3">
<a href="slides/all" class="btn btn-primary btn-lg">Pick a course</a>
<div class="col">
<p>Skill up and have an impact! Your business career starts here.<br/>Time to start a course.</p>
</div>
</div>
</div>
@@ -81,16 +55,12 @@
<div class="container o_wslides_home_main">
<div class="row">
<t t-set="is_achievements_active" t-value="request.website.viewref('website_slides.toggle_latest_achievements').active"/>
<t t-set="has_side_column" t-value="request.website.viewref('website_slides.toggle_leaderboard').active or is_achievements_active"/>
<t t-set="has_side_column" t-value="request.website.viewref('website_slides.toggle_leaderboard').active"/>
<t t-if="is_public_user">
<div t-if="has_side_column" class="col-lg-3 order-3 order-lg-2">
<div class="row">
<div class="col-12 col-md-7 col-lg-12">
<t t-call="website_slides.slides_home_achievements_small"/>
</div>
<div class="col-12 col-md-5 col-lg-12 mt-5 mt-md-0">
<div t-att-class="'pl-md-5 pl-lg-0 mt-lg-4' if is_achievements_active else 'pl-md-5 pl-lg-0'">
<div class="col-12 col-md-5 col-lg-12">
<div class="pl-md-5 pl-lg-0">
<t t-call="website_slides.slides_home_users_small"/>
</div>
</div>
@@ -116,9 +86,8 @@
<div class="row no-gutters mb-5 mt-3 mt-lg-0">
<div class="col-12 col-sm-6 col-lg-12">
<t t-call="website_slides.slides_home_user_profile_small"/>
<hr class="d-sm-none d-lg-block"/>
</div>
<div class="col-12 col-sm-6 col-lg-12 d-flex align-items-center">
<div class="col-12 col-sm-6 col-lg-12 pl-md-5 pl-lg-0 mt-lg-4">
<t t-call="website_slides.slides_home_user_achievements_small"/>
</div>
<div class="col-12 col-md-7 col-lg-12 pl-md-5 pl-lg-0 mt-lg-4 mb-3">
@@ -343,6 +312,12 @@
</t>
</template>
<template id='courses_footer'>
<section class="s_banner">
<div class="oe_structure oe_empty"/>
</section>
</template>
<template id='course_card' name="Course Card">
<div t-attf-class="card w-100 o_wslides_course_card mb-4 #{'o_wslides_course_unpublished' if not channel.is_published else ''}">
<t t-set="course_image" t-value="website.image_url(channel, 'image_1024')"/>
@@ -406,21 +381,20 @@
<template id="toggle_latest_achievements" inherit_id="website_slides.slides_home_achievements_small" active="True" customize_show="True" name='Display Achievements'>
<xpath expr="//t[hasclass('o_wslides_home_aside')]" position="inside">
<div class="row o_wslides_home_aside_title">
<div class="col">
<h5 class="m-0">Latest achievements</h5>
<hr class="mt-2"/>
<div t-if="achievements">
<div class="row o_wslides_home_aside_title">
<div class="col">
<h5 class="m-0">Latest achievements</h5>
<hr class="mt-2"/>
</div>
</div>
</div>
<div class="row">
<t t-if="achievements">
<div class="row">
<div class="col">
<t t-foreach="achievements" t-as="achievement">
<t t-call="website_slides.achievement_card"/>
</t>
</div>
</t>
<t t-else=""><p class="col mt8">No achievements currently :(</p></t>
</div>
</div>
</xpath>
</template>
@@ -499,7 +473,14 @@
</template>
<template id='slides_home_user_achievements_small' name="User Achievements">
<div class="o_wslides_home_aside flex-grow-1 mb-2">
<div class="o_wslides_home_aside flex-grow-1">
<div class="row o_wslides_home_aside_title">
<div class="col">
<a href="/profile/ranks_badges?badge_category=slides" class="float-right">View all</a>
<h5 class="m-0">Badges</h5>
<hr class="mt-2 pt-2"/>
</div>
</div>
<t t-foreach="challenges" t-as="challenge">
<t t-set="challenge_done" t-value="challenge in challenges_done if challenges_done else False"/>
<div t-attf-class="d-flex mb-3 align-items-center #{'o_wslides_entry_muted' if not challenge_done else ''}">
@@ -28,7 +28,6 @@ class SlideChannelInvite(models.TransientModel):
partner_ids = fields.Many2many('res.partner', string='Recipients')
# slide channel
channel_id = fields.Many2one('slide.channel', string='Slide channel', required=True)
channel_url = fields.Char(related="channel_id.website_url", readonly=True)
@api.depends('template_id')
def _compute_template_values(self):
@@ -107,10 +106,13 @@ class SlideChannelInvite(models.TransientModel):
except ValueError:
_logger.warning('QWeb template %s not found when sending slide channel mails. Sending without layouting.' % (notif_layout))
else:
# could be great to use _notify_prepare_template_context someday
template_ctx = {
'message': self.env['mail.message'].sudo().new(dict(body=mail_values['body_html'], record_name=self.channel_id.name)),
'model_description': self.env['ir.model']._get('website_slides.slide_channel').display_name,
'model_description': self.env['ir.model']._get('slide.channel').display_name,
'record': slide_channel_partner,
'company': self.env.company,
'signature': self.channel_id.user_id.signature,
}
body = template._render(template_ctx, engine='ir.qweb', minimal_qcontext=True)
mail_values['body_html'] = self.env['mail.render.mixin']._replace_local_links(body)
@@ -22,7 +22,7 @@
<field name="attachment_ids" widget="many2many_binary"/>
</group>
<group>
<field name="template_id" label="Use template"/>
<field name="template_id" label="Use template" context="{'default_model': 'slide.channel.partner'}"/>
</group>
</group>
</group>
@@ -54,7 +54,6 @@ class WebsiteSlidesSurvey(WebsiteSlides):
# Create survey first as certification slide needs a survey_id (constraint)
post['survey_id'] = request.env['survey.survey'].create({
'title': post['survey']['title'],
'background_image': post['image_1920'],
'questions_layout': 'page_per_question',
'is_attempts_limited': True,
'attempts_limit': 1,
@@ -4,23 +4,20 @@
<div>
<form class="clearfix">
<div class="row">
<div class="col" id="o_wslides_js_slide_upload_left_column">
<div id="o_wslides_js_slide_upload_left_column" class="col-md-6">
<div class="form-group">
<label for="certification_id" class="col-form-label col-md-3">Certification</label>
<label for="certification_id" class="col-form-label">Certification</label>
<input class="form-control" id="certification_id" required="required"/>
</div>
<div class="form-group">
<label for="upload" class="col-form-label">Choose an Image</label>
<input id="upload" class="form-control" data-prevent-onchange='true' name="file" accept="image/*" type="file" required="required"/>
</div>
<t t-call="website.slide.upload.modal.common"/>
<canvas id="data_canvas" class="d-none"></canvas>
</div>
<div id="o_wslides_js_slide_upload_preview_column" class="col-md-6 d-none">
<div class="img-thumbnail">
<div class="o_slide_preview">
<img src="/website_slides/static/src/img/document.png" id="slide-image" title="Content Preview" alt="Content Preview" class="img-fluid"/>
<div id="o_wslides_js_slide_upload_preview_column" class="col-md-6">
<div class="img-thumbnail h-100">
<div class="o_slide_tutorial p-3">
<div class="h5">How to upload a certification on your course?</div>
<div class="mx-3 my-4">You can create your certification from here or use an existing one. Once your certification is created, you can still edit it in backend.</div>
</div>
</div>
</div>
@@ -6,12 +6,10 @@
<field name="inherit_id" ref="website_slides.view_slide_channel_form"/>
<field name="arch" type="xml">
<xpath expr="//span[@name='members_done_count_label']" position="replace">
<field name="nbr_certification" invisible="1"/>
<span class="o_stat_text" attrs="{'invisible': [('nbr_certification', '>', 0)]}">Finished</span>
<span class="o_stat_text" attrs="{'invisible': [('nbr_certification', '=', 0)]}">Certified</span>
</xpath>
<xpath expr="//field[@name='nbr_webpage']" position="after">
<field name="nbr_certification" string="Certifications"/>
</xpath>
<xpath expr="//field[@name='slide_type']" position="after">
<field name="survey_id"/>
</xpath>