first import

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-08 11:40:19 +02:00
commit 1bc61b12ad
8435 changed files with 1582817 additions and 0 deletions

View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -0,0 +1,32 @@
INTRODUCTION
------------
This module provides an API for adding universally unique identifiers (UUID) to
Drupal objects, most notably entities.
FEATURES
--------
* Automatic UUID generation:
UUIDs will be generated for all core entities. An API is provided for other
modules to enable support for custom entities.
* UUID API for entities, properties and fields:
With this unified API you can load entities with entity_uuid_load() so that
all supported properties and fields are made with UUID references. You can
also save entities formatted this way with entity_uuid_save() (depends on
Entity API).
* Export entities to use as default/demo content:
The integration with Features module provides the ability to export UUID
enabled entities with intact dependencies and references to other entities.
This functionality depends on Deploy module 7.x-2.0-alpha1 (soon to be
released) and is probably the most robust way for installation profiles and
distributions to provide demo content!
* Services integration:
The integration with Services module alters all UUID enabled entity resources
(nodes, users, taxonomies etc) to be based on UUIDs instead. This way it
becomes easier to share and integrate content between sites. This
functionality is used by Deploy module.
* More integrations:
UUID module integrates with Views, Token, Rules and provides some CTools
plugins.

View File

@@ -0,0 +1,43 @@
INTRODUCTION
------------
This document describes how to upgrade between major and minor versions of the
UUID module.
Upgrading between all major and minor versions have been successfully tested
when following the steps described in UPGRADE.txt from Drupal core, with
addition to the notes in this document.
Taking full backups of your files and database is always recommended before
doing any upgrades.
UPGRADING FROM UUID 6.x to UUID 7.x
-----------------------------------
In addition to the steps described by UPGRADE.txt from Drupal core, you need to
follow these notes:
* Before upgrading, update to the latest UUID 6.x-1.x-dev
* When doing your upgrade, you must first upgrade to UUID 7.x-1.0-alpha1 in
order to not lose any data during the upgrade. The reason is because the
sub-modules responsible for the data migration between 6.x and 7.x was
removed after the release of UUID 7.x-1.0-alpha1.
* After upgrading to UUID 7.x-1.0-alpha1 you can proceed upgrading to later
versions of UUID.
UPGRADING FROM UUID 7.x-1.0-alpha1 TO LATER VERSION
---------------------------------------------------
Make sure to follow these notes when upgrading from 7.x-1.0-alpha1 to later
versions:
* Before upgrading, disable UUID and all UUID sub-modules.
* Run update.php.
* Enable the UUID module. The entity specific UUID sub-modules doesn't exist
anymore. They are replaced with more efficient functionality in the UUID
module. No data is lost.

View File

@@ -0,0 +1,72 @@
<?php
/**
* @file
* Plugin to provide an argument handler for all entity IDs.
*/
/**
* Plugins are described by creating a $plugin array which will be used
* by the system that includes this file.
*/
$plugin = array(
'title' => t("Entity: UUID"),
'description' => t('Creates an entity context from an entity UUID argument.'),
'context' => 'uuid_entity_uuid_context',
'get child' => 'uuid_entity_uuid_get_child',
'get children' => 'uuid_entity_uuid_get_children',
);
function uuid_entity_uuid_get_child($plugin, $parent, $child) {
$plugins = uuid_entity_uuid_get_children($plugin, $parent);
return $plugins[$parent . ':' . $child];
}
function uuid_entity_uuid_get_children($original_plugin, $parent) {
$entities = entity_get_info();
$plugins = array();
foreach ($entities as $entity_type => $entity) {
$plugin = $original_plugin;
$plugin['title'] = t('@entity: UUID', array('@entity' => $entity['label']));
$plugin['keyword'] = $entity_type;
$plugin['description'] = t('Creates @entity context from an UUID argument.', array('@entity' => $entity_type));
$plugin['name'] = $parent . ':' . $entity_type;
$plugin_id = $parent . ':' . $entity_type;
drupal_alter('ctools_entity_context', $plugin, $entity, $plugin_id);
$plugins[$plugin_id] = $plugin;
}
drupal_alter('ctools_entity_contexts', $plugins);
return $plugins;
}
/**
* Discover if this argument gives us the entity we crave.
*/
function uuid_entity_uuid_context($arg = NULL, $conf = NULL, $empty = FALSE) {
$entity_type = explode(':', $conf['name']);
$entity_type = $entity_type[1];
// If unset it wants a generic, unfilled context.
if ($empty) {
return ctools_context_create_empty('entity:' . $entity_type);
}
// We can accept either an entity object or a pure id.
if (is_object($arg)) {
return ctools_context_create('entity:' . $entity_type, $arg);
}
if (!is_string($arg)) {
return FALSE;
}
$entity_ids = entity_get_id_by_uuid($entity_type, array($arg));
if (!isset($entity_ids[$arg])) {
return FALSE;
}
$entity_id = $entity_ids[$arg];
$entities = entity_load($entity_type, array($entity_id));
if (!$entities) {
return FALSE;
}
return ctools_context_create('entity:' . $entity_type, $entities[$entity_id]);
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* @file
* Administration functions for the uuid module.
*/
/**
* Menu callback: options for UUID.
*/
function uuid_admin_form() {
$form = array();
$form['sync'] = array(
'#type' => 'fieldset',
'#title' => t('Synchronization'),
);
$form['sync']['submit'] = array(
'#type' => 'submit',
'#value' => t('Create missing UUIDs'),
'#submit' => array('uuid_admin_sync_submit'),
);
return system_settings_form($form);
}
/**
* Submit handler for the UUID sync.
*/
function uuid_admin_sync_submit() {
uuid_sync_all();
drupal_set_message(t('Generated missing UUIDs.'));
}
/**
* Page callback to display Devel information about a UUID entity.
*/
function uuid_devel_load_by_uuid($entity_type, $entity) {
$info = entity_get_info($entity_type);
if (isset($info['uuid']) && $info['uuid'] == TRUE && !empty($info['entity keys']['uuid'])) {
// Get the keys for local ID and UUID.
$uuid_key = $info['entity keys']['uuid'];
$uuid_entities = entity_uuid_load($entity_type, array($entity->{$uuid_key}));
return kdevel_print_object(reset($uuid_entities), '$' . $entity_type . '->');
}
else {
return t("This entity doesn't support UUID.");
}
}

View File

@@ -0,0 +1,165 @@
<?php
/**
* @file
* Hooks provided by the UUID module.
*/
/**
* Defines one or more UUID generators exposed by a module.
*
* @return
* An associative array with the key being the machine name for the
* implementation and the values being an array with the following keys:
* - title: The human readable name for the generator.
* - callback: The function to be called for generating the UUID.
*
* @see uuid_get_info()
*/
function hook_uuid_info() {
$generators = array();
$generators['my_module'] = array(
'title' => t('My module UUID generator'),
'callback' => 'my_module_generate_uuid',
);
return $generators;
}
/**
* Ensures all records have a UUID assigned to them.
*
* When called this hook should ensure all records it is responsible for
* have a UUID and if not create one.
*
* @see entity_uuid_sync()
*/
function hook_uuid_sync() {
// Do what you need to do to generate missing UUIDs for you implementation.
}
/**
* Let modules transform their properties with local IDs to UUIDs when an
* entity is loaded.
*/
function hook_entity_uuid_load(&$entities, $entity_type) {
}
/**
* Let modules transform their fields with local IDs to UUIDs when an entity
* is loaded.
*/
function hook_field_uuid_load($entity_type, $entity, $field, $instance, $langcode, &$items) {
}
/**
* Let modules transform their properties with UUIDs to local IDs when an
* entity is saved.
*/
function hook_entity_uuid_presave(&$entity, $entity_type) {
}
/**
* Let modules transform their fields with UUIDs to local IDs when an entity
* is saved.
*/
function hook_field_uuid_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
}
/**
* Let modules transform their properties when an entity is saved.
*/
function hook_entity_uuid_save($entity, $entity_type) {
}
/**
* Let modules act when an entity is deleted.
*/
function hook_entity_uuid_delete($entity, $entity_type) {
}
/**
* Let modules modify paths when they are being converted to UUID ones.
*/
function hook_uuid_menu_path_to_uri_alter($path, &$uri) {
}
/**
* Let modules modify paths when they are being converted from UUID ones.
*/
function hook_uuid_menu_uri_to_path(&$path, $uri) {
}
/**
* Allow modules to provide a list of default entities that will be imported.
*/
function hook_uuid_default_entities() {
}
/**
* Let other modules do things before default entities are created on rebuild.
*/
function hook_uuid_entities_pre_rebuild($plan_name) {
}
/**
* Let other modules do things after default entities are created on rebuild.
*/
function hook_uuid_entities_post_rebuild($plan_name) {
}
/**
* Let other modules do things before default entities are created on revert.
*/
function hook_uuid_entities_pre_rebuild($plan_name) {
}
/**
* Let other modules do things after default entities are created on revert.
*/
function hook_uuid_entities_post_rebuild($plan_name) {
}
/**
* Let other modules alter entities that are about to be exported.
*/
function hook_uuid_entities_features_export_entity_alter(&$entity, $entity_type) {
}
/**
* Let other modules alter fields on entities that are about to be exported.
*/
function hook_uuid_entities_features_export_field_alter($entity_type, &$entity, $field, $instance, $langcode, &$items) {
}
/**
* Alter UUID URI data after processing.
*/
function hook_uuid_uri_data($data) {
}
/**
* Alter UUID URI data after processing.
*/
function hook_uuid_uri_data($data) {
}
/**
* Alter entity URI before creating UUID URI.
*/
function hook_uuid_id_uri_data($data) {
}

View File

