diff --git a/modules/h5peditor/h5peditor.module b/modules/h5peditor/h5peditor.module
index 471c0498..630f8c6f 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'];
+
+            \Drupal::database()->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..775f0adb 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,38 @@ 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']);
+
+      if ($this->willBePublished) {
+        // Retain quiz scores when a new quiz revision is published.
+        $this->syncUserPoints($value, $return_value['h5p_content_id']);
+
+        // 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->syncContentUserData($value, $return_value['h5p_content_id']);
+        }
+      }
     }
 
     // Keep new files, delete files from old parameters
@@ -167,4 +207,42 @@ class H5PEditorWidget extends H5PWidgetBase {
     return $return_value;
   }
 
+  /**
+   * Synchronize the user points.
+   *
+   * @param array $value
+   *   The current value.
+   * @param int $content_id
+   *   The content id.
+   */
+  protected function syncUserPoints(array $value, $content_id){
+    \Drupal::database()->update('h5p_points')
+      ->fields([
+        'content_id' => $content_id,
+      ])
+      ->condition('content_id', $value['id'])
+      ->execute();
+  }
+
+  /**
+   * Synchronize the content user data.
+   *
+   * @param array $value
+   *   The current value.
+   * @param int $content_id
+   *   The content id.
+   */
+  protected function syncContentUserData(array $value, $content_id) {
+    $settings = H5PDrupal::getGenericH5PIntegrationSettings();
+    // Only update the data if we're actually capturing it.
+    if ($settings['saveFreq'] !== FALSE) {
+      \Drupal::database()->update('h5p_content_user_data')
+        ->fields([
+          'content_main_id' => $content_id
+        ])
+        ->condition('content_main_id', $value['id'])
+        ->execute();
+    }
+  }
+
 }
