uuid.module in Universally Unique IDentifier 6
Same filename and directory in other branches
Main module functions for the uuid module.
File
uuid.moduleView source
<?php
/**
* @file
* Main module functions for the uuid module.
*/
/**
* Implementation of hook_menu().
*/
function uuid_menu() {
$items = array();
$items['admin/settings/uuid'] = array(
'title' => 'Universally Unique IDentifier',
'description' => 'Configure automatically UUID generation settings.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'uuid_admin',
),
'access arguments' => array(
'administer content types',
),
'type' => MENU_NORMAL_ITEM,
'file' => 'uuid.admin.inc',
);
return $items;
}
/**
* Implementation of hook_nodeapi().
*/
function uuid_nodeapi(&$node, $op, $teaser, $page) {
$automatic_types = variable_get('uuid_automatic_for_nodes', array());
// Return early if we don't automatically generate UUIDs for this type.
if (empty($automatic_types[$node->type])) {
return;
}
switch ($op) {
case 'load':
return uuid_node_load($node);
case 'insert':
// Handle the case where automatic UUID generation is turned OFF but a
// node is created, node_save(), intentionally with the $node->uuid
// attribute.
if (isset($node->uuid) && uuid_is_valid($node->uuid)) {
db_query("INSERT INTO {uuid_node} (nid, uuid) VALUES (%d, '%s')", $node->nid, $node->uuid);
if (isset($node->revision_uuid) && uuid_is_valid($node->revision_uuid)) {
db_query("INSERT INTO {uuid_node_revisions} (vid, uuid, nid) VALUES (%d, '%s', %d)", $node->vid, $node->revision_uuid, $node->nid);
}
else {
// Attach new revision uuid to node, don't just insert it.
$node->revision_uuid = uuid_uuid();
db_query("INSERT INTO {uuid_node_revisions} (vid, uuid, nid) VALUES (%d, '%s', %d)", $node->vid, $node->revision_uuid, $node->nid);
}
}
elseif (in_array($node->type, $automatic_types, TRUE)) {
$node->uuid = uuid_uuid();
$node->revision_uuid = uuid_uuid();
db_query("INSERT INTO {uuid_node} (nid, uuid) VALUES (%d, '%s')", $node->nid, $node->uuid);
db_query("INSERT INTO {uuid_node_revisions} (vid, uuid, nid) VALUES (%d, '%s', %d)", $node->vid, $node->revision_uuid, $node->nid);
}
break;
case 'update':
// Get existing UUID info from the DB.
$uuid_information = uuid_node_load($node);
// If the $node object is not fully loaded, set any existing UUID properties.
// http://drupal.org/node/1065364
if (!empty($uuid_information['uuid']) && empty($node->uuid)) {
$node->uuid = $uuid_information['uuid'];
}
if (!empty($uuid_information['revision_uuid']) && empty($node->revision_uuid)) {
$node->revision_uuid = $uuid_information['revision_uuid'];
}
$auto_create = in_array($node->type, $automatic_types, TRUE);
$uuid_exists = isset($node->uuid) && uuid_is_valid($node->uuid);
// Create a UUID if this is an automatic type but no UUID exists. Save the
// UUID if it's valid but is not in the database.
if ($uuid_exists || $auto_create) {
$node->uuid = $uuid_exists ? $node->uuid : uuid_uuid();
// Update the existing UUID.
if ($uuid_information['uuid'] === FALSE) {
db_query("INSERT INTO {uuid_node} (nid, uuid) VALUES (%d, '%s')", $node->nid, $node->uuid);
}
elseif ($uuid_information['uuid'] != $node->uuid) {
db_query("UPDATE {uuid_node} SET uuid = '%s' WHERE nid = %d", $node->uuid, $node->nid);
}
$uuid_exists = TRUE;
}
$rev_uuid_exists = isset($node->revision_uuid) && uuid_is_valid($node->revision_uuid);
$rev_uuid_in_db = $rev_uuid_exists ? db_result(db_query("SELECT 1 FROM {uuid_node_revisions} WHERE uuid = '%s'", $node->revision_uuid)) : FALSE;
$rev_uuid_needs_saved = $rev_uuid_exists && !$rev_uuid_in_db;
// If a UUID exists, save a revision UUID if none exists or this is a new
// revision, or there's a programmatic revision set.
if ($uuid_exists && (empty($uuid_information['revision_uuid']) || !empty($node->revision) || $rev_uuid_needs_saved)) {
// We need to determine when to generate a new revision UUID. This
// should only happen if this is a new revision and if the existing
// revision uuid already exists in the database or doesn't exist at all.
$generate_new = !empty($node->revision) && ($rev_uuid_in_db || !$rev_uuid_exists);
$node->revision_uuid = $generate_new ? uuid_uuid() : $node->revision_uuid;
// Be sure to consider the case where the revision_uuid may be changing
// programmatically, so try updating first.
db_query("UPDATE {uuid_node_revisions} SET uuid = '%s' WHERE vid = %d", $node->revision_uuid, $node->vid);
if (!db_affected_rows()) {
db_query("INSERT INTO {uuid_node_revisions} (vid, uuid, nid) VALUES (%d, '%s', %d)", $node->vid, $node->revision_uuid, $node->nid);
}
}
break;
case 'delete':
db_query('DELETE FROM {uuid_node} WHERE nid = %d', $node->nid);
// Delete the revision uuids for the node.
db_query('DELETE FROM {uuid_node_revisions} WHERE nid = %d', $node->nid);
break;
case 'delete revision':
db_query('DELETE FROM {uuid_node_revisions} WHERE vid = %d', $node->vid);
break;
}
}
/**
* Load the UUID information for a node.
*
* @param $node
* Node object to load UUID information for.
*
* @return
* Array with UUID information.
*/
function uuid_node_load($node) {
if ($arr = db_fetch_array(db_query('SELECT un.uuid AS uuid, unr.uuid AS revision_uuid FROM {uuid_node} un LEFT JOIN {uuid_node_revisions} unr ON unr.vid=%d WHERE un.nid=%d', $node->vid, $node->nid))) {
return $arr;
}
else {
return array(
'uuid' => FALSE,
'revision_uuid' => FALSE,
);
}
}
/**
* Implementation of hook_views_api().
*/
function uuid_views_api() {
return array(
'api' => 2,
'path' => drupal_get_path('module', 'uuid'),
);
}
/**
* Returns the node associated with a UUID. Uses db_rewrite_sql() to ensure
* node_access rules are preserved.
*
* @return
* Either the $node object, or FALSE on failure.
*/
function node_get_by_uuid($uuid) {
$nid = db_result(db_query(db_rewrite_sql("SELECT n.nid\n FROM {node} AS n\n INNER JOIN {uuid_node} AS un ON n.nid = un.nid\n WHERE un.uuid = '%s'"), $uuid));
if ($nid) {
return node_load($nid);
}
else {
return FALSE;
}
}
/**
* Returns the node associated with a revision UUID.
*
* @param $revision_uuid
* The uuid to use for the node lookup.
*
* @return
* Either the $node object, or FALSE on failure.
*/
function node_get_by_revision_uuid($revision_uuid) {
$node = db_fetch_object(db_query(db_rewrite_sql("SELECT n.nid, n.vid\n FROM {node} AS n\n INNER JOIN {uuid_node_revisions} AS unr ON n.vid = unr.vid\n WHERE unr.uuid = '%s'"), $revision_uuid));
if ($node->nid && $node->vid) {
return node_load($node->nid, $node->vid);
}
else {
return FALSE;
}
}
/**
* Implementation of hook_user().
*/
function uuid_user($op, &$edit, &$user, $category = NULL) {
switch ($op) {
case 'load':
$user->uuid = db_result(db_query('SELECT uuid FROM {uuid_users} WHERE uid = %d', $user->uid));
break;
case 'insert':
$insert = NULL;
if (isset($user->uuid) && uuid_is_valid($user->uuid)) {
$insert = $user->uuid;
}
else {
if (isset($edit['uuid']) && uuid_is_valid($edit['uuid'])) {
$insert = $user->uuid = $edit['uuid'];
}
else {
if (variable_get('uuid_automatic_for_users', FALSE)) {
$insert = $user->uuid = uuid_uuid();
}
}
}
if ($insert) {
db_query("INSERT INTO {uuid_users} (uid, uuid) VALUES (%d, '%s')", $user->uid, $user->uuid);
}
break;
case 'update':
if (isset($user->uuid) && uuid_is_valid($user->uuid)) {
$exists = db_result(db_query('SELECT 1 FROM {uuid_users} WHERE uid = %d', $user->uid));
if (!$exists) {
db_query("INSERT INTO {uuid_users} (uid, uuid) VALUES (%d, '%s')", $user->uid, $user->uuid);
}
}
break;
case 'delete':
db_query('DELETE FROM {uuid_users} WHERE uid = %d', $user->uid);
break;
}
}
/**
* Returns the user associated with a UUID.
*
* @return
* Either the $account object, or FALSE on failure.
*/
function user_get_by_uuid($uuid) {
$uid = db_result(db_query("SELECT uid FROM {uuid_users} WHERE uuid = '%s'", $uuid));
if ($uid) {
return user_load(array(
'uid' => $uid,
));
}
else {
return FALSE;
}
}
/**
* Implementation of hook_taxonomy().
*/
function uuid_taxonomy($op, $type, $array = NULL) {
if (!isset($array['vid']) || !in_array($array['vid'], variable_get('uuid_automatic_for_taxonomy', array())) && !isset($array['uuid'])) {
// Nothing to do.
return;
}
switch ($type) {
case 'term':
$keyfield = 'tid';
$table = 'uuid_term_data';
$key = $array['tid'];
break;
case 'vocabulary':
$keyfield = 'vid';
$table = 'uuid_vocabulary';
$key = $array['vid'];
break;
default:
// Nothing to do.
return;
break;
}
switch ($op) {
case 'insert':
if (empty($array['uuid']) || !uuid_is_valid($array['uuid'])) {
$array['uuid'] = uuid_uuid();
}
db_query("INSERT INTO {$table} ({$keyfield}, uuid) VALUES (%d, '%s')", $key, $array['uuid']);
break;
case 'update':
$existing_uuid = db_result(db_query("SELECT uuid FROM {$table} WHERE {$keyfield} = %d", $key));
if ($existing_uuid) {
// If there is an existing uuid, but no new one, remove the existing one.
if (!isset($array['uuid']) || empty($array['uuid'])) {
uuid_taxonomy('delete', $type, $array);
}
elseif ($array['uuid'] != $existing_uuid) {
db_query("UPDATE {$table} SET uuid = '%s' WHERE {$keyfield} = %d", $array['uuid'], $key);
}
// Otherwise, don't do anything. No update necessary.
}
else {
uuid_taxonomy('insert', $type, $array);
}
break;
case 'delete':
db_query("DELETE FROM {$table} WHERE {$keyfield} = %d", $keyfield, $key);
break;
}
}
/**
* Implementation of hook_comment().
*/
function uuid_comment(&$a1, $op) {
switch ($op) {
// Make sure that a new entry gets made in the comments_uuid table when new content
// is added.
case 'insert':
if (!empty($a1['uuid']) && uuid_is_valid($a1['uuid'])) {
db_query("INSERT INTO {uuid_comments} (cid, uuid) VALUES (%d, '%s')", $a1['cid'], $a1['uuid']);
}
else {
if (variable_get('uuid_automatic_for_comments', FALSE)) {
db_query("INSERT INTO {uuid_comments} (cid, uuid) VALUES (%d, '%s')", $a1['cid'], uuid_uuid());
}
}
break;
case 'update':
if (isset($a1['uuid']) && uuid_is_valid($a1['uuid'])) {
$exists = db_result(db_query('SELECT 1 FROM {uuid_comments} WHERE cid = %d', $a1['cid']));
if (!$exists && variable_get('uuid_automatic_for_comments', FALSE)) {
db_query("INSERT INTO {uuid_comments} (cid, uuid) VALUES (%d, '%s')", $a1['cid'], uuid_uuid());
}
}
break;
// Clean up comments_uuid table when content is deleted.
case 'delete':
db_query("DELETE FROM {uuid_comments} WHERE cid = %d", $a1->cid);
break;
}
}
/**
* Determines if a UUID is valid.
*/
function uuid_is_valid($uuid) {
return (bool) preg_match('/^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$/', $uuid);
}
/**
* Generate and return an Universally Unique IDentifier (UUID).
*
* @see http://www.ietf.org/rfc/rfc4122.txt
* @see http://php.net/manual/en/function.uniqid.php#65879
*
* @return
* An UUID, made up of 32 hex digits and 4 hyphens.
*/
function uuid_uuid() {
// If we're not using postgres, the database will give us a UUID.
if ($GLOBALS['db_type'] != 'pgsql') {
return db_result(db_query('SELECT UUID()'));
}
// If the function provided by the uuid pecl extension is available, use that.
if (function_exists('uuid_create')) {
return uuid_create(UUID_TYPE_DEFAULT);
}
// Fall back to generating a Universally Unique IDentifier version 4.
// Base on http://php.net/manual/en/function.uniqid.php#65879
// The field names refer to RFC 4122 section 4.1.2.
return sprintf('%04x%04x-%04x-%03x4-%04x-%04x%04x%04x', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 4095), bindec(substr_replace(sprintf('%016b', mt_rand(0, 65535)), '01', 6, 2)), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535));
}
/**
* Token integration. UUID module provides the following tokens to be used on
* the node, user, comment and taxonomy contexts.
* - Global:
* - uuid: an arbitrary uuid generated on demand not linked with any object.
* - Node:
* - uuid: the node's uuid.
* - revision-uuid: the node revision's uuid
* - author-uuid: the node author's uuid
* - User:
* - uuid: the user's uuid.
*
* @todo Still to complete taxonomy and comment tokens.
*/
/**
* Implementation of hook_token_values().
*/
function uuid_token_values($type, $object = NULL, $options = array()) {
$tokens = array();
switch ($type) {
case 'global':
$tokens['uuid'] = uuid_uuid();
break;
case 'user':
$tokens['uuid'] = isset($object->uuid) ? $object->uuid : NULL;
break;
case 'node':
$tokens['uuid'] = isset($object->uuid) ? $object->uuid : NULL;
$tokens['revision-uuid'] = isset($object->revision_uuid) ? $object->revision_uuid : NULL;
$tokens['author-uuid'] = db_result(db_query("SELECT uuid FROM {uuid_users} WHERE uid = %d", $object->uid));
break;
}
return $tokens;
}
/**
* Implementation of hook_token_list().
*/
function uuid_token_list($type = 'all') {
$tokens = array();
if ($type == 'global' || $type == 'all') {
$tokens['global']['uuid'] = t("An arbitrary UUID value");
}
if ($type == 'node' || $type == 'all') {
$tokens['node']['uuid'] = t("The node UUID value");
$tokens['node']['revision_uuid'] = t("The node revision UUID value");
$tokens['node']['author-uuid'] = t("The node author UUID value");
}
if ($type == 'user' || $type == 'all') {
$tokens['user']['uuid'] = t("The user UUID value");
}
return $tokens;
}
Functions
Name | Description |
---|---|
node_get_by_revision_uuid | Returns the node associated with a revision UUID. |
node_get_by_uuid | Returns the node associated with a UUID. Uses db_rewrite_sql() to ensure node_access rules are preserved. |
user_get_by_uuid | Returns the user associated with a UUID. |
uuid_comment | Implementation of hook_comment(). |
uuid_is_valid | Determines if a UUID is valid. |
uuid_menu | Implementation of hook_menu(). |
uuid_nodeapi | Implementation of hook_nodeapi(). |
uuid_node_load | Load the UUID information for a node. |
uuid_taxonomy | Implementation of hook_taxonomy(). |
uuid_token_list | Implementation of hook_token_list(). |
uuid_token_values | Implementation of hook_token_values(). |
uuid_user | Implementation of hook_user(). |
uuid_uuid | Generate and return an Universally Unique IDentifier (UUID). |
uuid_views_api | Implementation of hook_views_api(). |