Merge branch 'master' into release
commit
0dddbf654e
|
@ -426,6 +426,18 @@ class H5PDefaultStorage implements \H5PFileStorage {
|
|||
$path = "{$this->path}/content/{$contentId}/{$file}";
|
||||
if (file_exists($path)) {
|
||||
unlink($path);
|
||||
|
||||
// Clean up any empty parent directories to avoid cluttering the file system
|
||||
$parts = explode('/', $path);
|
||||
while (array_pop($parts) !== NULL) {
|
||||
$dir = implode('/', $parts);
|
||||
if (is_dir($dir) && count(scandir($dir)) === 2) { // empty contains '.' and '..'
|
||||
rmdir($dir); // Remove empty parent
|
||||
}
|
||||
else {
|
||||
return; // Not empty
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1724,7 +1724,7 @@ class H5PCore {
|
|||
|
||||
public static $coreApi = array(
|
||||
'majorVersion' => 1,
|
||||
'minorVersion' => 13
|
||||
'minorVersion' => 14
|
||||
);
|
||||
public static $styles = array(
|
||||
'styles/h5p.css',
|
||||
|
@ -1746,7 +1746,7 @@ class H5PCore {
|
|||
'js/h5p-utils.js',
|
||||
);
|
||||
|
||||
public static $defaultContentWhitelist = 'json png jpg jpeg gif bmp tif tiff svg eot ttf woff woff2 otf webm mp4 ogg mp3 wav txt pdf rtf doc docx xls xlsx ppt pptx odt ods odp xml csv diff patch swf md textile';
|
||||
public static $defaultContentWhitelist = 'json png jpg jpeg gif bmp tif tiff svg eot ttf woff woff2 otf webm mp4 ogg mp3 wav txt pdf rtf doc docx xls xlsx ppt pptx odt ods odp xml csv diff patch swf md textile vtt webvtt';
|
||||
public static $defaultLibraryWhitelistExtras = 'js css';
|
||||
|
||||
public $librariesJsonData, $contentJsonData, $mainJsonData, $h5pF, $fs, $h5pD, $disableFileCheck;
|
||||
|
@ -2790,16 +2790,8 @@ class H5PCore {
|
|||
* @return string token
|
||||
*/
|
||||
public static function createToken($action) {
|
||||
if (!isset($_SESSION['h5p_token'])) {
|
||||
// Create an unique key which is used to create action tokens for this session.
|
||||
$_SESSION['h5p_token'] = uniqid();
|
||||
}
|
||||
|
||||
// Timefactor
|
||||
$time_factor = self::getTimeFactor();
|
||||
|
||||
// Create and return token
|
||||
return substr(hash('md5', $action . $time_factor . $_SESSION['h5p_token']), -16, 13);
|
||||
return self::hashToken($action, self::getTimeFactor());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2810,6 +2802,31 @@ class H5PCore {
|
|||
return ceil(time() / (86400 / 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique hash string based on action, time and token
|
||||
*
|
||||
* @param string $action
|
||||
* @param int $time_factor
|
||||
* @return string
|
||||
*/
|
||||
private static function hashToken($action, $time_factor) {
|
||||
if (!isset($_SESSION['h5p_token'])) {
|
||||
// Create an unique key which is used to create action tokens for this session.
|
||||
if (function_exists('random_bytes')) {
|
||||
$_SESSION['h5p_token'] = base64_encode(random_bytes(15));
|
||||
}
|
||||
else if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
$_SESSION['h5p_token'] = base64_encode(openssl_random_pseudo_bytes(15));
|
||||
}
|
||||
else {
|
||||
$_SESSION['h5p_token'] = uniqid('', TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
// Create hash and return
|
||||
return substr(hash('md5', $action . $time_factor . $_SESSION['h5p_token']), -16, 13);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the given token is valid for the given action.
|
||||
*
|
||||
|
@ -2818,9 +2835,12 @@ class H5PCore {
|
|||
* @return boolean valid token
|
||||
*/
|
||||
public static function validToken($action, $token) {
|
||||
// Get the timefactor
|
||||
$time_factor = self::getTimeFactor();
|
||||
return $token === substr(hash('md5', $action . $time_factor . $_SESSION['h5p_token']), -16, 13) || // Under 12 hours
|
||||
$token === substr(hash('md5', $action . ($time_factor - 1) . $_SESSION['h5p_token']), -16, 13); // Between 12-24 hours
|
||||
|
||||
// Check token to see if it's valid
|
||||
return $token === self::hashToken($action, $time_factor) || // Under 12 hours
|
||||
$token === self::hashToken($action, $time_factor - 1); // Between 12-24 hours
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3163,7 +3183,7 @@ class H5PContentValidator {
|
|||
$stylePatterns[] = '/^font-size: *[0-9.]+(em|px|%) *;?$/i';
|
||||
}
|
||||
if (isset($semantics->font->family) && $semantics->font->family) {
|
||||
$stylePatterns[] = '/^font-family: *[a-z0-9," ]+;?$/i';
|
||||
$stylePatterns[] = '/^font-family: *[-a-z0-9," ]+;?$/i';
|
||||
}
|
||||
if (isset($semantics->font->color) && $semantics->font->color) {
|
||||
$stylePatterns[] = '/^color: *(#[a-f0-9]{3}[a-f0-9]{3}?|rgba?\([0-9, ]+\)) *;?$/i';
|
||||
|
@ -3400,6 +3420,11 @@ class H5PContentValidator {
|
|||
$file->path = $matches[5];
|
||||
}
|
||||
|
||||
// Remove temporary files suffix
|
||||
if (substr($file->path, -4, 4) === '#tmp') {
|
||||
$file->path = substr($file->path, 0, strlen($file->path) - 4);
|
||||
}
|
||||
|
||||
// Make sure path and mime does not have any special chars
|
||||
$file->path = htmlspecialchars($file->path, ENT_QUOTES, 'UTF-8', FALSE);
|
||||
if (isset($file->mime)) {
|
||||
|
|
|
@ -315,7 +315,7 @@ H5P.ConfirmationDialog = (function (EventDispatcher) {
|
|||
if (resizeIFrame && options.instance) {
|
||||
var minHeight = parseInt(popup.offsetHeight, 10) +
|
||||
exitButtonOffset + (2 * shadowOffset);
|
||||
wrapperElement.style.minHeight = minHeight + 'px';
|
||||
self.setViewPortMinimumHeight(minHeight);
|
||||
options.instance.trigger('resize');
|
||||
resizeIFrame = false;
|
||||
}
|
||||
|
@ -340,10 +340,21 @@ H5P.ConfirmationDialog = (function (EventDispatcher) {
|
|||
setTimeout(function () {
|
||||
popupBackground.classList.add('hidden');
|
||||
wrapperElement.removeChild(popupBackground);
|
||||
self.setViewPortMinimumHeight(null);
|
||||
}, 100);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the minimum height of the view port
|
||||
*
|
||||
* @param {number|null} minHeight
|
||||
*/
|
||||
this.setViewPortMinimumHeight = function(minHeight) {
|
||||
var container = document.querySelector('.h5p-container') || document.body;
|
||||
container.style.minHeight = (typeof minHeight === 'number') ? (minHeight + 'px') : minHeight;
|
||||
};
|
||||
}
|
||||
|
||||
ConfirmationDialog.prototype = Object.create(EventDispatcher.prototype);
|
||||
|
|
45
js/h5p.js
45
js/h5p.js
|
@ -133,9 +133,26 @@ H5P.init = function (target) {
|
|||
|
||||
// Check if we should add and display a fullscreen button for this H5P.
|
||||
if (contentData.fullScreen == 1 && H5P.fullscreenSupported) {
|
||||
H5P.jQuery('<div class="h5p-content-controls"><div role="button" tabindex="0" class="h5p-enable-fullscreen" title="' + H5P.t('fullscreen') + '"></div></div>').prependTo($container).children().click(function () {
|
||||
H5P.jQuery(
|
||||
'<div class="h5p-content-controls">' +
|
||||
'<div role="button" ' +
|
||||
'tabindex="0" ' +
|
||||
'class="h5p-enable-fullscreen" ' +
|
||||
'title="' + H5P.t('fullscreen') + '">' +
|
||||
'</div>' +
|
||||
'</div>')
|
||||
.prependTo($container)
|
||||
.children()
|
||||
.click(function () {
|
||||
H5P.fullScreen($container, instance);
|
||||
});
|
||||
})
|
||||
.keydown(function (e) {
|
||||
if (e.which === 32 || e.which === 13) {
|
||||
H5P.fullScreen($container, instance);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -661,7 +678,8 @@ H5P.getPath = function (path, contentId) {
|
|||
}
|
||||
|
||||
var prefix;
|
||||
if (contentId !== undefined) {
|
||||
var isTmpFile = (path.substr(-4,4) === '#tmp');
|
||||
if (contentId !== undefined && !isTmpFile) {
|
||||
// Check for custom override URL
|
||||
if (H5PIntegration.contents !== undefined &&
|
||||
H5PIntegration.contents['cid-' + contentId]) {
|
||||
|
@ -1000,24 +1018,13 @@ H5P.findCopyrights = function (info, parameters, contentId) {
|
|||
continue; // Do not check
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Make parameters clean again
|
||||
* Some content types adds jQuery or other objects to parameters
|
||||
* in order to determine override settings for sub-content-types.
|
||||
* For instance Question Set tells Multiple Choice that it should
|
||||
* attach Multi Choice's confirmation dialog to a Question Set
|
||||
* jQuery element, so that the confirmation dialog will not be restricted
|
||||
* to the space confined by Multi Choice.
|
||||
* Ideally this should not be added to parameters, we must make a better
|
||||
* solution. We should likely be adding these to sub-content through
|
||||
* functions/setters instead of passing them down as params.
|
||||
*
|
||||
* This solution is implemented as a hack that will ignore all parameters
|
||||
* inside a "overrideSettings" field, this should suffice for now since
|
||||
* all overridden objects are added to this field, however this is not very
|
||||
* robust solution and will very likely lead to problems in the future.
|
||||
/**
|
||||
* @deprecated This hack should be removed after 2017-11-01
|
||||
* The code that was using this was removed by HFP-574
|
||||
*/
|
||||
if (field === 'overrideSettings') {
|
||||
console.warn("The semantics field 'overrideSettings' is DEPRECATED and should not be used.");
|
||||
console.warn(parameters);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -108,13 +108,20 @@ body.h5p-semi-fullscreen {
|
|||
width: 1.125em;
|
||||
height: 0.925em;
|
||||
text-indent: -0.0875em;
|
||||
outline: none;
|
||||
}
|
||||
.h5p-disable-fullscreen {
|
||||
line-height: 0.925em;
|
||||
width: 1.1em;
|
||||
height: 0.9em;
|
||||
}
|
||||
|
||||
.h5p-enable-fullscreen:focus,
|
||||
.h5p-disable-fullscreen:focus {
|
||||
outline-style: solid;
|
||||
outline-width: 1px;
|
||||
outline-offset: 0.25em;
|
||||
}
|
||||
|
||||
.h5p-enable-fullscreen:hover, .h5p-disable-fullscreen:hover {
|
||||
background: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue