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); - } - } -}