diff --git a/modules/h5peditor/h5peditor.module b/modules/h5peditor/h5peditor.module
index 471c0498..8e9707ed 100644
--- a/modules/h5peditor/h5peditor.module
+++ b/modules/h5peditor/h5peditor.module
@@ -4,6 +4,9 @@
  * Contains h5peditor.module
  */
 
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityInterface;
+
  /**
   * Implements hook_library_info_build().
   *
@@ -30,3 +33,44 @@ function h5peditor_library_info_build() {
 
    return $libraries;
  }
+
+/**
+ * Implements hook_entity_update().
+ */
+function h5peditor_entity_update(EntityInterface $entity) {
+  if ($entity instanceof ContentEntityInterface && !empty($entity->original)) {
+    foreach ($entity->getFieldDefinitions() as $field_definition) {
+
+      // Move user results for any h5p content from the old content id to the
+      // new content id. Even if the quiz completely changed, we shouldn't throw
+      // throw out results. The widget should take care of that beforehand.
+      // Updating quiz results is handled here instead of the widget to support
+      // reverting revisions without losing quiz results.
+      if ($field_definition->getType() == 'h5p') {
+        $field_name = $field_definition->getName();
+        try {
+          $original_entity = $entity->original->getTranslation($entity->language()->getId());
+        }
+        catch (InvalidArgumentException $e) {
+          $original_entity = $entity->original;
+        }
+        $original = $original_entity->{$field_name};
+        $updated = $entity->{$field_name};
+        // It only makes sense to move results if field cardinality matches.
+        if (count($updated) == count($original)) {
+          foreach ($updated as $delta => $new_item) {
+            $old_content_id = $original[$delta]->getValue()['h5p_content_id'];
+            $new_content_id = $new_item->getValue()['h5p_content_id'];
+
+            db_update('h5p_points')
+              ->fields([
+                'content_id' => $new_content_id,
+              ])
+            ->condition('content_id', $old_content_id)
+            ->execute();
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/modules/h5peditor/src/Plugin/Field/FieldWidget/H5PEditorWidget.php b/modules/h5peditor/src/Plugin/Field/FieldWidget/H5PEditorWidget.php
index 725df468..46e5f25e 100644
--- a/modules/h5peditor/src/Plugin/Field/FieldWidget/H5PEditorWidget.php
+++ b/modules/h5peditor/src/Plugin/Field/FieldWidget/H5PEditorWidget.php
@@ -25,6 +25,21 @@ class H5PEditorWidget extends H5PWidgetBase {
 
   protected static $counter = 0;
 
+  /**
+   * Whether the new quiz will become the published revision.
+   *
+   * @var bool
+   */
+  protected $willBePublished = FALSE;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
+    $this->willBePublished = $items->getEntity()->isPublished();
+    return parent::extractFormValues($items, $form, $form_state);
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -145,13 +160,47 @@ class H5PEditorWidget extends H5PWidgetBase {
       $content['id'] = $value['id'];
     }
 
-    // Save the new content
-    $return_value['h5p_content_id'] = $core->saveContent($content);
+    if (isset($content['id'])) {
+      $return_value['h5p_content_id'] = $core->h5pF->updateContent($content);
+    }
+    else {
+      $return_value['h5p_content_id'] = $core->h5pF->insertContent($content);
+    }
+
+    // Do a checksum on the params. Only reset user data if the H5P content is
+    // different.
+    $h5p_changed = md5(serialize($old_params)) !== md5(serialize($params->params));
+
+    // Some user data for content has to be reset when the content changes.
+    if ($h5p_changed) {
+      $core->h5pF->resetContentUserData($return_value['h5p_content_id']);
+    }
 
     // If we had existing content and did a new revision we need to make a copy
     // of the content folder from the old revision
     if ($value['id'] && ($do_new_revision || $value['new_translation'])) {
       $core->fs->cloneContent($value['id'], $return_value['h5p_content_id']);
+
+      // Retain quiz scores when a new quiz revision is published.
+      if ($this->willBePublished) {
+        db_update('h5p_points')
+          ->fields([
+            'content_id' => $return_value['h5p_content_id'],
+          ])
+          ->condition('content_id', $value['id'])
+          ->execute();
+      }
+
+      // If H5P form has not changed and there is a new revision then update
+      // any existing user data with the latest content id.
+      if (!$h5p_changed && $value['id'] != $return_value['h5p_content_id'] && $this->willBePublished) {
+        db_update('h5p_content_user_data')
+          ->fields(array(
+            'content_main_id' => $return_value['h5p_content_id']
+          ))
+          ->condition('content_main_id', $value['id'])
+          ->execute();
+      }
     }
 
     // Keep new files, delete files from old parameters
