Fix chrome issue when changing audio/video progress

- Undo changes in video plugin that replaces the
  /courses/document to app/courses/document
- Add "Accept-Ranges: bytes"

See CT#8462  BT#14020
pull/2458/head
jmontoyaa 8 years ago
parent f11e2ffc66
commit 11ffa3a29f
  1. 25
      main/inc/lib/document.lib.php
  2. 654
      main/inc/lib/javascript/ckeditor/plugins/video/dialogs/video.js
  3. 19
      main/template/default/layout/footer.js.tpl

@ -300,6 +300,9 @@ class DocumentManager
$filename = str_replace(',', '', $filename);
$sendFileHeaders = api_get_configuration_value('enable_x_sendfile_headers');
// Allows chrome to make videos and audios seekable
header('Accept-Ranges: bytes');
if ($forced) {
// Force the browser to save the file instead of opening it
if (isset($sendFileHeaders) &&
@ -323,7 +326,8 @@ class DocumentManager
header('Content-Transfer-Encoding: binary');
if (function_exists('ob_end_clean')) {
// Use ob_end_clean() to avoid weird buffering situations where file is sent broken/incomplete for download
// Use ob_end_clean() to avoid weird buffering situations
// where file is sent broken/incomplete for download
ob_end_clean();
}
@ -332,8 +336,7 @@ class DocumentManager
return true;
} else {
//no forced download, just let the browser decide what to do according to the mimetype
$content_type = self::file_get_mime_type($filename);
// no forced download, just let the browser decide what to do according to the mimetype
$lpFixedEncoding = api_get_configuration_value('lp_fixed_encoding');
// Commented to let courses content to be cached in order to improve performance:
@ -343,24 +346,25 @@ class DocumentManager
// Commented to avoid double caching declaration when playing with IE and HTTPS
//header('Cache-Control: no-cache, must-revalidate');
//header('Pragma: no-cache');
switch ($content_type) {
$contentType = self::file_get_mime_type($filename);
switch ($contentType) {
case 'text/html':
if (isset($lpFixedEncoding) && $lpFixedEncoding === 'true') {
$content_type .= '; charset=UTF-8';
$contentType .= '; charset=UTF-8';
} else {
$encoding = @api_detect_encoding_html(file_get_contents($full_file_name));
if (!empty($encoding)) {
$content_type .= '; charset='.$encoding;
$contentType .= '; charset='.$encoding;
}
}
break;
case 'text/plain':
if (isset($lpFixedEncoding) && $lpFixedEncoding === 'true') {
$content_type .= '; charset=UTF-8';
$contentType .= '; charset=UTF-8';
} else {
$encoding = @api_detect_encoding(strip_tags(file_get_contents($full_file_name)));
if (!empty($encoding)) {
$content_type .= '; charset='.$encoding;
$contentType .= '; charset='.$encoding;
}
}
break;
@ -369,7 +373,7 @@ class DocumentManager
header('Content-type: application/octet-stream');
break;
}
header('Content-type: '.$content_type);
header('Content-type: '.$contentType);
header('Content-Length: '.$len);
$user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
if (strpos($user_agent, 'msie')) {
@ -388,7 +392,8 @@ class DocumentManager
echo $content;
} else {
if (function_exists('ob_end_clean')) {
// Use ob_end_clean() to avoid weird buffering situations where file is sent broken/incomplete for download
// Use ob_end_clean() to avoid weird buffering situations
// where file is sent broken/incomplete for download
ob_end_clean();
}

@ -1,372 +1,354 @@
CKEDITOR.dialog.add( 'video', function ( editor )
{
var lang = editor.lang.video;
var lang = editor.lang.video;
function commitValue( videoNode, extraStyles )
{
var value=this.getValue();
function commitValue( videoNode, extraStyles )
{
var value=this.getValue();
if ( !value && this.id=='id' )
value = generateId();
if ( !value && this.id=='id' )
value = generateId();
if (value == '') {
return;
}
videoNode.setAttribute( this.id, value);
videoNode.setAttribute( this.id, value);
if ( !value )
return;
switch( this.id )
{
case 'poster':
extraStyles.backgroundImage = 'url(' + value + ')';
break;
case 'width':
extraStyles.width = value + 'px';
break;
case 'height':
extraStyles.height = value + 'px';
break;
}
}
if ( !value )
return;
switch( this.id )
{
case 'poster':
extraStyles.backgroundImage = 'url(' + value + ')';
break;
case 'width':
extraStyles.width = value + 'px';
break;
case 'height':
extraStyles.height = value + 'px';
break;
}
}
function commitSrc( videoNode, extraStyles, videos )
{
var match = this.id.match(/(\w+)(\d)/),
id = match[1],
number = parseInt(match[2], 10);
function commitSrc( videoNode, extraStyles, videos )
{
var match = this.id.match(/(\w+)(\d)/),
id = match[1],
number = parseInt(match[2], 10);
var video = videos[number] || (videos[number]={});
video[id] = this.getValue();
}
var video = videos[number] || (videos[number]={});
video[id] = this.getValue();
}
function loadValue( videoNode )
{
if ( videoNode )
this.setValue( videoNode.getAttribute( this.id ) );
else
{
if ( this.id == 'id')
this.setValue( generateId() );
}
}
function loadValue( videoNode )
{
if ( videoNode )
this.setValue( videoNode.getAttribute( this.id ) );
else
{
if ( this.id == 'id')
this.setValue( generateId() );
}
}
function loadSrc( videoNode, videos )
{
var match = this.id.match(/(\w+)(\d)/),
id = match[1],
number = parseInt(match[2], 10);
function loadSrc( videoNode, videos )
{
var match = this.id.match(/(\w+)(\d)/),
id = match[1],
number = parseInt(match[2], 10);
var video = videos[number];
if (!video)
return;
this.setValue( video[ id ] );
}
var video = videos[number];
if (!video)
return;
this.setValue( video[ id ] );
}
function generateId()
{
var now = new Date();
return 'video' + now.getFullYear() + now.getMonth() + now.getDate() + now.getHours() + now.getMinutes() + now.getSeconds();
}
function generateId()
{
var now = new Date();
return 'video' + now.getFullYear() + now.getMonth() + now.getDate() + now.getHours() + now.getMinutes() + now.getSeconds();
}
// To automatically get the dimensions of the poster image
var onImgLoadEvent = function()
{
// Image is ready.
var preview = this.previewImage;
preview.removeListener( 'load', onImgLoadEvent );
preview.removeListener( 'error', onImgLoadErrorEvent );
preview.removeListener( 'abort', onImgLoadErrorEvent );
// To automatically get the dimensions of the poster image
var onImgLoadEvent = function()
{
// Image is ready.
var preview = this.previewImage;
preview.removeListener( 'load', onImgLoadEvent );
preview.removeListener( 'error', onImgLoadErrorEvent );
preview.removeListener( 'abort', onImgLoadErrorEvent );
this.setValueOf( 'info', 'width', preview.$.width );
this.setValueOf( 'info', 'height', preview.$.height );
};
this.setValueOf( 'info', 'width', preview.$.width );
this.setValueOf( 'info', 'height', preview.$.height );
};
var onImgLoadErrorEvent = function()
{
// Error. Image is not loaded.
var preview = this.previewImage;
preview.removeListener( 'load', onImgLoadEvent );
preview.removeListener( 'error', onImgLoadErrorEvent );
preview.removeListener( 'abort', onImgLoadErrorEvent );
};
var onImgLoadErrorEvent = function()
{
// Error. Image is not loaded.
var preview = this.previewImage;
preview.removeListener( 'load', onImgLoadEvent );
preview.removeListener( 'error', onImgLoadErrorEvent );
preview.removeListener( 'abort', onImgLoadErrorEvent );
};
return {
title : lang.dialogTitle,
minWidth : 400,
minHeight : 200,
return {
title : lang.dialogTitle,
minWidth : 400,
minHeight : 200,
onShow : function()
{
// Clear previously saved elements.
this.fakeImage = this.videoNode = null;
// To get dimensions of poster image
this.previewImage = editor.document.createElement( 'img' );
onShow : function()
{
// Clear previously saved elements.
this.fakeImage = this.videoNode = null;
// To get dimensions of poster image
this.previewImage = editor.document.createElement( 'img' );
var fakeImage = this.getSelectedElement();
if ( fakeImage && fakeImage.data( 'cke-real-element-type' ) && fakeImage.data( 'cke-real-element-type' ) == 'video' )
{
this.fakeImage = fakeImage;
var fakeImage = this.getSelectedElement();
if ( fakeImage && fakeImage.data( 'cke-real-element-type' ) && fakeImage.data( 'cke-real-element-type' ) == 'video' )
{
this.fakeImage = fakeImage;
var videoNode = editor.restoreRealElement( fakeImage ),
videos = [],
sourceList = videoNode.getElementsByTag( 'source', '' );
if (sourceList.count()==0)
sourceList = videoNode.getElementsByTag( 'source', 'cke' );
var videoNode = editor.restoreRealElement( fakeImage ),
videos = [],
sourceList = videoNode.getElementsByTag( 'source', '' );
if (sourceList.count()==0)
sourceList = videoNode.getElementsByTag( 'source', 'cke' );
for ( var i = 0, length = sourceList.count() ; i < length ; i++ )
{
var item = sourceList.getItem( i );
videos.push( {src : item.getAttribute( 'src' ), type: item.getAttribute( 'type' )} );
}
for ( var i = 0, length = sourceList.count() ; i < length ; i++ )
{
var item = sourceList.getItem( i );
videos.push( {src : item.getAttribute( 'src' ), type: item.getAttribute( 'type' )} );
}
this.videoNode = videoNode;
this.videoNode = videoNode;
this.setupContent( videoNode, videos );
}
else
this.setupContent( null, [] );
},
this.setupContent( videoNode, videos );
}
else
this.setupContent( null, [] );
},
onOk : function()
{
// If there's no selected element create one. Otherwise, reuse it
var videoNode = null;
if ( !this.fakeImage )
{
videoNode = CKEDITOR.dom.element.createFromHtml( '<cke:video></cke:video>', editor.document );
videoNode.setAttributes(
{
controls : 'controls'
} );
}
else
{
videoNode = this.videoNode;
}
onOk : function()
{
// If there's no selected element create one. Otherwise, reuse it
var videoNode = null;
if ( !this.fakeImage )
{
videoNode = CKEDITOR.dom.element.createFromHtml( '<cke:video></cke:video>', editor.document );
videoNode.setAttributes(
{
controls : 'controls'
} );
}
else
{
videoNode = this.videoNode;
}
var extraStyles = {}, videos = [];
this.commitContent( videoNode, extraStyles, videos );
var extraStyles = {}, videos = [];
this.commitContent( videoNode, extraStyles, videos );
var innerHtml = '', links = '',
link = lang.linkTemplate || '',
fallbackTemplate = lang.fallbackTemplate || '';
for(var i=0; i<videos.length; i++)
{
var video = videos[i];
if ( !video || !video.src ) { continue; }
//local copy of video URL
var mySrc = video.src;
//Chrome is picky about the redirect, so point directly to app/courses/ if currently pointing to courses/
//See https://support.chamilo.org/issues/8462
var coursesPathIndex = mySrc.indexOf('courses');
var newHtmlTag = '<cke:source src="' + mySrc + '" type="' + video.type + '" />';
var newLinks = link.replace('%src%', mySrc).replace('%type%', video.type);
//is video saved on courses folder ?
if (coursesPathIndex >= 0) {
//test if real path is not already present (in case of video edition)
var myPath = mySrc.indexOf('app');
if (myPath==-1) {
//add real path (app/) to video.src...
var myChromeSrc = mySrc.slice(0, coursesPathIndex) + "app/" + mySrc.slice(coursesPathIndex);
//insert full path link...
newHtmlTag = '<cke:source src="' + myChromeSrc + '" type="' + video.type + '" />';
newLinks = link.replace('%src%', myChromeSrc).replace('%type%', video.type);
}
}
innerHtml += newHtmlTag;
links += newLinks;
}
videoNode.setHtml( innerHtml + fallbackTemplate.replace( '%links%', links ) );
var innerHtml = '', links = '',
link = lang.linkTemplate || '',
fallbackTemplate = lang.fallbackTemplate || '';
for(var i=0; i<videos.length; i++)
{
var video = videos[i];
if ( !video || !video.src )
continue;
innerHtml += '<cke:source src="' + video.src + '" type="' + video.type + '" />';
links += link.replace('%src%', video.src).replace('%type%', video.type);
}
videoNode.setHtml( innerHtml + fallbackTemplate.replace( '%links%', links ) );
// Refresh the fake image.
var newFakeImage = editor.createFakeElement( videoNode, 'cke_video', 'video', false );
newFakeImage.setStyles( extraStyles );
if ( this.fakeImage )
{
newFakeImage.replace( this.fakeImage );
editor.getSelection().selectElement( newFakeImage );
}
else
{
// Insert it in a div
var div = new CKEDITOR.dom.element( 'DIV', editor.document );
editor.insertElement( div );
div.append( newFakeImage );
}
},
onHide : function()
{
if ( this.previewImage )
{
this.previewImage.removeListener( 'load', onImgLoadEvent );
this.previewImage.removeListener( 'error', onImgLoadErrorEvent );
this.previewImage.removeListener( 'abort', onImgLoadErrorEvent );
this.previewImage.remove();
this.previewImage = null; // Dialog is closed.
}
},
// Refresh the fake image.
var newFakeImage = editor.createFakeElement( videoNode, 'cke_video', 'video', false );
newFakeImage.setStyles( extraStyles );
if ( this.fakeImage )
{
newFakeImage.replace( this.fakeImage );
editor.getSelection().selectElement( newFakeImage );
}
else
{
// Insert it in a div
var div = new CKEDITOR.dom.element( 'DIV', editor.document );
editor.insertElement( div );
div.append( newFakeImage );
}
},
onHide : function()
{
if ( this.previewImage )
{
this.previewImage.removeListener( 'load', onImgLoadEvent );
this.previewImage.removeListener( 'error', onImgLoadErrorEvent );
this.previewImage.removeListener( 'abort', onImgLoadErrorEvent );
this.previewImage.remove();
this.previewImage = null; // Dialog is closed.
}
},
contents :
[
{
id : 'info',
elements :
[
{
type : 'hbox',
widths: [ '33%', '33%', '33%'],
children : [
{
type : 'text',
id : 'width',
label : editor.lang.common.width,
'default' : 400,
validate : CKEDITOR.dialog.validate.notEmpty( lang.widthRequired ),
commit : commitValue,
setup : loadValue
},
{
type : 'text',
id : 'height',
label : editor.lang.common.height,
'default' : 300,
//validate : CKEDITOR.dialog.validate.notEmpty(lang.heightRequired ),
commit : commitValue,
setup : loadValue
},
{
type : 'text',
id : 'id',
label : 'Id',
commit : commitValue,
setup : loadValue
}
]
},
{
type : 'hbox',
widths: [ '', '100px', '75px'],
children : [
{
type : 'text',
id : 'src0',
label : lang.sourceVideo,
commit : commitSrc,
setup : loadSrc
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:src0',
url: editor.config.filebrowserVideoBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
},
{
id : 'type0',
label : lang.sourceType,
type : 'select',
'default' : 'video/mp4',
items :
[
[ 'MP4', 'video/mp4' ],
[ 'WebM', 'video/webm' ]
],
commit : commitSrc,
setup : loadSrc
}]
},
contents :
[
{
id : 'info',
elements :
[
{
type : 'hbox',
widths: [ '33%', '33%', '33%'],
children : [
{
type : 'text',
id : 'width',
label : editor.lang.common.width,
'default' : 400,
validate : CKEDITOR.dialog.validate.notEmpty( lang.widthRequired ),
commit : commitValue,
setup : loadValue
},
{
type : 'text',
id : 'height',
label : editor.lang.common.height,
'default' : 300,
//validate : CKEDITOR.dialog.validate.notEmpty(lang.heightRequired ),
commit : commitValue,
setup : loadValue
},
{
type : 'text',
id : 'id',
label : 'Id',
commit : commitValue,
setup : loadValue
}
]
},
{
type : 'hbox',
widths: [ '', '100px', '75px'],
children : [
{
type : 'text',
id : 'src0',
label : lang.sourceVideo,
commit : commitSrc,
setup : loadSrc
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:src0',
url: editor.config.filebrowserVideoBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
},
{
id : 'type0',
label : lang.sourceType,
type : 'select',
'default' : 'video/mp4',
items :
[
[ 'MP4', 'video/mp4' ],
[ 'WebM', 'video/webm' ]
],
commit : commitSrc,
setup : loadSrc
}]
},
{
type : 'hbox',
widths: [ '', '100px', '75px'],
children : [
{
type : 'text',
id : 'src1',
label : lang.sourceVideo,
commit : commitSrc,
setup : loadSrc
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:src1',
url: editor.config.filebrowserVideoBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
},
{
id : 'type1',
label : lang.sourceType,
type : 'select',
'default':'video/webm',
items :
[
[ 'MP4', 'video/mp4' ],
[ 'WebM', 'video/webm' ]
],
commit : commitSrc,
setup : loadSrc
}]
},
{
type : 'hbox',
widths: [ '', '100px'],
children : [
{
type : 'text',
id : 'poster',
label : lang.poster,
commit : commitValue,
setup : loadValue,
onChange : function()
{
var dialog = this.getDialog(),
newUrl = this.getValue();
{
type : 'hbox',
widths: [ '', '100px', '75px'],
children : [
{
type : 'text',
id : 'src1',
label : lang.sourceVideo,
commit : commitSrc,
setup : loadSrc
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:src1',
url: editor.config.filebrowserVideoBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
},
{
id : 'type1',
label : lang.sourceType,
type : 'select',
'default':'video/webm',
items :
[
[ 'MP4', 'video/mp4' ],
[ 'WebM', 'video/webm' ]
],
commit : commitSrc,
setup : loadSrc
}]
},
{
type : 'hbox',
widths: [ '', '100px'],
children : [
{
type : 'text',
id : 'poster',
label : lang.poster,
commit : commitValue,
setup : loadValue,
onChange : function()
{
var dialog = this.getDialog(),
newUrl = this.getValue();
//Update preview image
if ( newUrl.length > 0 ) //Prevent from load before onShow
{
dialog = this.getDialog();
var preview = dialog.previewImage;
//Update preview image
if ( newUrl.length > 0 ) //Prevent from load before onShow
{
dialog = this.getDialog();
var preview = dialog.previewImage;
preview.on( 'load', onImgLoadEvent, dialog );
preview.on( 'error', onImgLoadErrorEvent, dialog );
preview.on( 'abort', onImgLoadErrorEvent, dialog );
preview.setAttribute( 'src', newUrl );
}
}
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:poster',
url: editor.config.filebrowserImageBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
}]
}
]
}
preview.on( 'load', onImgLoadEvent, dialog );
preview.on( 'error', onImgLoadErrorEvent, dialog );
preview.on( 'abort', onImgLoadErrorEvent, dialog );
preview.setAttribute( 'src', newUrl );
}
}
},
{
type : 'button',
id : 'browse',
hidden : 'true',
style : 'display:inline-block;margin-top:10px;',
filebrowser :
{
action : 'Browse',
target: 'info:poster',
url: editor.config.filebrowserImageBrowseUrl || editor.config.filebrowserBrowseUrl
},
label : editor.lang.common.browseServer
}]
}
]
}
]
};
]
};
} );

@ -1,21 +1,16 @@
<script>
/* Makes row highlighting possible */
$(document).ready( function() {
$("[data-toggle=popover]").each(function(i, obj) {
$("[data-toggle=popover]").each(function(i, obj) {
$(this).popover({
html: true,
content: function() {
var id = $(this).attr('id')
return $('#popover-content-' + id).html();
}
html: true,
content: function() {
var id = $(this).attr('id')
return $('#popover-content-' + id).html();
}
});
});
});
// Date time settings.
moment.locale('{{ locale }}');
$.datepicker.setDefaults($.datepicker.regional["{{ locale }}"]);

Loading…
Cancel
Save