@@ -0,0 +1,407 @@
<?php
/**
* @file
* Implementation of UUID hooks for all core modules.
*
* @todo
* Replace all these hook implementations with a generic solution that uses
* info from hook_entity_field_info() and hook_entity_property_info(). That
* info should contain info about how UUIDs are mapped.
*/
/**
* @defgroup uuid_property Propery implementations
* @{
*/
/**
* Implements hook_entity_uuid_load().
*/
function node_entity_uuid_load(&$entities, $entity_type) {
if ($entity_type == 'node') {
entity_property_id_to_uuid($entities, 'user', array('uid', 'revision_uid'));
entity_property_id_to_uuid($entities, 'node', 'tnid');
}
}
/**
* Implements hook_entity_uuid_presave().
*/
function node_entity_uuid_presave(&$entity, $entity_type) {
if ($entity_type == 'node') {
entity_property_uuid_to_id($entity, 'user', array('uid', 'revision_uid'));
entity_property_uuid_to_id($entity, 'node', 'tnid');
}
}
/**
* Implements hook_entity_uuid_load().
*/
function book_uuid_entities_features_export_entity_alter(&$entity, $entity_type) {
if ($entity_type == 'node') {
if (!empty($entity->book)) {
$entity->book['bid'] = current(entity_get_uuid_by_id($entity_type, array($entity->book['bid'])));
}
}
}
/**
* Implements hook_entity_uuid_presave().
*/
function book_entity_uuid_presave(&$entity, $entity_type) {
if ($entity_type == 'node') {
if (!empty($entity->book)) {
$entity->book['bid'] = current(entity_get_id_by_uuid($entity_type, array($entity->book['bid'])));
if (!$entity->book['bid']) {
$entity->book['bid'] = 'new';
}
}
}
}
/**
* Implements hook_entity_uuid_presave().
*/
function user_entity_uuid_presave(&$entity, $entity_type) {
if ($entity_type == 'user') {
if (!empty($entity->picture)) {
$uuids = entity_get_id_by_uuid('file', array($entity->picture['uuid']));
$fid = current($uuids);
if (!$entity->is_new) {
$entity->picture = file_load($fid);
}
else {
$entity->picture = $fid;
}
}
}
}
/**
* Implements hook_entity_uuid_load().
*/
function comment_entity_uuid_load(&$entities, $entity_type) {
switch ($entity_type) {
case 'node':
entity_property_id_to_uuid($entities, 'user', 'last_comment_uid');
break;
case 'comment':
entity_property_id_to_uuid($entities, 'user', array('uid', 'u_uid'));
entity_property_id_to_uuid($entities, 'node', 'nid');
break;
}
}
/**
* Implements hook_entity_uuid_presave().
*/
function comment_entity_uuid_presave(&$entity, $entity_type) {
switch ($entity_type) {
case 'node':
entity_property_uuid_to_id($entity, 'user', 'last_comment_uid');
break;
case 'comment':
entity_property_uuid_to_id($entity, 'user', array('uid', 'u_uid'));
entity_property_uuid_to_id($entity, 'node', 'nid');
break;
}
}
/**
* Implements hook_entity_uuid_load().
*/
function file_entity_uuid_load(&$entities, $entity_type) {
if ($entity_type == 'file') {
entity_property_id_to_uuid($entities, 'user', 'uid');
}
}
/**
* Implements hook_entity_uuid_presave().
*/
function file_entity_uuid_presave(&$entity, $entity_type) {
if ($entity_type == 'file') {
entity_property_uuid_to_id($entity, 'user', 'uid');
if (isset($entity->file_contents)) {
$directory = drupal_dirname($entity->uri);
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
file_unmanaged_save_data(base64_decode($entity->file_contents), $entity->uri, FILE_EXISTS_REPLACE);
}
}
}
/**
* Implements hook_entity_uuid_load().
*/
function taxonomy_entity_uuid_load(&$entities, $entity_type) {
if ($entity_type == 'taxonomy_term') {
foreach ($entities as &$entity) {
if (isset($entity->parent)) {
if (!is_array($entity->parent)) {
$entity->parent = array($entity->parent);
}
$uuids = entity_get_uuid_by_id('taxonomy_term', $entity->parent);
$entity->parent = array_values($uuids);
}
unset($entity->vid);
}
}
}
/**
* Implements hook_entity_uuid_presave().
*/
function taxonomy_entity_uuid_presave(&$entity, $entity_type) {
if ($entity_type == 'taxonomy_term') {
if (isset($entity->parent)) {
if (!is_array($entity->parent)) {
$entity->parent = array($entity->parent);
}
$ids = entity_get_id_by_uuid('taxonomy_term', $entity->parent);
$entity->parent = array_values($ids);
}
$vocabulary = taxonomy_vocabulary_machine_name_load($entity->vocabulary_machine_name);
$entity->vid = $vocabulary->vid;
}
}
/**
* Implements hook_entity_uuid_load().
*/
function field_entity_uuid_load(&$entities, $entity_type) {
foreach ($entities as $i => $entity) {
list(, , $bundle_name) = entity_extract_ids($entity_type, $entity);
$instances = field_info_instances($entity_type, $bundle_name);
foreach ($instances as $field_name => $instance) {
$field = field_info_field($field_name);
if (!empty($field) && isset($entity->{$field_name})) {
foreach ($entity->{$field_name} as $langcode => &$items) {
// Invoke 'hook_field_uuid_load'. We can't use module_invoke() since
// that is not passing by reference.
$function = $field['module'] . '_field_uuid_load';
if (function_exists($function)) {
$function($entity_type, $entity, $field, $instance, $langcode, $items);
}
}
}
}
}
}
/**
* Implements hook_entity_uuid_presave().
*/
function field_entity_uuid_presave(&$entity, $entity_type) {
list(, , $bundle_name) = entity_extract_ids($entity_type, $entity);
$instances = field_info_instances($entity_type, $bundle_name);
foreach ($instances as $field_name => $instance) {
$field = field_info_field($field_name);
if (!empty($field) && isset($entity->{$field_name})) {
foreach ($entity->{$field_name} as $langcode => &$items) {
// Invoke 'hook_field_uuid_load'. We can't use module_invoke() since
// that is not passing by reference.
$function = $field['module'] . '_field_uuid_presave';
if (function_exists($function)) {
$function($entity_type, $entity, $field, $instance, $langcode, $items);
}
}
}
}
}
/**
* @} End of "Property implementations"
*/
/**
* @defgroup uuid_field Field implementations
* @{
*/
/**
* Implements hook_field_uuid_load().
*/
function taxonomy_field_uuid_load($entity_type, $entity, $field, $instance, $langcode, &$items) {
entity_property_id_to_uuid($items, 'taxonomy_term', 'tid');
}
/**
* Implements hook_field_uuid_presave().
*/
function taxonomy_field_uuid_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
entity_property_uuid_to_id($items, 'taxonomy_term', 'tid');
}
/**
* Implements hook_field_uuid_load().
*/
function file_field_uuid_load($entity_type, $entity, $field, $instance, $langcode, &$items) {
entity_property_id_to_uuid($items, 'file', 'fid');
entity_property_id_to_uuid($items, 'user', 'uid');
}
/**
* Implements hook_field_uuid_presave().
*/
function file_field_uuid_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
entity_property_uuid_to_id($items, 'file', 'fid');
entity_property_uuid_to_id($items, 'user', 'uid');
}
/**
* Implements hook_field_uuid_load().
*/
function image_field_uuid_load($entity_type, $entity, $field, $instance, $langcode, &$items) {
file_field_uuid_load($entity_type, $entity, $field, $instance, $langcode, $items);
}
/**
* Implements hook_field_uuid_presave().
*/
function image_field_uuid_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
file_field_uuid_presave($entity_type, $entity, $field, $instance, $langcode, $items);
}
/**
* Implements hook_field_uuid_load().
*/
function node_reference_field_uuid_load($entity_type, $entity, $field, $instance, $langcode, &$items) {
entity_property_id_to_uuid($items, 'node', 'nid');
}
/**
* Implements hook_field_uuid_presave().
*/
function node_reference_field_uuid_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
entity_property_uuid_to_id($items, 'node', 'nid');
}
/**
* Implements hook_field_uuid_load().
*/
function user_reference_field_uuid_load($entity_type, $entity, $field, $instance, $langcode, &$items) {
entity_property_id_to_uuid($items, 'user', 'uid');
}
/**
* Implements hook_field_uuid_presave().
*/
function user_reference_field_uuid_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
entity_property_uuid_to_id($items, 'user', 'uid');
}
/**
* Implements hook_field_uuid_load().
*/
function entityreference_field_uuid_load($entity_type, $entity, $field, $instance, $langcode, &$items) {
// TODO: This is not really good, but as of now 'entity_property_id_to_uuid()'
// can't handle a single $item.
entity_property_id_to_uuid($items, $field['settings']['target_type'], 'target_id');
}
/**
* Implements hook_field_uuid_presave().
*/
function entityreference_field_uuid_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
// TODO: This is not really good, but as of now 'entity_property_id_to_uuid()'
// can't handle a single $item.
entity_property_uuid_to_id($items, $field['settings']['target_type'], 'target_id');
}
/**
* Implements hook_entity_uuid_load().
*/
function field_collection_entity_uuid_load(&$entities, $entity_type) {
if ($entity_type == 'field_collection_item') {
entity_property_id_to_uuid($entities, 'field_collection_item', 'value');
}
}
/**
* Implements hook_entity_uuid_presave().
*/
function field_collection_entity_uuid_presave(&$entity, $entity_type) {
if ($entity_type == 'field_collection_item') {
entity_property_uuid_to_id($entity, 'field_collection_item', 'value');
}
}
/**
* Implements hook_field_uuid_load().
*/
function field_collection_field_uuid_load($entity_type, $entity, $field, $instance, $langcode, &$items) {
entity_property_id_to_uuid($items, 'field_collection_item', 'value');
}
/**
* Implements hook_field_uuid_presave().
*/
function field_collection_field_uuid_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
entity_property_uuid_to_id($items, 'field_collection_item', 'value');
}
/**
* @} End of "Field implementations"
*/
/**
* @defgroup uuid_export Export alterations
* @{
*/
/**
* Implements hook_uuid_entities_features_export_entity_alter().
*/
function node_uuid_entities_features_export_entity_alter(&$entity, $entity_type) {
if ($entity_type == 'node') {
foreach (array('data', 'name', 'picture', 'revision_uid', 'last_comment_timestamp') as $property) {
if (property_exists($entity, $property)) {
unset($entity->{$property});
}
}
}
}
/**
* Implementation of hook_uuid_entities_features_export_entity_alter().
*/
function user_uuid_entities_features_export_entity_alter(&$entity, $entity_type) {
if ($entity_type == 'user') {
foreach (array('data', 'access', 'login') as $property) {
if (property_exists($entity, $property)) {
unset($entity->{$property});
}
}
}
}
/**
* Implements hook_uuid_entities_features_export_alter().
*/
function file_uuid_entities_features_export_field_alter($entity_type, $entity, $field, $instance, $langcode, &$items) {
foreach ($items as &$item) {
if (isset($item['timestamp'])) {
unset($item['timestamp']);
}
}
}
/**
* Implements hook_uuid_entities_features_export_entity_alter().
*/
function workbench_uuid_entities_features_export_entity_alter(&$entity, $entity_type) {
foreach (array('workbench_moderation', 'my_revision', 'workbench_access', 'workbench_access_scheme', 'workbench_access_by_role') as $property) {
if (isset($entity->{$property})) {
unset($entity->{$property});
}
}
}
/**
* @} End of "Export alterations"
*/

View File

@@ -0,0 +1,41 @@
<?php
/**
* @file
* Drush implementation for the uuid module.
*/
/**
* Implementats hook_drush_command().
*/
function uuid_drush_command() {
$items = array();
$items['uuid-create-missing'] = array(
'description' => 'Create missing UUIDs for enabled entities.',
'aliases' => array('uuid-create'),
);
return $items;
}
/**
* Implementats of hook_drush_help().
*/
function uuid_drush_help($section) {
switch ($section) {
case 'drush:uuid-create-missing':
return dt("This command will create missing UUIDs for those content types specified in the module settings for automatic generation.");
}
}
/**
* Drush command callback.
*/
function drush_uuid_create_missing() {
if (!drush_confirm(dt('Are you sure?'))) {
return drush_user_abort();
}
module_load_include('inc', 'uuid', 'uuid');
drush_log(dt('Beginning bulk creation of UUIDs.'), 'ok');
uuid_sync_all();
}

View File

