From c1b4d20447dd3e0b2785f87bc22aeb2480189b59 Mon Sep 17 00:00:00 2001 From: docwilmot Date: Tue, 31 Mar 2020 10:03:41 -0500 Subject: [PATCH] Initial commit and rename --- LICENSE.txt | 339 +++ ctools/content_types/entity_view.inc | 133 -- ctools/relationships/entity_property.inc | 153 -- entity.api.php | 467 ---- entity.features.inc | 204 -- entity.i18n.inc | 210 -- entity.info | 27 - entity.info.inc | 265 -- entity.install | 142 -- entity.module | 1575 ------------ entity.rules.inc | 118 - entity.test | 2123 ----------------- entity_token.info | 7 +- entity_token.module | 195 +- entity_token.tokens.inc | 26 +- includes/entity.controller.inc | 981 -------- includes/entity.inc | 423 ---- includes/entity.property.inc | 657 ----- includes/entity.ui.inc | 767 ------ includes/entity.wrapper.inc | 1273 ---------- modules/book.info.inc | 30 - modules/callbacks.inc | 1099 --------- modules/comment.info.inc | 172 -- modules/field.info.inc | 173 -- modules/locale.info.inc | 40 - modules/node.info.inc | 172 -- modules/poll.info.inc | 50 - modules/statistics.info.inc | 40 - modules/system.info.inc | 132 - modules/taxonomy.info.inc | 124 - modules/user.info.inc | 110 - tests/entity_feature.info | 7 - tests/entity_feature.module | 32 - tests/entity_test.info | 8 - tests/entity_test.install | 170 -- tests/entity_test.module | 287 --- tests/entity_test_i18n.info | 7 - tests/entity_test_i18n.module | 53 - tests/entity_token.test | 102 + tests/entity_token.tests.info | 5 + theme/entity.theme.css | 4 - theme/entity.theme.inc | 215 -- theme/entity.tpl.php | 48 - views/entity.views.inc | 723 ------ views/entity_views_example_query.php | 88 - .../entity_views_field_handler_helper.inc | 527 ---- .../entity_views_handler_area_entity.inc | 150 -- .../entity_views_handler_field_boolean.inc | 99 - .../entity_views_handler_field_date.inc | 99 - .../entity_views_handler_field_duration.inc | 132 - .../entity_views_handler_field_entity.inc | 207 -- .../entity_views_handler_field_field.inc | 105 - .../entity_views_handler_field_numeric.inc | 99 - .../entity_views_handler_field_options.inc | 120 - .../entity_views_handler_field_text.inc | 101 - .../entity_views_handler_field_uri.inc | 99 - .../entity_views_handler_relationship.inc | 51 - ...y_views_handler_relationship_by_bundle.inc | 117 - .../entity_views_plugin_row_entity_view.inc | 98 - 59 files changed, 657 insertions(+), 15323 deletions(-) create mode 100644 LICENSE.txt delete mode 100644 ctools/content_types/entity_view.inc delete mode 100644 ctools/relationships/entity_property.inc delete mode 100644 entity.api.php delete mode 100644 entity.features.inc delete mode 100644 entity.i18n.inc delete mode 100644 entity.info delete mode 100644 entity.info.inc delete mode 100644 entity.install delete mode 100644 entity.module delete mode 100644 entity.rules.inc delete mode 100644 entity.test delete mode 100644 includes/entity.controller.inc delete mode 100644 includes/entity.inc delete mode 100644 includes/entity.property.inc delete mode 100644 includes/entity.ui.inc delete mode 100644 includes/entity.wrapper.inc delete mode 100644 modules/book.info.inc delete mode 100644 modules/callbacks.inc delete mode 100644 modules/comment.info.inc delete mode 100644 modules/field.info.inc delete mode 100644 modules/locale.info.inc delete mode 100644 modules/node.info.inc delete mode 100644 modules/poll.info.inc delete mode 100644 modules/statistics.info.inc delete mode 100644 modules/system.info.inc delete mode 100644 modules/taxonomy.info.inc delete mode 100644 modules/user.info.inc delete mode 100644 tests/entity_feature.info delete mode 100644 tests/entity_feature.module delete mode 100644 tests/entity_test.info delete mode 100644 tests/entity_test.install delete mode 100644 tests/entity_test.module delete mode 100644 tests/entity_test_i18n.info delete mode 100644 tests/entity_test_i18n.module create mode 100644 tests/entity_token.test create mode 100644 tests/entity_token.tests.info delete mode 100644 theme/entity.theme.css delete mode 100644 theme/entity.theme.inc delete mode 100644 theme/entity.tpl.php delete mode 100644 views/entity.views.inc delete mode 100644 views/entity_views_example_query.php delete mode 100644 views/handlers/entity_views_field_handler_helper.inc delete mode 100644 views/handlers/entity_views_handler_area_entity.inc delete mode 100644 views/handlers/entity_views_handler_field_boolean.inc delete mode 100644 views/handlers/entity_views_handler_field_date.inc delete mode 100644 views/handlers/entity_views_handler_field_duration.inc delete mode 100644 views/handlers/entity_views_handler_field_entity.inc delete mode 100644 views/handlers/entity_views_handler_field_field.inc delete mode 100644 views/handlers/entity_views_handler_field_numeric.inc delete mode 100644 views/handlers/entity_views_handler_field_options.inc delete mode 100644 views/handlers/entity_views_handler_field_text.inc delete mode 100644 views/handlers/entity_views_handler_field_uri.inc delete mode 100644 views/handlers/entity_views_handler_relationship.inc delete mode 100644 views/handlers/entity_views_handler_relationship_by_bundle.inc delete mode 100644 views/plugins/entity_views_plugin_row_entity_view.inc diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE.txt @@ -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. + + + Copyright (C) + + 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. + + , 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. diff --git a/ctools/content_types/entity_view.inc b/ctools/content_types/entity_view.inc deleted file mode 100644 index e505633..0000000 --- a/ctools/content_types/entity_view.inc +++ /dev/null @@ -1,133 +0,0 @@ - t('Rendered entity'), - 'defaults' => array('view_mode' => 'full'), - 'content type' => 'entity_entity_view_content_type_info', -); - -/** - * Get the entity content type info. - */ -function entity_entity_view_content_type_info($entity_type) { - $types = entity_entity_view_content_type_content_types(); - if (isset($types[$entity_type])) { - return $types[$entity_type]; - } -} - -/** - * Implements hook_PLUGIN_content_type_content_types(). - * - * Rendered entity use entity types machine name as subtype name. - */ -function entity_entity_view_content_type_content_types() { - $types = array(); - $entities = entity_get_info(); - - foreach ($entities as $entity_type => $info) { - if (entity_type_supports($entity_type, 'view')) { - $types[$entity_type] = array( - 'title' => t('Rendered @entity_type', array('@entity_type' => $info['label'])), - 'category' => t('Entity'), - 'required context' => new ctools_context_required(t('Entity'), $entity_type), - ); - } - } - - return $types; -} - -/** - * Returns an edit form for a entity. - * - * Rendered entity use entity types machine name as subtype name. - * - * @see entity_entity_view_get_content_types() - */ -function entity_entity_view_content_type_edit_form($form, &$form_state) { - $conf = $form_state['conf']; - $entity_type = $form_state['subtype_name']; - $entity_info = entity_get_info($entity_type); - - $options = array(); - if (!empty($entity_info['view modes'])) { - foreach ($entity_info['view modes'] as $mode => $settings) { - $options[$mode] = $settings['label']; - } - } - - if (count($options) > 1) { - $form['view_mode'] = array( - '#type' => 'select', - '#options' => $options, - '#title' => t('View mode'), - '#default_value' => $conf['view_mode'], - ); - } - else { - $form['view_mode_info'] = array( - '#type' => 'item', - '#title' => t('View mode'), - '#description' => t('Only one view mode is available for this entity type.'), - '#markup' => $options ? current($options) : t('Default'), - ); - - $form['view_mode'] = array( - '#type' => 'value', - '#value' => $options ? key($options) : 'default', - ); - } - - return $form; -} - -/** - * Save selected view mode. - */ -function entity_entity_view_content_type_edit_form_submit(&$form, &$form_state) { - if (isset($form_state['values']['view_mode'])) { - $form_state['conf']['view_mode'] = $form_state['values']['view_mode']; - } -} - -/** - * Implements hook_PLUGIN_content_type_render(). - * - * Ctools requires us to return a block. - * - * @see ctools_content_render() - */ -function entity_entity_view_content_type_render($entity_type, $conf, $panel_args, $context) { - if ($context->empty) { - return; - } - $block = new stdClass(); - $block->module = 'entity'; - $block->delta = $entity_type . '-' . str_replace('-', '_', $conf['view_mode']); - - $entity_id = $context->argument; - $entity = entity_load_single($entity_type, $entity_id); - $block->content = entity_view($entity_type, array($entity_id => $entity), $conf['view_mode']); - return $block; -} - -/** - * Implements hook_PLUGIN_content_type_admin_title(). - * - * Returns the administrative title for a type. - */ -function entity_entity_view_content_type_admin_title($entity_type, $conf, $contexts) { - $entity_info = entity_get_info($entity_type); - $view_mode = $conf['view_mode']; - if (isset($entity_info['view modes'][$view_mode])) { - $view_mode = $entity_info['view modes'][$view_mode]['label']; - } - return t('Rendered @entity_type using view mode "@view_mode"', array('@entity_type' => $entity_info['label'], '@view_mode' => $view_mode)); -} diff --git a/ctools/relationships/entity_property.inc b/ctools/relationships/entity_property.inc deleted file mode 100644 index 6869146..0000000 --- a/ctools/relationships/entity_property.inc +++ /dev/null @@ -1,153 +0,0 @@ - t('Entity property or field (via Entity Metadata Wrapper)'), - 'description' => t('Creates any kind of context for entity properties and fields.'), - 'context' => 'entity_entity_property_context', - 'required context' => new ctools_context_required(t('Entity'), 'entity'), - 'edit form' => 'entity_entity_property_edit_form', - 'edit form validate' => 'entity_entity_property_edit_form_validate', - 'defaults' => array( - 'selector' => '', - 'target_context' => 'entity', - 'concatenator' => ',' - ), -); - -/** - * Return a new context based on an existing context. - */ -function entity_entity_property_context($context, $conf) { - $plugin = $conf['name']; - - // If unset it wants a generic, unfilled context, which is just NULL. - if (empty($context->data)) { - return ctools_context_create_empty(isset($conf['target_context']) ? $conf['target_context'] : 'entity', NULL); - } - - list($part1, $entity_type) = explode(':', $context->plugin); - - if (isset($context->data) && $entity = $context->data) { - // Apply the selector. - $wrapper = entity_metadata_wrapper($entity_type, $entity); - $parts = explode(':', $conf['selector']); - - try { - foreach ($parts as $part) { - if (!($wrapper instanceof EntityStructureWrapper || $wrapper instanceof EntityListWrapper)) { - $wrapper = NULL; - $value = NULL; - continue; - } - $wrapper = $wrapper->get($part); - } - } - catch (EntityMetadataWrapperException $e) { - $wrapper = NULL; - $value = NULL; - } - - if (isset($wrapper)) { - $value = $wrapper->value(); - } - - // Massage list values. - if ($wrapper instanceof EntityListWrapper) { - $value = $wrapper[0]->value(); - $argument = implode($conf['concatenator'], $wrapper->value(array('identifier' => TRUE))); - } - elseif ($wrapper instanceof EntityDrupalWrapper) { - $argument = $wrapper->getIdentifier(); - } - - // Bail out if we were unable to retrieve the value. - if (!isset($value)) { - return ctools_context_create_empty(isset($conf['target_context']) ? $conf['target_context'] : 'entity', NULL); - } - - $context = ctools_context_create($conf['target_context'], $value); - // Provide a suiting argument if context is used as argument. - if (isset($argument)) { - $context->argument = $argument; - } - return $context; - } -} - -function entity_entity_property_edit_form($form, &$form_state) { - $conf = $form_state['conf']; - - $form['selector'] = array( - '#type' => 'textfield', - '#title' => t('Data selector'), - '#description' => t('Any valid data selector, e.g. "title" to select a node\'s title, or "field_tags:1" to select the second tag.'), - '#default_value' => $conf['selector'], - '#required' => TRUE, - ); - $form['concatenator'] = array( - '#title' => t('Concatenator (if multiple)'), - '#type' => 'select', - '#options' => array(',' => ', (AND)', '+' => '+ (OR)'), - '#default_value' => $conf['concatenator'], - '#prefix' => '
', - '#suffix' => '
', - '#description' => t("When the resulting value is multiple valued and the context is passed on to a view as argument, the value can be concatenated in the form of 1+2+3 (for OR) or 1,2,3 (for AND)."), - ); - return $form; -} - -function entity_entity_property_edit_form_validate($form, &$form_state) { - $context_key = $form_state['values']['context']; - $context = $form_state['contexts'][$context_key]; - $entity_type = $context->type[2]; - - try { - $all_properties = entity_get_all_property_info($entity_type); - $wrapper = entity_metadata_wrapper($entity_type, NULL, array('property info' => $all_properties)); - $parts = explode(':', $form_state['values']['selector']); - foreach ($parts as $part) { - if (!($wrapper instanceof EntityStructureWrapper || $wrapper instanceof EntityListWrapper)) { - form_set_error('selector', t('Unable to apply the data selector part %key.'. array('%key' => $part))); - continue; - } - $wrapper = $wrapper->get($part); - } - $type = entity_entity_property_map_data_type($wrapper->type()); - $form_state['conf']['target_context'] = $type; - } - catch (EntityMetadataWrapperException $e) { - form_set_error('selector', t('Unable to apply the data selector on entity type %type. @reason', array('@reason' => $e->getMessage(), '%type' => $entity_type))); - } - - // Auto-generate a sane identifier. - if ($form_state['values']['identifier'] == $form['identifier']['#default_value']) { - $form_state['values']['identifier'] = 'Entity property ' . $entity_type . ':' . check_plain($form_state['values']['selector']); - } -} - -/** - * Maps an entity-property data type to ctools types. - */ -function entity_entity_property_map_data_type($type) { - // Remove list<> wrappers from types. - while ($item_type = entity_property_list_extract_type($type)) { - $type = $item_type; - } - // Massage data type of entites to c - if (entity_get_info($type)) { - $type = "entity:$type"; - } - if ($type == 'text') { - $type = 'string'; - } - return $type; -} diff --git a/entity.api.php b/entity.api.php deleted file mode 100644 index 3f77752..0000000 --- a/entity.api.php +++ /dev/null @@ -1,467 +0,0 @@ - array( - 'label' => t('Test Entity'), - 'entity class' => 'Entity', - 'controller class' => 'EntityAPIController', - 'base table' => 'entity_test', - 'module' => 'entity_test', - 'fieldable' => TRUE, - 'entity keys' => array( - 'id' => 'pid', - 'name' => 'name', - 'bundle' => 'type', - ), - 'bundles' => array(), - ), - ); - foreach (entity_test_get_types() as $name => $info) { - $return['entity_test']['bundles'][$name] = array( - 'label' => $info['label'], - ); - } - return $return; -} - -/** - * Provide additional metadata for entities. - * - * This is a placeholder for describing further keys for hook_entity_info(), - * which are introduced the entity API in order to support any entity type; e.g. - * to make entity_save(), entity_create(), entity_view() and others work. - * See entity_crud_hook_entity_info() for the documentation of additional keys - * for hook_entity_info() as introduced by the entity API for providing new - * entity types with the entity CRUD API. - * - * Additional keys are: - * - plural label: (optional) The human-readable, plural name of the entity - * type. As 'label' it should start capitalized. - * - description: (optional) A human-readable description of the entity type. - * - access callback: (optional) Specify a callback that returns access - * permissions for the operations 'create', 'update', 'delete' and 'view'. - * The callback gets optionally the entity and the user account to check for - * passed. See entity_access() for more details on the arguments and - * entity_metadata_no_hook_node_access() for an example. - * - creation callback: (optional) A callback that creates a new instance of - * this entity type. See entity_metadata_create_node() for an example. - * - save callback: (optional) A callback that permanently saves an entity of - * this type. - * - deletion callback: (optional) A callback that permanently deletes an - * entity of this type. - * - revision deletion callback: (optional) A callback that deletes a revision - * of the entity. - * - view callback: (optional) A callback to render a list of entities. - * See entity_metadata_view_node() as example. - * - form callback: (optional) A callback that returns a fully built edit form - * for the entity type. - * - token type: (optional) A type name to use for token replacements. Set it - * to FALSE if there aren't any token replacements for this entity type. - * - configuration: (optional) A boolean value that specifies whether the entity - * type should be considered as configuration. Modules working with entities - * may use this value to decide whether they should deal with a certain entity - * type. Defaults to TRUE to for entity types that are exportable, else to - * FALSE. - * - * @see hook_entity_info() - * @see entity_crud_hook_entity_info() - * @see entity_access() - * @see entity_create() - * @see entity_save() - * @see entity_delete() - * @see entity_view() - * @see entity_form() - */ -function entity_metadata_hook_entity_info() { - return array( - 'node' => array( - 'label' => t('Node'), - 'access callback' => 'entity_metadata_no_hook_node_access', - // ... - ), - ); -} - -/** - * Allow modules to define metadata about entity properties. - * - * Modules providing properties for any entities defined in hook_entity_info() - * can implement this hook to provide metadata about this properties. - * For making use of the metadata have a look at the provided wrappers returned - * by entity_metadata_wrapper(). - * For providing property information for fields see entity_hook_field_info(). - * - * @return - * An array whose keys are entity type names and whose values are arrays - * containing the keys: - * - properties: The array describing all properties for this entity. Entries - * are keyed by the property name and contain an array of metadata for each - * property. The name may only contain alphanumeric lowercase characters - * and underscores. Known keys are: - * - label: A human readable, translated label for the property. - * - description: (optional) A human readable, translated description for - * the property. - * - type: The data type of the property. To make the property actually - * useful it is important to map your properties to one of the known data - * types, which currently are: - * - text: Any text. - * - token: A string containing only lowercase letters, numbers, and - * underscores starting with a letter; e.g. this type is useful for - * machine readable names. - * - integer: A usual PHP integer value. - * - decimal: A PHP float or integer. - * - date: A full date and time, as timestamp. - * - duration: A duration as number of seconds. - * - boolean: A usual PHP boolean value. - * - uri: An absolute URI or URL. - * - entities - You may use the type of each entity known by - * hook_entity_info(), e.g. 'node' or 'user'. Internally entities are - * represented by their identifieres. In case of single-valued - * properties getter callbacks may return full entity objects as well, - * while a value of FALSE is interpreted like a NULL value as "property - * is not set". - * - entity: A special type to be used generically for entities where the - * entity type is not known beforehand. The entity has to be - * represented using an EntityMetadataWrapper. - * - struct: This as well as any else not known type may be used for - * supporting arbitrary data structures. For that additional metadata - * has to be specified with the 'property info' key. New type names - * have to be properly prefixed with the module name. - * - list: A list of values, represented as numerically indexed array. - * The list notation may be used to specify the type of the - * contained items, where TYPE may be any valid type expression. - * - bundle: (optional) If the property is an entity, you may specify the - * bundle of the referenced entity. - * - options list: (optional) A callback that returns a list of possible - * values for the property. The callback has to return an array as - * used by hook_options_list(). - * Note that it is possible to return a different set of options depending - * whether they are used in read or in write context. See - * EntityMetadataWrapper::optionsList() for more details on that. - * - getter callback: (optional) A callback used to retrieve the value of - * the property. Defaults to entity_property_verbatim_get(). - * It is important that your data is represented, as documented for your - * data type, e.g. a date has to be a timestamp. Thus if necessary, the - * getter callback has to do the necessary conversion. In case of an empty - * or not set value, the callback has to return NULL. - * - setter callback: (optional) A callback used to set the value of the - * property. In many cases entity_property_verbatim_set() can be used. - * - validation callback: (optional) A callback that returns whether the - * passed data value is valid for the property. May be used to implement - * additional validation checks, such as to ensure the value is a valid - * mail address. - * - access callback: (optional) An access callback to allow for checking - * 'view' and 'edit' access for the described property. If no callback - * is specified, a 'setter permission' may be specified instead. - * - setter permission: (optional) A permission that describes whether - * a user has permission to set ('edit') this property. This permission - * is only be taken into account, if no 'access callback' is given. - * - schema field: (optional) In case the property is directly based upon - * a field specified in the entity's hook_schema(), the name of the field. - * - queryable: (optional) Whether a property is queryable with - * EntityFieldQuery. Defaults to TRUE if a 'schema field' is specified, or - * if the deprecated 'query callback' is set to - * 'entity_metadata_field_query'. Otherwise it defaults to FALSE. - * - query callback: (deprecated) A callback for querying for entities - * having the given property value. See entity_property_query(). - * Generally, properties should be queryable via EntityFieldQuery. If - * that is the case, just set 'queryable' to TRUE. - * - required: (optional) Whether this property is required for the creation - * of a new instance of its entity. See - * entity_property_values_create_entity(). - * - field: (optional) A boolean indicating whether a property is stemming - * from a field. - * - computed: (optional) A boolean indicating whether a property is - * computed, i.e. the property value is not stored or loaded by the - * entity's controller but determined on the fly by the getter callback. - * Defaults to FALSE. - * - entity views field: (optional) If enabled, the property is - * automatically exposed as views field available to all views query - * backends listing this entity-type. As the property value will always be - * generated from a loaded entity object, this is particularly useful for - * 'computed' properties. Defaults to FALSE. - * - sanitized: (optional) For textual properties only, whether the text is - * already sanitized. In this case you might want to also specify a raw - * getter callback. Defaults to FALSE. - * - sanitize: (optional) For textual properties, that are not sanitized - * yet, specify a function for sanitizing the value. Defaults to - * check_plain(). - * - raw getter callback: (optional) For sanitized textual properties, a - * separate callback which can be used to retrieve the raw, unprocessed - * value. - * - clear: (optional) An array of property names, of which the cache should - * be cleared too once this property is updated. As a rule of thumb any - * duplicated properties should be avoided though. - * - property info: (optional) An array of info for an arbitrary data - * structure together with any else not defined type, see data type - * 'struct'. Specify metadata in the same way as defined for this hook. - * - property info alter: (optional) A callback for altering the property - * info before it is used by the metadata wrappers. - * - property defaults: (optional) An array of property info defaults for - * each property derived of the wrapped data item (e.g. an entity). - * Applied by the metadata wrappers. - * - auto creation: (optional) Properties of type 'struct' may specify - * this callback which is used to automatically create the data structure - * (e.g. an array) if necessary. This is necessary in order to support - * setting a property of a not yet initialized data structure. - * See entity_metadata_field_file_callback() for an example. - * - translatable: (optional) Whether the property is translatable, defaults - * to FALSE. - * - entity token: (optional) If Entity tokens module is enabled, the - * module provides a token for the property if one does not exist yet. - * Specify FALSE to disable this functionality for the property. - * - bundles: An array keyed by bundle name containing further metadata - * related to the bundles only. This array may contain the key 'properties' - * with an array of info about the bundle specific properties, structured in - * the same way as the entity properties array. - * - * @see hook_entity_property_info_alter() - * @see entity_get_property_info() - * @see entity_metadata_wrapper() - */ -function hook_entity_property_info() { - $info = array(); - $properties = &$info['node']['properties']; - - $properties['nid'] = array( - 'label' => t("Content ID"), - 'type' => 'integer', - 'description' => t("The unique content ID."), - ); - return $info; -} - -/** - * Allow modules to alter metadata about entity properties. - * - * @see hook_entity_property_info() - */ -function hook_entity_property_info_alter(&$info) { - $properties = &$info['node']['bundles']['poll']['properties']; - - $properties['poll-votes'] = array( - 'label' => t("Poll votes"), - 'description' => t("The number of votes that have been cast on a poll node."), - 'type' => 'integer', - 'getter callback' => 'entity_property_poll_node_get_properties', - ); -} - -/** - * Provide entity property information for fields. - * - * This is a placeholder for describing further keys for hook_field_info(), - * which are introduced by the entity API. - * - * For providing entity property info for fields each field type may specify a - * property type to map to using the key 'property_type'. With that info in - * place useful defaults are generated, which suffice for a lot of field - * types. - * However it is possible to specify further callbacks that may alter the - * generated property info. To do so use the key 'property_callbacks' and set - * it to an array of function names. Apart from that any property info provided - * for a field instance using the key 'property info' is added in too. - * - * @see entity_field_info_alter() - * @see entity_metadata_field_text_property_callback() - */ -function entity_hook_field_info() { - return array( - 'text' => array( - 'label' => t('Text'), - 'property_type' => 'text', - // ... - ), - ); -} - -/** - * Alter the handlers used by the data selection tables provided by this module. - * - * @param array $field_handlers - * An array of the field handler classes to use for specific types. The keys - * are the types, mapped to their respective classes. Contained types are: - * - All primitive types known by the entity API (see - * hook_entity_property_info()). - * - options: Special type for fields having an options list. - * - field: Special type for Field API fields. - * - entity: Special type for entity-valued fields. - * - relationship: Views relationship handler to use for relationships. - * Values for all specific entity types can be additionally added. - * - * @see entity_views_field_definition() - * @see entity_views_get_field_handlers() - */ -function hook_entity_views_field_handlers_alter(array &$field_handlers) { - $field_handlers['duration'] = 'example_duration_handler'; - $field_handlers['node'] = 'example_node_handler'; -} - -/** - * @} End of "addtogroup hooks". - */ diff --git a/entity.features.inc b/entity.features.inc deleted file mode 100644 index 0ec3e61..0000000 --- a/entity.features.inc +++ /dev/null @@ -1,204 +0,0 @@ - 'EntityDefaultFeaturesController'); - $static[$type] = $info['features controller class'] ? new $info['features controller class']($type) : FALSE; - } - return $static[$type]; -} - -/** - * Default controller handling features integration. - */ -class EntityDefaultFeaturesController { - - protected $type, $info; - - public function __construct($type) { - $this->type = $type; - $this->info = entity_get_info($type); - $this->info['entity keys'] += array('module' => 'module', 'status' => 'status'); - $this->statusKey = $this->info['entity keys']['status']; - $this->moduleKey = $this->info['entity keys']['module']; - if (!empty($this->info['bundle of'])) { - $entity_info = entity_get_info($this->info['bundle of']); - $this->bundleKey = $entity_info['bundle keys']['bundle']; - } - } - - /** - * Defines the result for hook_features_api(). - */ - public function api() { - return array( - // The entity type has to be the features component name. - $this->type => array( - 'name' => $this->info['label'], - 'feature_source' => TRUE, - 'default_hook' => isset($this->info['export']['default hook']) ? $this->info['export']['default hook'] : 'default_' . $this->type, - // Use the provided component callbacks making use of the controller. - 'base' => 'entity', - 'file' => drupal_get_path('module', 'entity') . '/entity.features.inc', - ), - ); - } - - /** - * Generates the result for hook_features_export_options(). - */ - public function export_options() { - $options = array(); - foreach (entity_load_multiple_by_name($this->type, FALSE) as $name => $entity) { - $options[$name] = entity_label($this->type, $entity); - } - return $options; - } - - /** - * Generates the result for hook_features_export(). - */ - public function export($data, &$export, $module_name = '') { - $pipe = array(); - foreach (entity_load_multiple_by_name($this->type, $data) as $name => $entity) { - // If this entity is provided by a different module, add it as dependency. - if (($entity->{$this->statusKey} & ENTITY_IN_CODE) && $entity->{$this->moduleKey} != $module_name) { - $module = $entity->{$this->moduleKey}; - $export['dependencies'][$module] = $module; - } - // Otherwise export the entity. - else { - $export['features'][$this->type][$name] = $name; - - // If this is a bundle of a fieldable entity, add its fields to the pipe. - if (!empty($this->info['bundle of'])) { - $fields = field_info_instances($this->info['bundle of'], $entity->{$this->bundleKey}); - foreach ($fields as $name => $field) { - $pipe['field'][] = "{$field['entity_type']}-{$field['bundle']}-{$field['field_name']}"; - $pipe['field_instance'][] = "{$field['entity_type']}-{$field['bundle']}-{$field['field_name']}"; - } - } - } - } - // Add the module providing the entity type as dependency. - if ($data && !empty($this->info['module'])) { - $export['dependencies'][$this->info['module']] = $this->info['module']; - // In case entity is not already an indirect dependency, add it. - // We can do so without causing redundant dependencies because, - // if entity is an indirect dependency, Features will filter it out. - $export['dependencies']['entity'] = 'entity'; - } - return $pipe; - } - - /** - * Generates the result for hook_features_export_render(). - */ - function export_render($module, $data, $export = NULL) { - $output = array(); - $output[] = ' $items = array();'; - foreach (entity_load_multiple_by_name($this->type, $data) as $name => $entity) { - $export = " \$items['$name'] = entity_import('{$this->type}', '"; - // Make sure to escape the characters \ and '. - $export .= addcslashes(entity_export($this->type, $entity, ' '), '\\\''); - $export .= "');"; - $output[] = $export; - } - $output[] = ' return $items;'; - $output = implode("\n", $output); - - $hook = isset($this->info['export']['default hook']) ? $this->info['export']['default hook'] : 'default_' . $this->type; - return array($hook => $output); - } - - /** - * Generates the result for hook_features_revert(). - */ - function revert($module = NULL) { - if ($defaults = features_get_default($this->type, $module)) { - entity_delete_multiple($this->type, array_keys($defaults)); - } - } -} - -/** - * Implements of hook_features_api(). - */ -function entity_features_api() { - $items = array(); - foreach (entity_crud_get_info() as $type => $info) { - if (!empty($info['exportable']) && $controller = entity_features_get_controller($type)) { - $items += $controller->api(); - } - } - return $items; -} - -/** - * Implements hook_features_export_options(). - * - * Features component callback. - */ -function entity_features_export_options($a1, $a2 = NULL) { - // Due to a change in the Features API the first parameter might be a feature - // object or an entity type, depending on the Features version. This check is - // for backwards compatibility. - $entity_type = is_string($a1) ? $a1 : $a2; - return entity_features_get_controller($entity_type)->export_options(); -} - -/** - * Implements hook_features_export(). - * - * Features component callback. - */ -function entity_features_export($data, &$export, $module_name = '', $entity_type) { - return entity_features_get_controller($entity_type)->export($data, $export, $module_name); -} - -/** - * Implements hook_features_export_render(). - * - * Features component callback. - */ -function entity_features_export_render($module, $data, $export = NULL, $entity_type) { - return entity_features_get_controller($entity_type)->export_render($module, $data, $export); -} - -/** - * Implements hook_features_revert(). - * - * Features component callback. - */ -function entity_features_revert($module = NULL, $entity_type) { - return entity_features_get_controller($entity_type)->revert($module); -} - -/** - * Implements hook_features_post_restore(). - * - * Rebuild all defaults when a features rebuild is triggered - even the ones not - * handled by features itself. - */ -function entity_features_post_restore($op, $items = array()) { - if ($op == 'rebuild') { - // Use features rebuild to rebuild the features independent exports too. - entity_defaults_rebuild(); - } -} diff --git a/entity.i18n.inc b/entity.i18n.inc deleted file mode 100644 index 8347eaa..0000000 --- a/entity.i18n.inc +++ /dev/null @@ -1,210 +0,0 @@ - $info) { - entity_i18n_controller($entity_type); - } - return array_filter($static); - } - - if (!isset($static[$type])) { - $info = entity_get_info($type); - // Do not activate it by default. Modules have to explicitly enable it by - // specifying EntityDefaultI18nStringController or their customization. - $class = isset($info['i18n controller class']) ? $info['i18n controller class'] : FALSE; - $static[$type] = $class ? new $class($type, $info) : FALSE; - } - - return $static[$type]; -} - -/** - * Implements hook_i18n_string_info(). - */ -function entity_i18n_string_info() { - $groups = array(); - foreach (entity_i18n_controller() as $entity_type => $controller) { - $groups += $controller->hook_string_info(); - } - return $groups; -} - -/** - * Implements hook_i18n_object_info(). - */ -function entity_i18n_object_info() { - $info = array(); - foreach (entity_i18n_controller() as $entity_type => $controller) { - $info += $controller->hook_object_info(); - } - return $info; -} - -/** - * Implements hook_i18n_string_objects(). - */ -function entity_i18n_string_objects($type) { - if ($controller = entity_i18n_controller($type)) { - return $controller->hook_string_objects(); - } -} - -/** - * Default controller handling i18n integration. - * - * Implements i18n string translation for all non-field properties marked as - * 'translatable' and having the flag 'i18n string' set. This translation - * approach fits in particular for translating configuration, i.e. exportable - * entities. - * - * Requirements for the default controller: - * - The entity type providing module must be specified using the 'module' key - * in hook_entity_info(). - * - An 'entity class' derived from the provided class 'Entity' must be used. - * - Properties must be declared as 'translatable' and the 'i18n string' flag - * must be set to TRUE using hook_entity_property_info(). - * - i18n must be notified about changes manually by calling - * i18n_string_object_update(), i18n_string_object_remove() and - * i18n_string_update_context(). Ideally, this is done in a small integration - * module depending on the entity API and i18n_string. Look at the provided - * testing module "entity_test_i18n" for an example. - * - If the entity API admin UI is used, the "translate" tab will be - * automatically enabled and linked from the UI. - * - There are helpers for getting translated values which work regardless - * whether the i18n_string module is enabled, i.e. entity_i18n_string() - * and Entity::getTranslation(). - * - * Current limitations: - * - Translatable property values cannot be updated via the metadata wrapper, - * however reading works fine. See Entity::getTranslation(). - */ -class EntityDefaultI18nStringController { - - protected $entityType, $entityInfo; - - /** - * The i18n textgroup we are using. - */ - protected $textgroup; - - public function __construct($type) { - $this->entityType = $type; - $this->entityInfo = entity_get_info($type); - // By default we go with the module name as textgroup. - $this->textgroup = $this->entityInfo['module']; - } - - /** - * Implements hook_i18n_string_info() via entity_i18n_string_info(). - */ - public function hook_string_info() { - $list = system_list('module_enabled'); - $info = $list[$this->textgroup]->info; - - $groups[$this->textgroup] = array( - 'title' => $info['name'], - 'description' => !empty($info['description']) ? $info['description'] : NULL, - 'format' => FALSE, - 'list' => TRUE, - ); - return $groups; - } - - /** - * Implements hook_i18n_object_info() via entity_i18n_object_info(). - * - * Go with the same default values as the admin UI as far as possible. - */ - public function hook_object_info() { - $wildcard = $this->menuWildcard(); - $id_key = !empty($this->entityInfo['entity keys']['name']) ? $this->entityInfo['entity keys']['name'] : $this->entityInfo['entity keys']['id']; - - $info[$this->entityType] = array( - // Generic object title. - 'title' => $this->entityInfo['label'], - // The object key field. - 'key' => $id_key, - // Placeholders for automatic paths. - 'placeholders' => array( - $wildcard => $id_key, - ), - - // Properties for string translation. - 'string translation' => array( - // Text group that will handle this object's strings. - 'textgroup' => $this->textgroup, - // Object type property for string translation. - 'type' => $this->entityType, - // Translatable properties of these objects. - 'properties' => $this->translatableProperties(), - ), - ); - - // Integrate the translate tab into the admin-UI if enabled. - if ($base_path = $this->menuBasePath()) { - $info[$this->entityType] += array( - // To produce edit links automatically. - 'edit path' => $base_path . '/manage/' . $wildcard, - // Auto-generate translate tab. - 'translate tab' => $base_path . '/manage/' . $wildcard . '/translate', - ); - $info[$this->entityType]['string translation'] += array( - // Path to translate strings to every language. - 'translate path' => $base_path . '/manage/' . $wildcard . '/translate/%i18n_language', - ); - } - return $info; - } - - /** - * Defines the menu base path used by self::hook_object_info(). - */ - protected function menuBasePath() { - return !empty($this->entityInfo['admin ui']['path']) ? $this->entityInfo['admin ui']['path'] : FALSE; - } - - /** - * Defines the menu wildcard used by self::hook_object_info(). - */ - protected function menuWildcard() { - return isset($this->entityInfo['admin ui']['menu wildcard']) ? $this->entityInfo['admin ui']['menu wildcard'] : '%entity_object'; - } - - /** - * Defines translatable properties used by self::hook_object_info(). - */ - protected function translatableProperties() { - $list = array(); - foreach (entity_get_all_property_info($this->entityType) as $name => $info) { - if (!empty($info['translatable']) && !empty($info['i18n string'])) { - $list[$name] = array( - 'title' => $info['label'], - ); - } - } - return $list; - } - - /** - * Implements hook_i18n_string_objects() via entity_i18n_string_objects(). - */ - public function hook_string_objects() { - return entity_load_multiple_by_name($this->entityType, FALSE); - } -} diff --git a/entity.info b/entity.info deleted file mode 100644 index 0cae8cb..0000000 --- a/entity.info +++ /dev/null @@ -1,27 +0,0 @@ -name = Entity API -description = Enables modules to work with any entity type and to provide entities. -core = 7.x -files[] = entity.features.inc -files[] = entity.i18n.inc -files[] = entity.info.inc -files[] = entity.rules.inc -files[] = entity.test -files[] = includes/entity.inc -files[] = includes/entity.controller.inc -files[] = includes/entity.ui.inc -files[] = includes/entity.wrapper.inc -files[] = views/entity.views.inc -files[] = views/handlers/entity_views_field_handler_helper.inc -files[] = views/handlers/entity_views_handler_area_entity.inc -files[] = views/handlers/entity_views_handler_field_boolean.inc -files[] = views/handlers/entity_views_handler_field_date.inc -files[] = views/handlers/entity_views_handler_field_duration.inc -files[] = views/handlers/entity_views_handler_field_entity.inc -files[] = views/handlers/entity_views_handler_field_field.inc -files[] = views/handlers/entity_views_handler_field_numeric.inc -files[] = views/handlers/entity_views_handler_field_options.inc -files[] = views/handlers/entity_views_handler_field_text.inc -files[] = views/handlers/entity_views_handler_field_uri.inc -files[] = views/handlers/entity_views_handler_relationship_by_bundle.inc -files[] = views/handlers/entity_views_handler_relationship.inc -files[] = views/plugins/entity_views_plugin_row_entity_view.inc \ No newline at end of file diff --git a/entity.info.inc b/entity.info.inc deleted file mode 100644 index 30da655..0000000 --- a/entity.info.inc +++ /dev/null @@ -1,265 +0,0 @@ - $info) { - // Automatically enable the controller only if the module does not implement - // the hook itself. - if (!isset($info['metadata controller class']) && !empty($info['base table']) && (!isset($info['module']) || !module_hook($info['module'], 'entity_property_info'))) { - $info['metadata controller class'] = 'EntityDefaultMetadataController'; - } - if (!empty($info['metadata controller class'])) { - $controller = new $info['metadata controller class']($type); - $items += $controller->entityPropertyInfo(); - } - } - // Add in info for all core entities. - foreach (_entity_metadata_core_modules() as $module) { - module_load_include('inc', 'entity', "modules/$module.info"); - if (function_exists($function = "entity_metadata_{$module}_entity_property_info")) { - if ($return = $function()) { - $items = array_merge_recursive($items, $return); - } - } - } - return $items; -} - -/** - * Implements hook_entity_property_info_alter(). - */ -function entity_entity_property_info_alter(&$entity_info) { - // Add in info for all core entities. - foreach (_entity_metadata_core_modules() as $module) { - module_load_include('inc', 'entity', "modules/$module.info"); - if (function_exists($function = "entity_metadata_{$module}_entity_property_info_alter")) { - $function($entity_info); - } - } -} - -function _entity_metadata_core_modules() { - return array_filter(array('book', 'comment', 'field', 'locale', 'node', 'taxonomy', 'user', 'system', 'statistics'), 'module_exists'); -} - -/** - * Default controller for generating some basic metadata for CRUD entity types. - */ -class EntityDefaultMetadataController { - - protected $type, $info; - - public function __construct($type) { - $this->type = $type; - $this->info = entity_get_info($type); - } - - public function entityPropertyInfo() { - $entity_label = drupal_strtolower($this->info['label']); - - // Provide defaults based on the schema. - $info['properties'] = $this->convertSchema(); - foreach ($info['properties'] as $name => &$property) { - // Add a description. - $property['description'] = t('@entity "@property" property.', array('@entity' => drupal_ucfirst($entity_label), '@property' => $name)); - } - - // Set better metadata for known entity keys. - $id_key = $this->info['entity keys']['id']; - - if (!empty($this->info['entity keys']['name']) && $key = $this->info['entity keys']['name']) { - $info['properties'][$key]['type'] = 'token'; - $info['properties'][$key]['label'] = t('Machine-readable name'); - $info['properties'][$key]['description'] = t('The machine-readable name identifying this @entity.', array('@entity' => $entity_label)); - $info['properties'][$id_key]['label'] = t('Internal, numeric @entity ID', array('@entity' => $entity_label)); - $info['properties'][$id_key]['description'] = t('The ID used to identify this @entity internally.', array('@entity' => $entity_label)); - } - else { - $info['properties'][$id_key]['label'] = t('@entity ID', array('@entity' => drupal_ucfirst($entity_label))); - $info['properties'][$id_key]['description'] = t('The unique ID of the @entity.', array('@entity' => $entity_label)); - } - // Care for the bundle. - if (!empty($this->info['entity keys']['bundle']) && $key = $this->info['entity keys']['bundle']) { - $info['properties'][$key]['type'] = 'token'; - $info['properties'][$key]['options list'] = array(get_class($this), 'bundleOptionsList'); - } - // Care for the label. - if (!empty($this->info['entity keys']['label']) && $key = $this->info['entity keys']['label']) { - $info['properties'][$key]['label'] = t('Label'); - $info['properties'][$key]['description'] = t('The human readable label.'); - } - - // Add a computed property for the entity URL and expose it to views. - if (empty($info['properties']['url']) && !empty($this->info['uri callback'])) { - $info['properties']['url'] = array( - 'label' => t('URL'), - 'description' => t('The URL of the entity.'), - 'getter callback' => 'entity_metadata_entity_get_properties', - 'type' => 'uri', - 'computed' => TRUE, - 'entity views field' => TRUE, - ); - } - - return array($this->type => $info); - } - - /** - * A options list callback returning all bundles for an entity type. - */ - public static function bundleOptionsList($name, $info) { - if (!empty($info['parent']) && $type = $info['parent']) { - $entity_info = $info['parent']->entityInfo(); - $options = array(); - foreach ($entity_info['bundles'] as $name => $bundle_info) { - $options[$name] = $bundle_info['label']; - } - return $options; - } - } - - /** - * Return a set of properties for an entity based on the schema definition - */ - protected function convertSchema() { - return entity_metadata_convert_schema($this->info['base table']); - } -} - -/** - * Converts the schema information available for the given table to property info. - * - * @param $table - * The name of the table as used in hook_schema(). - * @return - * An array of property info as suiting for hook_entity_property_info(). - */ -function entity_metadata_convert_schema($table) { - $schema = drupal_get_schema($table); - $properties = array(); - foreach ($schema['fields'] as $name => $info) { - if ($type = _entity_metadata_convert_schema_type($info['type'])) { - $properties[$name] = array( - 'type' => $type, - 'label' => drupal_ucfirst($name), - 'schema field' => $name, - // As we cannot know about any setter access, leave out the setter - // callback. For getting usually no further access callback is needed. - ); - if ($info['type'] == 'serial') { - $properties[$name]['validation callback'] = 'entity_metadata_validate_integer_positive'; - } - } - } - return $properties; -} - -function _entity_metadata_convert_schema_type($type) { - switch ($type) { - case 'int': - case 'serial': - case 'date': - return 'integer'; - case 'float': - case 'numeric': - return 'decimal'; - case 'char': - case 'varchar': - case 'text': - return 'text'; - } -} - -/** - * Interface for extra fields controller. - * - * Note: Displays extra fields exposed by this controller are rendered by - * default by the EntityAPIController. - */ -interface EntityExtraFieldsControllerInterface { - - /** - * Returns extra fields for this entity type. - * - * @see hook_field_extra_fields(). - */ - public function fieldExtraFields(); -} - -/** - * Default controller for generating extra fields based on property metadata. - * - * By default a display extra field for each property not being a field, ID or - * bundle is generated. - */ -class EntityDefaultExtraFieldsController implements EntityExtraFieldsControllerInterface { - - /** - * @var string - */ - protected $entityType; - - /** - * @var array - */ - protected $entityInfo; - - /** - * Constructor. - */ - public function __construct($type) { - $this->entityType = $type; - $this->entityInfo = entity_get_info($type); - $this->propertyInfo = entity_get_property_info($type); - } - - /** - * Implements EntityExtraFieldsControllerInterface::fieldExtraFields(). - */ - public function fieldExtraFields() { - $extra = array(); - foreach ($this->propertyInfo['properties'] as $name => $property_info) { - // Skip adding the ID or bundle. - if ($this->entityInfo['entity keys']['id'] == $name || $this->entityInfo['entity keys']['bundle'] == $name) { - continue; - } - $extra[$this->entityType][$this->entityType]['display'][$name] = $this->generateExtraFieldInfo($name, $property_info); - } - - // Handle bundle properties. - $this->propertyInfo += array('bundles' => array()); - foreach ($this->propertyInfo['bundles'] as $bundle_name => $info) { - foreach ($info['properties'] as $name => $property_info) { - if (empty($property_info['field'])) { - $extra[$this->entityType][$bundle_name]['display'][$name] = $this->generateExtraFieldInfo($name, $property_info); - } - } - } - return $extra; - } - - /** - * Generates the display field info for a given property. - */ - protected function generateExtraFieldInfo($name, $property_info) { - $info = array( - 'label' => $property_info['label'], - 'weight' => 0, - ); - if (!empty($property_info['description'])) { - $info['description'] = $property_info['description']; - } - return $info; - } -} diff --git a/entity.install b/entity.install deleted file mode 100644 index 118820b..0000000 --- a/entity.install +++ /dev/null @@ -1,142 +0,0 @@ - $module_entitycache_entities) { - foreach ($module_entitycache_entities as $entity_type => $entity_info) { - // Do not break modules that create the cache tables for themselves. - if (!db_table_exists('cache_entity_' . $entity_type)) { - $schema = drupal_get_schema_unprocessed('system', 'cache'); - $schema['description'] = 'Cache table used to store' . $entity_type . ' entity records.'; - db_create_table('cache_entity_' . $entity_type, $schema); - $tables_created[$module][] = 'cache_entity_' . $entity_type; - } - } - } - variable_set('entity_cache_tables_created', $tables_created); -} - -/** - * Remove entity cache tables for entity types of uninstalled modules. - * - * @param $modules - * (optional) An array of uninstalled modules. If not specified, try to remove - * cache tables for all modules. - */ -function entity_entitycache_uninstalled_modules($modules = NULL) { - // If no modules are specified or if entitycache is being uninstalled, - // try to remove entitycache tables for supporting entities of all modules. - if (!isset($modules) || in_array('entitycache', $modules)) { - $modules = module_list(); - } - $tables_created = variable_get('entity_cache_tables_created'); - foreach ($modules as $module) { - if (!empty($tables_created[$module])) { - foreach ($tables_created[$module] as $table) { - db_drop_table($table); - } - unset($tables_created[$module]); - } - } - variable_set('entity_cache_tables_created', $tables_created); -} - -/** - * Helper to fetch entity info about entity types that use caching. - */ -function _entity_entitycache_get_module_info($modules) { - // Prepare a keyed array of all modules with their entity types and infos. - // Structure: [module][entity][info] - $entity_crud_info = entity_crud_get_info(); - $info = array(); - foreach ($entity_crud_info as $entity_name => $entity_info) { - // Make sure that the entity info specifies a module and supports entitycache. - if (!isset($entity_info['module']) || empty($entity_info['entity cache'])) { - continue; - } - $module = $entity_info['module']; - // Only treat installed modules. - if (!in_array($module, $modules)) { - continue; - } - // Add the entity info to the module key. - if (!isset($info[$module])) { - $info[$module] = array(); - } - $info[$module][$entity_name] = $entity_info; - } - return $info; -} diff --git a/entity.module b/entity.module deleted file mode 100644 index da0acf6..0000000 --- a/entity.module +++ /dev/null @@ -1,1575 +0,0 @@ - 'view callback', - 'create' => 'creation callback', - 'delete' => 'deletion callback', - 'revision delete' => 'revision deletion callback', - 'save' => 'save callback', - 'access' => 'access callback', - 'form' => 'form callback' - ); - if (isset($info[$keys[$op]])) { - return TRUE; - } - if ($op == 'revision delete') { - return in_array('EntityAPIControllerInterface', class_implements($info['controller class'])); - } - if ($op == 'form') { - return (bool) entity_ui_controller($entity_type); - } - if ($op != 'access') { - return in_array('EntityAPIControllerInterface', class_implements($info['controller class'])); - } - return FALSE; -} - -/** - * Menu loader function: load an entity from its path. - * - * This can be used to load entities of all types in menu paths: - * - * @code - * $items['myentity/%entity_object'] = array( - * 'load arguments' => array('myentity'), - * 'title' => ..., - * 'page callback' => ..., - * 'page arguments' => array(...), - * 'access arguments' => array(...), - * ); - * @endcode - * - * @param $entity_id - * The ID of the entity to load, passed by the menu URL. - * @param $entity_type - * The type of the entity to load. - * @return - * A fully loaded entity object, or FALSE in case of error. - */ -function entity_object_load($entity_id, $entity_type) { - $entities = entity_load($entity_type, array($entity_id)); - return reset($entities); -} - -/** - * Page callback to show links to add an entity of a specific bundle. - * - * Entity modules that provide a further description to their bundles may wish - * to implement their own version of this to show these. - * - * @param $entity_type - * The type of the entity. - */ -function entity_ui_bundle_add_page($entity_type) { - // Set the title, as we're a MENU_LOCAL_ACTION and hence just get tab titles. - module_load_include('inc', 'entity', 'includes/entity.ui'); - drupal_set_title(entity_ui_get_action_title('add', $entity_type)); - - // Get entity info for our bundles. - $info = entity_get_info($entity_type); - $items = array(); - foreach ($info['bundles'] as $bundle_name => $bundle_info) { - // Create an empty entity with just the bundle set to check for access. - $dummy_entity = entity_create($entity_type, array( - $info['entity keys']['bundle'] => $bundle_name, - )); - // If modules use a uid, they can default to the current-user - // in their create() method on the storage controller. - if (entity_access('create', $entity_type, $dummy_entity, $account = NULL)) { - $add_path = $info['admin ui']['path'] . '/add/' . $bundle_name; - $items[] = l(t('Add @label', array('@label' => $bundle_info['label'])), $add_path); - } - } - return theme('item_list', array('items' => $items)); -} - -/** - * Page callback to add an entity of a specific bundle. - * - * @param $entity_type - * The type of the entity. - * @param $bundle_name - * The bundle machine name. - */ -function entity_ui_get_bundle_add_form($entity_type, $bundle_name) { - $info = entity_get_info($entity_type); - $bundle_key = $info['entity keys']['bundle']; - - // Make a stub entity of the right bundle to pass to the entity_ui_get_form(). - $values = array( - $bundle_key => $bundle_name, - ); - $entity = entity_create($entity_type, $values); - - return entity_ui_get_form($entity_type, $entity, 'add'); -} - -/** - * Page callback for viewing an entity. - * - * @param Entity $entity - * The entity to be rendered. - * - * @return array - * A renderable array of the entity in full view mode. - */ -function entity_ui_entity_page_view($entity) { - module_load_include('inc', 'entity', 'includes/entity.ui'); - return $entity->view('full', NULL, TRUE); -} - -/** - * Gets the page title for the passed operation. - */ -function entity_ui_get_page_title($op, $entity_type, $entity = NULL) { - if (isset($entity)) { - module_load_include('inc', 'entity', 'includes/entity.ui'); - $label = entity_label($entity_type, $entity); - list(, , $bundle) = entity_extract_ids($entity_type, $entity); - } - else { - $info = entity_get_info($entity_type); - $label = $info['label']; - $bundle = NULL; - } - - switch ($op) { - case 'view': - return $label; - case 'edit': - return t('Edit @label', array('@label' => $label)); - case 'clone': - return t('Clone @label', array('@label' => $label)); - case 'revert': - return t('Revert @label', array('@label' => $label)); - case 'delete': - return t('Delete @label', array('@label' => $label)); - case 'export': - return t('Export @label', array('@label' => $label)); - } - - return entity_ui_get_action_title($op, $entity_type, $bundle); -} - -/** - * A wrapper around entity_load() to load a single entity by name or numeric id. - * - * @todo: Re-name entity_load() to entity_load_multiple() in d8 core and this - * to entity_load(). - * - * @param $entity_type - * The entity type to load, e.g. node or user. - * @param $id - * The entity id, either the numeric id or the entity name. In case the entity - * type has specified a name key, both the numeric id and the name may be - * passed. - * - * @return - * The entity object, or FALSE. - * - * @see entity_load() - */ -function entity_load_single($entity_type, $id) { - $entities = entity_load($entity_type, array($id)); - return reset($entities); -} - -/** - * A wrapper around entity_load() to return entities keyed by name key if existing. - * - * @param $entity_type - * The entity type to load, e.g. node or user. - * @param $names - * An array of entity names or ids, or FALSE to load all entities. - * @param $conditions - * (deprecated) An associative array of conditions on the base table, where - * the keys are the database fields and the values are the values those - * fields must have. Instead, it is preferable to use EntityFieldQuery to - * retrieve a list of entity IDs loadable by this function. - * - * @return - * An array of entity objects indexed by their names (or ids if the entity - * type has no name key). - * - * @see entity_load() - */ -function entity_load_multiple_by_name($entity_type, $names = FALSE, $conditions = array()) { - $entities = entity_load($entity_type, $names, $conditions); - $info = entity_get_info($entity_type); - if (!isset($info['entity keys']['name'])) { - return $entities; - } - return entity_key_array_by_property($entities, $info['entity keys']['name']); -} - -/** - * Permanently save an entity. - * - * In case of failures, an exception is thrown. - * - * @param $entity_type - * The type of the entity. - * @param $entity - * The entity to save. - * - * @return - * For entity types provided by the CRUD API, SAVED_NEW or SAVED_UPDATED is - * returned depending on the operation performed. If there is no information - * how to save the entity, FALSE is returned. - * - * @see entity_type_supports() - */ -function entity_save($entity_type, $entity) { - $info = entity_get_info($entity_type); - if (method_exists($entity, 'save')) { - return $entity->save(); - } - elseif (isset($info['save callback'])) { - $info['save callback']($entity); - } - elseif (in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) { - return entity_get_controller($entity_type)->save($entity); - } - else { - return FALSE; - } -} - -/** - * Permanently delete the given entity. - * - * In case of failures, an exception is thrown. - * - * @param $entity_type - * The type of the entity. - * @param $id - * The uniform identifier of the entity to delete. - * - * @return - * FALSE, if there were no information how to delete the entity. - * - * @see entity_type_supports() - */ -function entity_delete($entity_type, $id) { - return entity_delete_multiple($entity_type, array($id)); -} - -/** - * Permanently delete multiple entities. - * - * @param $entity_type - * The type of the entity. - * @param $ids - * An array of entity ids of the entities to delete. In case the entity makes - * use of a name key, both the names or numeric ids may be passed. - * @return - * FALSE if the given entity type isn't compatible to the CRUD API. - */ -function entity_delete_multiple($entity_type, $ids) { - $info = entity_get_info($entity_type); - if (isset($info['deletion callback'])) { - foreach ($ids as $id) { - $info['deletion callback']($id); - } - } - elseif (in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) { - entity_get_controller($entity_type)->delete($ids); - } - else { - return FALSE; - } -} - -/** - * Loads an entity revision. - * - * @param $entity_type - * The type of the entity. - * @param $revision_id - * The id of the revision to load. - * - * @return - * The entity object, or FALSE if there is no entity with the given revision - * id. - */ -function entity_revision_load($entity_type, $revision_id) { - $info = entity_get_info($entity_type); - if (!empty($info['entity keys']['revision'])) { - $entity_revisions = entity_load($entity_type, FALSE, array($info['entity keys']['revision'] => $revision_id)); - return reset($entity_revisions); - } - return FALSE; -} - -/** - * Deletes an entity revision. - * - * @param $entity_type - * The type of the entity. - * @param $revision_id - * The revision ID to delete. - * - * @return - * TRUE if the entity revision could be deleted, FALSE otherwise. - */ -function entity_revision_delete($entity_type, $revision_id) { - $info = entity_get_info($entity_type); - if (isset($info['revision deletion callback'])) { - return $info['revision deletion callback']($revision_id, $entity_type); - } - elseif (in_array('EntityAPIControllerRevisionableInterface', class_implements($info['controller class']))) { - return entity_get_controller($entity_type)->deleteRevision($revision_id); - } - return FALSE; -} - -/** - * Checks whether the given entity is the default revision. - * - * Note that newly created entities will always be created in default revision, - * thus TRUE is returned for not yet saved entities. - * - * @param $entity_type - * The type of the entity. - * @param $entity - * The entity object to check. - * - * @return boolean - * A boolean indicating whether the entity is in default revision is returned. - * If the entity is not revisionable or is new, TRUE is returned. - * - * @see entity_revision_set_default() - */ -function entity_revision_is_default($entity_type, $entity) { - $info = entity_get_info($entity_type); - if (empty($info['entity keys']['revision'])) { - return TRUE; - } - // Newly created entities will always be created in default revision. - if (!empty($entity->is_new) || empty($entity->{$info['entity keys']['id']})) { - return TRUE; - } - if (in_array('EntityAPIControllerRevisionableInterface', class_implements($info['controller class']))) { - $key = !empty($info['entity keys']['default revision']) ? $info['entity keys']['default revision'] : 'default_revision'; - return !empty($entity->$key); - } - else { - // Else, just load the default entity and compare the ID. Usually, the - // entity should be already statically cached anyway. - $default = entity_load_single($entity_type, $entity->{$info['entity keys']['id']}); - return $default->{$info['entity keys']['revision']} == $entity->{$info['entity keys']['revision']}; - } -} - -/** - * Sets a given entity revision as default revision. - * - * Note that the default revision flag will only be supported by entity types - * based upon the EntityAPIController, i.e. implementing the - * EntityAPIControllerRevisionableInterface. - * - * @param $entity_type - * The type of the entity. - * @param $entity - * The entity revision to update. - * - * @see entity_revision_is_default() - */ -function entity_revision_set_default($entity_type, $entity) { - $info = entity_get_info($entity_type); - if (!empty($info['entity keys']['revision'])) { - $key = !empty($info['entity keys']['default revision']) ? $info['entity keys']['default revision'] : 'default_revision'; - $entity->$key = TRUE; - } -} - -/** - * Create a new entity object. - * - * @param $entity_type - * The type of the entity. - * @param $values - * An array of values to set, keyed by property name. If the entity type has - * bundles the bundle key has to be specified. - * @return - * A new instance of the entity type or FALSE if there is no information for - * the given entity type. - * - * @see entity_type_supports() - */ -function entity_create($entity_type, array $values) { - $info = entity_get_info($entity_type); - if (isset($info['creation callback'])) { - return $info['creation callback']($values, $entity_type); - } - elseif (in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) { - return entity_get_controller($entity_type)->create($values); - } - return FALSE; -} - -/** - * Exports an entity. - * - * Note: Currently, this only works for entity types provided with the entity - * CRUD API. - * - * @param $entity_type - * The type of the entity. - * @param $entity - * The entity to export. - * @param $prefix - * An optional prefix for each line. - * @return - * The exported entity as serialized string. The format is determined by the - * respective entity controller, e.g. it is JSON for the EntityAPIController. - * The output is suitable for entity_import(). - */ -function entity_export($entity_type, $entity, $prefix = '') { - if (method_exists($entity, 'export')) { - return $entity->export($prefix); - } - $info = entity_get_info($entity_type); - if (in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) { - return entity_get_controller($entity_type)->export($entity, $prefix); - } -} - -/** - * Imports an entity. - * - * Note: Currently, this only works for entity types provided with the entity - * CRUD API. - * - * @param $entity_type - * The type of the entity. - * @param string $export - * The string containing the serialized entity as produced by - * entity_export(). - * @return - * The imported entity object not yet saved. - */ -function entity_import($entity_type, $export) { - $info = entity_get_info($entity_type); - if (in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) { - return entity_get_controller($entity_type)->import($export); - } -} - -/** - * Checks whether an entity type is fieldable. - * - * @param $entity_type - * The type of the entity. - * - * @return - * TRUE if the entity type is fieldable, FALSE otherwise. - */ -function entity_type_is_fieldable($entity_type) { - $info = entity_get_info($entity_type); - return !empty($info['fieldable']); -} - -/** - * Builds a structured array representing the entity's content. - * - * The content built for the entity will vary depending on the $view_mode - * parameter. - * - * Note: Currently, this only works for entity types provided with the entity - * CRUD API. - * - * @param $entity_type - * The type of the entity. - * @param $entity - * An entity object. - * @param $view_mode - * A view mode as used by this entity type, e.g. 'full', 'teaser'... - * @param $langcode - * (optional) A language code to use for rendering. Defaults to the global - * content language of the current request. - * @return - * The renderable array. - */ -function entity_build_content($entity_type, $entity, $view_mode = 'full', $langcode = NULL) { - $info = entity_get_info($entity_type); - if (method_exists($entity, 'buildContent')) { - return $entity->buildContent($view_mode, $langcode); - } - elseif (in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) { - return entity_get_controller($entity_type)->buildContent($entity, $view_mode, $langcode); - } -} - -/** - * Returns the entity identifier, i.e. the entities name or numeric id. - * - * Unlike entity_extract_ids() this function returns the name of the entity - * instead of the numeric id, in case the entity type has specified a name key. - * - * @param $entity_type - * The type of the entity. - * @param $entity - * An entity object. - * - * @see entity_extract_ids() - */ -function entity_id($entity_type, $entity) { - if (method_exists($entity, 'identifier')) { - return $entity->identifier(); - } - $info = entity_get_info($entity_type); - $key = isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id']; - return isset($entity->$key) ? $entity->$key : NULL; -} - -/** - * Generate an array for rendering the given entities. - * - * Entities being viewed, are generally expected to be fully-loaded entity - * objects, thus have their name or id key set. However, it is possible to - * view a single entity without any id, e.g. for generating a preview during - * creation. - * - * @param $entity_type - * The type of the entity. - * @param $entities - * An array of entities to render. - * @param $view_mode - * A view mode as used by this entity type, e.g. 'full', 'teaser'... - * @param $langcode - * (optional) A language code to use for rendering. Defaults to the global - * content language of the current request. - * @param $page - * (optional) If set will control if the entity is rendered: if TRUE - * the entity will be rendered without its title, so that it can be embedded - * in another context. If FALSE the entity will be displayed with its title - * in a mode suitable for lists. - * If unset, the page mode will be enabled if the current path is the URI - * of the entity, as returned by entity_uri(). - * This parameter is only supported for entities which controller is a - * EntityAPIControllerInterface. - * @return - * The renderable array, keyed by the entity type and by entity identifiers, - * for which the entity name is used if existing - see entity_id(). If there - * is no information on how to view an entity, FALSE is returned. - */ -function entity_view($entity_type, $entities, $view_mode = 'full', $langcode = NULL, $page = NULL) { - $info = entity_get_info($entity_type); - if (isset($info['view callback'])) { - $entities = entity_key_array_by_property($entities, $info['entity keys']['id']); - return $info['view callback']($entities, $view_mode, $langcode, $entity_type); - } - elseif (in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) { - return entity_get_controller($entity_type)->view($entities, $view_mode, $langcode, $page); - } - return FALSE; -} - -/** - * Determines whether the given user can perform actions on an entity. - * - * For create operations, the pattern is to create an entity and then - * check if the user has create access. - * - * @code - * $node = entity_create('node', array('type' => 'page')); - * $access = entity_access('create', 'node', $node, $account); - * @endcode - * - * @param $op - * The operation being performed. One of 'view', 'update', 'create' or - * 'delete'. - * @param $entity_type - * The entity type of the entity to check for. - * @param $entity - * Optionally an entity to check access for. If no entity is given, it will be - * determined whether access is allowed for all entities of the given type. - * @param $account - * The user to check for. Leave it to NULL to check for the global user. - * - * @return boolean - * Whether access is allowed or not. If the entity type does not specify any - * access information, NULL is returned. - * - * @see entity_type_supports() - */ -function entity_access($op, $entity_type, $entity = NULL, $account = NULL) { - if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) { - return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type); - } -} - -/** - * Gets the edit form for any entity. - * - * This helper makes use of drupal_get_form() and the regular form builder - * function of the entity type to retrieve and process the form as usual. - * - * In order to use this helper to show an entity add form, the new entity object - * can be created via entity_create() or entity_property_values_create_entity(). - * - * @param $entity_type - * The type of the entity. - * @param $entity - * The entity to show the edit form for. - * @return - * The renderable array of the form. If there is no entity form or missing - * metadata, FALSE is returned. - * - * @see entity_type_supports() - */ -function entity_form($entity_type, $entity) { - $info = entity_get_info($entity_type); - if (isset($info['form callback'])) { - return $info['form callback']($entity, $entity_type); - } - // If there is an UI controller, the providing module has to implement the - // entity form using entity_ui_get_form(). - elseif (entity_ui_controller($entity_type)) { - return entity_metadata_form_entity_ui($entity, $entity_type); - } - return FALSE; -} - -/** - * Converts an array of entities to be keyed by the values of a given property. - * - * @param array $entities - * The array of entities to convert. - * @param $property - * The name of entity property, by which the array should be keyed. To get - * reasonable results, the property has to have unique values. - * - * @return array - * The same entities in the same order, but keyed by their $property values. - */ -function entity_key_array_by_property(array $entities, $property) { - $ret = array(); - foreach ($entities as $entity) { - $key = isset($entity->$property) ? $entity->$property : NULL; - $ret[$key] = $entity; - } - return $ret; -} - -/** - * Get the entity info for the entity types provided via the entity CRUD API. - * - * @return - * An array in the same format as entity_get_info(), containing the entities - * whose controller class implements the EntityAPIControllerInterface. - */ -function entity_crud_get_info() { - $types = array(); - foreach (entity_get_info() as $type => $info) { - if (isset($info['controller class']) && in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) { - $types[$type] = $info; - } - } - return $types; -} - -/** - * Checks if a given entity has a certain exportable status. - * - * @param $entity_type - * The type of the entity. - * @param $entity - * The entity to check the status on. - * @param $status - * The constant status like ENTITY_CUSTOM, ENTITY_IN_CODE, ENTITY_OVERRIDDEN - * or ENTITY_FIXED. - * - * @return - * TRUE if the entity has the status, FALSE otherwise. - */ -function entity_has_status($entity_type, $entity, $status) { - $info = entity_get_info($entity_type); - $status_key = empty($info['entity keys']['status']) ? 'status' : $info['entity keys']['status']; - return isset($entity->{$status_key}) && ($entity->{$status_key} & $status) == $status; -} - -/** - * Export a variable. Copied from ctools. - * - * This is a replacement for var_export(), allowing us to more nicely - * format exports. It will recurse down into arrays and will try to - * properly export bools when it can. - */ -function entity_var_export($var, $prefix = '') { - if (is_array($var)) { - if (empty($var)) { - $output = 'array()'; - } - else { - $output = "array(\n"; - foreach ($var as $key => $value) { - $output .= " '$key' => " . entity_var_export($value, ' ') . ",\n"; - } - $output .= ')'; - } - } - elseif (is_bool($var)) { - $output = $var ? 'TRUE' : 'FALSE'; - } - else { - $output = var_export($var, TRUE); - } - - if ($prefix) { - $output = str_replace("\n", "\n$prefix", $output); - } - return $output; -} - -/** - * Export a variable in pretty formatted JSON. - */ -function entity_var_json_export($var, $prefix = '') { - if (is_array($var) && $var) { - // Defines whether we use a JSON array or object. - $use_array = ($var == array_values($var)); - $output = $use_array ? "[" : "{"; - - foreach ($var as $key => $value) { - if ($use_array) { - $values[] = entity_var_json_export($value, ' '); - } - else { - $values[] = entity_var_json_export((string) $key, ' ') . ' : ' . entity_var_json_export($value, ' '); - } - } - // Use several lines for long content. However for objects with a single - // entry keep the key in the first line. - if (strlen($content = implode(', ', $values)) > 70 && ($use_array || count($values) > 1)) { - $output .= "\n " . implode(",\n ", $values) . "\n"; - } - elseif (strpos($content, "\n") !== FALSE) { - $output .= " " . $content . "\n"; - } - else { - $output .= " " . $content . ' '; - } - $output .= $use_array ? ']' : '}'; - } - else { - $output = drupal_json_encode($var); - } - - if ($prefix) { - $output = str_replace("\n", "\n$prefix", $output); - } - return $output; -} - -/** - * Rebuild the default entities provided in code. - * - * Exportable entities provided in code get saved to the database once a module - * providing defaults in code is activated. This allows module and entity_load() - * to easily deal with exportable entities just by relying on the database. - * - * The defaults get rebuilt if the cache is cleared or new modules providing - * defaults are enabled, such that the defaults in the database are up to date. - * A default entity gets updated with the latest defaults in code during rebuild - * as long as the default has not been overridden. Once a module providing - * defaults is disabled, its default entities get removed from the database - * unless they have been overridden. In that case the overridden entity is left - * in the database, but its status gets updated to 'custom'. - * - * @param $entity_types - * (optional) If specified, only the defaults of the given entity types are - * rebuilt. - */ -function entity_defaults_rebuild($entity_types = NULL) { - if (!isset($entity_types)) { - $entity_types = array(); - foreach (entity_crud_get_info() as $type => $info) { - if (!empty($info['exportable'])) { - $entity_types[] = $type; - } - }; - } - foreach ($entity_types as $type) { - _entity_defaults_rebuild($type); - } -} - -/** - * Actually rebuild the defaults of a given entity type. - */ -function _entity_defaults_rebuild($entity_type) { - if (lock_acquire('entity_rebuild_' . $entity_type)) { - $info = entity_get_info($entity_type); - $hook = isset($info['export']['default hook']) ? $info['export']['default hook'] : 'default_' . $entity_type; - $keys = $info['entity keys'] + array('module' => 'module', 'status' => 'status', 'name' => $info['entity keys']['id']); - - // Check for the existence of the module and status columns. - if (!in_array($keys['status'], $info['schema_fields_sql']['base table']) || !in_array($keys['module'], $info['schema_fields_sql']['base table'])) { - trigger_error("Missing database columns for the exportable entity $entity_type as defined by entity_exportable_schema_fields(). Update the according module and run update.php!", E_USER_WARNING); - return; - } - - // Invoke the hook and collect default entities. - $entities = array(); - foreach (module_implements($hook) as $module) { - foreach ((array) module_invoke($module, $hook) as $name => $entity) { - $entity->{$keys['name']} = $name; - $entity->{$keys['module']} = $module; - $entities[$name] = $entity; - } - } - drupal_alter($hook, $entities); - - // Check for defaults that disappeared. - $existing_defaults = entity_load_multiple_by_name($entity_type, FALSE, array($keys['status'] => array(ENTITY_OVERRIDDEN, ENTITY_IN_CODE, ENTITY_FIXED))); - - foreach ($existing_defaults as $name => $entity) { - if (empty($entities[$name])) { - $entity->is_rebuild = TRUE; - if (entity_has_status($entity_type, $entity, ENTITY_OVERRIDDEN)) { - $entity->{$keys['status']} = ENTITY_CUSTOM; - entity_save($entity_type, $entity); - } - else { - entity_delete($entity_type, $name); - } - unset($entity->is_rebuild); - } - } - - // Load all existing entities. - $existing_entities = entity_load_multiple_by_name($entity_type, array_keys($entities)); - - foreach ($existing_entities as $name => $entity) { - if (entity_has_status($entity_type, $entity, ENTITY_CUSTOM)) { - // If the entity already exists but is not yet marked as overridden, we - // have to update the status. - if (!entity_has_status($entity_type, $entity, ENTITY_OVERRIDDEN)) { - $entity->{$keys['status']} |= ENTITY_OVERRIDDEN; - $entity->{$keys['module']} = $entities[$name]->{$keys['module']}; - $entity->is_rebuild = TRUE; - entity_save($entity_type, $entity); - unset($entity->is_rebuild); - } - - // The entity is overridden, so we do not need to save the default. - unset($entities[$name]); - } - } - - // Save defaults. - $originals = array(); - foreach ($entities as $name => $entity) { - if (!empty($existing_entities[$name])) { - // Make sure we are updating the existing default. - $entity->{$keys['id']} = $existing_entities[$name]->{$keys['id']}; - unset($entity->is_new); - } - // Pre-populate $entity->original as we already have it. So we avoid - // loading it again. - $entity->original = !empty($existing_entities[$name]) ? $existing_entities[$name] : FALSE; - // Keep original entities for hook_{entity_type}_defaults_rebuild() - // implementations. - $originals[$name] = $entity->original; - - if (!isset($entity->{$keys['status']})) { - $entity->{$keys['status']} = ENTITY_IN_CODE; - } - else { - $entity->{$keys['status']} |= ENTITY_IN_CODE; - } - $entity->is_rebuild = TRUE; - entity_save($entity_type, $entity); - unset($entity->is_rebuild); - } - - // Invoke an entity type-specific hook so modules may apply changes, e.g. - // efficiently rebuild caches. - module_invoke_all($entity_type . '_defaults_rebuild', $entities, $originals); - - lock_release('entity_rebuild_' . $entity_type); - } -} - -/** - * Implements hook_modules_installed(). - */ -function entity_modules_installed($modules) { - module_load_install('entity'); - entity_entitycache_installed_modules($modules); -} - -/** - * Implements hook_modules_uninstalled(). - */ -function entity_modules_uninstalled($modules) { - module_load_install('entity'); - entity_entitycache_uninstalled_modules($modules); -} - -/** - * Implements hook_modules_enabled(). - */ -function entity_modules_enabled($modules) { - foreach (_entity_modules_get_default_types($modules) as $type) { - _entity_defaults_rebuild($type); - } -} - -/** - * Implements hook_modules_disabled(). - */ -function entity_modules_disabled($modules) { - foreach (_entity_modules_get_default_types($modules) as $entity_type) { - $info = entity_get_info($entity_type); - - // Do nothing if the module providing the entity type has been disabled too. - if (isset($info['module']) && in_array($info['module'], $modules)) { - return; - } - - $keys = $info['entity keys'] + array('module' => 'module', 'status' => 'status', 'name' => $info['entity keys']['id']); - // Remove entities provided in code by one of the disabled modules. - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', $entity_type, '=') - ->propertyCondition($keys['module'], $modules, 'IN') - ->propertyCondition($keys['status'], array(ENTITY_IN_CODE, ENTITY_FIXED), 'IN'); - $result = $query->execute(); - if (isset($result[$entity_type])) { - $entities = entity_load($entity_type, array_keys($result[$entity_type])); - entity_delete_multiple($entity_type, array_keys($entities)); - } - - // Update overridden entities to be now custom. - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', $entity_type, '=') - ->propertyCondition($keys['module'], $modules, 'IN') - ->propertyCondition($keys['status'], ENTITY_OVERRIDDEN, '='); - $result = $query->execute(); - if (isset($result[$entity_type])) { - foreach (entity_load($entity_type, array_keys($result[$entity_type])) as $name => $entity) { - $entity->{$keys['status']} = ENTITY_CUSTOM; - $entity->{$keys['module']} = NULL; - entity_save($entity_type, $entity); - } - } - - // Rebuild the remaining defaults so any alterations of the disabled modules - // are gone. - _entity_defaults_rebuild($entity_type); - } -} - -/** - * Gets all entity types for which defaults are provided by the $modules. - */ -function _entity_modules_get_default_types($modules) { - $types = array(); - foreach (entity_crud_get_info() as $entity_type => $info) { - if (!empty($info['exportable'])) { - $hook = isset($info['export']['default hook']) ? $info['export']['default hook'] : 'default_' . $entity_type; - foreach ($modules as $module) { - if (module_hook($module, $hook) || module_hook($module, $hook . '_alter')) { - $types[] = $entity_type; - } - } - } - } - return $types; -} - -/** - * Defines schema fields required for exportable entities. - * - * Warning: Do not call this function in your module's hook_schema() - * implementation or update functions. It is not safe to call functions of - * dependencies at this point. Instead of calling the function, just copy over - * the content. - * For more details see the issue http://drupal.org/node/1122812. - */ -function entity_exportable_schema_fields($module_col = 'module', $status_col = 'status') { - return array( - $status_col => array( - 'type' => 'int', - 'not null' => TRUE, - // Set the default to ENTITY_CUSTOM without using the constant as it is - // not safe to use it at this point. - 'default' => 0x01, - 'size' => 'tiny', - 'description' => 'The exportable status of the entity.', - ), - $module_col => array( - 'description' => 'The name of the providing module if the entity has been defined in code.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - ), - ); -} - -/** - * Implements hook_flush_caches(). - */ -function entity_flush_caches() { - entity_property_info_cache_clear(); - // Re-build defaults in code, however skip it on the admin modules page. In - // case of enabling or disabling modules we already rebuild defaults in - // entity_modules_enabled() and entity_modules_disabled(), so we do not need - // to do it again. - // Also check if rebuilding on cache flush is explicitly disabled. - if (current_path() != 'admin/modules/list/confirm' && variable_get('entity_rebuild_on_flush', TRUE)) { - entity_defaults_rebuild(); - } - - // Care about entitycache tables. - if (module_exists('entitycache')) { - $tables = array(); - $tables_created = variable_get('entity_cache_tables_created'); - if (is_array($tables_created)) { - foreach ($tables_created as $module => $entity_cache_tables) { - $tables = array_merge($tables, $entity_cache_tables); - } - } - return $tables; - } -} - -/** - * Implements hook_theme(). - */ -function entity_theme() { - // Build a pattern in the form of "(type1|type2|...)(\.|__)" such that all - // templates starting with an entity type or named like the entity type - // are found. - // This has to match the template suggestions provided in - // template_preprocess_entity(). - $types = array_keys(entity_crud_get_info()); - $pattern = '(' . implode('|', $types) . ')(\.|__)'; - - return array( - 'entity_status' => array( - 'variables' => array('status' => NULL, 'html' => TRUE), - 'file' => 'theme/entity.theme.inc', - ), - 'entity' => array( - 'render element' => 'elements', - 'template' => 'entity', - 'pattern' => $pattern, - 'path' => drupal_get_path('module', 'entity') . '/theme', - 'file' => 'entity.theme.inc', - ), - 'entity_property' => array( - 'render element' => 'elements', - 'file' => 'theme/entity.theme.inc', - ), - 'entity_ui_overview_item' => array( - 'variables' => array('label' => NULL, 'entity_type' => NULL, 'url' => FALSE, 'name' => FALSE), - 'file' => 'includes/entity.ui.inc' - ), - ); -} - -/** - * Label callback that refers to the entity classes label method. - */ -function entity_class_label($entity) { - return $entity->label(); -} - -/** - * URI callback that refers to the entity classes uri method. - */ -function entity_class_uri($entity) { - return $entity->uri(); -} - -/** - * Implements hook_file_download_access() for entity types provided by the CRUD API. - */ -function entity_file_download_access($field, $entity_type, $entity) { - $info = entity_get_info($entity_type); - if (in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) { - return entity_access('view', $entity_type, $entity); - } -} - -/** - * Determines the UI controller class for a given entity type. - * - * @return EntityDefaultUIController - * If a type is given, the controller for the given entity type. Else an array - * of all enabled UI controllers keyed by entity type is returned. - */ -function entity_ui_controller($type = NULL) { - $static = &drupal_static(__FUNCTION__); - - if (!isset($type)) { - // Invoke the function for each type to ensure we have fully populated the - // static variable. - foreach (entity_get_info() as $entity_type => $info) { - entity_ui_controller($entity_type); - } - return array_filter($static); - } - - if (!isset($static[$type])) { - $info = entity_get_info($type); - $class = isset($info['admin ui']['controller class']) ? $info['admin ui']['controller class'] : 'EntityDefaultUIController'; - $static[$type] = (isset($info['admin ui']['path']) && $class) ? new $class($type, $info) : FALSE; - } - - return $static[$type]; -} - -/** - * Implements hook_menu(). - * - * @see EntityDefaultUIController::hook_menu() - */ -function entity_menu() { - $items = array(); - foreach (entity_ui_controller() as $controller) { - $items += $controller->hook_menu(); - } - return $items; -} - -/** - * Implements hook_forms(). - * - * @see EntityDefaultUIController::hook_forms() - * @see entity_ui_get_form() - */ -function entity_forms($form_id, $args) { - // For efficiency only invoke an entity types controller, if a form of it is - // requested. Thus if the first (overview and operation form) or the third - // argument (edit form) is an entity type name, add in the types forms. - if (isset($args[0]) && is_string($args[0]) && entity_get_info($args[0])) { - $type = $args[0]; - } - elseif (isset($args[2]) && is_string($args[2]) && entity_get_info($args[2])) { - $type = $args[2]; - } - if (isset($type) && $controller = entity_ui_controller($type)) { - return $controller->hook_forms(); - } -} - -/** - * A wrapper around drupal_get_form() that helps building entity forms. - * - * This function may be used by entities to build their entity form. It has to - * be used instead of calling drupal_get_form(). - * Entity forms built with this helper receive useful defaults suiting for - * editing a single entity, whereas the special cases of adding and cloning - * of entities are supported too. - * - * While this function is intended to be used to get entity forms for entities - * using the entity ui controller, it may be used for entity types not using - * the ui controller too. - * - * @param $entity_type - * The entity type for which to get the form. - * @param $entity - * The entity for which to return the form. - * If $op is 'add' the entity has to be either initialized before calling this - * function, or NULL may be passed. If NULL is passed, an entity will be - * initialized with empty values using entity_create(). Thus entities, for - * which this is problematic have to care to pass in an initialized entity. - * @param $op - * (optional) One of 'edit', 'add' or 'clone'. Defaults to edit. - * @param $form_state - * (optional) A pre-populated form state, e.g. to add in form include files. - * See entity_metadata_form_entity_ui(). - * - * @return - * The fully built and processed form, ready to be rendered. - * - * @see EntityDefaultUIController::hook_forms() - * @see entity_ui_form_submit_build_entity() - */ -function entity_ui_get_form($entity_type, $entity, $op = 'edit', $form_state = array()) { - if (isset($entity)) { - list(, , $bundle) = entity_extract_ids($entity_type, $entity); - } - $form_id = (!isset($bundle) || $bundle == $entity_type) ? $entity_type . '_form' : $entity_type . '_edit_' . $bundle . '_form'; - - if (!isset($entity) && $op == 'add') { - $entity = entity_create($entity_type, array()); - } - - // Do not use drupal_get_form(), but invoke drupal_build_form() ourself so - // we can prepulate the form state. - $form_state['wrapper_callback'] = 'entity_ui_main_form_defaults'; - $form_state['entity_type'] = $entity_type; - form_load_include($form_state, 'inc', 'entity', 'includes/entity.ui'); - - // Handle cloning. We cannot do that in the wrapper callback as it is too late - // for changing arguments. - if ($op == 'clone') { - $entity = entity_ui_clone_entity($entity_type, $entity); - } - - // We don't pass the entity type as first parameter, as the implementing - // module knows the type anyway. However, in order to allow for efficient - // hook_forms() implementiations we append the entity type as last argument, - // which the module implementing the form constructor may safely ignore. - // @see entity_forms() - $form_state['build_info']['args'] = array($entity, $op, $entity_type); - return drupal_build_form($form_id, $form_state); -} - - -/** - * Gets the page/menu title for local action operations. - * - * @param $op - * The current operation. One of 'add' or 'import'. - * @param $entity_type - * The entity type. - * @param $bundle_name - * (Optional) The name of the bundle. May be NULL if the bundle name is not - * relevant to the current page. If the entity type has only one bundle, or no - * bundles, this will be the same as the entity type. - */ -function entity_ui_get_action_title($op, $entity_type, $bundle_name = NULL) { - $info = entity_get_info($entity_type); - switch ($op) { - case 'add': - if (isset($bundle_name) && $bundle_name != $entity_type) { - return t('Add @bundle_name @entity_type', array( - '@bundle_name' => drupal_strtolower($info['bundles'][$bundle_name]['label']), - '@entity_type' => drupal_strtolower($info['label']), - )); - } - else { - return t('Add @entity_type', array('@entity_type' => drupal_strtolower($info['label']))); - } - case 'import': - return t('Import @entity_type', array('@entity_type' => drupal_strtolower($info['label']))); - } -} - -/** - * Helper for using i18n_string(). - * - * @param $name - * Textgroup and context glued with ':'. - * @param $default - * String in default language. Default language may or may not be English. - * @param $langcode - * (optional) The code of a certain language to translate the string into. - * Defaults to the i18n_string() default, i.e. the current language. - * - * @see i18n_string() - */ -function entity_i18n_string($name, $default, $langcode = NULL) { - return function_exists('i18n_string') ? i18n_string($name, $default, array('langcode' => $langcode)) : $default; -} - -/** - * Implements hook_views_api(). - */ -function entity_views_api() { - return array( - 'api' => '3.0-alpha1', - 'path' => drupal_get_path('module', 'entity') . '/views', - ); -} - -/** - * Implements hook_field_extra_fields(). - */ -function entity_field_extra_fields() { - // Invoke specified controllers for entity types provided by the CRUD API. - $items = array(); - foreach (entity_crud_get_info() as $type => $info) { - if (!empty($info['extra fields controller class'])) { - $items = array_merge_recursive($items, entity_get_extra_fields_controller($type)->fieldExtraFields()); - } - } - return $items; -} - -/** - * Gets the extra field controller class for a given entity type. - * - * @return EntityExtraFieldsControllerInterface|false - * The controller for the given entity type or FALSE if none is specified. - */ -function entity_get_extra_fields_controller($type = NULL) { - $static = &drupal_static(__FUNCTION__); - - if (!isset($static[$type])) { - $static[$type] = FALSE; - $info = entity_get_info($type); - if (!empty($info['extra fields controller class'])) { - $static[$type] = new $info['extra fields controller class']($type); - } - } - return $static[$type]; -} - -/** - * Returns a property wrapper for the given data. - * - * If an entity is wrapped, the wrapper can be used to retrieve further wrappers - * for the entity properties. For that the wrapper support chaining, e.g. you - * can use a node wrapper to get the node authors mail address: - * - * @code - * echo $wrappedNode->author->mail->value(); - * @endcode - * - * @param $type - * The type of the passed data. - * @param $data - * The data to wrap. It may be set to NULL, so the wrapper can be used - * without any data for getting information about properties. - * @param $info - * (optional) Specify additional information for the passed data: - * - langcode: (optional) If the data is language specific, its langauge - * code. Defaults to NULL, what means language neutral. - * - bundle: (optional) If an entity is wrapped but not passed, use this key - * to specify the bundle to return a wrapper for. - * - property info: (optional) May be used to use a wrapper with an arbitrary - * data structure (type 'struct'). Use this key for specifying info about - * properties in the same structure as used by hook_entity_property_info(). - * - property info alter: (optional) A callback for altering the property - * info before it is utilized by the wrapper. - * - property defaults: (optional) An array of defaults for the info of - * each property of the wrapped data item. - * @return EntityMetadataWrapper - * Dependend on the passed data the right wrapper is returned. - */ -function entity_metadata_wrapper($type, $data = NULL, array $info = array()) { - if ($type == 'entity' || (($entity_info = entity_get_info()) && isset($entity_info[$type]))) { - // If the passed entity is the global $user, we load the user object by only - // passing on the user id. The global user is not a fully loaded entity. - if ($type == 'user' && is_object($data) && $data == $GLOBALS['user']) { - $data = $data->uid; - } - return new EntityDrupalWrapper($type, $data, $info); - } - elseif ($type == 'list' || entity_property_list_extract_type($type)) { - return new EntityListWrapper($type, $data, $info); - } - elseif (isset($info['property info'])) { - return new EntityStructureWrapper($type, $data, $info); - } - else { - return new EntityValueWrapper($type, $data, $info); - } -} - -/** - * Returns a metadata wrapper for accessing site-wide properties. - * - * Although there is no 'site' entity or such, modules may provide info about - * site-wide properties using hook_entity_property_info(). This function returns - * a wrapper for making use of this properties. - * - * @return EntityMetadataWrapper - * A wrapper for accessing site-wide properties. - * - * @see entity_metadata_system_entity_property_info() - */ -function entity_metadata_site_wrapper() { - $site_info = entity_get_property_info('site'); - $info['property info'] = $site_info['properties']; - return entity_metadata_wrapper('site', FALSE, $info); -} - -/** - * Implements hook_module_implements_alter(). - * - * Moves the hook_entity_info_alter() implementation to the bottom so it is - * invoked after all modules relying on the entity API. - * That way we ensure to run last and clear the field-info cache after the - * others added in their bundle information. - * - * @see entity_entity_info_alter() - */ -function entity_module_implements_alter(&$implementations, $hook) { - if ($hook == 'entity_info_alter') { - // Move our hook implementation to the bottom. - $group = $implementations['entity']; - unset($implementations['entity']); - $implementations['entity'] = $group; - } -} - -/** - * Implements hook_entity_info_alter(). - * - * @see entity_module_implements_alter() - */ -function entity_entity_info_alter(&$entity_info) { - _entity_info_add_metadata($entity_info); - - // Populate a default value for the 'configuration' key of all entity types. - foreach ($entity_info as $type => $info) { - if (!isset($info['configuration'])) { - $entity_info[$type]['configuration'] = !empty($info['exportable']); - } - - if (isset($info['controller class']) && in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) { - // Automatically disable field cache when entity cache is used. - if (!empty($info['entity cache']) && module_exists('entitycache')) { - $entity_info[$type]['field cache'] = FALSE; - } - } - } -} - -/** - * Adds metadata and callbacks for core entities to the entity info. - */ -function _entity_info_add_metadata(&$entity_info) { - // Set plural labels. - $entity_info['node']['plural label'] = t('Nodes'); - $entity_info['user']['plural label'] = t('Users'); - $entity_info['file']['plural label'] = t('Files'); - - // Set descriptions. - $entity_info['node']['description'] = t('Nodes represent the main site content items.'); - $entity_info['user']['description'] = t('Users who have created accounts on your site.'); - $entity_info['file']['description'] = t('Uploaded file.'); - - // Set access callbacks. - $entity_info['node']['access callback'] = 'entity_metadata_no_hook_node_access'; - $entity_info['user']['access callback'] = 'entity_metadata_user_access'; - // File entity has it's own entity_access function. - if (!module_exists('file_entity')) { - $entity_info['file']['access callback'] = 'entity_metadata_file_access'; - } - - // CRUD function callbacks. - $entity_info['node']['creation callback'] = 'entity_metadata_create_node'; - $entity_info['node']['save callback'] = 'node_save'; - $entity_info['node']['deletion callback'] = 'node_delete'; - $entity_info['node']['revision deletion callback'] = 'node_revision_delete'; - $entity_info['user']['creation callback'] = 'entity_metadata_create_object'; - $entity_info['user']['save callback'] = 'entity_metadata_user_save'; - $entity_info['user']['deletion callback'] = 'user_delete'; - $entity_info['file']['save callback'] = 'file_save'; - $entity_info['file']['deletion callback'] = 'entity_metadata_delete_file'; - - // Form callbacks. - $entity_info['node']['form callback'] = 'entity_metadata_form_node'; - $entity_info['user']['form callback'] = 'entity_metadata_form_user'; - - // URI callbacks. - if (!isset($entity_info['file']['uri callback'])) { - $entity_info['file']['uri callback'] = 'entity_metadata_uri_file'; - } - - // View callbacks. - $entity_info['node']['view callback'] = 'entity_metadata_view_node'; - $entity_info['user']['view callback'] = 'entity_metadata_view_single'; - - if (module_exists('comment')) { - $entity_info['comment']['plural label'] = t('Comments'); - $entity_info['comment']['description'] = t('Remark or note that refers to a node.'); - $entity_info['comment']['access callback'] = 'entity_metadata_comment_access'; - $entity_info['comment']['creation callback'] = 'entity_metadata_create_comment'; - $entity_info['comment']['save callback'] = 'comment_save'; - $entity_info['comment']['deletion callback'] = 'comment_delete'; - $entity_info['comment']['view callback'] = 'entity_metadata_view_comment'; - $entity_info['comment']['form callback'] = 'entity_metadata_form_comment'; - } - if (module_exists('taxonomy')) { - $entity_info['taxonomy_term']['plural label'] = t('Taxonomy terms'); - $entity_info['taxonomy_term']['description'] = t('Taxonomy terms are used for classifying content.'); - $entity_info['taxonomy_term']['access callback'] = 'entity_metadata_taxonomy_access'; - $entity_info['taxonomy_term']['creation callback'] = 'entity_metadata_create_object'; - $entity_info['taxonomy_term']['save callback'] = 'taxonomy_term_save'; - $entity_info['taxonomy_term']['deletion callback'] = 'taxonomy_term_delete'; - $entity_info['taxonomy_term']['view callback'] = 'entity_metadata_view_single'; - $entity_info['taxonomy_term']['form callback'] = 'entity_metadata_form_taxonomy_term'; - - $entity_info['taxonomy_vocabulary']['plural label'] = t('Taxonomy vocabularies'); - $entity_info['taxonomy_vocabulary']['description'] = t('Vocabularies contain related taxonomy terms, which are used for classifying content.'); - $entity_info['taxonomy_vocabulary']['access callback'] = 'entity_metadata_taxonomy_access'; - $entity_info['taxonomy_vocabulary']['creation callback'] = 'entity_metadata_create_object'; - $entity_info['taxonomy_vocabulary']['save callback'] = 'taxonomy_vocabulary_save'; - $entity_info['taxonomy_vocabulary']['deletion callback'] = 'taxonomy_vocabulary_delete'; - $entity_info['taxonomy_vocabulary']['form callback'] = 'entity_metadata_form_taxonomy_vocabulary'; - // Token type mapping. - $entity_info['taxonomy_term']['token type'] = 'term'; - $entity_info['taxonomy_vocabulary']['token type'] = 'vocabulary'; - } -} - -/** - * Implements hook_ctools_plugin_directory(). - */ -function entity_ctools_plugin_directory($module, $plugin) { - if ($module == 'ctools') { - return "ctools/$plugin"; - } -} diff --git a/entity.rules.inc b/entity.rules.inc deleted file mode 100644 index c3ec8dc..0000000 --- a/entity.rules.inc +++ /dev/null @@ -1,118 +0,0 @@ -type = $type; - $this->info = entity_get_info($type); - } - - public function eventInfo() { - $info = $this->info; - $type = $this->type; - - $label = $info['label']; - $defaults = array( - 'module' => isset($info['module']) ? $info['module'] : 'entity', - 'group' => $label, - 'access callback' => 'entity_rules_integration_event_access', - ); - - $items[$type . '_insert'] = $defaults + array( - 'label' => t('After saving a new @entity', array('@entity' => drupal_strtolower($label))), - 'variables' => entity_rules_events_variables($type, t('created @entity', array('@entity' => drupal_strtolower($label)))), - ); - $items[$type . '_update'] = $defaults + array( - 'label' => t('After updating an existing @entity', array('@entity' => drupal_strtolower($label))), - 'variables' => entity_rules_events_variables($type, t('updated @entity', array('@entity' => drupal_strtolower($label))), TRUE), - ); - $items[$type . '_presave'] = $defaults + array( - 'label' => t('Before saving a @entity', array('@entity' => drupal_strtolower($label))), - 'variables' => entity_rules_events_variables($type, t('saved @entity', array('@entity' => drupal_strtolower($label))), TRUE), - ); - $items[$type . '_delete'] = $defaults + array( - 'label' => t('After deleting a @entity', array('@entity' => drupal_strtolower($label))), - 'variables' => entity_rules_events_variables($type, t('deleted @entity', array('@entity' => drupal_strtolower($label)))), - ); - if (count($info['view modes'])) { - $items[$type . '_view'] = $defaults + array( - 'label' => t('@entity is viewed', array('@entity' => $label)), - 'variables' => entity_rules_events_variables($type, t('viewed @entity', array('@entity' => drupal_strtolower($label)))) + array( - 'view_mode' => array( - 'type' => 'text', - 'label' => t('view mode'), - 'options list' => 'rules_get_entity_view_modes', - // Add the entity-type for the options list callback. - 'options list entity type' => $type, - ), - ), - ); - } - // Specify that on presave the entity is saved anyway. - $items[$type . '_presave']['variables'][$type]['skip save'] = TRUE; - return $items; - } - -} - -/** - * Returns some parameter info suiting for the specified entity type. - */ -function entity_rules_events_variables($type, $label, $update = FALSE) { - $args = array( - $type => array('type' => $type, 'label' => $label), - ); - if ($update) { - $args += array( - $type . '_unchanged' => array( - 'type' => $type, - 'label' => t('unchanged entity'), - 'handler' => 'rules_events_entity_unchanged', - ), - ); - } - return $args; -} - -/** - * Implements hook_rules_event_info(). - */ -function entity_rules_event_info() { - $items = array(); - foreach (entity_crud_get_info() as $type => $info) { - // By default we enable the controller only for non-configuration. - $configuration = !empty($info['configuration']) || !empty($info['exportable']); - $info += array('rules controller class' => $configuration ? FALSE : 'EntityDefaultRulesController'); - if ($info['rules controller class']) { - $controller = new $info['rules controller class']($type); - $items += $controller->eventInfo(); - } - } - return $items; -} - -/** - * Rules integration access callback. - */ -function entity_rules_integration_event_access($type, $event_name) { - // Cut of _insert/_update/.. from the event name. - $entity_type = substr($event_name, 0, strrpos($event_name, '_')); - - $result = entity_access('view', $entity_type); - // If no access callback is given, just grant access for viewing. - return isset($result) ? $result : TRUE; -} diff --git a/entity.test b/entity.test deleted file mode 100644 index fd8cea1..0000000 --- a/entity.test +++ /dev/null @@ -1,2123 +0,0 @@ - $this->randomName(), - 'machine_name' => drupal_strtolower($this->randomName()), - 'description' => $this->randomName(), - )); - entity_save('taxonomy_vocabulary', $vocab); - return $vocab; - } - - /** - * Creates a random file of the given type. - */ - protected function createFile($file_type = 'text') { - // Create a managed file. - $file = current($this->drupalGetTestFiles($file_type)); - - // Set additional file properties and save it. - $file->filemime = file_get_mimetype($file->filename); - $file->uid = 1; - $file->timestamp = REQUEST_TIME; - $file->filesize = filesize($file->uri); - $file->status = 0; - file_save($file); - return $file; - } -} - -/** - * Test basic API. - */ -class EntityAPITestCase extends EntityWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Entity CRUD', - 'description' => 'Tests basic CRUD API functionality.', - 'group' => 'Entity API', - ); - } - - function setUp() { - parent::setUp('entity', 'entity_test'); - } - - /** - * Tests CRUD. - */ - function testCRUD() { - module_enable(array('entity_feature')); - - $user1 = $this->drupalCreateUser(); - // Create test entities for the user1 and unrelated to a user. - $entity = entity_create('entity_test', array('name' => 'test', 'uid' => $user1->uid)); - $entity->save(); - $entity = entity_create('entity_test', array('name' => 'test2', 'uid' => $user1->uid)); - $entity->save(); - $entity = entity_create('entity_test', array('name' => 'test', 'uid' => NULL)); - $entity->save(); - - $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test'))); - - $this->assertEqual($entities[0]->name, 'test', 'Created and loaded entity.'); - $this->assertEqual($entities[1]->name, 'test', 'Created and loaded entity.'); - - $results = entity_test_load_multiple(array($entity->pid)); - $loaded = array_pop($results); - $this->assertEqual($loaded->pid, $entity->pid, 'Loaded the entity unrelated to a user.'); - - $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test2'))); - $entities[0]->delete(); - $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test2'))); - $this->assertEqual($entities, array(), 'Entity successfully deleted.'); - - $entity->save(); - $this->assertEqual($entity->pid, $loaded->pid, 'Entity successfully updated.'); - - // Try deleting multiple test entities by deleting all. - $pids = array_keys(entity_test_load_multiple(FALSE)); - entity_test_delete_multiple($pids); - } - - /** - * Tests CRUD for entities supporting revisions. - */ - function testCRUDRevisisions() { - module_enable(array('entity_feature')); - - // Add text field to entity. - $field_info = array( - 'field_name' => 'field_text', - 'type' => 'text', - 'entity_types' => array('entity_test2'), - ); - field_create_field($field_info); - - $instance = array( - 'label' => 'Text Field', - 'field_name' => 'field_text', - 'entity_type' => 'entity_test2', - 'bundle' => 'entity_test2', - 'settings' => array(), - 'required' => FALSE, - ); - field_create_instance($instance); - - // Create a test entity. - $entity_first_revision = entity_create('entity_test2', array('title' => 'first revision', 'name' => 'main', 'uid' => 1)); - $entity_first_revision->field_text[LANGUAGE_NONE][0]['value'] = 'first revision text'; - entity_save('entity_test2', $entity_first_revision); - - $entities = array_values(entity_load('entity_test2', FALSE)); - $this->assertEqual(count($entities), 1, 'Entity created.'); - $this->assertTrue($entities[0]->default_revision, 'Initial entity revision is marked as default revision.'); - - // Saving the entity in revision mode should create a new revision. - $entity_second_revision = clone $entity_first_revision; - $entity_second_revision->title = 'second revision'; - $entity_second_revision->is_new_revision = TRUE; - $entity_second_revision->default_revision = TRUE; - $entity_second_revision->field_text[LANGUAGE_NONE][0]['value'] = 'second revision text'; - - entity_save('entity_test2', $entity_second_revision); - $this->assertNotEqual($entity_second_revision->revision_id, $entity_first_revision->revision_id, 'Saving an entity in new revision mode creates a revision.'); - $this->assertTrue($entity_second_revision->default_revision, 'New entity revision is marked as default revision.'); - - // Check the saved entity. - $entity = current(entity_load('entity_test2', array($entity_first_revision->pid), array(), TRUE)); - $this->assertNotEqual($entity->title, $entity_first_revision->title, 'Default revision was changed.'); - - // Create third revision that is not default. - $entity_third_revision = clone $entity_first_revision; - $entity_third_revision->title = 'third revision'; - $entity_third_revision->is_new_revision = TRUE; - $entity_third_revision->default_revision = FALSE; - $entity_third_revision->field_text[LANGUAGE_NONE][0]['value'] = 'third revision text'; - entity_save('entity_test2', $entity_third_revision); - $this->assertNotEqual($entity_second_revision->revision_id, $entity_third_revision->revision_id, 'Saving an entity in revision mode creates a revision.'); - $this->assertFalse($entity_third_revision->default_revision, 'Entity revision is not marked as default revision.'); - - $entity = current(entity_load('entity_test2', array($entity_first_revision->pid), array(), TRUE)); - $this->assertEqual($entity->title, $entity_second_revision->title, 'Default revision was not changed.'); - $this->assertEqual($entity->field_text[LANGUAGE_NONE][0]['value'], $entity_second_revision->field_text[LANGUAGE_NONE][0]['value'], 'Default revision text field was not changed.'); - - // Load not default revision. - $revision = entity_revision_load('entity_test2', $entity_third_revision->revision_id); - $this->assertEqual($revision->revision_id, $entity_third_revision->revision_id, 'Revision successfully loaded.'); - $this->assertFalse($revision->default_revision, 'Entity revision is not marked as default revision after loading.'); - - // Save not default revision. - $entity_third_revision->title = 'third revision updated'; - $entity_third_revision->field_text[LANGUAGE_NONE][0]['value'] = 'third revision text updated'; - entity_save('entity_test2', $entity_third_revision); - - // Ensure that not default revision has been changed. - $entity = entity_revision_load('entity_test2', $entity_third_revision->revision_id); - $this->assertEqual($entity->title, 'third revision updated', 'Not default revision was updated successfully.'); - $this->assertEqual($entity->field_text[LANGUAGE_NONE][0]['value'], 'third revision text updated', 'Not default revision field was updated successfully.'); - - // Ensure that default revision has not been changed. - $entity = current(entity_load('entity_test2', array($entity_first_revision->pid), array(), TRUE)); - $this->assertEqual($entity->title, $entity_second_revision->title, 'Default revision was not changed.'); - - // Try to delete default revision. - $result = entity_revision_delete('entity_test2', $entity_second_revision->revision_id); - $this->assertFalse($result, 'Default revision cannot be deleted.'); - - // Make sure default revision is still set after trying to delete it. - $entity = current(entity_load('entity_test2', array($entity_first_revision->pid), array(), TRUE)); - $this->assertEqual($entity->revision_id, $entity_second_revision->revision_id, 'Second revision is still default.'); - - // Delete first revision. - $result = entity_revision_delete('entity_test2', $entity_first_revision->revision_id); - $this->assertTrue($result, 'Not default revision deleted.'); - - $entity = entity_revision_load('entity_test2', $entity_first_revision->revision_id); - $this->assertFalse($entity, 'First revision deleted.'); - - // Delete the entity and make sure third revision has been deleted as well. - entity_delete('entity_test2', $entity_second_revision->pid); - $entity_info = entity_get_info('entity_test2'); - $result = db_select($entity_info['revision table']) - ->condition('revision_id', $entity_third_revision->revision_id) - ->countQuery() - ->execute() - ->fetchField(); - $this->assertEqual($result, 0, 'Entity deleted with its all revisions.'); - } - - /** - * Tests CRUD API functions: entity_(create|delete|save) - */ - function testCRUDAPIfunctions() { - module_enable(array('entity_feature')); - - $user1 = $this->drupalCreateUser(); - // Create test entities for the user1 and unrelated to a user. - $entity = entity_create('entity_test', array('name' => 'test', 'uid' => $user1->uid)); - entity_save('entity_test', $entity); - $entity = entity_create('entity_test', array('name' => 'test2', 'uid' => $user1->uid)); - entity_save('entity_test', $entity); - $entity = entity_create('entity_test', array('name' => 'test', 'uid' => NULL)); - entity_save('entity_test', $entity); - - $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test'))); - $this->assertEqual($entities[0]->name, 'test', 'Created and loaded entity.'); - $this->assertEqual($entities[1]->name, 'test', 'Created and loaded entity.'); - - // Test getting the entity label, which is the used test-type's label. - $label = entity_label('entity_test', $entities[0]); - $this->assertEqual($label, 'label', 'Default label returned.'); - - $results = entity_test_load_multiple(array($entity->pid)); - $loaded = array_pop($results); - $this->assertEqual($loaded->pid, $entity->pid, 'Loaded the entity unrelated to a user.'); - - $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test2'))); - - entity_delete('entity_test', $entities[0]->pid); - $entities = array_values(entity_test_load_multiple(FALSE, array('name' => 'test2'))); - $this->assertEqual($entities, array(), 'Entity successfully deleted.'); - - entity_save('entity_test', $entity); - $this->assertEqual($entity->pid, $loaded->pid, 'Entity successfully updated.'); - - // Try deleting multiple test entities by deleting all. - $pids = array_keys(entity_test_load_multiple(FALSE)); - entity_delete_multiple('entity_test', $pids); - } - - /** - * Test loading entities defined in code. - */ - function testExportables() { - module_enable(array('entity_feature')); - - $types = entity_load_multiple_by_name('entity_test_type', array('test2', 'test')); - - $this->assertEqual(array_keys($types), array('test2', 'test'), 'Entities have been loaded in the order as specified.'); - $this->assertEqual($types['test']->label, 'label', 'Default type loaded.'); - $this->assertTrue($types['test']->status & ENTITY_IN_CODE && !($types['test']->status & ENTITY_CUSTOM), 'Default type status is correct.'); - - // Test using a condition, which has to be applied on the defaults. - $types = entity_load_multiple_by_name('entity_test_type', FALSE, array('label' => 'label')); - $this->assertEqual($types['test']->label, 'label', 'Condition to default type applied.'); - - $types['test']->label = 'modified'; - $types['test']->save(); - - // Ensure loading the changed entity works. - $types = entity_load_multiple_by_name('entity_test_type', FALSE, array('label' => 'modified')); - $this->assertEqual($types['test']->label, 'modified', 'Modified type loaded.'); - - // Clear the cache to simulate a new page load. - entity_get_controller('entity_test_type')->resetCache(); - - // Test loading using a condition again, now they default may not appear any - // more as it's overridden by an entity with another label. - $types = entity_load_multiple_by_name('entity_test_type', FALSE, array('label' => 'label')); - $this->assertTrue(empty($types), 'Conditions are applied to the overridden entity only.'); - - // But the overridden entity has to appear with another condition. - $types = entity_load_multiple_by_name('entity_test_type', FALSE, array('label' => 'modified')); - $this->assertEqual($types['test']->label, 'modified', 'Modified default type loaded by condition.'); - - $types = entity_load_multiple_by_name('entity_test_type', array('test', 'test2')); - $this->assertEqual($types['test']->label, 'modified', 'Modified default type loaded by id.'); - $this->assertTrue(entity_has_status('entity_test_type', $types['test'], ENTITY_OVERRIDDEN), 'Status of overridden type is correct.'); - - // Test rebuilding the defaults and make sure overridden entities stay. - entity_defaults_rebuild(); - $types = entity_load_multiple_by_name('entity_test_type', array('test', 'test2')); - $this->assertEqual($types['test']->label, 'modified', 'Overridden entity is still overridden.'); - $this->assertTrue(entity_has_status('entity_test_type', $types['test'], ENTITY_OVERRIDDEN), 'Status of overridden type is correct.'); - - // Test reverting. - $types['test']->delete(); - $types = entity_load_multiple_by_name('entity_test_type', array('test', 'test2')); - $this->assertEqual($types['test']->label, 'label', 'Entity has been reverted.'); - - // Test loading an exportable by its numeric id. - $result = entity_load_multiple_by_name('entity_test_type', array($types['test']->id)); - $this->assertTrue(isset($result['test']), 'Exportable entity loaded by the numeric id.'); - - // Test exporting an entity to JSON. - $serialized_string = $types['test']->export(); - $data = drupal_json_decode($serialized_string); - $this->assertNotNull($data, 'Exported entity is valid JSON.'); - $import = entity_import('entity_test_type', $serialized_string); - $this->assertTrue(get_class($import) == get_class($types['test']) && $types['test']->label == $import->label, 'Successfully exported entity to code.'); - $this->assertTrue(!isset($import->status), 'Exportable status has not been exported to code.'); - - // Test disabling the module providing the defaults in code. - $types = entity_load_multiple_by_name('entity_test_type', array('test', 'test2')); - $types['test']->label = 'modified'; - $types['test']->save(); - - module_disable(array('entity_feature')); - - // Make sure the overridden entity stays and the other one is deleted. - entity_get_controller('entity_test_type')->resetCache(); - $test = entity_load_single('entity_test_type', 'test'); - $this->assertTrue(!empty($test) && $test->label == 'modified', 'Overidden entity is still available.'); - $this->assertTrue(!empty($test) && !entity_has_status('entity_test_type', $test, ENTITY_IN_CODE) && entity_has_status('entity_test_type', $test, ENTITY_CUSTOM), 'Overidden entity is now marked as custom.'); - - $test2 = entity_load_single('entity_test_type', 'test2'); - $this->assertFalse($test2, 'Default entity has disappeared.'); - } - - /** - * Make sure insert() and update() hooks for exportables are invoked. - */ - function testExportableHooks() { - $_SESSION['entity_hook_test'] = array(); - // Enabling the module should invoke the enabled hook for the other - // entities provided in code. - module_enable(array('entity_feature')); - - $insert = array('main', 'test', 'test2'); - $this->assertTrue($_SESSION['entity_hook_test']['entity_insert'] == $insert, 'Hook entity_insert has been invoked.'); - $this->assertTrue($_SESSION['entity_hook_test']['entity_test_type_insert'] == $insert, 'Hook entity_test_type_insert has been invoked.'); - - // Load a default entity and make sure the rebuilt logic only ran once. - entity_load_single('entity_test_type', 'test'); - $this->assertTrue(!isset($_SESSION['entity_hook_test']['entity_test_type_update']), '"Entity-test-type" defaults have been rebuilt only once.'); - - // Add a new test entity in DB and make sure the hook is invoked too. - $test3 = entity_create('entity_test_type', array( - 'name' => 'test3', - 'label' => 'label', - 'weight' => 0, - )); - $test3->save(); - - $insert[] = 'test3'; - $this->assertTrue($_SESSION['entity_hook_test']['entity_insert'] == $insert, 'Hook entity_insert has been invoked.'); - $this->assertTrue($_SESSION['entity_hook_test']['entity_test_type_insert'] == $insert, 'Hook entity_test_type_insert has been invoked.'); - - // Now override the 'test' entity and make sure it invokes the update hook. - $result = entity_load_multiple_by_name('entity_test_type', array('test')); - $result['test']->label = 'modified'; - $result['test']->save(); - - $this->assertTrue($_SESSION['entity_hook_test']['entity_update'] == array('test'), 'Hook entity_update has been invoked.'); - $this->assertTrue($_SESSION['entity_hook_test']['entity_test_type_update'] == array('test'), 'Hook entity_test_type_update has been invoked.'); - - // 'test' has to remain enabled, as it has been overridden. - $delete = array('main', 'test2'); - module_disable(array('entity_feature')); - - $this->assertTrue($_SESSION['entity_hook_test']['entity_delete'] == $delete, 'Hook entity_deleted has been invoked.'); - $this->assertTrue($_SESSION['entity_hook_test']['entity_test_type_delete'] == $delete, 'Hook entity_test_type_deleted has been invoked.'); - - // Now make sure 'test' is not overridden any more, but custom. - $result = entity_load_multiple_by_name('entity_test_type', array('test')); - $this->assertTrue(!$result['test']->hasStatus(ENTITY_OVERRIDDEN), 'Entity is not marked as overridden any more.'); - $this->assertTrue(entity_has_status('entity_test_type', $result['test'], ENTITY_CUSTOM), 'Entity is marked as custom.'); - - // Test deleting the remaining entities from DB. - entity_delete_multiple('entity_test_type', array('test', 'test3')); - $delete[] = 'test'; - $delete[] = 'test3'; - $this->assertTrue($_SESSION['entity_hook_test']['entity_delete'] == $delete, 'Hook entity_deleted has been invoked.'); - $this->assertTrue($_SESSION['entity_hook_test']['entity_test_type_delete'] == $delete, 'Hook entity_test_type_deleted has been invoked.'); - } - - /** - * Tests determining changes. - */ - function testChanges() { - module_enable(array('entity_feature')); - $types = entity_load_multiple_by_name('entity_test_type'); - - // Override the default entity, such it gets saved in the DB. - $types['test']->label ='test_changes'; - $types['test']->save(); - - // Now test an update without applying any changes. - $types['test']->save(); - $this->assertEqual($types['test']->label, 'test_changes', 'No changes have been determined.'); - - // Apply changes. - $types['test']->label = 'updated'; - $types['test']->save(); - - // The hook implementations entity_test_entity_test_type_presave() and - // entity_test_entity_test_type_update() determine changes and change the - // label. - $this->assertEqual($types['test']->label, 'updated_presave_update', 'Changes have been determined.'); - - // Test the static load cache to be cleared. - $types = entity_load_multiple_by_name('entity_test_type'); - $this->assertEqual($types['test']->label, 'updated_presave', 'Static cache has been cleared.'); - } - - - /** - * Tests viewing entites. - */ - function testRendering() { - module_enable(array('entity_feature')); - - $user1 = $this->drupalCreateUser(); - // Create test entities for the user1 and unrelated to a user. - $entity = entity_create('entity_test', array('name' => 'test', 'uid' => $user1->uid)); - - $render = $entity->view(); - $output = drupal_render($render); - // The entity class adds the user name to the output. Verify it is there. - $this->assertTrue(strpos($output, format_username($user1)) !== FALSE, 'Entity has been rendered'); - } - - /** - * Test uninstall of the entity_test module. - */ - function testUninstall() { - // Add a test type and add a field instance, uninstall, then re-install and - // make sure the field instance can be re-created. - $test_type = entity_create('entity_test_type', array( - 'name' => 'test', - 'label' => 'label', - 'weight' => 0, - )); - $test_type->save(); - - $field = array( - 'field_name' => 'field_test_fullname', - 'type' => 'text', - 'cardinality' => 1, - 'translatable' => FALSE, - ); - field_create_field($field); - - $instance = array( - 'entity_type' => 'entity_test', - 'field_name' => 'field_test_fullname', - 'bundle' => 'test', - 'label' => 'Full name', - 'description' => 'Specify your first and last name.', - 'widget' => array( - 'type' => 'text_textfield', - 'weight' => 0, - ), - ); - field_create_instance($instance); - - // Uninstallation has to remove all bundles, thus also field instances. - module_disable(array('entity_test')); - require_once DRUPAL_ROOT . '/includes/install.inc'; - drupal_uninstall_modules(array('entity_test')); - - // Make sure the instance has been deleted. - $instance_read = field_read_instance('entity_test', 'field_test_fullname', 'test', array('include_inactive' => 1)); - $this->assertFalse((bool) $instance_read, 'Field instance has been deleted.'); - - // Ensure re-creating the same instance now works. - module_enable(array('entity_test')); - $test_type = entity_create('entity_test_type', array( - 'name' => 'test', - 'label' => 'label', - 'weight' => 0, - )); - $test_type->save(); - field_create_field($field); - field_create_instance($instance); - - $instance_read = field_info_instance('entity_test', 'field_test_fullname', 'test'); - $this->assertTrue((bool) $instance_read, 'Field instance has been re-created.'); - } -} - -/** - * Test the generated Rules integration. - */ -class EntityAPIRulesIntegrationTestCase extends EntityWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Entity CRUD Rules integration', - 'description' => 'Tests the Rules integration provided by the Entity CRUD API.', - 'group' => 'Entity API', - 'dependencies' => array('rules'), - ); - } - - function setUp() { - parent::setUp('entity', 'entity_test', 'rules'); - // Make sure the logger is enabled so the debug log is saved. - variable_set('rules_debug_log', 1); - } - - /** - * Test the events. - */ - function testEvents() { - $rule = rules_reaction_rule(); - $rule->event('entity_test_presave'); - $rule->event('entity_test_insert'); - $rule->event('entity_test_update'); - $rule->event('entity_test_delete'); - $rule->action('drupal_message', array('message' => 'hello!')); - $rule->save(); - rules_clear_cache(TRUE); - - // Let the events occur. - $user1 = $this->drupalCreateUser(); - RulesLog::logger()->clear(); - - $entity = entity_create('entity_test', array('name' => 'test', 'uid' => $user1->uid)); - $entity->save(); - $entity->name = 'update'; - $entity->save(); - $entity->delete(); - - // Now there should have been 5 events, 2 times presave and once insert, - // update and delete. - $count = substr_count(RulesLog::logger()->render(), '0 ms Reacting on event'); - $this->assertTrue($count == 5, 'Events have been properly invoked.'); - RulesLog::logger()->checkLog(); - } -} - -/** - * Tests comments with node access. - */ -class EntityAPICommentNodeAccessTestCase extends CommentHelperCase { - - public static function getInfo() { - return array( - 'name' => 'Entity API comment node access', - 'description' => 'Test viewing comments on nodes with node access.', - 'group' => 'Entity API', - ); - } - - function setUp() { - DrupalWebTestCase::setUp('comment', 'entity', 'node_access_test'); - node_access_rebuild(); - - // Create test node and user with simple node access permission. The - // 'node test view' permission is implemented and granted by the - // node_access_test module. - $this->accessUser = $this->drupalCreateUser(array('access comments', 'post comments', 'edit own comments', 'node test view')); - $this->noAccessUser = $this->drupalCreateUser(array('administer comments')); - $this->node = $this->drupalCreateNode(array('type' => 'article', 'uid' => $this->accessUser->uid)); - } - - /** - * Tests comment access when node access is enabled. - */ - function testCommentNodeAccess() { - // Post comment. - $this->drupalLogin($this->accessUser); - $comment_text = $this->randomName(); - $comment = $this->postComment($this->node, $comment_text); - $comment_loaded = comment_load($comment->id); - $this->assertTrue($this->commentExists($comment), 'Comment found.'); - $this->drupalLogout(); - - // Check access to node and associated comment for access user. - $this->assertTrue(entity_access('view', 'node', $this->node, $this->accessUser), 'Access to view node was granted for access user'); - $this->assertTrue(entity_access('view', 'comment', $comment_loaded, $this->accessUser), 'Access to view comment was granted for access user'); - $this->assertTrue(entity_access('update', 'comment', $comment_loaded, $this->accessUser), 'Access to update comment was granted for access user'); - $this->assertFalse(entity_access('delete', 'comment', $comment_loaded, $this->accessUser), 'Access to delete comment was denied for access user'); - - // Check access to node and associated comment for no access user. - $this->assertFalse(entity_access('view', 'node', $this->node, $this->noAccessUser), 'Access to view node was denied for no access user'); - $this->assertFalse(entity_access('view', 'comment', $comment_loaded, $this->noAccessUser), 'Access to view comment was denied for no access user'); - $this->assertFalse(entity_access('update', 'comment', $comment_loaded, $this->noAccessUser), 'Access to update comment was denied for no access user'); - $this->assertFalse(entity_access('delete', 'comment', $comment_loaded, $this->noAccessUser), 'Access to delete comment was denied for no access user'); - } -} - -/** - * Test the i18n integration. - */ -class EntityAPIi18nItegrationTestCase extends EntityWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Entity CRUD i18n integration', - 'description' => 'Tests the i18n integration provided by the Entity CRUD API.', - 'group' => 'Entity API', - 'dependencies' => array('i18n_string'), - ); - } - - function setUp() { - parent::setUp('entity_test_i18n'); - $this->admin_user = $this->drupalCreateUser(array('bypass node access', 'administer nodes', 'administer languages', 'administer content types', 'administer blocks', 'access administration pages')); - $this->drupalLogin($this->admin_user); - $this->addLanguage('de'); - } - - /** - * Copied from i18n module (class Drupali18nTestCase). - * - * We cannot extend from Drupali18nTestCase as else the test-bot would die. - */ - public function addLanguage($language_code) { - // Check to make sure that language has not already been installed. - $this->drupalGet('admin/config/regional/language'); - - if (strpos($this->drupalGetContent(), 'enabled[' . $language_code . ']') === FALSE) { - // Doesn't have language installed so add it. - $edit = array(); - $edit['langcode'] = $language_code; - $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); - - // Make sure we are not using a stale list. - drupal_static_reset('language_list'); - $languages = language_list('language'); - $this->assertTrue(array_key_exists($language_code, $languages), t('Language was installed successfully.')); - - if (array_key_exists($language_code, $languages)) { - $this->assertRaw(t('The language %language has been created and can now be used. More information is available on the help screen.', array('%language' => $languages[$language_code]->name, '@locale-help' => url('admin/help/locale'))), t('Language has been created.')); - } - } - elseif ($this->xpath('//input[@type="checkbox" and @name=:name and @checked="checked"]', array(':name' => 'enabled[' . $language_code . ']'))) { - // It's installed and enabled. No need to do anything. - $this->assertTrue(true, 'Language [' . $language_code . '] already installed and enabled.'); - } - else { - // It's installed but not enabled. Enable it. - $this->assertTrue(true, 'Language [' . $language_code . '] already installed.'); - $this->drupalPost(NULL, array('enabled[' . $language_code . ']' => TRUE), t('Save configuration')); - $this->assertRaw(t('Configuration saved.'), t('Language successfully enabled.')); - } - } - - /** - * Tests the provided default controller. - */ - function testDefaultController() { - // Create test entities for the user1 and unrelated to a user. - $entity = entity_create('entity_test_type', array( - 'name' => 'test', - 'uid' => $GLOBALS['user']->uid, - 'label' => 'label-en', - )); - $entity->save(); - - // Add a translation. - i18n_string_textgroup('entity_test')->update_translation("entity_test_type:{$entity->name}:label", 'de', 'label-de'); - - $default = entity_i18n_string("entity_test:entity_test_type:{$entity->name}:label", 'label-en'); - $translation = entity_i18n_string("entity_test:entity_test_type:{$entity->name}:label", 'label-en', 'de'); - - $this->assertEqual($translation, 'label-de', 'Label has been translated.'); - $this->assertEqual($default, 'label-en', 'Default label retrieved.'); - - // Test the helper method. - $translation = $entity->getTranslation('label', 'de'); - $default = $entity->getTranslation('label'); - $this->assertEqual($translation, 'label-de', 'Label has been translated via the helper method.'); - $this->assertEqual($default, 'label-en', 'Default label retrieved via the helper method.'); - - // Test updating and make sure the translation stays. - $entity->name = 'test2'; - $entity->save(); - $translation = $entity->getTranslation('label', 'de'); - $this->assertEqual($translation, 'label-de', 'Translation survives a name change.'); - - // Test using the wrapper to retrieve a translation. - $wrapper = entity_metadata_wrapper('entity_test_type', $entity); - $translation = $wrapper->language('de')->label->value(); - $this->assertEqual($translation, 'label-de', 'Translation retrieved via the wrapper.'); - - // Test deleting. - $entity->delete(); - $translation = entity_i18n_string("entity_test:entity_test_type:{$entity->name}:label", 'label-en', 'de'); - $this->assertEqual($translation, 'label-en', 'Translation has been deleted.'); - } -} - -/** - * Tests metadata wrappers. - */ -class EntityMetadataTestCase extends EntityWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Metadata Wrapper', - 'description' => 'Makes sure metadata wrapper are working right.', - 'group' => 'Entity API', - ); - } - - function setUp() { - parent::setUp('entity', 'entity_test', 'locale'); - // Create a field having 4 values for testing multiple value support. - $this->field_name = drupal_strtolower($this->randomName() . '_field_name'); - $this->field = array('field_name' => $this->field_name, 'type' => 'text', 'cardinality' => 4); - $this->field = field_create_field($this->field); - $this->field_id = $this->field['id']; - $this->instance = array( - 'field_name' => $this->field_name, - 'entity_type' => 'node', - 'bundle' => 'page', - 'label' => $this->randomName() . '_label', - 'description' => $this->randomName() . '_description', - 'weight' => mt_rand(0, 127), - 'settings' => array( - 'text_processing' => FALSE, - ), - 'widget' => array( - 'type' => 'text_textfield', - 'label' => 'Test Field', - 'settings' => array( - 'size' => 64, - ) - ) - ); - field_create_instance($this->instance); - - // Make the body field and the node type 'page' translatable. - $field = field_info_field('body'); - $field['translatable'] = TRUE; - field_update_field($field); - variable_set('language_content_type_page', 1); - } - - /** - * Creates a user and a node, then tests getting the properties. - */ - function testEntityMetadataWrapper() { - $account = $this->drupalCreateUser(); - // For testing sanitizing give the user a malicious user name - $account = user_save($account, array('name' => 'BadName')); - $title = 'Is it bold?'; - $body[LANGUAGE_NONE][0] = array('value' => 'The body & nothing.', 'summary' => 'The body.'); - $node = $this->drupalCreateNode(array('uid' => $account->uid, 'name' => $account->name, 'body' => $body, 'title' => $title, 'summary' => '', 'type' => 'page')); - - // First test without sanitizing. - $wrapper = entity_metadata_wrapper('node', $node); - - $this->assertEqual('Is it bold?', $wrapper->title->value(), 'Getting a field value.'); - $this->assertEqual($node->title, $wrapper->title->raw(), 'Getting a raw property value.'); - - // Test chaining. - $this->assertEqual($account->mail, $wrapper->author->mail->value(), 'Testing chained usage.'); - $this->assertEqual($account->name, $wrapper->author->name->value(), 'Testing chained usage with callback and sanitizing.'); - - // Test sanitized output. - $options = array('sanitize' => TRUE); - $this->assertEqual(check_plain('Is it bold?'), $wrapper->title->value($options), 'Getting sanitized field.'); - $this->assertEqual(filter_xss($node->name), $wrapper->author->name->value($options), 'Getting sanitized property with getter callback.'); - - // Test getting an not existing property. - try { - echo $wrapper->dummy; - $this->fail('Getting an not existing property.'); - } - catch (EntityMetadataWrapperException $e) { - $this->pass('Getting an not existing property.'); - } - - // Test setting. - $wrapper->author = 0; - $this->assertEqual(0, $wrapper->author->uid->value(), 'Setting a property.'); - try { - $wrapper->url = 'dummy'; - $this->fail('Setting an unsupported property.'); - } - catch (EntityMetadataWrapperException $e) { - $this->pass('Setting an unsupported property.'); - } - - // Test value validation. - $this->assertFalse($wrapper->author->name->validate(array(3)), 'Validation correctly checks for valid data types.'); - try { - $wrapper->author->mail = 'foo'; - $this->fail('An invalid mail address has been set.'); - } - catch (EntityMetadataWrapperException $e) { - $this->pass('Setting an invalid mail address throws exception.'); - } - // Test unsetting a required property. - try { - $wrapper->author = NULL; - $this->fail('The required node author has been unset.'); - } - catch (EntityMetadataWrapperException $e) { - $this->pass('Unsetting the required node author throws an exception.'); - } - - // Test setting a referenced entity by id. - $wrapper->author->set($GLOBALS['user']->uid); - $this->assertEqual($wrapper->author->getIdentifier(), $GLOBALS['user']->uid, 'Get the identifier of a referenced entity.'); - $this->assertEqual($wrapper->author->uid->value(), $GLOBALS['user']->uid, 'Successfully set referenced entity using the identifier.'); - // Set by object. - $wrapper->author->set($GLOBALS['user']); - $this->assertEqual($wrapper->author->uid->value(), $GLOBALS['user']->uid, 'Successfully set referenced entity using the entity.'); - - - // Test getting by the field API processed values like the node body. - $body_value = $wrapper->body->value; - $this->assertEqual("

The body & nothing.

\n", $body_value->value(), "Getting processed value."); - $this->assertEqual("The body & nothing.\n", $body_value->value(array('decode' => TRUE)), "Decoded value."); - $this->assertEqual("The body & nothing.", $body_value->raw(), "Raw body returned."); - - // Test getting the summary. - $this->assertEqual("

The body.

\n", $wrapper->body->summary->value(), "Getting body summary."); - - $wrapper->body->set(array('value' => "The second body.")); - $this->assertEqual("

The second body.

\n", $wrapper->body->value->value(), "Setting a processed field value and reading it again."); - $this->assertEqual($node->body[LANGUAGE_NONE][0]['value'], "The second body.", 'Update appears in the wrapped entity.'); - $this->assert(isset($node->body[LANGUAGE_NONE][0]['safe_value']), 'Formatted text has been processed.'); - - // Test translating the body on an English node. - locale_add_language('de'); - $body['en'][0] = array('value' => 'English body.', 'summary' => 'The body.'); - $node = $this->drupalCreateNode(array('body' => $body, 'language' => 'en', 'type' => 'page')); - $wrapper = entity_metadata_wrapper('node', $node); - - $wrapper->language('de'); - - $languages = language_list(); - $this->assertEqual($wrapper->getPropertyLanguage(), $languages['de'], 'Wrapper language has been set to German'); - $this->assertEqual($wrapper->body->value->value(), "

English body.

\n", 'Language fallback on default language.'); - - // Set a German text using the wrapper. - $wrapper->body->set(array('value' => "Der zweite Text.")); - $this->assertEqual($wrapper->body->value->value(), "

Der zweite Text.

\n", 'German body set and retrieved.'); - - $wrapper->language(LANGUAGE_NONE); - $this->assertEqual($wrapper->body->value->value(), "

English body.

\n", 'Default language text is still there.'); - - // Test iterator. - $type_info = entity_get_property_info('node'); - $this->assertFalse(array_diff_key($type_info['properties'], iterator_to_array($wrapper->getIterator())), 'Iterator is working.'); - foreach ($wrapper as $property) { - $this->assertTrue($property instanceof EntityMetadataWrapper, 'Iterate over wrapper properties.'); - } - - // Test setting a new node. - $node->title = 'foo'; - $wrapper->set($node); - $this->assertEqual($wrapper->title->value(), 'foo', 'Changed the wrapped node.'); - - // Test getting options lists. - $this->assertEqual($wrapper->type->optionsList(), node_type_get_names(), 'Options list returned.'); - - // Test making use of a generic 'entity' reference property the - // 'entity_test' module provides. The property defaults to the node author. - $this->assertEqual($wrapper->reference->uid->value(), $wrapper->author->getIdentifier(), 'Used generic entity reference property.'); - // Test updating a property of the generic entity reference. - $wrapper->reference->name->set('foo'); - $this->assertEqual($wrapper->reference->name->value(), 'foo', 'Updated property of generic entity reference'); - // For testing, just point the reference to the node itself now. - $wrapper->reference->set($wrapper); - $this->assertEqual($wrapper->reference->nid->value(), $wrapper->getIdentifier(), 'Correctly updated the generic entity referenced property.'); - - // Test saving and deleting. - $wrapper->save(); - $wrapper->delete(); - $return = node_load($wrapper->getIdentifier()); - $this->assertFalse($return, "Node has been successfully deleted."); - - // Ensure changing the bundle changes available wrapper properties. - $wrapper->type->set('article'); - $this->assertTrue(isset($wrapper->field_tags), 'Changing bundle changes available wrapper properties.'); - - // Test labels. - $user = $this->drupalCreateUser(); - user_save($user, array('roles' => array())); - $wrapper->author = $user->uid; - $this->assertEqual($wrapper->label(), $node->title, 'Entity label returned.'); - $this->assertEqual($wrapper->author->roles[0]->label(), t('authenticated user'), 'Label from options list returned'); - $this->assertEqual($wrapper->author->roles->label(), t('authenticated user'), 'Label for a list from options list returned'); - } - - /** - * Test supporting multi-valued fields. - */ - function testListMetadataWrappers() { - $property = $this->field_name; - $values = array(); - $values[LANGUAGE_NONE][0] = array('value' => '2009-09-05'); - $values[LANGUAGE_NONE][1] = array('value' => '2009-09-05'); - $values[LANGUAGE_NONE][2] = array('value' => '2009-08-05'); - - $node = $this->drupalCreateNode(array('type' => 'page', $property => $values)); - $wrapper = entity_metadata_wrapper('node', $node); - - $this->assertEqual('2009-09-05', $wrapper->{$property}[0]->value(), 'Getting array entry.'); - $this->assertEqual('2009-09-05', $wrapper->{$property}->get(1)->value(), 'Getting array entry.'); - $this->assertEqual(3, count($wrapper->{$property}->value()), 'Getting the whole array.'); - - // Test sanitizing - $this->assertEqual(check_plain('2009-09-05'), $wrapper->{$property}[0]->value(array('sanitize' => TRUE)), 'Getting array entry.'); - - // Test iterator - $this->assertEqual(array_keys(iterator_to_array($wrapper->$property->getIterator())), array_keys($wrapper->$property->value()), 'Iterator is working.'); - foreach ($wrapper->$property as $p) { - $this->assertTrue($p instanceof EntityMetadataWrapper, 'Iterate over list wrapper properties.'); - } - - // Make sure changing the array changes the actual entity property. - $wrapper->{$property}[0] = '2009-10-05'; - unset($wrapper->{$property}[1], $wrapper->{$property}[2]); - $this->assertEqual($wrapper->{$property}->value(), array('2009-10-05'), 'Setting multiple property values.'); - - // Test setting an arbitrary list item. - $list = array(0 => REQUEST_TIME); - $wrapper = entity_metadata_wrapper('list', $list); - $wrapper[1] = strtotime('2009-09-05'); - $this->assertEqual($wrapper->value(), array(REQUEST_TIME, strtotime('2009-09-05')), 'Setting a list item.'); - $this->assertEqual($wrapper->count(), 2, 'List count is correct.'); - - // Test using a list wrapper without data. - $wrapper = entity_metadata_wrapper('list'); - $info = array(); - foreach ($wrapper as $item) { - $info[] = $item->info(); - } - $this->assertTrue($info[0]['type'] == 'date', 'Iterated over empty list wrapper.'); - - // Test using a list of entities with a list of term objects. - $list = array(); - $list[] = entity_property_values_create_entity('taxonomy_term', array( - 'name' => 'term 1', - 'vocabulary' => 1, - ))->save()->value(); - $list[] = entity_property_values_create_entity('taxonomy_term', array( - 'name' => 'term 2', - 'vocabulary' => 1, - ))->save()->value(); - $wrapper = entity_metadata_wrapper('list', $list); - $this->assertTrue($wrapper[0]->name->value() == 'term 1', 'Used a list of entities.'); - // Test getting a list of identifiers. - $ids = $wrapper->value(array('identifier' => TRUE)); - $this->assertTrue(!is_object($ids[0]), 'Get a list of entity ids.'); - - $wrapper = entity_metadata_wrapper('list', $ids); - $this->assertTrue($wrapper[0]->name->value() == 'term 1', 'Created a list of entities with ids.'); - - // Test with a list of generic entities. The list is expected to be a list - // of entity wrappers, otherwise the entity type is unknown. - $node = $this->drupalCreateNode(array('title' => 'node 1')); - $list = array(); - $list[] = entity_metadata_wrapper('node', $node); - $wrapper = entity_metadata_wrapper('list', $list); - $this->assertEqual($wrapper[0]->title->value(), 'node 1', 'Wrapped node was found in generic list of entities.'); - } - - /** - * Tests using the wrapper without any data. - */ - function testWithoutData() { - $wrapper = entity_metadata_wrapper('node', NULL, array('bundle' => 'page')); - $this->assertTrue(isset($wrapper->title), 'Bundle properties have been added.'); - $info = $wrapper->author->mail->info(); - $this->assertTrue(!empty($info) && is_array($info) && isset($info['label']), 'Property info returned.'); - } - - /** - * Test using access() method. - */ - function testAccess() { - // Test without data. - $account = $this->drupalCreateUser(array('bypass node access')); - $this->assertTrue(entity_access('view', 'node', NULL, $account), 'Access without data checked.'); - - // Test with actual data. - $values[LANGUAGE_NONE][0] = array('value' => '2009-09-05'); - $values[LANGUAGE_NONE][1] = array('value' => '2009-09-05'); - $node = $this->drupalCreateNode(array('type' => 'page', $this->field_name => $values)); - $this->assertTrue(entity_access('delete', 'node', $node, $account), 'Access with data checked.'); - - // Test per property access without data. - $account2 = $this->drupalCreateUser(array('bypass node access', 'administer nodes')); - $wrapper = entity_metadata_wrapper('node', NULL, array('bundle' => 'page')); - $this->assertTrue($wrapper->access('edit', $account), 'Access to node granted.'); - $this->assertFalse($wrapper->status->access('edit', $account), 'Access for admin property denied.'); - $this->assertTrue($wrapper->status->access('edit', $account2), 'Access for admin property allowed for the admin.'); - - // Test per property access with data. - $wrapper = entity_metadata_wrapper('node', $node, array('bundle' => 'page')); - $this->assertFalse($wrapper->status->access('edit', $account), 'Access for admin property denied.'); - $this->assertTrue($wrapper->status->access('edit', $account2), 'Access for admin property allowed for the admin.'); - - // Test field level access. - $this->assertTrue($wrapper->{$this->field_name}->access('view'), 'Field access granted.'); - - // Create node owned by anonymous and test access() method on each of its - // properties. - $node = $this->drupalCreateNode(array('type' => 'page', 'uid' => 0)); - $wrapper = entity_metadata_wrapper('node', $node->nid); - foreach ($wrapper as $name => $property) { - $property->access('view'); - } - - // Property access of entity references takes entity access into account. - $node = $this->drupalCreateNode(array('type' => 'article')); - $wrapper = entity_metadata_wrapper('node', $node); - $unprivileged_user = $this->drupalCreateUser(); - $privileged_user = $this->drupalCreateUser(array('access user profiles')); - - $this->assertTrue($wrapper->author->access('view', $privileged_user)); - $this->assertFalse($wrapper->author->access('view', $unprivileged_user)); - - // Ensure the same works with multiple entity references by testing the - // $node->field_tags example. - $privileged_user = $this->drupalCreateUser(array('administer taxonomy')); - // Terms are view-able with access content, so make sure to remove this - // permission first. - user_role_revoke_permissions(DRUPAL_ANONYMOUS_RID, array('access content')); - $unprivileged_user = drupal_anonymous_user(); - - $this->assertTrue($wrapper->field_tags->access('view', $privileged_user), 'Privileged user has access.'); - $this->assertTrue($wrapper->field_tags->access('view', $unprivileged_user), 'Unprivileged user has access.'); - $this->assertTrue($wrapper->field_tags[0]->access('view', $privileged_user), 'Privileged user has access.'); - $this->assertFalse($wrapper->field_tags[0]->access('view', $unprivileged_user), 'Unprivileged user has no access.'); - } - - /** - * Tests using a data structure with passed in metadata. - */ - function testDataStructureWrapper() { - $log_entry = array( - 'type' => 'entity', - 'message' => $this->randomName(8), - 'variables' => array(), - 'severity' => WATCHDOG_NOTICE, - 'link' => '', - 'user' => $GLOBALS['user'], - ); - $info['property info'] = array( - 'type' => array('type' => 'text', 'label' => 'The category to which this message belongs.'), - 'message' => array('type' => 'text', 'label' => 'The log message.'), - 'user' => array('type' => 'user', 'label' => 'The user causing the log entry.'), - ); - $wrapper = entity_metadata_wrapper('log_entry', $log_entry, $info); - $this->assertEqual($wrapper->user->name->value(), $GLOBALS['user']->name, 'Wrapped custom entity.'); - } - - /** - * Tests using entity_property_query(). - */ - function testEntityQuery() { - // Creat a test node. - $title = 'Is it bold?'; - $values[LANGUAGE_NONE][0] = array('value' => 'foo'); - $node = $this->drupalCreateNode(array($this->field_name => $values, 'title' => $title, 'uid' => $GLOBALS['user']->uid)); - - $results = entity_property_query('node', 'title', $title); - $this->assertEqual($results, array($node->nid), 'Queried nodes with a given title.'); - - $results = entity_property_query('node', $this->field_name, 'foo'); - $this->assertEqual($results, array($node->nid), 'Queried nodes with a given field value.'); - - $results = entity_property_query('node', $this->field_name, array('foo', 'bar')); - $this->assertEqual($results, array($node->nid), 'Queried nodes with a list of possible values.'); - - $results = entity_property_query('node', 'author', $GLOBALS['user']); - $this->assertEqual($results, array($node->nid), 'Queried nodes with a given auhtor.'); - - // Create another test node and try querying for tags. - $tag = entity_property_values_create_entity('taxonomy_term', array( - 'name' => $this->randomName(), - 'vocabulary' => 1, - ))->save(); - $field_tag_value[LANGUAGE_NONE][0]['tid'] = $tag->getIdentifier(); - $node = $this->drupalCreateNode(array('type' => 'article', 'field_tags' => $field_tag_value)); - - // Try query-ing with a single value. - $results = entity_property_query('node', 'field_tags', $tag->getIdentifier()); - $this->assertEqual($results, array($node->nid), 'Queried nodes with a given term id.'); - - $results = entity_property_query('node', 'field_tags', $tag->value()); - $this->assertEqual($results, array($node->nid), 'Queried nodes with a given term object.'); - - // Try query-ing with a list of possible values. - $results = entity_property_query('node', 'field_tags', array($tag->getIdentifier())); - $this->assertEqual($results, array($node->nid), 'Queried nodes with a list of term ids.'); - } - - /** - * Tests serializing data wrappers, in particular for EntityDrupalWrapper. - */ - function testWrapperSerialization() { - $node = $this->drupalCreateNode(); - $wrapper = entity_metadata_wrapper('node', $node); - $this->assertTrue($wrapper->value() == $node, 'Data correctly wrapped.'); - - // Test serializing and make sure only the node id is stored. - $this->assertTrue(strpos(serialize($wrapper), $node->title) === FALSE, 'Node has been correctly serialized.'); - $this->assertEqual(unserialize(serialize($wrapper))->title->value(), $node->title, 'Serializing works right.'); - - $wrapper2 = unserialize(serialize($wrapper)); - // Test serializing the unloaded wrapper. - $this->assertEqual(unserialize(serialize($wrapper2))->title->value(), $node->title, 'Serializing works right.'); - - // Test loading a not more existing node. - $s = serialize($wrapper2); - node_delete($node->nid); - $this->assertFalse(node_load($node->nid), 'Node deleted.'); - - $value = unserialize($s)->value(); - $this->assertNull($value, 'Tried to load not existing node.'); - } -} - -/** - * Tests basic entity_access() functionality for nodes. - * - * This code is a modified version of NodeAccessTestCase. - * - * @see NodeAccessTestCase - */ -class EntityMetadataNodeAccessTestCase extends EntityWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Entity Metadata Node Access', - 'description' => 'Test entity_access() for nodes', - 'group' => 'Entity API', - ); - } - - /** - * Asserts node_access() correctly grants or denies access. - */ - function assertNodeMetadataAccess($ops, $node, $account) { - foreach ($ops as $op => $result) { - $msg = t("entity_access() returns @result with operation '@op'.", array('@result' => $result ? 'TRUE' : 'FALSE', '@op' => $op)); - $access = entity_access($op, 'node', $node, $account); - $this->assertEqual($result, $access, $msg); - } - } - - function setUp() { - parent::setUp('entity', 'node'); - // Clear permissions for authenticated users. - db_delete('role_permission') - ->condition('rid', DRUPAL_AUTHENTICATED_RID) - ->execute(); - } - - /** - * Runs basic tests for entity_access() function. - */ - function testNodeMetadataAccess() { - // Author user. - $node_author_account = $this->drupalCreateUser(array()); - // Make a node object. - $settings = array( - 'uid' => $node_author_account->uid, - 'type' => 'page', - 'title' => 'Node ' . $this->randomName(32), - ); - $node = $this->drupalCreateNode($settings); - - // Ensures user without 'access content' permission can do nothing. - $web_user1 = $this->drupalCreateUser(array('create page content', 'edit any page content', 'delete any page content')); - $this->assertNodeMetadataAccess(array('create' => FALSE, 'view' => FALSE, 'update' => FALSE, 'delete' => FALSE), $node, $web_user1); - - // Ensures user with 'bypass node access' permission can do everything. - $web_user2 = $this->drupalCreateUser(array('bypass node access')); - $this->assertNodeMetadataAccess(array('create' => TRUE, 'view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $node, $web_user2); - - // User cannot 'view own unpublished content'. - $web_user3 = $this->drupalCreateUser(array('access content')); - // Create an unpublished node. - $settings = array('type' => 'page', 'status' => 0, 'uid' => $web_user3->uid); - $node_unpublished = $this->drupalCreateNode($settings); - $this->assertNodeMetadataAccess(array('view' => FALSE), $node_unpublished, $web_user3); - // User cannot create content without permission. - $this->assertNodeMetadataAccess(array('create' => FALSE), $node, $web_user3); - - // User can 'view own unpublished content', but another user cannot. - $web_user4 = $this->drupalCreateUser(array('access content', 'view own unpublished content')); - $web_user5 = $this->drupalCreateUser(array('access content', 'view own unpublished content')); - $node4 = $this->drupalCreateNode(array('status' => 0, 'uid' => $web_user4->uid)); - $this->assertNodeMetadataAccess(array('view' => TRUE, 'update' => FALSE), $node4, $web_user4); - $this->assertNodeMetadataAccess(array('view' => FALSE), $node4, $web_user5); - - // Tests the default access provided for a published node. - $node5 = $this->drupalCreateNode(); - $this->assertNodeMetadataAccess(array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE, 'create' => FALSE), $node5, $web_user3); - } -} - -/** - * Test user permissions for node creation. - */ -class EntityMetadataNodeCreateAccessTestCase extends EntityWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Entity Metadata Node Create Access', - 'description' => 'Test entity_access() for nodes', - 'group' => 'Entity API', - ); - } - - function setUp() { - parent::setUp('entity', 'node'); - } - - /** - * Addresses the special case of 'create' access for nodes. - */ - public function testNodeMetadataCreateAccess() { - // Create some users. One with super-powers, one with create perms, - // and one with no perms, and a different one to author the node. - $admin_account = $this->drupalCreateUser(array( - 'bypass node access', - )); - $creator_account = $this->drupalCreateUser(array( - 'create page content', - )); - $auth_only_account = $this->drupalCreateUser(array()); - $node_author_account = $this->drupalCreateUser(array()); - - // Make a node object with Entity API (contrib) - $settings = array( - 'uid' => $node_author_account->uid, - 'type' => 'page', - 'title' => $this->randomName(32), - 'body' => array(LANGUAGE_NONE => array(array($this->randomName(64)))), - ); - $node = entity_create('node', $settings); - - // Test the populated wrapper. - $wrapper = entity_metadata_wrapper('node', $node); - $this->assertTrue($wrapper->entityAccess('create', $admin_account), 'Create access allowed for ADMIN, for populated wrapper.'); - $this->assertTrue($wrapper->entityAccess('create', $creator_account), 'Create access allowed for CREATOR, for populated wrapper.'); - $this->assertFalse($wrapper->entityAccess('create', $auth_only_account), 'Create access denied for USER, for populated wrapper.'); - - // Test entity_acces(). - $this->assertTrue(entity_access('create', 'node', $node, $admin_account), 'Create access allowed for ADMIN, for entity_access().'); - $this->assertTrue(entity_access('create', 'node', $node, $creator_account), 'Create access allowed for CREATOR, for entity_access().'); - $this->assertFalse(entity_access('create', 'node', $node, $auth_only_account), 'Create access denied for USER, for entity_access().'); - } -} - -/** - * Tests user permissions for node revisions. - * - * Based almost entirely on NodeRevisionPermissionsTestCase. - */ -class EntityMetadataNodeRevisionAccessTestCase extends DrupalWebTestCase { - protected $node_revisions = array(); - protected $accounts = array(); - - // Map revision permission names to node revision access ops. - protected $map = array( - 'view' => 'view revisions', - 'update' => 'revert revisions', - 'delete' => 'delete revisions', - ); - - public static function getInfo() { - return array( - 'name' => 'Entity Metadata Node Revision Access', - 'description' => 'Tests user permissions for node revision operations.', - 'group' => 'Entity API', - ); - } - - function setUp() { - parent::setUp('entity', 'node'); - - // Create a node with several revisions. - $node = $this->drupalCreateNode(); - $this->node_revisions[] = $node; - - for ($i = 0; $i < 3; $i++) { - // Create a revision for the same nid and settings with a random log. - $revision = node_load($node->nid); - $revision->revision = 1; - $revision->log = $this->randomName(32); - node_save($revision); - $this->node_revisions[] = node_load($revision->nid); - } - - // Create three users, one with each revision permission. - foreach ($this->map as $op => $permission) { - // Create the user. - $account = $this->drupalCreateUser( - array( - 'access content', - 'edit any page content', - 'delete any page content', - $permission, - ) - ); - $account->op = $op; - $this->accounts[] = $account; - } - - // Create an admin account (returns TRUE for all revision permissions). - $admin_account = $this->drupalCreateUser(array('access content', 'administer nodes')); - $admin_account->is_admin = TRUE; - $this->accounts['admin'] = $admin_account; - - // Create a normal account (returns FALSE for all revision permissions). - $normal_account = $this->drupalCreateUser(); - $normal_account->op = FALSE; - $this->accounts[] = $normal_account; - } - - /** - * Tests the entity_access() function for revisions. - */ - function testNodeRevisionAccess() { - // $node_revisions[1] won't be the latest revision. - $revision = $this->node_revisions[1]; - - $parameters = array( - 'op' => array_keys($this->map), - 'account' => $this->accounts, - ); - - $permutations = $this->generatePermutations($parameters); - $entity_type = 'node'; - foreach ($permutations as $case) { - if (!empty($case['account']->is_admin) || $case['op'] == $case['account']->op) { - $access = entity_access($case['op'], $entity_type, $revision, $case['account']); - $this->assertTrue($access, "{$this->map[$case['op']]} granted on $entity_type."); - } - else { - $access = entity_access($case['op'], $entity_type, $revision, $case['account']); - $this->assertFalse($access, "{$this->map[$case['op']]} NOT granted on $entity_type."); - } - } - } -} - -/** - * Tests basic entity_access() functionality for taxonomy terms. - */ -class EntityMetadataTaxonomyAccessTestCase extends EntityWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Entity Metadata Taxonomy Access', - 'description' => 'Test entity_access() for taxonomy terms', - 'group' => 'Entity API', - ); - } - - /** - * Asserts entity_access() correctly grants or denies access. - */ - function assertTaxonomyMetadataAccess($ops, $term, $account) { - foreach ($ops as $op => $result) { - $msg = t("entity_access() returns @result with operation '@op'.", array('@result' => $result ? 'TRUE' : 'FALSE', '@op' => $op)); - $access = entity_access($op, 'taxonomy_term', $term, $account); - $this->assertEqual($result, $access, $msg); - } - } - - /** - * @inheritdoc - */ - function setUp() { - parent::setUp('entity', 'taxonomy'); - // Clear permissions for authenticated users. - db_delete('role_permission') - ->condition('rid', DRUPAL_AUTHENTICATED_RID) - ->execute(); - } - - /** - * Runs basic tests for entity_access() function. - */ - function testTaxonomyMetadataAccess() { - $vocab = $this->createVocabulary(); - $term = entity_property_values_create_entity('taxonomy_term', array( - 'name' => $this->randomName(), - 'vocabulary' => $vocab, - ))->save()->value(); - // Clear permissions static cache to get new taxonomy permissions. - drupal_static_reset('checkPermissions'); - - // Check assignment of view permissions. - $user1 = $this->drupalCreateUser(array('access content')); - $this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => TRUE, 'update' => FALSE, 'delete' => FALSE), $term, $user1); - - // Check assignment of edit permissions. - $user2 = $this->drupalCreateUser(array('edit terms in ' . $vocab->vid)); - $this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => FALSE, 'update' => TRUE, 'delete' => FALSE), $term, $user2); - - // Check assignment of delete permissions. - $user3 = $this->drupalCreateUser(array('delete terms in ' . $vocab->vid)); - $this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => FALSE, 'update' => FALSE, 'delete' => TRUE), $term, $user3); - - // Check assignment of view, edit, delete permissions. - $user4 = $this->drupalCreateUser(array('access content', 'edit terms in ' . $vocab->vid, 'delete terms in ' . $vocab->vid)); - $this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $term, $user4); - - // Check assignment of administration permissions. - $user5 = $this->drupalCreateUser(array('administer taxonomy')); - $this->assertTaxonomyMetadataAccess(array('create' => TRUE, 'view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $term, $user5); - } -} - -/** - * Tests provided entity property info of the core modules. - */ -class EntityTokenTestCase extends EntityWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Entity tokens', - 'description' => 'Tests provided tokens for entity properties.', - 'group' => 'Entity API', - ); - } - - function setUp() { - parent::setUp('entity_token'); - } - - /** - * Tests whether token support is basically working. - */ - function testTokenSupport() { - // Test basic tokens. - $node = $this->drupalCreateNode(array('sticky' => TRUE, 'promote' => FALSE)); - $text = "Sticky: [node:sticky] Promote: [node:promote] User: [site:current-user:name]"; - $true = t('true'); - $false = t('false'); - $user_name = $GLOBALS['user']->name; - $target = "Sticky: $true Promote: $false User: $user_name"; - $replace = token_replace($text, array('node' => $node)); - $this->assertEqual($replace, $target, 'Provided tokens basically work.'); - - // Test multiple-value tokens using the tags field of articles. - for ($i = 0; $i < 4; $i++) { - $tags[$i] = entity_property_values_create_entity('taxonomy_term', array( - 'name' => $this->randomName(), - 'vocabulary' => 1, - ))->save(); - $field_value[LANGUAGE_NONE][$i]['tid'] = $tags[$i]->getIdentifier(); - $labels[$i] = $tags[$i]->label(); - } - $node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article', 'field_tags' => $field_value)); - - $text = "Tags: [node:field-tags] First: [node:field-tags:0] 2nd name: [node:field-tags:1:name] 1st vocab [node:field-tags:0:vocabulary]"; - $tag_labels = implode(', ', $labels); - $target = "Tags: $tag_labels First: $labels[0] 2nd name: $labels[1] 1st vocab {$tags[0]->vocabulary->label()}"; - $replace = token_replace($text, array('node' => $node)); - $this->assertEqual($replace, $target, 'Multiple-value token replacements have been replaced.'); - - // Make sure not existing values are not handled. - $replace = token_replace("[node:field-tags:43]", array('node' => $node)); - $this->assertEqual($replace, "[node:field-tags:43]", 'Not existing values are not replaced.'); - - // Test data-structure tokens like [site:current-page:url]. - $replace = token_replace("[site:current-page:url]", array()); - $this->assertEqual($replace, $GLOBALS['base_root'] . request_uri(), 'Token replacements of data structure properties replaced.'); - - // Test chaining of data-structure tokens using an image-field. - $file = $this->createFile('image'); - $node = $this->drupalCreateNode(array('type' => 'article')); - $wrapper = entity_metadata_wrapper('node', $node); - - $wrapper->field_image = array('fid' => $file->fid); - $replace = token_replace("[node:field-image:file:name]", array('node' => $node)); - $this->assertEqual($replace, $wrapper->field_image->file->name->value(), 'Token replacements of an image field have been replaced.'); - } -} - -/** - * Tests provided entity property info of the core modules. - */ -class EntityMetadataIntegrationTestCase extends EntityWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Property info core integration', - 'description' => 'Tests using metadata wrapper for drupal core.', - 'group' => 'Entity API', - ); - } - - function setUp() { - parent::setUp('entity', 'book', 'statistics', 'locale'); - } - - protected function assertException($wrapper, $name, $text = NULL) { - $this->assertTrue(isset($wrapper->$name), 'Property wrapper ' . check_plain($name) . ' exists.'); - $text = isset($text) ? $text : 'Getting the not existing property ' . $name . ' throws exception.'; - try { - $wrapper->$name->value(); - $this->fail($text); - } - catch (EntityMetadataWrapperException $e) { - $this->pass($text); - } - } - - protected function assertEmpty($wrapper, $name) { - $this->assertTrue(isset($wrapper->$name), 'Property ' . check_plain($name) . ' exists.'); - $this->assertTrue($wrapper->$name->value() === NULL, 'Property ' . check_plain($name) . ' is empty.'); - } - - protected function assertEmptyArray($wrapper, $name) { - $this->assertTrue(isset($wrapper->$name), 'Property ' . check_plain($name) . ' exists.'); - $this->assertTrue($wrapper->$name->value() === array(), 'Property ' . check_plain($name) . ' is an empty array.'); - } - - protected function assertValue($wrapper, $key) { - $this->assertTrue($wrapper->$key->value() !== NULL, check_plain($key) . ' property returned.'); - $info = $wrapper->$key->info(); - if (!empty($info['raw getter callback'])) { - // Also test getting the raw value - $this->assertTrue($wrapper->$key->raw() !== NULL, check_plain($key) . ' raw value returned.'); - } - } - - /** - * Test book module integration. - */ - function testBookModule() { - $title = 'Book 1'; - $node = $this->drupalCreateNode(array('title' => $title, 'type' => 'book', 'book' => array('bid' => 'new'))); - $book = array('bid' => $node->nid, 'plid' => $node->book['mlid']); - $node2 = $this->drupalCreateNode(array('type' => 'book', 'book' => $book)); - $node3 = $this->drupalCreateNode(array('type' => 'page')); - $node4 = $this->drupalCreateNode(array('type' => 'book', 'book' => array('bid' => 0, 'plid' => -1))); - - // Test whether the properties work. - $wrapper = entity_metadata_wrapper('node', $node2); - $this->assertEqual($title, $wrapper->book->title->value(), "Book title returned."); - $this->assertEqual(array($node->nid), $wrapper->book_ancestors->value(array('identifier' => TRUE)), "Book ancestors returned."); - $this->assertEqual($node->nid, $wrapper->book->nid->value(), "Book id returned."); - - // Try using book properties for no book nodes. - $wrapper = entity_metadata_wrapper('node', $node3); - $this->assertEmpty($wrapper, 'book'); - $this->assertEmptyArray($wrapper, 'book_ancestors'); - - // Test a book node which is not contained in a hierarchy. - $wrapper = entity_metadata_wrapper('node', $node4); - $this->assertEmptyArray($wrapper, 'book_ancestors'); - } - - /** - * Test properties of a comment. - */ - function testComments() { - $title = 'Node 1'; - $node = $this->drupalCreateNode(array('title' => $title, 'type' => 'page')); - $author = $this->drupalCreateUser(array('access comments', 'post comments', 'edit own comments')); - $comment = (object)array( - 'subject' => 'topic', - 'nid' => $node->nid, - 'uid' => $author->uid, - 'cid' => FALSE, - 'pid' => 0, - 'homepage' => '', - 'language' => LANGUAGE_NONE, - 'hostname' => ip_address(), - ); - $comment->comment_body[LANGUAGE_NONE][0] = array('value' => 'text', 'format' => 0); - comment_save($comment); - $wrapper = entity_metadata_wrapper('comment', $comment); - foreach ($wrapper as $key => $value) { - if ($key != 'parent') { - $this->assertValue($wrapper, $key); - } - } - $this->assertEmpty($wrapper, 'parent'); - - // Test comment entity access. - $admin_user = $this->drupalCreateUser(array('access comments', 'administer comments', 'access user profiles')); - // Also grant access to view user accounts to test the comment author - // property. - $unprivileged_user = $this->drupalCreateUser(array('access comments', 'access user profiles')); - // Published comments can be viewed and edited by the author. - $this->assertTrue($wrapper->access('view', $author), 'Comment author is allowed to view the published comment.'); - $this->assertTrue($wrapper->access('edit', $author), 'Comment author is allowed to edit the published comment.'); - // We cannot use $wrapper->access('delete') here because it only understands - // view and edit. - $this->assertFalse(entity_access('delete', 'comment', $comment, $author), 'Comment author is not allowed to delete the published comment.'); - - // Administrators can do anything with published comments. - $this->assertTrue($wrapper->access('view', $admin_user), 'Comment administrator is allowed to view the published comment.'); - $this->assertTrue($wrapper->access('edit', $admin_user), 'Comment administrator is allowed to edit the published comment.'); - $this->assertTrue(entity_access('delete', 'comment', $comment, $admin_user), 'Comment administrator is allowed to delete the published comment.'); - - // Unpriviledged users can only view the published comment. - $this->assertTrue($wrapper->access('view', $unprivileged_user), 'Unprivileged user is allowed to view the published comment.'); - $this->assertFalse($wrapper->access('edit', $unprivileged_user), 'Unprivileged user is not allowed to edit the published comment.'); - $this->assertFalse(entity_access('delete', 'comment', $comment, $unprivileged_user), 'Unprivileged user is not allowed to delete the published comment.'); - - // Test property view access. - $view_access = array('name', 'homepage', 'subject', 'created', 'author', 'node', 'parent', 'url', 'edit_url'); - foreach ($view_access as $property_name) { - $this->assertTrue($wrapper->{$property_name}->access('view', $unprivileged_user), "Unpriviledged user can view the $property_name property."); - } - - $view_denied = array('hostname', 'mail', 'status'); - foreach ($view_denied as $property_name) { - $this->assertFalse($wrapper->{$property_name}->access('view', $unprivileged_user), "Unpriviledged user can not view the $property_name property."); - $this->assertTrue($wrapper->{$property_name}->access('view', $admin_user), "Admin user can view the $property_name property."); - } - - // The author is allowed to edit the comment subject if they have the - // 'edit own comments' permission. - $this->assertTrue($wrapper->subject->access('edit', $author), "Author can edit the subject property."); - $this->assertFalse($wrapper->subject->access('edit', $unprivileged_user), "Unpriviledged user cannot edit the subject property."); - $this->assertTrue($wrapper->subject->access('edit', $admin_user), "Admin user can edit the subject property."); - - $edit_denied = array('hostname', 'mail', 'status', 'name', 'homepage', 'created', 'parent', 'node', 'author'); - foreach ($edit_denied as $property_name) { - $this->assertFalse($wrapper->{$property_name}->access('edit', $author), "Author cannot edit the $property_name property."); - $this->assertTrue($wrapper->{$property_name}->access('edit', $admin_user), "Admin user can edit the $property_name property."); - } - - // Test access to unpublished comments. - $comment->status = COMMENT_NOT_PUBLISHED; - comment_save($comment); - - // Unpublished comments cannot be accessed by the author. - $this->assertFalse($wrapper->access('view', $author), 'Comment author is not allowed to view the unpublished comment.'); - $this->assertFalse($wrapper->access('edit', $author), 'Comment author is not allowed to edit the unpublished comment.'); - $this->assertFalse(entity_access('delete', 'comment', $comment, $author), 'Comment author is not allowed to delete the unpublished comment.'); - - // Administrators can do anything with unpublished comments. - $this->assertTrue($wrapper->access('view', $admin_user), 'Comment administrator is allowed to view the unpublished comment.'); - $this->assertTrue($wrapper->access('edit', $admin_user), 'Comment administrator is allowed to edit the unpublished comment.'); - $this->assertTrue(entity_access('delete', 'comment', $comment, $admin_user), 'Comment administrator is allowed to delete the unpublished comment.'); - - // Unpriviledged users cannot access unpublished comments. - $this->assertFalse($wrapper->access('view', $unprivileged_user), 'Unprivileged user is not allowed to view the unpublished comment.'); - $this->assertFalse($wrapper->access('edit', $unprivileged_user), 'Unprivileged user is not allowed to edit the unpublished comment.'); - $this->assertFalse(entity_access('delete', 'comment', $comment, $unprivileged_user), 'Unprivileged user is not allowed to delete the unpublished comment.'); - } - - /** - * Test all properties of a node. - */ - function testNodeProperties() { - $title = 'Book 1'; - $node = $this->drupalCreateNode(array('title' => $title, 'type' => 'page')); - $wrapper = entity_metadata_wrapper('node', $node); - foreach ($wrapper as $key => $value) { - if ($key != 'book' && $key != 'book_ancestors' && $key != 'source' && $key != 'last_view') { - $this->assertValue($wrapper, $key); - } - } - $this->assertEmpty($wrapper, 'book'); - $this->assertEmptyArray($wrapper, 'book_ancestors'); - $this->assertEmpty($wrapper, 'source'); - $this->assertException($wrapper->source, 'title'); - $this->assertEmpty($wrapper, 'last_view'); - - // Test statistics module integration access. - $unpriviledged_user = $this->drupalCreateUser(array('access content')); - $this->assertTrue($wrapper->access('view', $unpriviledged_user), 'Unpriviledged user can view the node.'); - $this->assertFalse($wrapper->access('edit', $unpriviledged_user), 'Unpriviledged user can not edit the node.'); - $count_access_user = $this->drupalCreateUser(array('view post access counter')); - $admin_user = $this->drupalCreateUser(array('access content', 'view post access counter', 'access statistics')); - - $this->assertFalse($wrapper->views->access('view', $unpriviledged_user), "Unpriviledged user cannot view the statistics counter property."); - $this->assertTrue($wrapper->views->access('view', $count_access_user), "Count access user can view the statistics counter property."); - $this->assertTrue($wrapper->views->access('view', $admin_user), "Admin user can view the statistics counter property."); - - $admin_properties = array('day_views', 'last_view'); - foreach ($admin_properties as $property_name) { - $this->assertFalse($wrapper->{$property_name}->access('view', $unpriviledged_user), "Unpriviledged user cannot view the $property_name property."); - $this->assertFalse($wrapper->{$property_name}->access('view', $count_access_user), "Count access user cannot view the $property_name property."); - $this->assertTrue($wrapper->{$property_name}->access('view', $admin_user), "Admin user can view the $property_name property."); - } - } - - /** - * Tests properties provided by the taxonomy module. - */ - function testTaxonomyProperties() { - $vocab = $this->createVocabulary(); - $term_parent = entity_property_values_create_entity('taxonomy_term', array( - 'name' => $this->randomName(), - 'vocabulary' => $vocab, - ))->save()->value(); - $term_parent2 = entity_property_values_create_entity('taxonomy_term', array( - 'name' => $this->randomName(), - 'vocabulary' => $vocab, - ))->save()->value(); - $term = entity_property_values_create_entity('taxonomy_term', array( - 'name' => $this->randomName(), - 'vocabulary' => $vocab, - 'description' => $this->randomString(), - 'weight' => mt_rand(0, 10), - 'parent' => array($term_parent->tid), - ))->save()->value(); - - $wrapper = entity_metadata_wrapper('taxonomy_term', $term); - foreach ($wrapper as $key => $value) { - $this->assertValue($wrapper, $key); - } - // Test setting another parent using the full object. - $wrapper->parent[] = $term_parent2; - $this->assertEqual($wrapper->parent[1]->getIdentifier(), $term_parent2->tid, 'Term parent added.'); - - $parents = $wrapper->parent->value(); - $tids = $term_parent->tid . ':' . $term_parent2->tid; - $this->assertEqual($parents[0]->tid . ':' . $parents[1]->tid, $tids, 'Parents returned.'); - $this->assertEqual(implode(':', $wrapper->parent->value(array('identifier' => TRUE))), $tids, 'Parent ids returned.'); - - // Test vocabulary. - foreach ($wrapper->vocabulary as $key => $value) { - $this->assertValue($wrapper->vocabulary, $key); - } - // Test field integration. - $tags[LANGUAGE_NONE][0]['tid'] = $term->tid; - $node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article', 'field_tags' => $tags)); - $wrapper = entity_metadata_wrapper('node', $node); - $this->assertEqual($wrapper->field_tags[0]->name->value(), $term->name, 'Get an associated tag of a node with the wrapper.'); - - $wrapper->field_tags[1] = $term_parent; - $tags = $wrapper->field_tags->value(); - $this->assertEqual($tags[1]->tid, $term_parent->tid, 'Associated a new tag with a node.'); - $this->assertEqual($tags[0]->tid, $term->tid, 'Previsous set association kept.'); - - // Test getting a list of identifiers. - $tags = $wrapper->field_tags->value(array('identifier' => TRUE)); - $this->assertEqual($tags, array($term->tid, $term_parent->tid), 'List of referenced term identifiers returned.'); - - // Test setting tags by using ids. - $wrapper->field_tags->set(array(2)); - $this->assertEqual($wrapper->field_tags[0]->tid->value(), 2, 'Specified tags by a list of term ids.'); - - // Test unsetting all tags. - $wrapper->field_tags = NULL; - $this->assertFalse($wrapper->field_tags->value(), 'Unset all tags from a node.'); - - // Test setting entity references to NULL. - // Create a taxonomy term field for that purpose. - $field_name = drupal_strtolower($this->randomName() . '_field_name'); - $field = array('field_name' => $field_name, 'type' => 'taxonomy_term_reference', 'cardinality' => 1); - $field = field_create_field($field); - $field_id = $field['id']; - $field_instance = array( - 'field_name' => $field_name, - 'entity_type' => 'node', - 'bundle' => 'article', - 'label' => $this->randomName() . '_label', - 'description' => $this->randomName() . '_description', - 'weight' => mt_rand(0, 127), - 'widget' => array( - 'type' => 'options_select', - 'label' => 'Test term field', - ) - ); - field_create_instance($field_instance); - $term_field[LANGUAGE_NONE][0]['tid'] = $term->tid; - $node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article', $field_name => $term_field)); - $wrapper = entity_metadata_wrapper('node', $node); - $wrapper->$field_name->set(NULL); - $termref = $wrapper->$field_name->value(); - $this->assertNull($termref, 'Unset of a term reference successful.'); - } - - /** - * Test all properties of a user. - */ - function testUserProperties() { - $account = $this->drupalCreateUser(array('access user profiles', 'change own username')); - $account->login = REQUEST_TIME; - $account->access = REQUEST_TIME; - $wrapper = entity_metadata_wrapper('user', $account); - foreach ($wrapper as $key => $value) { - $this->assertValue($wrapper, $key); - } - - // Test property view access. - $unpriviledged_user = $this->drupalCreateUser(array('access user profiles')); - $admin_user = $this->drupalCreateUser(array('administer users')); - $this->assertTrue($wrapper->access('view', $unpriviledged_user), 'Unpriviledged account can view the user.'); - $this->assertFalse($wrapper->access('edit', $unpriviledged_user), 'Unpriviledged account can not edit the user.'); - - $view_access = array('name', 'url', 'edit_url', 'created'); - foreach ($view_access as $property_name) { - $this->assertTrue($wrapper->{$property_name}->access('view', $unpriviledged_user), "Unpriviledged user can view the $property_name property."); - } - - $view_denied = array('mail', 'last_access', 'last_login', 'roles', 'status', 'theme'); - foreach ($view_denied as $property_name) { - $this->assertFalse($wrapper->{$property_name}->access('view', $unpriviledged_user), "Unpriviledged user can not view the $property_name property."); - $this->assertTrue($wrapper->{$property_name}->access('view', $admin_user), "Admin user can view the $property_name property."); - } - - // Test property edit access. - $edit_own_allowed = array('name', 'mail'); - foreach ($edit_own_allowed as $property_name) { - $this->assertTrue($wrapper->{$property_name}->access('edit', $account), "Account owner can edit the $property_name property."); - } - - $this->assertTrue($wrapper->roles->access('view', $account), "Account owner can view their own roles."); - - $edit_denied = array('last_access', 'last_login', 'created', 'roles', 'status', 'theme'); - foreach ($edit_denied as $property_name) { - $this->assertFalse($wrapper->{$property_name}->access('edit', $account), "Account owner cannot edit the $property_name property."); - $this->assertTrue($wrapper->{$property_name}->access('edit', $admin_user), "Admin user can edit the $property_name property."); - } - } - - /** - * Test properties provided by system module. - */ - function testSystemProperties() { - $wrapper = entity_metadata_site_wrapper(); - foreach ($wrapper as $key => $value) { - $this->assertValue($wrapper, $key); - } - // Test page request related properties. - foreach ($wrapper->current_page as $key => $value) { - $this->assertValue($wrapper->current_page, $key); - } - - // Test files. - $file = $this->createFile(); - - $wrapper = entity_metadata_wrapper('file', $file); - foreach ($wrapper as $key => $value) { - $this->assertValue($wrapper, $key); - } - } - - /** - * Runs some generic tests on each entity. - */ - function testCRUDfunctions() { - $info = entity_get_info(); - foreach ($info as $entity_type => $entity_info) { - // Test using access callback. - entity_access('view', $entity_type); - entity_access('update', $entity_type); - entity_access('create', $entity_type); - entity_access('delete', $entity_type); - - // Test creating the entity. - if (!isset($entity_info['creation callback'])) { - continue; - } - - // Populate $values with all values that are setable. They will be set - // with an metadata wrapper, so we also test setting that way. - $values = array(); - foreach (entity_metadata_wrapper($entity_type) as $name => $wrapper) { - $info = $wrapper->info(); - if (!empty($info['setter callback'])) { - $values[$name] = $this->createValue($wrapper); - } - } - $entity = entity_property_values_create_entity($entity_type, $values)->value(); - $this->assertTrue($entity, "Created $entity_type and set all setable values."); - - // Save the new entity. - $return = entity_save($entity_type, $entity); - if ($return === FALSE) { - continue; // No support for saving. - } - $id = entity_metadata_wrapper($entity_type, $entity)->getIdentifier(); - $this->assertTrue($id, "$entity_type has been successfully saved."); - - // And delete it. - $return = entity_delete($entity_type, $id); - if ($return === FALSE) { - continue; // No support for deleting. - } - $return = entity_load_single($entity_type, $id); - $this->assertFalse($return, "$entity_type has been successfully deleted."); - } - } - - /** - * Test making use of a text fields. - */ - function testTextFields() { - // Create a simple text field without text processing. - $field = array( - 'field_name' => 'field_text', - 'type' => 'text', - 'cardinality' => 2, - ); - field_create_field($field); - $instance = array( - 'field_name' => 'field_text', - 'entity_type' => 'node', - 'label' => 'test', - 'bundle' => 'article', - 'widget' => array( - 'type' => 'text_textfield', - 'weight' => -1, - ), - ); - field_create_instance($instance); - - $node = $this->drupalCreateNode(array('type' => 'article')); - $wrapper = entity_metadata_wrapper('node', $node); - - $wrapper->field_text[0] = 'the text'; - - // Try saving the node and make sure the information is still there after - // loading the node again, thus the correct data structure has been written. - node_save($node); - $node = node_load($node->nid, NULL, TRUE); - $wrapper = entity_metadata_wrapper('node', $node); - - $this->assertEqual('the text', $wrapper->field_text[0]->value(), 'Text has been specified.'); - - // Now activate text processing. - $instance['settings']['text_processing'] = 1; - field_update_instance($instance); - - $node = $this->drupalCreateNode(array('type' => 'article')); - $wrapper = entity_metadata_wrapper('node', $node); - - $wrapper->field_text[0]->set(array('value' => "The second body.")); - $this->assertEqual("

The second body.

\n", $wrapper->field_text[0]->value->value(), "Setting a processed text field value and reading it again."); - - // Assert the summary property is correctly removed. - $this->assertFalse(isset($wrapper->field_text[0]->summary), 'Processed text has no summary.'); - - // Create a text field with summary but without text processing. - $field = array( - 'field_name' => 'field_text2', - 'type' => 'text_with_summary', - 'cardinality' => 1, - ); - field_create_field($field); - $instance = array( - 'field_name' => 'field_text2', - 'entity_type' => 'node', - 'label' => 'test', - 'bundle' => 'article', - 'settings' => array('text_processing' => 0), - 'widget' => array( - 'type' => 'text_textarea_with_summary', - 'weight' => -1, - ), - ); - field_create_instance($instance); - - $node = $this->drupalCreateNode(array('type' => 'article')); - $wrapper = entity_metadata_wrapper('node', $node); - - $wrapper->field_text2->summary = 'the summary'; - $wrapper->field_text2->value = 'the text'; - - // Try saving the node and make sure the information is still there after - // loading the node again, thus the correct data structure has been written. - node_save($node); - $node = node_load($node->nid, NULL, TRUE); - $wrapper = entity_metadata_wrapper('node', $node); - - $this->assertEqual('the text', $wrapper->field_text2->value->value(), 'Text has been specified.'); - $this->assertEqual('the summary', $wrapper->field_text2->summary->value(), 'Summary has been specified.'); - } - - /** - * Test making use of a file field. - */ - function testFileFields() { - $file = $this->createFile(); - - // Create a file field. - $field = array( - 'field_name' => 'field_file', - 'type' => 'file', - 'cardinality' => 2, - 'settings' => array('display_field' => TRUE), - ); - field_create_field($field); - $instance = array( - 'field_name' => 'field_file', - 'entity_type' => 'node', - 'label' => 'File', - 'bundle' => 'article', - 'settings' => array('description_field' => TRUE), - 'required' => FALSE, - 'widget' => array( - 'type' => 'file_generic', - 'weight' => -1, - ), - ); - field_create_instance($instance); - - $node = $this->drupalCreateNode(array('type' => 'article')); - $wrapper = entity_metadata_wrapper('node', $node); - - $wrapper->field_file[0] = array('fid' => $file->fid, 'display' => FALSE); - $this->assertEqual($file->filename, $wrapper->field_file[0]->file->name->value(), 'File has been specified.'); - - $wrapper->field_file[0]->description = 'foo'; - $wrapper->field_file[0]->display = TRUE; - - $this->assertEqual($wrapper->field_file[0]->description->value(), 'foo', 'File description has been correctly set.'); - - // Try saving the node and make sure the information is still there after - // loading the node again, thus the correct data structure has been written. - node_save($node); - $node = node_load($node->nid, NULL, TRUE); - $wrapper = entity_metadata_wrapper('node', $node); - - $this->assertEqual($wrapper->field_file[0]->description->value(), 'foo', 'File description has been correctly set.'); - $this->assertEqual($wrapper->field_file[0]->display->value(), TRUE, 'File display value has been correctly set.'); - - // Test adding a new file, the display-property has to be created - // automatically. - $wrapper->field_file[1]->file = $file; - node_save($node); - $node = node_load($node->nid, NULL, TRUE); - $this->assertEqual($file->fid, $wrapper->field_file[1]->file->getIdentifier(), 'New file has been added.'); - - // Test adding an invalid file-field item, i.e. without any file. - try { - $wrapper->field_file[] = array('description' => 'test'); - $this->fail('Exception not thrown.'); - } - catch (EntityMetadataWrapperException $e) { - $this->pass('Not valid file-field item has thrown an exception.'); - } - - // Test remove all file-field items. - $wrapper->field_file = NULL; - $this->assertFalse($wrapper->field_file->value(), 'Removed multiple file-field items.'); - } - - /** - * Test making use of an image field. - */ - function testImageFields() { - $file = $this->createFile('image'); - - // Just use the image field on the article node. - $node = $this->drupalCreateNode(array('type' => 'article')); - $wrapper = entity_metadata_wrapper('node', $node); - - $wrapper->field_image = array('fid' => $file->fid); - $this->assertEqual($file->filename, $wrapper->field_image->file->name->value(), 'File has been specified.'); - - $wrapper->field_image->alt = 'foo'; - $this->assertEqual($wrapper->field_image->alt->value(), 'foo', 'Image alt attribute has been correctly set.'); - - // Try saving the node and make sure the information is still there after - // loading the node again, thus the correct data structure has been written. - node_save($node); - $node = node_load($node->nid, NULL, TRUE); - $wrapper = entity_metadata_wrapper('node', $node); - - $this->assertEqual($wrapper->field_image->alt->value(), 'foo', 'File description has been correctly set.'); - - // Test adding a new image. - $wrapper->field_image->file = $file; - node_save($node); - $node = node_load($node->nid, NULL, TRUE); - $this->assertEqual($file->fid, $wrapper->field_image->file->getIdentifier(), 'New file has been added.'); - - // Test adding an invalid image-field item, i.e. without any file. - try { - $wrapper->field_image = array(); - $this->fail('Exception not thrown.'); - } - catch (EntityMetadataWrapperException $e) { - $this->pass('Not valid image-field item has thrown an exception.'); - } - } - - /** - * Creates a value for the given property. - */ - protected function createValue($wrapper) { - if (!isset($this->node)) { - $this->node = $this->drupalCreateNode(array('type' => 'page')); - $this->user = $this->drupalCreateUser(); - $this->taxonomy_vocabulary = $this->createVocabulary(); - } - - if ($options = $wrapper->optionsList()) { - $options = entity_property_options_flatten($options); - return $wrapper instanceof EntityListWrapper ? array(key($options)) : key($options); - } - - // For mail addresses properly pass an mail address. - $info = $wrapper->info(); - if ($info['name'] == 'mail') { - return 'webmaster@example.com'; - } - - switch ($wrapper->type()) { - case 'decimal': - case 'integer': - case 'duration': - return 1; - case 'date': - return REQUEST_TIME; - case 'boolean': - return TRUE; - case 'token': - return drupal_strtolower($this->randomName(8)); - case 'text': - return $this->randomName(32); - case 'text_formatted': - return array('value' => $this->randomName(16)); - case 'list': - return array(); - - default: - return $this->{$wrapper->type()}; - } - } -} diff --git a/entity_token.info b/entity_token.info index 66fddcb..53b859c 100644 --- a/entity_token.info +++ b/entity_token.info @@ -1,6 +1,7 @@ name = Entity tokens description = Provides token replacements for all properties that have no tokens and are known to the entity API. -core = 7.x -files[] = entity_token.tokens.inc -files[] = entity_token.module +backdrop = 1.x +type = module + dependencies[] = entity +dependencies[] = entity_plus diff --git a/entity_token.module b/entity_token.module index f0abe59..65aafa8 100644 --- a/entity_token.module +++ b/entity_token.module @@ -2,5 +2,198 @@ /** * @file - * Module file for the entity tokens module. Drupal needs this file. + * Module file for the entity tokens module. */ + +/** + * Implements hook_field_info_alter(). + */ +function entity_token_field_info_alter(&$info) { + foreach (array('date', 'datetime', 'datestamp') as $date_type) { + if (isset($info[$date_type])) { + $info[$date_type]['property_type'] = 'date'; + $info[$date_type]['property_callbacks'][] = 'date_entity_metadata_property_info_alter'; + } + } +} +/** + * Callback to alter the property info of date fields. + * + * @see entity_token_field_info_alter() + */ +function date_entity_metadata_property_info_alter(&$info, $entity_type, $field, $instance, $field_type) { + $name = $field['field_name']; + $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name]; + + if ($field['type'] != 'datestamp' || $field['settings']['timezone_db'] != 'UTC') { + // Add a getter callback to convert the date into the right format. + $property['getter callback'] = 'date_entity_metadata_field_getter'; + $property['setter callback'] = 'date_entity_metadata_field_setter'; + unset($property['query callback']); + } + if (!empty($field['settings']['todate'])) { + // Define a simple data structure containing both dates. + $property['type'] = ($field['cardinality'] != 1) ? 'list' : 'struct'; + $property['auto creation'] = 'date_entity_metadata_struct_create'; + $property['getter callback'] = 'entity_metadata_field_verbatim_get'; + $property['setter callback'] = 'entity_metadata_field_verbatim_set'; + $property['property info'] = array( + 'value' => array( + 'type' => 'date', + 'label' => t('Start date'), + 'getter callback' => 'date_entity_metadata_struct_getter', + 'setter callback' => 'date_entity_metadata_struct_setter', + // The getter and setter callbacks for 'value' and 'value2' + // will not provide the field name as $name, we'll add it to $info. + 'field_name' => $field['field_name'], + // Alert Microdata module that this value can be exposed in microdata. + 'microdata' => TRUE, + ), + 'value2' => array( + 'type' => 'date', + 'label' => t('End date'), + 'getter callback' => 'date_entity_metadata_struct_getter', + 'setter callback' => 'date_entity_metadata_struct_setter', + // The getter and setter callbacks for 'value' and 'value2' + // will not provide the field name as $name, we'll add it to $info. + 'field_name' => $field['field_name'], + // Alert Microdata module that this value can be exposed in microdata. + 'microdata' => TRUE, + ), + 'duration' => array( + 'type' => 'duration', + 'label' => t('Duration'), + 'desription' => t('The duration of the time period given by the dates.'), + 'getter callback' => 'date_entity_metadata_duration_getter', + // No setter callback for duration. + // The getter callback for duration will not provide the field name + // as $name, we'll add it to $info. + 'field_name' => $field['field_name'], + ), + ); + unset($property['query callback']); + } + else { + // If this doesn't have a todate, it is handled as a date rather than a + // struct. Enable microdata on the field itself rather than the properties. + $property['microdata'] = TRUE; + } +} + +/** + * Getter callback to return date values as datestamp in UTC from the field. + */ +function date_entity_metadata_field_getter($entity, array $options, $name, $entity_type, &$context) { + $return = entity_plus_metadata_field_verbatim_get($entity, $options, $name, $entity_type, $context); + $items = ($context['field']['cardinality'] == 1) ? array($return) : $return; + foreach ($items as $key => $item) { + $items[$key] = date_entity_metadata_struct_getter($item, $options, 'value', 'struct', $context); + } + return ($context['field']['cardinality'] == 1) ? $items[0] : $items; +} + +/** + * Getter callback to return date values as datestamp in UTC. + */ +function date_entity_metadata_struct_getter($item, array $options, $name, $type, $info) { + $value = trim($item[$name]); + if (empty($value)) { + return NULL; + } + + $timezone_db = !empty($item['timezone_db']) ? $item['timezone_db'] : 'UTC'; + $date = new BackdropDateTime($value, $timezone_db); + return !empty($date) ? date_format_date($date, 'custom', 'U') : NULL; +} + +/** + * Getter callback to return the duration of the time period given by the dates. + */ +function date_entity_metadata_duration_getter($item, array $options, $name, $type, $info) { + $value = date_entity_metadata_struct_getter($item, $options, 'value', 'struct', $info); + $value2 = date_entity_metadata_struct_getter($item, $options, 'value2', 'struct', $info); + if ($value && $value2) { + return $value2 - $value; + } +} + +/** + * Callback for setting field property values. + * + * Based on entity_metadata_field_property_set(), the original property setter, + * adapted to transform non-timestamp date values to timestamps. + */ +function date_entity_metadata_field_setter(&$entity, $name, $value, $langcode, $entity_type, $info) { + $field = field_info_field($name); + if (!isset($langcode)) { + // Try to figure out the default language used by the entity. + // @todo: Update once http://backdrop.org/node/1260640 has been fixed. + $langcode = isset($entity->language) ? $entity->language : LANGUAGE_NONE; + } + $values = $field['cardinality'] == 1 ? array($value) : (array) $value; + + $items = array(); + foreach ($values as $delta => $value) { + // Make use of the struct setter to convert the date back to a timestamp. + $info['field_name'] = $name; + date_entity_metadata_struct_setter($items[$delta], 'value', $value, $langcode, 'struct', $info); + } + $entity->{$name}[$langcode] = $items; + // Empty the static field language cache, so the field system picks up any + // possible new languages. + backdrop_static_reset('field_language'); +} + +/** + * Auto creation callback for fields which contain two date values in one. + */ +function date_entity_metadata_struct_create($name, $property_info) { + return array( + 'date_type' => $property_info['field']['columns'][$name]['type'], + 'timezone_db' => $property_info['field']['settings']['timezone_db'], + ); +} + +/** + * Callback for setting an individual field value if a to-date may be there too. + * + * Based on entity_property_verbatim_set(). + * + * The passed in unix timestamp (UTC) is converted to the right value and format dependent on the field. + * + * $name is either 'value' or 'value2'. + */ +function date_entity_metadata_struct_setter(&$item, $name, $value, $langcode, $type, $info) { + if (!isset($value)) { + $item[$name] = NULL; + } + else { + $field = field_info_field($info['field_name']); + $format = date_type_format($field['type']); + $timezone_db = date_get_timezone_db($field['settings']['tz_handling']); + + $date = new BackdropDateTime($value, 'UTC'); + if ($timezone_db != 'UTC') { + date_timezone_set($date, timezone_open($timezone_db)); + } + $item[$name] = $date->format($format); + } +} + +/** + * Returns a metadata wrapper for accessing site-wide properties. + * + * Although there is no 'site' entity or such, modules may provide info about + * site-wide properties using hook_entity_property_info(). This function returns + * a wrapper for making use of this properties. + * + * @return EntityMetadataWrapper + * A wrapper for accessing site-wide properties. + * + * @see entity_metadata_system_entity_property_info() + */ +function entity_token_metadata_site_wrapper() { + $site_info = entity_plus_get_property_info('site'); + $info['property info'] = $site_info['properties']; + return entity_metadata_wrapper('site', FALSE, $info); +} diff --git a/entity_token.tokens.inc b/entity_token.tokens.inc index 77a170d..b25bdf9 100644 --- a/entity_token.tokens.inc +++ b/entity_token.tokens.inc @@ -13,7 +13,7 @@ */ function entity_token_types() { $return = entity_token_types_chained(); - return $return + drupal_map_assoc(array('text', 'integer', 'decimal', 'duration', 'boolean', 'uri')); + return $return + backdrop_map_assoc(array('text', 'integer', 'decimal', 'duration', 'boolean', 'uri')); } /** @@ -26,12 +26,12 @@ function entity_token_types() { */ function entity_token_types_chained($type = NULL) { // This functions gets called rather often when replacing tokens, thus - // we statically cache $types using the advanced drupal static pattern. - static $drupal_static_fast; - if (!isset($drupal_static_fast)) { - $drupal_static_fast['types'] = &drupal_static(__FUNCTION__, array()); + // we statically cache $types using the advanced backdrop static pattern. + static $backdrop_static_fast; + if (!isset($backdrop_static_fast)) { + $backdrop_static_fast['types'] = &backdrop_static(__FUNCTION__, array()); } - $types = &$drupal_static_fast['types']; + $types = &$backdrop_static_fast['types']; if (!$types) { // Add entities. @@ -48,7 +48,7 @@ function entity_token_types_chained($type = NULL) { } if (isset($type)) { - return isset($types[$type]) || entity_property_list_extract_type($type); + return isset($types[$type]) || entity_plus_property_list_extract_type($type); } return $types; } @@ -57,7 +57,7 @@ function entity_token_types_chained($type = NULL) { * Gets the right token type for a given property info array. */ function _entity_token_map_to_token_type($property_info) { - $lookup = &drupal_static(__FUNCTION__); + $lookup = &backdrop_static(__FUNCTION__); if (!$lookup) { // Initialize a lookup array mapping property types to token types. @@ -70,7 +70,7 @@ function _entity_token_map_to_token_type($property_info) { $type = 'struct'; } - if ($item_type = entity_property_list_extract_type($type)) { + if ($item_type = entity_plus_property_list_extract_type($type)) { return isset($lookup[$item_type]) ? "list<$lookup[$item_type]>" : FALSE; } return isset($lookup[$type]) ? $lookup[$type] : FALSE; @@ -88,7 +88,7 @@ function entity_token_token_info_alter(&$info) { foreach ($token_types as $token_type => $type) { // Just add all properties regardless whether it's in a bundle, but only if // there is no token of the property yet. - foreach (entity_get_all_property_info($type) as $name => $property) { + foreach (entity_plus_get_all_property_info($type) as $name => $property) { $name = str_replace('_', '-', $name); $property += array('type' => 'text', 'description' => $property['label']); $property_token_type = _entity_token_map_to_token_type($property); @@ -129,8 +129,8 @@ function entity_token_token_info_alter(&$info) { } else { $info['types'][$token_type] = array( - 'name' => drupal_strtoupper($token_type), - 'description' => t('@name tokens.', array('@name' => drupal_strtoupper($token_type))), + 'name' => backdrop_strtoupper($token_type), + 'description' => t('@name tokens.', array('@name' => backdrop_strtoupper($token_type))), 'needs-data' => $token_type, ); } @@ -276,7 +276,7 @@ function entity_token_tokens($type, $tokens, array $data = array(), array $optio */ function _entity_token_wrap_data($token_type, $type, $data, $options) { if ($type == 'site') { - $wrapper = entity_metadata_site_wrapper(); + $wrapper = entity_token_metadata_site_wrapper(); } elseif ($type == 'struct') { // 'struct' data items are passed on wrapped. diff --git a/includes/entity.controller.inc b/includes/entity.controller.inc deleted file mode 100644 index 5e86b52..0000000 --- a/includes/entity.controller.inc +++ /dev/null @@ -1,981 +0,0 @@ -entityInfo['bundle of'])) { - $info = entity_get_info($this->entityInfo['bundle of']); - $this->bundleKey = $info['bundle keys']['bundle']; - } - $this->defaultRevisionKey = !empty($this->entityInfo['entity keys']['default revision']) ? $this->entityInfo['entity keys']['default revision'] : 'default_revision'; - } - - /** - * Overrides DrupalDefaultEntityController::buildQuery(). - */ - protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) { - $query = parent::buildQuery($ids, $conditions, $revision_id); - if ($this->revisionKey) { - // Compare revision id of the base and revision table, if equal then this - // is the default revision. - $query->addExpression('CASE WHEN base.' . $this->revisionKey . ' = revision.' . $this->revisionKey . ' THEN 1 ELSE 0 END', $this->defaultRevisionKey); - } - return $query; - } - - /** - * Builds and executes the query for loading. - * - * @return The results in a Traversable object. - */ - public function query($ids, $conditions, $revision_id = FALSE) { - // Build the query. - $query = $this->buildQuery($ids, $conditions, $revision_id); - $result = $query->execute(); - if (!empty($this->entityInfo['entity class'])) { - $result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['entity class'], array(array(), $this->entityType)); - } - return $result; - } - - /** - * Overridden. - * @see DrupalDefaultEntityController#load($ids, $conditions) - * - * In contrast to the parent implementation we factor out query execution, so - * fetching can be further customized easily. - */ - public function load($ids = array(), $conditions = array()) { - $entities = array(); - - // Revisions are not statically cached, and require a different query to - // other conditions, so separate the revision id into its own variable. - if ($this->revisionKey && isset($conditions[$this->revisionKey])) { - $revision_id = $conditions[$this->revisionKey]; - unset($conditions[$this->revisionKey]); - } - else { - $revision_id = FALSE; - } - - // Create a new variable which is either a prepared version of the $ids - // array for later comparison with the entity cache, or FALSE if no $ids - // were passed. The $ids array is reduced as items are loaded from cache, - // and we need to know if it's empty for this reason to avoid querying the - // database when all requested entities are loaded from cache. - $passed_ids = !empty($ids) ? array_flip($ids) : FALSE; - - // Try to load entities from the static cache. - if ($this->cache && !$revision_id) { - $entities = $this->cacheGet($ids, $conditions); - // If any entities were loaded, remove them from the ids still to load. - if ($passed_ids) { - $ids = array_keys(array_diff_key($passed_ids, $entities)); - } - } - - // Support the entitycache module if activated. - if (!empty($this->entityInfo['entity cache']) && !$revision_id && $ids && !$conditions) { - $cached_entities = EntityCacheControllerHelper::entityCacheGet($this, $ids, $conditions); - // If any entities were loaded, remove them from the ids still to load. - $ids = array_diff($ids, array_keys($cached_entities)); - $entities += $cached_entities; - - // Add loaded entities to the static cache if we are not loading a - // revision. - if ($this->cache && !empty($cached_entities) && !$revision_id) { - $this->cacheSet($cached_entities); - } - } - - // Load any remaining entities from the database. This is the case if $ids - // is set to FALSE (so we load all entities), if there are any ids left to - // load or if loading a revision. - if (!($this->cacheComplete && $ids === FALSE && !$conditions) && ($ids === FALSE || $ids || $revision_id)) { - $queried_entities = array(); - foreach ($this->query($ids, $conditions, $revision_id) as $record) { - // Skip entities already retrieved from cache. - if (isset($entities[$record->{$this->idKey}])) { - continue; - } - - // For DB-based entities take care of serialized columns. - if (!empty($this->entityInfo['base table'])) { - $schema = drupal_get_schema($this->entityInfo['base table']); - - foreach ($schema['fields'] as $field => $info) { - if (!empty($info['serialize']) && isset($record->$field)) { - $record->$field = unserialize($record->$field); - // Support automatic merging of 'data' fields into the entity. - if (!empty($info['merge']) && is_array($record->$field)) { - foreach ($record->$field as $key => $value) { - $record->$key = $value; - } - unset($record->$field); - } - } - } - } - - $queried_entities[$record->{$this->idKey}] = $record; - } - } - - // Pass all entities loaded from the database through $this->attachLoad(), - // which attaches fields (if supported by the entity type) and calls the - // entity type specific load callback, for example hook_node_load(). - if (!empty($queried_entities)) { - $this->attachLoad($queried_entities, $revision_id); - $entities += $queried_entities; - } - - // Entitycache module support: Add entities to the entity cache if we are - // not loading a revision. - if (!empty($this->entityInfo['entity cache']) && !empty($queried_entities) && !$revision_id) { - EntityCacheControllerHelper::entityCacheSet($this, $queried_entities); - } - - if ($this->cache) { - // Add entities to the cache if we are not loading a revision. - if (!empty($queried_entities) && !$revision_id) { - $this->cacheSet($queried_entities); - - // Remember if we have cached all entities now. - if (!$conditions && $ids === FALSE) { - $this->cacheComplete = TRUE; - } - } - } - // Ensure that the returned array is ordered the same as the original - // $ids array if this was passed in and remove any invalid ids. - if ($passed_ids && $passed_ids = array_intersect_key($passed_ids, $entities)) { - foreach ($passed_ids as $id => $value) { - $passed_ids[$id] = $entities[$id]; - } - $entities = $passed_ids; - } - return $entities; - } - - /** - * Overrides DrupalDefaultEntityController::resetCache(). - */ - public function resetCache(array $ids = NULL) { - $this->cacheComplete = FALSE; - parent::resetCache($ids); - // Support the entitycache module. - if (!empty($this->entityInfo['entity cache'])) { - EntityCacheControllerHelper::resetEntityCache($this, $ids); - } - } - - /** - * Implements EntityAPIControllerInterface. - */ - public function invoke($hook, $entity) { - // entity_revision_delete() invokes hook_entity_revision_delete() and - // hook_field_attach_delete_revision() just as node module does. So we need - // to adjust the name of our revision deletion field attach hook in order to - // stick to this pattern. - $field_attach_hook = ($hook == 'revision_delete' ? 'delete_revision' : $hook); - if (!empty($this->entityInfo['fieldable']) && function_exists($function = 'field_attach_' . $field_attach_hook)) { - $function($this->entityType, $entity); - } - - if (!empty($this->entityInfo['bundle of']) && entity_type_is_fieldable($this->entityInfo['bundle of'])) { - $type = $this->entityInfo['bundle of']; - // Call field API bundle attachers for the entity we are a bundle of. - if ($hook == 'insert') { - field_attach_create_bundle($type, $entity->{$this->bundleKey}); - } - elseif ($hook == 'delete') { - field_attach_delete_bundle($type, $entity->{$this->bundleKey}); - } - elseif ($hook == 'update' && $entity->original->{$this->bundleKey} != $entity->{$this->bundleKey}) { - field_attach_rename_bundle($type, $entity->original->{$this->bundleKey}, $entity->{$this->bundleKey}); - } - } - // Invoke the hook. - module_invoke_all($this->entityType . '_' . $hook, $entity); - // Invoke the respective entity level hook. - if ($hook == 'presave' || $hook == 'insert' || $hook == 'update' || $hook == 'delete') { - module_invoke_all('entity_' . $hook, $entity, $this->entityType); - } - // Invoke rules. - if (module_exists('rules')) { - rules_invoke_event($this->entityType . '_' . $hook, $entity); - } - } - - /** - * Implements EntityAPIControllerInterface. - * - * @param $transaction - * Optionally a DatabaseTransaction object to use. Allows overrides to pass - * in their transaction object. - */ - public function delete($ids, DatabaseTransaction $transaction = NULL) { - $entities = $ids ? $this->load($ids) : FALSE; - if (!$entities) { - // Do nothing, in case invalid or no ids have been passed. - return; - } - $transaction = isset($transaction) ? $transaction : db_transaction(); - - try { - $ids = array_keys($entities); - - db_delete($this->entityInfo['base table']) - ->condition($this->idKey, $ids, 'IN') - ->execute(); - - if (isset($this->revisionTable)) { - db_delete($this->revisionTable) - ->condition($this->idKey, $ids, 'IN') - ->execute(); - } - // Reset the cache as soon as the changes have been applied. - $this->resetCache($ids); - - foreach ($entities as $id => $entity) { - $this->invoke('delete', $entity); - } - // Ignore slave server temporarily. - db_ignore_slave(); - } - catch (Exception $e) { - $transaction->rollback(); - watchdog_exception($this->entityType, $e); - throw $e; - } - } - - /** - * Implements EntityAPIControllerRevisionableInterface::deleteRevision(). - */ - public function deleteRevision($revision_id) { - if ($entity_revision = entity_revision_load($this->entityType, $revision_id)) { - // Prevent deleting the default revision. - if (entity_revision_is_default($this->entityType, $entity_revision)) { - return FALSE; - } - - db_delete($this->revisionTable) - ->condition($this->revisionKey, $revision_id) - ->execute(); - - $this->invoke('revision_delete', $entity_revision); - return TRUE; - } - return FALSE; - } - - /** - * Implements EntityAPIControllerInterface. - * - * @param $transaction - * Optionally a DatabaseTransaction object to use. Allows overrides to pass - * in their transaction object. - */ - public function save($entity, DatabaseTransaction $transaction = NULL) { - $transaction = isset($transaction) ? $transaction : db_transaction(); - try { - // Load the stored entity, if any. - if (!empty($entity->{$this->idKey}) && !isset($entity->original)) { - // In order to properly work in case of name changes, load the original - // entity using the id key if it is available. - $entity->original = entity_load_unchanged($this->entityType, $entity->{$this->idKey}); - } - $entity->is_new = !empty($entity->is_new) || empty($entity->{$this->idKey}); - $this->invoke('presave', $entity); - - if ($entity->is_new) { - $return = drupal_write_record($this->entityInfo['base table'], $entity); - if ($this->revisionKey) { - $this->saveRevision($entity); - } - $this->invoke('insert', $entity); - } - else { - // Update the base table if the entity doesn't have revisions or - // we are updating the default revision. - if (!$this->revisionKey || !empty($entity->{$this->defaultRevisionKey})) { - $return = drupal_write_record($this->entityInfo['base table'], $entity, $this->idKey); - } - if ($this->revisionKey) { - $return = $this->saveRevision($entity); - } - $this->resetCache(array($entity->{$this->idKey})); - $this->invoke('update', $entity); - - // Field API always saves as default revision, so if the revision saved - // is not default we have to restore the field values of the default - // revision now by invoking field_attach_update() once again. - if ($this->revisionKey && !$entity->{$this->defaultRevisionKey} && !empty($this->entityInfo['fieldable'])) { - field_attach_update($this->entityType, $entity->original); - } - } - - // Ignore slave server temporarily. - db_ignore_slave(); - unset($entity->is_new); - unset($entity->is_new_revision); - unset($entity->original); - - return $return; - } - catch (Exception $e) { - $transaction->rollback(); - watchdog_exception($this->entityType, $e); - throw $e; - } - } - - /** - * Saves an entity revision. - * - * @param Entity $entity - * Entity revision to save. - */ - protected function saveRevision($entity) { - // Convert the entity into an array as it might not have the same properties - // as the entity, it is just a raw structure. - $record = (array) $entity; - // File fields assumes we are using $entity->revision instead of - // $entity->is_new_revision, so we also support it and make sure it's set to - // the same value. - $entity->is_new_revision = !empty($entity->is_new_revision) || !empty($entity->revision) || $entity->is_new; - $entity->revision = &$entity->is_new_revision; - $entity->{$this->defaultRevisionKey} = !empty($entity->{$this->defaultRevisionKey}) || $entity->is_new; - - - - // When saving a new revision, set any existing revision ID to NULL so as to - // ensure that a new revision will actually be created. - if ($entity->is_new_revision && isset($record[$this->revisionKey])) { - $record[$this->revisionKey] = NULL; - } - - if ($entity->is_new_revision) { - drupal_write_record($this->revisionTable, $record); - $update_default_revision = $entity->{$this->defaultRevisionKey}; - } - else { - drupal_write_record($this->revisionTable, $record, $this->revisionKey); - // @todo: Fix original entity to be of the same revision and check whether - // the default revision key has been set. - $update_default_revision = $entity->{$this->defaultRevisionKey} && $entity->{$this->revisionKey} != $entity->original->{$this->revisionKey}; - } - // Make sure to update the new revision key for the entity. - $entity->{$this->revisionKey} = $record[$this->revisionKey]; - - // Mark this revision as the default one. - if ($update_default_revision) { - db_update($this->entityInfo['base table']) - ->fields(array($this->revisionKey => $record[$this->revisionKey])) - ->condition($this->idKey, $entity->{$this->idKey}) - ->execute(); - } - return $entity->is_new_revision ? SAVED_NEW : SAVED_UPDATED; - } - - /** - * Implements EntityAPIControllerInterface. - */ - public function create(array $values = array()) { - // Add is_new property if it is not set. - $values += array('is_new' => TRUE); - if (isset($this->entityInfo['entity class']) && $class = $this->entityInfo['entity class']) { - return new $class($values, $this->entityType); - } - return (object) $values; - } - - /** - * Implements EntityAPIControllerInterface. - * - * @return - * A serialized string in JSON format suitable for the import() method. - */ - public function export($entity, $prefix = '') { - $vars = get_object_vars($entity); - unset($vars['is_new']); - return entity_var_json_export($vars, $prefix); - } - - /** - * Implements EntityAPIControllerInterface. - * - * @param $export - * A serialized string in JSON format as produced by the export() method. - */ - public function import($export) { - $vars = drupal_json_decode($export); - if (is_array($vars)) { - return $this->create($vars); - } - return FALSE; - } - - /** - * Implements EntityAPIControllerInterface. - * - * @param $content - * Optionally. Allows pre-populating the built content to ease overridding - * this method. - */ - public function buildContent($entity, $view_mode = 'full', $langcode = NULL, $content = array()) { - // Remove previously built content, if exists. - $entity->content = $content; - $langcode = isset($langcode) ? $langcode : $GLOBALS['language_content']->language; - - // Allow modules to change the view mode. - $context = array( - 'entity_type' => $this->entityType, - 'entity' => $entity, - 'langcode' => $langcode, - ); - drupal_alter('entity_view_mode', $view_mode, $context); - // Make sure the used view-mode gets stored. - $entity->content += array('#view_mode' => $view_mode); - - // By default add in properties for all defined extra fields. - if ($extra_field_controller = entity_get_extra_fields_controller($this->entityType)) { - $wrapper = entity_metadata_wrapper($this->entityType, $entity); - $extra = $extra_field_controller->fieldExtraFields(); - $type_extra = &$extra[$this->entityType][$this->entityType]['display']; - $bundle_extra = &$extra[$this->entityType][$wrapper->getBundle()]['display']; - - foreach ($wrapper as $name => $property) { - if (isset($type_extra[$name]) || isset($bundle_extra[$name])) { - $this->renderEntityProperty($wrapper, $name, $property, $view_mode, $langcode, $entity->content); - } - } - } - - // Add in fields. - if (!empty($this->entityInfo['fieldable'])) { - // Perform the preparation tasks if they have not been performed yet. - // An internal flag prevents the operation from running twice. - $key = isset($entity->{$this->idKey}) ? $entity->{$this->idKey} : NULL; - field_attach_prepare_view($this->entityType, array($key => $entity), $view_mode); - $entity->content += field_attach_view($this->entityType, $entity, $view_mode, $langcode); - } - // Invoke hook_ENTITY_view() to allow modules to add their additions. - if (module_exists('rules')) { - rules_invoke_all($this->entityType . '_view', $entity, $view_mode, $langcode); - } - else { - module_invoke_all($this->entityType . '_view', $entity, $view_mode, $langcode); - } - module_invoke_all('entity_view', $entity, $this->entityType, $view_mode, $langcode); - $build = $entity->content; - unset($entity->content); - return $build; - } - - /** - * Renders a single entity property. - */ - protected function renderEntityProperty($wrapper, $name, $property, $view_mode, $langcode, &$content) { - $info = $property->info(); - - $content[$name] = array( - '#label_hidden' => FALSE, - '#label' => $info['label'], - '#entity_wrapped' => $wrapper, - '#theme' => 'entity_property', - '#property_name' => $name, - '#access' => $property->access('view'), - '#entity_type' => $this->entityType, - ); - $content['#attached']['css']['entity.theme'] = drupal_get_path('module', 'entity') . '/theme/entity.theme.css'; - } - - /** - * Implements EntityAPIControllerInterface. - */ - public function view($entities, $view_mode = 'full', $langcode = NULL, $page = NULL) { - // For Field API and entity_prepare_view, the entities have to be keyed by - // (numeric) id. - $entities = entity_key_array_by_property($entities, $this->idKey); - if (!empty($this->entityInfo['fieldable'])) { - field_attach_prepare_view($this->entityType, $entities, $view_mode); - } - entity_prepare_view($this->entityType, $entities); - $langcode = isset($langcode) ? $langcode : $GLOBALS['language_content']->language; - - $view = array(); - foreach ($entities as $entity) { - $build = entity_build_content($this->entityType, $entity, $view_mode, $langcode); - $build += array( - // If the entity type provides an implementation, use this instead the - // generic one. - // @see template_preprocess_entity() - '#theme' => 'entity', - '#entity_type' => $this->entityType, - '#entity' => $entity, - '#view_mode' => $view_mode, - '#language' => $langcode, - '#page' => $page, - ); - // Allow modules to modify the structured entity. - drupal_alter(array($this->entityType . '_view', 'entity_view'), $build, $this->entityType); - $key = isset($entity->{$this->idKey}) ? $entity->{$this->idKey} : NULL; - $view[$this->entityType][$key] = $build; - } - return $view; - } -} - -/** - * A controller implementing exportables stored in the database. - */ -class EntityAPIControllerExportable extends EntityAPIController { - - protected $entityCacheByName = array(); - protected $nameKey, $statusKey, $moduleKey; - - /** - * Overridden. - * - * Allows specifying a name key serving as uniform identifier for this entity - * type while still internally we are using numeric identifieres. - */ - public function __construct($entityType) { - parent::__construct($entityType); - // Use the name key as primary identifier. - $this->nameKey = isset($this->entityInfo['entity keys']['name']) ? $this->entityInfo['entity keys']['name'] : $this->idKey; - if (!empty($this->entityInfo['exportable'])) { - $this->statusKey = isset($this->entityInfo['entity keys']['status']) ? $this->entityInfo['entity keys']['status'] : 'status'; - $this->moduleKey = isset($this->entityInfo['entity keys']['module']) ? $this->entityInfo['entity keys']['module'] : 'module'; - } - } - - /** - * Support loading by name key. - */ - protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) { - // Add the id condition ourself, as we might have a separate name key. - $query = parent::buildQuery(array(), $conditions, $revision_id); - if ($ids) { - // Support loading by numeric ids as well as by machine names. - $key = is_numeric(reset($ids)) ? $this->idKey : $this->nameKey; - $query->condition("base.$key", $ids, 'IN'); - } - return $query; - } - - /** - * Overridden to support passing numeric ids as well as names as $ids. - */ - public function load($ids = array(), $conditions = array()) { - $entities = array(); - - // Only do something if loaded by names. - if (!$ids || $this->nameKey == $this->idKey || is_numeric(reset($ids))) { - return parent::load($ids, $conditions); - } - - // Revisions are not statically cached, and require a different query to - // other conditions, so separate the revision id into its own variable. - if ($this->revisionKey && isset($conditions[$this->revisionKey])) { - $revision_id = $conditions[$this->revisionKey]; - unset($conditions[$this->revisionKey]); - } - else { - $revision_id = FALSE; - } - $passed_ids = !empty($ids) ? array_flip($ids) : FALSE; - - // Care about the static cache. - if ($this->cache && !$revision_id) { - $entities = $this->cacheGetByName($ids, $conditions); - } - // If any entities were loaded, remove them from the ids still to load. - if ($entities) { - $ids = array_keys(array_diff_key($passed_ids, $entities)); - } - - $entities_by_id = parent::load($ids, $conditions); - $entities += entity_key_array_by_property($entities_by_id, $this->nameKey); - - // Ensure that the returned array is keyed by numeric id and ordered the - // same as the original $ids array and remove any invalid ids. - $return = array(); - foreach ($passed_ids as $name => $value) { - if (isset($entities[$name])) { - $return[$entities[$name]->{$this->idKey}] = $entities[$name]; - } - } - return $return; - } - - /** - * Overridden. - * @see DrupalDefaultEntityController::cacheGet() - */ - protected function cacheGet($ids, $conditions = array()) { - if (!empty($this->entityCache) && $ids !== array()) { - $entities = $ids ? array_intersect_key($this->entityCache, array_flip($ids)) : $this->entityCache; - return $this->applyConditions($entities, $conditions); - } - return array(); - } - - /** - * Like cacheGet() but keyed by name. - */ - protected function cacheGetByName($names, $conditions = array()) { - if (!empty($this->entityCacheByName) && $names !== array() && $names) { - // First get the entities by ids, then apply the conditions. - // Generally, we make use of $this->entityCache, but if we are loading by - // name, we have to use $this->entityCacheByName. - $entities = array_intersect_key($this->entityCacheByName, array_flip($names)); - return $this->applyConditions($entities, $conditions); - } - return array(); - } - - protected function applyConditions($entities, $conditions = array()) { - if ($conditions) { - foreach ($entities as $key => $entity) { - $entity_values = (array) $entity; - // We cannot use array_diff_assoc() here because condition values can - // also be arrays, e.g. '$conditions = array('status' => array(1, 2))' - foreach ($conditions as $condition_key => $condition_value) { - if (is_array($condition_value)) { - if (!isset($entity_values[$condition_key]) || !in_array($entity_values[$condition_key], $condition_value)) { - unset($entities[$key]); - } - } - elseif (!isset($entity_values[$condition_key]) || $entity_values[$condition_key] != $condition_value) { - unset($entities[$key]); - } - } - } - } - return $entities; - } - - /** - * Overridden. - * @see DrupalDefaultEntityController::cacheSet() - */ - protected function cacheSet($entities) { - $this->entityCache += $entities; - // If we have a name key, also support static caching when loading by name. - if ($this->nameKey != $this->idKey) { - $this->entityCacheByName += entity_key_array_by_property($entities, $this->nameKey); - } - } - - /** - * Overridden. - * @see DrupalDefaultEntityController::attachLoad() - * - * Changed to call type-specific hook with the entities keyed by name if they - * have one. - */ - protected function attachLoad(&$queried_entities, $revision_id = FALSE) { - // Attach fields. - if ($this->entityInfo['fieldable']) { - if ($revision_id) { - field_attach_load_revision($this->entityType, $queried_entities); - } - else { - field_attach_load($this->entityType, $queried_entities); - } - } - - // Call hook_entity_load(). - foreach (module_implements('entity_load') as $module) { - $function = $module . '_entity_load'; - $function($queried_entities, $this->entityType); - } - // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are - // always the queried entities, followed by additional arguments set in - // $this->hookLoadArguments. - // For entities with a name key, pass the entities keyed by name to the - // specific load hook. - if ($this->nameKey != $this->idKey) { - $entities_by_name = entity_key_array_by_property($queried_entities, $this->nameKey); - } - else { - $entities_by_name = $queried_entities; - } - $args = array_merge(array($entities_by_name), $this->hookLoadArguments); - foreach (module_implements($this->entityInfo['load hook']) as $module) { - call_user_func_array($module . '_' . $this->entityInfo['load hook'], $args); - } - } - - public function resetCache(array $ids = NULL) { - $this->cacheComplete = FALSE; - if (isset($ids)) { - foreach (array_intersect_key($this->entityCache, array_flip($ids)) as $id => $entity) { - unset($this->entityCacheByName[$this->entityCache[$id]->{$this->nameKey}]); - unset($this->entityCache[$id]); - } - } - else { - $this->entityCache = array(); - $this->entityCacheByName = array(); - } - } - - /** - * Overridden to care about reverted entities. - */ - public function delete($ids, DatabaseTransaction $transaction = NULL) { - $entities = $ids ? $this->load($ids) : FALSE; - if ($entities) { - parent::delete($ids, $transaction); - - foreach ($entities as $id => $entity) { - if (entity_has_status($this->entityType, $entity, ENTITY_IN_CODE)) { - entity_defaults_rebuild(array($this->entityType)); - break; - } - } - } - } - - /** - * Overridden to care about reverted bundle entities and to skip Rules. - */ - public function invoke($hook, $entity) { - if ($hook == 'delete') { - // To ease figuring out whether this is a revert, make sure that the - // entity status is updated in case the providing module has been - // disabled. - if (entity_has_status($this->entityType, $entity, ENTITY_IN_CODE) && !module_exists($entity->{$this->moduleKey})) { - $entity->{$this->statusKey} = ENTITY_CUSTOM; - } - $is_revert = entity_has_status($this->entityType, $entity, ENTITY_IN_CODE); - } - - if (!empty($this->entityInfo['fieldable']) && function_exists($function = 'field_attach_' . $hook)) { - $function($this->entityType, $entity); - } - - if (isset($this->entityInfo['bundle of']) && $type = $this->entityInfo['bundle of']) { - // Call field API bundle attachers for the entity we are a bundle of. - if ($hook == 'insert') { - field_attach_create_bundle($type, $entity->{$this->bundleKey}); - } - elseif ($hook == 'delete' && !$is_revert) { - field_attach_delete_bundle($type, $entity->{$this->bundleKey}); - } - elseif ($hook == 'update' && $id = $entity->{$this->nameKey}) { - if ($entity->original->{$this->bundleKey} != $entity->{$this->bundleKey}) { - field_attach_rename_bundle($type, $entity->original->{$this->bundleKey}, $entity->{$this->bundleKey}); - } - } - } - // Invoke the hook. - module_invoke_all($this->entityType . '_' . $hook, $entity); - // Invoke the respective entity level hook. - if ($hook == 'presave' || $hook == 'insert' || $hook == 'update' || $hook == 'delete') { - module_invoke_all('entity_' . $hook, $entity, $this->entityType); - } - } - - /** - * Overridden to care exportables that are overridden. - */ - public function save($entity, DatabaseTransaction $transaction = NULL) { - // Preload $entity->original by name key if necessary. - if (!empty($entity->{$this->nameKey}) && empty($entity->{$this->idKey}) && !isset($entity->original)) { - $entity->original = entity_load_unchanged($this->entityType, $entity->{$this->nameKey}); - } - // Update the status for entities getting overridden. - if (entity_has_status($this->entityType, $entity, ENTITY_IN_CODE) && empty($entity->is_rebuild)) { - $entity->{$this->statusKey} |= ENTITY_CUSTOM; - } - return parent::save($entity, $transaction); - } - - /** - * Overridden. - */ - public function export($entity, $prefix = '') { - $vars = get_object_vars($entity); - unset($vars[$this->statusKey], $vars[$this->moduleKey], $vars['is_new']); - if ($this->nameKey != $this->idKey) { - unset($vars[$this->idKey]); - } - return entity_var_json_export($vars, $prefix); - } - - /** - * Implements EntityAPIControllerInterface. - */ - public function view($entities, $view_mode = 'full', $langcode = NULL, $page = NULL) { - $view = parent::view($entities, $view_mode, $langcode, $page); - - if ($this->nameKey != $this->idKey) { - // Re-key the view array to be keyed by name. - $return = array(); - foreach ($view[$this->entityType] as $id => $content) { - $key = isset($content['#entity']->{$this->nameKey}) ? $content['#entity']->{$this->nameKey} : NULL; - $return[$this->entityType][$key] = $content; - } - $view = $return; - } - return $view; - } -} diff --git a/includes/entity.inc b/includes/entity.inc deleted file mode 100644 index 2f504f3..0000000 --- a/includes/entity.inc +++ /dev/null @@ -1,423 +0,0 @@ -label() and - * $entity->uri() reflect this changes as well. - * - * Defaults for entity properties can be easily defined by adding class - * properties, e.g.: - * @code - * public $name = ''; - * public $count = 0; - * @endcode - */ -class Entity implements EntityInterface { - - protected $entityType; - protected $entityInfo; - protected $idKey, $nameKey, $statusKey; - protected $defaultLabel = FALSE; - protected $wrapper; - - /** - * {@inheritdoc} - */ - public function __construct(array $values = array(), $entityType = NULL) { - if (empty($entityType)) { - throw new Exception('Cannot create an instance of Entity without a specified entity type.'); - } - $this->entityType = $entityType; - $this->setUp(); - // Set initial values. - foreach ($values as $key => $value) { - $this->$key = $value; - } - } - - /** - * Set up the object instance on construction or unserializiation. - */ - protected function setUp() { - $this->entityInfo = entity_get_info($this->entityType); - $this->idKey = $this->entityInfo['entity keys']['id']; - $this->nameKey = isset($this->entityInfo['entity keys']['name']) ? $this->entityInfo['entity keys']['name'] : $this->idKey; - $this->statusKey = empty($this->entityInfo['entity keys']['status']) ? 'status' : $this->entityInfo['entity keys']['status']; - } - - /** - * {@inheritdoc} - */ - public function internalIdentifier() { - return isset($this->{$this->idKey}) ? $this->{$this->idKey} : NULL; - } - - /** - * {@inheritdoc} - */ - public function identifier() { - return isset($this->{$this->nameKey}) ? $this->{$this->nameKey} : NULL; - } - - /** - * {@inheritdoc} - */ - public function entityInfo() { - return $this->entityInfo; - } - - /** - * {@inheritdoc} - */ - public function entityType() { - return $this->entityType; - } - - /** - * {@inheritdoc} - */ - public function bundle() { - return !empty($this->entityInfo['entity keys']['bundle']) ? $this->{$this->entityInfo['entity keys']['bundle']} : $this->entityType; - } - - /** - * {@inheritdoc} - */ - public function wrapper() { - if (empty($this->wrapper)) { - $this->wrapper = entity_metadata_wrapper($this->entityType, $this); - } - elseif ($this->wrapper->value() !== $this) { - // Wrapper has been modified outside, so let's better create a new one. - $this->wrapper = entity_metadata_wrapper($this->entityType, $this); - } - return $this->wrapper; - } - - /** - * {@inheritdoc} - */ - public function label() { - // If the default label flag is enabled, this is being invoked recursively. - // In this case we need to use our default label callback directly. This may - // happen if a module provides a label callback implementation different - // from ours, but then invokes Entity::label() or entity_class_label() from - // there. - if ($this->defaultLabel || (isset($this->entityInfo['label callback']) && $this->entityInfo['label callback'] == 'entity_class_label')) { - return $this->defaultLabel(); - } - $this->defaultLabel = TRUE; - $label = entity_label($this->entityType, $this); - $this->defaultLabel = FALSE; - return $label; - } - - /** - * Defines the entity label if the 'entity_class_label' callback is used. - * - * Specify 'entity_class_label' as 'label callback' in hook_entity_info() to - * let the entity label point to this method. Override this in order to - * implement a custom default label. - */ - protected function defaultLabel() { - // Add in the translated specified label property. - return $this->getTranslation($this->entityInfo['entity keys']['label']); - } - - /** - * {@inheritdoc} - */ - public function uri() { - if (isset($this->entityInfo['uri callback']) && $this->entityInfo['uri callback'] == 'entity_class_uri') { - return $this->defaultUri(); - } - return entity_uri($this->entityType, $this); - } - - /** - * Override this in order to implement a custom default URI and specify - * 'entity_class_uri' as 'uri callback' hook_entity_info(). - */ - protected function defaultUri() { - return array('path' => 'default/' . $this->identifier()); - } - - /** - * {@inheritdoc} - */ - public function hasStatus($status) { - if (!empty($this->entityInfo['exportable'])) { - return isset($this->{$this->statusKey}) && ($this->{$this->statusKey} & $status) == $status; - } - } - - /** - * {@inheritdoc} - */ - public function save() { - return entity_get_controller($this->entityType)->save($this); - } - - /** - * {@inheritdoc} - */ - public function delete() { - $id = $this->identifier(); - if (isset($id)) { - entity_get_controller($this->entityType)->delete(array($id)); - } - } - - /** - * {@inheritdoc} - */ - public function export($prefix = '') { - return entity_get_controller($this->entityType)->export($this, $prefix); - } - - /** - * {@inheritdoc} - */ - public function view($view_mode = 'full', $langcode = NULL, $page = NULL) { - return entity_get_controller($this->entityType)->view(array($this), $view_mode, $langcode, $page); - } - - /** - * {@inheritdoc} - */ - public function buildContent($view_mode = 'full', $langcode = NULL) { - return entity_get_controller($this->entityType)->buildContent($this, $view_mode, $langcode); - } - - /** - * {@inheritdoc} - */ - public function getTranslation($property, $langcode = NULL) { - $all_info = entity_get_all_property_info($this->entityType); - // Assign by reference to avoid triggering notices if metadata is missing. - $property_info = &$all_info[$property]; - - if (!empty($property_info['translatable'])) { - if (!empty($property_info['field'])) { - return field_get_items($this->entityType, $this, $property, $langcode); - } - elseif (!empty($property_info['i18n string'])) { - $name = $this->entityInfo['module'] . ':' . $this->entityType . ':' . $this->identifier() . ':' . $property; - return entity_i18n_string($name, $this->$property, $langcode); - } - } - return $this->$property; - } - - /** - * {@inheritdoc} - */ - public function isDefaultRevision() { - if (!empty($this->entityInfo['entity keys']['revision'])) { - $key = !empty($this->entityInfo['entity keys']['default revision']) ? $this->entityInfo['entity keys']['default revision'] : 'default_revision'; - return !empty($this->$key); - } - return TRUE; - } - - /** - * Magic method to only serialize what's necessary. - */ - public function __sleep() { - $vars = get_object_vars($this); - unset($vars['entityInfo'], $vars['idKey'], $vars['nameKey'], $vars['statusKey']); - // Also key the returned array with the variable names so the method may - // be easily overridden and customized. - return drupal_map_assoc(array_keys($vars)); - } - - /** - * Magic method to invoke setUp() on unserialization. - */ - public function __wakeup() { - $this->setUp(); - } -} - -/** - * These classes are deprecated by "Entity" and are only here for backward - * compatibility reasons. - */ -class EntityDB extends Entity {} -class EntityExtendable extends Entity {} -class EntityDBExtendable extends Entity {} diff --git a/includes/entity.property.inc b/includes/entity.property.inc deleted file mode 100644 index e8714e6..0000000 --- a/includes/entity.property.inc +++ /dev/null @@ -1,657 +0,0 @@ -language; - - if (empty($info)) { - if ($cache = cache_get("entity_property_info:$langcode")) { - $info = $cache->data; - } - else { - $info = module_invoke_all('entity_property_info'); - // Let other modules alter the entity info. - drupal_alter('entity_property_info', $info); - cache_set("entity_property_info:$langcode", $info); - } - } - return empty($entity_type) ? $info : (isset($info[$entity_type]) ? $info[$entity_type] : array()); -} - -/** - * Returns the default information for an entity property. - * - * @return - * An array of optional property information keys mapped to their defaults. - * - * @see hook_entity_property_info() - */ -function entity_property_info_defaults() { - return array( - 'type' => 'text', - 'getter callback' => 'entity_property_verbatim_get', - ); -} - -/** - * Gets an array of info about all properties of a given entity type. - * - * In contrast to entity_get_property_info(), this function returns info about - * all properties the entity might have, thus it adds an all properties assigned - * to entity bundles. - * - * @param $entity_type - * (optiona) The entity type to return properties for. - * - * @return - * An array of info about properties. If the type is omitted, all known - * properties are returned. - */ -function entity_get_all_property_info($entity_type = NULL) { - if (!isset($entity_type)) { - // Retrieve all known properties. - $properties = array(); - foreach (entity_get_info() as $entity_type => $info) { - $properties += entity_get_all_property_info($entity_type); - } - return $properties; - } - // Else retrieve the properties of the given entity type only. - $info = entity_get_property_info($entity_type); - $info += array('properties' => array(), 'bundles' => array()); - // Add all bundle properties. - foreach ($info['bundles'] as $bundle => $bundle_info) { - $bundle_info += array('properties' => array()); - $info['properties'] += $bundle_info['properties']; - } - return $info['properties']; -} - -/** - * Queries for entities having the given property value. - * - * @param $entity_type - * The type of the entity. - * @param $property - * The name of the property to query for. - * @param $value - * A single property value or an array of possible values to query for. - * @param $limit - * Limit the numer of results. Defaults to 30. - * - * @return - * An array of entity ids or NULL if there is no information how to query for - * the given property. - */ -function entity_property_query($entity_type, $property, $value, $limit = 30) { - $properties = entity_get_all_property_info($entity_type); - $info = $properties[$property] + array('type' => 'text', 'queryable' => !empty($properties[$property]['schema field'])); - - // We still support the deprecated query callback, so just add in EFQ-based - // callbacks in case 'queryable' is set to TRUE and make use of the callback. - if ($info['queryable'] && empty($info['query callback'])) { - $info['query callback'] = !empty($info['field']) ? 'entity_metadata_field_query' : 'entity_metadata_table_query'; - } - - $type = $info['type']; - // Make sure an entity or a list of entities are passed on as identifiers - // with the help of the wrappers. For that ensure the data type matches the - // passed on value(s). - if (is_array($value) && !entity_property_list_extract_type($type)) { - $type = 'list<' . $type . '>'; - } - elseif (!is_array($value) && entity_property_list_extract_type($type)) { - $type = entity_property_list_extract_type($type); - } - - $wrapper = entity_metadata_wrapper($type, $value); - $value = $wrapper->value(array('identifier' => TRUE)); - - if (!empty($info['query callback'])) { - return $info['query callback']($entity_type, $property, $value, $limit); - } -} - -/** - * Resets the cached information of hook_entity_property_info(). - */ -function entity_property_info_cache_clear() { - drupal_static_reset('entity_get_property_info'); - // Clear all languages. - cache_clear_all('entity_property_info:', 'cache', TRUE); -} - -/** - * Implements hook_hook_info(). - */ -function entity_hook_info() { - $hook_info['entity_property_info'] = array( - 'group' => 'info', - ); - $hook_info['entity_property_info_alter'] = array( - 'group' => 'info', - ); - return $hook_info; -} - -/** - * Implements hook_field_info_alter(). - * Defines default property types for core field types. - */ -function entity_field_info_alter(&$field_info) { - if (module_exists('number')) { - $field_info['number_integer']['property_type'] = 'integer'; - $field_info['number_decimal']['property_type'] = 'decimal'; - $field_info['number_float']['property_type'] = 'decimal'; - } - if (module_exists('text')) { - $field_info['text']['property_type'] = 'text'; - $field_info['text']['property_callbacks'][] = 'entity_metadata_field_text_property_callback'; - $field_info['text_long']['property_type'] = 'text'; - $field_info['text_long']['property_callbacks'][] = 'entity_metadata_field_text_property_callback'; - $field_info['text_with_summary']['property_type'] = 'field_item_textsummary'; - $field_info['text_with_summary']['property_callbacks'][] = 'entity_metadata_field_text_property_callback'; - } - if (module_exists('list')) { - $field_info['list_integer']['property_type'] = 'integer'; - $field_info['list_boolean']['property_type'] = 'boolean'; - $field_info['list_float']['property_type'] = 'decimal'; - $field_info['list_text']['property_type'] = 'text'; - } - if (module_exists('taxonomy')) { - $field_info['taxonomy_term_reference']['property_type'] = 'taxonomy_term'; - $field_info['taxonomy_term_reference']['property_callbacks'][] = 'entity_metadata_field_term_reference_callback'; - } - if (module_exists('file')) { - // The callback specifies a custom data structure matching the file field - // items. We introduce a custom type name for this data structure. - $field_info['file']['property_type'] = 'field_item_file'; - $field_info['file']['property_callbacks'][] = 'entity_metadata_field_file_callback'; - } - if (module_exists('image')) { - // The callback specifies a custom data structure matching the image field - // items. We introduce a custom type name for this data structure. - $field_info['image']['property_type'] = 'field_item_image'; - $field_info['image']['property_callbacks'][] = 'entity_metadata_field_file_callback'; - $field_info['image']['property_callbacks'][] = 'entity_metadata_field_image_callback'; - } -} - -/** - * Implements hook_field_create_instance(). - * Clear the cache when a field instance changed. - */ -function entity_field_create_instance() { - entity_property_info_cache_clear(); -} - -/** - * Implements hook_field_delete_instance(). - * Clear the cache when a field instance changed. - */ -function entity_field_delete_instance() { - entity_property_info_cache_clear(); -} - -/** - * Implements hook_field_update_instance(). - * Clear the cache when a field instance changed. - */ -function entity_field_update_instance() { - entity_property_info_cache_clear(); -} - -/** - * Verifies that the given data can be safely used as the given type regardless - * of the PHP variable type of $data. Example: the string "15" is a valid - * integer, but "15nodes" is not. - * - * @return - * Whether the data is valid for the given type. - */ -function entity_property_verify_data_type($data, $type) { - // As this may be called very often statically cache the entity info using - // the fast pattern. - static $drupal_static_fast; - if (!isset($drupal_static_fast)) { - // Make use of the same static as entity info. - entity_get_info(); - $drupal_static_fast['entity_info'] = &drupal_static('entity_get_info'); - } - $info = &$drupal_static_fast['entity_info']; - - // First off check for entities, which may be represented by their ids too. - if (isset($info[$type])) { - if (is_object($data)) { - return TRUE; - } - elseif (isset($info[$type]['entity keys']['name'])) { - // Read the data type of the name key from the metadata if available. - $key = $info[$type]['entity keys']['name']; - $property_info = entity_get_property_info($type); - $property_type = isset($property_info['properties'][$key]['type']) ? $property_info['properties'][$key]['type'] : 'token'; - return entity_property_verify_data_type($data, $property_type); - } - return entity_property_verify_data_type($data, empty($info[$type]['fieldable']) ? 'text' : 'integer'); - } - - switch ($type) { - case 'site': - case 'unknown': - return TRUE; - case 'date': - case 'duration': - case 'integer': - return is_numeric($data) && strpos($data, '.') === FALSE; - case 'decimal': - return is_numeric($data); - case 'text': - return is_scalar($data); - case 'token': - return is_scalar($data) && preg_match('!^[a-z][a-z0-9_]*$!', $data); - case 'boolean': - return is_scalar($data) && (is_bool($data) || $data == 0 || $data == 1); - case 'uri': - return valid_url($data, TRUE); - case 'list': - return (is_array($data) && array_values($data) == $data) || (is_object($data) && $data instanceof EntityMetadataArrayObject); - case 'entity': - return is_object($data) && $data instanceof EntityDrupalWrapper; - default: - case 'struct': - return is_object($data) || is_array($data); - } -} - -/** - * Creates the entity object for an array of given property values. - * - * @param $entity_type - * The entity type to create an entity for. - * @param $values - * An array of values as described by the entity's property info. All entity - * properties of the given entity type that are marked as required, must be - * present. - * If the passed values have no matching property, their value will be - * assigned to the entity directly, without the use of the metadata-wrapper - * property. - * - * @return EntityDrupalWrapper - * An EntityDrupalWrapper wrapping the newly created entity or FALSE, if - * there were no information how to create the entity. - */ -function entity_property_values_create_entity($entity_type, $values = array()) { - if (entity_type_supports($entity_type, 'create')) { - $info = entity_get_info($entity_type); - // Create the initial entity by passing the values for all 'entity keys' - // to entity_create(). - $entity_keys = array_filter($info['entity keys']); - $creation_values = array_intersect_key($values, array_flip($entity_keys)); - - // In case the bundle key does not match the property that sets it, ensure - // the bundle key is initialized somehow, so entity_extract_ids() - // does not bail out during wrapper creation. - if (!empty($info['entity keys']['bundle'])) { - $creation_values += array($info['entity keys']['bundle'] => FALSE); - } - $entity = entity_create($entity_type, $creation_values); - - // Now set the remaining values using the wrapper. - $wrapper = entity_metadata_wrapper($entity_type, $entity); - foreach ($values as $key => $value) { - if (!in_array($key, $info['entity keys'])) { - if (isset($wrapper->$key)) { - $wrapper->$key->set($value); - } - else { - $entity->$key = $value; - } - } - } - // @todo: Once we require Drupal 7.7 or later, verify the entity has - // now a valid bundle and throw the EntityMalformedException if not. - return $wrapper; - } - return FALSE; -} - - -/** - * Extracts the contained type for a list type string like list. - * - * @return - * The contained type or FALSE, if the given type string is no list. - */ -function entity_property_list_extract_type($type) { - if (strpos($type, 'list<') === 0 && $type[strlen($type)-1] == '>') { - return substr($type, 5, -1); - } - return FALSE; -} - -/** - * Extracts the innermost type for a type string like list>. - * - * @param $type - * The type to examine. - * - * @return - * For list types, the innermost type. The type itself otherwise. - */ -function entity_property_extract_innermost_type($type) { - while (strpos($type, 'list<') === 0 && $type[strlen($type)-1] == '>') { - $type = substr($type, 5, -1); - } - return $type; -} - -/** - * Gets the property just as it is set in the data. - */ -function entity_property_verbatim_get($data, array $options, $name, $type, $info) { - $name = isset($info['schema field']) ? $info['schema field'] : $name; - if ((is_array($data) || (is_object($data) && $data instanceof ArrayAccess)) && isset($data[$name])) { - return $data[$name]; - } - elseif (is_object($data) && isset($data->$name)) { - // Incorporate i18n_string translations. We may rely on the entity class - // here as its usage is required by the i18n integration. - if (isset($options['language']) && !empty($info['i18n string'])) { - return $data->getTranslation($name, $options['language']->language); - } - else { - return $data->$name; - } - } - return NULL; -} - -/** - * Date values are converted from ISO strings to timestamp if needed. - */ -function entity_property_verbatim_date_get($data, array $options, $name, $type, $info) { - $name = isset($info['schema field']) ? $info['schema field'] : $name; - if (is_array($data) || (is_object($data) && $data instanceof ArrayAccess)) { - return is_numeric($data[$name]) ? $data[$name] : strtotime($data[$name], REQUEST_TIME); - } - elseif (is_object($data)) { - return is_numeric($data->$name) ? $data->$name : strtotime($data->$name, REQUEST_TIME); - } -} - -/** - * Sets the property to the given value. May be used as 'setter callback'. - */ -function entity_property_verbatim_set(&$data, $name, $value, $langcode, $type, $info) { - $name = isset($info['schema field']) ? $info['schema field'] : $name; - if (is_array($data) || (is_object($data) && $data instanceof ArrayAccess)) { - $data[$name] = $value; - } - elseif (is_object($data)) { - $data->$name = $value; - } -} - -/** - * Gets the property using the getter method (named just like the property). - */ -function entity_property_getter_method($object, array $options, $name) { - // Remove any underscores as classes are expected to use CamelCase. - $method = strtr($name, array('_' => '')); - return $object->$method(); -} - -/** - * Sets the property to the given value using the setter method. May be used as - * 'setter callback'. - */ -function entity_property_setter_method($object, $name, $value) { - // Remove any underscores as classes are expected to use CamelCase. - $method = 'set' . strtr($name, array('_' => '')); - // Invoke the setProperty() method where 'Property' is the property name. - $object->$method($value); -} - -/** - * Getter callback for getting an array. Makes sure it's numerically indexed. - */ -function entity_property_get_list($data, array $options, $name) { - return isset($data->$name) ? array_values($data->$name) : array(); -} - -/** - * A validation callback ensuring the passed integer is positive. - */ -function entity_property_validate_integer_positive($value) { - return $value > 0; -} - -/** - * A validation callback ensuring the passed integer is non-negative. - */ -function entity_property_validate_integer_non_negative($value) { - return $value >= 0; -} - -/** - * A simple auto-creation callback for array based data structures. - */ -function entity_property_create_array($property_name, $context) { - return array(); -} - -/** - * Flattens the given options in single dimensional array. - * We don't depend on options module, so we cannot use options_array_flatten(). - * - * @see options_array_flatten() - */ -function entity_property_options_flatten($options) { - $result = array(); - foreach ($options as $key => $value) { - if (is_array($value)) { - $result += $value; - } - else { - $result[$key] = $value; - } - } - return $result; -} - -/** - * Defines info for the properties of the text_formatted data structure. - */ -function entity_property_text_formatted_info() { - return array( - 'value' => array( - 'type' => 'text', - 'label' => t('Text'), - 'sanitized' => TRUE, - 'getter callback' => 'entity_metadata_field_text_get', - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer nodes', - 'raw getter callback' => 'entity_property_verbatim_get', - ), - 'summary' => array( - 'type' => 'text', - 'label' => t('Summary'), - 'sanitized' => TRUE, - 'getter callback' => 'entity_metadata_field_text_get', - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer nodes', - 'raw getter callback' => 'entity_property_verbatim_get', - ), - 'format' => array( - 'type' => 'token', - 'label' => t('Text format'), - 'options list' => 'entity_metadata_field_text_formats', - 'getter callback' => 'entity_property_verbatim_get', - 'setter callback' => 'entity_property_verbatim_set', - 'setter permissions' => 'administer filters', - ), - ); -} - -/** - * Defines info for the properties of the field_item_textsummary data structure. - */ -function entity_property_field_item_textsummary_info() { - return array( - 'value' => array( - 'type' => 'text', - 'label' => t('Text'), - 'setter callback' => 'entity_property_verbatim_set', - ), - 'summary' => array( - 'type' => 'text', - 'label' => t('Summary'), - 'setter callback' => 'entity_property_verbatim_set', - ), - ); -} - -/** - * Defines info for the properties of the file-field item data structure. - */ -function entity_property_field_item_file_info() { - $properties['file'] = array( - 'type' => 'file', - 'label' => t('The file.'), - 'getter callback' => 'entity_metadata_field_file_get', - 'setter callback' => 'entity_metadata_field_file_set', - 'required' => TRUE, - ); - $properties['description'] = array( - 'type' => 'text', - 'label' => t('The file description'), - 'setter callback' => 'entity_property_verbatim_set', - ); - $properties['display'] = array( - 'type' => 'boolean', - 'label' => t('Whether the file is being displayed.'), - 'setter callback' => 'entity_property_verbatim_set', - ); - return $properties; -} - -/** - * Defines info for the properties of the image-field item data structure. - */ -function entity_property_field_item_image_info() { - $properties['file'] = array( - 'type' => 'file', - 'label' => t('The image file.'), - 'getter callback' => 'entity_metadata_field_file_get', - 'setter callback' => 'entity_metadata_field_file_set', - 'required' => TRUE, - ); - $properties['alt'] = array( - 'type' => 'text', - 'label' => t('The "Alt" attribute text'), - 'setter callback' => 'entity_property_verbatim_set', - ); - $properties['title'] = array( - 'type' => 'text', - 'label' => t('The "Title" attribute text'), - 'setter callback' => 'entity_property_verbatim_set', - ); - return $properties; -} - - -/** - * Previously, hook_entity_property_info() has been provided by the removed - * entity metadata module. To provide backward compatibility for provided - * helpers that may be specified in hook_entity_property_info(), the following - * (deprecated) functions are provided. - */ - -/** - * Deprecated. - * Do not make use of this function, instead use the new one. - */ -function entity_metadata_verbatim_get($data, array $options, $name) { - return entity_property_verbatim_get($data, $options, $name); -} - -/** - * Deprecated. - * Do not make use of this function, instead use the new one. - */ -function entity_metadata_verbatim_set($data, $name, $value) { - return entity_property_verbatim_set($data, $name, $value); -} - -/** - * Deprecated. - * Do not make use of this function, instead use the new one. - */ -function entity_metadata_getter_method($object, array $options, $name) { - return entity_property_getter_method($object, $options, $name); -} - -/** - * Deprecated. - * Do not make use of this function, instead use the new one. - */ -function entity_metadata_setter_method($object, $name, $value) { - entity_property_setter_method($object, $name, $value); -} - -/** - * Deprecated. - * Do not make use of this function, instead use the new one. - */ -function entity_metadata_get_list($data, array $options, $name) { - return entity_property_get_list($data, $options, $name); -} - -/** - * Deprecated. - * Do not make use of this function, instead use the new one. - */ -function entity_metadata_validate_integer_positive($value) { - return entity_property_validate_integer_positive($value); -} - -/** - * Deprecated. - * Do not make use of this function, instead use the new one. - */ -function entity_metadata_validate_integer_non_negative($value) { - return entity_property_validate_integer_non_negative($value); -} - -/** - * Deprecated. - * Do not make use of this function, instead use the new one. - */ -function entity_metadata_text_formatted_properties() { - return entity_property_text_formatted_info(); -} diff --git a/includes/entity.ui.inc b/includes/entity.ui.inc deleted file mode 100644 index 24e3c2b..0000000 --- a/includes/entity.ui.inc +++ /dev/null @@ -1,767 +0,0 @@ -entityType = $entity_type; - $this->entityInfo = $entity_info; - $this->path = $this->entityInfo['admin ui']['path']; - $this->statusKey = empty($this->entityInfo['entity keys']['status']) ? 'status' : $this->entityInfo['entity keys']['status']; - } - - /** - * Provides definitions for implementing hook_menu(). - */ - public function hook_menu() { - $items = array(); - // Set this on the object so classes that extend hook_menu() can use it. - $this->id_count = count(explode('/', $this->path)); - $wildcard = isset($this->entityInfo['admin ui']['menu wildcard']) ? $this->entityInfo['admin ui']['menu wildcard'] : '%entity_object'; - $plural_label = isset($this->entityInfo['plural label']) ? $this->entityInfo['plural label'] : $this->entityInfo['label'] . 's'; - - $items[$this->path] = array( - 'title' => $plural_label, - 'page callback' => 'drupal_get_form', - 'page arguments' => array($this->entityType . '_overview_form', $this->entityType), - 'description' => 'Manage ' . $plural_label . '.', - 'access callback' => 'entity_access', - 'access arguments' => array('view', $this->entityType), - 'file' => 'includes/entity.ui.inc', - ); - $items[$this->path . '/list'] = array( - 'title' => 'List', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - $items[$this->path . '/add'] = array( - 'title callback' => 'entity_ui_get_action_title', - 'title arguments' => array('add', $this->entityType), - 'page callback' => 'entity_ui_get_form', - 'page arguments' => array($this->entityType, NULL, 'add'), - 'access callback' => 'entity_access', - 'access arguments' => array('create', $this->entityType), - 'type' => MENU_LOCAL_ACTION, - ); - $items[$this->path . '/manage/' . $wildcard] = array( - 'title' => 'Edit', - 'title callback' => 'entity_label', - 'title arguments' => array($this->entityType, $this->id_count + 1), - 'page callback' => 'entity_ui_get_form', - 'page arguments' => array($this->entityType, $this->id_count + 1), - 'load arguments' => array($this->entityType), - 'access callback' => 'entity_access', - 'access arguments' => array('update', $this->entityType, $this->id_count + 1), - ); - $items[$this->path . '/manage/' . $wildcard . '/edit'] = array( - 'title' => 'Edit', - 'load arguments' => array($this->entityType), - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - - // Clone form, a special case for the edit form. - $items[$this->path . '/manage/' . $wildcard . '/clone'] = array( - 'title' => 'Clone', - 'page callback' => 'entity_ui_get_form', - 'page arguments' => array($this->entityType, $this->id_count + 1, 'clone'), - 'load arguments' => array($this->entityType), - 'access callback' => 'entity_access', - 'access arguments' => array('create', $this->entityType), - ); - // Menu item for operations like revert and delete. - $items[$this->path . '/manage/' . $wildcard . '/%'] = array( - 'page callback' => 'drupal_get_form', - 'page arguments' => array($this->entityType . '_operation_form', $this->entityType, $this->id_count + 1, $this->id_count + 2), - 'load arguments' => array($this->entityType), - 'access callback' => 'entity_access', - 'access arguments' => array('delete', $this->entityType, $this->id_count + 1), - 'file' => 'includes/entity.ui.inc', - ); - - if (!empty($this->entityInfo['exportable'])) { - // Menu item for importing an entity. - $items[$this->path . '/import'] = array( - 'title callback' => 'entity_ui_get_action_title', - 'title arguments' => array('import', $this->entityType), - 'page callback' => 'drupal_get_form', - 'page arguments' => array($this->entityType . '_operation_form', $this->entityType, NULL, 'import'), - 'access callback' => 'entity_access', - 'access arguments' => array('create', $this->entityType), - 'file' => 'includes/entity.ui.inc', - 'type' => MENU_LOCAL_ACTION, - ); - } - - if (!empty($this->entityInfo['admin ui']['file'])) { - // Add in the include file for the entity form. - foreach (array("/manage/$wildcard", "/manage/$wildcard/clone", '/add') as $path_end) { - $items[$this->path . $path_end]['file'] = $this->entityInfo['admin ui']['file']; - $items[$this->path . $path_end]['file path'] = isset($this->entityInfo['admin ui']['file path']) ? $this->entityInfo['admin ui']['file path'] : drupal_get_path('module', $this->entityInfo['module']); - } - } - return $items; - } - - /** - * Provides definitions for implementing hook_forms(). - * - * Use per bundle form ids if possible, such that easy per bundle alterations - * are supported too. - * - * Note that for performance reasons, this method is invoked only for forms - * which receive the entity_type as first argument. Thus any forms added must - * follow that pattern. - * - * @see entity_forms() - */ - public function hook_forms() { - // The overview and the operation form are implemented by the controller, - // the callback and validation + submit handlers just invoke the controller. - $forms[$this->entityType . '_overview_form'] = array( - 'callback' => 'entity_ui_overview_form', - 'wrapper_callback' => 'entity_ui_form_defaults', - ); - $forms[$this->entityType . '_operation_form'] = array( - 'callback' => 'entity_ui_operation_form', - 'wrapper_callback' => 'entity_ui_form_defaults', - ); - - // The entity form (ENTITY_TYPE_form) handles editing, adding and cloning. - // For that form, the wrapper callback entity_ui_main_form_defaults() gets - // directly invoked via entity_ui_get_form(). - // If there are bundles though, we use form ids that include the bundle name - // (ENTITY_TYPE_edit_BUNDLE_NAME_form) to enable per bundle alterations - // as well as alterations based upon the base form id (ENTITY_TYPE_form). - if (!(count($this->entityInfo['bundles']) == 1 && isset($this->entityInfo['bundles'][$this->entityType]))) { - foreach ($this->entityInfo['bundles'] as $bundle => $bundle_info) { - $forms[$this->entityType . '_edit_' . $bundle . '_form']['callback'] = $this->entityType . '_form'; - // Again the wrapper callback is invoked by entity_ui_get_form() anyway. - } - } - return $forms; - } - - /** - * Builds the entity overview form. - */ - public function overviewForm($form, &$form_state) { - // By default just show a simple overview for all entities. - $form['table'] = $this->overviewTable(); - $form['pager'] = array('#theme' => 'pager'); - return $form; - } - - /** - * Overview form validation callback. - * - * @param $form - * The form array of the overview form. - * @param $form_state - * The overview form state which will be used for validating. - */ - public function overviewFormValidate($form, &$form_state) {} - - /** - * Overview form submit callback. - * - * @param $form - * The form array of the overview form. - * @param $form_state - * The overview form state which will be used for submitting. - */ - public function overviewFormSubmit($form, &$form_state) {} - - - /** - * Generates the render array for a overview table for arbitrary entities - * matching the given conditions. - * - * @param $conditions - * An array of conditions as needed by entity_load(). - - * @return Array - * A renderable array. - */ - public function overviewTable($conditions = array()) { - - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', $this->entityType); - - // Add all conditions to query. - foreach ($conditions as $key => $value) { - $query->propertyCondition($key, $value); - } - - if ($this->overviewPagerLimit) { - $query->pager($this->overviewPagerLimit); - } - - $results = $query->execute(); - - $ids = isset($results[$this->entityType]) ? array_keys($results[$this->entityType]) : array(); - $entities = $ids ? entity_load($this->entityType, $ids) : array(); - ksort($entities); - - $rows = array(); - foreach ($entities as $entity) { - $rows[] = $this->overviewTableRow($conditions, entity_id($this->entityType, $entity), $entity); - } - - $render = array( - '#theme' => 'table', - '#header' => $this->overviewTableHeaders($conditions, $rows), - '#rows' => $rows, - '#empty' => t('None.'), - ); - return $render; - } - - /** - * Generates the table headers for the overview table. - */ - protected function overviewTableHeaders($conditions, $rows, $additional_header = array()) { - $header = $additional_header; - array_unshift($header, t('Label')); - if (!empty($this->entityInfo['exportable'])) { - $header[] = t('Status'); - } - // Add operations with the right colspan. - $header[] = array('data' => t('Operations'), 'colspan' => $this->operationCount()); - return $header; - } - - /** - * Returns the operation count for calculating colspans. - */ - protected function operationCount() { - $count = 3; - $count += !empty($this->entityInfo['bundle of']) && entity_type_is_fieldable($this->entityInfo['bundle of']) && module_exists('field_ui') ? 2 : 0; - $count += !empty($this->entityInfo['exportable']) ? 1 : 0; - $count += !empty($this->entityInfo['i18n controller class']) ? 1 : 0; - return $count; - } - - /** - * Generates the row for the passed entity and may be overridden in order to - * customize the rows. - * - * @param $additional_cols - * Additional columns to be added after the entity label column. - */ - protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) { - $entity_uri = entity_uri($this->entityType, $entity); - - $row[] = array('data' => array( - '#theme' => 'entity_ui_overview_item', - '#label' => entity_label($this->entityType, $entity), - '#name' => !empty($this->entityInfo['exportable']) ? entity_id($this->entityType, $entity) : FALSE, - '#url' => $entity_uri ? $entity_uri : FALSE, - '#entity_type' => $this->entityType), - ); - - // Add in any passed additional cols. - foreach ($additional_cols as $col) { - $row[] = $col; - } - - // Add a row for the exportable status. - if (!empty($this->entityInfo['exportable'])) { - $row[] = array('data' => array( - '#theme' => 'entity_status', - '#status' => $entity->{$this->statusKey}, - )); - } - // In case this is a bundle, we add links to the field ui tabs. - $field_ui = !empty($this->entityInfo['bundle of']) && entity_type_is_fieldable($this->entityInfo['bundle of']) && module_exists('field_ui'); - // For exportable entities we add an export link. - $exportable = !empty($this->entityInfo['exportable']); - // If i18n integration is enabled, add a link to the translate tab. - $i18n = !empty($this->entityInfo['i18n controller class']); - - // Add operations depending on the status. - if (entity_has_status($this->entityType, $entity, ENTITY_FIXED)) { - $row[] = array('data' => l(t('clone'), $this->path . '/manage/' . $id . '/clone'), 'colspan' => $this->operationCount()); - } - else { - $row[] = l(t('edit'), $this->path . '/manage/' . $id); - - if ($field_ui) { - $row[] = l(t('manage fields'), $this->path . '/manage/' . $id . '/fields'); - $row[] = l(t('manage display'), $this->path . '/manage/' . $id . '/display'); - } - if ($i18n) { - $row[] = l(t('translate'), $this->path . '/manage/' . $id . '/translate'); - } - if ($exportable) { - $row[] = l(t('clone'), $this->path . '/manage/' . $id . '/clone'); - } - - if (empty($this->entityInfo['exportable']) || !entity_has_status($this->entityType, $entity, ENTITY_IN_CODE)) { - $row[] = l(t('delete'), $this->path . '/manage/' . $id . '/delete', array('query' => drupal_get_destination())); - } - elseif (entity_has_status($this->entityType, $entity, ENTITY_OVERRIDDEN)) { - $row[] = l(t('revert'), $this->path . '/manage/' . $id . '/revert', array('query' => drupal_get_destination())); - } - else { - $row[] = ''; - } - } - if ($exportable) { - $row[] = l(t('export'), $this->path . '/manage/' . $id . '/export'); - } - return $row; - } - - - /** - * Builds the operation form. - * - * For the export operation a serialized string of the entity is directly - * shown in the form (no submit function needed). - */ - public function operationForm($form, &$form_state, $entity, $op) { - switch ($op) { - case 'revert': - $label = entity_label($this->entityType, $entity); - $confirm_question = t('Are you sure you want to revert the %entity %label?', array('%entity' => $this->entityInfo['label'], '%label' => $label)); - return confirm_form($form, $confirm_question, $this->path); - - case 'delete': - $label = entity_label($this->entityType, $entity); - $confirm_question = t('Are you sure you want to delete the %entity %label?', array('%entity' => $this->entityInfo['label'], '%label' => $label)); - return confirm_form($form, $confirm_question, $this->path); - - case 'export': - if (!empty($this->entityInfo['exportable'])) { - $export = entity_export($this->entityType, $entity); - $form['export'] = array( - '#type' => 'textarea', - '#title' => t('Export'), - '#description' => t('For importing copy the content of the text area and paste it into the import page.'), - '#rows' => 25, - '#default_value' => $export, - ); - return $form; - } - - case 'import': - $form['import'] = array( - '#type' => 'textarea', - '#title' => t('Import'), - '#description' => t('Paste an exported %entity_type here.', array('%entity_type' => $this->entityInfo['label'])), - '#rows' => 20, - ); - $form['overwrite'] = array( - '#title' => t('Overwrite'), - '#type' => 'checkbox', - '#description' => t('If checked, any existing %entity with the same identifier will be replaced by the import.', array('%entity' => $this->entityInfo['label'])), - '#default_value' => FALSE, - ); - $form['submit'] = array( - '#type' => 'submit', - '#value' => t('Import'), - ); - return $form; - } - drupal_not_found(); - exit; - } - - /** - * Operation form validation callback. - */ - public function operationFormValidate($form, &$form_state) { - if ($form_state['op'] == 'import') { - if ($entity = entity_import($this->entityType, $form_state['values']['import'])) { - // Store the successfully imported entity in $form_state. - $form_state[$this->entityType] = $entity; - if (!$form_state['values']['overwrite']) { - // Check for existing entities with the same identifier. - $id = entity_id($this->entityType, $entity); - $entities = entity_load($this->entityType, array($id)); - if (!empty($entities)) { - $label = entity_label($this->entityType, $entity); - $vars = array('%entity' => $this->entityInfo['label'], '%label' => $label); - form_set_error('import', t('Import of %entity %label failed, a %entity with the same machine name already exists. Check the overwrite option to replace it.', $vars)); - } - } - } - else { - form_set_error('import', t('Import failed.')); - } - } - } - - /** - * Operation form submit callback. - */ - public function operationFormSubmit($form, &$form_state) { - $msg = $this->applyOperation($form_state['op'], $form_state[$this->entityType]); - drupal_set_message($msg); - $form_state['redirect'] = $this->path; - } - - /** - * Applies an operation to the given entity. - * - * Note: the export operation is directly carried out by the operationForm() - * method. - * - * @param string $op - * The operation (revert, delete or import). - * @param $entity - * The entity to manipulate. - * - * @return - * The status message of what has been applied. - */ - public function applyOperation($op, $entity) { - $label = entity_label($this->entityType, $entity); - $vars = array('%entity' => $this->entityInfo['label'], '%label' => $label); - $id = entity_id($this->entityType, $entity); - $edit_link = l(t('edit'), $this->path . '/manage/' . $id . '/edit'); - - switch ($op) { - case 'revert': - entity_delete($this->entityType, $id); - watchdog($this->entityType, 'Reverted %entity %label to the defaults.', $vars, WATCHDOG_NOTICE, $edit_link); - return t('Reverted %entity %label to the defaults.', $vars); - - case 'delete': - entity_delete($this->entityType, $id); - watchdog($this->entityType, 'Deleted %entity %label.', $vars); - return t('Deleted %entity %label.', $vars); - - case 'import': - // First check if there is any existing entity with the same ID. - $id = entity_id($this->entityType, $entity); - $entities = entity_load($this->entityType, array($id)); - if ($existing_entity = reset($entities)) { - // Copy DB id and remove the new indicator to overwrite the DB record. - $idkey = $this->entityInfo['entity keys']['id']; - $entity->{$idkey} = $existing_entity->{$idkey}; - unset($entity->is_new); - } - entity_save($this->entityType, $entity); - watchdog($this->entityType, 'Imported %entity %label.', $vars); - return t('Imported %entity %label.', $vars); - - default: - return FALSE; - } - } - - /** - * Entity submit builder invoked via entity_ui_form_submit_build_entity(). - * - * Extracts the form values and updates the entity. - * - * The provided implementation makes use of the helper function - * entity_form_submit_build_entity() provided by core, which already invokes - * the field API attacher for fieldable entities. - * - * @return - * The updated entity. - * - * @see entity_ui_form_submit_build_entity() - */ - public function entityFormSubmitBuildEntity($form, &$form_state) { - // Add the bundle property to the entity if the entity type supports bundles - // and the form provides a value for the bundle key. Especially new entities - // need to have their bundle property pre-populated before we invoke - // entity_form_submit_build_entity(). - if (!empty($this->entityInfo['entity keys']['bundle']) && isset($form_state['values'][$this->entityInfo['entity keys']['bundle']])) { - $form_state[$this->entityType]->{$this->entityInfo['entity keys']['bundle']} = $form_state['values'][$this->entityInfo['entity keys']['bundle']]; - } - entity_form_submit_build_entity($this->entityType, $form_state[$this->entityType], $form, $form_state); - return $form_state[$this->entityType]; - } -} - -/** - * UI controller providing UI for content entities. - * - * For a controller providing UI for bundleable content entities, see - * EntityBundleableUIController. - * For a controller providing admin UI for configuration entities, see - * EntityDefaultUIController. - */ -class EntityContentUIController extends EntityDefaultUIController { - - /** - * Provides definitions for implementing hook_menu(). - */ - public function hook_menu() { - $items = parent::hook_menu(); - $wildcard = isset($this->entityInfo['admin ui']['menu wildcard']) ? $this->entityInfo['admin ui']['menu wildcard'] : '%entity_object'; - - // Unset the manage entity path, as the provided UI is for admin entities. - unset($items[$this->path]); - - $defaults = array( - 'file' => $this->entityInfo['admin ui']['file'], - 'file path' => isset($this->entityInfo['admin ui']['file path']) ? $this->entityInfo['admin ui']['file path'] : drupal_get_path('module', $this->entityInfo['module']), - ); - - // Add view, edit and delete menu items for content entities. - $items[$this->path . '/' . $wildcard] = array( - 'title callback' => 'entity_ui_get_page_title', - 'title arguments' => array('view', $this->entityType, $this->id_count), - 'page callback' => 'entity_ui_entity_page_view', - 'page arguments' => array($this->id_count), - 'load arguments' => array($this->entityType), - 'access callback' => 'entity_access', - 'access arguments' => array('view', $this->entityType, $this->id_count), - ) + $defaults; - $items[$this->path . '/' . $wildcard . '/view'] = array( - 'title' => 'View', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'load arguments' => array($this->entityType), - 'weight' => -10, - ) + $defaults; - $items[$this->path . '/' . $wildcard . '/edit'] = array( - 'page callback' => 'entity_ui_get_form', - 'page arguments' => array($this->entityType, $this->id_count), - 'load arguments' => array($this->entityType), - 'access callback' => 'entity_access', - 'access arguments' => array('edit', $this->entityType, $this->id_count), - 'title' => 'Edit', - 'type' => MENU_LOCAL_TASK, - 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, - ) + $defaults; - $items[$this->path . '/' . $wildcard . '/delete'] = array( - 'page callback' => 'drupal_get_form', - 'page arguments' => array($this->entityType . '_operation_form', $this->entityType, $this->id_count, 'delete'), - 'load arguments' => array($this->entityType), - 'access callback' => 'entity_access', - 'access arguments' => array('delete', $this->entityType, $this->id_count), - 'title' => 'Delete', - 'type' => MENU_LOCAL_TASK, - 'context' => MENU_CONTEXT_INLINE, - 'file' => $this->entityInfo['admin ui']['file'], - 'file path' => isset($this->entityInfo['admin ui']['file path']) ? $this->entityInfo['admin ui']['file path'] : drupal_get_path('module', $this->entityInfo['module']), - ) + $defaults; - - return $items; - } - - /** - * Operation form submit callback. - */ - public function operationFormSubmit($form, &$form_state) { - parent::operationFormSubmit($form, $form_state); - // The manage entity path is unset for the content entity UI. - $form_state['redirect'] = ''; - } -} - -/** - * UI controller providing UI for bundleable content entities. - * - * Adds a bundle selection page to the entity/add path, analogously to the - * node/add path. - */ -class EntityBundleableUIController extends EntityContentUIController { - - /** - * Provides definitions for implementing hook_menu(). - */ - public function hook_menu() { - $items = parent::hook_menu(); - - // Extend the 'add' path. - $items[$this->path . '/add'] = array( - 'title callback' => 'entity_ui_get_action_title', - 'title arguments' => array('add', $this->entityType), - 'page callback' => 'entity_ui_bundle_add_page', - 'page arguments' => array($this->entityType), - 'access callback' => 'entity_access', - 'access arguments' => array('create', $this->entityType), - 'type' => MENU_LOCAL_ACTION, - ); - $items[$this->path . '/add/%'] = array( - 'title callback' => 'entity_ui_get_action_title', - 'title arguments' => array('add', $this->entityType, $this->id_count + 1), - 'page callback' => 'entity_ui_get_bundle_add_form', - 'page arguments' => array($this->entityType, $this->id_count + 1), - 'access callback' => 'entity_access', - 'access arguments' => array('create', $this->entityType), - ); - - if (!empty($this->entityInfo['admin ui']['file'])) { - // Add in the include file for the entity form. - foreach (array('/add', '/add/%') as $path_end) { - $items[$this->path . $path_end]['file'] = $this->entityInfo['admin ui']['file']; - $items[$this->path . $path_end]['file path'] = isset($this->entityInfo['admin ui']['file path']) ? $this->entityInfo['admin ui']['file path'] : drupal_get_path('module', $this->entityInfo['module']); - } - } - - return $items; - } -} - -/** - * Form builder function for the overview form. - * - * @see EntityDefaultUIController::overviewForm() - */ -function entity_ui_overview_form($form, &$form_state, $entity_type) { - return entity_ui_controller($entity_type)->overviewForm($form, $form_state); -} - -/** - * Form builder for the entity operation form. - * - * @see EntityDefaultUIController::operationForm() - */ -function entity_ui_operation_form($form, &$form_state, $entity_type, $entity, $op) { - $form_state['op'] = $op; - return entity_ui_controller($entity_type)->operationForm($form, $form_state, $entity, $op); -} - -/** - * Form wrapper the main entity form. - * - * @see entity_ui_form_defaults() - */ -function entity_ui_main_form_defaults($form, &$form_state, $entity = NULL, $op = NULL) { - // Now equals entity_ui_form_defaults() but is still here to keep backward - // compatibility. - return entity_ui_form_defaults($form, $form_state, $form_state['entity_type'], $entity, $op); -} - -/** - * Clones the entity object and makes sure it will get saved as new entity. - * - * @return - * The cloned entity object. - */ -function entity_ui_clone_entity($entity_type, $entity) { - // Clone the entity and make sure it will get saved as a new entity. - $entity = clone $entity; - - $entity_info = entity_get_info($entity_type); - $entity->{$entity_info['entity keys']['id']} = FALSE; - if (!empty($entity_info['entity keys']['name'])) { - $entity->{$entity_info['entity keys']['name']} = FALSE; - } - $entity->is_new = TRUE; - - // Make sure the status of a cloned exportable is custom. - if (!empty($entity_info['exportable'])) { - $status_key = isset($entity_info['entity keys']['status']) ? $entity_info['entity keys']['status'] : 'status'; - $entity->$status_key = ENTITY_CUSTOM; - } - return $entity; -} - -/** - * Form wrapper callback for all entity ui forms. - * - * This callback makes sure the form state is properly initialized and sets - * some useful default titles. - * - * @see EntityDefaultUIController::hook_forms() - */ -function entity_ui_form_defaults($form, &$form_state, $entity_type, $entity = NULL, $op = NULL) { - $defaults = array( - 'entity_type' => $entity_type, - ); - if (isset($entity)) { - $defaults[$entity_type] = $entity; - } - if (isset($op)) { - $defaults['op'] = $op; - } - $form_state += $defaults; - if (isset($op)) { - drupal_set_title(entity_ui_get_page_title($op, $entity_type, $entity), PASS_THROUGH); - } - // Add in handlers pointing to the controller for the forms implemented by it. - if (isset($form_state['build_info']['base_form_id']) && $form_state['build_info']['base_form_id'] != $entity_type . '_form') { - $form['#validate'][] = 'entity_ui_controller_form_validate'; - $form['#submit'][] = 'entity_ui_controller_form_submit'; - } - return $form; -} - -/** - * Validation callback for forms implemented by the UI controller. - */ -function entity_ui_controller_form_validate($form, &$form_state) { - // Remove 'entity_ui_' prefix and the '_form' suffix. - $base = substr($form_state['build_info']['base_form_id'], 10, -5); - $method = $base . 'FormValidate'; - entity_ui_controller($form_state['entity_type'])->$method($form, $form_state); -} - -/** - * Submit callback for forms implemented by the UI controller. - */ -function entity_ui_controller_form_submit($form, &$form_state) { - // Remove 'entity_ui_' prefix and the '_form' suffix. - $base = substr($form_state['build_info']['base_form_id'], 10, -5); - $method = $base . 'FormSubmit'; - entity_ui_controller($form_state['entity_type'])->$method($form, $form_state); -} - -/** - * Submit builder for the main entity form, which extracts the form values and updates the entity. - * - * This is a helper function for entities making use of the entity UI - * controller. - * - * @return - * The updated entity. - * - * @see EntityDefaultUIController::hook_forms() - * @see EntityDefaultUIController::entityFormSubmitBuildEntity() - */ -function entity_ui_form_submit_build_entity($form, &$form_state) { - return entity_ui_controller($form_state['entity_type'])->entityFormSubmitBuildEntity($form, $form_state); -} - -/** - * Validation callback for machine names of exportables. - * - * We don't allow numeric machine names, as entity_load() treats them as the - * numeric identifier and they are easily confused with ids in general. - */ -function entity_ui_validate_machine_name($element, &$form_state) { - if (is_numeric($element['#value'])) { - form_error($element, t('Machine-readable names must not consist of numbers only.')); - } -} - -/** - * Returns HTML for an entity on the entity overview listing. - * - * @ingroup themeable - */ -function theme_entity_ui_overview_item($variables) { - $output = $variables['url'] ? l($variables['label'], $variables['url']['path'], $variables['url']['options']) : check_plain($variables['label']); - if ($variables['name']) { - $output .= ' (' . t('Machine name') . ': ' . check_plain($variables['name']) . ')'; - } - return $output; -} - diff --git a/includes/entity.wrapper.inc b/includes/entity.wrapper.inc deleted file mode 100644 index 860d2c3..0000000 --- a/includes/entity.wrapper.inc +++ /dev/null @@ -1,1273 +0,0 @@ -type = $type; - $this->info = $info + array( - 'langcode' => NULL, - ); - $this->info['type'] = $type; - if (isset($data)) { - $this->set($data); - } - } - - /** - * Gets info about the wrapped data. - * - * @return Array - * Keys set are all keys as specified for a property in hook_entity_info() - * as well as possible the following keys: - * - name: If this wraps a property, the name of the property. - * - parent: The parent wrapper, if any. - * - langcode: The language code, if this data is language specific. - */ - public function info() { - return $this->info; - } - - /** - * Gets the (entity)type of the wrapped data. - */ - public function type() { - return $this->type; - } - - /** - * Returns the wrapped data. If no options are given the data is returned as - * described in the info. - * - * @param $options - * (optional) A keyed array of options: - * - sanitize: A boolean flag indicating that textual properties should be - * sanitized for display to a web browser. Defaults to FALSE. - * - decode: If set to TRUE and some textual data is already sanitized, it - * strips HTML tags and decodes HTML entities. Defaults to FALSE. - * - * @return - * The value of the wrapped data. If the data property is not set, NULL - * is returned. - * - * @throws EntityMetadataWrapperException - * In case there are no data values available to the wrapper, an exception - * is thrown. E.g. if the value for an entity property is to be retrieved - * and there is no entity available, the exception is thrown. However, if - * an entity is available but the property is not set, NULL is returned. - */ - public function value(array $options = array()) { - if (!$this->dataAvailable() && isset($this->info['parent'])) { - throw new EntityMetadataWrapperException('Missing data values.'); - } - if (!isset($this->data) && isset($this->info['name'])) { - $this->data = $this->info['parent']->getPropertyValue($this->info['name'], $this->info); - } - return $this->data; - } - - /** - * Returns the raw, unprocessed data. Most times this is the same as returned - * by value(), however for already processed and sanitized textual data, this - * will return the unprocessed data in contrast to value(). - */ - public function raw() { - if (!$this->dataAvailable()) { - throw new EntityMetadataWrapperException('Missing data values.'); - } - if (isset($this->info['name']) && isset($this->info['parent'])) { - return $this->info['parent']->getPropertyRaw($this->info['name'], $this->info); - } - // Else return the usual value, which should be raw in this case. - return $this->value(); - } - - /** - * Returns whether data is available to work with. - * - * @return - * If we operate without any data FALSE, else TRUE. - */ - protected function dataAvailable() { - return isset($this->data) || (isset($this->info['parent']) && $this->info['parent']->dataAvailable()); - } - - /** - * Set a new data value. - */ - public function set($value) { - if (!$this->validate($value)) { - throw new EntityMetadataWrapperException(t('Invalid data value given. Be sure it matches the required data type and format. Value at !location: !value.', array( - // An exception's message is output through check_plain(). - '!value' => is_array($value) || is_object($value) ? var_export($value, TRUE) : $value, - '!location' => $this->debugIdentifierLocation(), - ))); - } - $this->clear(); - $this->data = $value; - $this->updateParent($value); - return $this; - } - - /** - * Updates the parent data structure of a data property with the latest data value. - */ - protected function updateParent($value) { - if (isset($this->info['parent'])) { - $this->info['parent']->setProperty($this->info['name'], $value); - } - } - - /** - * Returns whether $value is a valid value to set. - */ - public function validate($value) { - if (isset($value) && !entity_property_verify_data_type($value, $this->type)) { - return FALSE; - } - // Only proceed with further checks if this is not a list item. If this is - // a list item, the checks are performed on the list property level. - if (isset($this->info['parent']) && $this->info['parent'] instanceof EntityListWrapper) { - return TRUE; - } - if (!isset($value) && !empty($this->info['required'])) { - // Do not allow NULL values if the property is required. - return FALSE; - } - return !isset($this->info['validation callback']) || call_user_func($this->info['validation callback'], $value, $this->info); - } - - public function __toString() { - return isset($this->info) ? 'Property ' . $this->info['name'] : $this->type; - } - - /** - * Clears the data value and the wrapper cache. - */ - protected function clear() { - $this->data = NULL; - foreach ($this->cache as $wrapper) { - $wrapper->clear(); - } - } - - /** - * Returns the options list specifying possible values for the property, if - * defined. - * - * @param $op - * (optional) One of 'edit' or 'view'. In case the list of possible values - * a user could set for a property differs from the list of values a - * property could have, $op determines which options should be returned. - * Defaults to 'edit'. - * E.g. all possible roles a user could have include the anonymous and the - * authenticated user roles, while those roles cannot be added to a user - * account. So their options would be included for 'view', but for 'edit' - * not. - * - * @return - * An array as used by hook_options_list() or FALSE. - */ - public function optionsList($op = 'edit') { - if (isset($this->info['options list']) && is_callable($this->info['options list'])) { - $name = isset($this->info['name']) ? $this->info['name'] : NULL; - return call_user_func($this->info['options list'], $name, $this->info, $op); - } - return FALSE; - } - - /** - * Returns the label for the currently set property value if there is one - * available, i.e. if an options list has been specified. - */ - public function label() { - if ($options = $this->optionsList('view')) { - $options = entity_property_options_flatten($options); - $value = $this->value(); - if (is_scalar($value) && isset($options[$value])) { - return $options[$value]; - } - } - } - - /** - * Determines whether the given user has access to view or edit this property. - * Apart from relying on access metadata of properties, this takes into - * account information about entity level access, if available: - * - Referenced entities can only be viewed, when the user also has - * permission to view the entity. - * - A property may be only edited, if the user has permission to update the - * entity containing the property. - * - * @param $op - * The operation being performed. One of 'view' or 'edit. - * @param $account - * The user to check for. Leave it to NULL to check for the global user. - * @return boolean - * Whether access to entity property is allowed for the given operation. - * However if we wrap no data, it returns whether access is allowed to the - * property of all entities of this type. - * If there is no access information for this property, TRUE is returned. - */ - public function access($op, $account = NULL) { - return !empty($this->info['parent']) ? $this->info['parent']->propertyAccess($this->info['name'], $op, $account) : TRUE; - } - - /** - * Returns a string to use to identify this wrapper in error messages. - * - * @return - * A string that identifies this wrapper and its chain of ancestors, of the - * form 'grandparentidentifier->parentidentifier->identifier'. - */ - public function debugIdentifierLocation() { - $debug = $this->info['name']; - if (isset($this->info['parent'])) { - $debug = $this->info['parent']->debugIdentifierLocation() . '->' . $debug; - } - return $debug; - } - - /** - * Prepare for serializiation. - */ - public function __sleep() { - $vars = get_object_vars($this); - unset($vars['cache']); - return drupal_map_assoc(array_keys($vars)); - } -} - -/** - * Wraps a single value. - */ -class EntityValueWrapper extends EntityMetadataWrapper { - - /** - * Overrides EntityMetadataWrapper#value(). - * Sanitizes or decode textual data if necessary. - */ - public function value(array $options = array()) { - $data = parent::value(); - if ($this->type == 'text' && isset($data)) { - $info = $this->info + array('sanitized' => FALSE, 'sanitize' => 'check_plain'); - $options += array('sanitize' => FALSE, 'decode' => FALSE); - if ($options['sanitize'] && !$info['sanitized']) { - return call_user_func($info['sanitize'], $data); - } - elseif ($options['decode'] && $info['sanitized']) { - return decode_entities(strip_tags($data)); - } - } - return $data; - } -} - -/** - * Provides a general wrapper for any data structure. For this to work the - * metadata has to be passed during construction. - */ -class EntityStructureWrapper extends EntityMetadataWrapper implements IteratorAggregate { - - protected $propertyInfo = array(), $propertyInfoAltered = FALSE; - protected $langcode = LANGUAGE_NONE; - - protected $propertyInfoDefaults = array( - 'type' => 'text', - 'getter callback' => 'entity_property_verbatim_get', - 'clear' => array(), - ); - - /** - * Construct a new EntityStructureWrapper object. - * - * @param $type - * The type of the passed data. - * @param $data - * Optional. The data to wrap. - * @param $info - * Used to for specifying metadata about the data and internally to pass - * info about properties down the tree. For specifying metadata known keys - * are: - * - property info: An array of info about the properties of the wrapped - * data structure. It has to contain an array of property info in the same - * structure as used by hook_entity_property_info(). - */ - public function __construct($type, $data = NULL, $info = array()) { - parent::__construct($type, $data, $info); - $this->info += array('property defaults' => array()); - $info += array('property info' => array()); - $this->propertyInfo['properties'] = $info['property info']; - } - - /** - * May be used to lazy-load additional info about the data, depending on the - * concrete passed data. - */ - protected function spotInfo() { - // Apply the callback if set, such that the caller may alter the info. - if (!empty($this->info['property info alter']) && !$this->propertyInfoAltered) { - $this->propertyInfo = call_user_func($this->info['property info alter'], $this, $this->propertyInfo); - $this->propertyInfoAltered = TRUE; - } - } - - /** - * Gets the info about the given property. - * - * @param $name - * The name of the property. If not given, info about all properties will - * be returned. - * @throws EntityMetadataWrapperException - * If there is no such property. - * @return - * An array of info about the property. - */ - public function getPropertyInfo($name = NULL) { - $this->spotInfo(); - if (!isset($name)) { - return $this->propertyInfo['properties']; - } - if (!isset($this->propertyInfo['properties'][$name])) { - throw new EntityMetadataWrapperException('Unknown data property ' . check_plain($name) . '.'); - } - return $this->propertyInfo['properties'][$name] + $this->info['property defaults'] + $this->propertyInfoDefaults; - } - - /** - * Returns a reference on the property info. - * - * If possible, use the property info alter callback for spotting metadata. - * The reference may be used to alter the property info for any remaining - * cases, e.g. if additional metadata has been asserted. - */ - public function &refPropertyInfo() { - return $this->propertyInfo; - } - - /** - * Sets a new language to use for retrieving properties. - * - * @param $langcode - * The language code of the language to set. - * @return EntityWrapper - */ - public function language($langcode = LANGUAGE_NONE) { - if ($langcode != $this->langcode) { - $this->langcode = $langcode; - $this->cache = array(); - } - return $this; - } - - /** - * Gets the language used for retrieving properties. - * - * @return String - * The language object of the language or NULL for the default language. - * - * @see EntityStructureWrapper::language() - */ - public function getPropertyLanguage() { - if ($this->langcode != LANGUAGE_NONE && $list = language_list()) { - if (isset($list[$this->langcode])) { - return $list[$this->langcode]; - } - } - return NULL; - } - - /** - * Get the wrapper for a property. - * - * @return - * An instance of EntityMetadataWrapper. - */ - public function get($name) { - // Look it up in the cache if possible. - if (!array_key_exists($name, $this->cache)) { - if ($info = $this->getPropertyInfo($name)) { - $info += array('parent' => $this, 'name' => $name, 'langcode' => $this->langcode, 'property defaults' => array()); - $info['property defaults'] += $this->info['property defaults']; - $this->cache[$name] = entity_metadata_wrapper($info['type'], NULL, $info); - } - else { - throw new EntityMetadataWrapperException('There is no property ' . check_plain($name) . " for this entity."); - } - } - return $this->cache[$name]; - } - - /** - * Magic method: Get a wrapper for a property. - */ - public function __get($name) { - if (strpos($name, 'krumo') === 0) { - // #914934 Ugly workaround to allow krumo to write its recursion property. - // This is necessary to make dpm() work without throwing exceptions. - return NULL; - } - $get = $this->get($name); - return $get; - } - - /** - * Magic method: Set a property. - */ - public function __set($name, $value) { - if (strpos($name, 'krumo') === 0) { - // #914934 Ugly workaround to allow krumo to write its recursion property. - // This is necessary to make dpm() work without throwing exceptions. - $this->$name = $value; - } - else { - $this->get($name)->set($value); - } - } - - /** - * Gets the value of a property. - */ - protected function getPropertyValue($name, &$info) { - $options = array('language' => $this->getPropertyLanguage(), 'absolute' => TRUE); - $data = $this->value(); - if (!isset($data)) { - throw new EntityMetadataWrapperException('Unable to get the data property ' . check_plain($name) . ' as the parent data structure is not set.'); - } - return $info['getter callback']($data, $options, $name, $this->type, $info); - } - - /** - * Gets the raw value of a property. - */ - protected function getPropertyRaw($name, &$info) { - if (!empty($info['raw getter callback'])) { - $options = array('language' => $this->getPropertyLanguage(), 'absolute' => TRUE); - $data = $this->value(); - if (!isset($data)) { - throw new EntityMetadataWrapperException('Unable to get the data property ' . check_plain($name) . ' as the parent data structure is not set.'); - } - return $info['raw getter callback']($data, $options, $name, $this->type, $info); - } - return $this->getPropertyValue($name, $info); - } - - /** - * Sets a property. - */ - protected function setProperty($name, $value) { - $info = $this->getPropertyInfo($name); - if (!empty($info['setter callback'])) { - $data = $this->value(); - - // In case the data structure is not set, support simple auto-creation - // for arrays. Else an exception is thrown. - if (!isset($data)) { - if (!empty($this->info['auto creation']) && !($this instanceof EntityDrupalWrapper)) { - $data = $this->info['auto creation']($name, $this->info); - } - else { - throw new EntityMetadataWrapperException('Unable to set the data property ' . check_plain($name) . ' as the parent data structure is not set.'); - } - } - - // Invoke the setter callback for updating our data. - $info['setter callback']($data, $name, $value, $this->langcode, $this->type, $info); - - // If the setter has not thrown any exceptions, proceed and apply the - // update to the current and any parent wrappers as necessary. - $data = $this->info['type'] == 'entity' ? $this : $data; - $this->set($data); - - // Clear the cache of properties dependent on this value. - foreach ($info['clear'] as $name) { - if (isset($this->cache[$name])) { - $this->cache[$name]->clear(); - } - } - } - else { - throw new EntityMetadataWrapperException('Entity property ' . check_plain($name) . " doesn't support writing."); - } - } - - protected function propertyAccess($name, $op, $account = NULL) { - $info = $this->getPropertyInfo($name); - - // If a property should be edited and this is part of an entity, make sure - // the user has update access for this entity. - if ($op == 'edit') { - $entity = $this; - while (!($entity instanceof EntityDrupalWrapper) && isset($entity->info['parent'])) { - $entity = $entity->info['parent']; - } - if ($entity instanceof EntityDrupalWrapper && $entity->entityAccess('update', $account) === FALSE) { - return FALSE; - } - } - if (!empty($info['access callback'])) { - $data = $this->dataAvailable() ? $this->value() : NULL; - return call_user_func($info['access callback'], $op, $name, $data, $account, $this->type); - } - elseif ($op == 'edit' && isset($info['setter permission'])) { - return user_access($info['setter permission'], $account); - } - // If access is unknown, we return TRUE. - return TRUE; - } - - /** - * Magic method: Can be used to check if a property is known. - */ - public function __isset($name) { - $this->spotInfo(); - return isset($this->propertyInfo['properties'][$name]); - } - - public function getIterator() { - $this->spotInfo(); - return new EntityMetadataWrapperIterator($this, array_keys($this->propertyInfo['properties'])); - } - - /** - * Returns the identifier of the data structure. If there is none, NULL is - * returned. - */ - public function getIdentifier() { - return isset($this->id) && $this->dataAvailable() ? $this->id->value() : NULL; - } - - /** - * Prepare for serializiation. - */ - public function __sleep() { - $vars = parent::__sleep(); - unset($vars['propertyInfoDefaults']); - return $vars; - } - - public function clear() { - $this->propertyInfoAltered = FALSE; - parent::clear(); - } -} - -/** - * Provides a wrapper for entities registrered in hook_entity_info(). - * - * The wrapper eases applying getter and setter callbacks of entity properties - * specified in hook_entity_property_info(). - */ -class EntityDrupalWrapper extends EntityStructureWrapper { - - /** - * Contains the entity id. - */ - protected $id = FALSE; - protected $bundle; - protected $entityInfo; - - /** - * Construct a new EntityDrupalWrapper object. - * - * @param $type - * The type of the passed data. - * @param $data - * Optional. The entity to wrap or its identifier. - * @param $info - * Optional. Used internally to pass info about properties down the tree. - */ - public function __construct($type, $data = NULL, $info = array()) { - parent::__construct($type, $data, $info); - $this->setUp(); - } - - protected function setUp() { - $this->propertyInfo = entity_get_property_info($this->type) + array('properties' => array()); - $info = $this->info + array('property info' => array(), 'bundle' => NULL); - $this->propertyInfo['properties'] += $info['property info']; - $this->bundle = $info['bundle']; - $this->entityInfo = entity_get_info($this->type); - if (isset($this->bundle)) { - $this->spotBundleInfo(FALSE); - } - } - - /** - * Sets the entity internally accepting both the entity id and object. - */ - protected function setEntity($data) { - // For entities we allow getter callbacks to return FALSE, which we - // interpret like NULL values as unset properties. - if (isset($data) && $data !== FALSE && !is_object($data)) { - $this->id = $data; - $this->data = FALSE; - } - elseif (is_object($data) && $data instanceof EntityDrupalWrapper) { - // We got a wrapped entity passed, so take over its values. - $this->id = $data->id; - $this->data = $data->data; - // For generic entity references, also update the entity type accordingly. - if ($this->info['type'] == 'entity') { - $this->type = $data->type; - } - } - elseif (is_object($data)) { - // We got the entity object passed. - $this->data = $data; - $id = entity_id($this->type, $data); - $this->id = isset($id) ? $id : FALSE; - } - else { - $this->id = FALSE; - $this->data = NULL; - } - } - - /** - * Used to lazy-load bundle info. So the wrapper can be loaded e.g. just - * for setting without the data being loaded. - */ - protected function spotInfo() { - if (!$this->propertyInfoAltered) { - if ($this->info['type'] == 'entity' && $this->dataAvailable() && $this->value()) { - // Add in entity-type specific details. - $this->setUp(); - } - $this->spotBundleInfo(TRUE); - parent::spotInfo(); - $this->propertyInfoAltered = TRUE; - } - } - - /** - * Tries to determine the bundle and adds in the according property info. - * - * @param $load - * Whether the entity should be loaded to spot the info if necessary. - */ - protected function spotBundleInfo($load = TRUE) { - // Like entity_extract_ids() assume the entity type if no key is given. - if (empty($this->entityInfo['entity keys']['bundle']) && $this->type != 'entity') { - $this->bundle = $this->type; - } - // Detect the bundle if not set yet and add in properties from the bundle. - elseif (!$this->bundle && $load && $this->dataAvailable()) { - try { - if ($entity = $this->value()) { - list($id, $vid, $bundle) = entity_extract_ids($this->type, $entity); - $this->bundle = $bundle; - } - } - catch (EntityMetadataWrapperException $e) { - // Loading data failed, so we cannot derive the used bundle. - } - } - - if ($this->bundle && isset($this->propertyInfo['bundles'][$this->bundle])) { - $bundle_info = (array) $this->propertyInfo['bundles'][$this->bundle] + array('properties' => array()); - // Allow bundles to re-define existing properties, such that the bundle - // can add in more bundle-specific details like the bundle of a referenced - // entity. - $this->propertyInfo['properties'] = $bundle_info['properties'] + $this->propertyInfo['properties']; - } - } - - /** - * Returns the identifier of the wrapped entity. - * - * @see entity_id() - */ - public function getIdentifier() { - return $this->dataAvailable() ? $this->value(array('identifier' => TRUE)) : NULL; - } - - /** - * Returns the bundle of an entity, or FALSE if it has no bundles. - */ - public function getBundle() { - if ($this->dataAvailable()) { - $this->spotInfo(); - return $this->bundle; - } - } - - /** - * Overridden. - * - * @param $options - * An array of options. Known keys: - * - identifier: If set to TRUE, the entity identifier is returned. - */ - public function value(array $options = array()) { - // Try loading the data via the getter callback if there is none yet. - if (!isset($this->data)) { - $this->setEntity(parent::value()); - } - if (!empty($options['identifier'])) { - return $this->id; - } - elseif (!$this->data && !empty($this->id)) { - // Lazy load the entity if necessary. - $return = entity_load($this->type, array($this->id)); - // In case the entity cannot be loaded, we return NULL just as for empty - // properties. - $this->data = $return ? reset($return) : NULL; - } - return $this->data; - } - - /** - * Returns the entity prepared for rendering. - * - * @see entity_view() - */ - public function view($view_mode = 'full', $langcode = NULL, $page = NULL) { - return entity_view($this->type(), array($this->value()), $view_mode, $langcode, $page); - } - - /** - * Overridden to support setting the entity by either the object or the id. - */ - public function set($value) { - if (!$this->validate($value)) { - throw new EntityMetadataWrapperException(t('Invalid data value given. Be sure it matches the required data type and format. Value at !location: !value.', array( - // An exception's message is output through check_plain(). - '!value' => is_array($value) || is_object($value) ? var_export($value, TRUE) : $value, - '!location' => $this->debugIdentifierLocation(), - ))); - } - if ($this->info['type'] == 'entity' && $value === $this) { - // Nothing to do. - return $this; - } - $previous_id = $this->id; - $previous_type = $this->type; - // Set value, so we get the identifier and pass it to the normal setter. - $this->clear(); - $this->setEntity($value); - // Generally, we have to update the parent only if the entity reference - // has changed. In case of a generic entity reference, we pass the entity - // wrapped. Else we just pass the id of the entity to the setter callback. - if ($this->info['type'] == 'entity' && ($previous_id != $this->id || $previous_type != $this->type)) { - // We need to clone the wrapper we pass through as value, so it does not - // get cleared when the current wrapper instance gets cleared. - $this->updateParent(clone $this); - } - // In case the entity has been unset, we cannot properly detect changes as - // the previous id defaults to FALSE for unloaded entities too. So in that - // case we just always update the parent. - elseif ($this->id === FALSE && !$this->data) { - $this->updateParent(NULL); - } - elseif ($previous_id !== $this->id) { - $this->updateParent($this->id); - } - return $this; - } - - /** - * Overridden. - */ - public function clear() { - $this->id = NULL; - $this->bundle = isset($this->info['bundle']) ? $this->info['bundle'] : NULL; - if ($this->type != $this->info['type']) { - // Reset entity info / property info based upon the info provided during - // the creation of the wrapper. - $this->type = $this->info['type']; - $this->setUp(); - } - parent::clear(); - } - - /** - * Overridden. - */ - public function type() { - // In case of a generic entity wrapper, load the data first to determine - // the type of the concrete entity. - if ($this->dataAvailable() && $this->info['type'] == 'entity') { - try { - $this->value(array('identifier' => TRUE)); - } - catch (EntityMetadataWrapperException $e) { - // If loading data fails, we cannot determine the concrete entity type. - } - } - return $this->type; - } - - /** - * {@inheritdoc} - * - * Note that this method checks property access, but can be used for checking - * entity access *only* if the wrapper is not a property (i.e. has no parent - * wrapper). - * To be safe, better use EntityDrupalWrapper::entityAccess() for checking - * entity access. - */ - public function access($op, $account = NULL) { - if (!empty($this->info['parent'])) { - // If this is a property, make sure the user is able to view the - // currently referenced entity also. - if ($this->entityAccess('view', $account) === FALSE) { - return FALSE; - } - if (parent::access($op, $account) === FALSE) { - return FALSE; - } - // If access is unknown, we return TRUE. - return TRUE; - } - else { - // This is not a property, so fallback on entity access. - if ($op == 'edit') { - // If the operation is "edit" determine if its actually a "create" for - // new un-saved entities, or an "update" for existing ones. - return $this->entityAccess($this->getIdentifier() ? 'update' : 'create', $account); - } - return $this->entityAccess('view', $account); - } - } - - /** - * Checks whether the operation $op is allowed on the entity. - * - * @see entity_access() - */ - public function entityAccess($op, $account = NULL) { - $entity = $this->dataAvailable() ? $this->value() : NULL; - // The value() method could return FALSE on entities such as user 0, so we - // need to use NULL instead to conform to the expectations of - // entity_access(). - if ($entity === FALSE) { - $entity = NULL; - } - return entity_access($op, $this->type, $entity, $account); - } - - /** - * Permanently save the wrapped entity. - * - * @throws EntityMetadataWrapperException - * If the entity type does not support saving. - * - * @return EntityDrupalWrapper - */ - public function save() { - if ($this->data) { - if (!entity_type_supports($this->type, 'save')) { - throw new EntityMetadataWrapperException("There is no information about how to save entities of type " . check_plain($this->type) . '.'); - } - entity_save($this->type, $this->data); - // On insert, update the identifier afterwards. - if (!$this->id) { - list($this->id, , ) = entity_extract_ids($this->type, $this->data); - } - } - // If the entity hasn't been loaded yet, don't bother saving it. - return $this; - } - - /** - * Permanently delete the wrapped entity. - * - * @return EntityDrupalWrapper - */ - public function delete() { - if ($this->dataAvailable() && $this->value()) { - $return = entity_delete($this->type, $this->id); - if ($return === FALSE) { - throw new EntityMetadataWrapperException("There is no information about how to delete entities of type " . check_plain($this->type) . '.'); - } - } - return $this; - } - - /** - * Gets the info about the wrapped entity. - */ - public function entityInfo() { - return $this->entityInfo; - } - - /** - * Returns the name of the key used by the entity for given entity key. - * - * @param $name - * One of 'id', 'name', 'bundle' or 'revision'. - * @return - * The name of the key used by the entity. - */ - public function entityKey($name) { - return isset($this->entityInfo['entity keys'][$name]) ? $this->entityInfo['entity keys'][$name] : FALSE; - } - - /** - * Returns the entity label. - * - * @see entity_label() - */ - public function label() { - if ($entity = $this->value()) { - return entity_label($this->type, $entity); - } - } - - /** - * Returns a string to use to identify this wrapper in error messages. - */ - public function debugIdentifierLocation() { - // An entity wrapper can be at the top of the chain or a part of it. - if (isset($this->info['name'])) { - // This wrapper is part of a chain, eg in the position node->author. - // Return the name. - $debug = $this->info['name']; - } - else { - // This is a wrapper for an actual entity: return its type and id. - $debug = $this->info['type'] . '(' . $this->getIdentifier() . ')'; - } - - if (isset($this->info['parent'])) { - $debug = $this->info['parent']->debugIdentifierLocation() . '->' . $debug; - } - return $debug; - } - - /** - * Prepare for serializiation. - */ - public function __sleep() { - $vars = parent::__sleep(); - // Don't serialize the loaded entity and its property info. - unset($vars['data'], $vars['propertyInfo'], $vars['propertyInfoAltered'], $vars['entityInfo']); - // In case the entity is not saved yet, serialize the unsaved data. - if ($this->dataAvailable() && $this->id === FALSE) { - $vars['data'] = 'data'; - } - return $vars; - } - - public function __wakeup() { - $this->setUp(); - if ($this->id !== FALSE) { - // Make sure data is set, so the entity will be loaded when needed. - $this->data = FALSE; - } - } -} - -/** - * Wraps a list of values. - * - * If the wrapped data is a list of data, its numerical indexes may be used to - * retrieve wrappers for the list items. For that this wrapper implements - * ArrayAccess so it may be used like a usual numerically indexed array. - */ -class EntityListWrapper extends EntityMetadataWrapper implements IteratorAggregate, ArrayAccess, Countable { - - /** - * The type of contained items. - */ - protected $itemType; - - /** - * Whether this is a list of entities with a known entity type, i.e. for - * generic list of entities (list) this is FALSE. - */ - protected $isEntityList; - - - public function __construct($type, $data = NULL, $info = array()) { - parent::__construct($type, NULL, $info); - - $this->itemType = entity_property_list_extract_type($this->type); - if (!$this->itemType) { - $this->itemType = 'unknown'; - } - $this->isEntityList = (bool) entity_get_info($this->itemType); - - if (isset($data)) { - $this->set($data); - } - } - - /** - * Get the wrapper for a single item. - * - * @return - * An instance of EntityMetadataWrapper. - */ - public function get($delta) { - // Look it up in the cache if possible. - if (!array_key_exists($delta, $this->cache)) { - if (!isset($delta)) { - // The [] operator has been used so point at a new entry. - $values = parent::value(); - $delta = $values ? max(array_keys($values)) + 1 : 0; - } - if (is_numeric($delta)) { - $info = array('parent' => $this, 'name' => $delta) + $this->info; - $this->cache[$delta] = entity_metadata_wrapper($this->itemType, NULL, $info); - } - else { - throw new EntityMetadataWrapperException('There can be only numerical keyed items in a list.'); - } - } - return $this->cache[$delta]; - } - - protected function getPropertyValue($delta) { - // Make use parent::value() to easily by-pass any entity-loading. - $data = parent::value(); - if (isset($data[$delta])) { - return $data[$delta]; - } - } - - protected function getPropertyRaw($delta) { - return $this->getPropertyValue($delta); - } - - protected function setProperty($delta, $value) { - $data = parent::value(); - if (is_numeric($delta)) { - $data[$delta] = $value; - $this->set($data); - } - } - - protected function propertyAccess($delta, $op, $account = NULL) { - return $this->access($op, $account); - } - - /** - * Returns the list as numerically indexed array. - * - * Note that a list of entities might contain stale entity references. In - * that case the wrapper and the identifier of a stale reference would be - * still accessible, however the entity object value would be NULL. That way, - * there may be NULL values in lists of entity objects due to stale entity - * references. - * - * @param $options - * An array of options. Known keys: - * - identifier: If set to TRUE for a list of entities, it won't be returned - * as list of fully loaded entity objects, but as a list of entity ids. - * Note that this list may contain ids of stale entity references. - */ - public function value(array $options = array()) { - // For lists of entities fetch full entity objects before returning. - // Generic entity-wrappers need to be handled separately though. - if ($this->isEntityList && empty($options['identifier']) && $this->dataAvailable()) { - $list = parent::value(); - $entities = $list ? entity_load($this->get(0)->type, $list) : array(); - // Make sure to keep the array keys as present in the list. - foreach ($list as $key => $id) { - // In case the entity cannot be loaded, we return NULL just as for empty - // properties. - $list[$key] = isset($entities[$id]) ? $entities[$id] : NULL; - } - return $list; - } - return parent::value(); - } - - public function set($values) { - // Support setting lists of fully loaded entities. - if ($this->isEntityList && $values && is_object(reset($values))) { - foreach ($values as $key => $value) { - // Ignore outdated NULL value references in lists of entities. - if (isset($value)) { - list($id, $vid, $bundle) = entity_extract_ids($this->itemType, $value); - $values[$key] = $id; - } - } - } - return parent::set($values); - } - - /** - * If we wrap a list, we return an iterator over the data list. - */ - public function getIterator() { - // In case there is no data available, just iterate over the first item. - return new EntityMetadataWrapperIterator($this, ($this->dataAvailable() && is_array(parent::value())) ? array_keys(parent::value()) : array(0)); - } - - /** - * Implements the ArrayAccess interface. - */ - public function offsetGet($delta) { - return $this->get($delta); - } - - public function offsetExists($delta) { - return $this->dataAvailable() && ($data = $this->value()) && array_key_exists($delta, $data); - } - - public function offsetSet($delta, $value) { - $this->get($delta)->set($value); - } - - public function offsetUnset($delta) { - if ($this->offsetExists($delta)) { - unset($this->data[$delta]); - $this->set($this->data); - } - } - - public function count() { - return $this->dataAvailable() ? count($this->value()) : 0; - } - - /** - * Overridden. - */ - public function validate($value) { - // Required lists may not be empty or unset. - if (!empty($this->info['required']) && empty($value)) { - return FALSE; - } - return parent::validate($value); - } - - /** - * Returns the label for the list of set values if available. - */ - public function label() { - if ($options = $this->optionsList('view')) { - $options = entity_property_options_flatten($options); - $labels = array_intersect_key($options, array_flip((array) parent::value())); - } - else { - // Get each label on its own, e.g. to support getting labels of a list - // of entities. - $labels = array(); - foreach ($this as $key => $property) { - $label = $property->label(); - if (!$label) { - return NULL; - } - $labels[] = $label; - } - } - return isset($labels) ? implode(', ', $labels) : NULL; - } -} - -/** - * Provide a separate Exception so it can be caught separately. - */ -class EntityMetadataWrapperException extends Exception { } - - -/** - * Allows to easily iterate over existing child wrappers. - */ -class EntityMetadataWrapperIterator implements RecursiveIterator { - - protected $position = 0; - protected $wrapper, $keys; - - public function __construct(EntityMetadataWrapper $wrapper, array $keys) { - $this->wrapper = $wrapper; - $this->keys = $keys; - } - - function rewind() { - $this->position = 0; - } - - function current() { - return $this->wrapper->get($this->keys[$this->position]); - } - - function key() { - return $this->keys[$this->position]; - } - - function next() { - $this->position++; - } - - function valid() { - return isset($this->keys[$this->position]); - } - - public function hasChildren() { - return $this->current() instanceof IteratorAggregate; - } - - public function getChildren() { - return $this->current()->getIterator(); - } -} - -/** - * An array object implementation keeping the reference on the given array so - * changes to the object are reflected in the passed array. - */ -class EntityMetadataArrayObject implements ArrayAccess, Countable, IteratorAggregate { - - protected $data; - - public function __construct(&$array) { - $this->data =& $array; - } - - public function &getArray() { - return $this->data; - } - - /** - * Implements the ArrayAccess interface. - */ - public function offsetGet($delta) { - return $this->data[$delta]; - } - - public function offsetExists($delta) { - return array_key_exists($delta, $this->data); - } - - public function offsetSet($delta, $value) { - $this->data[$delta] = $value; - } - - public function offsetUnset($delta) { - unset($this->data[$delta]); - } - - public function count() { - return count($this->data); - } - - public function getIterator() { - return new ArrayIterator($this->data); - } -} diff --git a/modules/book.info.inc b/modules/book.info.inc deleted file mode 100644 index 96629c3..0000000 --- a/modules/book.info.inc +++ /dev/null @@ -1,30 +0,0 @@ - t("Book"), - 'type' => 'node', - 'description' => t("If part of a book, the book to which this book page belongs."), - 'getter callback' => 'entity_metadata_book_get_properties', - ); - $properties['book_ancestors'] = array( - 'label' => t("Book ancestors"), - 'type' => 'list', - 'computed' => TRUE, - 'description' => t("If part of a book, a list of all book pages upwards in the book hierarchy."), - 'getter callback' => 'entity_metadata_book_get_properties', - ); -} diff --git a/modules/callbacks.inc b/modules/callbacks.inc deleted file mode 100644 index ee156ab..0000000 --- a/modules/callbacks.inc +++ /dev/null @@ -1,1099 +0,0 @@ -book['bid'])) { - return $node->book['bid']; - } - return NULL; - - case 'book_ancestors': - $ancestors = array(); - while (!empty($node->book['plid']) && $node->book['plid'] != -1) { - $link = book_link_load($node->book['plid']); - array_unshift($ancestors, $link['nid']); - $node = node_load($link['nid']); - } - return $ancestors; - } -} - -/** - * Callback for getting comment properties. - * @see entity_metadata_comment_entity_info_alter() - */ -function entity_metadata_comment_get_properties($comment, array $options, $name) { - switch ($name) { - case 'name': - return $comment->name; - - case 'mail': - if ($comment->uid != 0) { - $account = user_load($comment->uid); - return $account->mail; - } - return $comment->mail; - - case 'edit_url': - return url('comment/edit/' . $comment->cid, $options); - - case 'parent': - if (!empty($comment->pid)) { - return $comment->pid; - } - // There is no parent comment. - return NULL; - } -} - -/** - * Callback for setting comment properties. - * @see entity_metadata_comment_entity_info_alter() - */ -function entity_metadata_comment_setter($comment, $name, $value) { - switch ($name) { - case 'node': - $comment->nid = $value; - // Also set the bundle name. - $node = node_load($value); - $comment->node_type = 'comment_node_' . $node->type; - break; - } -} - -/** - * Callback for getting comment related node properties. - * @see entity_metadata_comment_entity_info_alter() - */ -function entity_metadata_comment_get_node_properties($node, array $options, $name, $entity_type) { - switch ($name) { - case 'comment_count': - return isset($node->comment_count) ? $node->comment_count : 0; - - case 'comment_count_new': - return comment_num_new($node->nid); - - case 'comments': - $select = db_select('comment', 'c') - ->fields('c', array('cid')) - ->condition('c.nid', $node->nid); - return array_keys($select->execute()->fetchAllKeyed(0, 0)); - } -} - -/** - * Getter callback for getting global languages. - */ -function entity_metadata_locale_get_languages($data, array $options, $name) { - return isset($GLOBALS[$name]) ? $GLOBALS[$name]->language : NULL; -} - -/** - * Getter callback for getting the preferred user language. - */ -function entity_metadata_locale_get_user_language($account, array $options, $name) { - return user_preferred_language($account)->language; -} - -/** - * Return the options lists for the node and comment status property. - */ -function entity_metadata_status_options_list() { - return array( - NODE_PUBLISHED => t('Published'), - NODE_NOT_PUBLISHED => t('Unpublished'), - ); -} - -/** - * Callback for getting node properties. - * - * @see entity_metadata_node_entity_info_alter() - */ -function entity_metadata_node_get_properties($node, array $options, $name, $entity_type) { - switch ($name) { - case 'is_new': - return empty($node->nid) || !empty($node->is_new); - - case 'source': - if (!empty($node->tnid) && $source = node_load($node->tnid)) { - return $source; - } - return NULL; - - case 'edit_url': - return url('node/' . $node->nid . '/edit', $options); - - case 'author': - return !empty($node->uid) ? $node->uid : drupal_anonymous_user(); - } -} - -/** - * Callback for determing access for node revision related properties. - */ -function entity_metadata_node_revision_access($op, $name, $entity = NULL, $account = NULL) { - return $op == 'view' ? user_access('view revisions', $account) : user_access('administer nodes', $account); -} - -/** - * Callback for getting poll properties. - * @see entity_metadata_poll_entity_info_alter() - */ -function entity_metadata_poll_node_get_properties($node, array $options, $name) { - $total_votes = $highest_votes = 0; - foreach ($node->choice as $choice) { - if ($choice['chvotes'] > $highest_votes) { - $winner = $choice; - $highest_votes = $choice['chvotes']; - } - $total_votes = $total_votes + $choice['chvotes']; - } - - if ($name == 'poll_duration') { - return $node->runtime; - } - elseif ($name == 'poll_votes') { - return $total_votes; - } - elseif (!isset($winner)) { - // There is no poll winner yet. - return NULL; - } - switch ($name) { - case 'poll_winner_votes': - return $winner['chvotes']; - - case 'poll_winner': - return $winner['chtext']; - - case 'poll_winner_percent': - return ($winner['chvotes'] / $total_votes) * 100; - } -} - -/** - * Callback for getting statistics properties. - * @see entity_metadata_statistics_entity_info_alter() - */ -function entity_metadata_statistics_node_get_properties($node, array $options, $name) { - $statistics = (array) statistics_get($node->nid); - $statistics += array('totalcount' => 0, 'daycount' => 0, 'timestamp' => NULL); - - switch ($name) { - case 'views': - return $statistics['totalcount']; - - case 'day_views': - return $statistics['daycount']; - - case 'last_view': - return $statistics['timestamp']; - } -} - -/** - * Access callback for restricted node statistics properties. - */ -function entity_metadata_statistics_properties_access($op, $property, $entity = NULL, $account = NULL) { - if ($property == 'views' && user_access('view post access counter', $account)) { - return TRUE; - } - return user_access('access statistics', $account); -} - -/** - * Callback for getting site-wide properties. - * @see entity_metadata_system_entity_info_alter() - */ -function entity_metadata_system_get_properties($data = FALSE, array $options, $name) { - switch ($name) { - case 'name': - return variable_get('site_name', 'Drupal'); - - case 'url': - return url('', $options); - - case 'login_url': - return url('user', $options); - - case 'current_user': - return $GLOBALS['user']->uid ? $GLOBALS['user']->uid : drupal_anonymous_user(); - - case 'current_date': - return REQUEST_TIME; - - case 'current_page': - // Subsequent getters of the struct retrieve the actual values. - return array(); - - default: - return variable_get('site_' . $name, ''); - } -} - -/** - * Callback for getting properties for the current page request. - * @see entity_metadata_system_entity_info_alter() - */ -function entity_metadata_system_get_page_properties($data = array(), array $options, $name) { - switch ($name) { - case 'url': - return $GLOBALS['base_root'] . request_uri(); - } -} - -/** - * Callback for getting file properties. - * @see entity_metadata_system_entity_info_alter() - */ -function entity_metadata_system_get_file_properties($file, array $options, $name) { - switch ($name) { - case 'name': - return $file->filename; - - case 'mime': - return $file->filemime; - - case 'size': - return $file->filesize; - - case 'url': - return url(file_create_url($file->uri), $options); - - case 'owner': - return $file->uid; - } -} - -/** - * Callback for getting term properties. - * - * @see entity_metadata_taxonomy_entity_info_alter() - */ -function entity_metadata_taxonomy_term_get_properties($term, array $options, $name) { - switch ($name) { - case 'node_count': - return count(taxonomy_select_nodes($term->tid)); - - case 'description': - return check_markup($term->description, isset($term->format) ? $term->format : NULL, '', TRUE); - - case 'parent': - if (isset($term->parent[0]) && !is_array(isset($term->parent[0]))) { - return $term->parent; - } - return array_keys(taxonomy_get_parents($term->tid)); - - case 'parents_all': - // We have to return an array of ids. - $tids = array(); - foreach (taxonomy_get_parents_all($term->tid) as $parent) { - $tids[] = $parent->tid; - } - return $tids; - } -} - -/** - * Callback for setting term properties. - * - * @see entity_metadata_taxonomy_entity_info_alter() - */ -function entity_metadata_taxonomy_term_setter($term, $name, $value) { - switch ($name) { - case 'vocabulary': - // Make sure to update the taxonomy bundle key, so load the vocabulary. - // Support both, loading by name or ID. - $vocabulary = is_numeric($value) ? taxonomy_vocabulary_load($value) : taxonomy_vocabulary_machine_name_load($value); - $term->vocabulary_machine_name = $vocabulary->machine_name; - return $term->vid = $vocabulary->vid; - case 'parent': - return $term->parent = $value; - } -} - -/** - * Callback for getting vocabulary properties. - * @see entity_metadata_taxonomy_entity_info_alter() - */ -function entity_metadata_taxonomy_vocabulary_get_properties($vocabulary, array $options, $name) { - switch ($name) { - case 'term_count': - $sql = "SELECT COUNT (1) FROM {taxonomy_term_data} td WHERE td.vid = :vid"; - return db_query($sql, array(':vid' => $vocabulary->vid))->fetchField(); - } -} - -/** - * Callback for getting user properties. - * @see entity_metadata_user_entity_info_alter() - */ -function entity_metadata_user_get_properties($account, array $options, $name, $entity_type) { - switch ($name) { - case 'last_access': - // In case there was no access the value is 0, but we have to return NULL. - return empty($account->access) ? NULL : $account->access; - - case 'last_login': - return empty($account->login) ? NULL : $account->login; - - case 'name': - return empty($account->uid) ? variable_get('anonymous', t('Anonymous')) : $account->name; - - case 'url': - if (empty($account->uid)) { - return NULL; - } - $return = entity_uri('user', $account); - return $return ? url($return['path'], $return['options'] + $options) : ''; - - case 'edit_url': - return empty($account->uid) ? NULL : url("user/$account->uid/edit", $options); - - case 'roles': - return isset($account->roles) ? array_keys($account->roles) : array(); - - case 'theme': - return empty($account->theme) ? variable_get('theme_default', 'bartik') : $account->theme; - } -} - -/** - * Callback for setting user properties. - * @see entity_metadata_user_entity_info_alter() - */ -function entity_metadata_user_set_properties($account, $name, $value) { - switch ($name) { - case 'roles': - $account->roles = array_intersect_key(user_roles(), array_flip($value)); - break; - } -} - -/** - * Options list callback returning all user roles. - */ -function entity_metadata_user_roles($property_name = 'roles', $info = array(), $op = 'edit') { - $roles = user_roles(); - if ($op == 'edit') { - unset($roles[DRUPAL_AUTHENTICATED_RID], $roles[DRUPAL_ANONYMOUS_RID]); - } - return $roles; -} - -/** - * Return the options lists for user status property. - */ -function entity_metadata_user_status_options_list() { - return array( - 0 => t('Blocked'), - 1 => t('Active'), - ); -} - -/** - * Callback defining an options list for language properties. - */ -function entity_metadata_language_list() { - $list = array(); - $list[LANGUAGE_NONE] = t('Language neutral'); - foreach (language_list() as $language) { - $list[$language->language] = t($language->name); - } - return $list; -} - -/** - * Callback for getting field property values. - */ -function entity_metadata_field_property_get($entity, array $options, $name, $entity_type, $info) { - $field = field_info_field($name); - $columns = array_keys($field['columns']); - $langcode = isset($options['language']) ? $options['language']->language : LANGUAGE_NONE; - $langcode = entity_metadata_field_get_language($entity_type, $entity, $field, $langcode, TRUE); - $values = array(); - if (isset($entity->{$name}[$langcode])) { - foreach ($entity->{$name}[$langcode] as $delta => $data) { - $values[$delta] = $data[$columns[0]]; - if ($info['type'] == 'boolean' || $info['type'] == 'list') { - // Ensure that we have a clean boolean data type. - $values[$delta] = (boolean) $values[$delta]; - } - } - } - // For an empty single-valued field, we have to return NULL. - return $field['cardinality'] == 1 ? ($values ? reset($values) : NULL) : $values; -} - -/** - * Callback for setting field property values. - */ -function entity_metadata_field_property_set($entity, $name, $value, $langcode, $entity_type, $info) { - $field = field_info_field($name); - $columns = array_keys($field['columns']); - $langcode = entity_metadata_field_get_language($entity_type, $entity, $field, $langcode); - $values = $field['cardinality'] == 1 ? array($value) : (array) $value; - - $items = array(); - foreach ($values as $delta => $value) { - if (isset($value)) { - $items[$delta][$columns[0]] = $value; - if ($info['type'] == 'boolean' || $info['type'] == 'list') { - // Convert boolean values back to an integer for writing. - $items[$delta][$columns[0]] = (integer) $items[$delta][$columns[0]] = $value; - } - } - } - $entity->{$name}[$langcode] = $items; - // Empty the static field language cache, so the field system picks up any - // possible new languages. - drupal_static_reset('field_language'); -} - -/** - * Callback returning the options list of a field. - */ -function entity_metadata_field_options_list($name, $info) { - $field_property_info = $info; - if (is_numeric($name) && isset($info['parent'])) { - // The options list is to be returned for a single item of a multiple field. - $field_property_info = $info['parent']->info(); - $name = $field_property_info['name']; - } - if (($field = field_info_field($name)) && isset($field_property_info['parent'])) { - // Retrieve the wrapped entity holding the field. - $wrapper = $field_property_info['parent']; - try { - $entity = $wrapper->value(); - } - catch (EntityMetadataWrapperException $e) { - // No data available. - $entity = NULL; - } - - // Support translating labels via i18n field. - if (module_exists('i18n_field') && ($translate = i18n_field_type_info($field['type'], 'translate_options'))) { - return $translate($field); - } - else { - $instance = $wrapper->getBundle() ? field_info_instance($wrapper->type(), $name, $wrapper->getBundle()) : NULL; - return (array) module_invoke($field['module'], 'options_list', $field, $instance, $wrapper->type(), $entity); - } - } -} - -/** - * Callback to verbatim get the data structure of a field. Useful for fields - * that add metadata for their own data structure. - */ -function entity_metadata_field_verbatim_get($entity, array $options, $name, $entity_type, &$context) { - // Set contextual info useful for getters of any child properties. - $context['instance'] = field_info_instance($context['parent']->type(), $name, $context['parent']->getBundle()); - $context['field'] = field_info_field($name); - $langcode = isset($options['language']) ? $options['language']->language : LANGUAGE_NONE; - $langcode = entity_metadata_field_get_language($entity_type, $entity, $context['field'], $langcode, TRUE); - - if ($context['field']['cardinality'] == 1) { - return isset($entity->{$name}[$langcode][0]) ? $entity->{$name}[$langcode][0] : NULL; - } - return isset($entity->{$name}[$langcode]) ? $entity->{$name}[$langcode] : array(); -} - -/** - * Writes the passed field items in the object. Useful as field level setter - * to set the whole data structure at once. - */ -function entity_metadata_field_verbatim_set($entity, $name, $items, $langcode, $entity_type) { - $field = field_info_field($name); - $langcode = entity_metadata_field_get_language($entity_type, $entity, $field, $langcode); - $value = $field['cardinality'] == 1 ? array($items) : (array) $items; - // Filter out any items set to NULL. - $entity->{$name}[$langcode] = array_filter($value); - - // Empty the static field language cache, so the field system picks up any - // possible new languages. - drupal_static_reset('field_language'); -} - -/** - * Helper for determining the field language to be used. - * - * Note that we cannot use field_language() as we are not about to display - * values, but generally read/write values. - * - * @param $fallback - * (optional) Whether to fall back to the entity default language, if no - * value is available for the given language code yet. - * - * @return - * The language code to use. - */ -function entity_metadata_field_get_language($entity_type, $entity, $field, $langcode = LANGUAGE_NONE, $fallback = FALSE) { - // Try to figure out the default language used by the entity. - // With Drupal >= 7.15 we can use entity_language(). - if (function_exists('entity_language')) { - $default_langcode = entity_language($entity_type, $entity); - } - else { - $default_langcode = !empty($entity->language) ? $entity->language : LANGUAGE_NONE; - } - - // Determine the right language to use. - if ($default_langcode != LANGUAGE_NONE && field_is_translatable($entity_type, $field)) { - $langcode = ($langcode != LANGUAGE_NONE) ? field_valid_language($langcode, $default_langcode) : $default_langcode; - if (!isset($entity->{$field['field_name']}[$langcode]) && $fallback) { - $langcode = $default_langcode; - } - return $langcode; - } - else { - return LANGUAGE_NONE; - } -} - -/** - * Callback for getting the sanitized text of 'text_formatted' properties. - * This callback is used for both the 'value' and the 'summary'. - */ -function entity_metadata_field_text_get($item, array $options, $name, $type, $context) { - // $name is either 'value' or 'summary'. - if (!isset($item['safe_' . $name])) { - // Apply input formats. - $langcode = isset($options['language']) ? $options['language']->language : ''; - $format = isset($item['format']) ? $item['format'] : filter_default_format(); - $item['safe_' . $name] = check_markup($item[$name], $format, $langcode); - // To speed up subsequent calls, update $item with the 'safe_value'. - $context['parent']->set($item); - } - return $item['safe_' . $name]; -} - -/** - * Defines the list of all available text formats. - */ -function entity_metadata_field_text_formats() { - foreach (filter_formats() as $key => $format) { - $formats[$key] = $format->name; - } - return $formats; -} - -/** - * Callback for getting the file entity of file fields. - */ -function entity_metadata_field_file_get($item) { - return $item['fid']; -} - -/** - * Callback for setting the file entity of file fields. - */ -function entity_metadata_field_file_set(&$item, $property_name, $value) { - $item['fid'] = $value; -} - -/** - * Callback for auto-creating file field $items. - */ -function entity_metadata_field_file_create_item($property_name, $context) { - // 'fid' is required, so 'file' has to be set as initial property. - return array('display' => isset($context['field']['settings']['display_default']) ? $context['field']['settings']['display_default'] : 0); -} - -/** - * Callback for validating file field $items. - */ -function entity_metadata_field_file_validate_item($items, $context) { - // Allow NULL values. - if (!isset($items)) { - return TRUE; - } - - // Stream-line $items for multiple vs non-multiple fields. - $items = !entity_property_list_extract_type($context['type']) ? array($items) : (array) $items; - - foreach ($items as $item) { - // File-field items require a valid file. - if (!isset($item['fid']) || !file_load($item['fid'])) { - return FALSE; - } - if (isset($context['property info']['display']) && !isset($item['display'])) { - return FALSE; - } - } - return TRUE; -} - -/** - * Access callback for the node entity. - * - * This function does not implement hook_node_access(), thus it may not be - * called entity_metadata_node_access(). - * - * @see entity_access() - * - * @param $op - * The operation being performed. One of 'view', 'update', 'create' or - * 'delete'. - * @param $node - * A node to check access for. Must be a node object. Must have nid, - * except in the case of 'create' operations. - * @param $account - * The user to check for. Leave it to NULL to check for the global user. - * - * @throws EntityMalformedException - * - * @return boolean - * TRUE if access is allowed, FALSE otherwise. - */ -function entity_metadata_no_hook_node_access($op, $node = NULL, $account = NULL) { - // First deal with the case where a $node is provided. - if (isset($node)) { - if (empty($node->vid) && in_array($op, array('create', 'update'))) { - // This is a new node or the original node. - if (isset($node->type)) { - $op = empty($node->nid) || !empty($node->is_new) ? 'create' : 'update'; - return node_access($op, $op == 'create' ? $node->type : $node, $account); - } - else { - throw new EntityMalformedException('Permission to create a node was requested but no node type was given.'); - } - } - // If a non-default revision is given, incorporate revision access. - $default_revision = node_load($node->nid); - if ($node->vid !== $default_revision->vid) { - return _node_revision_access($node, $op, $account); - } - else { - return node_access($op, $node, $account); - } - } - // No node is provided. Check for access to all nodes. - if (user_access('bypass node access', $account)) { - return TRUE; - } - if (!user_access('access content', $account)) { - return FALSE; - } - if ($op == 'view' && node_access_view_all_nodes($account)) { - return TRUE; - } - return FALSE; -} - -/** - * Access callback for the user entity. - */ -function entity_metadata_user_access($op, $entity = NULL, $account = NULL, $entity_type = NULL) { - $account = isset($account) ? $account : $GLOBALS['user']; - // Grant access to the users own user account and to the anonymous one. - if (isset($entity->uid) && $op != 'delete' && (($entity->uid == $account->uid && $entity->uid) || (!$entity->uid && $op == 'view'))) { - return TRUE; - } - if (user_access('administer users', $account) - || user_access('access user profiles', $account) && $op == 'view' && (empty($entity) || !empty($entity->status))) { - return TRUE; - } - return FALSE; -} - -/** - * Access callback for restricted user properties. - */ -function entity_metadata_user_properties_access($op, $property, $entity = NULL, $account = NULL) { - if (user_access('administer users', $account)) { - return TRUE; - } - $account = isset($account) ? $account : $GLOBALS['user']; - // Flag to indicate if this user entity is the own user account. - $is_own_account = isset($entity->uid) && $account->uid == $entity->uid; - switch ($property) { - case 'name': - // Allow view access to anyone with access to the entity. - if ($op == 'view') { - return TRUE; - } - // Allow edit access for own user name if the permission is satisfied. - return $is_own_account && user_access('change own username', $account); - case 'mail': - // Allow access to own mail address. - return $is_own_account; - case 'roles': - // Allow view access for own roles. - return ($op == 'view' && $is_own_account); - } - return FALSE; -} - -/** - * Access callback for the comment entity. - */ -function entity_metadata_comment_access($op, $entity = NULL, $account = NULL) { - // When determining access to a comment, 'comment_access' does not take any - // access restrictions to the comment's associated node into account. If a - // comment has an associated node, the user must be able to view it in order - // to access the comment. - if (isset($entity->nid)) { - if (!entity_access('view', 'node', node_load($entity->nid), $account)) { - return FALSE; - } - } - - // Comment administrators are allowed to perform all operations on all - // comments. - if (user_access('administer comments', $account)) { - return TRUE; - } - - // Unpublished comments can never be accessed by non-admins. - if (isset($entity->status) && $entity->status == COMMENT_NOT_PUBLISHED) { - return FALSE; - } - - if (isset($entity) && $op == 'update') { - // Because 'comment_access' only checks the current user, we need to do our - // own access checking if an account was specified. - if (!isset($account)) { - return comment_access('edit', $entity); - } - else { - return $account->uid && $account->uid == $entity->uid && user_access('edit own comments', $account); - } - } - if (user_access('access comments', $account) && $op == 'view') { - return TRUE; - } - return FALSE; -} - -/** - * Access callback for restricted comment properties. - */ -function entity_metadata_comment_properties_access($op, $property, $entity = NULL, $account = NULL) { - return user_access('administer comments', $account); -} - -/** - * Access callback for the taxonomy entities. - */ -function entity_metadata_taxonomy_access($op, $entity = NULL, $account = NULL, $entity_type = NULL) { - // If user has administer taxonomy permission then no further checks. - if (user_access('administer taxonomy', $account)) { - return TRUE; - } - switch ($op) { - case "view": - if (user_access('access content', $account)) { - return TRUE; - } - break; - case "update": - if ($entity_type == 'taxonomy_term') { - return user_access("edit terms in $entity->vid", $account); - } - break; - case "create": - if ($entity_type == 'taxonomy_term') { - // Check for taxonomy_access_fix contrib module which adds additional - // permissions to create new terms in a given vocabulary. - if (function_exists('taxonomy_access_fix_access')) { - return taxonomy_access_fix_access('add terms', $entity->vocabulary_machine_name); - } - } - break; - case "delete": - if ($entity_type == 'taxonomy_term') { - return user_access("delete terms in $entity->vid", $account); - } - break; - } - return FALSE; -} - -/** - * Access callback for file entities. - */ -function entity_metadata_file_access($op, $file = NULL, $account = NULL, $entity_type) { - // We can only check access for the current user, so return FALSE on other accounts. - global $user; - if ($op == 'view' && isset($file) && (!isset($account) || $user->uid == $account->uid)) { - // Invoke hook_file_download() to obtain access information. - foreach (module_implements('file_download') as $module) { - $result = module_invoke($module, 'file_download', $file->uri); - if ($result == -1) { - return FALSE; - } - } - return TRUE; - } - return FALSE; -} - - -/** - * Callback to determine access for properties which are fields. - */ -function entity_metadata_field_access_callback($op, $name, $entity = NULL, $account = NULL, $entity_type) { - $field = field_info_field($name); - return field_access($op, $field, $entity_type, $entity, $account); -} - -/** - * Callback to create entity objects. - */ -function entity_metadata_create_object($values = array(), $entity_type) { - $info = entity_get_info($entity_type); - // Make sure at least the bundle and label properties are set. - if (isset($info['entity keys']['bundle']) && $key = $info['entity keys']['bundle']) { - $values += array($key => NULL); - } - if (isset($info['entity keys']['label']) && $key = $info['entity keys']['label']) { - $values += array($key => NULL); - } - $entity = (object) $values; - $entity->is_new = TRUE; - return $entity; -} - -/** - * Callback to create a new comment. - */ -function entity_metadata_create_comment($values = array()) { - $comment = (object) ($values + array( - 'status' => COMMENT_PUBLISHED, - 'pid' => 0, - 'subject' => '', - 'uid' => 0, - 'language' => LANGUAGE_NONE, - 'node_type' => NULL, - 'is_new' => TRUE, - )); - $comment->cid = FALSE; - return $comment; -} - -/** - * Callback to create a new node. - */ -function entity_metadata_create_node($values = array()) { - $node = (object) array( - 'type' => $values['type'], - 'language' => LANGUAGE_NONE, - 'is_new' => TRUE, - ); - // Set some defaults. - $node_options = variable_get('node_options_' . $node->type, array('status', 'promote')); - foreach (array('status', 'promote', 'sticky') as $key) { - $node->$key = (int) in_array($key, $node_options); - } - if (module_exists('comment') && !isset($node->comment)) { - $node->comment = variable_get("comment_$node->type", COMMENT_NODE_OPEN); - } - // Apply the given values. - foreach ($values as $key => $value) { - $node->$key = $value; - } - return $node; -} - -/** - * Callback to save a user account. - */ -function entity_metadata_user_save($account) { - $edit = (array) $account; - // Don't save the hashed password as password. - unset($edit['pass']); - user_save($account, $edit); -} - -/** - * Callback to delete a file. - * Watch out to not accidentilly implement hook_file_delete(). - */ -function entity_metadata_delete_file($fid) { - file_delete(file_load($fid), TRUE); -} - -/** - * Callback to view nodes. - */ -function entity_metadata_view_node($entities, $view_mode = 'full', $langcode = NULL) { - $result = node_view_multiple($entities, $view_mode, 0, $langcode); - // Make sure to key the result with 'node' instead of 'nodes'. - return array('node' => reset($result)); -} - -/** - * Callback to view comments. - */ -function entity_metadata_view_comment($entities, $view_mode = 'full', $langcode = NULL) { - $build = array(); - $nodes = array(); - // The comments, indexed by nid and then by cid. - $nid_comments = array(); - foreach ($entities as $cid => $comment) { - $nid = $comment->nid; - $nodes[$nid] = $nid; - $nid_comments[$nid][$cid] = $comment; - } - $nodes = node_load_multiple(array_keys($nodes)); - foreach ($nid_comments as $nid => $comments) { - $node = isset($nodes[$nid]) ? $nodes[$nid] : NULL; - $build += comment_view_multiple($comments, $node, $view_mode, 0, $langcode); - } - return array('comment' => $build); -} - -/** - * Callback to view an entity, for which just ENTITYTYPE_view() is available. - */ -function entity_metadata_view_single($entities, $view_mode = 'full', $langcode = NULL, $entity_type) { - $function = $entity_type . '_view'; - $build = array(); - foreach ($entities as $key => $entity) { - $build[$entity_type][$key] = $function($entity, $view_mode, $langcode); - } - return $build; -} - -/** - * Callback to get the form of a node. - */ -function entity_metadata_form_node($node) { - // Pre-populate the form-state with the right form include. - $form_state['build_info']['args'] = array($node); - form_load_include($form_state, 'inc', 'node', 'node.pages'); - return drupal_build_form($node->type . '_node_form', $form_state); -} - -/** - * Callback to get the form of a comment. - */ -function entity_metadata_form_comment($comment) { - if (!isset($comment->node_type)) { - $node = node_load($comment->nid); - $comment->node_type = 'comment_node_' . $node->type; - } - return drupal_get_form($comment->node_type . '_form', $comment); -} - -/** - * Callback to get the form of a user account. - */ -function entity_metadata_form_user($account) { - // If $account->uid is set then we want a user edit form. - // Otherwise we want the user register form. - if (isset($account->uid)) { - $form_id = 'user_profile_form'; - form_load_include($form_state, 'inc', 'user', 'user.pages'); - } - else { - $form_id = 'user_register_form'; - } - $form_state['build_info']['args'] = array($account); - return drupal_build_form($form_id, $form_state); -} - -/** - * Callback to get the form of a term. - */ -function entity_metadata_form_taxonomy_term($term) { - // Pre-populate the form-state with the right form include. - $form_state['build_info']['args'] = array($term); - form_load_include($form_state, 'inc', 'taxonomy', 'taxonomy.admin'); - return drupal_build_form('taxonomy_form_term', $form_state); -} - -/** - * Callback to get the form of a vocabulary. - */ -function entity_metadata_form_taxonomy_vocabulary($vocab) { - // Pre-populate the form-state with the right form include. - $form_state['build_info']['args'] = array($vocab); - form_load_include($form_state, 'inc', 'taxonomy', 'taxonomy.admin'); - return drupal_build_form('taxonomy_form_vocabulary', $form_state); -} - -/** - * Callback to get the form for entities using the entity API admin ui. - */ -function entity_metadata_form_entity_ui($entity, $entity_type) { - $info = entity_get_info($entity_type); - $form_state = form_state_defaults(); - // Add in the include file as the form API does else with the include file - // specified for the active menu item. - if (!empty($info['admin ui']['file'])) { - $path = isset($info['admin ui']['file path']) ? $info['admin ui']['file path'] : drupal_get_path('module', $info['module']); - $form_state['build_info']['files']['entity_ui'] = $path . '/' . $info['admin ui']['file']; - // Also load the include file. - if (file_exists($form_state['build_info']['files']['entity_ui'])) { - require_once DRUPAL_ROOT . '/' . $form_state['build_info']['files']['entity_ui']; - } - } - return entity_ui_get_form($entity_type, $entity, $op = 'edit', $form_state); -} - -/** - * Callback for querying entity properties having their values stored in the - * entities main db table. - */ -function entity_metadata_table_query($entity_type, $property, $value, $limit) { - $properties = entity_get_all_property_info($entity_type); - $info = $properties[$property] + array('schema field' => $property); - - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', $entity_type, '=') - ->propertyCondition($info['schema field'], $value, is_array($value) ? 'IN' : '=') - ->range(0, $limit); - - $result = $query->execute(); - return !empty($result[$entity_type]) ? array_keys($result[$entity_type]) : array(); -} - -/** - * Callback for querying entities by field values. This function just queries - * for the value of the first specified column. Also it is only suitable for - * fields that don't process the data, so it's stored the same way as returned. - */ -function entity_metadata_field_query($entity_type, $property, $value, $limit) { - $query = new EntityFieldQuery(); - $field = field_info_field($property); - $columns = array_keys($field['columns']); - - $query->entityCondition('entity_type', $entity_type, '=') - ->fieldCondition($field, $columns[0], $value, is_array($value) ? 'IN' : '=') - ->range(0, $limit); - - $result = $query->execute(); - return !empty($result[$entity_type]) ? array_keys($result[$entity_type]) : array(); -} - -/** - * Implements entity_uri() callback for file entities. - */ -function entity_metadata_uri_file($file) { - return array( - 'path' => file_create_url($file->uri), - ); -} diff --git a/modules/comment.info.inc b/modules/comment.info.inc deleted file mode 100644 index 663c09c..0000000 --- a/modules/comment.info.inc +++ /dev/null @@ -1,172 +0,0 @@ - t("Comment ID"), - 'type' => 'integer', - 'description' => t("The unique ID of the comment."), - 'schema field' => 'cid', - ); - $properties['hostname'] = array( - 'label' => t("IP Address"), - 'description' => t("The IP address of the computer the comment was posted from."), - 'access callback' => 'entity_metadata_comment_properties_access', - 'schema field' => 'hostname', - ); - $properties['name'] = array( - 'label' => t("Name"), - 'description' => t("The name left by the comment author."), - 'getter callback' => 'entity_metadata_comment_get_properties', - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer comments', - 'sanitize' => 'filter_xss', - 'schema field' => 'name', - ); - $properties['mail'] = array( - 'label' => t("Email address"), - 'description' => t("The email address left by the comment author."), - 'getter callback' => 'entity_metadata_comment_get_properties', - 'setter callback' => 'entity_property_verbatim_set', - 'validation callback' => 'valid_email_address', - 'access callback' => 'entity_metadata_comment_properties_access', - 'schema field' => 'mail', - ); - $properties['homepage'] = array( - 'label' => t("Home page"), - 'description' => t("The home page URL left by the comment author."), - 'sanitize' => 'filter_xss_bad_protocol', - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer comments', - 'schema field' => 'homepage', - ); - $properties['subject'] = array( - 'label' => t("Subject"), - 'description' => t("The subject of the comment."), - 'setter callback' => 'entity_property_verbatim_set', - 'sanitize' => 'filter_xss', - 'required' => TRUE, - 'schema field' => 'subject', - ); - $properties['url'] = array( - 'label' => t("URL"), - 'description' => t("The URL of the comment."), - 'getter callback' => 'entity_metadata_entity_get_properties', - 'type' => 'uri', - 'computed' => TRUE, - ); - $properties['edit_url'] = array( - 'label' => t("Edit URL"), - 'description' => t("The URL of the comment's edit page."), - 'getter callback' => 'entity_metadata_comment_get_properties', - 'type' => 'uri', - 'computed' => TRUE, - ); - $properties['created'] = array( - 'label' => t("Date created"), - 'description' => t("The date the comment was posted."), - 'type' => 'date', - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer comments', - 'schema field' => 'created', - ); - $properties['parent'] = array( - 'label' => t("Parent"), - 'description' => t("The comment's parent, if comment threading is active."), - 'type' => 'comment', - 'getter callback' => 'entity_metadata_comment_get_properties', - 'setter permission' => 'administer comments', - 'schema field' => 'pid', - ); - $properties['node'] = array( - 'label' => t("Node"), - 'description' => t("The node the comment was posted to."), - 'type' => 'node', - 'setter callback' => 'entity_metadata_comment_setter', - 'setter permission' => 'administer comments', - 'required' => TRUE, - 'schema field' => 'nid', - ); - $properties['author'] = array( - 'label' => t("Author"), - 'description' => t("The author of the comment."), - 'type' => 'user', - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer comments', - 'required' => TRUE, - 'schema field' => 'uid', - ); - $properties['status'] = array( - 'label' => t("Status"), - 'description' => t("Whether the comment is published or unpublished."), - 'setter callback' => 'entity_property_verbatim_set', - // Although the status is expected to be boolean, its schema suggests - // it is an integer, so we follow the schema definition. - 'type' => 'integer', - 'options list' => 'entity_metadata_status_options_list', - 'access callback' => 'entity_metadata_comment_properties_access', - 'schema field' => 'status', - ); - return $info; -} - -/** - * Implements hook_entity_property_info_alter() on top of comment module. - * @see entity_entity_property_info_alter() - */ -function entity_metadata_comment_entity_property_info_alter(&$info) { - // Add info about comment module related properties to the node entity. - $properties = &$info['node']['properties']; - $properties['comment'] = array( - 'label' => t("Comments allowed"), - 'description' => t("Whether comments are allowed on this node: 0 = no, 1 = closed (read only), 2 = open (read/write)."), - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer comments', - 'type' => 'integer', - ); - $properties['comments'] = array( - 'label' => t("Comments"), - 'type' => 'list', - 'description' => t("The node comments."), - 'getter callback' => 'entity_metadata_comment_get_node_properties', - 'computed' => TRUE, - ); - $properties['comment_count'] = array( - 'label' => t("Comment count"), - 'description' => t("The number of comments posted on a node."), - 'getter callback' => 'entity_metadata_comment_get_node_properties', - 'type' => 'integer', - ); - $properties['comment_count_new'] = array( - 'label' => t("New comment count"), - 'description' => t("The number of comments posted on a node since the reader last viewed it."), - 'getter callback' => 'entity_metadata_comment_get_node_properties', - 'type' => 'integer', - ); - - // The comment body field is usually available for all bundles, so add it - // directly to the comment entity. - $info['comment']['properties']['comment_body'] = array( - 'type' => 'text_formatted', - 'label' => t('The main body text'), - 'getter callback' => 'entity_metadata_field_verbatim_get', - 'setter callback' => 'entity_metadata_field_verbatim_set', - 'property info' => entity_property_text_formatted_info(), - 'field' => TRUE, - 'required' => TRUE, - ); - unset($info['comment']['properties']['comment_body']['property info']['summary']); -} diff --git a/modules/field.info.inc b/modules/field.info.inc deleted file mode 100644 index aeea79a..0000000 --- a/modules/field.info.inc +++ /dev/null @@ -1,173 +0,0 @@ - $field) { - $field += array('bundles' => array()); - if ($field_type = field_info_field_types($field['type'])) { - // Add in our default callback as the first one. - $field_type += array('property_callbacks' => array()); - array_unshift($field_type['property_callbacks'], 'entity_metadata_field_default_property_callback'); - - foreach ($field['bundles'] as $entity_type => $bundles) { - foreach ($bundles as $bundle) { - $instance = field_info_instance($entity_type, $field_name, $bundle); - - if ($instance && empty($instance['deleted'])) { - foreach ($field_type['property_callbacks'] as $callback) { - $callback($info, $entity_type, $field, $instance, $field_type); - } - } - } - } - } - } - return $info; -} - -/** - * Callback to add in property info defaults per field instance. - * @see entity_metadata_field_entity_property_info(). - */ -function entity_metadata_field_default_property_callback(&$info, $entity_type, $field, $instance, $field_type) { - if (!empty($field_type['property_type'])) { - if ($field['cardinality'] != 1) { - $field_type['property_type'] = 'list<' . $field_type['property_type'] . '>'; - } - // Add in instance specific property info, if given and apply defaults. - $name = $field['field_name']; - $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name]; - $instance += array('property info' => array()); - $property = $instance['property info'] + array( - // Since the label will be exposed via hook_token_info() and it is not - // clearly defined if that should be sanitized already we prevent XSS - // right here (field labels are user provided text). - 'label' => filter_xss_admin($instance['label']), - 'type' => $field_type['property_type'], - 'description' => t('Field "@name".', array('@name' => $name)), - 'getter callback' => 'entity_metadata_field_property_get', - 'setter callback' => 'entity_metadata_field_property_set', - 'access callback' => 'entity_metadata_field_access_callback', - 'query callback' => 'entity_metadata_field_query', - 'translatable' => !empty($field['translatable']), - // Specify that this property stems from a field. - 'field' => TRUE, - 'required' => !empty($instance['required']), - ); - // For field types of the list module add in the options list callback. - if (strpos($field['type'], 'list') === 0) { - $property['options list'] = 'entity_metadata_field_options_list'; - } - } -} - -/** - * Additional callback to adapt the property info for text fields. If a text - * field is processed we make use of a separate data structure so that format - * filters are available too. For the text value the sanitized, thus processed - * value is returned by default. - * - * @see entity_metadata_field_entity_property_info() - * @see entity_field_info_alter() - * @see entity_property_text_formatted_info() - */ -function entity_metadata_field_text_property_callback(&$info, $entity_type, $field, $instance, $field_type) { - if (!empty($instance['settings']['text_processing']) || $field['type'] == 'text_with_summary') { - // Define a data structure for dealing with text that is formatted or has - // a summary. - $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']]; - - $property['getter callback'] = 'entity_metadata_field_verbatim_get'; - $property['setter callback'] = 'entity_metadata_field_verbatim_set'; - unset($property['query callback']); - - if (empty($instance['settings']['text_processing'])) { - $property['property info'] = entity_property_field_item_textsummary_info(); - } - else { - // For formatted text we use the type name 'text_formatted'. - $property['type'] = ($field['cardinality'] != 1) ? 'list' : 'text_formatted'; - $property['property info'] = entity_property_text_formatted_info(); - } - // Enable auto-creation of the item, so that it is possible to just set - // the textual or summary value. - $property['auto creation'] = 'entity_property_create_array'; - - if ($field['type'] != 'text_with_summary') { - unset($property['property info']['summary']); - } - } -} - -/** - * Additional callback to adapt the property info for term reference fields. - * @see entity_metadata_field_entity_property_info(). - */ -function entity_metadata_field_term_reference_callback(&$info, $entity_type, $field, $instance, $field_type) { - $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']]; - if (count($field['settings']['allowed_values']) == 1) { - $settings = reset($field['settings']['allowed_values']); - $property['bundle'] = $settings['vocabulary']; - } - // Only add the options list callback for controlled vocabularies, thus - // vocabularies not using the autocomplete widget. - if ($instance['widget']['type'] != 'taxonomy_autocomplete') { - $property['options list'] = 'entity_metadata_field_options_list'; - } -} - -/** - * Additional callback to adapt the property info for file fields. - * @see entity_metadata_field_entity_property_info(). - */ -function entity_metadata_field_file_callback(&$info, $entity_type, $field, $instance, $field_type) { - $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']]; - // Define a data structure so it's possible to deal with files and their - // descriptions. - $property['getter callback'] = 'entity_metadata_field_verbatim_get'; - $property['setter callback'] = 'entity_metadata_field_verbatim_set'; - - // Auto-create the field $items as soon as a property is set. - $property['auto creation'] = 'entity_metadata_field_file_create_item'; - $property['validation callback'] = 'entity_metadata_field_file_validate_item'; - - $property['property info'] = entity_property_field_item_file_info(); - - if (empty($instance['settings']['description_field'])) { - unset($property['property info']['description']); - } - if (empty($field['settings']['display_field'])) { - unset($property['property info']['display']); - } - unset($property['query callback']); -} - -/** - * Additional callback to adapt the property info for image fields. - * This callback gets invoked after entity_metadata_field_file_callback(). - * @see entity_metadata_field_entity_property_info(). - */ -function entity_metadata_field_image_callback(&$info, $entity_type, $field, $instance, $field_type) { - $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']]; - // Update the property info with the info for image fields. - $property['property info'] = entity_property_field_item_image_info(); - - if (empty($instance['settings']['alt_field'])) { - unset($property['property info']['alt']); - } - if (empty($instance['settings']['title_field'])) { - unset($property['property info']['title']); - } -} diff --git a/modules/locale.info.inc b/modules/locale.info.inc deleted file mode 100644 index 3c0f36a..0000000 --- a/modules/locale.info.inc +++ /dev/null @@ -1,40 +0,0 @@ - t("Language"), - 'description' => t("This account's default language for e-mails, and preferred language for site presentation."), - 'type' => 'token', - 'getter callback' => 'entity_metadata_locale_get_user_language', - 'setter callback' => 'entity_property_verbatim_set', - 'options list' => 'entity_metadata_language_list', - 'schema field' => 'language', - 'setter permission' => 'administer users', - ); - - $info['site']['properties']['current_page']['property info']['language'] = array( - 'label' => t("Interface language"), - 'description' => t("The language code of the current user interface language."), - 'type' => 'token', - 'getter callback' => 'entity_metadata_locale_get_languages', - 'options list' => 'entity_metadata_language_list', - ); - $info['site']['properties']['current_page']['property info']['language_content'] = array( - 'label' => t("Content language"), - 'description' => t("The language code of the current content language."), - 'type' => 'token', - 'getter callback' => 'entity_metadata_locale_get_languages', - 'options list' => 'entity_metadata_language_list', - ); -} diff --git a/modules/node.info.inc b/modules/node.info.inc deleted file mode 100644 index f146a7e..0000000 --- a/modules/node.info.inc +++ /dev/null @@ -1,172 +0,0 @@ - t("Node ID"), - 'type' => 'integer', - 'description' => t("The unique ID of the node."), - 'schema field' => 'nid', - ); - $properties['vid'] = array( - 'label' => t("Revision ID"), - 'type' => 'integer', - 'description' => t("The unique ID of the node's revision."), - 'schema field' => 'vid', - ); - $properties['is_new'] = array( - 'label' => t("Is new"), - 'type' => 'boolean', - 'description' => t("Whether the node is new and not saved to the database yet."), - 'getter callback' => 'entity_metadata_node_get_properties', - ); - $properties['type'] = array( - 'label' => t("Content type"), - 'type' => 'token', - 'description' => t("The type of the node."), - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer nodes', - 'options list' => 'node_type_get_names', - 'required' => TRUE, - 'schema field' => 'type', - ); - $properties['title'] = array( - 'label' => t("Title"), - 'description' => t("The title of the node."), - 'setter callback' => 'entity_property_verbatim_set', - 'schema field' => 'title', - 'required' => TRUE, - ); - $properties['language'] = array( - 'label' => t("Language"), - 'type' => 'token', - 'description' => t("The language the node is written in."), - 'setter callback' => 'entity_property_verbatim_set', - 'options list' => 'entity_metadata_language_list', - 'schema field' => 'language', - 'setter permission' => 'administer nodes', - ); - $properties['url'] = array( - 'label' => t("URL"), - 'description' => t("The URL of the node."), - 'getter callback' => 'entity_metadata_entity_get_properties', - 'type' => 'uri', - 'computed' => TRUE, - ); - $properties['edit_url'] = array( - 'label' => t("Edit URL"), - 'description' => t("The URL of the node's edit page."), - 'getter callback' => 'entity_metadata_node_get_properties', - 'type' => 'uri', - 'computed' => TRUE, - ); - $properties['status'] = array( - 'label' => t("Status"), - 'description' => t("Whether the node is published or unpublished."), - // Although the status is expected to be boolean, its schema suggests - // it is an integer, so we follow the schema definition. - 'type' => 'integer', - 'options list' => 'entity_metadata_status_options_list', - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer nodes', - 'schema field' => 'status', - ); - $properties['promote'] = array( - 'label' => t("Promoted to frontpage"), - 'description' => t("Whether the node is promoted to the frontpage."), - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer nodes', - 'schema field' => 'promote', - 'type' => 'boolean', - ); - $properties['sticky'] = array( - 'label' => t("Sticky in lists"), - 'description' => t("Whether the node is displayed at the top of lists in which it appears."), - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer nodes', - 'schema field' => 'sticky', - 'type' => 'boolean', - ); - $properties['created'] = array( - 'label' => t("Date created"), - 'type' => 'date', - 'description' => t("The date the node was posted."), - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer nodes', - 'schema field' => 'created', - ); - $properties['changed'] = array( - 'label' => t("Date changed"), - 'type' => 'date', - 'schema field' => 'changed', - 'description' => t("The date the node was most recently updated."), - ); - $properties['author'] = array( - 'label' => t("Author"), - 'type' => 'user', - 'description' => t("The author of the node."), - 'getter callback' => 'entity_metadata_node_get_properties', - 'setter callback' => 'entity_property_verbatim_set', - 'setter permission' => 'administer nodes', - 'required' => TRUE, - 'schema field' => 'uid', - ); - $properties['source'] = array( - 'label' => t("Translation source node"), - 'type' => 'node', - 'description' => t("The original-language version of this node, if one exists."), - 'getter callback' => 'entity_metadata_node_get_properties', - ); - $properties['log'] = array( - 'label' => t("Revision log message"), - 'type' => 'text', - 'description' => t("In case a new revision is to be saved, the log entry explaining the changes for this version."), - 'setter callback' => 'entity_property_verbatim_set', - 'access callback' => 'entity_metadata_node_revision_access', - ); - $properties['revision'] = array( - 'label' => t("Creates revision"), - 'type' => 'boolean', - 'description' => t("Whether saving this node creates a new revision."), - 'setter callback' => 'entity_property_verbatim_set', - 'access callback' => 'entity_metadata_node_revision_access', - ); - return $info; -} - -/** - * Implements hook_entity_property_info_alter() on top of node module. - * @see entity_metadata_entity_property_info_alter() - */ -function entity_metadata_node_entity_property_info_alter(&$info) { - // Move the body property to the node by default, as its usually there this - // makes dealing with it more convenient. - $info['node']['properties']['body'] = array( - 'type' => 'text_formatted', - 'label' => t('The main body text'), - 'getter callback' => 'entity_metadata_field_verbatim_get', - 'setter callback' => 'entity_metadata_field_verbatim_set', - 'property info' => entity_property_text_formatted_info(), - 'auto creation' => 'entity_property_create_array', - 'field' => TRUE, - ); - - // Make it a list if cardinality is not 1. - $field_body = field_info_field('body'); - if (isset($field_body) && $field_body['cardinality'] != 1) { - $info['node']['properties']['body']['type'] = 'list'; - } -} diff --git a/modules/poll.info.inc b/modules/poll.info.inc deleted file mode 100644 index 12d6b35..0000000 --- a/modules/poll.info.inc +++ /dev/null @@ -1,50 +0,0 @@ - t("Poll votes"), - 'description' => t("The number of votes that have been cast on a poll node."), - 'type' => 'integer', - 'getter callback' => 'entity_metadata_poll_node_get_properties', - 'computed' => TRUE, - ); - $properties['poll_winner'] = array( - 'label' => t("Poll winner"), - 'description' => t("The winning poll answer."), - 'getter callback' => 'entity_metadata_poll_node_get_properties', - 'sanitize' => 'filter_xss', - 'computed' => TRUE, - ); - $properties['poll_winner_votes'] = array( - 'label' => t("Poll winner votes"), - 'description' => t("The number of votes received by the winning poll answer."), - 'type' => 'integer', - 'getter callback' => 'entity_metadata_poll_node_get_properties', - 'computed' => TRUE, - ); - $properties['poll_winner_percent'] = array( - 'label' => t("Poll winner percent"), - 'description' => t("The percentage of votes received by the winning poll answer."), - 'getter callback' => 'entity_metadata_poll_node_get_properties', - 'type' => 'decimal', - 'computed' => TRUE, - ); - $properties['poll_duration'] = array( - 'label' => t("Poll duration"), - 'description' => t("The length of time the poll node is set to run."), - 'getter callback' => 'entity_metadata_poll_node_get_properties', - 'type' => 'duration', - ); -} diff --git a/modules/statistics.info.inc b/modules/statistics.info.inc deleted file mode 100644 index cb3fb37..0000000 --- a/modules/statistics.info.inc +++ /dev/null @@ -1,40 +0,0 @@ - t("Number of views"), - 'description' => t("The number of visitors who have read the node."), - 'type' => 'integer', - 'getter callback' => 'entity_metadata_statistics_node_get_properties', - 'computed' => TRUE, - 'access callback' => 'entity_metadata_statistics_properties_access', - ); - $properties['day_views'] = array( - 'label' => t("Views today"), - 'description' => t("The number of visitors who have read the node today."), - 'type' => 'integer', - 'getter callback' => 'entity_metadata_statistics_node_get_properties', - 'computed' => TRUE, - 'access callback' => 'entity_metadata_statistics_properties_access', - ); - $properties['last_view'] = array( - 'label' => t("Last view"), - 'description' => t("The date on which a visitor last read the node."), - 'type' => 'date', - 'getter callback' => 'entity_metadata_statistics_node_get_properties', - 'computed' => TRUE, - 'access callback' => 'entity_metadata_statistics_properties_access', - ); -} diff --git a/modules/system.info.inc b/modules/system.info.inc deleted file mode 100644 index 0c6ba6b..0000000 --- a/modules/system.info.inc +++ /dev/null @@ -1,132 +0,0 @@ - t("Name"), - 'description' => t("The name of the site."), - 'getter callback' => 'entity_metadata_system_get_properties', - 'sanitize' => 'check_plain', - ); - $properties['slogan'] = array( - 'label' => t("Slogan"), - 'description' => t("The slogan of the site."), - 'getter callback' => 'entity_metadata_system_get_properties', - 'sanitize' => 'check_plain', - ); - $properties['mail'] = array( - 'label' => t("Email"), - 'description' => t("The administrative email address for the site."), - 'getter callback' => 'entity_metadata_system_get_properties', - ); - $properties['url'] = array( - 'label' => t("URL"), - 'description' => t("The URL of the site's front page."), - 'getter callback' => 'entity_metadata_system_get_properties', - 'type' => 'uri', - ); - $properties['login_url'] = array( - 'label' => t("Login page"), - 'description' => t("The URL of the site's login page."), - 'getter callback' => 'entity_metadata_system_get_properties', - 'type' => 'uri', - ); - $properties['current_user'] = array( - 'label' => t("Logged in user"), - 'description' => t("The currently logged in user."), - 'getter callback' => 'entity_metadata_system_get_properties', - 'type' => 'user', - ); - $properties['current_date'] = array( - 'label' => t("Current date"), - 'description' => t("The current date and time."), - 'getter callback' => 'entity_metadata_system_get_properties', - 'type' => 'date', - ); - $properties['current_page'] = array( - 'label' => t("Current page"), - 'description' => t("Information related to the current page request."), - 'getter callback' => 'entity_metadata_system_get_properties', - 'type' => 'struct', - 'property info' => array( - 'path' => array( - 'label' => t("Path"), - 'description' => t("The internal Drupal path of the current page request."), - 'getter callback' => 'current_path', - 'type' => 'text', - ), - 'url' => array( - 'label' => t("URL"), - 'description' => t("The full URL of the current page request."), - 'getter callback' => 'entity_metadata_system_get_page_properties', - 'type' => 'uri', - ), - ), - ); - - // Files. - $properties = &$info['file']['properties']; - $properties['fid'] = array( - 'label' => t("File ID"), - 'description' => t("The unique ID of the uploaded file."), - 'type' => 'integer', - 'validation callback' => 'entity_metadata_validate_integer_positive', - 'schema field' => 'fid', - ); - $properties['name'] = array( - 'label' => t("File name"), - 'description' => t("The name of the file on disk."), - 'getter callback' => 'entity_metadata_system_get_file_properties', - 'schema field' => 'filename', - ); - $properties['mime'] = array( - 'label' => t("MIME type"), - 'description' => t("The MIME type of the file."), - 'getter callback' => 'entity_metadata_system_get_file_properties', - 'sanitize' => 'filter_xss', - 'schema field' => 'filemime', - ); - $properties['size'] = array( - 'label' => t("File size"), - 'description' => t("The size of the file, in kilobytes."), - 'getter callback' => 'entity_metadata_system_get_file_properties', - 'type' => 'integer', - 'schema field' => 'filesize', - ); - $properties['url'] = array( - 'label' => t("URL"), - 'description' => t("The web-accessible URL for the file."), - 'getter callback' => 'entity_metadata_system_get_file_properties', - ); - $properties['timestamp'] = array( - 'label' => t("Timestamp"), - 'description' => t("The date the file was most recently changed."), - 'type' => 'date', - 'schema field' => 'timestamp', - ); - $properties['owner'] = array( - 'label' => t("Owner"), - 'description' => t("The user who originally uploaded the file."), - 'type' => 'user', - 'getter callback' => 'entity_metadata_system_get_file_properties', - 'schema field' => 'uid', - ); - return $info; -} diff --git a/modules/taxonomy.info.inc b/modules/taxonomy.info.inc deleted file mode 100644 index e50c63d..0000000 --- a/modules/taxonomy.info.inc +++ /dev/null @@ -1,124 +0,0 @@ - t("Term ID"), - 'description' => t("The unique ID of the taxonomy term."), - 'type' => 'integer', - 'schema field' => 'tid', - ); - $properties['name'] = array( - 'label' => t("Name"), - 'description' => t("The name of the taxonomy term."), - 'setter callback' => 'entity_property_verbatim_set', - 'required' => TRUE, - 'schema field' => 'name', - ); - $properties['description'] = array( - 'label' => t("Description"), - 'description' => t("The optional description of the taxonomy term."), - 'sanitized' => TRUE, - 'raw getter callback' => 'entity_property_verbatim_get', - 'getter callback' => 'entity_metadata_taxonomy_term_get_properties', - 'setter callback' => 'entity_property_verbatim_set', - 'schema field' => 'description', - ); - $properties['weight'] = array( - 'label' => t("Weight"), - 'type' => 'integer', - 'description' => t('The weight of the term, which is used for ordering terms during display.'), - 'setter callback' => 'entity_property_verbatim_set', - 'schema field' => 'weight', - ); - $properties['node_count'] = array( - 'label' => t("Node count"), - 'type' => 'integer', - 'description' => t("The number of nodes tagged with the taxonomy term."), - 'getter callback' => 'entity_metadata_taxonomy_term_get_properties', - 'computed' => TRUE, - ); - $properties['url'] = array( - 'label' => t("URL"), - 'description' => t("The URL of the taxonomy term."), - 'getter callback' => 'entity_metadata_entity_get_properties', - 'type' => 'uri', - 'computed' => TRUE, - ); - $properties['vocabulary'] = array( - 'label' => t("Vocabulary"), - 'description' => t("The vocabulary the taxonomy term belongs to."), - 'setter callback' => 'entity_metadata_taxonomy_term_setter', - 'type' => 'taxonomy_vocabulary', - 'required' => TRUE, - 'schema field' => 'vid', - ); - $properties['parent'] = array( - 'label' => t("Parent terms"), - 'description' => t("The parent terms of the taxonomy term."), - 'getter callback' => 'entity_metadata_taxonomy_term_get_properties', - 'setter callback' => 'entity_metadata_taxonomy_term_setter', - 'type' => 'list', - ); - $properties['parents_all'] = array( - 'label' => t("All parent terms"), - 'description' => t("Ancestors of the term, i.e. parent of all above hierarchy levels."), - 'getter callback' => 'entity_metadata_taxonomy_term_get_properties', - 'type' => 'list', - 'computed' => TRUE, - ); - - // Add meta-data about the basic vocabulary properties. - $properties = &$info['taxonomy_vocabulary']['properties']; - - // Taxonomy vocabulary related variables. - $properties['vid'] = array( - 'label' => t("Vocabulary ID"), - 'description' => t("The unique ID of the taxonomy vocabulary."), - 'type' => 'integer', - 'schema field' => 'vid', - ); - $properties['name'] = array( - 'label' => t("Name"), - 'description' => t("The name of the taxonomy vocabulary."), - 'setter callback' => 'entity_property_verbatim_set', - 'required' => TRUE, - 'schema field' => 'name', - ); - $properties['machine_name'] = array( - 'label' => t("Machine name"), - 'type' => 'token', - 'description' => t("The machine name of the taxonomy vocabulary."), - 'setter callback' => 'entity_property_verbatim_set', - 'required' => TRUE, - 'schema field' => 'machine_name', - ); - $properties['description'] = array( - 'label' => t("Description"), - 'description' => t("The optional description of the taxonomy vocabulary."), - 'setter callback' => 'entity_property_verbatim_set', - 'sanitize' => 'filter_xss', - 'schema field' => 'description', - ); - $properties['term_count'] = array( - 'label' => t("Term count"), - 'type' => 'integer', - 'description' => t("The number of terms belonging to the taxonomy vocabulary."), - 'getter callback' => 'entity_metadata_taxonomy_vocabulary_get_properties', - 'computed' => TRUE, - ); - return $info; -} diff --git a/modules/user.info.inc b/modules/user.info.inc deleted file mode 100644 index 67a62b5..0000000 --- a/modules/user.info.inc +++ /dev/null @@ -1,110 +0,0 @@ - t("User ID"), - 'type' => 'integer', - 'description' => t("The unique ID of the user account."), - 'schema field' => 'uid', - ); - $properties['name'] = array( - 'label' => t("Name"), - 'description' => t("The login name of the user account."), - 'getter callback' => 'entity_metadata_user_get_properties', - 'setter callback' => 'entity_property_verbatim_set', - 'sanitize' => 'filter_xss', - 'required' => TRUE, - 'access callback' => 'entity_metadata_user_properties_access', - 'schema field' => 'name', - ); - $properties['mail'] = array( - 'label' => t("Email"), - 'description' => t("The email address of the user account."), - 'setter callback' => 'entity_property_verbatim_set', - 'validation callback' => 'valid_email_address', - 'required' => TRUE, - 'access callback' => 'entity_metadata_user_properties_access', - 'schema field' => 'mail', - ); - $properties['url'] = array( - 'label' => t("URL"), - 'description' => t("The URL of the account profile page."), - 'getter callback' => 'entity_metadata_user_get_properties', - 'type' => 'uri', - 'computed' => TRUE, - ); - $properties['edit_url'] = array( - 'label' => t("Edit URL"), - 'description' => t("The url of the account edit page."), - 'getter callback' => 'entity_metadata_user_get_properties', - 'type' => 'uri', - 'computed' => TRUE, - ); - $properties['last_access'] = array( - 'label' => t("Last access"), - 'description' => t("The date the user last accessed the site."), - 'getter callback' => 'entity_metadata_user_get_properties', - 'type' => 'date', - 'access callback' => 'entity_metadata_user_properties_access', - 'schema field' => 'access', - ); - $properties['last_login'] = array( - 'label' => t("Last login"), - 'description' => t("The date the user last logged in to the site."), - 'getter callback' => 'entity_metadata_user_get_properties', - 'type' => 'date', - 'access callback' => 'entity_metadata_user_properties_access', - 'schema field' => 'login', - ); - $properties['created'] = array( - 'label' => t("Created"), - 'description' => t("The date the user account was created."), - 'type' => 'date', - 'schema field' => 'created', - 'setter permission' => 'administer users', - ); - $properties['roles'] = array( - 'label' => t("User roles"), - 'description' => t("The roles of the user."), - 'type' => 'list', - 'getter callback' => 'entity_metadata_user_get_properties', - 'setter callback' => 'entity_metadata_user_set_properties', - 'options list' => 'entity_metadata_user_roles', - 'access callback' => 'entity_metadata_user_properties_access', - ); - $properties['status'] = array( - 'label' => t("Status"), - 'description' => t("Whether the user is active or blocked."), - 'setter callback' => 'entity_property_verbatim_set', - // Although the status is expected to be boolean, its schema suggests - // it is an integer, so we follow the schema definition. - 'type' => 'integer', - 'options list' => 'entity_metadata_user_status_options_list', - 'access callback' => 'entity_metadata_user_properties_access', - 'schema field' => 'status', - ); - $properties['theme'] = array( - 'label' => t("Default theme"), - 'description' => t("The user's default theme."), - 'getter callback' => 'entity_metadata_user_get_properties', - 'setter callback' => 'entity_property_verbatim_set', - 'access callback' => 'entity_metadata_user_properties_access', - 'schema field' => 'theme', - ); - return $info; -} - diff --git a/tests/entity_feature.info b/tests/entity_feature.info deleted file mode 100644 index 03e37ce..0000000 --- a/tests/entity_feature.info +++ /dev/null @@ -1,7 +0,0 @@ -name = Entity feature module -description = Provides some entities in code. -version = VERSION -core = 7.x -files[] = entity_feature.module -dependencies[] = entity_test -hidden = TRUE diff --git a/tests/entity_feature.module b/tests/entity_feature.module deleted file mode 100644 index c2c9fbf..0000000 --- a/tests/entity_feature.module +++ /dev/null @@ -1,32 +0,0 @@ - 'main', - 'label' => t('Main test type'), - 'weight' => 0, - 'locked' => TRUE, - )); - - // Types used during CRUD testing. - $types['test'] = entity_create('entity_test_type', array( - 'name' => 'test', - 'label' => 'label', - 'weight' => 0, - )); - $types['test2'] = entity_create('entity_test_type', array( - 'name' => 'test2', - 'label' => 'label2', - 'weight' => 2, - )); - - return $types; -} diff --git a/tests/entity_test.info b/tests/entity_test.info deleted file mode 100644 index a6ad0b6..0000000 --- a/tests/entity_test.info +++ /dev/null @@ -1,8 +0,0 @@ -name = Entity CRUD test module -description = Provides entity types based upon the CRUD API. -version = VERSION -core = 7.x -files[] = entity_test.module -files[] = entity_test.install -dependencies[] = entity -hidden = TRUE diff --git a/tests/entity_test.install b/tests/entity_test.install deleted file mode 100644 index a969323..0000000 --- a/tests/entity_test.install +++ /dev/null @@ -1,170 +0,0 @@ -fields('et') - ->execute() - ->fetchAllAssoc('name'); - - foreach ($types as $name => $type) { - field_attach_delete_bundle('entity_test', $name); - } -} - -/** - * Implements hook_schema(). - */ -function entity_test_schema() { - $schema['entity_test'] = array( - 'description' => 'Stores entity_test items.', - 'fields' => array( - 'pid' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique entity_test item ID.', - ), - 'name' => array( - 'description' => 'The name of the entity_test.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - ), - 'uid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => NULL, - 'description' => "The {users}.uid of the associated user.", - ), - ), - 'indexes' => array( - 'uid' => array('uid'), - ), - 'foreign keys' => array( - 'uid' => array( - 'table' => 'users', - 'columns' => array('uid' => 'uid') - ), - 'name' => array( - 'table' => 'entity_test_types', - 'columns' => array('name' => 'name') - ), - ), - 'primary key' => array('pid'), - ); - - $schema['entity_test_type'] = array( - 'description' => 'Stores information about all defined entity_test types.', - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique entity_test type ID.', - ), - 'name' => array( - 'description' => 'The machine-readable name of this entity_test type.', - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - ), - 'label' => array( - 'description' => 'The human-readable name of this entity_test type.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'weight' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'The weight of this entity_test type in relation to others.', - ), - 'locked' => array( - 'description' => 'A boolean indicating whether the administrator may delete this type.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - ), - 'data' => array( - 'type' => 'text', - 'not null' => FALSE, - 'size' => 'big', - 'serialize' => TRUE, - 'description' => 'A serialized array of additional data related to this entity_test type.', - 'merge' => TRUE, - ), - 'status' => array( - 'type' => 'int', - 'not null' => TRUE, - // Set the default to ENTITY_CUSTOM without using the constant as it is - // not safe to use it at this point. - 'default' => 0x01, - 'size' => 'tiny', - 'description' => 'The exportable status of the entity.', - ), - 'module' => array( - 'description' => 'The name of the providing module if the entity has been defined in code.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => FALSE, - ), - ), - 'primary key' => array('id'), - 'unique keys' => array( - 'name' => array('name'), - ), - ); - - // Add schema for the revision-test-entity. - $schema['entity_test2'] = $schema['entity_test']; - $schema['entity_test2']['fields']['revision_id'] = array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => NULL, - 'description' => 'The ID of the entity\'s default revision.', - ); - $schema['entity_test2']['fields']['title'] = array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ); - - $schema['entity_test2_revision'] = $schema['entity_test']; - $schema['entity_test2_revision']['fields']['revision_id'] = array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'Primary Key: Unique revision ID.', - ); - $schema['entity_test2_revision']['fields']['pid'] = array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => NULL, - 'description' => 'The ID of the attached entity.', - ); - $schema['entity_test2_revision']['fields']['title'] = array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ); - $schema['entity_test2_revision']['primary key'] = array('revision_id'); - - return $schema; -} - diff --git a/tests/entity_test.module b/tests/entity_test.module deleted file mode 100644 index 727797a..0000000 --- a/tests/entity_test.module +++ /dev/null @@ -1,287 +0,0 @@ - array( - 'label' => t('Test Entity'), - 'plural label' => t('Test Entities'), - 'description' => t('An entity type used by the entity API tests.'), - 'entity class' => 'EntityClass', - 'controller class' => 'EntityAPIController', - 'base table' => 'entity_test', - 'fieldable' => TRUE, - 'entity keys' => array( - 'id' => 'pid', - 'bundle' => 'name', - ), - // Make use the class' label() and uri() implementation by default. - 'label callback' => 'entity_class_label', - 'uri callback' => 'entity_class_uri', - 'bundles' => array(), - 'bundle keys' => array( - 'bundle' => 'name', - ), - 'module' => 'entity_test', - ), - 'entity_test_type' => array( - 'label' => t('Test entity type'), - 'entity class' => 'Entity', - 'controller class' => 'EntityAPIControllerExportable', - 'base table' => 'entity_test_type', - 'fieldable' => FALSE, - 'bundle of' => 'entity_test', - 'exportable' => TRUE, - 'entity keys' => array( - 'id' => 'id', - 'name' => 'name', - ), - 'module' => 'entity_test', - ), - - 'entity_test2' => array( - 'label' => t('Test Entity (revision support)'), - 'entity class' => 'EntityClassRevision', - 'controller class' => 'EntityAPIController', - 'base table' => 'entity_test2', - 'revision table' => 'entity_test2_revision', - 'fieldable' => TRUE, - 'entity keys' => array( - 'id' => 'pid', - 'revision' => 'revision_id', - ), - // Make use of the class label() and uri() implementation by default. - 'label callback' => 'entity_class_label', - 'uri callback' => 'entity_class_uri', - 'bundles' => array(), - 'bundle keys' => array( - 'bundle' => 'name', - ), - ), - ); - - // Add bundle info but bypass entity_load() as we cannot use it here. - $types = db_select('entity_test_type', 'et') - ->fields('et') - ->execute() - ->fetchAllAssoc('name'); - - foreach ($types as $name => $type) { - $return['entity_test']['bundles'][$name] = array( - 'label' => $type->label, - ); - } - - // Support entity cache module. - if (module_exists('entitycache')) { - $return['entity_test']['field cache'] = FALSE; - $return['entity_test']['entity cache'] = TRUE; - } - - return $return; -} - -/** - * Gets an array of all test entity types, keyed by the name. - * - * @param $name - * If set, the type with the given name is returned. - */ -function entity_test_get_types($name = NULL) { - $types = entity_load_multiple_by_name('entity_test_type', isset($name) ? array($name) : FALSE); - return isset($name) ? reset($types) : $types; -} - -/** - * Load multiple test entities based on certain conditions. - * - * @param $pids - * An array of entity IDs. - * @param $conditions - * An array of conditions to match against the {entity} table. - * @param $reset - * A boolean indicating that the internal cache should be reset. - * @return - * An array of test entity objects, indexed by pid. - */ -function entity_test_load_multiple($pids = array(), $conditions = array(), $reset = FALSE) { - return entity_load('entity_test', $pids, $conditions, $reset); -} - -/** - * Delete multiple test entities. - * - * @param $pids - * An array of test entity IDs. - */ -function entity_test_delete_multiple(array $pids) { - entity_get_controller('entity_test')->delete($pids); -} - - -/** - * Main class for test entities. - */ -class EntityClass extends Entity { - - public function __construct(array $values = array(), $entityType = NULL) { - parent::__construct($values, 'entity_test'); - } - - /** - * Override buildContent() to add the username to the output. - */ - public function buildContent($view_mode = 'full', $langcode = NULL) { - $content['user'] = array( - '#markup' => "User: ". format_username(user_load($this->uid)), - ); - return entity_get_controller($this->entityType)->buildContent($this, $view_mode, $langcode, $content); - } - - /** - * Specifies the default label, which is picked up by label() by default. - */ - protected function defaultLabel() { - $type = entity_test_get_types($this->name); - return $type->label; - } - - /** - * Specifies the default uri, which is picked up by uri() by default. - */ - protected function defaultURI() { - return array('path' => 'custom/' . $this->identifier()); - } -} - -/** - * Main class for test entities (with revision support). - */ -class EntityClassRevision extends EntityClass { - - public function __construct(array $values = array(), $entityType = NULL) { - Entity::__construct($values, 'entity_test2'); - } - -} - -/** - * - * - * Some hook implementations used by the tests. - * - * - */ - - -/** - * Implements hook_entity_insert(). - */ -function entity_test_entity_insert($entity, $entity_type) { - if ($entity_type == 'entity_test_type') { - $_SESSION['entity_hook_test']['entity_insert'][] = entity_id($entity_type, $entity); - } -} - -/** - * Implements hook_entity_update(). - */ -function entity_test_entity_update($entity, $entity_type) { - $_SESSION['entity_hook_test']['entity_update'][] = entity_id($entity_type, $entity); -} - -/** - * Implements hook_entity_delete(). - */ -function entity_test_entity_delete($entity, $entity_type) { - if ($entity_type == 'entity_test_type') { - $_SESSION['entity_hook_test']['entity_delete'][] = entity_id($entity_type, $entity); - } -} - -/** - * Implements hook_entity_test_type_insert(). - */ -function entity_test_entity_test_type_insert($entity) { - $_SESSION['entity_hook_test']['entity_test_type_insert'][] = $entity->identifier(); -} - -/** - * Implements hook_entity_test_type_update(). - */ -function entity_test_entity_test_type_update($entity) { - $_SESSION['entity_hook_test']['entity_test_type_update'][] = $entity->identifier(); - - // Determine changes on update. - if (!empty($entity->original) && $entity->original->label == 'test_changes') { - if ($entity->original->label != $entity->label) { - $entity->label .= '_update'; - } - } -} - -/** - * Implements hook_entity_test_type_delete(). - */ -function entity_test_entity_test_type_delete($entity) { - $_SESSION['entity_hook_test']['entity_test_type_delete'][] = $entity->identifier(); -} - -/** - * Implements hook_entity_test_type_presave(). - */ -function entity_test_entity_test_type_presave($entity) { - // Determine changes. - if (!empty($entity->original) && $entity->original->label == 'test_changes') { - if ($entity->original->label != $entity->label) { - $entity->label .= '_presave'; - } - } -} - -/** - * Implements hook_entity_property_info_alter() for testing an property of type - * 'entity'. - */ -function entity_test_entity_property_info_alter(&$info) { - $info['node']['properties']['reference'] = array( - 'label' => t('Test reference'), - 'description' => t('A generic entity reference.'), - 'getter callback' => 'entity_test_entity_getter', - 'setter callback' => 'entity_test_entity_setter', - 'type' => 'entity', - ); -} - -/** - * Getter callback for the 'reference' property. - */ -function entity_test_entity_getter($node) { - if (empty($node->entity)) { - $node->entity = array('type' => 'user', 'id' => $node->uid); - } - - // We have to return the entity wrapped. - // Special handling for anonymous user. - if ($node->entity['type'] === 'user' && empty($node->entity['id'])) { - return entity_metadata_wrapper('user', drupal_anonymous_user()); - } - else { - return entity_metadata_wrapper($node->entity['type'], $node->entity['id']); - } -} - -/** - * Setter callback for the 'reference' property. - */ -function entity_test_entity_setter($node, $property_name, $wrapper) { - // The entity has to be passed wrapped. - $node->entity = array('type' => $wrapper->type(), 'id' => $wrapper->getIdentifier()); -} diff --git a/tests/entity_test_i18n.info b/tests/entity_test_i18n.info deleted file mode 100644 index b1f6500..0000000 --- a/tests/entity_test_i18n.info +++ /dev/null @@ -1,7 +0,0 @@ -name = Entity-test type translation -description = Allows translating entity-test types. -dependencies[] = entity_test -dependencies[] = i18n_string -package = Multilingual - Internationalization -core = 7.x -hidden = TRUE \ No newline at end of file diff --git a/tests/entity_test_i18n.module b/tests/entity_test_i18n.module deleted file mode 100644 index 2aa3736..0000000 --- a/tests/entity_test_i18n.module +++ /dev/null @@ -1,53 +0,0 @@ -original->name != $test_type->name) { - i18n_string_update_context("entity_test:entity_test_type:{$test_type->original->name}:*", "entity_test:entity_test_type:{$test_type->name}:*"); - } - i18n_string_object_update('entity_test_type', $test_type); -} - -/** - * Implements hook_{entity_test_type}_delete(). - */ -function entity_test_i18n_entity_test_type_delete($test_type) { - i18n_string_object_remove('entity_test_type', $test_type); -} diff --git a/tests/entity_token.test b/tests/entity_token.test new file mode 100644 index 0000000..4d4b382 --- /dev/null +++ b/tests/entity_token.test @@ -0,0 +1,102 @@ +backdropCreateUser(array('create post content', 'bypass node access', 'administer nodes')); + $this->backdropLogin($admin_user); + } + + /** + * Creates a random file of the given type. + */ + protected function createFile($file_type = 'text') { + // Create a managed file. + $file = current($this->backdropGetTestFiles($file_type)); + + // Set additional file properties and save it. + $file->filemime = file_get_mimetype($file->filename); + $file->uid = 1; + $file->timestamp = REQUEST_TIME; + $file->filesize = filesize($file->uri); + $file->status = 0; + $file = new File((array) $file); + file_save($file); + return $file; + } + + /** + * Tests whether token support is basically working. + */ + function testTokenSupport() { + + // Test basic tokens. + $node = $this->backdropCreateNode(array('sticky' => TRUE, 'promote' => FALSE)); + $text = "Sticky: [node:sticky] Promote: [node:promote] User: [site:current-user:name]"; + $true = t('true'); + $false = t('false'); + $user_name = $GLOBALS['user']->name; + $target = "Sticky: $true Promote: $false User: $user_name"; + $replace = token_replace($text, array('node' => $node)); + $this->assertEqual($replace, $target, 'Provided tokens basically work.'); + + // Test multiple-value tokens using the tags field of articles. + for ($i = 0; $i < 4; $i++) { + $tags[$i] = entity_plus_property_values_create_entity('taxonomy_term', array( + 'name' => $this->randomName(), + 'vocabulary' => 'tags', + ))->save(); + $field_value[LANGUAGE_NONE][$i]['tid'] = $tags[$i]->getIdentifier(); + $labels[$i] = $tags[$i]->label(); + } + $node = $this->backdropCreateNode(array('title' => 'foo', 'type' => 'post', 'field_tags' => $field_value)); + + $text = "Tags: [node:field-tags] First: [node:field-tags:0] 2nd name: [node:field-tags:1:name] 1st vocab [node:field-tags:0:vocabulary]"; + $tag_labels = implode(', ', $labels); + $target = "Tags: $tag_labels First: $labels[0] 2nd name: $labels[1] 1st vocab {$tags[0]->vocabulary->label()}"; + $replace = token_replace($text, array('node' => $node)); + $this->assertEqual($replace, $target, 'Multiple-value token replacements have been replaced.'); + + // Make sure not existing values are not handled. + $replace = token_replace("[node:field-tags:43]", array('node' => $node)); + $this->assertEqual($replace, "[node:field-tags:43]", 'Not existing values are not replaced.'); + + // Test data-structure tokens like [site:current-page:url]. + $replace = token_replace("[site:current-page:url]", array()); + $this->assertEqual($replace, $GLOBALS['base_root'] . request_uri(), 'Token replacements of data structure properties replaced.'); + + // Test chaining of data-structure tokens using an image-field. + $file = $this->createFile('image'); + + // Create node to edit. + $edit = array(); + $edit['title'] = $this->randomName(8); + $this->backdropPost('node/add/post', $edit, t('Save')); + + // Check that the node exists in the database. + $node = $this->backdropGetNodeByTitle($edit['title']); + $this->backdropGet('node/' . $node->nid . '/edit'); + + // $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_0]'] = backdrop_realpath($image->uri); + $node_edit = array( + 'field_image[und][0][fid]' => $file->fid, + ); + $this->backdropPost('node/' . $node->nid . '/edit', $node_edit, t('Save')); + $wrapper = entity_metadata_wrapper('node', $node); + + $wrapper->field_image = array('fid' => $file->fid); + $replace = token_replace("[node:field-image:file:name]", array('node' => $node)); + $this->assertEqual($replace, $wrapper->field_image->file->name->value(), 'Token replacements of an image field have been replaced.'); + } +} diff --git a/tests/entity_token.tests.info b/tests/entity_token.tests.info new file mode 100644 index 0000000..73eb458 --- /dev/null +++ b/tests/entity_token.tests.info @@ -0,0 +1,5 @@ +[EntityTokenTestCase] +name = Entity token functionality +description = Tests provided tokens for entity properties. +group = Entity token +file = entity_token.test diff --git a/theme/entity.theme.css b/theme/entity.theme.css deleted file mode 100644 index a57318d..0000000 --- a/theme/entity.theme.css +++ /dev/null @@ -1,4 +0,0 @@ - -.entity-property-label { - font-weight: bold; -} diff --git a/theme/entity.theme.inc b/theme/entity.theme.inc deleted file mode 100644 index fc0ba7c..0000000 --- a/theme/entity.theme.inc +++ /dev/null @@ -1,215 +0,0 @@ -' . $variables['label'] . ': '; - } - - // Render the content. - $content_suffix = ''; - if (!$variables['label_hidden'] || $variables['content_attributes']) { - $output .= ''; - $content_suffix = ''; - } - $output .= $variables['content'] . $content_suffix; - - // Render the top-level DIV. - return '' . $output . ''; -} - -/** - * Theme preprocess function for theme_entity_property(). - * - * @see theme_entity_property() - */ -function template_preprocess_entity_property(&$variables, $hook) { - $element = $variables['elements']; - - $variables += array( - 'theme_hook_suggestions' => array(), - 'attributes_array' => array(), - ); - // Generate variables from element properties. - foreach (array('label_hidden', 'label', 'property_name') as $name) { - $variables[$name] = check_plain($element['#' . $name]); - } - $variables['title_attributes_array']['class'][] = 'entity-property-label'; - $variables['attributes_array'] = array_merge($variables['attributes_array'], isset($element['#attributes']) ? $element['#attributes'] : array()); - - $variables['property_name_css'] = strtr($element['#property_name'], '_', '-'); - $variables['attributes_array']['class'][] = 'entity-property'; - $variables['attributes_array']['class'][] = 'entity-property-' . $variables['property_name_css']; - - // Add specific suggestions that can override the default implementation. - $variables['theme_hook_suggestions'] += array( - 'entity_property__' . $element['#property_name'], - 'entity_property__' . $element['#entity_type'] . '__' . $element['#property_name'], - ); - - // Populate the content with sensible defaults. - if (!isset($element['#content'])) { - $variables['content'] = entity_property_default_render_value_by_type($element['#entity_wrapped']->{$element['#property_name']}); - } - else { - $variables['content'] = $element['#content']; - } -} - -/** - * Renders a property using simple defaults based upon the property type. - * - * @return string - */ -function entity_property_default_render_value_by_type(EntityMetadataWrapper $property) { - // If there is an options list or entity label, render that by default. - if ($label = $property->label()) { - if ($property instanceof EntityDrupalWrapper && $uri = entity_uri($property->type(), $property->value())) { - return l($label, $uri['path'], $uri['options']); - } - else { - return check_plain($label); - } - } - switch ($property->type()) { - case 'boolean': - return $property->value() ? t('yes') : t('no'); - default: - return check_plain($property->value()); - } -} - -/** - * Theme process function for theme_entity_property(). - * - * Taken over from template_process_field() - * - * @see theme_entity_property() - */ -function template_process_entity_property(&$variables, $hook) { - $element = $variables['elements']; - // The default theme implementation is a function, so template_process() does - // not automatically run, so we need to flatten the classes and attributes - // here. For best performance, only call drupal_attributes() when needed, and - // note that template_preprocess_field() does not initialize the - // *_attributes_array variables. - $variables['attributes'] = empty($variables['attributes_array']) ? '' : drupal_attributes($variables['attributes_array']); - $variables['title_attributes'] = empty($variables['title_attributes_array']) ? '' : drupal_attributes($variables['title_attributes_array']); - $variables['content_attributes'] = empty($variables['content_attributes_array']) ? '' : drupal_attributes($variables['content_attributes_array']); -} - -/** - * Themes the exportable status of an entity. - */ -function theme_entity_status($variables) { - $status = $variables['status']; - $html = $variables['html']; - if (($status & ENTITY_FIXED) == ENTITY_FIXED) { - $label = t('Fixed'); - $help = t('The configuration is fixed and cannot be changed.'); - return $html ? "" . $label . "" : $label; - } - elseif (($status & ENTITY_OVERRIDDEN) == ENTITY_OVERRIDDEN) { - $label = t('Overridden'); - $help = t('This configuration is provided by a module, but has been changed.'); - return $html ? "" . $label . "" : $label; - } - elseif ($status & ENTITY_IN_CODE) { - $label = t('Default'); - $help = t('A module provides this configuration.'); - return $html ? "" . $label . "" : $label; - } - elseif ($status & ENTITY_CUSTOM) { - $label = t('Custom'); - $help = t('A custom configuration by a user.'); - return $html ? "" . $label . "" : $label; - } -} - -/** - * Process variables for entity.tpl.php. - */ -function template_preprocess_entity(&$variables) { - $variables['view_mode'] = $variables['elements']['#view_mode']; - $entity_type = $variables['elements']['#entity_type']; - $variables['entity_type'] = $entity_type; - $entity = $variables['elements']['#entity']; - $variables[$variables['elements']['#entity_type']] = $entity; - $info = entity_get_info($entity_type); - - $variables['title'] = check_plain(entity_label($entity_type, $entity)); - - $uri = entity_uri($entity_type, $entity); - $variables['url'] = $uri && !empty($uri['path']) ? url($uri['path'], $uri['options']) : FALSE; - - if (isset($variables['elements']['#page'])) { - // If set by the caller, respect the page property. - $variables['page'] = $variables['elements']['#page']; - } - else { - // Else, try to automatically detect it. - $variables['page'] = $uri && !empty($uri['path']) && $uri['path'] == $_GET['q']; - } - - // Helpful $content variable for templates. - $variables['content'] = array(); - foreach (element_children($variables['elements']) as $key) { - $variables['content'][$key] = $variables['elements'][$key]; - } - - if (!empty($info['fieldable'])) { - // Make the field variables available with the appropriate language. - field_attach_preprocess($entity_type, $entity, $variables['content'], $variables); - } - list(, , $bundle) = entity_extract_ids($entity_type, $entity); - - // Gather css classes. - $variables['classes_array'][] = drupal_html_class('entity-' . $entity_type); - $variables['classes_array'][] = drupal_html_class($entity_type . '-' . $bundle); - - // Add RDF type and about URI. - if (module_exists('rdf')) { - $variables['attributes_array']['about'] = empty($uri['path']) ? NULL: url($uri['path']); - $variables['attributes_array']['typeof'] = empty($entity->rdf_mapping['rdftype']) ? NULL : $entity->rdf_mapping['rdftype']; - } - - // Add suggestions. - $variables['theme_hook_suggestions'][] = $entity_type; - $variables['theme_hook_suggestions'][] = $entity_type . '__' . $bundle; - $variables['theme_hook_suggestions'][] = $entity_type . '__' . $bundle . '__' . $variables['view_mode']; - if ($id = entity_id($entity_type, $entity)) { - $variables['theme_hook_suggestions'][] = $entity_type . '__' . $id; - } -} diff --git a/theme/entity.tpl.php b/theme/entity.tpl.php deleted file mode 100644 index cfe94dd..0000000 --- a/theme/entity.tpl.php +++ /dev/null @@ -1,48 +0,0 @@ - -
> - - - > - - - - - - - - -
> - -
-
diff --git a/views/entity.views.inc b/views/entity.views.inc deleted file mode 100644 index a0179c0..0000000 --- a/views/entity.views.inc +++ /dev/null @@ -1,723 +0,0 @@ - $info) { - // Provide default integration with the basic controller class if we know - // the module providing the entity and it does not provide views integration. - if (!isset($info['views controller class'])) { - $info['views controller class'] = isset($info['module']) && !module_hook($info['module'], 'views_data') ? 'EntityDefaultViewsController' : FALSE; - } - if ($info['views controller class']) { - $controller = new $info['views controller class']($type); - // Relationship data may return views data for already existing tables, - // so merge results on the second level. - foreach ($controller->views_data() as $table => $table_data) { - $data += array($table => array()); - $data[$table] = array_merge($data[$table], $table_data); - } - } - } - - // Add tables based upon data selection "queries" for all entity types. - foreach (entity_get_info() as $type => $info) { - $table = entity_views_table_definition($type); - if ($table) { - $data['entity_' . $type] = $table; - } - // Generally expose properties marked as 'entity views field'. - $data['views_entity_' . $type] = array(); - foreach (entity_get_all_property_info($type) as $key => $property) { - if (!empty($property['entity views field'])) { - entity_views_field_definition($key, $property, $data['views_entity_' . $type]); - } - } - } - - // Expose generally usable entity-related fields. - foreach (entity_get_info() as $entity_type => $info) { - if (entity_type_supports($entity_type, 'view')) { - // Expose a field allowing to display the rendered entity. - $data['views_entity_' . $entity_type]['rendered_entity'] = array( - 'title' => t('Rendered @entity-type', array('@entity-type' => $info['label'])), - 'help' => t('The @entity-type of the current relationship rendered using a view mode.', array('@entity-type' => $info['label'])), - 'field' => array( - 'handler' => 'entity_views_handler_field_entity', - 'type' => $entity_type, - // The EntityFieldHandlerHelper treats the 'entity object' data - // selector as special case for loading the base entity. - 'real field' => 'entity object', - ), - ); - } - } - - $data['entity__global']['table']['group'] = t('Entity'); - $data['entity__global']['table']['join'] = array( - // #global let's it appear all the time. - '#global' => array(), - ); - $data['entity__global']['entity'] = array( - 'title' => t('Rendered entity'), - 'help' => t('Displays a single chosen entity.'), - 'area' => array( - 'handler' => 'entity_views_handler_area_entity', - ), - ); - - return $data; -} - -/** - * Helper function for getting data selection based entity Views table definitions. - * - * This creates extra tables for each entity type that are not associated with a - * query plugin (and thus are not base tables) and just rely on the entities to - * retrieve the displayed data. To obtain the entities corresponding to a - * certain result set, the field handlers defined on the table use a generic - * interface defined for query plugins that are based on entity handling, and - * which is described in the entity_views_example_query class. - * - * These tables are called "data selection tables". - * - * Other modules providing Views integration with new query plugins that are - * based on entities can then use these tables as a base for their own tables - * (by directly using this method and modifying the returned table) and/or by - * specifying relationships to them. The tables returned here already specify - * relationships to each other wherever an entity contains a reference to - * another (e.g., the node author constructs a relationship from nodes to - * users). - * - * As filtering and other query manipulation is potentially more plugin-specific - * than the display, only field handlers and relationships are provided with - * these tables. By providing a add_selector_orderby() method, the query plugin - * can, however, support click-sorting for the field handlers in these tables. - * - * For a detailed discussion see http://drupal.org/node/1266036 - * - * For example use see the Search API views module in the Search API project: - * http://drupal.org/project/search_api - * - * @param $type - * The entity type whose table definition should be returned. - * @param $exclude - * Whether properties already exposed as 'entity views field' should be - * excluded. Defaults to TRUE, as they are available for all views tables for - * the entity type anyways. - * - * @return - * An array containing the data selection Views table definition for the - * entity type. - * - * @see entity_views_field_definition() - */ -function entity_views_table_definition($type, $exclude = TRUE) { - // As other modules might want to copy these tables as a base for their own - // Views integration, we statically cache the tables to save some time. - $tables = &drupal_static(__FUNCTION__, array()); - - if (!isset($tables[$type])) { - // Work-a-round to fix updating, see http://drupal.org/node/1330874. - // Views data might be rebuilt on update.php before the registry is rebuilt, - // thus the class cannot be auto-loaded. - if (!class_exists('EntityFieldHandlerHelper')) { - module_load_include('inc', 'entity', 'views/handlers/entity_views_field_handler_helper'); - } - - $info = entity_get_info($type); - $tables[$type]['table'] = array( - 'group' => $info['label'], - 'entity type' => $type, - ); - foreach (entity_get_all_property_info($type) as $key => $property) { - if (!$exclude || empty($property['entity views field'])) { - entity_views_field_definition($key, $property, $tables[$type]); - } - } - } - - return $tables[$type]; -} - -/** - * Helper function for adding a Views field definition to data selection based Views tables. - * - * @param $field - * The data selector of the field to add. E.g. "title" would derive the node - * title property, "body:summary" the node body's summary. - * @param array $property_info - * The property information for which to create a field definition. - * @param array $table - * The table into which the definition should be inserted. - * @param $title_prefix - * Internal use only. - * - * @see entity_views_table_definition() - */ -function entity_views_field_definition($field, array $property_info, array &$table, $title_prefix = '') { - $additional = array(); - $additional_field = array(); - - // Create a valid Views field identifier (no colons, etc.). Keep the original - // data selector as real field though. - $key = _entity_views_field_identifier($field, $table); - if ($key != $field) { - $additional['real field'] = $field; - } - $field_name = EntityFieldHandlerHelper::get_selector_field_name($field); - - $field_handlers = entity_views_get_field_handlers(); - - $property_info += entity_property_info_defaults(); - $type = entity_property_extract_innermost_type($property_info['type']); - $title = $title_prefix . $property_info['label']; - if ($info = entity_get_info($type)) { - $additional['relationship'] = array( - 'handler' => $field_handlers['relationship'], - 'base' => 'entity_' . $type, - 'base field' => $info['entity keys']['id'], - 'relationship field' => $field, - 'label' => $title, - ); - if ($property_info['type'] != $type) { - // This is a list of entities, so we should mark the relationship as such. - $additional['relationship']['multiple'] = TRUE; - } - // Implementers of the field handlers alter hook could add handlers for - // specific entity types. - if (!isset($field_handlers[$type])) { - $type = 'entity'; - } - } - elseif (!empty($property_info['field'])) { - $type = 'field'; - // Views' Field API field handler needs some extra definitions to work. - $additional_field['field_name'] = $field_name; - $additional_field['entity_tables'] = array(); - $additional_field['entity type'] = $table['table']['entity type']; - $additional_field['is revision'] = FALSE; - } - // Copied from EntityMetadataWrapper::optionsList() - elseif (isset($property_info['options list']) && is_callable($property_info['options list'])) { - // If this is a nested property, we need to get rid of all prefixes first. - $type = 'options'; - $additional_field['options callback'] = array( - 'function' => $property_info['options list'], - 'info' => $property_info, - ); - } - elseif ($type == 'decimal') { - $additional_field['float'] = TRUE; - } - - if (isset($field_handlers[$type])) { - $table += array($key => array()); - $table[$key] += array( - 'title' => $title, - 'help' => empty($property_info['description']) ? t('(No information available)') : $property_info['description'], - 'field' => array(), - ); - $table[$key]['field'] += array( - 'handler' => $field_handlers[$type], - 'type' => $property_info['type'], - ); - $table[$key] += $additional; - $table[$key]['field'] += $additional_field; - } - if (!empty($property_info['property info'])) { - foreach ($property_info['property info'] as $nested_key => $nested_property) { - entity_views_field_definition($field . ':' . $nested_key, $nested_property, $table, $title . ' » '); - } - } -} - -/** - * @return array - * The handlers to use for the data selection based Views tables. - * - * @see hook_entity_views_field_handlers_alter() - */ -function entity_views_get_field_handlers() { - $field_handlers = drupal_static(__FUNCTION__); - if (!isset($field_handlers)) { - // Field handlers for the entity tables, by type. - $field_handlers = array( - 'text' => 'entity_views_handler_field_text', - 'token' => 'entity_views_handler_field_text', - 'integer' => 'entity_views_handler_field_numeric', - 'decimal' => 'entity_views_handler_field_numeric', - 'date' => 'entity_views_handler_field_date', - 'duration' => 'entity_views_handler_field_duration', - 'boolean' => 'entity_views_handler_field_boolean', - 'uri' => 'entity_views_handler_field_uri', - 'options' => 'entity_views_handler_field_options', - 'field' => 'entity_views_handler_field_field', - 'entity' => 'entity_views_handler_field_entity', - 'relationship' => 'entity_views_handler_relationship', - ); - drupal_alter('entity_views_field_handlers', $field_handlers); - } - return $field_handlers; -} - -/** - * Helper function for creating valid Views field identifiers out of data selectors. - * - * Uses $table to test whether the identifier is already used, and also - * recognizes if a definition for the same field is already present and returns - * that definition's identifier. - * - * @return string - * A valid Views field identifier that is not yet used as a key in $table. - */ -function _entity_views_field_identifier($field, array $table) { - $key = $base = preg_replace('/[^a-zA-Z0-9]+/S', '_', $field); - $i = 0; - // The condition checks whether this sanitized field identifier is already - // used for another field in this table (and whether the identifier is - // "table", which can never be used). - // If $table[$key] is set, the identifier is already used, but this might be - // already for the same field. To test that, we need the original field name, - // which is either $table[$key]['real field'], if set, or $key. If this - // original field name is equal to $field, we can use that key. Otherwise, we - // append numeric suffixes until we reach an unused key. - while ($key == 'table' || (isset($table[$key]) && (isset($table[$key]['real field']) ? $table[$key]['real field'] : $key) != $field)) { - $key = $base . '_' . ++$i; - } - return $key; -} - -/** - * Implements hook_views_plugins(). - */ -function entity_views_plugins() { - // Have views cache the table list for us so it gets - // cleared at the appropriate times. - $data = views_cache_get('entity_base_tables', TRUE); - if (!empty($data->data)) { - $base_tables = $data->data; - } - else { - $base_tables = array(); - foreach (views_fetch_data() as $table => $data) { - if (!empty($data['table']['entity type']) && !empty($data['table']['base'])) { - $base_tables[] = $table; - } - } - views_cache_set('entity_base_tables', $base_tables, TRUE); - } - if (!empty($base_tables)) { - return array( - 'module' => 'entity', - 'row' => array( - 'entity' => array( - 'title' => t('Rendered entity'), - 'help' => t('Renders a single entity in a specific view mode (e.g. teaser).'), - 'handler' => 'entity_views_plugin_row_entity_view', - 'uses fields' => FALSE, - 'uses options' => TRUE, - 'type' => 'normal', - 'base' => $base_tables, - ), - ), - ); - } -} - -/** - * Default controller for generating basic views integration. - * - * The controller tries to generate suiting views integration for the entity - * based upon the schema information of its base table and the provided entity - * property information. - * For that it is possible to map a property name to its schema/views field - * name by adding a 'schema field' key with the name of the field as value to - * the property info. - */ -class EntityDefaultViewsController { - - protected $type, $info, $relationships; - - public function __construct($type) { - $this->type = $type; - $this->info = entity_get_info($type); - } - - /** - * Defines the result for hook_views_data(). - */ - public function views_data() { - $data = array(); - $this->relationships = array(); - - if (!empty($this->info['base table'])) { - $table = $this->info['base table']; - // Define the base group of this table. Fields that don't - // have a group defined will go into this field by default. - $data[$table]['table']['group'] = drupal_ucfirst($this->info['label']); - $data[$table]['table']['entity type'] = $this->type; - - // If the plural label isn't available, use the regular label. - $label = isset($this->info['plural label']) ? $this->info['plural label'] : $this->info['label']; - $data[$table]['table']['base'] = array( - 'field' => $this->info['entity keys']['id'], - 'access query tag' => $this->type . '_access', - 'title' => drupal_ucfirst($label), - 'help' => isset($this->info['description']) ? $this->info['description'] : '', - ); - $data[$table]['table']['entity type'] = $this->type; - $data[$table] += $this->schema_fields(); - - // Add in any reverse-relationships which have been determined. - $data += $this->relationships; - } - if (!empty($this->info['revision table']) && !empty($this->info['entity keys']['revision'])) { - $revision_table = $this->info['revision table']; - - $data[$table]['table']['default_relationship'] = array( - $revision_table => array( - 'table' => $revision_table, - 'field' => $this->info['entity keys']['revision'], - ), - ); - - // Define the base group of this table. Fields that don't - // have a group defined will go into this field by default. - $data[$revision_table]['table']['group'] = drupal_ucfirst($this->info['label']) . ' ' . t('Revisions'); - $data[$revision_table]['table']['entity type'] = $this->type; - - // If the plural label isn't available, use the regular label. - $label = isset($this->info['plural label']) ? $this->info['plural label'] : $this->info['label']; - $data[$revision_table]['table']['base'] = array( - 'field' => $this->info['entity keys']['revision'], - 'access query tag' => $this->type . '_access', - 'title' => drupal_ucfirst($label) . ' ' . t('Revisions'), - 'help' => (isset($this->info['description']) ? $this->info['description'] . ' ' : '') . t('Revisions'), - ); - $data[$revision_table]['table']['entity type'] = $this->type; - $data[$revision_table] += $this->schema_revision_fields(); - - // Add in any reverse-relationships which have been determined. - $data += $this->relationships; - - // For other base tables, explain how we join. - $data[$revision_table]['table']['join'] = array( - // Directly links to base table. - $table => array( - 'left_field' => $this->info['entity keys']['revision'], - 'field' => $this->info['entity keys']['revision'], - ), - ); - $data[$revision_table]['table']['default_relationship'] = array( - $table => array( - 'table' => $table, - 'field' => $this->info['entity keys']['id'], - ), - ); - } - return $data; - } - - /** - * Try to come up with some views fields with the help of the schema and - * the entity property information. - */ - protected function schema_fields() { - $schema = drupal_get_schema($this->info['base table']); - $properties = entity_get_property_info($this->type) + array('properties' => array()); - $data = array(); - - foreach ($properties['properties'] as $name => $property_info) { - if (isset($property_info['schema field']) && isset($schema['fields'][$property_info['schema field']])) { - if ($views_info = $this->map_from_schema_info($name, $schema['fields'][$property_info['schema field']], $property_info)) { - $data[$name] = $views_info; - } - } - } - return $data; - } - - /** - * Try to come up with some views fields with the help of the revision schema - * and the entity property information. - */ - protected function schema_revision_fields() { - $data = array(); - if (!empty($this->info['revision table'])) { - $schema = drupal_get_schema($this->info['revision table']); - $properties = entity_get_property_info($this->type) + array('properties' => array()); - - foreach ($properties['properties'] as $name => $property_info) { - if (isset($property_info['schema field']) && isset($schema['fields'][$property_info['schema field']])) { - if ($views_info = $this->map_from_schema_info($name, $schema['fields'][$property_info['schema field']], $property_info)) { - $data[$name] = $views_info; - } - } - } - } - return $data; - } - - /** - * Comes up with views information based on the given schema and property - * info. - */ - protected function map_from_schema_info($property_name, $schema_field_info, $property_info) { - $type = isset($property_info['type']) ? $property_info['type'] : 'text'; - $views_field_name = $property_info['schema field']; - - $return = array(); - - if (!empty($schema_field_info['serialize'])) { - return FALSE; - } - - $description = array( - 'title' => $property_info['label'], - 'help' => isset($property_info['description']) ? $property_info['description'] : NULL, - ); - - // Add in relationships to related entities. - if (($info = entity_get_info($type)) && !empty($info['base table'])) { - - // Prepare reversed relationship data. - $label_lowercase = drupal_strtolower($this->info['label'][0]) . drupal_substr($this->info['label'], 1); - $property_label_lowercase = drupal_strtolower($property_info['label'][0]) . drupal_substr($property_info['label'], 1); - - // We name the field of the first reverse-relationship just with the - // base table to be backward compatible, for subsequents relationships we - // append the views field name in order to get a unique name. - $name = !isset($this->relationships[$info['base table']][$this->info['base table']]) ? $this->info['base table'] : $this->info['base table'] . '_' . $views_field_name; - $this->relationships[$info['base table']][$name] = array( - 'title' => $this->info['label'], - 'help' => t("Associated @label via the @label's @property.", array('@label' => $label_lowercase, '@property' => $property_label_lowercase)), - 'relationship' => array( - 'label' => $this->info['label'], - 'handler' => $this->getRelationshipHandlerClass($this->type, $type), - 'base' => $this->info['base table'], - 'base field' => $views_field_name, - 'relationship field' => isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'], - ), - ); - - $return['relationship'] = array( - 'label' => drupal_ucfirst($info['label']), - 'handler' => $this->getRelationshipHandlerClass($type, $this->type), - 'base' => $info['base table'], - 'base field' => isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'], - 'relationship field' => $views_field_name, - ); - - // Add in direct field/filters/sorts for the id itself too. - $type = isset($info['entity keys']['name']) ? 'token' : 'integer'; - // Append the views-field-name to the title if it is different to the - // property name. - if ($property_name != $views_field_name) { - $description['title'] .= ' ' . $views_field_name; - } - } - - switch ($type) { - case 'token': - case 'text': - $return += $description + array( - 'field' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_field', - 'click sortable' => TRUE, - ), - 'sort' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_sort', - ), - 'filter' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_filter_string', - ), - 'argument' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_argument_string', - ), - ); - break; - - case 'decimal': - case 'integer': - $return += $description + array( - 'field' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - 'float' => ($type == 'decimal'), - ), - 'sort' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_sort', - ), - 'filter' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_filter_numeric', - ), - 'argument' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_argument_numeric', - ), - ); - break; - - case 'date': - $return += $description + array( - 'field' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_field_date', - 'click sortable' => TRUE, - ), - 'sort' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_sort_date', - ), - 'filter' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_filter_date', - ), - 'argument' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_argument_date', - ), - ); - break; - - case 'duration': - $return += $description + array( - 'field' => array( - 'real field' => $views_field_name, - 'handler' => 'entity_views_handler_field_duration', - 'click sortable' => TRUE, - ), - 'sort' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_sort', - ), - 'filter' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_filter_numeric', - ), - 'argument' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_argument_numeric', - ), - ); - break; - - case 'uri': - $return += $description + array( - 'field' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_field_url', - 'click sortable' => TRUE, - ), - 'sort' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_sort', - ), - 'filter' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_filter_string', - ), - 'argument' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_argument_string', - ), - ); - break; - - case 'boolean': - $return += $description + array( - 'field' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_field_boolean', - 'click sortable' => TRUE, - ), - 'sort' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_sort', - ), - 'filter' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_filter_boolean_operator', - ), - 'argument' => array( - 'real field' => $views_field_name, - 'handler' => 'views_handler_argument_string', - ), - ); - break; - } - - // If there is an options list callback, add to the filter and field. - if (isset($return['filter']) && !empty($property_info['options list'])) { - $return['filter']['handler'] = 'views_handler_filter_in_operator'; - $return['filter']['options callback'] = array('EntityDefaultViewsController', 'optionsListCallback'); - $return['filter']['options arguments'] = array($this->type, $property_name, 'view'); - } - // @todo: This class_exists is needed until views 3.2. - if (isset($return['field']) && !empty($property_info['options list']) && class_exists('views_handler_field_machine_name')) { - $return['field']['handler'] = 'views_handler_field_machine_name'; - $return['field']['options callback'] = array('EntityDefaultViewsController', 'optionsListCallback'); - $return['field']['options arguments'] = array($this->type, $property_name, 'view'); - } - return $return; - } - - /** - * Determines the handler to use for a relationship to an entity type. - * - * @param $entity_type - * The entity type to join to. - * @param $left_type - * The data type from which to join. - */ - function getRelationshipHandlerClass($entity_type, $left_type) { - // Look for an entity type which is used as bundle for the given entity - // type. If there is one, allow filtering the relation by bundle by using - // our own handler. - foreach (entity_get_info() as $type => $info) { - // In case we already join from the bundle entity we do not need to filter - // by bundle entity any more, so we stay with the general handler. - if (!empty($info['bundle of']) && $info['bundle of'] == $entity_type && $type != $left_type) { - return 'entity_views_handler_relationship_by_bundle'; - } - } - return 'views_handler_relationship'; - } - - /** - * A callback returning property options, suitable to be used as views options callback. - */ - public static function optionsListCallback($type, $selector, $op = 'view') { - $wrapper = entity_metadata_wrapper($type, NULL); - $parts = explode(':', $selector); - foreach ($parts as $part) { - $wrapper = $wrapper->get($part); - } - return $wrapper->optionsList($op); - } -} diff --git a/views/entity_views_example_query.php b/views/entity_views_example_query.php deleted file mode 100644 index 7e98e2c..0000000 --- a/views/entity_views_example_query.php +++ /dev/null @@ -1,88 +0,0 @@ -definition['type']) && entity_property_list_extract_type($handler->definition['type'])) { - $options['list']['contains']['mode'] = array('default' => 'collapse'); - $options['list']['contains']['separator'] = array('default' => ', '); - $options['list']['contains']['type'] = array('default' => 'ul'); - } - $options['link_to_entity'] = array('default' => FALSE); - - return $options; - } - - /** - * Provide an appropriate default option form for a handler. - */ - public static function options_form($handler, &$form, &$form_state) { - if (isset($handler->definition['type']) && entity_property_list_extract_type($handler->definition['type'])) { - $form['list']['mode'] = array( - '#type' => 'select', - '#title' => t('List handling'), - '#options' => array( - 'collapse' => t('Concatenate values using a seperator'), - 'list' => t('Output values as list'), - 'first' => t('Show first (if present)'), - 'count' => t('Show item count'), - ), - '#default_value' => $handler->options['list']['mode'], - ); - $form['list']['separator'] = array( - '#type' => 'textfield', - '#title' => t('List seperator'), - '#default_value' => $handler->options['list']['separator'], - '#dependency' => array('edit-options-list-mode' => array('collapse')), - ); - $form['list']['type'] = array( - '#type' => 'select', - '#title' => t('List type'), - '#options' => array( - 'ul' => t('Unordered'), - 'ol' => t('Ordered'), - ), - '#default_value' => $handler->options['list']['type'], - '#dependency' => array('edit-options-list-mode' => array('list')), - ); - } - $form['link_to_entity'] = array( - '#type' => 'checkbox', - '#title' => t('Link this field to its entity'), - '#description' => t("When using this, you should not set any other link on the field."), - '#default_value' => $handler->options['link_to_entity'], - ); - } - - /** - * Add the field for the entity ID (if necessary). - */ - public static function query($handler) { - // Copied over from views_handler_field_entity::query(). - // Sets table_alias (entity table), base_field (entity id field) and - // field_alias (the field's alias). - $handler->table_alias = $base_table = $handler->view->base_table; - $handler->base_field = $handler->view->base_field; - - if (!empty($handler->relationship)) { - foreach ($handler->view->relationship as $relationship) { - if ($relationship->alias == $handler->relationship) { - $base_table = $relationship->definition['base']; - $handler->table_alias = $relationship->alias; - - $table_data = views_fetch_data($base_table); - $handler->base_field = empty($relationship->definition['base field']) ? $table_data['table']['base']['field'] : $relationship->definition['base field']; - } - } - } - - // Add the field if the query back-end implements an add_field() method, - // just like the default back-end. - if (method_exists($handler->query, 'add_field')) { - $handler->field_alias = $handler->query->add_field($handler->table_alias, $handler->base_field, ''); - } - else { - // To ensure there is an alias just set the field alias to the real field. - $handler->field_alias = $handler->real_field; - } - } - - /** - * Extracts the innermost field name from a data selector. - * - * @param $selector - * The data selector. - * - * @return - * The last component of the data selector. - */ - public static function get_selector_field_name($selector) { - return ltrim(substr($selector, strrpos($selector, ':')), ':'); - } - - /** - * Adds a click-sort to the query. - * - * @param $order - * Either 'ASC' or 'DESC'. - */ - public static function click_sort($handler, $order) { - // The normal orderby() method for this usually won't work here. So we need - // query plugins to provide their own method for this. - if (method_exists($handler->query, 'add_selector_orderby')) { - $selector = self::construct_property_selector($handler, TRUE); - $handler->query->add_selector_orderby($selector, $order); - } - } - - /** - * Load the entities for all rows that are about to be displayed. - * - * Automatically takes care of relationships, including data selection - * relationships. Results are written into @code $handler->wrappers @endcode - * and @code $handler->entity_type @endcode is set. - */ - public static function pre_render($handler, &$values, $load_always = FALSE) { - if (empty($values)) { - return; - } - if (!$load_always && empty($handler->options['link_to_entity'])) { - // Check whether we even need to load the entities. - $selector = self::construct_property_selector($handler, TRUE); - $load = FALSE; - foreach ($values as $row) { - if (empty($row->_entity_properties) || !array_key_exists($selector, $row->_entity_properties)) { - $load = TRUE; - break; - } - } - if (!$load) { - return; - } - } - - if (method_exists($handler->query, 'get_result_wrappers')) { - list($handler->entity_type, $handler->wrappers) = $handler->query->get_result_wrappers($values, $handler->relationship, $handler->real_field); - } - else { - list($handler->entity_type, $entities) = $handler->query->get_result_entities($values, $handler->relationship, $handler->real_field); - $handler->wrappers = array(); - foreach ($entities as $id => $entity) { - $handler->wrappers[$id] = entity_metadata_wrapper($handler->entity_type, $entity); - } - } - } - - /** - * Return an Entity API data selector for the given handler's relationship. - * - * A data selector is a concatenation of properties which should be followed - * to arrive at a desired property that may be nested in related entities or - * structures. The separate properties are herein concatenated with colons. - * - * For instance, a data selector of "author:roles" would mean to first - * access the "author" property of the given wrapper, and then for this new - * wrapper to access and return the "roles" property. - * - * Lists of entities are handled automatically by always returning only the - * first entity. - * - * @param $handler - * The handler for which to construct the selector. - * @param $complete - * If TRUE, the complete selector for the field is returned, not just the - * one for its parent. Defaults to FALSE. - * - * @return - * An Entity API data selector for the given handler's relationship. - */ - public static function construct_property_selector($handler, $complete = FALSE) { - $return = ''; - if ($handler->relationship) { - $current_handler = $handler; - $view = $current_handler->view; - $relationships = array(); - // Collect all relationships, keyed by alias. - foreach ($view->relationship as $key => $relationship) { - $key = $relationship->alias ? $relationship->alias : $key; - $relationships[$key] = $relationship; - } - while (!empty($current_handler->relationship) && !empty($relationships[$current_handler->relationship])) { - $current_handler = $relationships[$current_handler->relationship]; - $return = $current_handler->real_field . ($return ? ":$return" : ''); - } - } - - if ($complete) { - $return .= ($return ? ':' : '') . $handler->real_field; - } - elseif ($pos = strrpos($handler->real_field, ':')) { - // If we have a selector as the real_field, append this to the returned - // relationship selector. - $return .= ($return ? ':' : '') . substr($handler->real_field, 0, $pos); - } - - return $return; - } - - /** - * Extracts data from several metadata wrappers based on a data selector. - * - * All metadata wrappers passed to this function have to be based on the exact - * same property information. The data will be returned wrapped by one or more - * metadata wrappers. - * - * Can be used in query plugins for the get_result_entities() and - * get_result_wrappers() methods. - * - * @param array $wrappers - * The EntityMetadataWrapper objects from which to extract data. - * @param $selector - * The selector specifying the data to extract. - * - * @return array - * An array with numeric indices, containing the type of the extracted - * wrappers in the first element. The second element of the array contains - * the extracted property value(s) for each wrapper, keyed to the same key - * that was used for the respecive wrapper in $wrappers. All extracted - * properties are returned as metadata wrappers. - */ - public static function extract_property_multiple(array $wrappers, $selector) { - $parts = explode(':', $selector, 2); - $name = $parts[0]; - - $results = array(); - $entities = array(); - $type = ''; - foreach ($wrappers as $i => $wrapper) { - try { - $property = $wrapper->$name; - $type = $property->type(); - if ($property instanceof EntityDrupalWrapper) { - // Remember the entity IDs to later load all at once (so as to - // properly utilize multiple load functionality). - $id = $property->getIdentifier(); - // Only accept valid ids. $id can be FALSE for entity values that are - // NULL. - if ($id) { - $entities[$type][$i] = $id; - } - } - elseif ($property instanceof EntityStructureWrapper) { - $results[$i] = $property; - } - elseif ($property instanceof EntityListWrapper) { - foreach ($property as $item) { - $results[$i] = $item; - $type = $item->type(); - break; - } - } - // Do nothing in case it cannot be applied. - } - catch (EntityMetadataWrapperException $e) { - // Skip single empty properties. - } - } - - if ($entities) { - // Map back the loaded entities back to the results array. - foreach ($entities as $type => $id_map) { - $loaded = entity_load($type, $id_map); - foreach ($id_map as $i => $id) { - if (isset($loaded[$id])) { - $results[$i] = entity_metadata_wrapper($type, $loaded[$id]); - } - } - } - } - - // If there are no further parts in the selector, we are done now. - if (empty($parts[1])) { - return array($type, $results); - } - return self::extract_property_multiple($results, $parts[1]); - } - - /** - * Get the value of a certain data selector. - * - * Uses $values->_entity_properties to look for already extracted properties. - * - * @param $handler - * The field handler for which to return a value. - * @param $values - * The values for the current row retrieved from the Views query, as an - * object. - * @param $field - * The field to extract. If no value is given, the field of the given - * handler is used instead. The special "entity object" value can be used to - * get the base entity instead of a special field. - * @param $default - * The value to return if the entity or field are not present. - */ - public static function get_value($handler, $values, $field = NULL, $default = NULL) { - // There is a value cache on each handler so parent handlers rendering a - // single field value from a list will get the single value, not the whole - // list. - if (!isset($field) && isset($handler->current_value)) { - return $handler->current_value; - } - $field = isset($field) ? $field : self::get_selector_field_name($handler->real_field); - $selector = self::construct_property_selector($handler); - $selector = $selector ? "$selector:$field" : $field; - if (!isset($values->_entity_properties)) { - $values->_entity_properties = array(); - } - if (!array_key_exists($selector, $values->_entity_properties)) { - if (!isset($handler->wrappers[$handler->view->row_index])) { - $values->_entity_properties[$selector] = $default; - } - elseif (is_array($handler->wrappers[$handler->view->row_index])) { - $values->_entity_properties[$selector] = self::extract_list_wrapper_values($handler->wrappers[$handler->view->row_index], $field); - } - else { - $wrapper = $handler->wrappers[$handler->view->row_index]; - try { - if ($field === 'entity object') { - $values->_entity_properties[$selector] = $wrapper->value(); - } - else { - $values->_entity_properties[$selector] = isset($wrapper->$field) ? $wrapper->$field->value(array('identifier' => TRUE, 'sanitize' => TRUE)) : $default; - } - } - catch (EntityMetadataWrapperException $e) { - $values->_entity_properties[$selector] = $default; - } - } - } - return $values->_entity_properties[$selector]; - } - - /** - * Helper method for extracting the values from an array of wrappers. - * - * Nested arrays of wrappers are also handled, the values are returned in a - * flat (not nested) array. - */ - public static function extract_list_wrapper_values(array $wrappers, $field) { - $return = array(); - foreach ($wrappers as $wrapper) { - if (is_array($wrapper)) { - $values = self::extract_list_wrapper_values($wrapper, $field); - if ($values) { - $return = array_merge($return, $values); - } - } - else { - try { - if ($field == 'entity object') { - $return[] = $wrapper->value(); - } - elseif (isset($wrapper->$field)) { - $return[] = $wrapper->$field->value(array('identifier' => TRUE)); - } - } - catch (EntityMetadataWrapperException $e) { - // An exception probably signifies a non-present property, so we just - // ignore it. - } - } - } - return $return; - } - - /** - * Render the field. - * - * Implements the entity link functionality and list handling. Basic handling - * of the single values is delegated back to the field handler. - * - * @param $handler - * The field handler whose field should be rendered. - * @param $values - * The values for the current row retrieved from the Views query, as an - * object. - * - * @return - * The rendered value for the field. - */ - public static function render($handler, $values) { - $value = $handler->get_value($values); - if (is_array($value)) { - return self::render_list($handler, $value, $values); - } - return self::render_entity_link($handler, $value, $values); - } - - /** - * Render a list of values. - * - * @param $handler - * The field handler whose field is rendered. - * @param $list - * The list of values to render. - * @param $values - * The values for the current row retrieved from the Views query, as an - * object. - * - * @return - * The rendered value for the given list. - */ - public static function render_list($handler, $list, $values) { - // Allow easy overriding of this behaviour in the specific field handler. - if (method_exists($handler, 'render_list')) { - return $handler->render_list($list, $values); - } - $mode = isset($handler->options['list']['mode']) ? $handler->options['list']['mode'] : NULL; - switch ($mode) { - case 'first': - $list = count($list) ? array_shift($list) : NULL; - if (is_array($list)) { - return self::render_list($handler, $list, $values); - } - elseif (isset($list)) { - return self::render_entity_link($handler, $list, $values); - } - return NULL; - - case 'count': - return count($list); - - // Handles both collapse and list output. Fallback is to collapse. - default: - $inner_values = array(); - foreach ($list as $value) { - $value = is_array($value) ? self::render_list($handler, $value, $values) : self::render_entity_link($handler, $value, $values); - if ($value) { - $inner_values[] = $value; - } - } - - // Format output as list. - if ($mode == 'list') { - $type = isset($handler->options['list']['type']) ? $handler->options['list']['type'] : 'ul'; - return theme('item_list', array( - 'items' => $inner_values, - 'type' => $type, - )); - } - - $separator = isset($handler->options['list']['separator']) ? $handler->options['list']['separator'] : ', '; - return implode($separator, $inner_values); - } - } - - /** - * Render a single value as a link to the entity if applicable. - * - * @param $handler - * The field handler whose field is rendered. - * @param $value - * The single value to render. - * @param $values - * The values for the current row retrieved from the Views query, as an - * object. - * - * @return - * The rendered value. - */ - public static function render_entity_link($handler, $value, $values) { - // Allow easy overriding of this behaviour in the specific field handler. - if (method_exists($handler, 'render_entity_link')) { - return $handler->render_entity_link($value, $values); - } - $render = self::render_single_value($handler, $value, $values); - if (!$handler->options['link_to_entity']) { - return $render; - } - $entity = $handler->get_value($values, 'entity object'); - if (is_object($entity) && ($url = entity_uri($handler->entity_type, $entity))) { - return l($render, $url['path'], array('html' => TRUE) + $url['options']); - } - return $render; - } - - /** - * Render a single value. - * - * @param $handler - * The field handler whose field is rendered. - * @param $value - * The single value to render. - * @param $values - * The values for the current row retrieved from the Views query, as an - * object. - * - * @return - * The rendered value. - */ - public static function render_single_value($handler, $value, $values) { - // Try to use the method in the specific field handler. - if (method_exists($handler, 'render_single_value')) { - $handler->current_value = $value; - $return = $handler->render_single_value($value, $values); - unset($handler->current_value); - return $return; - } - // Default fallback in case the field handler doesn't provide the method. - return is_scalar($value) ? check_plain($value) : nl2br(check_plain(print_r($value, TRUE))); - } - -} diff --git a/views/handlers/entity_views_handler_area_entity.inc b/views/handlers/entity_views_handler_area_entity.inc deleted file mode 100644 index 0c5b714..0000000 --- a/views/handlers/entity_views_handler_area_entity.inc +++ /dev/null @@ -1,150 +0,0 @@ - 'node'); - $options['entity_id'] = array('default' => ''); - $options['view_mode'] = array('default' => 'full'); - $options['bypass_access'] = array('default' => FALSE); - return $options; - } - - function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - - $entity_type_options = array(); - foreach (entity_get_info() as $entity_type => $entity_info) { - $entity_type_options[$entity_type] = $entity_info['label']; - } - - $entity_type = $this->options['entity_type']; - - $form['entity_type'] = array( - '#type' => 'select', - '#title' => t('Entity type'), - '#options' => $entity_type_options, - '#description' => t('Choose the entity type you want to display in the area.'), - '#default_value' => $entity_type, - '#ajax' => array( - 'path' => views_ui_build_form_url($form_state), - ), - '#submit' => array('views_ui_config_item_form_submit_temporary'), - '#executes_submit_callback' => TRUE, - ); - - $form['entity_id'] = array( - '#type' => 'textfield', - '#title' => t('Entity id'), - '#description' => t('Choose the entity you want to display in the area. To render an entity given by a contextual filter use "%1" for the first argument, "%2" for the second, etc.'), - '#default_value' => $this->options['entity_id'], - ); - - if ($entity_type) { - $entity_info = entity_get_info($entity_type); - $options = array(); - if (!empty($entity_info['view modes'])) { - foreach ($entity_info['view modes'] as $mode => $settings) { - $options[$mode] = $settings['label']; - } - } - - if (count($options) > 1) { - $form['view_mode'] = array( - '#type' => 'select', - '#options' => $options, - '#title' => t('View mode'), - '#default_value' => $this->options['view_mode'], - ); - } - else { - $form['view_mode_info'] = array( - '#type' => 'item', - '#title' => t('View mode'), - '#description' => t('Only one view mode is available for this entity type.'), - '#markup' => $options ? current($options) : t('Default'), - ); - $form['view_mode'] = array( - '#type' => 'value', - '#value' => $options ? key($options) : 'default', - ); - } - } - $form['bypass_access'] = array( - '#type' => 'checkbox', - '#title' => t('Bypass access checks'), - '#description' => t('If enabled, access permissions for rendering the entity are not checked.'), - '#default_value' => !empty($this->options['bypass_access']), - ); - return $form; - } - - public function admin_summary() { - $label = parent::admin_summary(); - if (!empty($this->options['entity_id'])) { - return t('@label @entity_type:@entity_id', array( - '@label' => $label, - '@entity_type' => $this->options['entity_type'], - '@entity_id' => $this->options['entity_id'], - )); - } - } - - public function render($empty = FALSE) { - if (!$empty || !empty($this->options['empty'])) { - return $this->render_entity($this->options['entity_type'], $this->options['entity_id'], $this->options['view_mode']); - } - return ''; - } - - /** - * Render an entity using the view mode. - */ - public function render_entity($entity_type, $entity_id, $view_mode) { - $tokens = $this->get_render_tokens(); - // Replace argument tokens in entity id. - $entity_id = strtr($entity_id, $tokens); - if (!empty($entity_type) && !empty($entity_id) && !empty($view_mode)) { - $entity = entity_load_single($entity_type, $entity_id); - if (!empty($this->options['bypass_access']) || entity_access('view', $entity_type, $entity)) { - $render = entity_view($entity_type, array($entity), $view_mode); - $render_entity = reset($render); - return drupal_render($render_entity); - } - } - else { - return ''; - } - } - - /** - * Get the 'render' tokens to use for advanced rendering. - * - * This runs through all of the fields and arguments that - * are available and gets their values. This will then be - * used in one giant str_replace(). - */ - function get_render_tokens() { - $tokens = array(); - if (!empty($this->view->build_info['substitutions'])) { - $tokens = $this->view->build_info['substitutions']; - } - $count = 0; - foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) { - $token = '%' . ++$count; - if (!isset($tokens[$token])) { - $tokens[$token] = ''; - } - // Use strip tags as there should never be HTML in the path. - // However, we need to preserve special characters like " that - // were removed by check_plain(). - $tokens['%' . $count] = $handler->argument; - } - - return $tokens; - } -} diff --git a/views/handlers/entity_views_handler_field_boolean.inc b/views/handlers/entity_views_handler_field_boolean.inc deleted file mode 100644 index 42943d9..0000000 --- a/views/handlers/entity_views_handler_field_boolean.inc +++ /dev/null @@ -1,99 +0,0 @@ - TRUE); - $options['granularity'] = array('default' => 2); - $options['prefix'] = array('default' => '', 'translatable' => TRUE); - $options['suffix'] = array('default' => '', 'translatable' => TRUE); - - return $options; - } - - public function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - EntityFieldHandlerHelper::options_form($this, $form, $form_state); - - $form['format_interval'] = array( - '#type' => 'checkbox', - '#title' => t('Format interval'), - '#description' => t('If checked, the value will be formatted as a time interval. Otherwise, just the number of seconds will be displayed.'), - '#default_value' => $this->options['format_interval'], - ); - $form['granularity'] = array( - '#type' => 'textfield', - '#title' => t('Granularity'), - '#default_value' => $this->options['granularity'], - '#description' => t('Specify how many different units to display.'), - '#dependency' => array('edit-options-format-interval' => array(TRUE)), - '#size' => 2, - ); - $form['prefix'] = array( - '#type' => 'textfield', - '#title' => t('Prefix'), - '#default_value' => $this->options['prefix'], - '#description' => t('Text to put before the duration text.'), - ); - $form['suffix'] = array( - '#type' => 'textfield', - '#title' => t('Suffix'), - '#default_value' => $this->options['suffix'], - '#description' => t('Text to put after the duration text.'), - ); - } - - /** - * Render the field. - * - * @param $values - * The values retrieved from the database. - */ - public function render($values) { - return EntityFieldHandlerHelper::render($this, $values); - } - - /** - * Render a single field value. - */ - public function render_single_value($value, $values) { - if ($this->options['format_interval']) { - $value = format_interval($value, (int) $this->options['granularity']); - } - // Value sanitization is handled by the wrapper, see - // EntityFieldHandlerHelper::get_value(). - return $this->sanitize_value($this->options['prefix'], 'xss') . - $value . - $this->sanitize_value($this->options['suffix'], 'xss'); - } - -} diff --git a/views/handlers/entity_views_handler_field_entity.inc b/views/handlers/entity_views_handler_field_entity.inc deleted file mode 100644 index d9fe73b..0000000 --- a/views/handlers/entity_views_handler_field_entity.inc +++ /dev/null @@ -1,207 +0,0 @@ -field_entity_type = entity_property_extract_innermost_type($this->definition['type']); - } - - /** - * Overridden to add the field for the entity ID (if necessary). - */ - public function query() { - EntityFieldHandlerHelper::query($this); - } - - /** - * Adds a click-sort to the query. - */ - public function click_sort($order) { - EntityFieldHandlerHelper::click_sort($this, $order); - } - - /** - * Load the entities for all rows that are about to be displayed. - */ - public function pre_render(&$values) { - EntityFieldHandlerHelper::pre_render($this, $values); - } - - /** - * Overridden to use a metadata wrapper. - */ - public function get_value($values, $field = NULL) { - return EntityFieldHandlerHelper::get_value($this, $values, $field); - } - - public function option_definition() { - $options = parent::option_definition(); - $options += EntityFieldHandlerHelper::option_definition($this); - - $options['display'] = array('default' => 'label'); - $options['link_to_entity']['default'] = TRUE; - $options['view_mode'] = array('default' => 'default'); - $options['bypass_access'] = array('default' => FALSE); - - return $options; - } - - public function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - EntityFieldHandlerHelper::options_form($this, $form, $form_state); - // We want a different form field at a different place. - unset($form['link_to_entity']); - - $options = array( - 'label' => t('Show entity label'), - 'id' => t('Show entity ID'), - 'view' => t('Show complete entity'), - ); - $form['display'] = array( - '#type' => 'select', - '#title' => t('Display'), - '#description' => t('Decide how this field will be displayed.'), - '#options' => $options, - '#default_value' => $this->options['display'], - ); - $form['link_to_entity'] = array( - '#type' => 'checkbox', - '#title' => t('Link to entity'), - '#description' => t('Link this field to the entity.'), - '#default_value' => $this->options['link_to_entity'], - '#dependency' => array('edit-options-display' => array('label', 'id')), - ); - - // Stolen from entity_views_plugin_row_entity_view. - $entity_info = entity_get_info($this->field_entity_type); - $options = array(); - if (!empty($entity_info['view modes'])) { - foreach ($entity_info['view modes'] as $mode => $settings) { - $options[$mode] = $settings['label']; - } - } - - if (count($options) > 1) { - $form['view_mode'] = array( - '#type' => 'select', - '#options' => $options, - '#title' => t('View mode'), - '#default_value' => $this->options['view_mode'], - '#dependency' => array('edit-options-display' => array('view')), - ); - } - else { - $form['view_mode'] = array( - '#type' => 'value', - '#value' => $options ? key($options) : 'default', - ); - } - $form['bypass_access'] = array( - '#type' => 'checkbox', - '#title' => t('Bypass access checks'), - '#description' => t('If enabled, access permissions for rendering the entity are not checked.'), - '#default_value' => !empty($this->options['bypass_access']), - ); - } - - public function render($values) { - return EntityFieldHandlerHelper::render($this, $values); - } - - /** - * Render a value as a link to the entity if applicable. - * - * @param $value - * The value to render. - * @param $values - * The values for the current row retrieved from the Views query, as an - * object. - */ - public function render_entity_link($entity, $values) { - $type = $this->field_entity_type; - if (!is_object($entity) && isset($entity) && $entity !== FALSE) { - $entity = entity_load_single($type, $entity); - } - if (!$entity) { - return ''; - } - $render = $this->render_single_value($entity, $values); - if (!$this->options['link_to_entity'] || $this->options['display'] == 'view') { - return $render; - } - if (is_object($entity) && ($url = entity_uri($type, $entity))) { - return l($render, $url['path'], array('html' => TRUE) + $url['options']); - } - return $render; - } - - /** - * Render a single field value. - */ - public function render_single_value($entity, $values) { - $type = $this->field_entity_type; - if (!is_object($entity) && isset($entity) && $entity !== FALSE) { - $entity = entity_load_single($type, $entity); - } - // Make sure the entity exists and access is either given or bypassed. - if (!$entity || !(!empty($this->options['bypass_access']) || entity_access('view', $type, $entity))) { - return ''; - } - - if ($this->options['display'] === 'view') { - $entity_view = entity_view($type, array($entity), $this->options['view_mode']); - return render($entity_view); - } - - if ($this->options['display'] == 'label') { - $value = entity_label($type, $entity); - } - // Either $options[display] == 'id', or we have no label. - if (empty($value)) { - $value = entity_id($type, $entity); - } - $value = $this->sanitize_value($value); - - return $value; - } - -} diff --git a/views/handlers/entity_views_handler_field_field.inc b/views/handlers/entity_views_handler_field_field.inc deleted file mode 100644 index f3cd7da..0000000 --- a/views/handlers/entity_views_handler_field_field.inc +++ /dev/null @@ -1,105 +0,0 @@ -field_info, $this->definition['entity type']); - } - - /** - * Overridden to add the field for the entity ID (if necessary). - */ - public function query($use_groupby = FALSE) { - EntityFieldHandlerHelper::query($this); - } - - /** - * Adds a click-sort to the query. - */ - public function click_sort($order) { - EntityFieldHandlerHelper::click_sort($this, $order); - } - - /** - * Override so it doesn't do any harm (or, anything at all). - */ - public function post_execute(&$values) { } - - /** - * Load the entities for all rows that are about to be displayed. - */ - public function pre_render(&$values) { - parent::pre_render($values); - EntityFieldHandlerHelper::pre_render($this, $values, TRUE); - } - - /** - * Overridden to get the items our way. - */ - public function get_items($values) { - $items = array(); - // Set the entity type for the parent handler. - $values->_field_data[$this->field_alias]['entity_type'] = $this->entity_type; - // We need special handling for lists of entities as the base. - $entities = EntityFieldHandlerHelper::get_value($this, $values, 'entity object'); - if (!is_array($entities)) { - $entities = $entities ? array($entities) : array(); - } - foreach ($entities as $entity) { - // Only try to render the field if it is even present on this bundle. - // Otherwise, field_view_field() will trigger a fatal. - list (, , $bundle) = entity_extract_ids($this->entity_type, $entity); - if (field_info_instance($this->entity_type, $this->definition['field_name'], $bundle)) { - // Set the currently rendered entity. - $values->_field_data[$this->field_alias]['entity'] = $entity; - $items = array_merge($items, $this->set_items($values, $this->view->row_index)); - } - } - return $items; - } - - /** - * Overridden to force displaying multiple values in a single row. - */ - function multiple_options_form(&$form, &$form_state) { - parent::multiple_options_form($form, $form_state); - $form['group_rows']['#default_value'] = TRUE; - $form['group_rows']['#disabled'] = TRUE; - } -} diff --git a/views/handlers/entity_views_handler_field_numeric.inc b/views/handlers/entity_views_handler_field_numeric.inc deleted file mode 100644 index bf98149..0000000 --- a/views/handlers/entity_views_handler_field_numeric.inc +++ /dev/null @@ -1,99 +0,0 @@ - TRUE); - return $options; - } - - /** - * Returns an option form for setting this handler's options. - */ - public function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - EntityFieldHandlerHelper::options_form($this, $form, $form_state); - - $form['format_name'] = array( - '#title' => t('Use human-readable name'), - '#type' => 'checkbox', - '#description' => t("If this is checked, the values' names will be displayed instead of their internal identifiers."), - '#default_value' => $this->options['format_name'], - '#weight' => -5, - ); - } - - public function render($values) { - return EntityFieldHandlerHelper::render($this, $values); - } - - /** - * Render a single field value. - */ - public function render_single_value($value, $values) { - if (!isset($this->option_list)) { - $this->option_list = array(); - $callback = $this->definition['options callback']; - if (is_callable($callback['function'])) { - // If a selector is used, get the name of the selected field. - $field_name = EntityFieldHandlerHelper::get_selector_field_name($this->real_field); - $this->option_list = call_user_func($callback['function'], $field_name, $callback['info'], 'view'); - } - } - if ($this->options['format_name'] && isset($this->option_list[$value])) { - $value = $this->option_list[$value]; - } - // Sanitization is handled by the wrapper, see - // EntityFieldHandlerHelper::get_value(). - return $value; - } - -} diff --git a/views/handlers/entity_views_handler_field_text.inc b/views/handlers/entity_views_handler_field_text.inc deleted file mode 100644 index 425abf2..0000000 --- a/views/handlers/entity_views_handler_field_text.inc +++ /dev/null @@ -1,101 +0,0 @@ -definition['multiple'])) { - $form['multiple_note'] = array( - '#markup' => t('Note: This is a multi-valued relationship, which is currently not supported. ' . - 'Only the first related entity will be shown.'), - '#weight' => -5, - ); - } - } - - /** - * Called to implement a relationship in a query. - * - * As we don't add any data to the query itself, we don't have to do anything - * here. Views just don't thinks we have been called unless we set our - * $alias property. Otherwise, this override is just here to keep PHP from - * blowing up by calling inexistent methods on the query plugin. - */ - public function query() { - $this->alias = $this->options['id']; - } - -} diff --git a/views/handlers/entity_views_handler_relationship_by_bundle.inc b/views/handlers/entity_views_handler_relationship_by_bundle.inc deleted file mode 100644 index a4d09d3..0000000 --- a/views/handlers/entity_views_handler_relationship_by_bundle.inc +++ /dev/null @@ -1,117 +0,0 @@ - array()); - - return $options; - } - - /** - * Add an entity type option. - */ - function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - - // Get the entity type and info from the table data for the base on the - // right hand side of the relationship join. - $table_data = views_fetch_data($this->definition['base']); - $entity_type = $table_data['table']['entity type']; - $entity_info = entity_get_info($entity_type); - - // Get the info of the bundle entity. - foreach (entity_get_info() as $type => $info) { - if (isset($info['bundle of']) && $info['bundle of'] == $entity_type) { - $entity_bundle_info = $info; - break; - } - } - - $plural_label = isset($entity_bundle_info['plural label']) ? $entity_bundle_info['plural label'] : $entity_bundle_info['label'] . 's'; - $bundle_options = array(); - foreach ($entity_info['bundles'] as $name => $info) { - $bundle_options[$name] = $info['label']; - } - - $form['bundle_types'] = array( - '#title' => $plural_label, - '#type' => 'checkboxes', - '#description' => t('Restrict this relationship to one or more @bundles.', array('@bundles' => strtolower($entity_bundle_info['plural label']))), - '#options' => $bundle_options, - '#default_value' => $this->options['bundle_types'], - ); - } - - /** - * Make sure only checked bundle types are left. - */ - function options_submit(&$form, &$form_state) { - $form_state['values']['options']['bundle_types'] = array_filter($form_state['values']['options']['bundle_types']); - parent::options_submit($form, $form_state); - } - - /** - * Called to implement a relationship in a query. - * - * Mostly the same as the parent method, except we add an extra clause to - * the join. - */ - function query() { - $table_data = views_fetch_data($this->definition['base']); - $base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field']; - $this->ensure_my_table(); - - $def = $this->definition; - $def['table'] = $this->definition['base']; - $def['field'] = $base_field; - $def['left_table'] = $this->table_alias; - $def['left_field'] = $this->field; - if (!empty($this->options['required'])) { - $def['type'] = 'INNER'; - } - - // Add an extra clause to the join if there are bundle types selected. - if ($this->options['bundle_types']) { - $entity_info = entity_get_info($table_data['table']['entity type']); - $def['extra'] = array( - array( - // The table and the IN operator are implicit. - 'field' => $entity_info['entity keys']['bundle'], - 'value' => $this->options['bundle_types'], - ), - ); - } - - if (!empty($def['join_handler']) && class_exists($def['join_handler'])) { - $join = new $def['join_handler']; - } - else { - $join = new views_join(); - } - - $join->definition = $def; - $join->construct(); - $join->adjusted = TRUE; - - // Use a short alias for this. - $alias = $def['table'] . '_' . $this->table; - $this->alias = $this->query->add_relationship($alias, $join, $this->definition['base'], $this->relationship); - } -} diff --git a/views/plugins/entity_views_plugin_row_entity_view.inc b/views/plugins/entity_views_plugin_row_entity_view.inc deleted file mode 100644 index 5e738a8..0000000 --- a/views/plugins/entity_views_plugin_row_entity_view.inc +++ /dev/null @@ -1,98 +0,0 @@ -view->base_table); - $this->entity_type = $table_data['table']['entity type']; - // Set base table and field information as used by views_plugin_row to - // select the entity id if used with default query class. - $info = entity_get_info($this->entity_type); - if (!empty($info['base table']) && $info['base table'] == $this->view->base_table) { - $this->base_table = $info['base table']; - $this->base_field = $info['entity keys']['id']; - } - } - - public function option_definition() { - $options = parent::option_definition(); - $options['view_mode'] = array('default' => 'full'); - return $options; - } - - public function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - - $entity_info = entity_get_info($this->entity_type); - $options = array(); - if (!empty($entity_info['view modes'])) { - foreach ($entity_info['view modes'] as $mode => $settings) { - $options[$mode] = $settings['label']; - } - } - - if (count($options) > 1) { - $form['view_mode'] = array( - '#type' => 'select', - '#options' => $options, - '#title' => t('View mode'), - '#default_value' => $this->options['view_mode'], - ); - } - else { - $form['view_mode_info'] = array( - '#type' => 'item', - '#title' => t('View mode'), - '#description' => t('Only one view mode is available for this entity type.'), - '#markup' => $options ? current($options) : t('Default'), - ); - $form['view_mode'] = array( - '#type' => 'value', - '#value' => $options ? key($options) : 'default', - ); - } - return $form; - } - - public function pre_render($values) { - if (!empty($values)) { - list($this->entity_type, $this->entities) = $this->view->query->get_result_entities($values, !empty($this->relationship) ? $this->relationship : NULL, isset($this->field_alias) ? $this->field_alias : NULL); - } - // Render the entities. - if ($this->entities) { - $render = entity_view($this->entity_type, $this->entities, $this->options['view_mode']); - // Remove the first level of the render array. - $this->rendered_content = reset($render); - } - } - - /** - * Overridden to return the entity object. - */ - function get_value($values, $field = NULL) { - return isset($this->entities[$this->view->row_index]) ? $this->entities[$this->view->row_index] : FALSE; - } - - public function render($values) { - if ($entity = $this->get_value($values)) { - // Add the view object as views_plugin_row_node_view::render() would. - // Otherwise the views theme suggestions won't work properly. - $entity->view = $this->view; - $render = $this->rendered_content[entity_id($this->entity_type, $entity)]; - return drupal_render($render); - } - } -}