[REF] website_slides: refactor the slide archive modal on frontend

Purpose
=======

This commits refactors the slide archiving confirmation modal to make it a
standard Dialog (remove global selectors, remove most of useless xml, ...).

It also adds a new slide archive route to allow channel publishers to archive
slides in sudo mode. Indeed ACLs only website_publishers can write on slide
model. In some case people without real rights may update slides.

Commit linked to task ID 1942852 and PR #31394.
This commit is contained in:
Aurélien Warnon
2019-02-26 16:07:16 +00:00
committed by Thibault Delavallée
parent 362271f35a
commit 5db9ed0391
9 changed files with 120 additions and 119 deletions
+11
View File
@@ -559,6 +559,17 @@ class WebsiteSlides(WebsiteProfile):
slide.invalidate_cache()
return slide.read(['likes', 'dislikes', 'user_vote'])[0]
@http.route('/slides/slide/archive', type='json', auth='user', website=True)
def slide_archive(self, slide_id):
""" This route allows channel publishers to archive slides.
It has to be done in sudo mode since only website_publishers can write on slides in ACLs """
slide = request.env['slide.slide'].browse(int(slide_id))
if slide.channel_id.can_publish:
slide.sudo().active = False
return True
return False
@http.route(['/slides/slide/send_share_email'], type='json', auth='user', website=True)
def slide_send_share_email(self, slide_id, email):
slide = request.env['slide.slide'].browse(int(slide_id))
@@ -16,9 +16,9 @@ odoo.define('website_slides.slideslist', function (require) {
},
start: function (){
this._super.apply(this,arguments);
this.slideCount = $('li.content-slide').length;
this.slideCount = $('li.o_wslides_content_slide').length;
//Change links HREF to fullscreen mode for SEO
var links = $(".link-to-slide");
var links = $('.o_wslides_course_slide_link');
for (var i = 0; i < links.length; i++){
$(links[i]).attr('href', $(links[i]).attr('href') + "?fullscreen=1");
}
@@ -1,88 +0,0 @@
odoo.define('website_slides.delete.slide', function (require) {
var sAnimations = require('website.content.snippets.animation');
var core = require('web.core');
var Widget = require('web.Widget');
var _t = core._t;
var QWeb = core.qweb;
var DeleteSlideDialog = Widget.extend({
template: 'website.slide.delete.slide',
events: {
'hidden.bs.modal': 'destroy',
'click button[data-dismiss="modal"]': '_cancel',
'click button.delete': '_delete'
},
/**
* @override
* @param {Object} el
* @param {number} channel_id
*/
init: function (el, slideID) {
this._super(el, slideID);
this.slide_id = parseInt(slideID, 10);
},
/**
* @override
*/
start: function () {
this.$el.modal({
backdrop: 'static'
});
return this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
_delete: function (ev) {
var self = this;
// TO FIX: CallBack is not executed
$('[slide_id='+this.slide_id+']').remove();
this._rpc({
model: 'slide.slide',
method: 'unlink',
args: [[self.slide_id]],
}).then(function () {
$('[slide='+this.slide_id+']').remove();
});
},
/**
* @override
*/
_cancel: function () {
this.trigger('cancel');
}
});
sAnimations.registry.websiteSlidesDeleteSlide = sAnimations.Class.extend({
selector: '.oe_slide_js_delete_slide',
xmlDependencies: ['/website_slides/static/src/xml/website_slides_upload.xml'],
read_events: {
'click': '_onDeleteSlideClick',
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
_openDialog: function (slideID) {
new DeleteSlideDialog(this, slideID).appendTo(document.body);
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
* @param {Event} ev
*/
_onDeleteSlideClick: function (ev) {
var target = $(ev.currentTarget);
this._openDialog(target.attr('slide_id'));
},
});
return DeleteSlideDialog;
});
@@ -0,0 +1,91 @@
odoo.define('website_slides.slide.archive', function (require) {
'use strict';
var sAnimations = require('website.content.snippets.animation');
var Dialog = require('web.Dialog');
var core = require('web.core');
var _t = core._t;
var SlideArchiveDialog = Dialog.extend({
template: 'slides.slide.archive',
/**
* @override
*/
init: function (parent, options) {
options = _.defaults(options || {}, {
title: _t('Archive Slide'),
size: 'medium',
buttons: [{
text: _t('Archive'),
classes: 'btn-primary',
click: this._onClickArchive.bind(this)
}, {
text: _t('Cancel'),
close: true
}]
});
this.$slideTarget = options.slideTarget;
this.slideId = this.$slideTarget.attr('slide_id');
this._super(parent, options);
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* Calls 'archive' on slide controller and then visually removes the slide dom element
*/
_onClickArchive: function () {
var self = this;
this._rpc({
route: '/slides/slide/archive',
params: {
slide_id: this.slideId
},
}).then(function () {
self.$slideTarget.closest('.o_wslides_content_slide').remove();
self.close();
});
}
});
sAnimations.registry.websiteSlidesSlideArchive = sAnimations.Class.extend({
selector: '.o_wslides_js_slide_archive',
xmlDependencies: ['/website_slides/static/src/xml/slide_management.xml'],
read_events: {
'click': '_onArchiveSlideClick',
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
_openDialog: function ($slideTarget) {
new SlideArchiveDialog(this, {slideTarget: $slideTarget}).open();
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
* @param {Event} ev
*/
_onArchiveSlideClick: function (ev) {
ev.preventDefault();
var $slideTarget = $(ev.currentTarget);
this._openDialog($slideTarget);
},
});
return {
slideArchiveDialog: SlideArchiveDialog,
websiteSlidesSlideArchive: sAnimations.registry.websiteSlidesSlideArchive
};
});
@@ -542,7 +542,7 @@ var SlideUploadDialog = Dialog.extend({
//Quick and really dirty fix for reordering issues
if (data.channel_type === 'training' && self.categoryID){
var categoryElement = $('ul[category_id='+self.categoryID+']');
$('<li hidden class="content-slide" slide_id="'+data.slide_id+'" category_id="'+self.categoryID+'">temp</li>').appendTo(categoryElement);
$('<li hidden class="o_wslides_content_slide" slide_id="'+data.slide_id+'" category_id="'+self.categoryID+'">temp</li>').appendTo(categoryElement);
self._reorderSlidesSequence();
}
window.location = data.url;
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="slides.slide.archive">
<div>
<p>Are you sure you want to archive this slide ?</p>
</div>
</t>
</templates>
@@ -208,28 +208,6 @@
</div>
</t>
<t t-name="website.slide.delete.slide">
<div role="dialog" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<header class="modal-header">
<h3 class="modal-title">Delete Slide</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>
</header>
<main class="modal-body">
<div>
<p>Are you sure you want to remove this slide ? </p>
</div>
</main>
<footer class="modal-footer">
<button type="button" class="btn btn-primary delete" data-dismiss="modal">Delete</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
</footer>
</div>
</div>
</div>
</t>
<t t-name="website.slide.add.section">
<div role="dialog" class="modal" tabindex="-1">
<div class="modal-dialog">
+1 -1
View File
@@ -18,7 +18,7 @@
<script type="text/javascript" src="/website_slides/static/src/js/slides_share.js"/>
<script type="text/javascript" src="/website_slides/static/src/js/slides_upload.js"/>
<script type="text/javascript" src="/website_slides/static/src/js/slides_category.js"/>
<script type="text/javascript" src="/website_slides/static/src/js/slides_delete_slide.js"/>
<script type="text/javascript" src="/website_slides/static/src/js/slides_slide_archive.js"/>
<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_progress_bar.js"/>
<script type="text/javascript" src="/website_slides/static/src/js/slides_course_fullscreen_player.js"/>
@@ -270,7 +270,8 @@
</div>
<ul t-attf-category_id="#{category.id}" >
<t t-foreach="category.slide_ids" t-as="slide">
<li t-att-index="j" t-attf-slide_id="#{slide.id}" t-attf-category_id="#{category.id}" t-attf-class="#{'o_wslides_content_slide slide-draggable d-flex justify-content-between align-items-center p-2' if channel.user_id == user else 'content-slide d-flex justify-content-between align-items-center p-2'}">
<li t-att-index="j" t-attf-slide_id="#{slide.id}" t-attf-category_id="#{category.id}"
t-att-class="'o_wslides_content_slide %s d-flex justify-content-between align-items-center p-2' % ('slide-draggable' if channel.user_id == user else '')}">
<div class="ml-2">
<i t-if="channel.user_id == user" class="fa fa-arrows mr-2 text-muted"></i>
<i t-if="slide.slide_type == 'document'" class="fa fa-file-pdf-o mr-2 text-muted"></i>
@@ -280,14 +281,14 @@
<i t-if="slide.slide_type == 'webpage'" class="fa fa-file-text mr-2 text-muted"></i>
<i t-if="slide.slide_type == 'quiz'" class="fa fa-question-circle mr-2 text-muted"></i>
<i t-if="slide.slide_type == 'certification'" class="fa fa-trophy mr-2 text-muted"></i>
<a class="link-to-slide" t-attf-href="/slides/slide/#{slug(slide)}"><span t-field="slide.name"/></a>
<a class="o_wslides_course_slide_link" t-attf-href="/slides/slide/#{slug(slide)}"><span t-field="slide.name"/></a>
</div>
<div class="o_wslides_content_slide_controls mr-2">
<i t-if="not slide.id in user_progress or not user_progress[slide.id].completed" class="check-done fa fa-check-circle text-muted mr-1"></i>
<i t-if="slide.id in user_progress and user_progress[slide.id].completed" class="check-done text-primary fa fa-check-circle mr-1"></i>
<a t-if="channel.user_id == user and not slide.slide_type == 'webpage'" t-attf-href="/web#id=#{slide.id}&amp;action=#{slide_action}&amp;model=slide.slide&amp;view_type=form"><i class="fa fa-pencil text-muted mr-1"></i></a>
<a t-if="channel.user_id == user and slide.slide_type == 'webpage'" t-attf-href="/slides/slide/#{slug(slide)}?enable_editor=1"><i class="fa fa-pencil text-muted mr-1"></i></a>
<i t-if="channel.user_id == user" t-attf-slide_id="#{slide.id}" class="fa fa-trash text-muted oe_slide_js_delete_slide"></i>
<a t-if="channel.can_publish and not slide.slide_type == 'webpage'" t-attf-href="/web#id=#{slide.id}&amp;action=#{slide_action}&amp;model=slide.slide&amp;view_type=form"><i class="fa fa-pencil text-muted mr-1"></i></a>
<a t-if="channel.can_publish and slide.slide_type == 'webpage'" t-attf-href="/slides/slide/#{slug(slide)}?enable_editor=1"><i class="fa fa-pencil text-muted mr-1"></i></a>
<i t-if="channel.can_publish" t-attf-slide_id="#{slide.id}" class="fa fa-trash text-muted o_wslides_js_slide_archive"></i>
</div>
</li>
<t t-set="j" t-value="j+1"/>