diff --git a/fonts/h5p-core-16.eot b/fonts/h5p-core-16.eot deleted file mode 100644 index 239079b..0000000 Binary files a/fonts/h5p-core-16.eot and /dev/null differ diff --git a/fonts/h5p-core-16.svg b/fonts/h5p-core-16.svg deleted file mode 100644 index d0bdb88..0000000 --- a/fonts/h5p-core-16.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - -{ - "fontFamily": "h5p", - "description": "Font generated by IcoMoon.", - "majorVersion": 1, - "minorVersion": 1, - "version": "Version 1.1", - "fontId": "h5p", - "psName": "h5p", - "subFamily": "Regular", - "fullName": "h5p" -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fonts/h5p-core-16.ttf b/fonts/h5p-core-16.ttf deleted file mode 100644 index daf1571..0000000 Binary files a/fonts/h5p-core-16.ttf and /dev/null differ diff --git a/fonts/h5p-core-16.woff b/fonts/h5p-core-16.woff deleted file mode 100644 index d654df4..0000000 Binary files a/fonts/h5p-core-16.woff and /dev/null differ diff --git a/fonts/h5p-core-18.eot b/fonts/h5p-core-18.eot new file mode 100755 index 0000000..eba9d6c Binary files /dev/null and b/fonts/h5p-core-18.eot differ diff --git a/fonts/h5p-core-18.svg b/fonts/h5p-core-18.svg new file mode 100755 index 0000000..13da36e --- /dev/null +++ b/fonts/h5p-core-18.svg @@ -0,0 +1,52 @@ + + + + + + +{ + "fontFamily": "h5p", + "description": "Font generated by IcoMoon.", + "majorVersion": 1, + "minorVersion": 1, + "version": "Version 1.1", + "fontId": "h5p", + "psName": "h5p", + "subFamily": "Regular", + "fullName": "h5p" +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/h5p-core-18.ttf b/fonts/h5p-core-18.ttf new file mode 100755 index 0000000..37f845e Binary files /dev/null and b/fonts/h5p-core-18.ttf differ diff --git a/fonts/h5p-core-18.woff b/fonts/h5p-core-18.woff new file mode 100755 index 0000000..8450f3d Binary files /dev/null and b/fonts/h5p-core-18.woff differ diff --git a/h5p-default-storage.class.php b/h5p-default-storage.class.php index 0c67341..8d2daf8 100644 --- a/h5p-default-storage.class.php +++ b/h5p-default-storage.class.php @@ -285,7 +285,7 @@ class H5PDefaultStorage implements \H5PFileStorage { * @return string */ public function getContent($file_path) { - return file_get_contents($this->path . $file_path); + return file_get_contents($file_path); } /** @@ -299,7 +299,7 @@ class H5PDefaultStorage implements \H5PFileStorage { // Prepare directory if (empty($contentId)) { // Should be in editor tmp folder - $path = ($this->alteditorpath !== NULL ? $this->alteditorpath : $this->path . '/editor'); + $path = $this->getEditorPath(); } else { // Should be in content folder @@ -319,7 +319,7 @@ class H5PDefaultStorage implements \H5PFileStorage { copy($_FILES['file']['tmp_name'], $path); } - return $path; + return $file; } /** @@ -333,7 +333,7 @@ class H5PDefaultStorage implements \H5PFileStorage { public function cloneContentFile($file, $fromId, $toId) { // Determine source path if ($fromId === 'editor') { - $sourcepath = ($this->alteditorpath !== NULL ? $this->alteditorpath : "{$this->path}/editor"); + $sourcepath = $this->getEditorPath(); } else { $sourcepath = "{$this->path}/content/{$fromId}"; @@ -358,6 +358,49 @@ class H5PDefaultStorage implements \H5PFileStorage { copy($sourcepath, $targetpath); } + /** + * Copy a content from one directory to another. Defaults to cloning + * content from the current temporary upload folder to the editor path. + * + * @param string $source path to source directory + * @param string $contentId Id of content + * + * @return object Object containing h5p json and content json data + */ + public function moveContentDirectory($source, $contentId = NULL) { + if ($source === NULL) { + return NULL; + } + + if ($contentId === NULL || $contentId == 0) { + $target = $this->getEditorPath(); + } + else { + // Use content folder + $target = "{$this->path}/content/{$contentId}"; + } + + $contentSource = $source . DIRECTORY_SEPARATOR . 'content'; + $contentFiles = array_diff(scandir($contentSource), array('.','..', 'content.json')); + foreach ($contentFiles as $file) { + if (is_dir("{$contentSource}/{$file}")) { + self::copyFileTree("{$contentSource}/{$file}", "{$target}/{$file}"); + } + else { + copy("{$contentSource}/{$file}", "{$target}/{$file}"); + } + } + + // Successfully loaded content json of file into editor + $h5pJson = $this->getContent($source . DIRECTORY_SEPARATOR . 'h5p.json'); + $contentJson = $this->getContent($contentSource . DIRECTORY_SEPARATOR . 'content.json'); + + return (object) array( + 'h5pJson' => $h5pJson, + 'contentJson' => $contentJson + ); + } + /** * Checks to see if content has the given file. * Used when saving content. @@ -386,6 +429,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. * @@ -472,4 +525,13 @@ class H5PDefaultStorage implements \H5PFileStorage { return TRUE; } + + /** + * Easy helper function for retrieving the editor path + * + * @return string Path to editor files + */ + private function getEditorPath() { + return ($this->alteditorpath !== NULL ? $this->alteditorpath : "{$this->path}/editor"); + } } diff --git a/h5p-file-storage.interface.php b/h5p-file-storage.interface.php index 3c621f7..394210b 100644 --- a/h5p-file-storage.interface.php +++ b/h5p-file-storage.interface.php @@ -139,7 +139,7 @@ interface H5PFileStorage { * The files must be marked as temporary until the content form is saved. * * @param \H5peditorFile $file - * @param int $contentid + * @param int $contentId */ public function saveFile($file, $contentId); @@ -148,11 +148,22 @@ interface H5PFileStorage { * Used when copy pasting content in H5P. * * @param string $file path + name - * @param string|int $fromid Content ID or 'editor' string - * @param int $toid Target Content ID + * @param string|int $fromId Content ID or 'editor' string + * @param int $toId Target Content ID */ public function cloneContentFile($file, $fromId, $toId); + /** + * Copy a content from one directory to another. Defaults to cloning + * content from the current temporary upload folder to the editor path. + * + * @param string $source path to source directory + * @param string $contentId Id of content + * + * @return object Object containing h5p json and content json data + */ + public function moveContentDirectory($source, $contentId = NULL); + /** * Checks to see if content has the given file. * Used when saving content. @@ -171,4 +182,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..1fe5140 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -19,11 +19,13 @@ interface H5PFrameworkInterface { /** * Fetches a file from a remote server using HTTP GET * - * @param $url - * @param $data + * @param string $url Where you want to get or send data. + * @param array $data Data to post to the URL. + * @param bool $blocking Set to 'FALSE' to instantly time out (fire and forget). + * @param string $stream Path to where the file should be saved. * @return string The content (response body). NULL if something went wrong */ - public function fetchExternalData($url, $data); + public function fetchExternalData($url, $data = NULL, $blocking = TRUE, $stream = NULL); /** * Set the tutorial URL for a library. All versions of the library is set @@ -67,6 +69,14 @@ interface H5PFrameworkInterface { */ public function t($message, $replacements = array()); + /** + * Get URL to file in the specific library + * @param string $libraryFolderName + * @param string $fileName + * @return string URL to file + */ + public function getLibraryFileUrl($libraryFolderName, $fileName); + /** * Get the Path to the last uploaded h5p * @@ -568,6 +578,14 @@ interface H5PFrameworkInterface { * @return boolean */ public function hasPermission($permission, $id = NULL); + + /** + * Replaces existing content type cache with the one passed in + * + * @param object $contentTypeCache Json with an array called 'libraries' + * containing the new content type cache that should replace the old one. + */ + public function replaceContentTypeCache($contentTypeCache); } /** @@ -713,7 +731,7 @@ class H5PValidator { $mainH5pData = null; $libraryJsonData = null; $contentJsonData = null; - $mainH5pExists = $imageExists = $contentExists = FALSE; + $mainH5pExists = $contentExists = FALSE; foreach ($files as $file) { if (in_array(substr($file, 0, 1), array('.', '_'))) { continue; @@ -741,10 +759,6 @@ class H5PValidator { } } } - // Check for h5p.jpg? - elseif (strtolower($file) == 'h5p.jpg') { - $imageExists = TRUE; - } // Content directory holds content. elseif ($file == 'content') { // We do a separate skipContent check to avoid having the content folder being treated as a library @@ -775,7 +789,7 @@ class H5PValidator { } // The rest should be library folders - elseif ($this->h5pF->mayUpdateLibraries()) { + elseif ($this->h5pC->mayUpdateLibraries()) { if (!is_dir($filePath)) { // Ignore this. Probably a file that shouldn't have been included. continue; @@ -858,7 +872,7 @@ class H5PValidator { foreach ($missingLibraries as $libString => $library) { $this->h5pF->setErrorMessage($this->h5pF->t('Missing required library @library', array('@library' => $libString))); } - if (!$this->h5pF->mayUpdateLibraries()) { + if (!$this->h5pC->mayUpdateLibraries()) { $this->h5pF->setInfoMessage($this->h5pF->t("Note that the libraries may exist in the file you uploaded, but you're not allowed to upload new libraries. Contact the site administrator about this.")); } } @@ -929,6 +943,9 @@ class H5PValidator { } } + // Check for icon: + $h5pData['hasIcon'] = file_exists($filePath . DIRECTORY_SEPARATOR . 'icon.svg'); + $validLibrary = $this->isValidH5pData($h5pData, $file, $this->libraryRequired, $this->libraryOptional); $validLibrary = $this->h5pCV->validateContentFiles($filePath, TRUE) && $validLibrary; @@ -1295,7 +1312,7 @@ class H5PStorage { * FALSE otherwise */ public function savePackage($content = NULL, $contentMainId = NULL, $skipContent = FALSE, $options = array()) { - if ($this->h5pF->mayUpdateLibraries()) { + if ($this->h5pC->mayUpdateLibraries()) { // Save the libraries we processed during validation $this->saveLibraries(); } @@ -1678,6 +1695,9 @@ Class H5PExport { abstract class H5PPermission { const DOWNLOAD_H5P = 0; const EMBED_H5P = 1; + const CREATE_RESTRICTED = 2; + const UPDATE_LIBRARIES = 3; + const INSTALL_RECOMMENDED = 4; } abstract class H5PDisplayOptionBehaviour { @@ -1688,6 +1708,9 @@ abstract class H5PDisplayOptionBehaviour { const CONTROLLED_BY_PERMISSIONS = 4; } +abstract class H5PHubEndpoints { + const CONTENT_TYPES = 'api.h5p.org/v1/content-types/'; +} /** * Functions and storage shared by the other H5P classes @@ -2400,69 +2423,99 @@ class H5PCore { } /** - * Fetch a list of libraries' metadata from h5p.org. - * Save URL tutorial to database. Each platform implementation - * is responsible for invoking this, eg using cron + * Communicate with H5P.org and get content type cache. Each platform + * implementation is responsible for invoking this, eg using cron + * * @param bool $fetchingDisabled + * + * @return bool|object Returns endpoint data if found, otherwise FALSE */ public function fetchLibrariesMetadata($fetchingDisabled = FALSE) { // Gather data $uuid = $this->h5pF->getOption('site_uuid', ''); $platform = $this->h5pF->getPlatformInfo(); - $data = array( - 'api_version' => 2, + $registrationData = array( 'uuid' => $uuid, 'platform_name' => $platform['name'], 'platform_version' => $platform['version'], 'h5p_version' => $platform['h5pVersion'], 'disabled' => $fetchingDisabled ? 1 : 0, 'local_id' => hash('crc32', $this->fullPluginPath), - 'type' => $this->h5pF->getOption('site_type', 'local'), - 'num_authors' => $this->h5pF->getNumAuthors(), - 'libraries' => json_encode($this->combineArrayValues(array( - 'patch' => $this->getLibrariesInstalled(), - 'content' => $this->h5pF->getLibraryContentCount(), - 'loaded' => $this->h5pF->getLibraryStats('library'), - 'created' => $this->h5pF->getLibraryStats('content create'), - 'createdUpload' => $this->h5pF->getLibraryStats('content create upload'), - 'deleted' => $this->h5pF->getLibraryStats('content delete'), - 'resultViews' => $this->h5pF->getLibraryStats('results content'), - 'shortcodeInserts' => $this->h5pF->getLibraryStats('content shortcode insert') - ))) + 'type' => $this->h5pF->getOption('site_type', 'local') ); - // Send request - $protocol = (extension_loaded('openssl') ? 'https' : 'http'); - $result = $this->h5pF->fetchExternalData("{$protocol}://h5p.org/libraries-metadata.json", $data); - if (empty($result)) { - return; + // Register site if it is not registered + if (empty($uuid)) { + $protocol = (extension_loaded('openssl') ? 'https' : 'http'); + $endpoint = 'api.h5p.org/v1/sites'; + $registration = $this->h5pF->fetchExternalData("{$protocol}://{$endpoint}", $registrationData); + + // Failed retrieving uuid + if (!$registration) { + $errorMessage = $this->h5pF->t('Site could not be registered with the hub. Please contact your site administrator.'); + H5PCore::ajaxError( + $errorMessage, + 'SITE_REGISTRATION_FAILED' + ); + $this->h5pF->setErrorMessage($errorMessage); + $this->h5pF->setErrorMessage( + $this->h5pF->t('The H5P Hub has been disabled until this problem can be resolved. You may still upload libraries through the "H5P Libraries" page.') + ); + return FALSE; + } + + // Successfully retrieved new uuid + $json = json_decode($registration); + $registrationData['uuid'] = $json->uuid; + $this->h5pF->setOption('site_uuid', $json->uuid); + $this->h5pF->setInfoMessage( + $this->h5pF->t('Your site was successfully registered with the H5P Hub.') + ); + // TODO: Uncomment when key is once again available in H5P Settings +// $this->h5pF->setInfoMessage( +// $this->h5pF->t('You have been provided a unique key that identifies you with the Hub when receiving new updates. The key is available for viewing in the "H5P Settings" page.') +// ); } - // Process results - $json = json_decode($result); - if (empty($json)) { + if ($this->h5pF->getOption('send_usage_statistics', TRUE)) { + $siteData = array_merge( + $registrationData, + array( + 'num_authors' => $this->h5pF->getNumAuthors(), + 'libraries' => json_encode($this->combineArrayValues(array( + 'patch' => $this->getLibrariesInstalled(), + 'content' => $this->h5pF->getLibraryContentCount(), + 'loaded' => $this->h5pF->getLibraryStats('library'), + 'created' => $this->h5pF->getLibraryStats('content create'), + 'createdUpload' => $this->h5pF->getLibraryStats('content create upload'), + 'deleted' => $this->h5pF->getLibraryStats('content delete'), + 'resultViews' => $this->h5pF->getLibraryStats('results content'), + 'shortcodeInserts' => $this->h5pF->getLibraryStats('content shortcode insert') + ))) + ) + ); + } + else { + $siteData = $registrationData; + } + + $result = $this->updateContentTypeCache($siteData); + + // No data received + if (!$result || empty($result)) { return; } // Handle libraries metadata - if (isset($json->libraries)) { - foreach ($json->libraries as $machineName => $libInfo) { - if (isset($libInfo->tutorialUrl)) { - $this->h5pF->setLibraryTutorialUrl($machineName, $libInfo->tutorialUrl); + if (isset($result->libraries)) { + foreach ($result->libraries as $library) { + if (isset($library->tutorialUrl) && isset($library->machineName)) { + $this->h5pF->setLibraryTutorialUrl($library->machineNamee, $library->tutorialUrl); } } } - // Handle new uuid - if ($uuid === '' && isset($json->uuid)) { - $this->h5pF->setOption('site_uuid', $json->uuid); - } - - // Handle latest version of H5P - if (!empty($json->latest)) { - $this->h5pF->setOption('update_available', $json->latest->releasedAt); - $this->h5pF->setOption('update_available_path', $json->latest->path); - } + return $result; } /** @@ -2676,12 +2729,17 @@ class H5PCore { * @param mixed $data * @since 1.6.0 */ - public static function ajaxSuccess($data = NULL) { + public static function ajaxSuccess($data = NULL, $only_data = FALSE) { $response = array( 'success' => TRUE ); if ($data !== NULL) { $response['data'] = $data; + + // Pass data flatly to support old methods + if ($only_data) { + $response = $data; + } } self::printJson($response); } @@ -2690,17 +2748,25 @@ class H5PCore { * Makes it easier to print response when AJAX request fails. * Will exit after printing error. * - * @param string $message + * @param string $message A human readable error message + * @param string $error_code An machine readable error code that a client + * should be able to interpret + * @param null|int $status_code Http response code * @since 1.6.0 */ - public static function ajaxError($message = NULL) { + public static function ajaxError($message = NULL, $error_code = NULL, $status_code = NULL) { $response = array( 'success' => FALSE ); if ($message !== NULL) { $response['message'] = $message; } - self::printJson($response); + + if ($error_code !== NULL) { + $response['errorCode'] = $error_code; + } + + self::printJson($response, $status_code); } /** @@ -2708,8 +2774,13 @@ class H5PCore { * Makes it easier to respond using JSON. * * @param mixed $data + * @param null|int $status_code Http response code */ - private static function printJson($data) { + private static function printJson($data, $status_code = NULL) { + if ($status_code !== NULL) { + http_response_code($status_code); + } + header('Cache-Control: no-cache'); header('Content-type: application/json; charset=utf-8'); print json_encode($data); @@ -2754,6 +2825,191 @@ 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 } + + /** + * Update content type cache + * + * @param object $postData Data sent to the hub + * + * @return bool|object Returns endpoint data if found, otherwise FALSE + */ + public function updateContentTypeCache($postData = NULL) { + $interface = $this->h5pF; + + // Make sure data is sent! + if (!isset($postData) || !isset($postData['uuid'])) { + return $this->fetchLibrariesMetadata(); + } + + $postData['current_cache'] = $this->h5pF->getOption('content_type_cache_updated_at', 0); + + $protocol = (extension_loaded('openssl') ? 'https' : 'http'); + $endpoint = H5PHubEndpoints::CONTENT_TYPES; + $data = $interface->fetchExternalData("{$protocol}://{$endpoint}", $postData); + + if (! $this->h5pF->getOption('hub_is_enabled', TRUE)) { + return TRUE; + } + + // No data received + if (!$data) { + $interface->setErrorMessage( + $interface->t("Couldn't communicate with the H5P Hub. Please try again later.") + ); + return FALSE; + } + + $json = json_decode($data); + + // No libraries received + if (!isset($json->contentTypes) || empty($json->contentTypes)) { + $interface->setErrorMessage( + $interface->t('No content types were received from the H5P Hub. Please try again later.') + ); + return FALSE; + } + + // Replace content type cache + $interface->replaceContentTypeCache($json); + + // Inform of the changes and update timestamp + $interface->setInfoMessage($interface->t('Library cache was successfully updated!')); + $interface->setOption('content_type_cache_updated_at', time()); + return $data; + } + + /** + * Check if the current server setup is valid and set error messages + * + * @return object Setup object with errors and disable hub properties + */ + public function checkSetupErrorMessage() { + $setup = (object) array( + 'errors' => array(), + 'disable_hub' => FALSE + ); + + if (!class_exists('ZipArchive')) { + $setup->errors[] = $this->h5pF->t('Your PHP version does not support ZipArchive.'); + $setup->disable_hub = TRUE; + } + + if (!extension_loaded('mbstring')) { + $setup->errors[] = $this->h5pF->t( + 'The mbstring PHP extension is not loaded. H5P needs this to function properly' + ); + $setup->disable_hub = TRUE; + } + + // Check php version >= 5.2 + $php_version = explode('.', phpversion()); + if ($php_version[0] < 5 || ($php_version[0] === 5 && $php_version[1] < 2)) { + $setup->errors[] = $this->h5pF->t('Your PHP version is outdated. H5P requires version 5.2 to function properly. Version 5.6 or later is recommended.'); + $setup->disable_hub = TRUE; + } + + // Check write access + if (!$this->fs->hasWriteAccess()) { + $setup->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.'); + $setup->disable_hub = TRUE; + } + + $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) { + $setup->errors[] = + $this->h5pF->t('Your PHP max upload size is quite small. With your current setup, you may not upload files larger than %number MB. This might be a problem when trying to upload H5Ps, images and videos. Please consider to increase it to more than 5MB.', array('%number' => number_format($max_upload_size / 1024 / 1024, 2, '.', ' '))); + } + + if ($max_post_size < $byte_threshold) { + $setup->errors[] = + $this->h5pF->t('Your PHP max post size is quite small. With your current setup, you may not upload files larger than %number MB. This might be a problem when trying to upload H5Ps, images and videos. Please consider to increase it to more than 5MB', array('%number' => number_format($max_upload_size / 1024 / 1024, 2, '.', ' '))); + } + + if ($max_upload_size > $max_post_size) { + $setup->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')) { + $setup->errors[] = + $this->h5pF->t('Your server does not have SSL enabled. SSL should be enabled to ensure a secure connection with the H5P hub.'); + $setup->disable_hub = TRUE; + } + + return $setup; + } + + /** + * Check that all H5P requirements for the server setup is met. + */ + public function checkSetupForRequirements() { + $setup = $this->checkSetupErrorMessage(); + + $this->h5pF->setOption('hub_is_enabled', !$setup->disable_hub); + if (!empty($setup->errors)) { + foreach ($setup->errors as $err) { + $this->h5pF->setErrorMessage($err); + } + } + + if ($setup->disable_hub) { + // 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; + } + + /** + * Check if the current user has permission to update and install new + * libraries. + * + * @param bool [$set] Optional, sets the permission + * @return bool + */ + public function mayUpdateLibraries($set = null) { + static $can; + + if ($set !== null) { + // Use value set + $can = $set; + } + + if ($can === null) { + // Ask our framework + $can = $this->h5pF->mayUpdateLibraries(); + } + + return $can; + } } /** diff --git a/js/h5p-action-bar.js b/js/h5p-action-bar.js index 9a0f441..3af1a43 100644 --- a/js/h5p-action-bar.js +++ b/js/h5p-action-bar.js @@ -36,7 +36,7 @@ H5P.ActionBar = (function ($, EventDispatcher) { self.trigger(type); }; H5P.jQuery('
  • ', { - 'class': 'h5p-button h5p-' + (customClass ? customClass : type), + 'class': 'h5p-button h5p-noselect h5p-' + (customClass ? customClass : type), role: 'button', tabindex: 0, title: H5P.t(type + 'Description'), diff --git a/js/h5p-confirmation-dialog.js b/js/h5p-confirmation-dialog.js index a06166b..d34f620 100644 --- a/js/h5p-confirmation-dialog.js +++ b/js/h5p-confirmation-dialog.js @@ -118,8 +118,8 @@ H5P.ConfirmationDialog = (function (EventDispatcher) { // Confirm button var confirmButton = document.createElement('button'); - confirmButton.classList.add('h5p-core-button', - 'h5p-confirmation-dialog-confirm-button'); + confirmButton.classList.add('h5p-core-button'); + confirmButton.classList.add('h5p-confirmation-dialog-confirm-button'); confirmButton.textContent = options.confirmText; // Exit button diff --git a/js/h5p-display-options.js b/js/h5p-display-options.js index 9c8f664..2165152 100644 --- a/js/h5p-display-options.js +++ b/js/h5p-display-options.js @@ -2,7 +2,7 @@ * Utility that makes it possible to hide fields when a checkbox is unchecked */ (function ($) { - function setupHiding () { + function setupHiding() { var $toggler = $(this); // Getting the field which should be hidden: @@ -16,8 +16,39 @@ toggle(); } + function setupRevealing() { + var $button = $(this); + + // Getting the field which should have the value: + var $input = $('#' + $button.data('control')); + + if (!$input.data('value')) { + $button.remove(); + return; + } + + // Setup button action + var revealed = false; + var text = $button.html(); + $button.click(function () { + if (revealed) { + $input.val(''); + $button.html(text); + revealed = false; + } + else { + $input.val($input.data('value')); + $button.html($button.data('hide')); + revealed = true; + } + }); + } + $(document).ready(function () { // Get the checkboxes making other fields being hidden: $('.h5p-visibility-toggler').each(setupHiding); + + // Get the buttons making other fields have hidden values: + $('.h5p-reveal-value').each(setupRevealing); }); })(H5P.jQuery); 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); diff --git a/styles/h5p-admin.css b/styles/h5p-admin.css index 64c025c..100dfea 100644 --- a/styles/h5p-admin.css +++ b/styles/h5p-admin.css @@ -232,7 +232,8 @@ button.h5p-admin.disabled:hover { .h5p-admin-header { margin-top: 1.5em; } -#h5p-library-upload-form.h5p-admin-upload-libraries-form { +#h5p-library-upload-form.h5p-admin-upload-libraries-form, +#h5p-content-type-cache-update-form.h5p-admin-upload-libraries-form { position: relative; margin: 0; diff --git a/styles/h5p-confirmation-dialog.css b/styles/h5p-confirmation-dialog.css index 47f727a..5a8f597 100644 --- a/styles/h5p-confirmation-dialog.css +++ b/styles/h5p-confirmation-dialog.css @@ -113,4 +113,6 @@ button.h5p-confirmation-dialog-exit:hover { .h5p-core-button.h5p-confirmation-dialog-confirm-button:before { content: "\e601"; + margin-top: -6px; + display: inline-block; } diff --git a/styles/h5p.css b/styles/h5p.css old mode 100644 new mode 100755 index 2e5842e..760c916 --- a/styles/h5p.css +++ b/styles/h5p.css @@ -3,11 +3,11 @@ /* Custom H5P font to use for icons. */ @font-face { font-family: 'h5p'; - src: url('../fonts/h5p-core-16.eot?80e76o'); - src: url('../fonts/h5p-core-16.eot?80e76o#iefix') format('embedded-opentype'), - url('../fonts/h5p-core-16.ttf?80e76o') format('truetype'), - url('../fonts/h5p-core-16.woff?80e76o') format('woff'), - url('../fonts/h5p-core-16.svg?80e76o#h5p-core-15') format('svg'); + src: url('../fonts/h5p-core-18.eot?cb8kvi'); + src: url('../fonts/h5p-core-18.eot?cb8kvi#iefix') format('embedded-opentype'), + url('../fonts/h5p-core-18.ttf?cb8kvi') format('truetype'), + url('../fonts/h5p-core-18.woff?cb8kvi') format('woff'), + url('../fonts/h5p-core-18.svg?cb8kvi#h5p') format('svg'); font-weight: normal; font-style: normal; } @@ -30,6 +30,14 @@ html.h5p-iframe, html.h5p-iframe > body { box-sizing: border-box; -moz-box-sizing: border-box; } +.h5p-noselect +{ + -khtml-user-select: none; + -ms-user-select: none; + -moz-user-select: none; + -webkit-user-select: none; + user-select: none; +} html.h5p-iframe .h5p-content { font-size: 16px; line-height: 1.5em; @@ -147,7 +155,11 @@ div.h5p-fullscreen { z-index: 20; } .h5p-iframe-wrapper iframe.h5p-iframe { - width: 100%; + /* Hack for IOS landscape / portrait */ + width: 10px; + min-width: 100%; + *width: 100%; + /* End of hack */ height: 100%; z-index: 10; overflow: hidden; @@ -203,11 +215,10 @@ div.h5p-fullscreen { } .h5p-actions > .h5p-button:before { font-family: 'H5P'; - font-size: 1em; + font-size: 20px; + line-height: 20px; + vertical-align: top; padding-right: 0; - font-size: 1.7em; - line-height: 1.125em; - vertical-align: middle; } .h5p-actions > .h5p-button.h5p-export:before { content: "\e893";