@@ -0,0 +1,543 @@
<?php
/**
* @file
* Entity related functions for UUID module.
*/
/**
* Entity UUID exception class.
*/
class UuidEntityException extends Exception {}
/**
* Helper function that returns entity info for all supported core modules,
* relevant for UUID functionality.
*
* @see uuid_entity_info()
* @see uuid_schema_alter()
* @see uuid_install()
* @see uuid_uninstall()
*/
function uuid_get_core_entity_info() {
$info = array();
$info['user'] = array(
'base table' => 'users',
'entity keys' => array(
'uuid' => 'uuid',
),
);
$info['node'] = array(
'base table' => 'node',
'revision table' => 'node_revision',
'entity keys' => array(
'uuid' => 'uuid',
'revision uuid' => 'vuuid',
),
);
if (module_exists('comment')) {
$info['comment'] = array(
'base table' => 'comment',
'entity keys' => array(
'uuid' => 'uuid',
),
);
}
if (module_exists('file')) {
$info['file'] = array(
'base table' => 'file_managed',
'entity keys' => array(
'uuid' => 'uuid',
),
);
}
if (module_exists('taxonomy')) {
$info['taxonomy_term'] = array(
'base table' => 'taxonomy_term_data',
'entity keys' => array(
'uuid' => 'uuid',
),
);
}
if (module_exists('field_collection')) {
$info['field_collection_item'] = array(
'base table' => 'field_collection_item',
'entity keys' => array(
'uuid' => 'uuid',
),
);
}
return $info;
}
/**
* @defgroup uuid_entity_hooks UUID implementation of Entity API
* @{
*/
/**
* Implements of hook_entity_info_alter().
*
* @see uuid_core_entity_info().
*/
function uuid_entity_info_alter(&$info) {
foreach (uuid_get_core_entity_info() as $entity_type => $core_info) {
$info[$entity_type]['uuid'] = TRUE;
$info[$entity_type]['entity keys']['uuid'] = $core_info['entity keys']['uuid'];
if (!empty($core_info['entity keys']['revision uuid'])) {
$info[$entity_type]['entity keys']['revision uuid'] = $core_info['entity keys']['revision uuid'];
}
}
}
/**
* Implements of hook_entity_property_info_alter().
*
* This adds the UUID as an entity property for all UUID-enabled entities
* which automatically gives us token and Rules integration.
*/
function uuid_entity_property_info_alter(&$info) {
foreach (entity_get_info() as $entity_type => $entity_info) {
if (isset($entity_info['uuid']) && $entity_info['uuid'] == TRUE && !empty($entity_info['entity keys']['uuid'])) {
$info[$entity_type]['properties'][$entity_info['entity keys']['uuid']] = array(
'label' => t('UUID'),
'type' => 'text',
'description' => t('The universally unique ID.'),
'schema field' => $entity_info['entity keys']['uuid'],
);
if (!empty($entity_info['entity keys']['revision uuid'])) {
$info[$entity_type]['properties'][$entity_info['entity keys']['revision uuid']] = array(
'label' => t('Revision UUID'),
'type' => 'text',
'description' => t("The revision's universally unique ID."),
'schema field' => $entity_info['entity keys']['revision uuid'],
);
}
}
}
}
/**
* Implements of hook_entity_presave().
*
* This is where all UUID-enabled entities get their UUIDs.
*/
function uuid_entity_presave($entity, $entity_type) {
$info = entity_get_info($entity_type);
if (isset($info['uuid']) && $info['uuid'] == TRUE && !empty($info['entity keys']['uuid'])) {
$uuid_key = $info['entity keys']['uuid'];
if (empty($entity->{$uuid_key})) {
$entity->{$uuid_key} = uuid_generate();
}
if (!empty($info['entity keys']['revision uuid'])) {
$vuuid_key = $info['entity keys']['revision uuid'];
if ((isset($entity->revision) && $entity->revision == TRUE) || empty($entity->{$vuuid_key})) {
$entity->{$vuuid_key} = uuid_generate();
}
}
}
}
/**
* @} End of "UUID implementation of Entity API"
*/
/**
* @defgroup uuid_entity_support UUID support for Entity API
* @{
* Functions that extends the Entity API with UUID support.
*/
/**
* Load entities by their UUID, that only should containing UUID references.
*
* This function is mostly useful if you want to load an entity from the local
* database that only should contain UUID references.
*
* @see entity_load()
*/
function entity_uuid_load($entity_type, $uuids = array(), $conditions = array(), $reset = FALSE) {
$ids = entity_get_id_by_uuid($entity_type, $uuids);
$results = entity_load($entity_type, $ids, $conditions, $reset);
$entities = array();
// We need to do this little magic here, because objects are passed by
// reference. And because hook_entity_uuid_load() has the intention changing
// primary properties and fields from local IDs to UUIDs it will also change
// DrupalDefaultEntityController::entityCache by reference which is a static
// cache of entities. And that is not something we want.
foreach ($results as $key => $entity) {
// This will avoid passing our loaded entities by reference.
$entities[$key] = clone $entity;
}
entity_make_entity_universal($entity_type, $entities);
return $entities;
}
/**
* Helper function to make an entity universal (i.e. only global references).
*/
function entity_make_entity_universal($entity_type, $entities) {
// Let other modules transform local ID references to UUID references.
if (!empty($entities)) {
$hook = 'entity_uuid_load';
foreach (module_implements($hook) as $module) {
$function = $module . '_' . $hook;
if (function_exists($function)) {
$function($entities, $entity_type);
}
}
}
}
/**
* Permanently saves an entity by its UUID.
*
* This function depends on the Entity API module to provide the
* 'entity_save()' function.
*
* This function is mostly useful if you want to save an entity into the local
* database that only contains UUID references.
*
* @see entity_save()
*/
function entity_uuid_save($entity_type, $entity) {
// This function, and this function only, depends on the entity module.
if (!module_exists('entity')) {
throw new UuidEntityException(t('Calling %function requires the Entity API module (!link).', array('%function' => __FUNCTION__, '!link' => 'http://drupal.org/project/entity')));
}
entity_make_entity_local($entity_type, $entity);
// Save the entity.
entity_save($entity_type, $entity);
$hook = 'entity_uuid_save';
foreach (module_implements($hook) as $module) {
$function = $module . '_' . $hook;
if (function_exists($function)) {
$function($entity, $entity_type);
}
}
}
/**
* Helper function to make an entity local (i.e. only local references).
*/
function entity_make_entity_local($entity_type, $entity) {
$info = entity_get_info($entity_type);
if (isset($info['uuid']) && $info['uuid'] == TRUE && !empty($info['entity keys']['uuid'])) {
// Get the keys for local ID and UUID.
$id_key = $info['entity keys']['id'];
$uuid_key = $info['entity keys']['uuid'];
// UUID entites must always provide a valid UUID when saving in order to do
// the correct mapping between local and global IDs.
if (empty($entity->{$uuid_key}) || !uuid_is_valid($entity->{$uuid_key})) {
throw new UuidEntityException(t('Trying to save a @type entity with empty or invalid UUID.', array('@type' => $info['label'])));
}
// Fetch the local ID by its UUID.
$ids = entity_get_id_by_uuid($entity_type, array($entity->{$uuid_key}));
$id = reset($ids);
// Set the correct local ID.
if (empty($id)) {
unset($entity->{$id_key});
$entity->is_new = TRUE;
}
else {
$entity->{$id_key} = $id;
$entity->is_new = FALSE;
}
if (!empty($info['entity keys']['revision uuid'])) {
// Get the keys for local revison ID and revision UUID.
$vid_key = $info['entity keys']['revision'];
$vuuid_key = $info['entity keys']['revision uuid'];
$vid = NULL;
// Fetch the local revision ID by its UUID.
if (isset($entity->{$vuuid_key})) {
$vids = entity_get_id_by_uuid($entity_type, array($entity->{$vuuid_key}), TRUE);
$vid = reset($vids);
}
if (empty($vid) && isset($entity->{$vid_key})) {
unset($entity->{$vid_key});
}
elseif (!empty($vid)) {
$entity->{$vid_key} = $vid;
}
// Nodes need this when trying to save an existing node without a vid.
if ($entity_type == 'node' && !isset($entity->vid) && !$entity->is_new) {
$entity->revision = 0;
$entity->vid = db_select('node', 'n')
->condition('n.nid', $entity->nid)
->fields('n', array('vid'))
->execute()
->fetchField();
}
}
// Let other modules transform UUID references to local ID references.
$hook = 'entity_uuid_presave';
foreach (module_implements($hook) as $module) {
$function = $module . '_' . $hook;
if (function_exists($function)) {
$function($entity, $entity_type);
}
}
}
else {
throw new UuidEntityException(t('Trying to operate on a @type entity, which doesn\'t support UUIDs.', array('@type' => $info['label'])));
}
}
/**
* Permanently delete the given entity by its UUID.
*
* This function depends on the Entity API module to provide the
* 'entity_delete()' function.
*
* @see entity_delete()
*/
function entity_uuid_delete($entity_type, $uuid) {
// This function, and this function only, depends on the entity module.
if (!module_exists('entity')) {
throw new UuidEntityException(t('Calling %function requires the Entity API module (!link).', array('%function' => __FUNCTION__, '!link' => 'http://drupal.org/project/entity')));
}
$info = entity_get_info($entity_type);
if (isset($info['uuid']) && $info['uuid'] == TRUE) {
// Fetch the local ID by its UUID.
$ids = entity_get_id_by_uuid($entity_type, array($uuid));
$id = reset($ids);
// Let other modules transform UUID references to local ID references.
$hook = 'entity_uuid_delete';
foreach (module_implements($hook) as $module) {
$function = $module . '_' . $hook;
if (function_exists($function)) {
$function($entity, $entity_type);
}
}
// Delete the entity.
return entity_delete($entity_type, $id);
}
else {
throw new UuidEntityException(t('Trying to delete a @type entity, which doesn\'t support UUIDs.', array('@type' => $info['label'])));
}
}
/**
* Helper function that retrieves entity IDs by their UUIDs.
*
* @todo
* Statically cache as many IDs as possible and limit the query.
*
* @param $entity_type
* The entity type we should be dealing with.
* @param $uuids
* An array of UUIDs for which we should find their entity IDs. If $revision
* is TRUE this should be revision UUIDs instead.
* @param $revision
* If TRUE the revision IDs is returned instead.
* @return
* Array of entity IDs keyed by their UUIDs. If $revision is TRUE revision
* IDs and UUIDs are returned instead.
*/
function entity_get_id_by_uuid($entity_type, $uuids, $revision = FALSE) {
if (empty($uuids)) {
return array();
}
$info = entity_get_info($entity_type);
// Find out what entity keys to use.
if (!$revision) {
$table = $info['base table'];
$id_key = $info['entity keys']['id'];
$uuid_key = $info['entity keys']['uuid'];
}
elseif (isset($info['revision table'])) {
$table = $info['revision table'];
$id_key = $info['entity keys']['revision'];
$uuid_key = $info['entity keys']['revision uuid'];
}
// If we want revision IDs, but the entity doesn't support it. Return empty.
else {
return array();
}
// Get all UUIDs in one query.
return db_select($table, 't')
->fields('t', array($uuid_key, $id_key))
->condition($uuid_key, array_values($uuids), 'IN')
->execute()
->fetchAllKeyed();
}
/**
* Helper function that retrieves UUIDs by their entity IDs.
*
* @todo
* Statically cache as many IDs as possible and limit the query.
*
* @param $entity_type
* The entity type we should be dealing with.
* @param $ids
* An array of entity IDs for which we should find their UUIDs. If $revision
* is TRUE this should be revision IDs instead.
* @param $revision
* If TRUE the revision UUIDs is returned instead.
* @return
* Array of entity UUIDs keyed by their IDs. If $revision is TRUE revision
* IDs and UUIDs are returned instead.
*/
function entity_get_uuid_by_id($entity_type, $ids, $revision = FALSE) {
if (empty($ids)) {
return array();
}
$info = entity_get_info($entity_type);
// Find out what entity keys to use.
if (!$revision) {
$table = $info['base table'];
$id_key = $info['entity keys']['id'];
$uuid_key = $info['entity keys']['uuid'];
}
elseif (isset($info['revision table'])) {
$table = $info['revision table'];
$id_key = $info['entity keys']['revision'];
$uuid_key = $info['entity keys']['revision uuid'];
}
// If we want revision UUIDs, but the entity doesn't support it. Return empty.
else {
return array();
}
// Get all UUIDs in one query.
return db_select($table, 't')
->fields('t', array($id_key, $uuid_key))
->condition($id_key, array_values($ids), 'IN')
->execute()
->fetchAllKeyed();
}
/**
* Helper function to change entity properties from ID to UUID.
*
* We never change user UID 0 or 1 to UUIDs. Those are low level user accounts
* ("anonymous" and "root") that needs to be identified consistently across
* any system.
*
* @todo
* Add tests for this function.
*
* @param $objects
* An array of objects that should get $properties changed. Can be either an
* entity object or a field items array.
* @param $entity_type
* The type of entity that all $properties refers to.
* @param $properties
* An array of properties that should be changed. All properties must refer to
* the same type of entity (the one referenced in $entity_type).
*/
function entity_property_id_to_uuid(&$objects, $entity_type, $properties) {
if (!is_array($objects)) {
$things = array(&$objects);
}
else {
$things = &$objects;
}
if (!is_array($properties)) {
$properties = array($properties);
}
$ids = array();
$values = array();
$i = 0;
foreach ($things as &$object) {
foreach ($properties as $property) {
// This is probably an entity object.
if (is_object($object) && isset($object->{$property})) {
$values[$i] = &$object->{$property};
}
// This is probably a field items array.
elseif (is_array($object) && isset($object[$property])) {
$values[$i] = &$object[$property];
}
else {
$i++;
continue;
}
if (!($entity_type == 'user' && ($values[$i] == 0 || $values[$i] == 1))) {
$ids[] = $values[$i];
}
$i++;
}
}
$uuids = entity_get_uuid_by_id($entity_type, $ids);
foreach ($values as $i => $value) {
if (isset($uuids[$value])) {
$values[$i] = $uuids[$value];
}
}
}
/**
* Helper function to change entity properties from UUID to ID.
*
* @todo
* Add tests for this function.
*
* @param $objects
* An array of objects that should get $properties changed. Can be either an
* entity object or a field items array.
* @param $entity_type
* The type of entity that all $properties refers to.
* @param $properties
* An array of properties that should be changed. All properties must refer to
* the same type of entity (the one referenced in $entity_type).
*/
function entity_property_uuid_to_id(&$objects, $entity_type, $properties) {
if (!is_array($objects)) {
$things = array(&$objects);
}
else {
$things = &$objects;
}
if (!is_array($properties)) {
$properties = array($properties);
}
$uuids = array();
$values = array();
$i = 0;
foreach ($things as &$object) {
foreach ($properties as $property) {
// This is probably an entity object.
if (is_object($object) && isset($object->{$property})) {
$values[$i] = &$object->{$property};
}
// This is probably a field items array.
elseif (is_array($object) && isset($object[$property])) {
$values[$i] = &$object[$property];
}
else {
$i++;
continue;
}
if (uuid_is_valid($values[$i])) {
$uuids[] = $values[$i];
}
$i++;
}
}
$ids = entity_get_id_by_uuid($entity_type, $uuids);
foreach ($values as $i => $value) {
if (isset($ids[$value])) {
$values[$i] = $ids[$value];
}
}
}
/**
* @} End of "UUID support for Entity API"
*/

View File

@@ -0,0 +1,206 @@
<?php
/**
* @file
* Features support to export entities from any Deploy <em>fetch-only</em> plan.
*/
/**
* Implements [component]_features_export_options().
*
* Deploy plans are used as the initial source for an export. Deploy solves
* complex dependency chains for us, so the entities actually can be used as is.
*/
function uuid_entities_features_export_options() {
$options = array();
if (module_exists('deploy')) {
$plans = deploy_plan_load_all(array('fetch_only' => 1));
foreach ($plans as $plan) {
$options[$plan->name] = $plan->title;
}
}
return $options;
}
/**
* Implements [component]_features_export().
*/
function uuid_entities_features_export($components, &$export, $module_name) {
foreach ($components as $plan_name) {
$export['features']['uuid_entities'][$plan_name] = $plan_name;
}
}
/**
* Implements [component]_features_export_render().
*
* Components corresponds to the name of Deploy plans. However, once exported,
* entities can be rendered directly from the default hook without Deploy or the
* initial plan.
*/
function uuid_entities_features_export_render($module_name, $components, $export = NULL) {
$code = array();
$code[] = ' $entities = array();';
$code[] = '';
foreach ($components as $component) {
$entities = array();
if (module_exists('deploy') && $plan = deploy_plan_load($component)) {
$entities = $plan->getIterator();
}
else {
features_include_defaults(array('uuid_entities'));
$default_entities = module_invoke($module_name, 'uuid_default_entities');
foreach ($default_entities[$component] as $entity) {
$metadata = $entity->__metadata;
$entity_type = $metadata['type'];
$entity_info = entity_get_info($entity_type);
$results = entity_uuid_load($entity_type, array($entity->{$entity_info['entity keys']['uuid']}), NULL, TRUE);
if (!empty($results)) {
$entity = reset($results);
// Re-attach the metadata after loading the clean entity.
$entity->__metadata = $metadata;
$entities[] = $entity;
}
}
}
foreach ($entities as $entity) {
$entity_type = $entity->__metadata['type'];
$entity_info = entity_get_info($entity_type);
// We need to remove entity id and revision keys for better consistency.
$id_key = $entity_info['entity keys']['id'];
if (isset($entity->{$id_key})) {
unset($entity->{$id_key});
}
if (!empty($entity_info['entity keys']['revision'])) {
$vid_key = $entity_info['entity keys']['revision'];
if (isset($entity->{$vid_key})) {
unset($entity->{$vid_key});
}
if (!empty($entity_info['entity keys']['revision uuid'])) {
$vuuid_key = $entity_info['entity keys']['revision uuid'];
unset($entity->{$vuuid_key});
}
}
// We unset some common timestamp properties, since those will change and
// constantly leave the feature overidden.
$keys = array('created', 'updated', 'changed', 'revision_timestamp', 'timestamp', 'stamp', 'current');
foreach ($keys as $key) {
if (isset($entity->{$key})) {
unset($entity->{$key});
}
}
// Let other modules alter exported entities.
drupal_alter('uuid_entities_features_export_entity', $entity, $entity_type);
// Field handling.
list(,, $bundle_name) = entity_extract_ids($entity_type, $entity);
$instances = field_info_instances($entity_type, $bundle_name);
foreach ($instances as $field_name => $instance) {
$field = field_info_field($field_name);
if (!empty($entity->{$field_name})) {
foreach ($entity->{$field_name} as $langcode => &$items) {
// Let other modules alter fields on exported entities.
// We are not using drupal_alter() here, because of it's complexity
// dealing with over two alterable arguments.
$hook = 'uuid_entities_features_export_field_alter';
foreach (module_implements($hook) as $module_name) {
$function = $module_name . '_' . $hook;
$function($entity_type, $entity, $field, $instance, $langcode, $items);
}
foreach ($items as &$item) {
// We don't need to export these common field keys.
foreach (array('safe_value', 'safe_summary') as $key) {
if (isset($item[$key])) {
unset($item[$key]);
}
}
uuid_entities_features_clean($item);
}
}
}
}
uuid_entities_features_clean($entity);
// Convert entities to array to avoid having them in JSON, returned from standard implementation of $entity->export().
if (is_object($entity) && method_exists($entity, 'export')) {
$entity = get_object_vars($entity);
}
$code[] = ' $entities[\'' . check_plain($component) . '\'][] = (object) ' . features_var_export($entity, ' ') . ';';
}
}
$code[] = '';
$code[] = ' return $entities;';
return array('uuid_default_entities' => implode("\n", $code));
}
/**
* Implements [component]_features_export_rebuild().
*/
function uuid_entities_features_rebuild($module_name) {
uuid_entities_rebuild($module_name, 'rebuild');
}
/**
* Implements [component]_features_export_revert().
*/
function uuid_entities_features_revert($module_name) {
uuid_entities_rebuild($module_name, 'revert');
}
/**
* Helper function to rebuild entities from a plan.
*/
function uuid_entities_rebuild($module_name = '', $op = 'rebuild') {
features_include_defaults(array('uuid_entities'));
$entities = module_invoke($module_name, 'uuid_default_entities');
if (!empty($entities)) {
foreach ($entities as $plan_name => $entities) {
// Let other modules do things before default entities are created.
module_invoke_all("uuid_entities_pre_$op", $plan_name);
foreach ($entities as $entity) {
entity_uuid_save($entity->__metadata['type'], $entity);
}
// Let other modules do things after default entities are created.
module_invoke_all("uuid_entities_post_$op", $plan_name);
}
}
}
/**
* Helper function to sort properties of an object.
*
* This will maintain better consistency. Keys might get shifted order or type
* due to alterations sometimes.
*/
function uuid_entities_features_clean(&$object) {
$properties = array();
foreach ($object as $key => $value) {
$properties[$key] = $value;
if (is_object($object)) {
unset($object->{$key});
}
elseif (is_array($object)) {
unset($object[$key]);
}
}
ksort($properties);
foreach ($properties as $key => $value) {
// Make properties type consistent.
if (is_string($value) || is_numeric($value)) {
if (is_object($object)) {
$object->{$key} = "$value";
}
elseif (is_array($object)) {
$object[$key] = "$value";
}
}
else {
if (is_object($object)) {
$object->{$key} = $value;
}
elseif (is_array($object)) {
$object[$key] = $value;
}
}
}
}

