[FIX] website_slides: Fix add pdf and image content

When uploading an image or a pdf content to a course, it was not uploaded. When
viewing it, as nothing was uploaded:
- for image, a default one was displayed
- for pdf, it kept loading for ever
This fixes the problem.

We also add tests checking the upload of a new image and a pdf.

Technical note: The file to upload is converted to base64 when the upload input
is changed and stored in file.data. When the user submit the file, it is that
preprocessed data that is being sent to the server. Following 542cb1dc,
nothing was assigned any more to file.data when the input field was changed so
submitted file was then always empty:
- for image, before this fix, undefined was always assigned to file.data
because the "dataURL" was split in 1 element and the second element was taken
for file.data. The submitted file was then always empty.
- for pdf, before this fix, only the preview was generated and file.data was
not assigned. Before that commit (542cb1dc), the file was read twice, one for
assigning the base64 content of the file to file.data and one to render the
preview. Here we read it only once in base64 to store it in file.data and then
convert it to binary for computing the preview using atob.

Task-3178726

closes odoo/odoo#117293

X-original-commit: 8a7453d9fc43cde13e2e07697e0b0b104a537b93
Signed-off-by: Stéphane Debauche (std) <std@odoo.com>
This commit is contained in:
Pierre-Yves Dufays
2023-03-31 10:53:16 +02:00
parent 6ca723dee4
commit 7710b82c26
3 changed files with 142 additions and 10 deletions
@@ -570,16 +570,13 @@ var SlideUploadDialog = Dialog.extend({
if (isImage) {
preview.src = dataURL;
}
this.file.data = dataURL.split(',', 1)[1];
this.file.data = dataURL.split(',', 2)[1];
this._showPreviewColumn();
});
} else {
var ArrayReader = new FileReader();
this.set('can_submit_form', false);
// file read as ArrayBuffer for pdfjsLib get_Document API
ArrayReader.readAsArrayBuffer(file);
ArrayReader.onload = async function (evt) {
var buffer = evt.target.result;
utils.getDataURLFromFile(file).then(async dataURL => {
this.file.data = dataURL.split(',', 2)[1];
/**
* The following line fixes pdfjsLib 'Util' global variable.
* This is (most likely) related to #32181 which lazy loads most assets.
@@ -598,7 +595,8 @@ var SlideUploadDialog = Dialog.extend({
* cause much harm.
*/
window.Util = window.pdfjsLib.Util;
const pdfTask = window.pdfjsLib.getDocument(new Uint8Array(buffer));
// pdf is stored in file.data in base64 and converted in binary (atob) to generate the preview
const pdfTask = window.pdfjsLib.getDocument({ data: atob(this.file.data) });
pdfTask.onPassword = () => {
this._alertDisplay(_t("You can not upload password protected file."));
this._fileReset();
@@ -608,7 +606,7 @@ var SlideUploadDialog = Dialog.extend({
const pdf = await pdfTask.promise;
self._formSetFieldValue('duration', (pdf.numPages || 0) * 5);
const page = await pdf.getPage(1)
const page = await pdf.getPage(1);
var viewport = page.getViewport({scale: 1});
var canvas = document.getElementById('data_canvas');
var context = canvas.getContext('2d');
@@ -625,7 +623,7 @@ var SlideUploadDialog = Dialog.extend({
}
loaded = true;
self._showPreviewColumn();
};
});
}
if (!preventOnchange) {
@@ -98,7 +98,9 @@ wTourUtils.registerWebsitePreviewTour('course_publisher_standard', {
}, {
content: "eLearning: use breadcrumb to go back to channel",
trigger: 'iframe .o_wslides_course_nav a:contains("Déboulonnate")',
}]
}],
slidesTourTools.addImageToSection('Introduction', 'Overview', true),
slidesTourTools.addPdfToSection('Introduction', 'Exercise', true),
// [
// {
// content: 'eLearning: move new course inside introduction',
@@ -1,5 +1,13 @@
/** @odoo-module **/
import utils from 'web.utils';
/*
* Constant
*/
const testPngImage = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAD0lEQVQIHQEEAPv/AIdaewLIAV0IjhGPAAAAAElFTkSuQmCC';
const testPdf = 'JVBERi0xLjEKJWPDtsO2bW1lbnQKMSAwIG9iago8PC9UeXBlIC9DYXRhbG9nCi9QYWdlcyAyIDAgUgo+PgplbmRvYmoKMiAwIG9iago8PC9UeXBlIC9QYWdlcwovS2lkcyBbMyAwIFJdCi9Db3VudCAxCi9NZWRpYUJveCBbMCAwIDIxMCAyOTddCj4+CmVuZG9iagozIDAgb2JqCjw8L1R5cGUgL1BhZ2UKL1BhcmVudCAyIDAgUgovUmVzb3VyY2VzCjw8L0ZvbnQKPDwvRjEKPDwvVHlwZSAvRm9udAovU3VidHlwZSAvVHlwZTEKL0Jhc2VGb250IC9UaW1lcy1Sb21hbgo+Pgo+Pgo+PgovQ29udGVudHMgNCAwIFIKPj4KZW5kb2JqCjQgMCBvYmoKPDwvTGVuZ3RoIDQ4Pj4Kc3RyZWFtCkJUCi9GMSA0OCBUZgo1MCAxMzAgVGQKKFRFU1QpIFRqCkVUCmVuZHN0cmVhbQplbmRvYmoKeHJlZgowIDUKMDAwMDAwMDAwMCA2NTUzNSBmCjAwMDAwMDAwMjEgMDAwMDAgbgowMDAwMDAwMDY5IDAwMDAwIG4KMDAwMDAwMDE0OSAwMDAwMCBuCjAwMDAwMDAyOTggMDAwMDAgbgp0cmFpbGVyCjw8L1Jvb3QgMSAwIFIKL1NpemUgNQo+PgpzdGFydHhyZWYKMzgxCiUlRU9G';
/*
* PUBLISHER / CONTENT CREATION
*/
@@ -87,6 +95,128 @@ var addArticleToSection = function (sectionName, pageName, backend) {
}];
};
const fillInFileInput = async (input, name, type, content) => {
const blob = await (await fetch(`data:${type};base64,${content}`)).blob();
const file = new File([blob], name, { type: type});
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
input.files = dataTransfer.files;
input.dispatchEvent(new Event('change', { bubbles: true }));
};
const compareBase64Content = async (url, name, type, expectedContent) => {
const blob = await (await fetch(url)).blob();
const file = new File([blob], name, { type: type});
const actualContent = await utils.getDataURLFromFile(file).then(dataURL => dataURL.split(',', 2)[1]);
return expectedContent === actualContent;
};
/**
* Test the upload of an image file as a new content and especially the binary content of the uploaded image.
*/
const addImageToSection = (sectionName, pageName, backend) => {
const prefix = backend ? 'iframe ' : '';
return [
{
content: 'eLearning: add content to section',
trigger: `${prefix}div.o_wslides_slide_list_category_header:contains("${sectionName}") a:contains("Add Content")`,
}, {
content: 'eLearning: click on image',
trigger: `${prefix}a[data-slide-category=infographic]`,
}, {
content: 'eLearning: choose upload from device',
trigger: `${prefix}#source_type_local_file`,
}, {
content: 'eLearning: load image',
trigger: 'body',
run: async () => {
const uploadInput = backend ?
document.getElementsByTagName('iframe')[0].contentWindow.document.getElementById('upload') :
document.getElementById('upload');
await fillInFileInput(uploadInput, 'Overview.png', 'image/png', testPngImage);
},
}, {
content: 'eLearning: ensure that the preview is displayed which means that data is loaded and can be submitted',
trigger: `${prefix}#slide-image[src*="data:"]`,
}, {
content: 'eLearning: create and publish slide',
trigger: `${prefix}footer.modal-footer button:contains("Publish")`,
}, {
content: 'eLearning: launch content',
trigger: `${prefix}a.o_wslides_js_slides_list_slide_link:contains("Overview")`,
}, {
content: 'eLearning: check uploaded image presence and perform comparison',
trigger: (backend ? 'iframe ' : '') + '.o_wslides_fs_player img',
run: async () => {
const baseElement = backend ? document.querySelector('iframe').contentDocument : document;
const img = baseElement.querySelector('.o_wslides_fs_player img');
if (await compareBase64Content(img.getAttribute('src'), 'Overview.png', 'image/png', testPngImage)) {
img.classList.add('o_wslides_tour_img_upload_success');
}
}
}, {
content: 'eLearning: check uploaded image content',
trigger: `${prefix}.o_wslides_fs_player img.o_wslides_tour_img_upload_success`,
},
{
content: 'eLearning: back to course',
trigger: `${prefix}.o_wslides_fs_sidebar_header a:contains("Déboulonnate")`,
}];
};
/**
* Test the upload of a pdf file as a new content and especially the binary content of the uploaded pdf.
*/
const addPdfToSection = function (sectionName, pageName, backend) {
const prefix = backend ? 'iframe ' : '';
return [
{
content: 'eLearning: add content to section',
trigger: `${prefix}div.o_wslides_slide_list_category_header:contains("${sectionName}") a:contains("Add Content")`,
}, {
content: 'eLearning: click on document',
trigger: `${prefix}a[data-slide-category=document]`,
}, {
content: 'eLearning: choose upload from device',
trigger: `${prefix}#source_type_local_file`,
}, {
content: 'eLearning: load pdf',
trigger: 'body',
run: async () => {
const upload = backend ?
document.getElementsByTagName('iframe')[0].contentWindow.document.getElementById('upload') :
document.getElementById('upload');
await fillInFileInput(upload, 'Exercise.pdf', 'application/pdf', testPdf);
},
}, {
content: 'eLearning: ensure that the preview is displayed which means that data is loaded and can be submitted',
trigger: `${prefix}#slide-image[src*="data:"]`,
}, {
content: 'eLearning: create and publish slide',
trigger: `${prefix}footer.modal-footer button:contains("Publish")`,
}, {
content: 'eLearning: launch content',
trigger: `${prefix}a.o_wslides_js_slides_list_slide_link:contains("Exercise")`,
}, {
content: 'eLearning: check uploaded pdf presence and perform comparison',
trigger: (backend ? 'iframe ' : '') + '.o_wslides_fs_content',
run: async () => {
const baseElement = backend ? document.querySelector('iframe').contentDocument : document;
const pdfViewer = baseElement.querySelector('iframe.o_wslides_iframe_viewer').contentDocument
.querySelector('#PDFSlideViewer');
if (await compareBase64Content(pdfViewer.getAttribute('data-slideurl'), 'Exercise.pdf', 'application/pdf', testPdf)) {
baseElement.querySelector('.o_wslides_fs_content').classList.add('o_wslides_tour_pdf_upload_success');
}
},
}, {
content: 'eLearning: check uploaded pdf content',
trigger: `${prefix}.o_wslides_fs_content.o_wslides_tour_pdf_upload_success`,
}, {
content: 'eLearning: back to course',
trigger: `${prefix}.o_wslides_fs_sidebar_header a:contains("Déboulonnate")`,
}];
};
var addExistingCourseTag = function (backend = false) {
return [
{
@@ -146,6 +276,8 @@ var addNewCourseTag = function (courseTagName, backend) {
export default {
addSection: addSection,
addImageToSection: addImageToSection,
addPdfToSection: addPdfToSection,
addVideoToSection: addVideoToSection,
addArticleToSection: addArticleToSection,
addExistingCourseTag: addExistingCourseTag,