@@ -207,10 +217,102 @@ class="h5p-settings-disable-hub-checkbox"
plugin_slug); ?>
- data is collected on h5p.org.", $this->plugin_slug), array('a' => array('href' => array(), 'target' => array()))), 'https://h5p.org/tracking-the-usage-of-h5p'); ?>
+ data is collected on h5p.org.", $this->plugin_slug),array('a' => array('href' => array(), 'target' => array()))),'https://h5p.org/tracking-the-usage-of-h5p'); ?>
+
+
+ plugin_slug); ?> |
+
+
+
+
+ plugin_slug)); ?>
+
+
+
+
+ Register an account on the H5P Hub", $this->plugin_slug), array('a' => array('href' => array()))), admin_url('options-general.php?page=h5p_settings&task=register&_wpnonce=' . wp_create_nonce( 'h5p_content_hub_registration_form' ))); ?>
+
+
+
+ logo)): ?>
+
+
+
+
+
+
+
+ name)): ?>
+
+ plugin_slug)) ?> |
+ name))?> |
+
+
+
+ contactPerson)): ?>
+
+ plugin_slug)) ?> |
+ contactPerson))?> |
+
+
+
+ email)): ?>
+
+ plugin_slug)) ?> |
+ email))?> |
+
+
+
+ address)): ?>
+
+ plugin_slug)) ?> |
+ address))?> |
+
+
+
+ zip)): ?>
+
+ plugin_slug)) ?> |
+ zip))?> |
+
+
+
+ city)): ?>
+
+ plugin_slug)) ?> |
+ city))?> |
+
+
+
+ country)): ?>
+
+ plugin_slug)) ?> |
+ country))?> |
+
+
+
+ phone)): ?>
+
+ plugin_slug)) ?> |
+ phone))?> |
+
+
+
+
+
+
+
+ Change account settings", $this->plugin_slug), array('a' => array('href' => array()))), admin_url('options-general.php?page=h5p_settings&task=register&_wpnonce=' . wp_create_nonce( 'h5p_content_hub_registration_form' ))); ?>
+
+
+
+
+ |
+
+
diff --git a/admin/views/show-content.php b/admin/views/show-content.php
index 594deb2..c5ae825 100644
--- a/admin/views/show-content.php
+++ b/admin/views/show-content.php
@@ -19,6 +19,15 @@
current_user_can_edit($this->content)): ?>
plugin_slug); ?>
+ current_user_can_share($this->content)): ?>
+ is_content_shared()): ?>
+
plugin_slug); ?>
+
plugin_slug); ?>
+
plugin_slug); ?>
+
+
plugin_slug); ?>
+
+
diff --git a/autoloader.php b/autoloader.php
index cd36648..615032a 100644
--- a/autoloader.php
+++ b/autoloader.php
@@ -41,7 +41,9 @@ function h5p_autoloader($class) {
'H5PLibraryAdmin' => 'admin/class-h5p-library-admin.php',
'H5PEditorWordPressStorage' => 'admin/class-h5p-editor-wordpress-storage.php',
'H5PEditorWordPressAjax' => 'admin/class-h5p-editor-wordpress-ajax.php',
- 'H5PPrivacyPolicy' => 'admin/class-h5p-privacy-policy.php'
+ 'H5PPrivacyPolicy' => 'admin/class-h5p-privacy-policy.php',
+ 'H5PContentHubRegistration' => 'admin/class-h5p-content-hub-registration.php',
+ 'H5PContentSharing' => 'admin/class-h5p-content-sharing.php'
);
}
diff --git a/public/class-h5p-plugin.php b/public/class-h5p-plugin.php
index f8cbc15..ce9b931 100644
--- a/public/class-h5p-plugin.php
+++ b/public/class-h5p-plugin.php
@@ -24,7 +24,7 @@ class H5P_Plugin {
* @since 1.0.0
* @var string
*/
- const VERSION = '1.16.0';
+ const VERSION = '1.16.1';
/**
* The Unique identifier for this plugin.
@@ -218,6 +218,9 @@ public static function update_database() {
changes LONGTEXT NULL,
default_language VARCHAR(32) NULL,
a11y_title VARCHAR(255) NULL,
+ shared TINYINT UNSIGNED NOT NULL DEFAULT 0,
+ synced TINYINT UNSIGNED NULL,
+ content_hub_id BIGINT UNSIGNED NULL,
PRIMARY KEY (id)
) {$charset};");
@@ -380,6 +383,12 @@ public static function update_database() {
KEY created_at (created_at)
) {$charset};");
+ dbDelta("CREATE TABLE {$wpdb->prefix}h5p_content_hub_metadata_cache (
+ language VARCHAR(31) UNIQUE NOT NULL,
+ json LONGTEXT NULL,
+ last_checked INT UNSIGNED NULL
+ ) {$charset};");
+
// Add default setting options
add_option('h5p_frame', TRUE);
add_option('h5p_export', TRUE);
@@ -466,6 +475,8 @@ public static function check_for_updates() {
$pre_1113 = ($v->major < 1 || ($v->major === 1 && $v->minor < 11) ||
($v->major === 1 && $v->minor === 11 && $v->patch < 3)); // < 1.11.3
$pre_1150 = ($v->major < 1 || ($v->major === 1 && $v->minor < 15)); // < 1.15.0
+ $pre_1155 = ($v->major < 1 || ($v->major === 1 && $v->minor < 15) ||
+ ($v->major === 1 && $v->minor === 15 && $v->patch < 5)); // < 1.15.5
// Run version specific updates
if ($pre_120) {
@@ -482,6 +493,10 @@ public static function check_for_updates() {
// Does only add new permissions
self::upgrade_1150();
}
+ if ($pre_1155) {
+ // Does only add new permissions
+ self::upgrade_1155();
+ }
}
if ($pre_180) {
@@ -609,6 +624,27 @@ public static function upgrade_1150() {
}
}
+ /**
+ * Add new permissions for content hub registration and content sharing.
+ *
+ * @since 1.15.5
+ * @global \WP_Roles $wp_roles
+ */
+ public static function upgrade_1155() {
+ global $wp_roles;
+ if (!isset($wp_roles)) {
+ $wp_roles = new WP_Roles();
+ }
+
+ $all_roles = $wp_roles->roles;
+ foreach ($all_roles as $role_name => $role_info) {
+ $role = get_role($role_name);
+ self::map_capability($role, $role_info, 'manage_options', 'manage_h5p_content_hub_registration');
+ self::map_capability($role, $role_info, 'edit_others_h5p_contents', 'share_others_h5p_contents');
+ self::map_capability($role, $role_info, 'edit_h5p_contents', 'share_h5p_contents');
+ }
+ }
+
/**
* Remove duplicate keys that might have been created by a bug in dbDelta.
*
@@ -658,9 +694,12 @@ public static function assign_capabilities() {
self::map_capability($role, $role_info, 'install_plugins', 'disable_h5p_security');
}
self::map_capability($role, $role_info, 'manage_options', 'manage_h5p_libraries');
+ self::map_capability($role, $role_info, 'manage_options', 'manage_h5p_content_hub_registration');
self::map_capability($role, $role_info, 'edit_others_pages', 'install_recommended_h5p_libraries');
self::map_capability($role, $role_info, 'edit_others_pages', 'edit_others_h5p_contents');
+ self::map_capability($role, $role_info, 'edit_others_pages', 'share_others_h5p_contents');
self::map_capability($role, $role_info, 'edit_posts', 'edit_h5p_contents');
+ self::map_capability($role, $role_info, 'edit_posts', 'share_h5p_contents');
self::map_capability($role, $role_info, 'read', 'view_others_h5p_contents');
self::map_capability($role, $role_info, 'read', 'view_h5p_contents');
self::map_capability($role, $role_info, 'read', 'view_h5p_results');
@@ -1607,6 +1646,7 @@ public static function uninstall() {
$wpdb->query("DROP TABLE {$wpdb->prefix}h5p_counters");
$wpdb->query("DROP TABLE {$wpdb->prefix}h5p_events");
$wpdb->query("DROP TABLE {$wpdb->prefix}h5p_tmpfiles");
+ $wpdb->query("DROP TABLE {$wpdb->prefix}h5p_content_hub_metadata_cache");
// Remove settings
delete_option('h5p_version');
diff --git a/public/class-h5p-wordpress.php b/public/class-h5p-wordpress.php
index 58b8b14..510608b 100644
--- a/public/class-h5p-wordpress.php
+++ b/public/class-h5p-wordpress.php
@@ -605,26 +605,30 @@ public function loadLibrary($name, $majorVersion, $minorVersion) {
ARRAY_A
);
- $dependencies = $wpdb->get_results($wpdb->prepare(
- "SELECT hl.name as machineName, hl.major_version as majorVersion, hl.minor_version as minorVersion, hll.dependency_type as dependencyType
- FROM {$wpdb->prefix}h5p_libraries_libraries hll
- JOIN {$wpdb->prefix}h5p_libraries hl ON hll.required_library_id = hl.id
- WHERE hll.library_id = %d",
- $library['libraryId'])
- );
- foreach ($dependencies as $dependency) {
- $library[$dependency->dependencyType . 'Dependencies'][] = array(
- 'machineName' => $dependency->machineName,
- 'majorVersion' => $dependency->majorVersion,
- 'minorVersion' => $dependency->minorVersion,
- );
- }
- if ($this->isInDevMode()) {
- $semantics = $this->getSemanticsFromFile($library['machineName'], $library['majorVersion'], $library['minorVersion']);
- if ($semantics) {
- $library['semantics'] = $semantics;
+ // TODO: Fix independent of OER-Hub
+ if (!empty($library)) {
+ $dependencies = $wpdb->get_results($wpdb->prepare(
+ "SELECT hl.name as machineName, hl.major_version as majorVersion, hl.minor_version as minorVersion, hll.dependency_type as dependencyType
+ FROM {$wpdb->prefix}h5p_libraries_libraries hll
+ JOIN {$wpdb->prefix}h5p_libraries hl ON hll.required_library_id = hl.id
+ WHERE hll.library_id = %d",
+ $library['libraryId'])
+ );
+ foreach ($dependencies as $dependency) {
+ $library[$dependency->dependencyType . 'Dependencies'][] = array(
+ 'machineName' => $dependency->machineName,
+ 'majorVersion' => $dependency->majorVersion,
+ 'minorVersion' => $dependency->minorVersion,
+ );
+ }
+ if ($this->isInDevMode()) {
+ $semantics = $this->getSemanticsFromFile($library['machineName'], $library['majorVersion'], $library['minorVersion']);
+ if ($semantics) {
+ $library['semantics'] = $semantics;
+ }
}
}
+
return $library;
}
@@ -712,6 +716,9 @@ public function loadContent($id) {
, hc.changes AS changes
, hc.default_language AS defaultLanguage
, hc.a11y_title AS a11yTitle
+ , hc.shared AS shared
+ , hc.synced AS synced
+ , hc.content_hub_id AS contentHubId
FROM {$wpdb->prefix}h5p_contents hc
JOIN {$wpdb->prefix}h5p_libraries hl ON hl.id = hc.library_id
WHERE hc.id = %d",
@@ -935,35 +942,132 @@ public function getPlatformInfo() {
}
/**
- * Implements fetchExternalData
+ * Function to multipart encode a uploaded file.
+ *
+ * @param string $name The form data name.
+ * @param string $filepath Path to file location (tmp_name).
+ * @param string $filename Original file name (as posted).
+ * @param string $mimetype Original mime type (as posted).
+ *
+ * @return string A string with the mimetype and the file.
*/
- public function fetchExternalData($url, $data = NULL, $blocking = TRUE, $stream = NULL, $fullData = FALSE, $headers = array(), $files = array(), $method = 'POST') {
+ function h5p_multipart_enc_file($name, $filepath, $filename, $mimetype = 'application/octet-stream') {
+ if (substr($filepath, 0, 1) === '@') {
+ $filepath = substr($filepath, 1);
+ }
+ $data = "Content-Disposition: form-data; name=\"$name\"; filename=\"$filename\"\r\n"; // "file" key.
+ $data .= "Content-Transfer-Encoding: binary\r\n";
+ $data .= "Content-Type: $mimetype\r\n\r\n";
+
+ $data .= file_get_contents($filepath) . "\r\n";
+ return $data;
+ }
+
+ /**
+ * Function to encode text data.
+ *
+ * @param string $name The name of the field.
+ * @param string $value The value of the field.
+ * @return string A string encoded text data.
+ */
+ function h5p_multipart_enc_text($name, $value){
+ return "Content-Disposition: form-data; name=\"$name\"\r\n\r\n$value\r\n"; // TODO: Should we be using rawurlencode ?
+ }
+
+ /**
+ * Implements fetchExternalData.
+ *
+ * @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.
+ * @param bool $fullData Return additional response data such as headers and potentially other data
+ * @param array $headers Headers to send
+ *
+ * @return string|array The content (response body), or an array with data. NULL if something went wrong
+ */
+ public function fetchExternalData($url, $data = NULL, $blocking = TRUE, $stream = NULL, $fullData = FALSE, $headers = [], $files = [], $method = 'POST') {
@set_time_limit(0);
$options = array(
'timeout' => !empty($blocking) ? 30 : 0.01,
'stream' => !empty($stream),
- 'filename' => !empty($stream) ? $stream : FALSE
+ 'filename' => !empty($stream) ? $stream : FALSE,
+ 'headers' => $headers
);
- if ($data !== NULL) {
- // Post
- $options['body'] = $data;
- $response = wp_remote_post($url, $options);
- }
- else {
- // Get
+ if (!empty($files)) {
+ // TODO: Is this required on WordPress? Better option?
+
+ // We have to use multipart form-data encoding with boundary since the
+ // old drupal http client does not natively support posting files
+ $boundary = md5(uniqid('', TRUE));
+ $options['method'] = $method;
+ $encoded_data = '';
+ foreach ($data as $key => $value) {
+ if (empty($value)) {
+ continue;
+ }
- if (empty($options['filename'])) {
- // Support redirects
- $response = wp_remote_get($url);
+ if (is_array($value)) {
+ foreach ($value as $val) {
+ $encoded_data .= "--" . $boundary . "\r\n";
+ $encoded_data .= $this->h5p_multipart_enc_text($key . '[]', $val);
+ }
+ }
+ else {
+ $encoded_data .= "--" . $boundary . "\r\n";
+ $encoded_data .= $this->h5p_multipart_enc_text($key, $value);
+ }
}
- else {
- // Use safe when downloading files
- $response = wp_safe_remote_get($url, $options);
+
+ // TODO: Should we check $_FILES[]['size'] (+ combiend size) before trying to post something we know is too large?
+ foreach ($files as $name => $file) {
+ if ($file === NULL) {
+ continue;
+ }
+ elseif (is_array($file['name'])) {
+ // Array of files uploaded (multiple)
+ for ($i = 0; $i < count($file['name']); $i++) {
+ $encoded_data .= "--" . $boundary . "\r\n";
+ $encoded_data .= $this->h5p_multipart_enc_file($name . '[]', $file['tmp_name'][$i], $file['name'][$i], $file['type'][$i]);
+ }
+ }
+ else {
+ // Single file
+ $encoded_data .= "--" . $boundary . "\r\n";
+ $encoded_data .= $this->h5p_multipart_enc_file($name, $file['tmp_name'], $file['name'], $file['type']);
+ }
}
+
+ $encoded_data .= "--" . $boundary . "--";
+ $options['body'] = $encoded_data;
+ $options['headers'] = array_merge(array(
+ 'Content-Type' => 'multipart/form-data; boundary=' . $boundary
+ ), $options['headers']);
+ }
+ elseif (!empty($data)) {
+ $options['method'] = $method;
+ $options['headers'] = array_merge(array(
+ 'Content-Type' => 'application/x-www-form-urlencoded'
+ ), $options['headers']);
+ $options['body'] = $data;
}
- if (is_wp_error($response)) {
+ if (!empty($options['filename']) && empty($data)) {
+ $response = wp_safe_remote_get($url, $options);
+ }
+ else {
+ $response = wp_remote_request($url, $options);
+ }
+
+ if ($fullData) {
+ return [
+ 'status' => intval($response['response']['code']),
+ 'data' => isset($response['body']) ? $response['body'] : NULL,
+ 'headers' => isset($response['headers']) ? $response['headers'] : NULL
+ ];
+ }
+ else if (is_wp_error($response)) {
$this->setErrorMessage($response->get_error_message(), 'failed-fetching-external-data');
return FALSE;
}
@@ -1304,9 +1408,133 @@ public function libraryHasUpgrade($library) {
)) !== NULL;
}
- // Content hub not implemented in Wordpress, ignore abstract functions
- public function replaceContentHubMetadataCache($metadata, $lang) { return []; }
- public function getContentHubMetadataCache($lang = 'en') { die('Called'); return json_encode([]); }
- public function getContentHubMetadataChecked($lang = 'en') {return []; }
- public function setContentHubMetadataChecked($time, $lang = 'en') { return []; }
+ /**
+ * Implements replaceContentHubMetadataCache
+ *
+ * @param JsonSerializable $metadata Metadata as received from content hub
+ * @param string $lang Language in ISO 639-1
+ *
+ * @return mixed
+ */
+ public function replaceContentHubMetadataCache($metadata, $lang = 'en') {
+ global $wpdb;
+
+ $count = $wpdb->get_var($wpdb->prepare(
+ "SELECT COUNT(*)
+ FROM {$wpdb->prefix}h5p_content_hub_metadata_cache
+ WHERE language = '%s'
+ LIMIT 1",
+ $lang
+ ));
+
+ if ($count === '0') {
+ $result = $wpdb->insert(
+ "{$wpdb->prefix}h5p_content_hub_metadata_cache",
+ array(
+ 'language' => $lang,
+ 'json' => $metadata
+ ),
+ array('%s', '%s')
+ );
+ }
+ else {
+ $result = $wpdb->update(
+ "{$wpdb->prefix}h5p_content_hub_metadata_cache",
+ array('json' => $metadata),
+ array('language' => $lang),
+ array('%s'),
+ array('%s')
+ );
+ }
+ }
+
+ /**
+ * Implements libraryHasUpgrade
+ * Get content hub metadata cache as json from db
+ *
+ * @param string $lang
+ *
+ * @return JsonSerializable|NULL
+ */
+ public function getContentHubMetadataCache($lang = 'en') {
+ global $wpdb;
+
+ return $wpdb->get_var($wpdb->prepare(
+ "SELECT json
+ FROM {$wpdb->prefix}h5p_content_hub_metadata_cache
+ WHERE language = '%s'
+ LIMIT 1",
+ $lang
+ ));
+ }
+
+ /**
+ * Get when content hub metadata cache was last checked
+ *
+ * @param string $lang ISO 639-1 lang code
+ *
+ * @return string|NULL Returns time in RFC 7231 format
+ */
+ public function getContentHubMetadataChecked($lang = 'en') {
+ global $wpdb;
+
+ $results = $wpdb->get_var($wpdb->prepare(
+ "SELECT last_checked
+ FROM {$wpdb->prefix}h5p_content_hub_metadata_cache
+ WHERE language = '%s'
+ LIMIT 1",
+ $lang
+ ));
+
+ if (!empty($results)) {
+ $time = new DateTime();
+ $time->setTimestamp($results);
+ $results = $time->format("D, d M Y H:i:s \G\M\T");
+ }
+
+ return $results;
+ }
+
+ /**
+ * Set when content hub metadata cache was last checked.
+ *
+ * @param int|NULL $time UNIX timestamp for when last check
+ * @param string $lang ISO 639-1 lang code
+ *
+ * @return bool
+ */
+ public function setContentHubMetadataChecked($time, $lang = 'en') {
+ global $wpdb;
+
+ $result = $wpdb->update(
+ "{$wpdb->prefix}h5p_content_hub_metadata_cache",
+ array('last_checked' => $time),
+ array('language' => $lang),
+ array('%d'),
+ array('%s')
+ );
+
+ return TRUE;
+ }
+
+ /**
+ * Callback for reset hub data
+ *
+ * @return void
+ */
+ public function resetHubOrganizationData() {
+ global $wpdb;
+
+ delete_option('h5p_hub_secret');
+
+ $wpdb->update(
+ "{$wpdb->prefix}h5p_contents",
+ array(
+ 'content_hub_id' => NULL,
+ 'synced' => NULL,
+ 'shared' => 0
+ ),
+ array('1' => '1'), // all rows
+ );
+ }
}
diff --git a/uninstall.php b/uninstall.php
index f5500f8..80f63f1 100644
--- a/uninstall.php
+++ b/uninstall.php
@@ -46,6 +46,15 @@
if (isset($role_info['capabilities']['view_h5p_results'])) {
$role->remove_cap('view_h5p_results');
}
+ if (isset($role_info['capabilities']['manage_h5p_content_hub_registration'])) {
+ $role->remove_cap('manage_h5p_content_hub_registration');
+ }
+ if (isset($role_info['capabilities']['share_h5p_contents'])) {
+ $role->remove_cap('share_h5p_contents');
+ }
+ if (isset($role_info['capabilities']['share_others_h5p_contents'])) {
+ $role->remove_cap('share_others_h5p_contents');
+ }
}
global $wpdb;