From d0851c208012fab840fcae0f9a5e95da8f487bb3 Mon Sep 17 00:00:00 2001
From: Dave Hall <dave.hall@skwashd.com>
Date: Sun, 25 Nov 2012 22:58:56 +1100
Subject: [PATCH] Issue #1812202 by skwashd: Add revision UUIDs to Entity API
 and node

---
 .../Core/Entity/DatabaseStorageController.php      |   18 ++++++++
 core/lib/Drupal/Core/Entity/Entity.php             |   10 +++++
 core/lib/Drupal/Core/Entity/EntityInterface.php    |   11 +++++
 .../node/lib/Drupal/node/NodeStorageController.php |    1 +
 .../lib/Drupal/node/Plugin/Core/Entity/Node.php    |   10 ++++-
 core/modules/node/node.install                     |   43 ++++++++++++++++++++
 6 files changed, 92 insertions(+), 1 deletion(-)

diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index bf515de..dbbcf1f 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -136,6 +136,14 @@ public function __construct($entityType) {
       $this->revisionKey = FALSE;
     }
 
+    // Check if the entity type supports revision UUIDs.
+    if (!empty($this->entityInfo['entity_keys']['vuuid'])) {
+      $this->vuuidKey = $this->entityInfo['entity_keys']['vuuid'];
+    }
+    else {
+      $this->vuuidKey = FALSE;
+    }
+
     // Check if the entity type supports static caching of loaded entities.
     $this->cache = !empty($this->entityInfo['static_cache']);
   }
@@ -555,6 +563,16 @@ protected function saveRevision(EntityInterface $entity) {
     // ensure that a new revision will actually be created.
     if ($entity->isNewRevision() && $record[$this->revisionKey]) {
       $record[$this->revisionKey] = NULL;
+
+      // Generating a new revision should generate a new revision UUID. There are
+      // cases where external sources may preset the revision UUID, so we need to
+      // allow for that here.
+      if ($this->vuuidKey && (empty($record['vuuid'])
+        || (isset($entity->original)
+          && $entity->original->{$this->vuuidKey} == $record[$this->vuuidKey]))) {
+        $uuid = new Uuid();
+        $record[$this->vuuidKey] = $uuid->generate();
+      }
     }
 
     // Cast to object as preSaveRevision() expects one to be compatible with the
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 979614f..7c14a75 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -88,6 +88,13 @@ public function uuid() {
   }
 
   /**
+   * Implements EntityInterface::vuuid().
+   */
+  public function vuuid() {
+    return isset($this->vuuid) ? $this->vuuid : NULL;
+  }
+
+  /**
    * Implements EntityInterface::isNew().
    */
   public function isNew() {
@@ -344,6 +351,9 @@ public function createDuplicate() {
       $uuid = new Uuid();
       $duplicate->{$entity_info['entity_keys']['uuid']} = $uuid->generate();
     }
+    if (!empty($entity_info['entity_keys']['vuuid'])) {
+      $duplicate->{$entity_info['entity_keys']['vuuid']} = NULL;
+    }
     return $duplicate;
   }
 
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index 9b751d4..ee7bb5b 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -40,6 +40,17 @@ public function id();
   public function uuid();
 
   /**
+   * Returns the revision UUID (Universally Unique Identifier) for the entity.
+   *
+   * The revision UUID is guaranteed to be unique and can be used to identify
+   * an entity revision across multiple systems.
+   *
+   * @return string
+   *   The revision UUID for the entity, or NULL if the entity does not have one.
+   */
+  public function vuuid();
+
+  /**
    * Returns whether the entity is new.
    *
    * Usually an entity is new if no ID exists for it yet. However, entities may
diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php
index d855384..a7cd045 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorageController.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Component\Uuid\Uuid;
 
 /**
  * Controller class for nodes.
diff --git a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
index 1fd8fc8..0f537a4 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
@@ -34,7 +34,8 @@
  *     "revision" = "vid",
  *     "bundle" = "type",
  *     "label" = "title",
- *     "uuid" = "uuid"
+ *     "uuid" = "uuid",
+ *     "vuuid" = "vuuid"
  *   },
  *   bundle_keys = {
  *     "bundle" = "type"
@@ -89,6 +90,13 @@ class Node extends Entity implements ContentEntityInterface {
   public $uuid;
 
   /**
+   * The node revision UUID.
+   *
+   * @var string
+   */
+  public $vuuid;
+
+  /**
    * The node content type (bundle).
    *
    * @var string
diff --git a/core/modules/node/node.install b/core/modules/node/node.install
index c6fd7e6..b3abab5 100644
--- a/core/modules/node/node.install
+++ b/core/modules/node/node.install
@@ -213,6 +213,12 @@ function node_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
       ),
+      'vuuid' => array(
+        'description' => 'The universally unique identifier (UUID) for this version.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
       'uid' => array(
         'description' => 'The {users}.uid that created this version.',
         'type' => 'int',
@@ -269,6 +275,9 @@ function node_schema() {
       'uid' => array('uid'),
     ),
     'primary key' => array('vid'),
+    'unique keys' => array(
+      'vuuid' => array('vuuid'),
+    ),
     'foreign keys' => array(
       'versioned_node' => array(
         'table' => 'node',
@@ -717,6 +726,40 @@ function node_update_8012() {
   update_module_enable(array('history'));
 }
 
+/**
+ * Create a revision UUID column for node_revision and generate the UUIDs.
+ */
+function node_update_8013(&$sandbox) {
+  $spec = array(
+    'description' => 'The universally unique identifier (UUID) for this version.',
+    'type' => 'varchar',
+    'length' => 128,
+    'not null' => FALSE,
+  );
+  $keys = array(
+    'unique keys' => array(
+      'vuuid' => array('vuuid'),
+    ),
+  );
+  // Account for sites having the contributed UUID module installed.
+  if (db_field_exists('node_revision', 'vuuid')) {
+    db_change_field('node_revision', 'vuuid', 'vuuid', $spec, $keys);
+  }
+  else {
+    db_add_field('node_revision', 'vuuid', $spec, $keys);
+  }
+
+  if (!isset($sandbox['progress'])) {
+    $sandbox['progress'] = 0;
+    $sandbox['last'] = 0;
+    $sandbox['max'] = db_query('SELECT COUNT(vid) FROM {node_revision} WHERE vuuid IS NULL')->fetchField();
+  }
+
+  $vids = db_query_range('SELECT vid FROM {node_revision} WHERE vid > :vid AND vuuid IS NULL ORDER BY vid ASC', 0, 10, array(':vid' => $sandbox['last']))->fetchCol();
+  update_add_uuids($sandbox, 'node_revision', 'vid', $vids);
+
+  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
+}
 
 /**
  * @} End of "addtogroup updates-7.x-to-8.x"
-- 
1.7.10.4

