summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThibault Delavallée <tde@odoo.com>2019-07-25 10:50:35 +0000
committerThibault Delavallée <tde@odoo.com>2019-08-05 08:40:33 +0000
commit98bdb241ded7beb4a2d958387ad2d5ec620feb3e (patch)
treeb22971651dbea8bf1dd33f0a2640108c499b77e6
parent665d0dfa67f549d42f0f275e3f6da9944dbc81c7 (diff)
[IMP] website_slides: add an overview (kanban view) on course model
PURPOSE eLearning should have its own application in manager. Indeed this is becoming a big application and having it embedded inside Website application is not enough anymore. It should also hold updated and easy-to-use menus, navigation, actions and views in backend. SPECIFICATIONS Add course (slide.channel) kanban view. Each tile should contain * Title of the course (make it big, o_primary) * Tags below the title * Primary button "New Lesson" * Statistics : * Attendees * Running (progress != 100%) * (if one content is certificate) Certified or Finished * See * v1: https://drive.google.com/a/odoo.com/file/d/13K8FmkbwmBG4JQ5gH3Q5T8OlWPs3blcu/view?usp=drivesdk With FP remarks https://drive.google.com/a/odoo.com/file/d/1mb7LUT0VUNs5fMcKB9c0Bfaqk2-XFFuu/view?usp=drivesdk * remove globe, type and visibility of course, lessen padding between rating views and watch time; * for rating, instead of 8.00 => 5 reviews (4.5/5); * replace "View Lesson" / "Invite" by "View Course" => Send to the front-end * In the menu of the card, add: * Invite (same behavior than before, when it was a primary btn) * click on x Contents: land on the contents, group by category with sequence * re-order of the 3 infos/Stats at the bottom of the card: 1. Content, 2. Attendee, 3. Certified * + number: font-weight: 500 + font-size 1.2em * + labels: remove text-uppercase, + text-color: #666666 * + the "whole box" is clickable LINKS Task 1978729 PR #35061 Co-Authored-By: Thibault Delavallée <tde@odoo.com> Co-Authored-By: Jérémy Hennecart <jeh@odoo.com>
-rw-r--r--addons/website_slides/__manifest__.py1
-rw-r--r--addons/website_slides/models/res_config_settings.py4
-rw-r--r--addons/website_slides/models/slide_channel.py64
-rw-r--r--addons/website_slides/static/src/scss/slide_channel_views.scss7
-rw-r--r--addons/website_slides/views/assets.xml1
-rw-r--r--addons/website_slides/views/res_config_settings_views.xml6
-rw-r--r--addons/website_slides/views/res_partner_views.xml5
-rw-r--r--addons/website_slides/views/slide_channel_views.xml96
-rw-r--r--addons/website_slides_survey/views/slide_channel_views.xml15
9 files changed, 182 insertions, 17 deletions
diff --git a/addons/website_slides/__manifest__.py b/addons/website_slides/__manifest__.py
index 5373f5bd6de..1da8adc4eee 100644
--- a/addons/website_slides/__manifest__.py
+++ b/addons/website_slides/__manifest__.py
@@ -29,6 +29,7 @@ Featuring
'views/res_config_settings_views.xml',
'views/res_partner_views.xml',
'views/rating_rating_views.xml',
+ 'views/res_partner_views.xml',
'views/slide_question_views.xml',
'views/slide_slide_views.xml',
'views/slide_channel_partner_views.xml',
diff --git a/addons/website_slides/models/res_config_settings.py b/addons/website_slides/models/res_config_settings.py
index fa9e7508afc..8085cd407c8 100644
--- a/addons/website_slides/models/res_config_settings.py
+++ b/addons/website_slides/models/res_config_settings.py
@@ -8,6 +8,6 @@ class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
website_slide_google_app_key = fields.Char(related='website_id.website_slide_google_app_key', readonly=False)
- module_website_sale_slides = fields.Boolean(string="Sell courses")
- module_website_slides_forum = fields.Boolean(string="Forum on Courses")
+ module_website_sale_slides = fields.Boolean(string="Sell on eCommerce")
+ module_website_slides_forum = fields.Boolean(string="Forum")
module_website_slides_survey = fields.Boolean(string="Certifications")
diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py
index 4496c59b391..78c32b55b74 100644
--- a/addons/website_slides/models/slide_channel.py
+++ b/addons/website_slides/models/slide_channel.py
@@ -73,6 +73,7 @@ class ChannelUsersRelation(models.Model):
def _post_completion_hook(self):
pass
+
class Channel(models.Model):
""" A channel is a container of slides. """
_name = 'slide.channel'
@@ -96,6 +97,7 @@ class Channel(models.Model):
string="Course type", default="documentation", 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')
tag_ids = fields.Many2many(
'slide.channel.tag', 'slide_channel_tag_rel', 'channel_id', 'tag_id',
string='Tags', help='Used to categorize and filter displayed channels/courses')
@@ -151,6 +153,7 @@ class Channel(models.Model):
'res.partner', 'slide_channel_partner', 'channel_id', 'partner_id',
string='Members', help="All members of the channel.", context={'active_test': False})
members_count = fields.Integer('Attendees count', compute='_compute_members_count')
+ members_done_count = fields.Integer('Attendees Done Count', compute='_compute_members_done_count')
is_member = fields.Boolean(string='Is Member', compute='_compute_is_member')
channel_partner_ids = fields.One2many('slide.channel.partner', 'channel_id', string='Members Information', groups='website.group_website_publisher')
upload_group_ids = fields.Many2many(
@@ -184,6 +187,13 @@ class Channel(models.Model):
for channel in self:
channel.members_count = data.get(channel.id, 0)
+ @api.depends('channel_partner_ids.channel_id', 'channel_partner_ids.completed')
+ def _compute_members_done_count(self):
+ read_group_res = self.env['slide.channel.partner'].sudo().read_group(['&', ('channel_id', 'in', self.ids), ('completed', '=', True)], ['channel_id'], 'channel_id')
+ data = dict((res['channel_id'][0], res['channel_id_count']) for res in read_group_res)
+ for channel in self:
+ channel.members_done_count = data.get(channel.id, 0)
+
@api.depends('channel_partner_ids.partner_id')
@api.model
def _compute_is_member(self):
@@ -366,21 +376,34 @@ class Channel(models.Model):
# Business / Actions
# ---------------------------------------------------------
- def action_redirect_to_members(self):
- action = self.env.ref('website_slides.slide_channel_partner_action').read()[0]
- action['view_mode'] = 'tree'
- action['domain'] = [('channel_id', 'in', self.ids)]
+ def action_redirect_to_members(self, state=None):
+ action = self.env.ref('website_slides.res_partner_action_slide_channel').read()[0]
+ action['context'] = {'active_test': False}
+ if state == 'running':
+ action['domain'] = [('id', 'in', self.env['slide.channel.partner'].sudo().search([
+ ('channel_id', 'in', self.ids),
+ ('completed', '=', False)
+ ]).mapped('partner_id').ids)]
+ elif state == 'completed':
+ action['domain'] = [('id', 'in', self.env['slide.channel.partner'].sudo().search([
+ ('channel_id', 'in', self.ids),
+ ('completed', '=', True)
+ ]).mapped('partner_id').ids)]
+ else:
+ action['domain'] = [('id', 'in', self.mapped('partner_ids').ids)]
if len(self) == 1:
- action['context'] = {'default_channel_id': self.id}
-
+ action['display_name'] = _('Attendees of %s') % self.name
+ action['context'] = {'active_test': False, 'default_channel_id': self.id}
return action
- def action_channel_invite(self):
- self.ensure_one()
+ def action_redirect_to_running_members(self):
+ return self.action_redirect_to_members('running')
- if self.enroll != 'invite':
- raise UserError(_("You cannot send invitations for channels that are not set as 'invite'."))
+ def action_redirect_to_done_members(self):
+ return self.action_redirect_to_members('completed')
+ def action_channel_invite(self):
+ self.ensure_one()
template = self.env.ref('website_slides.mail_template_slide_channel_invite', raise_if_not_found=False)
local_context = dict(
@@ -474,6 +497,27 @@ class Channel(models.Model):
if removed_channel_partner_domain:
self.env['slide.channel.partner'].sudo().search(removed_channel_partner_domain).unlink()
+ def action_view(self):
+ return {
+ 'type': 'ir.actions.act_window',
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'slide.channel',
+ 'res_id': self.id,
+ }
+
+ def action_view_slides(self):
+ action = self.env.ref('website_slides.slide_slide_action').read()[0]
+ action['context'] = {'default_channel_id': self.id}
+ action['domain'] = [('channel_id', "=", self.id)]
+ return action
+
+ def action_view_ratings(self):
+ action = self.env.ref('website_slides.rating_rating_action_slide_channel').read()[0]
+ action['name'] = _('Rating of %s') % (self.name)
+ action['domain'] = [('res_id', 'in', self.ids)]
+ return action
+
# ---------------------------------------------------------
# Rating Mixin API
# ---------------------------------------------------------
diff --git a/addons/website_slides/static/src/scss/slide_channel_views.scss b/addons/website_slides/static/src/scss/slide_channel_views.scss
new file mode 100644
index 00000000000..3bd8203c4eb
--- /dev/null
+++ b/addons/website_slides/static/src/scss/slide_channel_views.scss
@@ -0,0 +1,7 @@
+$o-kanban-large-record-width: 400px;
+
+.o_kanban_view.o_slide_channel_kanban {
+ .o_kanban_record {
+ width: $o-kanban-large-record-width
+ }
+}
diff --git a/addons/website_slides/views/assets.xml b/addons/website_slides/views/assets.xml
index 1c990e7ad50..62129e58e6b 100644
--- a/addons/website_slides/views/assets.xml
+++ b/addons/website_slides/views/assets.xml
@@ -4,6 +4,7 @@
<template id="assets_backend" name="Slides Backend Assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" type="text/scss" href="/website_slides/static/src/scss/rating_rating_views.scss"/>
+ <link rel="stylesheet" type="text/scss" href="/website_slides/static/src/scss/slide_channel_views.scss"/>
</xpath>
</template>
diff --git a/addons/website_slides/views/res_config_settings_views.xml b/addons/website_slides/views/res_config_settings_views.xml
index 6925fbc33b5..3e5a64010f0 100644
--- a/addons/website_slides/views/res_config_settings_views.xml
+++ b/addons/website_slides/views/res_config_settings_views.xml
@@ -38,7 +38,7 @@
<div class="o_setting_right_pane">
<label for="module_website_slides_forum"/>
<div class="text-muted">
- Allow Forum on Courses
+ Create a community and let the members help each others
</div>
</div>
</div>
@@ -50,7 +50,7 @@
<div class="o_setting_right_pane">
<label for="module_website_slides_survey"/>
<div class="text-muted">
- Allow to take certifications
+ Evaluate your students and certify them
</div>
</div>
</div>
@@ -62,7 +62,7 @@
<div class="o_setting_right_pane">
<label for="module_website_sale_slides"/>
<div class="text-muted">
- Sell courses on your website
+ Generate revenues thanks to your courses
</div>
</div>
</div>
diff --git a/addons/website_slides/views/res_partner_views.xml b/addons/website_slides/views/res_partner_views.xml
index a5e7161c00b..5455a0195f5 100644
--- a/addons/website_slides/views/res_partner_views.xml
+++ b/addons/website_slides/views/res_partner_views.xml
@@ -36,4 +36,9 @@
</field>
</record>
+ <record id="res_partner_action_slide_channel" model="ir.actions.act_window">
+ <field name="name">Attendees</field>
+ <field name="res_model">res.partner</field>
+ <field name="view_mode">kanban,tree,form</field>
+ </record>
</data></odoo>
diff --git a/addons/website_slides/views/slide_channel_views.xml b/addons/website_slides/views/slide_channel_views.xml
index edd662541e5..898599ede25 100644
--- a/addons/website_slides/views/slide_channel_views.xml
+++ b/addons/website_slides/views/slide_channel_views.xml
@@ -218,11 +218,103 @@
</field>
</record>
+ <record id="slide_channel_view_kanban" model="ir.ui.view">
+ <field name="name">slide.channel.view.kanban</field>
+ <field name="model">slide.channel</field>
+ <field name="arch" type="xml">
+ <kanban string="eLearning Overview" class="o_emphasize_colors o_kanban_dashboard o_slide_channel_kanban breadcrumb_item active" edit="false">
+ <field name="color"/>
+ <field name="website_published"/>
+ <templates>
+ <t t-name="kanban-box">
+ <div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click">
+ <div class="o_dropdown_kanban dropdown">
+ <a role="button" class="dropdown-toggle o-no-caret btn" data-toggle="dropdown" href="#" aria-label="Dropdown menu" title="Dropdown menu">
+ <span class="fa fa-ellipsis-v" aria-hidden="false"/>
+ </a>
+ <div class="dropdown-menu" role="menu">
+ <ul class="oe_kanban_colorpicker" data-field="color"/>
+ <t t-if="widget.deletable">
+ <a class="dropdown-item" role="menuitem" type="delete">Delete</a>
+ </t>
+ <a class="dropdown-item" role="menuitem" type="edit">
+ Edit
+ </a>
+ <a class="dropdown-item" name="action_view_slides" role="menuitem" type="object">
+ Lessons
+ </a>
+ <a class="dropdown-item" name="action_channel_invite" role="menuitem" type="object">
+ Invite
+ </a>
+ </div>
+ </div>
+ <div class="o_kanban_card_header">
+ <div class="o_kanban_card_header_title mb16">
+ <div class="o_primary">
+ <a name="action_view" type="object" class="mr-auto">
+ <span><field name="name" class="o_primary"/></span>
+ </a>
+ </div>
+ <div t-if="record.tag_ids">
+ <field name="tag_ids" widget="many2many_tags"/>
+ </div>
+ </div>
+ </div>
+ <div class="container o_kanban_card_content mt0">
+ <div class="row">
+ <div class="col-6 o_kanban_primary_left">
+ <button class="btn btn-primary" name="open_website_url" type="object">View course</button>
+ </div>
+ <div class="col-6 o_kanban_primary_right">
+ <div class="d-flex">
+ <span class="mr-auto"><label for="rating_avg" class="mb0">Rating</label></span>
+ <a name="action_view_ratings" type="object">
+ <field name="rating_count"/><span class="ml-2">reviews</span> (<field name="rating_avg_stars"/>/5)
+ </a>
+ </div>
+ <div class="d-flex">
+ <span class="mr-auto"><label for="total_views" class="mb0">Views</label></span>
+ <field name="total_views"/>
+ </div>
+ <div class="d-flex">
+ <span class="mr-auto"><label for="total_time" class="mb0">Watch Time</label></span>
+ <field name="total_time" widget="float_time"/>
+ </div>
+ </div>
+ </div>
+ <div class="row mt3">
+ <div class="col-4 border-right">
+ <a name="action_view_slides" type="object" class="d-flex flex-column align-items-center">
+ <span class="font-weight-bold"><field name="total_slides"/></span>
+ <span class="text-secondary">Contents</span>
+ </a>
+ </div>
+ <div class="col-4 border-right">
+ <a name="action_redirect_to_members" type="object" class="d-flex flex-column align-items-center">
+ <span class="font-weight-bold"><field name="members_count"/></span>
+ <span class="text-secondary">Attendees</span>
+ </a>
+ </div>
+ <div class="col-4">
+ <a name="action_redirect_to_done_members" type="object" class="d-flex flex-column align-items-center">
+ <span class="font-weight-bold"><field name="members_done_count"/></span>
+ <span class="text-secondary">Finished</span>
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </t>
+ </templates>
+ </kanban>
+ </field>
+ </record>
+
<record id="slide_channel_action_overview" model="ir.actions.act_window">
<field name="name">eLearning Overview</field>
<field name="res_model">slide.channel</field>
- <field name="view_mode">tree,form</field>
- <field name="view_id" ref="slide_channel_view_tree"/>
+ <field name="view_mode">kanban,tree,form</field>
+ <field name="view_id" ref="slide_channel_view_kanban"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a course
diff --git a/addons/website_slides_survey/views/slide_channel_views.xml b/addons/website_slides_survey/views/slide_channel_views.xml
index e28b67997cf..2b93ef0dd2a 100644
--- a/addons/website_slides_survey/views/slide_channel_views.xml
+++ b/addons/website_slides_survey/views/slide_channel_views.xml
@@ -10,4 +10,19 @@
</xpath>
</field>
</record>
+
+ <record id="slide_channel_view_kanban" model="ir.ui.view">
+ <field name="name">slide.channel.view.kanban.inherit.survey</field>
+ <field name="model">slide.channel</field>
+ <field name="inherit_id" ref="website_slides.slide_channel_view_kanban"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='members_running_count']" position="after">
+ <field name="nbr_certification"/>
+ </xpath>
+ <xpath expr="//a[@name='action_redirect_to_done_members']/span" position="replace">
+ <t t-if="record.nbr_certification.raw_value"><span class="text-uppercase">Certified</span></t>
+ <t t-else=""><span class="text-uppercase">Finished</span></t>
+ </xpath>
+ </field>
+ </record>
</odoo>