View File

@@ -0,0 +1,204 @@
<?php
/**
* @file
* Handling of universally unique identifiers.
*/
/**
* Pattern for detecting a valid UUID.
*/
define('UUID_PATTERN', '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}');
/**
* Generates an universally unique identifier.
*
* This function first checks for the PECL alternative for generating
* universally unique identifiers. If that doesn't exist, then it falls back on
* PHP for generating that.
*
* @return
* An UUID, made up of 32 hex digits and 4 hyphens.
*/
function uuid_generate() {
$callback = drupal_static(__FUNCTION__);
if (empty($callback)) {
if (function_exists('uuid_create') && !function_exists('uuid_make')) {
$callback = '_uuid_generate_pecl';
}
elseif (function_exists('com_create_guid')) {
$callback = '_uuid_generate_com';
}
else {
$callback = '_uuid_generate_php';
}
}
return $callback();
}
/**
* Generate all missing UUIDs.
*/
function uuid_sync_all() {
module_invoke_all('uuid_sync');
}
/**
* Generates a UUID URI for an entity.
*
* @param object $entity
* The entity to use for generating the UUID URI.
* @param string $entity_type
* The type of entity being used.
*
* @return string
* The generated UUID URI or normal URI if entity doesn't support UUIDs.
*/
function uuid_entity_uuid_uri($entity, $entity_type) {
$entity_info = entity_get_info($entity_type);
if (empty($entity_info['uuid'])) {
$uri = $entity_info['uri callback']($entity);
return $uri['path'];
}
if (isset($entity_info['uuid uri callback'])) {
return $entity_info['uuid uri callback']($entity);
}
return "uuid/{$entity_type}/" . $entity->{$entity_info['entity keys']['uuid']};
}
/**
* Converts an ID URI string to an entity data array.
*
* @see uuid_id_uri_array_to_data()
*
* @param string $uri
* The URI to convert.
*
* @return array
* The entity data.
*/
function uuid_id_uri_to_data($uri) {
$parts = explode('/', $uri);
return uuid_id_uri_array_to_data($parts);
}
/**
* Converts a URI array to entity data array.
*
* @param array $uri
* The URI parts, often taken from arg().
*
* @return array
* The entity data.
*/
function uuid_id_uri_array_to_data($uri) {
$data = array(
'request' => $uri,
'entity_type' => $uri[0],
'id' => $uri[1],
);
drupal_alter('uuid_id_uri_data', $data);
return $data;
}
/**
* Converts a UUID URI string to an entity data array.
*
* @see uuid_uri_array_to_data()
*
* @param string $uri
* The URI to convert.
*
* @return array
* The entity data.
*/
function uuid_uri_to_data($uri, $strip_uuid = TRUE) {
return uuid_uri_array_to_data(explode('/', $uri));
}
/**
* Converts a URI array to entity data array.
*
* @param array $uri
* The URI parts, often taken from arg().
*
* @return array
* The entity data.
*/
function uuid_uri_array_to_data($uri, $strip_uuid = TRUE) {
if ($strip_uuid) {
array_shift($uri);
}
$data = array(
'request' => $uri,
'entity_type' => $uri[0],
'uuid' => $uri[1],
);
drupal_alter('uuid_uri_data', $data);
return $data;
}
/**
* Generates a UUID using the Windows internal GUID generator.
*
* @see http://php.net/com_create_guid
*/
function _uuid_generate_com() {
// Remove {} wrapper and make lower case to keep result consistent.
return drupal_strtolower(trim(com_create_guid(), '{}'));
}
/**
* Generates an universally unique identifier using the PECL extension.
*/
function _uuid_generate_pecl() {
return uuid_create(UUID_TYPE_DEFAULT);
}
/**
* Generates a UUID v4 using PHP code.
*
* Based on code from @see http://php.net/uniqid#65879 , but corrected.
*/
function _uuid_generate_php() {
// The field names refer to RFC 4122 section 4.1.2.
return sprintf('%04x%04x-%04x-4%03x-%04x-%04x%04x%04x',
// 32 bits for "time_low".
mt_rand(0, 65535), mt_rand(0, 65535),
// 16 bits for "time_mid".
mt_rand(0, 65535),
// 12 bits after the 0100 of (version) 4 for "time_hi_and_version".
mt_rand(0, 4095),
bindec(substr_replace(sprintf('%016b', mt_rand(0, 65535)), '10', 0, 2)),
// 8 bits, the last two of which (positions 6 and 7) are 01, for "clk_seq_hi_res"
// (hence, the 2nd hex digit after the 3rd hyphen can only be 1, 5, 9 or d)
// 8 bits for "clk_seq_low" 48 bits for "node".
mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535)
);
}
// This is wrapped in an if block to avoid conflicts with PECL's uuid_is_valid().
/**
* Check that a string appears to be in the format of a UUID.
*
* @param $uuid
* The string to test.
*
* @return
* TRUE if the string is well formed.
*/
if (!function_exists('uuid_is_valid')) {
function uuid_is_valid($uuid) {
return preg_match('/^' . UUID_PATTERN . '$/', $uuid);
}
}

View File

@@ -0,0 +1,13 @@
name = Universally Unique ID
description = Extends the entity functionality and adds support for universally unique identifiers.
core = 7.x
package = UUID
configure = admin/config/system/uuid
files[] = uuid.test
; Information added by drupal.org packaging script on 2013-02-03
version = "7.x-1.0-alpha3+52-dev"
core = "7.x"
project = "uuid"
datestamp = "1359858369"

View File

@@ -0,0 +1,232 @@
<?php
/**
* @file
* Install, update and uninstall functions for the uuid module.
*/
define('UUID_UPGRADE_VAR', 'uuid_upgrade_in_progress');
/**
* Include some helper functions for the Entity API.
*/
module_load_include('inc', 'uuid', 'uuid.entity');
/**
* Helper function that returns a schema field definition for UUID fields.
*
* @see uuid_schema_alter()
* @see uuid_install()
*/
function uuid_schema_field_definition() {
return array(
'type' => 'char',
'length' => 36,
'not null' => TRUE,
'default' => '',
'description' => 'The Universally Unique Identifier.',
);
}
/**
* Implements of hook_schema_alter().
*/
function uuid_schema_alter(&$schema = array()) {
$field = uuid_schema_field_definition();
foreach (uuid_get_core_entity_info() as $entity_type => $info) {
$schema[$info['base table']]['fields'][$info['entity keys']['uuid']] = $field;
if (!empty($info['revision table']) && !empty($info['entity keys']['revision uuid'])) {
$schema[$info['revision table']]['fields'][$info['entity keys']['revision uuid']] = $field;
}
}
}
/**
* Implements hook_install().
*/
function uuid_install() {
_uuid_install_uuid_fields();
uuid_sync_all();
}
/**
* Install the 'uuid' and 'vuuid' fields into Drupal core entity tables where needed.
*
* IMPORTANT: This function is called both at install and update time. If this method
* is modified to add additional fields in the future, the update strategy must be
* considered. See the comment in uuid_update_7102.
*/
function _uuid_install_uuid_fields() {
$field = uuid_schema_field_definition();
foreach (uuid_get_core_entity_info() as $entity_type => $info) {
if (!db_field_exists($info['base table'], $info['entity keys']['uuid'])) {
db_add_field($info['base table'], $info['entity keys']['uuid'], $field);
db_add_index($info['base table'], $info['entity keys']['uuid'], array($info['entity keys']['uuid']));
}
if (!empty($info['revision table']) && !empty($info['entity keys']['revision uuid'])) {
if (!db_field_exists($info['revision table'], $info['entity keys']['revision uuid'])) {
db_add_field($info['revision table'], $info['entity keys']['revision uuid'], $field);
db_add_index($info['revision table'], $info['entity keys']['revision uuid'], array($info['entity keys']['revision uuid']));
}
}
}
}
/**
* Implements hook_uninstall().
*/
function uuid_uninstall() {
foreach (uuid_get_core_entity_info() as $entity_type => $info) {
if (db_field_exists($info['base table'], $info['entity keys']['uuid'])) {
db_drop_field($info['base table'], $info['entity keys']['uuid']);
db_drop_index($info['base table'], $info['entity keys']['uuid']);
}
if (!empty($info['revision table']) && !empty($info['entity keys']['revision uuid'])) {
if (db_field_exists($info['revision table'], $info['entity keys']['revision uuid'])) {
db_drop_field($info['revision table'], $info['entity keys']['revision uuid']);
db_drop_index($info['revision table'], $info['entity keys']['revision uuid']);
}
}
}
}
/**
* Implements hook_modules_installed().
*/
function uuid_modules_installed($modules) {
// Run the installation hook. This makes sure that the schema for all
// supported core entity types is set correct.
uuid_install();
}
/**
* Create uuid_vocabulary and uuid_term_data tables.
*/
function uuid_update_6001() {
$ret = array();
db_create_table($ret, 'uuid_vocabulary', uuid_table_schema('vocabulary', 'vid'));
db_create_table($ret, 'uuid_term_data', uuid_table_schema('term_data', 'tid'));
return $ret;
}
/**
* For each of out tables, drop the indexe on the UUID column and add a unique
* key on that column.
*/
function uuid_update_6002() {
$ret = array();
foreach (uuid_schema() as $table => $schema) {
db_drop_index($ret, $table, $table . '_uuid_idx');
db_add_unique_key($ret, $table, $table . '_uuid_key', array('uuid'));
}
return $ret;
}
/**
* Create uuid_comment table.
*/
function uuid_update_6003() {
$ret = array();
db_create_table($ret, 'uuid_comments', uuid_table_schema('comments', 'cid'));
return $ret;
}
/**
* Fix the column definitions for uuid columns in all tables
* to use the more efficient char spec.
*/
function uuid_update_6004() {
$ret = array();
// Use what's in uuid_table_schema in order to be consistent.
$tables = uuid_schema();
$spec = $tables['uuid_node']['fields']['uuid'];
foreach ($tables as $tablename => $schema) {
if (db_table_exists($tablename)) {
db_change_field($ret, $tablename, 'uuid', 'uuid', $spec);
}
}
return $ret;
}
/**
* Modify existing uuid_node_revisions table to support revision deletion, and
* add in as much legacy data as possible.
*/
function uuid_update_6005() {
$ret = array();
if (db_table_exists('uuid_node_revisions')) {
// Use what's already defined in uuid schema in order to be consistent.
$schema = uuid_schema();
$spec = $schema['uuid_node_revisions']['fields']['nid'];
db_add_field($ret, 'uuid_node_revisions', 'nid', $spec);
// Add node ids to the new column, for revisions that exist, but now have a
// default value of 0 in uuid_node_revisions.
$result = db_query('SELECT nr.nid, nr.vid FROM {node_revisions} AS nr LEFT JOIN {uuid_node_revisions} AS unr ON unr.vid=nr.vid WHERE unr.nid=%d', 0);
while ($item = db_fetch_object($result)) {
$ret[] = update_sql('UPDATE {uuid_node_revisions} SET nid=' . (int) $item->nid . ' WHERE vid=' . (int) $item->vid);
}
// Add uuid_node_revision rows for rows that don't exist, but should.
$result = db_query('SELECT nr.nid, nr.vid FROM {node_revisions} AS nr LEFT JOIN {uuid_node_revisions} AS unr ON unr.vid=nr.vid WHERE unr.nid IS NULL');
while ($item = db_fetch_object($result)) {
$ret[] = update_sql(sprintf("INSERT INTO {uuid_node_revisions} (vid, uuid, nid) VALUES(%d, '%s', %d)", $item->vid, uuid_uuid(), $item->nid));
}
// Delete any orphaned revision vid, uuid pairs.
$ret[] = update_sql('DELETE FROM {uuid_node_revisions} WHERE nid=0');
}
return $ret;
}
/**
* Remove old variables.
*/
function uuid_update_7100() {
$types = array(
'nodes',
'users',
'taxonomy',
'comments',
);
foreach ($types as $type) {
variable_del('uuid_automatic_for_' . $type);
}
return array();
}
/**
* Clear cache for installations that used alpha1. Modules that previously was
* enabled in uuid_update_7100() doesn't exist anymore.
*/
function uuid_update_7101() {
drupal_flush_all_caches();
}
/**
* Insure that the uuid and vuuid fields are added where needed.
*
* Note that update 7102 calls _uuid_install_uuid_fields(), which is an
* idempotent function. If _uuid_install_uuid_fields() is changed at some
* point in the future (but remains idempotent), then some uuid users
* will have run update 7102, and some will not. A new uuid_update_7103()
* function would would therefore be necessary to update all users to
* the latest schema. At the same time, uuid_update_7102() could become
* an empty function, as it would not be necessary to call _uuid_install_uuid_fields()
* twice.
*/
function uuid_update_7102() {
// If the user have disabled the UUID module during upgrade (as UPGRADE.txt
// instructs), some functions will be missing. So include the module file.
module_load_include('module', 'uuid', 'uuid');
_uuid_install_uuid_fields();
uuid_sync_all();
}

View File

