diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index 0c67341..3ea2ba9 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -386,6 +386,16 @@ class H5PDefaultStorage implements \H5PFileStorage { } } + /** + * Check if server setup has write permission to + * the required folders + * + * @return bool True if site can write to the H5P files folder + */ + public function hasWriteAccess() { + return self::dirReady($this->path); + } + /** * Recursive function for copying directories. * diff --git a/h5p-file-storage.interface.php b/h5p-file-storage.interface.php index 3c621f7..93ca771 100644 --- a/h5p-file-storage.interface.php +++ b/h5p-file-storage.interface.php @@ -171,4 +171,12 @@ interface H5PFileStorage { * @param int $contentId */ public function removeContentFile($file, $contentId); + + /** + * Check if server setup has write permission to + * the required folders + * + * @return bool True if server has the proper write access + */ + public function hasWriteAccess(); } diff --git a/h5p.classes.php b/h5p.classes.php index e04a0e9..367c0a9 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -2754,6 +2754,107 @@ class H5PCore { 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 if the current server setup is valid and set error messages + * + * @return array Errors found + */ + public function checkSetupErrorMessage() { + $errors = array(); + + if (!class_exists('ZipArchive')) { + $errors[] = $this->h5pF->t('Your PHP version does not support ZipArchive.'); + } + + if (!extension_loaded('mbstring')) { + $errors[] = + $this->h5pF->t('The mbstring PHP extension is not loaded. H5P need this to function properly'); + } + + // Check php version >= 5.2 + $php_version = explode('.', phpversion()); + if ($php_version[0] < 5 || ($php_version[0] === 5 && $php_version[1] < 2)) { + $errors[] = + $this->h5pF->t('Your PHP version is outdated. H5P requires version 5.2 to function properly. Version 5.6 or later is recommended.'); + } + + // Check write access + if (!$this->fs->hasWriteAccess()) { + $errors[] = + $this->h5pF->t('A problem with the server write access was detected. Please make sure that your server can write to your data folder.'); + } + + $max_upload_size = self::returnBytes(ini_get('upload_max_filesize')); + $max_post_size = self::returnBytes(ini_get('post_max_size')); + $byte_threshold = 5000000; // 5MB + if ($max_upload_size < $byte_threshold) { + $errors[] = + $this->h5pF->t('Your PHP max upload size option is too small. You should consider to increase it to more than 5MB.'); + } + + if ($max_post_size < $byte_threshold) { + $errors[] = + $this->h5pF->t('Your PHP max post size option is too small. You should consider to increase it to more than 5MB.'); + } + + if ($max_upload_size > $max_post_size) { + $errors[] = + $this->h5pF->t('Your PHP max upload size is bigger than your max post size. This is known to cause issues in some installations.'); + } + + // Check SSL + if (!extension_loaded('openssl')) { + $errors[] = + $this->h5pF->t('Your server does not have SSL enabled. SSL should be enabled to ensure a secure connection with the H5P hub.'); + } + + return $errors; + } + + /** + * Check that all H5P requirements for the server setup is met. + */ + public function checkSetupForRequirements() { + $errors = $this->checkSetupErrorMessage(); + + $this->h5pF->setOption('hub_is_enabled', empty($errors)); + if (!empty($errors)) { + foreach ($errors as $err) { + $this->h5pF->setErrorMessage($err); + } + + // Inform how to re-enable hub + $this->h5pF->setErrorMessage( + $this->h5pF->t('H5P hub communication has been disabled because one or more H5P requirements failed.') + ); + $this->h5pF->setErrorMessage( + $this->h5pF->t('When you have revised your server setup you may re-enable H5P hub communication in H5P Settings.') + ); + } + } + + /** + * Return bytes from php_ini string value + * + * @param string $val + * + * @return int|string + */ + public static function returnBytes($val) { + $val = trim($val); + $last = strtolower($val[strlen($val) - 1]); + switch ($last) { + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + + return $val; + } } /** diff --git a/js/settings/h5p-disable-hub.js b/js/settings/h5p-disable-hub.js new file mode 100644 index 0000000..406e8b2 --- /dev/null +++ b/js/settings/h5p-disable-hub.js @@ -0,0 +1,68 @@ +/* global H5PDisableHubData */ + +/** + * Global data for disable hub functionality + * + * @typedef {object} H5PDisableHubData Data passed in from the backend + * + * @property {string} selector Selector for the disable hub check-button + * @property {string} overlaySelector Selector for the element that the confirmation dialog will mask + * @property {Array} errors Errors found with the current server setup + * + * @property {string} header Header of the confirmation dialog + * @property {string} confirmationDialogMsg Body of the confirmation dialog + * @property {string} cancelLabel Cancel label of the confirmation dialog + * @property {string} confirmLabel Confirm button label of the confirmation dialog + * + */ +/** + * Utility that makes it possible to force the user to confirm that he really + * wants to use the H5P hub without proper server settings. + */ +(function ($) { + + $(document).on('ready', function () { + + // No data found + if (!H5PDisableHubData) { + return; + } + + // No errors found, no need for confirmation dialog + if (!H5PDisableHubData.errors || !H5PDisableHubData.errors.length) { + return; + } + + H5PDisableHubData.selector = H5PDisableHubData.selector || + '.h5p-settings-disable-hub-checkbox'; + H5PDisableHubData.overlaySelector = H5PDisableHubData.overlaySelector || + '.h5p-settings-container'; + + var dialogHtml = '
' + H5PDisableHubData.errors.join('
') + '
' + + '' + H5PDisableHubData.confirmationDialogMsg + '
'; + + // Create confirmation dialog, make sure to include translations + var confirmationDialog = new H5P.ConfirmationDialog({ + headerText: H5PDisableHubData.header, + dialogText: dialogHtml, + cancelText: H5PDisableHubData.cancelLabel, + confirmText: H5PDisableHubData.confirmLabel + }).appendTo($(H5PDisableHubData.overlaySelector).get(0)); + + confirmationDialog.on('confirmed', function () { + enableButton.get(0).checked = true; + }); + + confirmationDialog.on('canceled', function () { + enableButton.get(0).checked = false; + }); + + var enableButton = $(H5PDisableHubData.selector); + enableButton.change(function () { + if ($(this).is(':checked')) { + confirmationDialog.show(enableButton.offset().top); + } + }); + }); +})(H5P.jQuery);