@@ -0,0 +1,229 @@
<?php
/**
* @file
* Main module functions for the uuid module.
*/
/**
* Include main API functions.
*/
module_load_include('inc', 'uuid', 'uuid');
/**
* Include the Entity API implementation of the UUID API.
*/
module_load_include('inc', 'uuid', 'uuid.entity');
/**
* Include implementations for all core modules.
*
* Instead of having separate modules for all core implementations this file
* implements all of them in their own name space. In some cases this adds some
* unecessary code weight, but instead it saves us from module mania.
*/
module_load_include('inc', 'uuid', 'uuid.core');
/**
* Implements of hook_menu().
*/
function uuid_menu() {
$items = array();
$items['uuid'] = array(
'title' => 'UUID redirector',
'description' => 'Redirects requests for UUID URIs to the referenced entity.',
'page callback' => 'uuid_redirector',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$items['admin/config/system/uuid'] = array(
'title' => 'Universally unique identifiers',
'description' => 'Configure universally unique identifiers.',
'page callback' => 'drupal_get_form',
'page arguments' => array('uuid_admin_form'),
'access arguments' => array('administer uuid'),
'type' => MENU_NORMAL_ITEM,
'file' => 'uuid.admin.inc',
);
// Conditional support for Devel. A good thing for developers.
if (module_exists('devel')) {
$entity_types = array(
'user' => array('path' => 'user/%user/devel/load-by-uuid', 'arg' => 1),
'node' => array('path' => 'node/%node/devel/load-by-uuid', 'arg' => 1),
'comment' => array('path' => 'comment/%comment/devel/load-by-uuid', 'arg' => 1),
'taxonomy_term' => array('path' => 'taxonomy/term/%taxonomy_term/devel/load-by-uuid', 'arg' => 2),
);
foreach ($entity_types as $entity_type => $info) {
$items[$info['path']] = array(
'title' => 'Load by UUID',
'page callback' => 'uuid_devel_load_by_uuid',
'page arguments' => array($entity_type, $info['arg']),
'access arguments' => array('access devel information'),
'type' => MENU_LOCAL_TASK,
'file' => 'uuid.admin.inc',
'weight' => 100,
);
}
}
return $items;
}
/**
* Implements of hook_ctools_plugin_directory().
*/
function uuid_ctools_plugin_directory($module, $plugin) {
if ($module == 'ctools') {
return 'plugins/' . $plugin;
}
}
/**
* Implements of hook_permission().
*/
function uuid_permission() {
return array(
'administer uuid' => array(
'title' => t('Administer UUID'),
'description' => t('Allows configuration of the UUID module and APIs.'),
),
);
}
/**
* Implements hook_hook_info().
*/
function uuid_hook_info() {
$hook_names = array(
'uuid_info',
'uuid_sync',
'entity_uuid_load',
'field_uuid_load',
'entity_uuid_presave',
'entity_uuid_save',
'entity_uuid_delete',
'field_uuid_presave',
'uuid_menu_path_to_uri_alter',
'uuid_menu_uri_to_path_alter',
'uuid_default_entities',
'uuid_entities_pre_rebuild',
'uuid_entities_pre_revert',
'uuid_entities_post_rebuild',
'uuid_entities_post_revert',
'uuid_entities_features_export_entity_alter',
'uuid_entities_features_export_field_alter',
'uuid_uri_data',
'uuid_id_uri_data',
'uuid_uri_data',
'uuid_id_uri_data',
);
return array_fill_keys($hook_names, array('group' => 'uuid'));
}
/**
* Implementation of hook_views_api().
*/
function uuid_views_api() {
return array(
'api' => 2,
'path' => drupal_get_path('module', 'uuid'),
);
}
/**
* Implements of hook_module_implements_alter().
*
* Moves implementation of hook_entity_info_alter() to the bottom so it is
* invoked after all modules relying on the entity API.
*
* @see uuid_entity_info_alter()
*/
function uuid_module_implements_alter(&$Implementss, $hook) {
if ($hook == 'entity_info_alter') {
// Move our hook Implements to the bottom.
$group = $Implementss['uuid'];
unset($Implementss['uuid']);
$Implementss['uuid'] = $group;
}
}
/**
* Implements of hook_uuid_sync().
*/
function uuid_uuid_sync() {
foreach (entity_get_info() as $entity_type => $info) {
if (isset($info['uuid']) && $info['uuid'] == TRUE && !empty($info['entity keys']['uuid'])) {
_uuid_sync_table($info['base table'], $info['entity keys']['id'], $info['entity keys']['uuid']);
if (!empty($info['entity keys']['revision uuid'])) {
_uuid_sync_table($info['revision table'], $info['entity keys']['revision'], $info['entity keys']['revision uuid']);
}
}
}
}
/**
* Helper function that executes the update on the actual table.
*/
function _uuid_sync_table($table, $id_field, $uuid_field) {
// Fetch empty records.
$result = db_select($table, 't')
->fields('t', array($id_field))
->condition(db_or()->condition($uuid_field, '')->isNull($uuid_field))
->execute();
// Update empty records.
foreach ($result as $record) {
db_update($table)
->fields(array($uuid_field => uuid_generate()))
->condition($id_field, $record->{$id_field})
->execute();
}
}
/**
* Implementation of hook_features_api().
*
* The Features support consists of exporting entities from a Deploy
* <em>fetch-only</em> plan. Deploy is only required to generate the feature
* it self.
*
* The reason why we depend on Deploy for the generation of the content is
* because Deploy has the kind of dependency detection framework we need, to
* identify all dependencies for all entities.
*/
function uuid_features_api() {
return array(
'uuid_entities' => array(
'name' => t('UUID entities'),
'default_hook' => 'uuid_default_entities',
'default_file' => FEATURES_DEFAULTS_INCLUDED,
'feature_source' => TRUE,
'file' => drupal_get_path('module', 'uuid') .'/uuid.features.inc',
),
);
}
/**
* Redirects all UUID URI requests to the appropriate entity page.
*/
function uuid_redirector() {
$entity_data = uuid_uri_array_to_data(arg());
$entity_info = entity_get_info($entity_data['entity_type']);
if (empty($entity_info['uuid'])) {
return drupal_not_found();
}
$entities = entity_uuid_load($entity_data['entity_type'], array($entity_data['uuid']));
if (!count($entities)) {
return drupal_not_found();
}
$uri = entity_uri($entity_data['entity_type'], current($entities));
drupal_goto($uri['path'], array(), 301);
}

View File

@@ -0,0 +1,690 @@
<?php
/**
* @file
* Test suite for UUID module.
*/
/**
* Base class with some helper methods.
*/
class UUIDTestCase extends DrupalWebTestCase {
function setUp() {
parent::setUp(func_get_args());
}
/**
* Helper function that asserts a UUID.
*/
function assertUUID($uuid, $message = NULL) {
$this->assertTrue(uuid_is_valid($uuid), $message);
}
}
/**
* Tests the UUID API functions.
*/
class UUIDAPITestCase extends UUIDTestCase {
public static function getInfo() {
return array(
'name' => 'UUID API',
'description' => 'Tests the UUID API functions.',
'group' => 'UUID',
);
}
function setUp() {
parent::setUp('uuid');
}
function testAPIFunctions() {
// This is a valid UUID, we know that.
$valid_uuid = '0ab26e6b-f074-4e44-9da6-1205fa0e9761';
// Test the uuid_is_valid() function.
$this->assertUUID($valid_uuid, 'UUID validation works.');
// The default generator is 'php'.
$uuid = uuid_generate();
$this->assertUUID($uuid, 'PHP generator works.');
// Test the 'mysql' generator.
variable_set('uuid_generator', 'mysql');
drupal_static_reset('uuid_generate');
$uuid = uuid_generate();
$this->assertUUID($uuid, 'MySQL generator works.');
}
}
/**
* Tests the Entity API functions.
*/
class UUIDEntityTestCase extends UUIDTestCase {
public static function getInfo() {
return array(
'name' => 'Entity API functions',
'description' => 'Tests the Entity API functions.',
'group' => 'UUID',
);
}
function setUp() {
parent::setUp('uuid');
}
/**
* Tests Entity API's UUID functions.
*/
function testEntityAPIFunctions() {
// Create some entities that we will work with.
$user = $this->drupalCreateUser();
$node = $this->drupalCreateNode(array('title' => 'original title', 'uid' => $user->uid));
// Test entity_get_id_by_uuid().
$nids = entity_get_id_by_uuid('node', array($node->uuid), FALSE);
$this->assertTrue(in_array($node->nid, $nids), 'Lookup of entity ID works.');
$vids = entity_get_id_by_uuid('node', array($node->vuuid), TRUE);
$this->assertTrue(in_array($node->vid, $vids), 'Lookup of entity revision ID works.');
// Test entity_get_uuid_by_id().
$uuids = entity_get_uuid_by_id('node', array($node->nid), FALSE);
$this->assertTrue(in_array($node->uuid, $uuids), 'Lookup of entity UUID works.');
$vuuids = entity_get_uuid_by_id('node', array($node->vid), TRUE);
$this->assertTrue(in_array($node->vuuid, $vuuids), 'Lookup of entity revision UUID works.');
}
}
/**
* Tests the User implementation.
*/
class UUIDUserTestCase extends UUIDTestCase {
public static function getInfo() {
return array(
'name' => 'User implementation',
'description' => 'Tests the User implementation.',
'group' => 'UUID',
);
}
function setUp() {
// Some tests depends on the optional Entity API module.
if (module_exists('entity')) {
parent::setUp('uuid', 'entity');
}
else {
parent::setUp('uuid');
}
}
/**
* Test CRUD on users with UUID functions.
*/
function testUserCRUD() {
$user = $this->drupalCreateUser();
$this->assertUUID($user->uuid, 'User UUID was generated.');
// Test updating user.
$user_test = clone $user;
user_save($user_test, array('name' => 'new name'));
$user_test = user_load($user->uid, TRUE);
$this->assertEqual($user_test->uuid, $user->uuid, 'User UUID was intact after update.');
// Test entity_uuid_load().
$users = entity_uuid_load('user', array($user->uuid), array(), TRUE);
$user_test = reset($users);
$this->assertEqual($user_test->uid, $user->uid, 'User was correctly loaded with UUID.');
// The following tests depends on the optional Entity API module.
if (module_exists('entity')) {
// Test entity_uuid_save() for users.
$user_test = clone $user;
$user_test->uid = rand();
$user_test->name = 'new name';
entity_uuid_save('user', $user_test);
$user_test = user_load($user->uid, TRUE);
$this->assertEqual($user_test->name, 'new name', 'Saving user with UUID mapped to correct user.');
$this->assertEqual($user_test->uuid, $user->uuid, 'User UUID was intact after saving with UUID.');
// Test entity_uuid_delete() for users.
entity_uuid_delete('user', $user->uuid);
$user_test = user_load($user->uid);
$this->assertFalse($user_test, 'Deleting user with UUID worked.');
}
}
}
/**
* Tests the Node implementation.
*/
class UUIDNodeTestCase extends UUIDTestCase {
public static function getInfo() {
return array(
'name' => 'Node implementation',
'description' => 'Tests the Node implementation.',
'group' => 'UUID',
);
}
function setUp() {
// Some tests depends on the optional Entity API module.
if (module_exists('entity')) {
parent::setUp('uuid', 'entity');
}
else {
parent::setUp('uuid');
}
}
/**
* Tests CRUD on nodes with UUID functions.
*/
function testNodeCRUD() {
// Create some entities that we will work with.
$user = $this->drupalCreateUser();
$node = $this->drupalCreateNode(array('title' => 'original title', 'uid' => $user->uid));
$this->assertUUID($node->uuid, 'Node UUID was generated.');
$this->assertUUID($node->vuuid, 'Node revision UUID was generated.');
// Test node update, without creating new revision.
$node_test = clone $node;
$node_test->title = 'new title';
$node_test->revision = FALSE;
node_save($node_test);
$node_test = node_load($node->nid, FALSE, TRUE);
$this->assertEqual($node_test->uuid, $node->uuid, 'Node UUID was intact after update, when not creating new revision.');
$this->assertEqual($node_test->vuuid, $node->vuuid, 'Node revision UUID was intact after updating, when not creating new revision.');
// Test node update, with new revision.
$node_test = clone $node;
$node_test->title = 'newer title';
$node_test->revision = TRUE;
node_save($node_test);
$node_test = node_load($node->nid, FALSE, TRUE);
$this->assertEqual($node_test->uuid, $node->uuid, 'Node UUID was intact after updating, when creating new revision.');
$this->assertNotEqual($node_test->vuuid, $node->vuuid, 'A new node revision UUID was generated, when creating new revision.');
$this->assertUUID($node_test->vuuid, 'The new node revision UUID was valid.');
// Test entity_uuid_load().
$nodes = entity_uuid_load('node', array($node->uuid), array(), TRUE);
$node_test = reset($nodes);
$this->assertEqual($node_test->nid, $node->nid, 'Node was correctly loaded with UUID.');
$this->assertEqual($node_test->uid, $user->uuid, "Node property 'uid' was transformed to UUID when loaded with UUID.");
// The following tests depends on the optional Entity API module.
if (module_exists('entity')) {
// Reload the node again because we have created new revisions above.
$node = node_load($node->nid, FALSE, TRUE);
// Test entity_uuid_save() for nodes.
$nodes = entity_uuid_load('node', array($node->uuid), array(), TRUE);
$node_test = reset($nodes);
$node_test->nid = rand();
$node_test->vid = rand();
$node_test->title = 'new title';
$node_test->revision = FALSE;
entity_uuid_save('node', $node_test);
$node_test = node_load($node->nid, FALSE, TRUE);
$this->assertEqual($node_test->title, 'new title', 'Saving node with UUID mapped to correct node, when not creating new revision.');
$this->assertEqual($node_test->uuid, $node->uuid, 'Node UUID was intact after saving with UUID, when not creating new revision.');
$this->assertEqual($node_test->vuuid, $node->vuuid, 'Node revison UUID was intact after saving with UUID, when not creating new revision.');
$this->assertEqual($node_test->uid, $node->uid, "Node property 'uid' was intact after saving with UUID, when not creating new revision.");
// Test the same thing again, but now triggering a new revision.
$nodes = entity_uuid_load('node', array($node->uuid), array(), TRUE);
$node_test = reset($nodes);
$node_test->nid = rand();
$node_test->vid = rand();
$node_test->title = 'newer title';
$node_test->revision = TRUE;
entity_uuid_save('node', $node_test);
$node_test = node_load($node->nid, FALSE, TRUE);
$this->assertEqual($node_test->title, 'newer title', 'Saving node with UUID mapped to correct node, when creating new revision.');
$this->assertEqual($node_test->uuid, $node->uuid, 'Node UUID was intact after saving with UUID, when creating new revision.');
$this->assertNotEqual($node_test->vuuid, $node->vuuid, 'A new node revison UUID was generated after saving with UUID, when creating new revision.');
$this->assertUUID($node_test->vuuid, 'New node revision UUID was valid.');
$this->assertEqual($node_test->uid, $node->uid, "Node property 'uid' was intact after saving with UUID, when creating new revision.");
// Test entity_uuid_delete() for nodes.
entity_uuid_delete('node', $node->uuid);
$node_test = node_load($node->nid);
$this->assertFalse($node_test, 'Deleting node with UUID worked.');
}
}
}
/**
* Tests the Comment implementation.
*
* @todo
* Contribute patch to CommentHelperCase::setUp() to make it extendable.
*/
class UUIDCommentTestCase extends CommentHelperCase {
public static function getInfo() {
return array(
'name' => 'Comment implementation',
'description' => 'Tests the Comment implementation.',
'group' => 'UUID',
);
}
/**
* Helper function that asserts a UUID.
*
* We have duplicated this function from UUIDTestCase since we have to extend
* CommentHelperCase instead.
*/
function assertUUID($uuid, $message = NULL) {
$this->assertTrue(uuid_is_valid($uuid), $message);
}
/**
* Test CRUD on comments with UUID functions.
*/
function testCommentCRUD() {
// This is sub optimal, but due to how CommentHelperCase::setUp() is
// constructed we are enforced to do this. So unfortunately this test
// depends on 'entity' module for now.
module_enable(array('uuid', 'entity'), TRUE);
$user = $this->drupalCreateUser();
$this->drupalLogin($user);
$node = $this->drupalCreateNode();
$return = $this->postComment($node, 'Lorem ipsum');
$comment = comment_load($return->id);
$this->assertUUID($comment->uuid, 'Comment UUID was generated.');
// Test updating comment.
$comment_test = clone $comment;
$comment_test->subject = 'new subject';
comment_save($comment_test);
$comment_test = comment_load($comment->cid);
$this->assertEqual($comment_test->uuid, $comment->uuid, 'Comment UUID was intact after update.');
// Test entity_uuid_load().
$comments = entity_uuid_load('comment', array($comment->uuid), array(), TRUE);
$comment_test = reset($comments);
$this->assertEqual($comment_test->cid, $return->id, 'Comment was correctly loaded with UUID.');
$this->assertEqual($comment_test->uid, $user->uuid, "Comment property 'uid' was transformed to UUID when loaded with UUID.");
$this->assertEqual($comment_test->nid, $node->uuid, "Comment property 'nid' was transformed to UUID when loaded with UUID.");
// The following tests depends on the optional Entity API module.
if (module_exists('entity')) {
// Test entity_uuid_save() for comments.
$comments = entity_uuid_load('comment', array($comment->uuid), array(), TRUE);
$comment_test = reset($comments);
$comment_test->cid = rand();
$comment_test->subject = 'newer subject';
entity_uuid_save('comment', $comment_test);
$comment_test = comment_load($comment->cid);
$this->assertEqual($comment_test->subject, 'newer subject', 'Saving comment with UUID mapped to correct comment.');
$this->assertEqual($comment_test->uuid, $comment->uuid, 'Comment UUID was intact after saving with UUID.');
$this->assertEqual($comment_test->uid, $user->uid, "Comment property 'uid' was after saving with UUID.");
$this->assertEqual($comment_test->nid, $node->nid, "Comment property 'nid' was after saving with UUID.");
// Test entity_uuid_delete() for comments.
entity_uuid_delete('comment', $comment->uuid);
$comment_test = comment_load($comment->cid);
$this->assertFalse($comment_test, 'Deleting comment with UUID worked.');
}
}
}
/**
* Tests the Taxonomy implementation.
*/
class UUIDTaxonomyTestCase extends TaxonomyWebTestCase {
public static function getInfo() {
return array(
'name' => 'Taxonomy implementation',
'description' => 'Tests the Taxonomy implementation.',
'group' => 'UUID',
);
}
/**
* A lot of code here is taken from TaxonomyTermTestCase::setUp().
*/
function setUp() {
// Some tests depends on the optional Entity API module.
if (module_exists('entity')) {
parent::setUp('taxonomy', 'uuid', 'entity');
}
else {
parent::setUp('taxonomy', 'uuid');
}
}
/**
* Helper function that asserts a UUID.
*
* We have duplicated this function from UUIDTestCase since we have to extend
* TaxonomyWebTestCase instead.
*/
function assertUUID($uuid, $message = NULL) {
$this->assertTrue(uuid_is_valid($uuid), $message);
}
/**
* Test CRUD on comments with UUID functions.
*/
function testTaxonomyCRUD() {
$user = $this->drupalCreateUser(array('administer taxonomy', 'administer nodes', 'bypass node access'));
$this->drupalLogin($user);
// Create a term by tagging a node. We'll use this node later too.
$vocabulary = new stdClass;
$vocabulary->vid = 1;
$term = $this->createTerm($vocabulary);
$this->assertUUID($term->uuid, 'Term UUID was generated.');
// Test updating term.
$term_test = clone $term;
$term_test->name = 'new name';
taxonomy_term_save($term_test);
$term_test = taxonomy_term_load($term->tid);
$this->assertEqual($term_test->uuid, $term->uuid, 'Term UUID was intact after update.');
// Test entity_uuid_load().
$terms = entity_uuid_load('taxonomy_term', array($term->uuid), array(), TRUE);
$term_test = reset($terms);
$this->assertEqual($term_test->tid, $term->tid, 'Term was correctly loaded with UUID.');
// The following tests depends on the Entity API module.
if (module_exists('entity')) {
// Test entity_uuid_save() for terms.
$terms = entity_uuid_load('taxonomy_term', array($term->uuid), array(), TRUE);
$term_test = reset($terms);
$term_test->tid = rand();
$term_test->name = 'newer name';
entity_uuid_save('taxonomy_term', $term_test);
$term_test = taxonomy_term_load($term->tid);
$this->assertEqual($term_test->name, 'newer name', 'Saving term with UUID mapped to correct term.');
$this->assertEqual($term_test->uuid, $term->uuid, 'Term UUID was intact after saving with UUID.');
// Test entity_uuid_delete() for nodes.
entity_uuid_delete('taxonomy_term', $term->uuid);
$term_test = taxonomy_term_load($term->tid);
$this->assertFalse($term_test, 'Deleting term with UUID worked.');
}
}
}
/**
* Tests for the UUID synchronization.
*/
class UUIDSyncTestCase extends UUIDTestCase {
public static function getInfo() {
return array(
'name' => 'UUID sync',
'description' => 'Tests the UUID synchronization.',
'group' => 'UUID',
);
}
/**
* Helper function that asserts that a database table column exists.
*
* @todo
* There are something weird around this assertion.
*/
function assertTableColumn($table, $column, $message) {
$result = db_query("SHOW COLUMNS FROM {$table}");
$exists = FALSE;
foreach ($result as $record) {
if ($record->field == $column) {
$exists = TRUE;
break;
}
}
$this->assertTrue($exists, $message);
}
function testSync() {
// These entities will not have UUID from the start, since the UUID module
// isn't installed yet.
$user = $this->drupalCreateUser();
$node = $this->drupalCreateNode();
$this->assertTrue(!isset($node->uuid), "Node has no UUID before installation of UUID module.");
$this->assertTrue(!isset($node->vuuid), "Node has no revision UUID before installation of UUID module.");
$this->assertTrue(!isset($user->uuid), "User has no UUID before installation of UUID module.");
// Now enable the UUID module.
module_enable(array('uuid'), TRUE);
drupal_flush_all_caches();
drupal_static_reset();
// Check that the UUID column was generated for {node}.
$this->assertTableColumn('node', 'uuid', 'UUID column was generated for the node table.');
$this->assertTableColumn('node_revision', 'vuuid', 'Revision UUID column was generated for the node_revision table.');
$this->assertTableColumn('users', 'uuid', 'UUID column was generated for the user table.');
// Login with a user and click the sync button.
$web_user = $this->drupalCreateUser(array('administer uuid'));
$this->drupalLogin($web_user);
$this->drupalPost('admin/config/system/uuid', array(), t('Create missing UUIDs'));
// Test if UUID was generated for nodes.
$node_test = node_load($node->nid, FALSE, TRUE);
$this->assertUUID($node_test->uuid, 'Node UUID was generated when clicking the sync button.');
$this->assertUUID($node_test->vuuid, 'Node revision UUID was generated when clicking the sync button.');
// Test if UUID was generated for users.
$user_test = user_load($user->uid, TRUE);
$this->assertUUID($user_test->uuid, 'User UUID was generated when clicking the sync button.');
}
}
class UUIDExportEntitiesWithDeploy extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Export UUID entities',
'description' => 'Test exporting UUID entities with Deploy and Features.',
'group' => 'UUID',
);
}
function setUp() {
parent::setUp('taxonomy', 'uuid', 'entity', 'features', 'deploy', 'deploy_example');
}
function testExport() {
$test_user = $this->drupalCreateUser();
$test_node = $this->drupalCreateNode(array(
'uid' => $test_user->uid,
));
deploy_manager_add_to_plan('deploy_example_plan', 'node', $test_node);
// TODO: Test the actual insert.
$this->assertTrue(TRUE, 'Added a node with a user dependency to be exported as a Feature module.');
// Login and recreate the example feature. The feature isn't installed. But
// Features can still export the code, and we can test it.
$web_user = $this->drupalCreateUser(array('administer features'));
$this->drupalLogin($web_user);
$code = $this->drupalPost('admin/structure/features/uuid_default_entities_example/recreate', array(), t('Download feature'));
$this->assertTrue($code, 'Feature module was exported.');
// Ensure that we find what we expect in the exported code.
$node_test1 = preg_match('/' . $test_node->title . '/', $code);
$node_test2 = preg_match("/'uri' => 'node\/" . $test_node->uuid . "'/", $code);
$this->assertTrue($node_test1, 'Node title was found in the expoted code.');
$this->assertTrue($node_test2, 'Node URI was found in the expoted code.');
$user_test1 = preg_match('/' . $test_user->name . '/', $code);
$user_test2 = preg_match("/'uri' => 'user\/" . $test_user->uuid . "'/", $code);
$this->assertTrue($user_test1, 'User was found in the expoted code.');
$this->assertTrue($user_test2, 'User URI was found in the expoted code.');
}
}
/**
* Tests for the UUID synchronization.
*/
class UUIDImportEntitiesTestCase extends UUIDTestCase {
/**
* Representation of the UUIDs that is exported in our example feature, that
* we use for testing.
*/
public $term1_uuid = 'bcb92ce8-2236-e264-65c8-0c163ae716d1';
public $term2_uuid = '4293a15c-531a-6164-7d1b-668ed019a6bd';
public $term3_uuid = 'af738a46-f278-cf84-d94d-9e03879fd71e';
public $node1_uuid = 'b0558664-c94b-3674-d9df-3e1696b2e471';
public $node2_uuid = '5e3d8bbe-a1f2-f2d4-fdc0-71e6c23aa837';
public $user1_uuid = '7cf875e6-dc15-4404-f190-5a7c3e91d14c';
/**
* Helper method to assert the uuid_entities component in any features.
*/
function assertFeatureState($feature, $state, $message = '') {
if (empty($message)) {
switch ($state) {
case FEATURES_DEFAULT:
$readable_state = 'default';
break;
case FEATURES_OVERRIDDEN:
$readable_state = 'overridden';
break;
default:
$readable_state = 'unknown';
break;
}
$message = format_string('%component in %feature had state: %state', array('%component' => 'uuid_entities', '%feature' => $feature, '%state' => $readable_state));
}
// Ensure that the features we used is in default state.
$states = features_get_component_states(array($feature), TRUE, TRUE);
if (!$this->assertEqual($states[$feature]['uuid_entities'], $state, $message)) {
debug(format_string('Enabling functionality to show diff output for debug purposes.'));
$success = module_enable(array('diff'));
if ($success) {
// Make sure we run on cold caches.
drupal_flush_all_caches();
drupal_static_reset();
$user = $this->drupalCreateUser(array('administer features'));
$this->drupalLogin($user);
$this->drupalGet('admin/structure/features/' . $feature . '/diff');
}
else {
debug(format_string('Download !module to see diff output for debug purposes.', array('!module' => 'diff.module')));
}
}
}
function getEntityByUuid($entity_type, $uuid) {
$ids = entity_get_id_by_uuid($entity_type, array($uuid));
$entities = entity_load($entity_type, $ids, NULL, TRUE);
return reset($entities);
}
function enableFeature($feature) {
$success = module_enable(array($feature), TRUE);
$this->assertTrue($success, t('Enabled modules: %modules', array('%modules' => implode(', ', array($feature)))));
// Make sure we run on cold caches.
drupal_flush_all_caches();
drupal_static_reset();
}
function revertFeature($feature) {
features_revert(array($feature => array('uuid_entities')));
$this->assertTrue(TRUE, format_string('Reverted feature: %feature', array('%feature' => $feature)));
}
function testImport() {
$term1 = $this->getEntityByUuid('taxonomy_term', $this->term1_uuid);
$term2 = $this->getEntityByUuid('taxonomy_term', $this->term2_uuid);
$term3 = $this->getEntityByUuid('taxonomy_term', $this->term3_uuid);
$node1 = $this->getEntityByUuid('node', $this->node1_uuid);
$node2 = $this->getEntityByUuid('node', $this->node2_uuid);
$user1 = $this->getEntityByUuid('user', $this->user1_uuid);
// Ensure that we don't have our entities yet.
$this->assertTrue(empty($term1), 'Term 1 has not been created yet.');
$this->assertTrue(empty($term2), 'Term 2 has not been created yet.');
$this->assertTrue(empty($term3), 'Term 3 has not been created yet.');
$this->assertTrue(empty($node1), 'Node 1 has not been created yet.');
$this->assertTrue(empty($node2), 'Node 2 has not been created yet.');
$this->assertTrue(empty($user1), 'User 1 has not been created yet.');
$this->enableFeature('uuid_default_entities_example');
$term1 = $this->getEntityByUuid('taxonomy_term', $this->term1_uuid);
$term2 = $this->getEntityByUuid('taxonomy_term', $this->term2_uuid);
$term3 = $this->getEntityByUuid('taxonomy_term', $this->term3_uuid);
$node1 = $this->getEntityByUuid('node', $this->node1_uuid);
$node2 = $this->getEntityByUuid('node', $this->node2_uuid);
$user1 = $this->getEntityByUuid('user', $this->user1_uuid);
// Ensure that our entities was created.
$this->assertEqual($term1->uuid, $this->term1_uuid, 'Term 1 was created.');
$this->assertEqual($term2->uuid, $this->term2_uuid, 'Term 2 was created.');
$this->assertEqual($term3->uuid, $this->term3_uuid, 'Term 3 was created.');
$this->assertEqual($node1->uuid, $this->node1_uuid, 'Node 1 was created.');
$this->assertEqual($node2->uuid, $this->node2_uuid, 'Node 2 was created.');
$this->assertEqual($user1->uuid, $this->user1_uuid, 'User 1 was created.');
// Check the features state.
$this->assertFeatureState('uuid_default_entities_example', FEATURES_DEFAULT);
// New property.
$new = 'foo bar';
// Change a term.
$term1->name = $new;
$status = taxonomy_term_save($term1);
$this->assertEqual($status, SAVED_UPDATED, 'Updated term 1.');
// Change a node.
$node1->title = $new;
node_save($node1);
$this->assertEqual($node1->title, $new, 'Updated node 1.');
// Change a user.
$user1->name = $new;
$updated_user = user_save($user1);
$this->assertEqual($user1->name, $updated_user->name, 'Updated user 1.');
// Check the features state.
$this->assertFeatureState('uuid_default_entities_example', FEATURES_OVERRIDDEN);
// Revert the feature.
$this->revertFeature('uuid_default_entities_example');
// Check the features state.
$this->assertFeatureState('uuid_default_entities_example', FEATURES_DEFAULT);
}
}
class UUIDImportEntitiesWithDeploy extends UUIDImportEntitiesTestCase {
public static function getInfo() {
return array(
'name' => 'Import UUID entities, with Deploy',
'description' => 'Test importing UUID entities with Features and Deploy.',
'group' => 'UUID',
);
}
function setUp() {
parent::setUp('taxonomy', 'uuid', 'entity', 'features', 'deploy', 'deploy_example');
}
}
class UUIDImportEntitiesWithoutDeploy extends UUIDImportEntitiesTestCase {
public static function getInfo() {
return array(
'name' => 'Import UUID entities, without Deploy',
'description' => 'Test importing UUID entities with Features only.',
'group' => 'UUID',
);
}
function setUp() {
parent::setUp('taxonomy', 'uuid', 'entity', 'features');
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* @file
* Builds placeholder replacement tokens for uuid-related data.
*/
/**
* Implements hook_token_info().
*/
function uuid_token_info() {
$tokens = array();
foreach (entity_get_info() as $entity_type => $info) {
if (isset($info['uuid']) && $info['uuid'] == TRUE && !empty($info['entity keys']['uuid'])) {
$token_type = !empty($info['token type']) ? $info['token type'] : $entity_type;
$tokens['tokens'][$token_type][$info['entity keys']['uuid']] = array(
'name' => t('@entity_type UUID', array('@entity_type' => $info['label'])),
'description' => t('The universally unique ID of the @entity', array('@entity' => drupal_strtolower($info['label']))),
);
if (!empty($info['entity keys']['revision uuid'])) {
$tokens['tokens'][$token_type][$info['entity keys']['revision uuid']] = array(
'name' => t('@entity_type revision UUID', array('@entity_type' => $info['label'])),
'description' => t('The universally unique revision ID of the @entity', array('@entity' => drupal_strtolower($info['label']))),
);
}
}
}
return $tokens;
}
/**
* Implements hook_tokens().
*/
function uuid_tokens($token_type, $tokens, $data = array(), $options = array()) {
$replacements = array();
if ($token_type == 'entity') {
$info = entity_get_info($data['entity_type']);
if (isset($info['uuid']) && $info['uuid'] == TRUE && !empty($info['entity keys']['uuid']) && !empty($tokens[$info['entity keys']['uuid']])) {
$replacements[$tokens[$info['entity keys']['uuid']]] = $data['entity']->{$info['entity keys']['uuid']};
if (!empty($info['entity keys']['revision uuid']) && !empty($tokens[$info['entity keys']['revision uuid']])) {
$replacements[$tokens[$info['entity keys']['revision uuid']]] = $data['entity']->{$info['entity keys']['revision uuid']};
}
}
}
return $replacements;
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* @file
* Views Implementation for UUID
*/
/**
* Implements hook_views_data_alter().
*/
function uuid_views_data_alter(&$data) {
foreach (entity_get_info() as $info) {
if (isset($info['uuid']) && $info['uuid'] == TRUE && !empty($info['entity keys']['uuid'])) {
$table = $info['base table'];
$field = $info['entity keys']['uuid'];
$data[$table][$field] = array(
'title' => t('@type UUID', array('@type' => $info['label'])),
'help' => t('The universally unique ID of the @type.', array('@type' => $info['label'])),
'field' => array(
'handler' => 'views_handler_field',
'click sortable' => TRUE,
),
'filter' => array(
'handler' => 'views_handler_filter_string',
'allow empty' => TRUE,
),
'argument' => array(
'handler' => 'views_handler_argument_string',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
);
}
}
}

View File

@@ -0,0 +1,402 @@
<?php
/**
* @file
* uuid_default_entities_example.features.uuid_entities.inc
*/
/**
* Implements hook_uuid_default_entities().
*/
function uuid_default_entities_example_uuid_default_entities() {
$entities = array();
$entities['deploy_example_plan'][] = (object) array(
'__metadata' => array(
'type' => 'taxonomy_term',
'uri' => 'taxonomy_term/bcb92ce8-2236-e264-65c8-0c163ae716d1',
'cause' => 'node/b0558664-c94b-3674-d9df-3e1696b2e471',
),
'description' => NULL,
'format' => NULL,
'name' => 'Foo',
'path' => array(
'alias' => 'term/foo',
),
'rdf_mapping' => array(
'rdftype' => array(
0 => 'skos:Concept',
),
'name' => array(
'predicates' => array(
0 => 'rdfs:label',
1 => 'skos:prefLabel',
),
),
'description' => array(
'predicates' => array(
0 => 'skos:definition',
),
),
'vid' => array(
'predicates' => array(
0 => 'skos:inScheme',
),
'type' => 'rel',
),
'parent' => array(
'predicates' => array(
0 => 'skos:broader',
),
'type' => 'rel',
),
),
'uuid' => 'bcb92ce8-2236-e264-65c8-0c163ae716d1',
'vocabulary_machine_name' => 'tags',
'weight' => '0',
);
$entities['deploy_example_plan'][] = (object) array(
'__metadata' => array(
'type' => 'taxonomy_term',
'uri' => 'taxonomy_term/4293a15c-531a-6164-7d1b-668ed019a6bd',
'cause' => 'node/b0558664-c94b-3674-d9df-3e1696b2e471',
),
'description' => NULL,
'format' => NULL,
'name' => 'Bar',
'rdf_mapping' => array(
'rdftype' => array(
0 => 'skos:Concept',
),
'name' => array(
'predicates' => array(
0 => 'rdfs:label',
1 => 'skos:prefLabel',
),
),
'description' => array(
'predicates' => array(
0 => 'skos:definition',
),
),
'vid' => array(
'predicates' => array(
0 => 'skos:inScheme',
),
'type' => 'rel',
),
'parent' => array(
'predicates' => array(
0 => 'skos:broader',
),
'type' => 'rel',
),
),
'uuid' => '4293a15c-531a-6164-7d1b-668ed019a6bd',
'vocabulary_machine_name' => 'tags',
'weight' => '0',
);
$entities['deploy_example_plan'][] = (object) array(
'__metadata' => array(
'type' => 'node',
'uri' => 'node/b0558664-c94b-3674-d9df-3e1696b2e471',
'cause' => FALSE,
),
'body' => array(
'und' => array(
0 => array(
'format' => 'filtered_html',
'summary' => '',
'value' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
),
),
),
'cid' => '0',
'comment' => '2',
'comment_count' => '0',
'field_image' => array(),
'field_tags' => array(
'und' => array(
0 => array(
'tid' => 'bcb92ce8-2236-e264-65c8-0c163ae716d1',
),
1 => array(
'tid' => '4293a15c-531a-6164-7d1b-668ed019a6bd',
),
),
),
'language' => 'und',
'last_comment_name' => NULL,
'last_comment_uid' => '1',
'log' => '',
'path' => array(
'alias' => 'lorem-ipsum',
),
'promote' => '1',
'rdf_mapping' => array(
'field_image' => array(
'predicates' => array(
0 => 'og:image',
1 => 'rdfs:seeAlso',
),
'type' => 'rel',
),
'field_tags' => array(
'predicates' => array(
0 => 'dc:subject',
),
'type' => 'rel',
),
'rdftype' => array(
0 => 'sioc:Item',
1 => 'foaf:Document',
),
'title' => array(
'predicates' => array(
0 => 'dc:title',
),
),
'created' => array(
'predicates' => array(
0 => 'dc:date',
1 => 'dc:created',
),
'datatype' => 'xsd:dateTime',
'callback' => 'date_iso8601',
),
'changed' => array(
'predicates' => array(
0 => 'dc:modified',
),
'datatype' => 'xsd:dateTime',
'callback' => 'date_iso8601',
),
'body' => array(
'predicates' => array(
0 => 'content:encoded',
),
),
'uid' => array(
'predicates' => array(
0 => 'sioc:has_creator',
),
'type' => 'rel',
),
'name' => array(
'predicates' => array(
0 => 'foaf:name',
),
),
'comment_count' => array(
'predicates' => array(
0 => 'sioc:num_replies',
),
'datatype' => 'xsd:integer',
),
'last_activity' => array(
'predicates' => array(
0 => 'sioc:last_activity_date',
),
'datatype' => 'xsd:dateTime',
'callback' => 'date_iso8601',
),
),
'status' => '1',
'sticky' => '0',
'title' => 'Lorem ipsum',
'tnid' => '0',
'translate' => '0',
'type' => 'article',
'uid' => '1',
'uuid' => 'b0558664-c94b-3674-d9df-3e1696b2e471',
);
$entities['deploy_example_plan'][] = (object) array(
'__metadata' => array(
'type' => 'taxonomy_term',
'uri' => 'taxonomy_term/af738a46-f278-cf84-d94d-9e03879fd71e',
'cause' => 'node/5e3d8bbe-a1f2-f2d4-fdc0-71e6c23aa837',
),
'description' => NULL,
'format' => NULL,
'name' => 'Baz',
'rdf_mapping' => array(
'rdftype' => array(
0 => 'skos:Concept',
),
'name' => array(
'predicates' => array(
0 => 'rdfs:label',
1 => 'skos:prefLabel',
),
),
'description' => array(
'predicates' => array(
0 => 'skos:definition',
),
),
'vid' => array(
'predicates' => array(
0 => 'skos:inScheme',
),
'type' => 'rel',
),
'parent' => array(
'predicates' => array(
0 => 'skos:broader',
),
'type' => 'rel',
),
),
'uuid' => 'af738a46-f278-cf84-d94d-9e03879fd71e',
'vocabulary_machine_name' => 'tags',
'weight' => '0',
);
$entities['deploy_example_plan'][] = (object) array(
'__metadata' => array(
'type' => 'user',
'uri' => 'user/7cf875e6-dc15-4404-f190-5a7c3e91d14c',
'cause' => 'node/5e3d8bbe-a1f2-f2d4-fdc0-71e6c23aa837',
),
'init' => 'no@example.com',
'language' => '',
'mail' => 'no@example.com',
'name' => 'mohamed',
'pass' => '$S$DtyVr4YQkvCpofZdLT4.L23Xb6E8HUkmEgZikN919RTZXZSePwso',
'picture' => NULL,
'rdf_mapping' => array(
'rdftype' => array(
0 => 'sioc:UserAccount',
),
'name' => array(
'predicates' => array(
0 => 'foaf:name',
),
),
'homepage' => array(
'predicates' => array(
0 => 'foaf:page',
),
'type' => 'rel',
),
),
'roles' => array(
2 => 'authenticated user',
3 => 'administrator',
),
'signature' => '',
'signature_format' => 'filtered_html',
'status' => '1',
'theme' => '',
'timezone' => 'Asia/Riyadh',
'uuid' => '7cf875e6-dc15-4404-f190-5a7c3e91d14c',
);
$entities['deploy_example_plan'][] = (object) array(
'__metadata' => array(
'type' => 'node',
'uri' => 'node/5e3d8bbe-a1f2-f2d4-fdc0-71e6c23aa837',
'cause' => FALSE,
),
'body' => array(
'und' => array(
0 => array(
'format' => 'filtered_html',
'summary' => '',
'value' => 'Nunc sollicitudin justo ut augue egestas et varius quam consectetur.',
),
),
),
'cid' => '0',
'comment' => '2',
'comment_count' => '0',
'field_image' => array(),
'field_tags' => array(
'und' => array(
0 => array(
'tid' => 'af738a46-f278-cf84-d94d-9e03879fd71e',
),
),
),
'language' => 'und',
'last_comment_name' => NULL,
'last_comment_uid' => '7cf875e6-dc15-4404-f190-5a7c3e91d14c',
'log' => '',
'promote' => '1',
'rdf_mapping' => array(
'field_image' => array(
'predicates' => array(
0 => 'og:image',
1 => 'rdfs:seeAlso',
),
'type' => 'rel',
),
'field_tags' => array(
'predicates' => array(
0 => 'dc:subject',
),
'type' => 'rel',
),
'rdftype' => array(
0 => 'sioc:Item',
1 => 'foaf:Document',
),
'title' => array(
'predicates' => array(
0 => 'dc:title',
),
),
'created' => array(
'predicates' => array(
0 => 'dc:date',
1 => 'dc:created',
),
'datatype' => 'xsd:dateTime',
'callback' => 'date_iso8601',
),
'changed' => array(
'predicates' => array(
0 => 'dc:modified',
),
'datatype' => 'xsd:dateTime',
'callback' => 'date_iso8601',
),
'body' => array(
'predicates' => array(
0 => 'content:encoded',
),
),
'uid' => array(
'predicates' => array(
0 => 'sioc:has_creator',
),
'type' => 'rel',
),
'name' => array(
'predicates' => array(
0 => 'foaf:name',
),
),
'comment_count' => array(
'predicates' => array(
0 => 'sioc:num_replies',
),
'datatype' => 'xsd:integer',
),
'last_activity' => array(
'predicates' => array(
0 => 'sioc:last_activity_date',
),
'datatype' => 'xsd:dateTime',
'callback' => 'date_iso8601',
),
),
'status' => '1',
'sticky' => '0',
'title' => 'Nunc sollicitudin',
'tnid' => '0',
'translate' => '0',
'type' => 'article',
'uid' => '7cf875e6-dc15-4404-f190-5a7c3e91d14c',
'uuid' => '5e3d8bbe-a1f2-f2d4-fdc0-71e6c23aa837',
);
return $entities;
}

View File

@@ -0,0 +1,15 @@
core = "7.x"
dependencies[] = "entity"
dependencies[] = "features"
dependencies[] = "uuid"
description = "Example feature mainly used for testing."
features[uuid_entities][] = "deploy_example_plan"
name = "UUID default entities example"
package = "Features"
; Information added by drupal.org packaging script on 2013-02-03
version = "7.x-1.0-alpha3+52-dev"
core = "7.x"
project = "uuid"
datestamp = "1359858369"

View File

@@ -0,0 +1,6 @@
<?php
/**
* @file
*/
// Drupal needs this blank file.

View File

@@ -0,0 +1,13 @@
name = UUID Path
description = Provides export functionality for url aliases.
core = 7.x
package = UUID
dependencies[] = uuid
; Information added by drupal.org packaging script on 2013-02-03
version = "7.x-1.0-alpha3+52-dev"
core = "7.x"
project = "uuid"
datestamp = "1359858369"

View File

@@ -0,0 +1,93 @@
<?php
/**
* @file
* UUID path module functions.
*/
/**
* Implements hook_entity_uuid_load().
*/
function uuid_path_entity_uuid_load(&$entities, $entity_type) {
_uuid_path_load_url_aliases($entities, $entity_type);
}
/**
* Implements hook_entity_uuid_save().
*/
function uuid_path_entity_uuid_save(&$entity, $entity_type) {
_uuid_path_save_url_aliases($entity, $entity_type);
}
/**
* Loads url aliases in the corresponding entity.
*/
function _uuid_path_load_url_aliases(&$entities, $entity_type) {
$info = entity_get_info($entity_type);
// we only care about entities with URLs.
if (!isset($info['uri callback'])) {
return;
}
$callback = $info['uri callback'];
foreach ($entities as $id => $entity) {
$path = $callback($entity);
$aliases = _uuid_path_url_alias_load($path['path']);
// Ignore local IDs.
foreach($aliases as &$alias) {
unset($alias->pid);
unset($alias->source);
}
$entities[$id]->url_alias = $aliases;
}
}
/**
* Saves the received url aliases.
*/
function _uuid_path_save_url_aliases(&$entity, $entity_type) {
$info = entity_get_info($entity_type);
// We only care when there is a url callback
if (!isset($info['uri callback'])) {
return FALSE;
}
$callback = $info['uri callback'];
$uri = $callback($entity);
$path = $uri['path'];
// Delete existing aliases.
path_delete(array('source' => $path));
// Continue if aliases are present.
if(empty($entity->url_alias)) {
return FALSE;
}
foreach ($entity->url_alias as $alias) {
$entry = (array) $alias;
$entry['source'] = $path;
path_save($entry);
}
}
/**
* Loads all aliases associated with a path.
*
* @param $path
* The source path to look up.
*
* @return
* Array of paths or NULL if none found.
*/
function _uuid_path_url_alias_load($path) {
return db_select('url_alias')
->condition('source', $path)
->fields('url_alias')
->execute()
->fetchAll(PDO::FETCH_OBJ);
}

View File

@@ -0,0 +1,12 @@
<?php
function _field_collection_resource_definition() {
if (module_exists('field_collection')) {
// We will allow uuid_services_services_resources_alter() to add the
// default UUID-related operations to this resource.
return array('field_collection_item' => array());
}
else {
return array();
}
}

View File

@@ -0,0 +1,15 @@
name = UUID Services
description = Provides integration with the Services module, like exposing a UUID entity resource.
core = 7.x
package = Services - resources
dependencies[] = services
dependencies[] = uuid
dependencies[] = entity
; Information added by drupal.org packaging script on 2013-02-03
version = "7.x-1.0-alpha3+52-dev"
core = "7.x"
project = "uuid"
datestamp = "1359858369"

View File

@@ -0,0 +1,228 @@
<?php
/**
* Implements hook_services_resources_alter().
*
* Alter all resources that support UUIDs, to make use this functionality when
* exposing them through Services.
*
* Since we are working with UUID enabled entities, the 'create' method is
* redundant. Instead, clients should do a PUT to '<entity_type>/<uuid>'. This
* will route through the 'update' method and create the entity if it doesn't
* exist. This is the most logical thing to do, since it's up to the client to
* generate and set the UUID on the entity.
*/
function uuid_services_services_resources_alter(&$resources, &$endpoint) {
foreach (entity_get_info() as $entity_type => $entity_info) {
if (isset($entity_info['uuid']) && $entity_info['uuid'] == TRUE && isset($resources[$entity_type])) {
unset($resources[$entity_type]['operations']['create']);
// Alter 'retrieve' method to use UUID enabled functions and arguments.
$resources[$entity_type]['operations']['retrieve']['help'] = t('Retrieve %label entities based on UUID.', array('%label' => $entity_info['label']));
$resources[$entity_type]['operations']['retrieve']['callback'] = '_uuid_services_entity_retrieve';
$resources[$entity_type]['operations']['retrieve']['access callback'] = '_uuid_services_entity_access';
$resources[$entity_type]['operations']['retrieve']['access arguments'] = array('view');
$resources[$entity_type]['operations']['retrieve']['access arguments append'] = TRUE;
$resources[$entity_type]['operations']['retrieve']['args'] = array(
// This argument isn't exposed in the service, only used internally..
array(
'name' => 'entity_type',
'description' => t('The entity type.'),
'type' => 'string',
'default value' => $entity_type,
'optional' => TRUE,
),
array(
'name' => 'uuid',
'description' => t('The %label UUID.', array('%label' => $entity_info['label'])),
'type' => 'text',
'source' => array('path' => 0),
),
);
// Alter 'update' method to use UUID enabled functions and arguments.
$resources[$entity_type]['operations']['update']['help'] = t('Update or create %label entities based on UUID. The payload must be formatted according to the <a href="!url">OData protocol</a>.', array('%label' => $entity_info['label'], '!url' => 'http://www.odata.org/developers/protocols'));
$resources[$entity_type]['operations']['update']['callback'] = '_uuid_services_entity_update';
$resources[$entity_type]['operations']['update']['access callback'] = '_uuid_services_entity_access';
$resources[$entity_type]['operations']['update']['access arguments'] = array('update');
$resources[$entity_type]['operations']['update']['access arguments append'] = TRUE;
$resources[$entity_type]['operations']['update']['args'] = array(
// This argument isn't exposed in the service, only used internally..
array(
'name' => 'entity_type',
'description' => t('The entity type.'),
'type' => 'string',
'default value' => $entity_type,
'optional' => TRUE,
),
array(
'name' => 'uuid',
'description' => t('The %label UUID.', array('%label' => $entity_info['label'])),
'type' => 'text',
'source' => array('path' => 0),
),
array(
'name' => 'entity',
'description' => t('The %label entity object.', array('%label' => $entity_info['label'])),
'type' => 'struct',
'source' => 'data',
),
);
// Alter 'delete' method to use UUID enabled functions and arguments.
$resources[$entity_type]['operations']['delete']['help'] = t('Delete %label entities based on UUID.', array('%label' => $entity_info['label']));
$resources[$entity_type]['operations']['delete']['callback'] = '_uuid_services_entity_delete';
$resources[$entity_type]['operations']['delete']['access callback'] = '_uuid_services_entity_access';
$resources[$entity_type]['operations']['delete']['access arguments'] = array('delete');
$resources[$entity_type]['operations']['delete']['access arguments append'] = TRUE;
$resources[$entity_type]['operations']['delete']['args'] = array(
// This argument isn't exposed in the service, only used internally..
array(
'name' => 'entity_type',
'description' => t('The entity type.'),
'type' => 'string',
'default value' => $entity_type,
'optional' => TRUE,
),
array(
'name' => 'uuid',
'description' => t('The %label UUID.', array('%label' => $entity_info['label'])),
'type' => 'text',
'source' => array('path' => 0),
),
);
}
}
}
/**
* Callback for the 'retrieve' method.
*
* @see entity_uuid_load()
*/
function _uuid_services_entity_retrieve($entity_type, $uuid) {
try {
$entities = entity_uuid_load($entity_type, array($uuid));
$entity = reset($entities);
return $entity;
}
catch (Exception $exception) {
watchdog_exception('uuid_services', $exception);
return services_error($exception, 406, $uuid);
}
}
/**
* Callback for the 'update' method.
*
* @see entity_uuid_save()
*/
function _uuid_services_entity_update($entity_type, $uuid, $entity) {
try {
$controller = entity_get_controller($entity_type);
if ($controller instanceof EntityAPIControllerInterface) {
$entity = $controller->create($entity);
}
else {
$entity = (object) $entity;
}
entity_uuid_save($entity_type, $entity);
return $entity;
}
catch (Exception $exception) {
watchdog_exception('uuid_services', $exception);
return services_error($exception, 406, $entity);
}
}
/**
* Callback for the 'delete' method.
*
* @see entity_uuid_delete()
*/
function _uuid_services_entity_delete($entity_type, $uuid) {
try {
$return = entity_uuid_delete($entity_type, array($uuid));
return $return;
}
catch (Exception $exception) {
watchdog_exception('uuid_services', $exception);
return services_error($exception, 406, $uuid);
}
}
/**
* Access callback.
*
* @param $op
* The operation we are trying to do on the entity. Can only be:
* - "view"
* - "update"
* - "delete"
* See 'uuid_services_services_resources_alter()' for an explanation why
* 'create' is missing.
* @param $args
* The arguments passed to the method. The keys are holding the following:
* 0. <entity_type>
* 1. <uuid>
* 2. <entity> (only available if $op == 'update')
*/
function _uuid_services_entity_access($op, $args) {
try {
// Fetch the information we have to work with.
$entity_type = $args[0];
// Load functions always deal with multiple entities. So does this lookup
// function. But in practice this will always only be one id.
$entity_ids = entity_get_id_by_uuid($entity_type, array($args[1]));
$entity = NULL;
if (!empty($args[2])) {
$entity = entity_create($entity_type, $args[2]);
// We have to make the entity local (i.e. only have local references), for
// access functions to work on it.
entity_make_entity_local($entity_type, $entity);
}
// Fetch the local entity if we've got an id.
elseif (!empty($entity_id)) {
$entities = entity_load($entity_type, $entity_ids);
$entity = reset($entities);
}
// If we've been routed to the 'update' method and the entity we are
// operating on doesn't exist yet, that should be reflected.
if ($op == 'update' && empty($entity_ids)) {
$op = 'create';
}
// Taxonomy and Comment module uses 'edit' instead of 'update'.
// Oh, how I love Drupal consistency.
if (($entity_type == 'taxonomy_term' || $entity_type == 'comment') && $op == 'update') {
$op = 'edit';
}
// The following code is taken from entity_access() with some extra logic
// to handle the case where an entity type is not defining an access
// callback. With this logic, it's important that all entity types that
// needs access control have an access callback defined.
if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
return $info[$entity_type]['access callback']($op, $entity, NULL, $entity_type);
}
return TRUE;
}
catch (Exception $exception) {
watchdog_exception('uuid_services', $exception);
return services_error($exception, 406, $entity_type);
}
}
/**
* Implements hook_services_resources().
*/
function uuid_services_services_resources() {
module_load_include('inc', 'uuid_services', 'resources/field_collection.resource');
$resources = array(
'#api_version' => 3002,
);
$resources += _field_collection_resource_definition();
return $resources;
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* @file
* uuid_services_example.features.inc
*/
/**
* Implements hook_ctools_plugin_api().
*/
function uuid_services_example_ctools_plugin_api() {
list($module, $api) = func_get_args();
if ($module == "services" && $api == "services") {
return array("version" => "3");
}
}

View File

@@ -0,0 +1,17 @@
core = "7.x"
dependencies[] = "rest_server"
dependencies[] = "services"
dependencies[] = "uuid"
dependencies[] = "uuid_services"
description = "Example feature of a UUID service. Works well with the Deploy Example feature as a client."
features[ctools][] = "services:services:3"
features[services_endpoint][] = "uuid_services_example"
name = "UUID Services Example"
package = "Features"
; Information added by drupal.org packaging script on 2013-02-03
version = "7.x-1.0-alpha3+52-dev"
core = "7.x"
project = "uuid"
datestamp = "1359858369"

View File

@@ -0,0 +1,7 @@
<?php
/**
* @file
* Code for the UUID Services Example feature.
*/
include_once('uuid_services_example.features.inc');

View File

@@ -0,0 +1,89 @@
<?php
/**
* @file
* uuid_services_example.services.inc
*/
/**
* Implements hook_default_services_endpoint().
*/
function uuid_services_example_default_services_endpoint() {
$export = array();
$endpoint = new stdClass;
$endpoint->disabled = FALSE; /* Edit this to true to make a default endpoint disabled initially */
$endpoint->api_version = 3;
$endpoint->name = 'uuid_services_example';
$endpoint->server = 'rest_server';
$endpoint->path = 'api';
$endpoint->authentication = array(
'services' => 'services',
);
$endpoint->server_settings = array(
'formatters' => array(
'json' => TRUE,
'bencode' => FALSE,
'jsonp' => FALSE,
'php' => FALSE,
'rss' => FALSE,
'xml' => FALSE,
'yaml' => FALSE,
),
'parsers' => array(
'application/json' => TRUE,
'application/x-www-form-urlencoded' => TRUE,
'multipart/form-data' => TRUE,
'application/vnd.php.serialized' => FALSE,
'application/x-yaml' => FALSE,
),
);
$endpoint->resources = array(
'comment' => array(
'operations' => array(
'update' => array(
'enabled' => 1,
),
),
),
'file' => array(
'operations' => array(
'update' => array(
'enabled' => 1,
),
),
),
'node' => array(
'operations' => array(
'update' => array(
'enabled' => 1,
),
),
),
'taxonomy_term' => array(
'operations' => array(
'update' => array(
'enabled' => 1,
),
),
),
'user' => array(
'operations' => array(
'update' => array(
'enabled' => 1,
),
),
'actions' => array(
'login' => array(
'enabled' => 1,
),
'logout' => array(
'enabled' => 1,
),
),
),
);
$endpoint->debug = 1;
$export['uuid_services_example'] = $endpoint;
return $export;
}