first import
This commit is contained in:
339
sites/all/modules/entity/LICENSE.txt
Normal file
339
sites/all/modules/entity/LICENSE.txt
Normal file
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
156
sites/all/modules/entity/README.txt
Normal file
156
sites/all/modules/entity/README.txt
Normal file
@@ -0,0 +1,156 @@
|
||||
|
||||
Entity API module
|
||||
-----------------
|
||||
by Wolfgang Ziegler, nuppla@zites.net
|
||||
|
||||
This module extends the entity API of Drupal core in order to provide a unified
|
||||
way to deal with entities and their properties. Additionally, it provides an
|
||||
entity CRUD controller, which helps simplifying the creation of new entity types.
|
||||
|
||||
|
||||
This is an API module. You only need to enable it if a module depends on it or
|
||||
you are interested in using it for development.
|
||||
|
||||
This README is for interested developers. If you are not interested in
|
||||
developing, you may stop reading now.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Entity API
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
* The module provides API functions allowing modules to create, save, delete
|
||||
or to determine access for entities based on any entity type, for which the
|
||||
necessary metadata is available. The module comes with integration for all
|
||||
core entity types, as well as for entities provided via the Entity CRUD API
|
||||
(see below). However, for any other entity type implemented by a contrib
|
||||
module, the module integration has to be provided by the contrib module
|
||||
itself.
|
||||
|
||||
* Thus the module provides API functions like entity_save(), entity_create(),
|
||||
entity_delete(), entity_revision_delete(), entity_view() and entity_access()
|
||||
among others.
|
||||
entity_load(), entity_label() and entity_uri() are already provided by
|
||||
Drupal core.
|
||||
|
||||
* For more information about how to provide this metadata, have a look at the
|
||||
API documentation, i.e. entity_metadata_hook_entity_info().
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Entity CRUD API - Providing new entity types
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
* This API helps you when defining a new entity type. It provides an entity
|
||||
controller, which implements full CRUD functionality for your entities.
|
||||
|
||||
* To make use of the CRUD functionality you may just use the API functions
|
||||
entity_create(), entity_delete() and entity_save().
|
||||
|
||||
Alternatively you may specify a class to use for your entities, for which the
|
||||
"Entity" class is provided. In particular, it is useful to extend this class
|
||||
in order to easily customize the entity type, e.g. saving.
|
||||
|
||||
* The controller supports fieldable entities and revisions. There is also a
|
||||
controller which supports implementing exportable entities.
|
||||
|
||||
* The Entity CRUD API helps with providing additional module integration too,
|
||||
e.g. exportable entities are automatically integrated with the Features
|
||||
module. These module integrations are implemented in separate controller
|
||||
classes, which may be overridden and deactivated on their own.
|
||||
|
||||
* There is also an optional ui controller class, which assists with providing
|
||||
an administrative UI for managing entities of a certain type.
|
||||
|
||||
* For more details check out the documentation in the drupal.org handbook
|
||||
http://drupal.org/node/878804 as well as the API documentation, i.e.
|
||||
entity_crud_hook_entity_info().
|
||||
|
||||
|
||||
Basic steps to add a new entity type:
|
||||
---------------------------------------
|
||||
|
||||
* You might want to study the code of the "entity_test.module".
|
||||
|
||||
* Describe your entities db table as usual in hook_schema().
|
||||
|
||||
* Just use the "Entity" directly or extend it with your own class.
|
||||
To see how to provide a separate class have a look at the "EntityClass" from
|
||||
the "entity_test.module".
|
||||
|
||||
* Implement hook_entity_info() for your entity. At least specifiy the
|
||||
controller class (EntityAPIController, EntityAPIControllerExportable or your
|
||||
own), your db table and your entity's keys.
|
||||
Again just look at "entity_test.module"'s hook_entity_info() for guidance.
|
||||
|
||||
* If you want your entity to be fieldable just set 'fieldable' in
|
||||
hook_entity_info() to TRUE. The field API attachers are then called
|
||||
automatically in the entity CRUD functions.
|
||||
|
||||
* The entity API is able to deal with bundle objects too (e.g. the node type
|
||||
object). For that just specify another entity type for the bundle objects
|
||||
and set the 'bundle of' property for it.
|
||||
Again just look at "entity_test.module"'s hook_entity_info() for guidance.
|
||||
|
||||
* Schema fields marked as 'serialized' are automatically unserialized upon
|
||||
loading as well as serialized on saving. If the 'merge' attribute is also
|
||||
set to TRUE the unserialized data is automatically "merged" into the entity.
|
||||
|
||||
* Further details can be found at http://drupal.org/node/878804.
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Entity Properties & Entity metadata wrappers
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
* This module introduces a unique place for metadata about entity properties:
|
||||
hook_entity_property_info(), whereas hook_entity_property_info() may be
|
||||
placed in your module's {YOUR_MODULE}.info.inc include file. For details
|
||||
have a look at the API documentation, i.e. hook_entity_property_info() and
|
||||
at http://drupal.org/node/878876.
|
||||
|
||||
* The information about entity properties contains the data type and callbacks
|
||||
for how to get and set the data of the property. That way the data of an
|
||||
entity can be easily re-used, e.g. to export it into other data formats like
|
||||
XML.
|
||||
|
||||
* For making use of this information (metadata) the module provides some
|
||||
wrapper classes which ease getting and setting values. The wrapper supports
|
||||
chained usage for retrieving wrappers of entity properties, e.g. to get a
|
||||
node author's mail address one could use:
|
||||
|
||||
$wrapper = entity_metadata_wrapper('node', $node);
|
||||
$wrapper->author->mail->value();
|
||||
|
||||
To update the user's mail address one could use
|
||||
|
||||
$wrapper->author->mail->set('sepp@example.com');
|
||||
|
||||
or
|
||||
|
||||
$wrapper->author->mail = 'sepp@example.com';
|
||||
|
||||
The wrappers always return the data as described in the property
|
||||
information, which may be retrieved directly via entity_get_property_info()
|
||||
or from the wrapper:
|
||||
|
||||
$mail_info = $wrapper->author->mail->info();
|
||||
|
||||
In order to force getting a textual value sanitized for output one can use,
|
||||
e.g.
|
||||
|
||||
$wrapper->title->value(array('sanitize' => TRUE));
|
||||
|
||||
to get the sanitized node title. When a property is already returned
|
||||
sanitized by default, like the node body, one possibly wants to get the
|
||||
not-sanitized data as it would appear in a browser for other use-cases.
|
||||
To do so one can enable the 'decode' option, which ensures for any sanitized
|
||||
data the tags are stripped and HTML entities are decoded before the property
|
||||
is returned:
|
||||
|
||||
$wrapper->body->value->value(array('decode' => TRUE));
|
||||
|
||||
That way one always gets the data as shown to the user. However if you
|
||||
really want to get the raw, unprocessed value, even for sanitized textual
|
||||
data, you can do so via:
|
||||
|
||||
$wrapper->body->value->raw();
|
133
sites/all/modules/entity/ctools/content_types/entity_view.inc
Normal file
133
sites/all/modules/entity/ctools/content_types/entity_view.inc
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Content type plugin to expose rendered entities, view mode configuration
|
||||
* still available.
|
||||
*/
|
||||
|
||||
$plugin = array(
|
||||
'title' => 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));
|
||||
}
|
456
sites/all/modules/entity/entity.api.php
Normal file
456
sites/all/modules/entity/entity.api.php
Normal file
@@ -0,0 +1,456 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the entity API.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provide an entity type via the entity CRUD API.
|
||||
*
|
||||
* This is a placeholder for describing further keys for hook_entity_info(),
|
||||
* which are introduced the entity API for providing a new entity type with the
|
||||
* entity CRUD API. For that the entity API provides two controllers:
|
||||
* - EntityAPIController: A regular CRUD controller.
|
||||
* - EntityAPIControllerExportable: Extends the regular controller to
|
||||
* additionally support exportable entities and/or entities making use of a
|
||||
* name key.
|
||||
* See entity_metadata_hook_entity_info() for the documentation of additional
|
||||
* keys for hook_entity_info() as introduced by the entity API and supported for
|
||||
* any entity type.
|
||||
*
|
||||
* The entity CRUD API supports the following keys:
|
||||
* - entity class: (optional) A class the controller will use for instantiating
|
||||
* entities. It is suggested to make use of the provided "Entity" class or to
|
||||
* extend it.
|
||||
* - bundle of: (optional) Entity types can be used as bundles for
|
||||
* other entity types. To enable this functionality, use the 'bundle of' key
|
||||
* to indicate which entity type this entity serves as a bundle for. But note
|
||||
* that the other entity type will still need to declare entities of this
|
||||
* type as bundles, as described by the documentation of hook_entity_info().
|
||||
* If the other entity type is fieldable, the entity API controller takes
|
||||
* care of invoking the field API bundle attachers. Note that
|
||||
* field_attach_delete_bundle() has to be invoked manually upon module
|
||||
* uninstallation. See entity_test_entity_info() and entity_test_uninstall()
|
||||
* for examples.
|
||||
* - module: (optional) The module providing the entity type. This is optional,
|
||||
* but strongly suggested.
|
||||
* - exportable: (optional) Whether the entity is exportable. Defaults to FALSE.
|
||||
* If enabled, a name key should be specified and db columns for the module
|
||||
* and status key as defined by entity_exportable_schema_fields() have to
|
||||
* exist in the entity's base table. Also see 'entity keys' below.
|
||||
* This option requires the EntityAPIControllerExportable to work.
|
||||
* - entity keys: An array of keys as defined by Drupal core. The following
|
||||
* additional keys are used by the entity CRUD API:
|
||||
* - name: (optional) The key of the entity property containing the unique,
|
||||
* machine readable name of the entity. If specified, this is used as
|
||||
* identifier of the entity, while the usual 'id' key is still required and
|
||||
* may be used when modules deal with entities generically, or to refer to
|
||||
* the entity internally, i.e. in the database.
|
||||
* If a name key is given, the name is used as entity identifier by the
|
||||
* entity API module, metadata wrappers and entity-type specific hooks.
|
||||
* However note that for consistency all generic entity hooks like
|
||||
* hook_entity_load() are invoked with the entities keyed by numeric id,
|
||||
* while entity-type specific hooks like hook_{entity_type}_load() are
|
||||
* invoked with the entities keyed by name.
|
||||
* Also, just as entity_load_single() entity_load() may be called
|
||||
* with names passed as the $ids parameter, while the results of
|
||||
* entity_load() are always keyed by numeric id. Thus, it is suggested to
|
||||
* make use of entity_load_multiple_by_name() to implement entity-type
|
||||
* specific loading functions like {entity_type}_load_multiple(), as this
|
||||
* function returns the entities keyed by name. See entity_test_get_types()
|
||||
* for an example.
|
||||
* For exportable entities, it is strongly recommended to make use of a
|
||||
* machine name as names are portable across systems.
|
||||
* This option requires the EntityAPIControllerExportable to work.
|
||||
* - module: (optional) A key for the module property used by the entity CRUD
|
||||
* API to save the source module name for exportable entities that have been
|
||||
* provided in code. Defaults to 'module'.
|
||||
* - status: (optional) The name of the entity property used by the entity
|
||||
* CRUD API to save the exportable entity status using defined bit flags.
|
||||
* Defaults to 'status'. See entity_has_status().
|
||||
* - default revision: (optional) The name of the entity property used by
|
||||
* the entity CRUD API to determine if a newly-created revision should be
|
||||
* set as the default revision. Defaults to 'default_revision'.
|
||||
* Note that on entity insert the created revision will be always default
|
||||
* regardless of the value of this entity property.
|
||||
* - export: (optional) An array of information used for exporting. For ctools
|
||||
* exportables compatibility any export-keys supported by ctools may be added
|
||||
* to this array too.
|
||||
* - default hook: What hook to invoke to find exportable entities that are
|
||||
* currently defined. This hook is automatically called by the CRUD
|
||||
* controller during entity_load(). Defaults to 'default_' . $entity_type.
|
||||
* - admin ui: (optional) An array of optional information used for providing an
|
||||
* administrative user interface. To enable the UI at least the path must be
|
||||
* given. Apart from that, the 'access callback' (see below) is required for
|
||||
* the entity, as well as the 'ENTITY_TYPE_form' for editing, adding and
|
||||
* cloning. The form gets the entity and the operation ('edit', 'add' or
|
||||
* 'clone') passed. See entity_ui_get_form() for more details.
|
||||
* Known keys are:
|
||||
* - path: A path where the UI should show up as expected by hook_menu().
|
||||
* - controller class: (optional) A controller class name for providing the
|
||||
* UI. Defaults to EntityDefaultUIController, which implements an admin UI
|
||||
* suiting for managing configuration entities.
|
||||
* For customizing the UI inherit from the default class and overide methods
|
||||
* as suiting and specify your class as controller class.
|
||||
* - file: (optional) The name of the file in which the entity form resides
|
||||
* as it is required by hook_menu().
|
||||
* - file path: (optional) The path to the file as required by hook_menu. If
|
||||
* not set, it defaults to entity type's module's path, thus the entity
|
||||
* types 'module' key is required.
|
||||
* - menu wildcard: The wildcard to use in paths of the hook_menu() items.
|
||||
* Defaults to %entity_object which is the loader provided by Entity API.
|
||||
* - rules controller class: (optional) A controller class for providing Rules
|
||||
* integration, or FALSE to disable this feature. The given class has to
|
||||
* inherit from the class EntityDefaultRulesController, which serves as
|
||||
* default in case the entity type is not marked as configuration. For
|
||||
* configuration entities it defaults to FALSE.
|
||||
* - metadata controller class: (optional) A controller class for providing
|
||||
* entity property info. By default some info is generated out of the
|
||||
* information provided in your hook_schema() implementation, while only read
|
||||
* access is granted to that properties by default. Based upon that the
|
||||
* Entity tokens module also generates token replacements for your entity
|
||||
* type, once activated.
|
||||
* Override the controller class to adapt the defaults and to improve and
|
||||
* complete the generated metadata. Set it to FALSE to disable this feature.
|
||||
* Defaults to the EntityDefaultMetadataController class.
|
||||
* - features controller class: (optional) A controller class for providing
|
||||
* Features module integration for exportable entities. The given class has to
|
||||
* inherit from the default class being EntityDefaultFeaturesController. Set
|
||||
* it to FALSE to disable this feature.
|
||||
* - i18n controller class: (optional) A controller class for providing
|
||||
* i18n module integration for (exportable) entities. The given class has to
|
||||
* inherit from the class EntityDefaultI18nStringController. Defaults to
|
||||
* FALSE (disabled). See EntityDefaultI18nStringController for more
|
||||
* information.
|
||||
* - views controller class: (optional) A controller class for providing views
|
||||
* integration. The given class has to inherit from the class
|
||||
* EntityDefaultViewsController, which is set as default in case the providing
|
||||
* module has been specified (see 'module') and the module does not provide
|
||||
* any views integration. Else it defaults to FALSE, which disables this
|
||||
* feature. See EntityDefaultViewsController.
|
||||
* - 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.
|
||||
* This is optional, but suggested for the Rules integration, and required for
|
||||
* the admin ui (see above).
|
||||
* - form callback: (optional) Specfiy a callback that returns a fully built
|
||||
* edit form for your entity type. See entity_form().
|
||||
* In case the 'admin ui' is used, no callback needs to be specified.
|
||||
* - entity cache: (optional) Whether entities should be cached using the cache
|
||||
* system. Requires the entitycache module to be installed and enabled. As
|
||||
* cached entities are only retrieved by id key, the cache would not apply to
|
||||
* exportable entities retrieved by name key. If enabled, 'field cache' is
|
||||
* obsolete and should be disabled. Defaults to FALSE.
|
||||
*
|
||||
* @see hook_entity_info()
|
||||
* @see entity_metadata_hook_entity_info()
|
||||
*/
|
||||
function entity_crud_hook_entity_info() {
|
||||
$return = array(
|
||||
'entity_test' => 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<TYPE> 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 indiciating 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_metadata_get_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".
|
||||
*/
|
174
sites/all/modules/entity/entity.features.inc
Normal file
174
sites/all/modules/entity/entity.features.inc
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides Features integration for entity types using the CRUD API.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the configured entity features controller.
|
||||
*/
|
||||
function entity_features_get_controller($type) {
|
||||
$static = &drupal_static(__FUNCTION__);
|
||||
if (!isset($static[$type])) {
|
||||
$info = entity_get_info($type);
|
||||
$info += array('features controller class' => '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']}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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)) {
|
||||
foreach ($defaults as $name => $entity) {
|
||||
entity_delete($this->type, $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Features component callback.
|
||||
*/
|
||||
function entity_features_export_options($entity_type) {
|
||||
return entity_features_get_controller($entity_type)->export_options();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Features component callback.
|
||||
*/
|
||||
function entity_features_revert($module = NULL, $entity_type) {
|
||||
return entity_features_get_controller($entity_type)->revert($module);
|
||||
}
|
210
sites/all/modules/entity/entity.i18n.inc
Normal file
210
sites/all/modules/entity/entity.i18n.inc
Normal file
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Internationalization (i18n) integration.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the i18n controller for a given entity type.
|
||||
*
|
||||
* @return EntityDefaultI18nStringController|array|false
|
||||
* If a type is given, the controller for the given entity type. Else an array
|
||||
* of all enabled controllers keyed by entity type is returned.
|
||||
*/
|
||||
function entity_i18n_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_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);
|
||||
}
|
||||
}
|
33
sites/all/modules/entity/entity.info
Normal file
33
sites/all/modules/entity/entity.info
Normal file
@@ -0,0 +1,33 @@
|
||||
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
|
||||
; Information added by drupal.org packaging script on 2012-12-25
|
||||
version = "7.x-1.0"
|
||||
core = "7.x"
|
||||
project = "entity"
|
||||
datestamp = "1356471145"
|
||||
|
181
sites/all/modules/entity/entity.info.inc
Normal file
181
sites/all/modules/entity/entity.info.inc
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides basic entity property info for entities provided via the CRUD API,
|
||||
* as well as property info for all entity types defined by core. For that
|
||||
* the respective modules/MODULE.info.inc files are included.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info().
|
||||
*/
|
||||
function entity_entity_property_info() {
|
||||
$items = array();
|
||||
// Add in info about entities provided by the CRUD API.
|
||||
foreach (entity_crud_get_info() as $type => $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':
|
||||
return 'integer';
|
||||
case 'float':
|
||||
case 'numeric':
|
||||
return 'decimal';
|
||||
case 'char':
|
||||
case 'varchar':
|
||||
case 'text':
|
||||
return 'text';
|
||||
}
|
||||
}
|
28
sites/all/modules/entity/entity.install
Normal file
28
sites/all/modules/entity/entity.install
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install file for the entity API.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The entity API modules have been merged into a single module.
|
||||
*/
|
||||
function entity_update_7000() {
|
||||
// This empty update is required such that all caches are cleared as
|
||||
// necessary.
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the deprecated 'entity_defaults_built' variable.
|
||||
*/
|
||||
function entity_update_7001() {
|
||||
variable_del('entity_defaults_built');
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear caches and rebuild registry.
|
||||
*/
|
||||
function entity_update_7002() {
|
||||
// Do nothing, update.php clears cache for us in case there is an update.
|
||||
}
|
1422
sites/all/modules/entity/entity.module
Normal file
1422
sites/all/modules/entity/entity.module
Normal file
File diff suppressed because it is too large
Load Diff
118
sites/all/modules/entity/entity.rules.inc
Normal file
118
sites/all/modules/entity/entity.rules.inc
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides Rules integration for entities provided via the CRUD API.
|
||||
*
|
||||
* Rules automatically provides us with actions for CRUD and a suiting entity
|
||||
* data type. For events the controller automatically invokes Rules events once
|
||||
* Rules is active, so we just have to provide the appropriate info.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default controller for generating Rules integration.
|
||||
*/
|
||||
class EntityDefaultRulesController {
|
||||
|
||||
protected $type, $info;
|
||||
|
||||
public function __construct($type) {
|
||||
$this->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;
|
||||
}
|
1621
sites/all/modules/entity/entity.test
Normal file
1621
sites/all/modules/entity/entity.test
Normal file
File diff suppressed because it is too large
Load Diff
48
sites/all/modules/entity/entity.tpl.php
Normal file
48
sites/all/modules/entity/entity.tpl.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for entities.
|
||||
*
|
||||
* Available variables:
|
||||
* - $content: An array of comment items. Use render($content) to print them all, or
|
||||
* print a subset such as render($content['field_example']). Use
|
||||
* hide($content['field_example']) to temporarily suppress the printing of a
|
||||
* given element.
|
||||
* - $title: The (sanitized) entity label.
|
||||
* - $url: Direct url of the current entity if specified.
|
||||
* - $page: Flag for the full page state.
|
||||
* - $classes: String of classes that can be used to style contextually through
|
||||
* CSS. It can be manipulated through the variable $classes_array from
|
||||
* preprocess functions. By default the following classes are available, where
|
||||
* the parts enclosed by {} are replaced by the appropriate values:
|
||||
* - entity-{ENTITY_TYPE}
|
||||
* - {ENTITY_TYPE}-{BUNDLE}
|
||||
*
|
||||
* Other variables:
|
||||
* - $classes_array: Array of html class attribute values. It is flattened
|
||||
* into a string within the variable $classes.
|
||||
*
|
||||
* @see template_preprocess()
|
||||
* @see template_preprocess_entity()
|
||||
* @see template_process()
|
||||
*/
|
||||
?>
|
||||
<div class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
|
||||
|
||||
<?php if (!$page): ?>
|
||||
<h2<?php print $title_attributes; ?>>
|
||||
<?php if ($url): ?>
|
||||
<a href="<?php print $url; ?>"><?php print $title; ?></a>
|
||||
<?php else: ?>
|
||||
<?php print $title; ?>
|
||||
<?php endif; ?>
|
||||
</h2>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="content"<?php print $content_attributes; ?>>
|
||||
<?php
|
||||
print render($content);
|
||||
?>
|
||||
</div>
|
||||
</div>
|
13
sites/all/modules/entity/entity_token.info
Normal file
13
sites/all/modules/entity/entity_token.info
Normal file
@@ -0,0 +1,13 @@
|
||||
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
|
||||
dependencies[] = entity
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-25
|
||||
version = "7.x-1.0"
|
||||
core = "7.x"
|
||||
project = "entity"
|
||||
datestamp = "1356471145"
|
||||
|
6
sites/all/modules/entity/entity_token.module
Normal file
6
sites/all/modules/entity/entity_token.module
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Module file for the entity tokens module. Drupal needs this file.
|
||||
*/
|
341
sites/all/modules/entity/entity_token.tokens.inc
Normal file
341
sites/all/modules/entity/entity_token.tokens.inc
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides tokens for entity properties which have no token yet.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines the types of properties to be added as token.
|
||||
*
|
||||
* @return
|
||||
* An array mapping token types to the usual (entity) type names.
|
||||
*/
|
||||
function entity_token_types() {
|
||||
$return = entity_token_types_chained();
|
||||
return $return + drupal_map_assoc(array('text', 'integer', 'decimal', 'duration', 'boolean', 'uri'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a list of token types that need to be chained.
|
||||
*
|
||||
* @return
|
||||
* If a (token) type is given, whether the given type needs to be chained.
|
||||
* Else a full list of token types to be chained as returned by
|
||||
* entity_token_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());
|
||||
}
|
||||
$types = &$drupal_static_fast['types'];
|
||||
|
||||
if (!$types) {
|
||||
// Add entities.
|
||||
foreach (entity_get_info() as $entity_type => $info) {
|
||||
if ($token_type = isset($info['token type']) ? $info['token type'] : $entity_type) {
|
||||
$types[$token_type] = $entity_type;
|
||||
}
|
||||
}
|
||||
// Add 'date' and 'site' tokens.
|
||||
$types['date'] = 'date';
|
||||
$types['site'] = 'site';
|
||||
// Add a 'struct' type.
|
||||
$types['struct'] = 'struct';
|
||||
}
|
||||
|
||||
if (isset($type)) {
|
||||
return isset($types[$type]) || entity_property_list_extract_type($type);
|
||||
}
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the right token type for a given property info array.
|
||||
*/
|
||||
function _entity_token_map_to_token_type($property_info) {
|
||||
$lookup = &drupal_static(__FUNCTION__);
|
||||
|
||||
if (!$lookup) {
|
||||
// Initialize a lookup array mapping property types to token types.
|
||||
$lookup = array_flip(entity_token_types());
|
||||
}
|
||||
|
||||
$type = isset($property_info['type']) ? $property_info['type'] : 'text';
|
||||
// Just use the type 'struct' for all structures.
|
||||
if (!empty($property_info['property info'])) {
|
||||
$type = 'struct';
|
||||
}
|
||||
|
||||
if ($item_type = entity_property_list_extract_type($type)) {
|
||||
return isset($lookup[$item_type]) ? "list<$lookup[$item_type]>" : FALSE;
|
||||
}
|
||||
return isset($lookup[$type]) ? $lookup[$type] : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_token_info_alter().
|
||||
*/
|
||||
function entity_token_token_info_alter(&$info) {
|
||||
$entity_info = entity_get_info();
|
||||
$token_types = entity_token_types_chained();
|
||||
|
||||
// Loop over all chain-able token types, as those may contain further tokens,
|
||||
// e.g. entity types or 'site'.
|
||||
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) {
|
||||
$name = str_replace('_', '-', $name);
|
||||
$property += array('type' => 'text', 'description' => $property['label']);
|
||||
$property_token_type = _entity_token_map_to_token_type($property);
|
||||
|
||||
if (!isset($info['tokens'][$token_type][$name]) && $property_token_type) {
|
||||
|
||||
$info['tokens'][$token_type][$name] = array(
|
||||
'name' => $property['label'],
|
||||
'description' => $property['description'],
|
||||
'type' => $property_token_type,
|
||||
// Mark the token so we know we have to provide the value afterwards.
|
||||
'entity-token' => TRUE,
|
||||
);
|
||||
}
|
||||
if ($property_token_type == 'struct' && !empty($property['property info'])) {
|
||||
$info['tokens'][$token_type][$name]['dynamic'] = TRUE;
|
||||
$help = array();
|
||||
foreach ($property['property info'] as $key => $property_info) {
|
||||
$help[] = $key . ' (' . $property_info['label'] . ')';
|
||||
}
|
||||
$info['tokens'][$token_type][$name]['description'] .= ' ' . t('The following properties may be appended to the token: @keys',
|
||||
array('@keys' => implode(', ', $help))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all chain-able token types we support are registered.
|
||||
foreach ($token_types as $token_type => $type) {
|
||||
|
||||
if (!empty($info['tokens'][$token_type]) && !isset($info['types'][$token_type])) {
|
||||
if (isset($entity_info[$type])) {
|
||||
$info['types'][$token_type] = array(
|
||||
'name' => $entity_info[$type]['label'],
|
||||
'description' => t('Tokens related to the "@name" entities.', array('@name' => $entity_info[$type]['label'])),
|
||||
'needs-data' => $token_type,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$info['types'][$token_type] = array(
|
||||
'name' => drupal_strtoupper($token_type),
|
||||
'description' => t('@name tokens.', array('@name' => drupal_strtoupper($token_type))),
|
||||
'needs-data' => $token_type,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!empty($info['tokens'][$token_type]) && !isset($info['types']["list<$token_type>"]) && $token_type != 'site') {
|
||||
if (isset($entity_info[$type])) {
|
||||
$info['types']["list<$token_type>"] = array(
|
||||
'name' => t('List of @entities', array('@entities' => isset($entity_info[$type]['plural label']) ? $entity_info[$type]['plural label'] : $entity_info[$type]['label'] . 's')),
|
||||
'description' => t('Tokens related to the "@name" entities.', array('@name' => $entity_info[$type]['label'])),
|
||||
'needs-data' => "list<$token_type>",
|
||||
);
|
||||
}
|
||||
else {
|
||||
$info['types']["list<$token_type>"] = array(
|
||||
'name' => t('List of @type values', array('@type' => $token_type)),
|
||||
'description' => t('Tokens for lists of @type values.', array('@type' => $token_type)),
|
||||
'needs-data' => "list<$token_type>",
|
||||
);
|
||||
}
|
||||
// Also add some basic token replacements for lists...
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$info['tokens']["list<$token_type>"][$i] = array(
|
||||
'name' => t('@type with delta @delta', array('@delta' => $i, '@type' => $info['types'][$token_type]['name'])),
|
||||
'description' => t('The list item with delta @delta. Delta values start from 0 and are incremented by one per list item.', array('@delta' => $i)),
|
||||
'type' => $token_type,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function entity_token_tokens($type, $tokens, array $data = array(), array $options = array()) {
|
||||
$token_types = entity_token_types_chained();
|
||||
$replacements = array();
|
||||
|
||||
if (isset($token_types[$type]) && (!empty($data[$type]) || $type == 'site')) {
|
||||
$data += array($type => FALSE);
|
||||
|
||||
// Make use of token module's token cache if available.
|
||||
$info = module_exists('token') ? token_get_info() : token_info();
|
||||
foreach ($tokens as $name => $original) {
|
||||
// Provide the token for all properties marked to stem from us.
|
||||
if (!empty($info['tokens'][$type][$name]['entity-token']) || $type == 'struct') {
|
||||
$wrapper = !isset($wrapper) ? _entity_token_wrap_data($type, $token_types[$type], $data[$type], $options) : $wrapper;
|
||||
$property_name = str_replace('-', '_', $name);
|
||||
try {
|
||||
$replacement = _entity_token_get_token($wrapper->$property_name, $options);
|
||||
if (isset($replacement)) {
|
||||
$replacements[$original] = $replacement;
|
||||
}
|
||||
}
|
||||
catch (EntityMetadataWrapperException $e) {
|
||||
// If tokens for not existing values are requested, just do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Properly chain everything of a type marked as needs chaining.
|
||||
$info['tokens'] += array($type => array());
|
||||
foreach ($info['tokens'][$type] as $name => $token_info) {
|
||||
if (!empty($token_info['entity-token']) && isset($token_info['type']) && entity_token_types_chained($token_info['type'])) {
|
||||
|
||||
if ($chained_tokens = token_find_with_prefix($tokens, $name)) {
|
||||
$wrapper = !isset($wrapper) ? _entity_token_wrap_data($type, $token_types[$type], $data[$type], $options) : $wrapper;
|
||||
$property_name = str_replace('-', '_', $name);
|
||||
|
||||
try {
|
||||
// Pass on 'struct' properties wrapped, else un-wrap the data.
|
||||
$value = ($token_info['type'] == 'struct') ? $wrapper->$property_name : $wrapper->$property_name->value();
|
||||
$replacements += token_generate($token_info['type'], $chained_tokens, array($token_info['type'] => $value), $options);
|
||||
}
|
||||
catch (EntityMetadataWrapperException $e) {
|
||||
// If tokens for not existing values are requested, just do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add support for evaluating tokens for "list<type"> types.
|
||||
elseif ($item_token_type = entity_property_list_extract_type($type)) {
|
||||
foreach ($tokens as $name => $original) {
|
||||
// Care about getting entries of a list.
|
||||
if (is_numeric($name)) {
|
||||
$wrapper = !isset($wrapper) ? _entity_token_wrap_data($type, "list<$token_types[$item_token_type]>", $data[$type], $options) : $wrapper;
|
||||
try {
|
||||
$replacement = _entity_token_get_token($wrapper->get($name), $options);
|
||||
if (isset($replacement)) {
|
||||
$replacements[$original] = $replacement;
|
||||
}
|
||||
}
|
||||
catch (EntityMetadataWrapperException $e) {
|
||||
// If tokens for not existing values are requested, just do nothing.
|
||||
}
|
||||
}
|
||||
// Care about generating chained tokens for list-items.
|
||||
else {
|
||||
$parts = explode(':', $name, 2);
|
||||
$delta = $parts[0];
|
||||
|
||||
if (is_numeric($delta) && $chained_tokens = token_find_with_prefix($tokens, $delta)) {
|
||||
$wrapper = !isset($wrapper) ? _entity_token_wrap_data($type, "list<$token_types[$item_token_type]>", $data[$type], $options) : $wrapper;
|
||||
try {
|
||||
$replacements += token_generate($item_token_type, $chained_tokens, array($item_token_type => $wrapper->get($delta)->value()), $options);
|
||||
}
|
||||
catch (EntityMetadataWrapperException $e) {
|
||||
// If tokens for not existing values are requested, just do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add support for chaining struct data. As struct data has no registered
|
||||
// tokens, we have to chain based upon wrapper property info.
|
||||
if ($type == 'struct') {
|
||||
$wrapper = $data[$type];
|
||||
foreach ($wrapper as $name => $property) {
|
||||
$token_type = _entity_token_map_to_token_type($property->info());
|
||||
|
||||
if (entity_token_types_chained($token_type) && $chained_tokens = token_find_with_prefix($tokens, $name)) {
|
||||
try {
|
||||
// Pass on 'struct' properties wrapped, else un-wrap the data.
|
||||
$value = ($token_type == 'struct') ? $property : $property->value();
|
||||
$replacements += token_generate($token_type, $chained_tokens, array($token_type => $value), $options);
|
||||
}
|
||||
catch (EntityMetadataWrapperException $e) {
|
||||
// If tokens for not existing values are requested, just do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the given data by correctly obeying the options.
|
||||
*/
|
||||
function _entity_token_wrap_data($token_type, $type, $data, $options) {
|
||||
if ($type == 'site') {
|
||||
$wrapper = entity_metadata_site_wrapper();
|
||||
}
|
||||
elseif ($type == 'struct') {
|
||||
// 'struct' data items are passed on wrapped.
|
||||
$wrapper = $data;
|
||||
}
|
||||
else {
|
||||
$wrapper = entity_metadata_wrapper($type, $data);
|
||||
}
|
||||
if (isset($options['language']) && $wrapper instanceof EntityStructureWrapper) {
|
||||
$wrapper->language($options['language']->language);
|
||||
}
|
||||
return $wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token replacement by correctly obeying the options.
|
||||
*/
|
||||
function _entity_token_get_token($wrapper, $options) {
|
||||
|
||||
if ($wrapper->value() === NULL) {
|
||||
// Do not provide a replacement if there is no value.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (empty($options['sanitize'])) {
|
||||
// When we don't need sanitized tokens decode already sanitizied texts.
|
||||
$options['decode'] = TRUE;
|
||||
}
|
||||
$langcode = isset($options['language']) ? $options['language']->language : NULL;
|
||||
|
||||
// If there is a label for a property, e.g. defined by an options list or an
|
||||
// entity label, make use of it.
|
||||
if ($label = $wrapper->label()) {
|
||||
return empty($options['sanitize']) ? $label : check_plain($label);
|
||||
}
|
||||
|
||||
switch ($wrapper->type()) {
|
||||
case 'integer':
|
||||
return $wrapper->value();
|
||||
case 'decimal':
|
||||
return number_format($wrapper->value(), 2);
|
||||
case 'date':
|
||||
return format_date($wrapper->value(), 'medium', '', NULL, $langcode);
|
||||
case 'duration':
|
||||
return format_interval($wrapper->value(), 2, $langcode);
|
||||
case 'boolean':
|
||||
return $wrapper->value() ? t('true') : t('false');
|
||||
case 'uri':
|
||||
case 'text':
|
||||
return $wrapper->value($options);
|
||||
}
|
||||
|
||||
// Care for outputing list values.
|
||||
if ($wrapper instanceof EntityListWrapper) {
|
||||
$output = array();
|
||||
foreach ($wrapper as $item) {
|
||||
$output[] = _entity_token_get_token($item, $options);
|
||||
}
|
||||
return implode(', ', $output);
|
||||
}
|
||||
// Else we do not have a good string to output, e.g. for struct values. Just
|
||||
// output the string representation of the wrapper.
|
||||
return (string) $wrapper;
|
||||
}
|
944
sites/all/modules/entity/includes/entity.controller.inc
Normal file
944
sites/all/modules/entity/includes/entity.controller.inc
Normal file
@@ -0,0 +1,944 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides a controller building upon the core controller but providing more
|
||||
* features like full CRUD functionality.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for EntityControllers compatible with the entity API.
|
||||
*/
|
||||
interface EntityAPIControllerInterface extends DrupalEntityControllerInterface {
|
||||
|
||||
/**
|
||||
* Delete permanently saved entities.
|
||||
*
|
||||
* In case of failures, an exception is thrown.
|
||||
*
|
||||
* @param $ids
|
||||
* An array of entity IDs.
|
||||
*/
|
||||
public function delete($ids);
|
||||
|
||||
/**
|
||||
* Invokes a hook on behalf of the entity. For hooks that have a respective
|
||||
* field API attacher like insert/update/.. the attacher is called too.
|
||||
*/
|
||||
public function invoke($hook, $entity);
|
||||
|
||||
/**
|
||||
* Permanently saves the given entity.
|
||||
*
|
||||
* In case of failures, an exception is thrown.
|
||||
*
|
||||
* @param $entity
|
||||
* The entity to save.
|
||||
*
|
||||
* @return
|
||||
* SAVED_NEW or SAVED_UPDATED is returned depending on the operation
|
||||
* performed.
|
||||
*/
|
||||
public function save($entity);
|
||||
|
||||
/**
|
||||
* Create a new entity.
|
||||
*
|
||||
* @param array $values
|
||||
* An array of values to set, keyed by property name.
|
||||
* @return
|
||||
* A new instance of the entity type.
|
||||
*/
|
||||
public function create(array $values = array());
|
||||
|
||||
/**
|
||||
* Exports an entity as serialized string.
|
||||
*
|
||||
* @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 controller and has to be compatible with the format that is accepted
|
||||
* by the import() method.
|
||||
*/
|
||||
public function export($entity, $prefix = '');
|
||||
|
||||
/**
|
||||
* Imports an entity from a string.
|
||||
*
|
||||
* @param string $export
|
||||
* An exported entity as serialized string.
|
||||
*
|
||||
* @return
|
||||
* An entity object not yet saved.
|
||||
*/
|
||||
public function import($export);
|
||||
|
||||
/**
|
||||
* Builds a structured array representing the entity's content.
|
||||
*
|
||||
* The content built for the entity will vary depending on the $view_mode
|
||||
* parameter.
|
||||
*
|
||||
* @param $entity
|
||||
* An entity object.
|
||||
* @param $view_mode
|
||||
* View mode, 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.
|
||||
*/
|
||||
public function buildContent($entity, $view_mode = 'full', $langcode = NULL);
|
||||
|
||||
/**
|
||||
* Generate an array for rendering the given entities.
|
||||
*
|
||||
* @param $entities
|
||||
* An array of entities to render.
|
||||
* @param $view_mode
|
||||
* View mode, 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 embeded
|
||||
* 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 entity name or numeric id.
|
||||
*/
|
||||
public function view($entities, $view_mode = 'full', $langcode = NULL, $page = NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for EntityControllers of entities that support revisions.
|
||||
*/
|
||||
interface EntityAPIControllerRevisionableInterface extends EntityAPIControllerInterface {
|
||||
|
||||
/**
|
||||
* Delete an entity revision.
|
||||
*
|
||||
* Note that the default revision of an entity cannot be deleted.
|
||||
*
|
||||
* @param $revision_id
|
||||
* The ID of the revision to delete.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the entity revision could be deleted, FALSE otherwise.
|
||||
*/
|
||||
public function deleteRevision($revision_id);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A controller implementing EntityAPIControllerInterface for the database.
|
||||
*/
|
||||
class EntityAPIController extends DrupalDefaultEntityController implements EntityAPIControllerRevisionableInterface {
|
||||
|
||||
protected $cacheComplete = FALSE;
|
||||
protected $bundleKey;
|
||||
protected $defaultRevisionKey;
|
||||
|
||||
/**
|
||||
* Overridden.
|
||||
* @see DrupalDefaultEntityController#__construct()
|
||||
*/
|
||||
public function __construct($entityType) {
|
||||
parent::__construct($entityType);
|
||||
// If this is the bundle of another entity, set the bundle key.
|
||||
if (isset($this->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('base.' . $this->revisionKey . ' = revision.' . $this->revisionKey, $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;
|
||||
}
|
||||
// This transaction causes troubles on MySQL, see
|
||||
// http://drupal.org/node/1007830. So we deactivate this by default until
|
||||
// is shipped in a point release.
|
||||
// $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) {
|
||||
if (isset($transaction)) {
|
||||
$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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
310
sites/all/modules/entity/includes/entity.inc
Normal file
310
sites/all/modules/entity/includes/entity.inc
Normal file
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides a base class for entities.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A common class for entities.
|
||||
*
|
||||
* It's suggested, but not required, to extend this class and to override
|
||||
* __construct() in order to specify a fixed entity type.
|
||||
*
|
||||
* For providing an entity label and URI it is suggested to override the
|
||||
* defaultLabel() and defaultUri() methods, and to specify the
|
||||
* entity_class_label() and entity_class_uri() as respective callbacks in
|
||||
* hook_entity_info(). That way modules are able to override your defaults
|
||||
* by altering the hook_entity_info() callbacks, while $entity->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 {
|
||||
|
||||
protected $entityType;
|
||||
protected $entityInfo;
|
||||
protected $idKey, $nameKey, $statusKey;
|
||||
|
||||
/**
|
||||
* Creates a new entity.
|
||||
*
|
||||
* @see entity_create()
|
||||
*/
|
||||
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($info['entity keys']['status']) ? 'status' : $info['entity keys']['status'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal, numeric identifier.
|
||||
*
|
||||
* Returns the numeric identifier, even if the entity type has specified a
|
||||
* name key. In the latter case, the numeric identifier is supposed to be used
|
||||
* when dealing generically with entities or internally to refer to an entity,
|
||||
* i.e. in a relational database. If unsure, use Entity:identifier().
|
||||
*/
|
||||
public function internalIdentifier() {
|
||||
return isset($this->{$this->idKey}) ? $this->{$this->idKey} : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity identifier, i.e. the entities name or numeric id.
|
||||
*
|
||||
* @return
|
||||
* The identifier of the entity. If the entity type makes use of a name key,
|
||||
* the name is returned, else the numeric id.
|
||||
*
|
||||
* @see entity_id()
|
||||
*/
|
||||
public function identifier() {
|
||||
return isset($this->{$this->nameKey}) ? $this->{$this->nameKey} : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the info of the type of the entity.
|
||||
*
|
||||
* @see entity_get_info()
|
||||
*/
|
||||
public function entityInfo() {
|
||||
return $this->entityInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the entity.
|
||||
*/
|
||||
public function entityType() {
|
||||
return $this->entityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bundle of the entity.
|
||||
*
|
||||
* @return
|
||||
* The bundle of the entity. Defaults to the entity type if the entity type
|
||||
* does not make use of different bundles.
|
||||
*/
|
||||
public function bundle() {
|
||||
return !empty($this->entityInfo['entity keys']['bundle']) ? $this->{$this->entityInfo['entity keys']['bundle']} : $this->entityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label of the entity.
|
||||
*
|
||||
* Modules may alter the label by specifying another 'label callback' using
|
||||
* hook_entity_info_alter().
|
||||
*
|
||||
* @see entity_label()
|
||||
*/
|
||||
public function label() {
|
||||
if (isset($this->entityInfo['label callback']) && $this->entityInfo['label callback'] == 'entity_class_label') {
|
||||
return $this->defaultLabel();
|
||||
}
|
||||
return entity_label($this->entityType, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uri of the entity just as entity_uri().
|
||||
*
|
||||
* Modules may alter the uri by specifying another 'uri callback' using
|
||||
* hook_entity_info_alter().
|
||||
*
|
||||
* @see entity_uri()
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entity has a certain exportable status.
|
||||
*
|
||||
* @param $status
|
||||
* A status constant, i.e. one of ENTITY_CUSTOM, ENTITY_IN_CODE,
|
||||
* ENTITY_OVERRIDDEN or ENTITY_FIXED.
|
||||
*
|
||||
* @return
|
||||
* For exportable entities TRUE if the entity has the status, else FALSE.
|
||||
* In case the entity is not exportable, NULL is returned.
|
||||
*
|
||||
* @see entity_has_status()
|
||||
*/
|
||||
public function hasStatus($status) {
|
||||
if (!empty($this->entityInfo['exportable'])) {
|
||||
return isset($this->{$this->statusKey}) && ($this->{$this->statusKey} & $status) == $status;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Permanently saves the entity.
|
||||
*
|
||||
* @see entity_save()
|
||||
*/
|
||||
public function save() {
|
||||
return entity_get_controller($this->entityType)->save($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permanently deletes the entity.
|
||||
*
|
||||
* @see entity_delete()
|
||||
*/
|
||||
public function delete() {
|
||||
$id = $this->identifier();
|
||||
if (isset($id)) {
|
||||
entity_get_controller($this->entityType)->delete(array($id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the entity.
|
||||
*
|
||||
* @see entity_export()
|
||||
*/
|
||||
public function export($prefix = '') {
|
||||
return entity_get_controller($this->entityType)->export($this, $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an array for rendering the entity.
|
||||
*
|
||||
* @see entity_view()
|
||||
*/
|
||||
public function view($view_mode = 'full', $langcode = NULL, $page = NULL) {
|
||||
return entity_get_controller($this->entityType)->view(array($this), $view_mode, $langcode, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a structured array representing the entity's content.
|
||||
*
|
||||
* @see entity_build_content()
|
||||
*/
|
||||
public function buildContent($view_mode = 'full', $langcode = NULL) {
|
||||
return entity_get_controller($this->entityType)->buildContent($this, $view_mode, $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw, translated value of a property or field.
|
||||
*
|
||||
* Supports retrieving field translations as well as i18n string translations.
|
||||
*
|
||||
* Note that this returns raw data values, which might not reflect what
|
||||
* has been declared for hook_entity_property_info() as no 'getter callbacks'
|
||||
* are invoked or no referenced entities are loaded. For retrieving values
|
||||
* reflecting the property info make use of entity metadata wrappers, see
|
||||
* entity_metadata_wrapper().
|
||||
*
|
||||
* @param $property_name
|
||||
* The name of the property to return; e.g., 'title'.
|
||||
* @param $langcode
|
||||
* (optional) The language code of the language to which the value should
|
||||
* be translated. If set to NULL, the default display language is being
|
||||
* used.
|
||||
*
|
||||
* @return
|
||||
* The raw, translated property value; or the raw, un-translated value if no
|
||||
* translation is available.
|
||||
*
|
||||
* @todo Implement an analogous setTranslation() method for updating.
|
||||
*/
|
||||
public function getTranslation($property, $langcode = NULL) {
|
||||
$all_info = entity_get_all_property_info($this->entityType);
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the entity is the default revision.
|
||||
*
|
||||
* @return Boolean
|
||||
*
|
||||
* @see entity_revision_is_default()
|
||||
*/
|
||||
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 {}
|
646
sites/all/modules/entity/includes/entity.property.inc
Normal file
646
sites/all/modules/entity/includes/entity.property.inc
Normal file
@@ -0,0 +1,646 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides API functions around hook_entity_property_info(). Also see
|
||||
* entity.info.inc, which cares for providing entity property info for all core
|
||||
* entity types.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the entity property info array of an entity type.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type, e.g. node, for which the info shall be returned, or NULL
|
||||
* to return an array with info about all types.
|
||||
*
|
||||
* @see hook_entity_property_info()
|
||||
* @see hook_entity_property_info_alter()
|
||||
*/
|
||||
function entity_get_property_info($entity_type = NULL) {
|
||||
// Use the advanced drupal_static() pattern, since this is called very often.
|
||||
static $drupal_static_fast;
|
||||
if (!isset($drupal_static_fast)) {
|
||||
$drupal_static_fast['info'] = &drupal_static(__FUNCTION__);
|
||||
}
|
||||
$info = &$drupal_static_fast['info'];
|
||||
|
||||
// hook_entity_property_info() includes translated strings, so each language
|
||||
// is cached separately.
|
||||
$langcode = $GLOBALS['language']->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 ommitted, 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'])) {
|
||||
return entity_property_verify_data_type($data, 'token');
|
||||
}
|
||||
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<date>.
|
||||
*
|
||||
* @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<list<date>>.
|
||||
*
|
||||
* @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;
|
||||
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',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
672
sites/all/modules/entity/includes/entity.ui.inc
Normal file
672
sites/all/modules/entity/includes/entity.ui.inc
Normal file
@@ -0,0 +1,672 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides a controller for building an entity overview form.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default controller for providing UI.
|
||||
*/
|
||||
class EntityDefaultUIController {
|
||||
|
||||
protected $entityType;
|
||||
protected $entityInfo, $path;
|
||||
|
||||
/**
|
||||
* Defines the number of entries to show per page in overview table.
|
||||
*/
|
||||
public $overviewPagerLimit = 25;
|
||||
|
||||
public function __construct($entity_type, $entity_info) {
|
||||
$this->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();
|
||||
$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, $id_count + 1),
|
||||
'page callback' => 'entity_ui_get_form',
|
||||
'page arguments' => array($this->entityType, $id_count + 1),
|
||||
'load arguments' => array($this->entityType),
|
||||
'access callback' => 'entity_access',
|
||||
'access arguments' => array('update', $this->entityType, $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, $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, $id_count + 1, $id_count + 2),
|
||||
'load arguments' => array($this->entityType),
|
||||
'access callback' => 'entity_access',
|
||||
'access arguments' => array('delete', $this->entityType, $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 only invoked 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];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
// compatability.
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the page title for the passed operation.
|
||||
*/
|
||||
function entity_ui_get_page_title($op, $entity_type, $entity = NULL) {
|
||||
$label = entity_label($entity_type, $entity);
|
||||
switch ($op) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the page/menu title for local action operations.
|
||||
*/
|
||||
function entity_ui_get_action_title($op, $entity_type) {
|
||||
$info = entity_get_info($entity_type);
|
||||
switch ($op) {
|
||||
case 'add':
|
||||
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'])));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 .= ' <small> (' . t('Machine name') . ': ' . check_plain($variables['name']) . ')</small>';
|
||||
}
|
||||
return $output;
|
||||
}
|
1194
sites/all/modules/entity/includes/entity.wrapper.inc
Normal file
1194
sites/all/modules/entity/includes/entity.wrapper.inc
Normal file
File diff suppressed because it is too large
Load Diff
23
sites/all/modules/entity/modules/book.info.inc
Normal file
23
sites/all/modules/entity/modules/book.info.inc
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides info about book nodes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info_alter() on top of book module.
|
||||
*
|
||||
* @see entity_entity_property_info_alter()
|
||||
*/
|
||||
function entity_metadata_book_entity_property_info_alter(&$info) {
|
||||
// Add meta-data about the added node properties.
|
||||
$properties = &$info['node']['properties'];
|
||||
|
||||
$properties['book'] = array(
|
||||
'label' => 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',
|
||||
);
|
||||
}
|
954
sites/all/modules/entity/modules/callbacks.inc
Normal file
954
sites/all/modules/entity/modules/callbacks.inc
Normal file
@@ -0,0 +1,954 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides various callbacks for the whole core module integration.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Callback for getting properties of an entity.
|
||||
*/
|
||||
function entity_metadata_entity_get_properties($entity, array $options, $name, $entity_type) {
|
||||
if ($name == 'url') {
|
||||
$return = entity_uri($entity_type, $entity);
|
||||
return url($return['path'], $return['options'] + $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for getting book node properties.
|
||||
* @see entity_metadata_book_entity_info_alter()
|
||||
*/
|
||||
function entity_metadata_book_get_properties($node, array $options, $name, $entity_type) {
|
||||
if (!isset($node->book['bid'])) {
|
||||
throw new EntityMetadataWrapperException('This node is no book page.');
|
||||
}
|
||||
return $node->book['bid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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('<front>', $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] = $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<boolean>') {
|
||||
// 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) {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
$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;
|
||||
}
|
||||
$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' => $context['field']['settings']['display_default']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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().
|
||||
*/
|
||||
function entity_metadata_no_hook_node_access($op, $node = NULL, $account = NULL) {
|
||||
if (isset($node)) {
|
||||
// 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);
|
||||
}
|
||||
else {
|
||||
return node_access($op, $node, $account);
|
||||
}
|
||||
}
|
||||
// Is access to all nodes allowed?
|
||||
if (!user_access('access content', $account)) {
|
||||
return FALSE;
|
||||
}
|
||||
if (user_access('bypass node access', $account) || (!isset($account) && $op == 'view' && node_access_view_all_nodes())) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback for the user entity.
|
||||
*/
|
||||
function entity_metadata_user_access($op, $entity = NULL, $account = NULL, $entity_type) {
|
||||
$account = isset($account) ? $account : $GLOBALS['user'];
|
||||
// Grant access to the users own user account and to the anonymous one.
|
||||
if (isset($entity) && $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' && $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) && $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) {
|
||||
if (isset($entity) && !isset($account) && comment_access($op, $entity)) {
|
||||
return TRUE;
|
||||
}
|
||||
if (user_access('administer comments', $account) || user_access('access comments', $account) && $op == 'view') {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback for the taxonomy entities.
|
||||
*/
|
||||
function entity_metadata_taxonomy_access($op, $entity = NULL, $account = NULL, $entity_type) {
|
||||
if ($entity_type == 'taxonomy_vocabulary') {
|
||||
return user_access('administer taxonomy', $account);
|
||||
}
|
||||
if (isset($entity) && $op == 'update' && !isset($account) && taxonomy_term_edit_access($entity)) {
|
||||
return TRUE;
|
||||
}
|
||||
if (user_access('administer taxonomy', $account) || user_access('access content', $account) && $op == 'view') {
|
||||
return TRUE;
|
||||
}
|
||||
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) {
|
||||
// Pre-populate the form-state with the right form include.
|
||||
$form_state['build_info']['args'] = array($account);
|
||||
form_load_include($form_state, 'inc', 'user', 'user.pages');
|
||||
return drupal_build_form('user_profile_form', $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($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 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();
|
||||
}
|
164
sites/all/modules/entity/modules/comment.info.inc
Normal file
164
sites/all/modules/entity/modules/comment.info.inc
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides info about the comment entity.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info() on top of comment module.
|
||||
*
|
||||
* @see entity_entity_property_info()
|
||||
*/
|
||||
function entity_metadata_comment_entity_property_info() {
|
||||
$info = array();
|
||||
// Add meta-data about the basic comment properties.
|
||||
$properties = &$info['comment']['properties'];
|
||||
|
||||
$properties['cid'] = array(
|
||||
'label' => 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."),
|
||||
'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',
|
||||
'setter permission' => 'administer comments',
|
||||
'validation callback' => 'valid_email_address',
|
||||
'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',
|
||||
'setter permission' => 'administer comments',
|
||||
'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',
|
||||
'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',
|
||||
'setter permission' => 'administer comments',
|
||||
'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['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']);
|
||||
}
|
170
sites/all/modules/entity/modules/field.info.inc
Normal file
170
sites/all/modules/entity/modules/field.info.inc
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides info for fields.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info() on top of field module.
|
||||
*
|
||||
* @see entity_field_info_alter()
|
||||
* @see entity_entity_property_info()
|
||||
*/
|
||||
function entity_metadata_field_entity_property_info() {
|
||||
$info = array();
|
||||
// Loop over all field instances and add them as property.
|
||||
foreach (field_info_fields() as $field_name => $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(
|
||||
'label' => $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>' : '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($field['settings']['title_field'])) {
|
||||
unset($property['property info']['title']);
|
||||
}
|
||||
}
|
40
sites/all/modules/entity/modules/locale.info.inc
Normal file
40
sites/all/modules/entity/modules/locale.info.inc
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides locale-related properties.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info_alter() on top of locale module.
|
||||
*
|
||||
* @see entity_entity_property_info_alter()
|
||||
*/
|
||||
function entity_metadata_locale_entity_property_info_alter(&$info) {
|
||||
|
||||
$info['user']['properties']['language'] = array(
|
||||
'label' => 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',
|
||||
);
|
||||
}
|
165
sites/all/modules/entity/modules/node.info.inc
Normal file
165
sites/all/modules/entity/modules/node.info.inc
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides info about the node entity.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info() on top of node module.
|
||||
*
|
||||
* @see entity_entity_property_info()
|
||||
*/
|
||||
function entity_metadata_node_entity_property_info() {
|
||||
$info = array();
|
||||
// Add meta-data about the basic node properties.
|
||||
$properties = &$info['node']['properties'];
|
||||
|
||||
$properties['nid'] = array(
|
||||
'label' => 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."),
|
||||
'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,
|
||||
);
|
||||
}
|
50
sites/all/modules/entity/modules/poll.info.inc
Normal file
50
sites/all/modules/entity/modules/poll.info.inc
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides info about poll nodes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info_alter() on top of poll module.
|
||||
*
|
||||
* @see entity_entity_property_info_alter()
|
||||
*/
|
||||
function entity_metadata_poll_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_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',
|
||||
);
|
||||
}
|
37
sites/all/modules/entity/modules/statistics.info.inc
Normal file
37
sites/all/modules/entity/modules/statistics.info.inc
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides info about statistics.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info_alter() on top of statistics module.
|
||||
*
|
||||
* @see entity_entity_property_info_alter()
|
||||
*/
|
||||
function entity_metadata_statistics_entity_property_info_alter(&$info) {
|
||||
$properties = &$info['node']['properties'];
|
||||
|
||||
$properties['views'] = array(
|
||||
'label' => 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,
|
||||
);
|
||||
$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,
|
||||
);
|
||||
$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,
|
||||
);
|
||||
}
|
132
sites/all/modules/entity/modules/system.info.inc
Normal file
132
sites/all/modules/entity/modules/system.info.inc
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides info about system-wide entities.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info() on top of system module.
|
||||
*
|
||||
* @see entity_entity_property_info()
|
||||
* @see entity_metadata_site_wrapper()
|
||||
*/
|
||||
function entity_metadata_system_entity_property_info() {
|
||||
$info = array();
|
||||
|
||||
// There is no site entity, but still add metadata for global site properties
|
||||
// here. That way modules can alter and add further properties at this place.
|
||||
// In order to make use of this metadata modules may use the wrapper returned
|
||||
// by entity_metadata_site_wrapper().
|
||||
$properties = &$info['site']['properties'];
|
||||
$properties['name'] = array(
|
||||
'label' => 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;
|
||||
}
|
124
sites/all/modules/entity/modules/taxonomy.info.inc
Normal file
124
sites/all/modules/entity/modules/taxonomy.info.inc
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides info about the taxonomy entity.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info() on top of taxonomy module.
|
||||
*
|
||||
* @see entity_entity_property_info()
|
||||
*/
|
||||
function entity_metadata_taxonomy_entity_property_info() {
|
||||
$info = array();
|
||||
// Add meta-data about the basic taxonomy properties.
|
||||
$properties = &$info['taxonomy_term']['properties'];
|
||||
|
||||
$properties['tid'] = array(
|
||||
'label' => 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<taxonomy_term>',
|
||||
);
|
||||
$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<taxonomy_term>',
|
||||
'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;
|
||||
}
|
108
sites/all/modules/entity/modules/user.info.inc
Normal file
108
sites/all/modules/entity/modules/user.info.inc
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides info about the user entity.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info() on top of user module.
|
||||
*
|
||||
* @see entity_entity_property_info()
|
||||
*/
|
||||
function entity_metadata_user_entity_property_info() {
|
||||
$info = array();
|
||||
// Add meta-data about the user properties.
|
||||
$properties = &$info['user']['properties'];
|
||||
|
||||
$properties['uid'] = array(
|
||||
'label' => 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',
|
||||
'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',
|
||||
'schema field' => 'login',
|
||||
);
|
||||
$properties['created'] = array(
|
||||
'label' => t("Created"),
|
||||
'description' => t("The date the user account was created."),
|
||||
'type' => 'date',
|
||||
'schema field' => 'created',
|
||||
);
|
||||
$properties['roles'] = array(
|
||||
'label' => t("User roles"),
|
||||
'description' => t("The roles of the user."),
|
||||
'type' => 'list<integer>',
|
||||
'getter callback' => 'entity_metadata_user_get_properties',
|
||||
'setter callback' => 'entity_metadata_user_set_properties',
|
||||
'setter permission' => 'administer users',
|
||||
'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',
|
||||
'setter permission' => 'administer users',
|
||||
'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;
|
||||
}
|
||||
|
14
sites/all/modules/entity/tests/entity_feature.info
Normal file
14
sites/all/modules/entity/tests/entity_feature.info
Normal file
@@ -0,0 +1,14 @@
|
||||
name = Entity feature module
|
||||
description = Provides some entities in code.
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
files[] = entity_feature.module
|
||||
dependencies[] = entity_test
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-25
|
||||
version = "7.x-1.0"
|
||||
core = "7.x"
|
||||
project = "entity"
|
||||
datestamp = "1356471145"
|
||||
|
32
sites/all/modules/entity/tests/entity_feature.module
Normal file
32
sites/all/modules/entity/tests/entity_feature.module
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test module providing some entities in code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_default_entity_test_type().
|
||||
*/
|
||||
function entity_feature_default_entity_test_type() {
|
||||
$types['main'] = entity_create('entity_test_type', array(
|
||||
'name' => '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;
|
||||
}
|
15
sites/all/modules/entity/tests/entity_test.info
Normal file
15
sites/all/modules/entity/tests/entity_test.info
Normal file
@@ -0,0 +1,15 @@
|
||||
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
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-12-25
|
||||
version = "7.x-1.0"
|
||||
core = "7.x"
|
||||
project = "entity"
|
||||
datestamp = "1356471145"
|
||||
|
164
sites/all/modules/entity/tests/entity_test.install
Normal file
164
sites/all/modules/entity/tests/entity_test.install
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the entity_test module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function entity_test_uninstall() {
|
||||
// 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) {
|
||||
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('users' => 'uid'),
|
||||
'name' => array('entity_test_types' => '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;
|
||||
}
|
||||
|
280
sites/all/modules/entity/tests/entity_test.module
Normal file
280
sites/all/modules/entity/tests/entity_test.module
Normal file
@@ -0,0 +1,280 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test moduel for the entity API.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info().
|
||||
*/
|
||||
function entity_test_entity_info() {
|
||||
$return = array(
|
||||
'entity_test' => 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.
|
||||
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());
|
||||
}
|
13
sites/all/modules/entity/tests/entity_test_i18n.info
Normal file
13
sites/all/modules/entity/tests/entity_test_i18n.info
Normal file
@@ -0,0 +1,13 @@
|
||||
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
|
||||
; Information added by drupal.org packaging script on 2012-12-25
|
||||
version = "7.x-1.0"
|
||||
core = "7.x"
|
||||
project = "entity"
|
||||
datestamp = "1356471145"
|
||||
|
53
sites/all/modules/entity/tests/entity_test_i18n.module
Normal file
53
sites/all/modules/entity/tests/entity_test_i18n.module
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Entity-test i18n integration module via entity API i18n support.
|
||||
*
|
||||
* @see EntityDefaultI18nController
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info_alter().
|
||||
*/
|
||||
function entity_test_i18n_entity_info_alter(&$info) {
|
||||
// Enable i18n support via the entity API.
|
||||
$info['entity_test_type']['i18n controller class'] = 'EntityDefaultI18nStringController';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_property_info_alter().
|
||||
*/
|
||||
function entity_test_i18n_entity_property_info_alter(&$info) {
|
||||
// Mark some properties as translatable, but also denote that translation
|
||||
// works with i18n_string.
|
||||
foreach (array('label') as $name) {
|
||||
$info['entity_test_type']['properties'][$name]['translatable'] = TRUE;
|
||||
$info['entity_test_type']['properties'][$name]['i18n string'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_{entity_test_type}_insert().
|
||||
*/
|
||||
function entity_test_i18n_entity_test_type_insert($test_type) {
|
||||
i18n_string_object_update('entity_test_type', $test_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_{entity_test_type}_update().
|
||||
*/
|
||||
function entity_test_i18n_entity_test_type_update($test_type) {
|
||||
// Account for name changes.
|
||||
if ($test_type->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);
|
||||
}
|
635
sites/all/modules/entity/views/entity.views.inc
Normal file
635
sites/all/modules/entity/views/entity.views.inc
Normal file
@@ -0,0 +1,635 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide views data for modules making use of the entity CRUD API.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_data().
|
||||
*
|
||||
* Provides Views integration for entities if they satisfy one of these
|
||||
* conditions:
|
||||
* - hook_entity_info() specifies a 'views controller class' key.
|
||||
* - hook_entity_info() specifies a 'module' key, and the module does not
|
||||
* implement hook_views_data().
|
||||
*
|
||||
* @see entity_crud_hook_entity_info()
|
||||
* @see entity_views_table_definition()
|
||||
*/
|
||||
function entity_views_data() {
|
||||
$data = array();
|
||||
|
||||
foreach (entity_crud_get_info() as $type => $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'],
|
||||
'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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 '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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains an example for a Views query plugin that could use the data selection tables.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Describes the additional methods looked for on a query plugin if data selection based tables or fields are used.
|
||||
*
|
||||
* Only get_result_entities() needs to be present, so results can be retrieved.
|
||||
* The other methods are optional.
|
||||
*
|
||||
* If the table does not contain entities, however, the get_result_wrappers()
|
||||
* method is necessary, too. If this is the case and there are no relations to
|
||||
* entity tables, the get_result_entities() method is not needed.
|
||||
*
|
||||
* @see entity_views_table_definition()
|
||||
*/
|
||||
abstract class entity_views_example_query extends views_plugin_query {
|
||||
|
||||
/**
|
||||
* Add a sort to the query.
|
||||
*
|
||||
* This is used to add a sort based on an Entity API data selector instead
|
||||
* of a field alias.
|
||||
*
|
||||
* This method has to be present if click-sorting on fields should be allowed
|
||||
* for some fields using the default Entity API field handlers.
|
||||
*
|
||||
* @param $selector
|
||||
* The field to sort on, as an Entity API data selector.
|
||||
* @param $order
|
||||
* The order to sort items in - either 'ASC' or 'DESC'. Defaults to 'ASC'.
|
||||
*/
|
||||
public abstract function add_selector_orderby($selector, $order = 'ASC');
|
||||
|
||||
/**
|
||||
* Returns the according entity objects for the given query results.
|
||||
*
|
||||
* This is compatible to the get_result_entities() method used by Views.
|
||||
*
|
||||
* The method is responsible for resolving the relationship and returning the
|
||||
* entity objects for that relationship. The helper methods
|
||||
* EntityFieldHandlerHelper::construct_property_selector() and
|
||||
* EntityFieldHandlerHelper::extract_property_multiple() can be used to do
|
||||
* this.
|
||||
*
|
||||
* @param $results
|
||||
* The results of the query, as returned by this query plugin.
|
||||
* @param $relationship
|
||||
* (optional) A relationship for which the entities should be returned.
|
||||
* @param $field
|
||||
* (optional) The field for which the entity should be returned. This is
|
||||
* only needed in case a field is derived via a referenced entity without
|
||||
* using a relationship. For example, if the node's field "author:name" is
|
||||
* used, the user entity would be returned instead of the node entity.
|
||||
*
|
||||
* @return
|
||||
* A numerically indexed array containing two items: the entity type of
|
||||
* entities returned by this method; and the array of entities, keyed by the
|
||||
* same indexes as the results.
|
||||
*
|
||||
* @see EntityFieldHandlerHelper::extract_property_multiple()
|
||||
*/
|
||||
public abstract function get_result_entities($results, $relationship = NULL, $field = NULL);
|
||||
|
||||
/**
|
||||
* Returns the according metadata wrappers for the given query results.
|
||||
*
|
||||
* This can be used if no entities for the results can be given, but entity
|
||||
* metadata wrappers can be constructed for them.
|
||||
*
|
||||
* @param $results
|
||||
* The results of the query, as returned by this query plugin.
|
||||
* @param $relationship
|
||||
* (optional) A relationship for which the wrappers should be returned.
|
||||
* @param $field
|
||||
* (optional) The field of which a wrapper should be returned.
|
||||
*
|
||||
* @return
|
||||
* A numerically indexed array containing two items: the data type of
|
||||
* the wrappers returned by this method; and the array of retrieved
|
||||
* EntityMetadataWrapper objects, keyed by the same indexes as the results.
|
||||
*/
|
||||
public abstract function get_result_wrappers($results, $relationship = NULL, $field = NULL);
|
||||
|
||||
}
|
@@ -0,0 +1,521 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the EntityFieldHandlerHelper class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper class containing static implementations of common field handler methods.
|
||||
*
|
||||
* Used by the data selection entity field handlers to avoid code duplication.
|
||||
*
|
||||
* @see entity_views_table_definition()
|
||||
*/
|
||||
class EntityFieldHandlerHelper {
|
||||
|
||||
/**
|
||||
* Provide appropriate default options for a handler.
|
||||
*/
|
||||
public static function option_definition($handler) {
|
||||
if (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 (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;
|
||||
while (!empty($current_handler->relationship) && !empty($view->relationship[$current_handler->relationship])) {
|
||||
$current_handler = $view->relationship[$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)) : $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)));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Renders a full entity in a views area.
|
||||
*/
|
||||
|
||||
class entity_views_handler_area_entity extends views_handler_area {
|
||||
public function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
$options['entity_type'] = array('default' => 'node');
|
||||
$options['entity_id'] = array('default' => '');
|
||||
$options['view_mode'] = array('default' => 'full');
|
||||
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.'),
|
||||
'#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',
|
||||
);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
if (!empty($entity_type) && !empty($entity_id) && !empty($view_mode)) {
|
||||
$entities = entity_load($entity_type, array($entity_id));
|
||||
$render = entity_view($entity_type, $entities, $view_mode);
|
||||
$render_entity = reset($render);
|
||||
return drupal_render($render_entity);
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the entity_views_handler_field_boolean class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A handler to provide proper displays for booleans.
|
||||
*
|
||||
* Overrides the default Views handler to retrieve the data from an entity via
|
||||
* data selection.
|
||||
*
|
||||
* This handler may only be used in conjunction with data selection based Views
|
||||
* tables or other base tables using a query plugin that supports data
|
||||
* selection.
|
||||
*
|
||||
* @see entity_views_field_definition()
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class entity_views_handler_field_boolean extends views_handler_field_boolean {
|
||||
|
||||
/**
|
||||
* Stores the entity type of the result entities.
|
||||
*/
|
||||
public $entity_type;
|
||||
|
||||
/**
|
||||
* Stores the result entities' metadata wrappers.
|
||||
*/
|
||||
public $wrappers = array();
|
||||
|
||||
/**
|
||||
* Stores the current value when rendering list fields.
|
||||
*/
|
||||
public $current_value;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
parent::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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide options for this handler.
|
||||
*/
|
||||
public function option_definition() {
|
||||
return parent::option_definition() + EntityFieldHandlerHelper::option_definition($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a options form for this handler.
|
||||
*/
|
||||
public function options_form(&$form, &$form_state) {
|
||||
parent::options_form($form, $form_state);
|
||||
EntityFieldHandlerHelper::options_form($this, $form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return parent::render($values);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the entity_views_handler_field_date class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A handler to provide proper displays for dates.
|
||||
*
|
||||
* Overrides the default Views handler to retrieve the data from an entity via
|
||||
* data selection.
|
||||
*
|
||||
* This handler may only be used in conjunction with data selection based Views
|
||||
* tables or other base tables using a query plugin that supports data
|
||||
* selection.
|
||||
*
|
||||
* @see entity_views_field_definition()
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class entity_views_handler_field_date extends views_handler_field_date {
|
||||
|
||||
/**
|
||||
* Stores the entity type of the result entities.
|
||||
*/
|
||||
public $entity_type;
|
||||
|
||||
/**
|
||||
* Stores the result entities' metadata wrappers.
|
||||
*/
|
||||
public $wrappers = array();
|
||||
|
||||
/**
|
||||
* Stores the current value when rendering list fields.
|
||||
*/
|
||||
public $current_value;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
parent::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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide options for this handler.
|
||||
*/
|
||||
public function option_definition() {
|
||||
return parent::option_definition() + EntityFieldHandlerHelper::option_definition($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a options form for this handler.
|
||||
*/
|
||||
public function options_form(&$form, &$form_state) {
|
||||
parent::options_form($form, $form_state);
|
||||
EntityFieldHandlerHelper::options_form($this, $form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return parent::render($values);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the entity_views_handler_field_duration class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A handler to provide proper displays for duration properties retrieved via data selection.
|
||||
*
|
||||
* This handler may only be used in conjunction with data selection based Views
|
||||
* tables or other base tables using a query plugin that supports data
|
||||
* selection.
|
||||
*
|
||||
* @see entity_views_field_definition()
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class entity_views_handler_field_duration extends views_handler_field {
|
||||
|
||||
/**
|
||||
* Stores the entity type of the result entities.
|
||||
*/
|
||||
public $entity_type;
|
||||
|
||||
/**
|
||||
* Stores the result entities' metadata wrappers.
|
||||
*/
|
||||
public $wrappers = array();
|
||||
|
||||
/**
|
||||
* Stores the current value when rendering list fields.
|
||||
*/
|
||||
public $current_value;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
parent::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['format_interval'] = array('default' => 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']);
|
||||
}
|
||||
return $this->sanitize_value($this->options['prefix'], 'xss') .
|
||||
$this->sanitize_value($value) .
|
||||
$this->sanitize_value($this->options['suffix'], 'xss');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the entity_views_handler_field_entity class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A handler to provide proper displays for entities retrieved via data selection.
|
||||
*
|
||||
* This handler may only be used in conjunction with data selection based Views
|
||||
* tables or other base tables using a query plugin that supports data
|
||||
* selection.
|
||||
*
|
||||
* @see entity_views_field_definition()
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class entity_views_handler_field_entity extends views_handler_field {
|
||||
|
||||
/**
|
||||
* Stores the entity type of the result entities.
|
||||
*/
|
||||
public $entity_type;
|
||||
|
||||
/**
|
||||
* Stores the result entities' metadata wrappers.
|
||||
*/
|
||||
public $wrappers = array();
|
||||
|
||||
/**
|
||||
* The entity type of the entity displayed by this field.
|
||||
*/
|
||||
public $field_entity_type;
|
||||
|
||||
/**
|
||||
* Stores the current value when rendering list fields.
|
||||
*/
|
||||
public $current_value;
|
||||
|
||||
/**
|
||||
* Initialize the entity type with the field's entity type.
|
||||
*/
|
||||
public function init(&$view, &$options) {
|
||||
parent::init($view, $options);
|
||||
$this->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');
|
||||
|
||||
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',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (!$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;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the entity_views_handler_field_field class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A handler to provide proper displays for Field API fields.
|
||||
*
|
||||
* Overrides the default Views handler to retrieve the data from an entity via
|
||||
* data selection.
|
||||
*
|
||||
* This handler may only be used in conjunction with data selection based Views
|
||||
* tables or other base tables using a query plugin that supports data
|
||||
* selection.
|
||||
*
|
||||
* @see entity_views_field_definition()
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class entity_views_handler_field_field extends views_handler_field_field {
|
||||
|
||||
/**
|
||||
* Stores the entity type of the result entities.
|
||||
*/
|
||||
public $entity_type;
|
||||
|
||||
/**
|
||||
* Stores the result entities' metadata wrappers.
|
||||
*/
|
||||
public $wrappers = array();
|
||||
|
||||
/**
|
||||
* The entity for which this field is currently rendered.
|
||||
*/
|
||||
public $entity;
|
||||
|
||||
/**
|
||||
* Return TRUE if the user has access to view this field.
|
||||
*/
|
||||
public function access() {
|
||||
return field_access('view', $this->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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the entity_views_handler_field_numeric class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Render a field as a numeric value.
|
||||
*
|
||||
* Overrides the default Views handler to retrieve the data from an entity via
|
||||
* data selection.
|
||||
*
|
||||
* This handler may only be used in conjunction with data selection based Views
|
||||
* tables or other base tables using a query plugin that supports data
|
||||
* selection.
|
||||
*
|
||||
* @see entity_views_field_definition()
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class entity_views_handler_field_numeric extends views_handler_field_numeric {
|
||||
|
||||
/**
|
||||
* Stores the entity type of the result entities.
|
||||
*/
|
||||
public $entity_type;
|
||||
|
||||
/**
|
||||
* Stores the result entities' metadata wrappers.
|
||||
*/
|
||||
public $wrappers = array();
|
||||
|
||||
/**
|
||||
* Stores the current value when rendering list fields.
|
||||
*/
|
||||
public $current_value;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
parent::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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide options for this handler.
|
||||
*/
|
||||
public function option_definition() {
|
||||
return parent::option_definition() + EntityFieldHandlerHelper::option_definition($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a options form for this handler.
|
||||
*/
|
||||
public function options_form(&$form, &$form_state) {
|
||||
parent::options_form($form, $form_state);
|
||||
EntityFieldHandlerHelper::options_form($this, $form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return parent::render($values);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the entity_views_handler_field_options class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A handler to provide proper displays for values chosen from a set of options.
|
||||
*
|
||||
* This handler may only be used in conjunction with data selection based Views
|
||||
* tables or other base tables using a query plugin that supports data
|
||||
* selection.
|
||||
*
|
||||
* @see entity_views_field_definition()
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class entity_views_handler_field_options extends views_handler_field {
|
||||
|
||||
/**
|
||||
* Stores the entity type of the result entities.
|
||||
*/
|
||||
public $entity_type;
|
||||
|
||||
/**
|
||||
* Stores the result entities' metadata wrappers.
|
||||
*/
|
||||
public $wrappers = array();
|
||||
|
||||
/**
|
||||
* Stores the current value when rendering list fields.
|
||||
*/
|
||||
public $current_value;
|
||||
|
||||
/**
|
||||
* The key / name mapping for the options.
|
||||
*/
|
||||
public $option_list;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
parent::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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the options this handler uses.
|
||||
*/
|
||||
public function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
$options += EntityFieldHandlerHelper::option_definition($this);
|
||||
$options['format_name'] = array('default' => 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];
|
||||
}
|
||||
|
||||
return $this->sanitize_value($value);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the entity_views_handler_field_text class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A handler to display text data.
|
||||
*
|
||||
* Overrides the default Views handler to retrieve the data from an entity via
|
||||
* data selection.
|
||||
*
|
||||
* This handler may only be used in conjunction with data selection based Views
|
||||
* tables or other base tables using a query plugin that supports data
|
||||
* selection.
|
||||
*
|
||||
* @see entity_views_field_definition()
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class entity_views_handler_field_text extends views_handler_field {
|
||||
|
||||
/**
|
||||
* Stores the entity type of the result entities.
|
||||
*/
|
||||
public $entity_type;
|
||||
|
||||
/**
|
||||
* Stores the result entities' metadata wrappers.
|
||||
*/
|
||||
public $wrappers = array();
|
||||
|
||||
/**
|
||||
* Stores the current value when rendering list fields.
|
||||
*/
|
||||
public $current_value;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
parent::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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide options for this handler.
|
||||
*/
|
||||
public function option_definition() {
|
||||
return parent::option_definition() + EntityFieldHandlerHelper::option_definition($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a options form for this handler.
|
||||
*/
|
||||
public function options_form(&$form, &$form_state) {
|
||||
parent::options_form($form, $form_state);
|
||||
EntityFieldHandlerHelper::options_form($this, $form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return $this->sanitize_value($value, 'xss');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the entity_views_handler_field_uri class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Field handler to provide simple renderer that turns a URL into a clickable link.
|
||||
*
|
||||
* Overrides the default Views handler to retrieve the data from an entity via
|
||||
* data selection.
|
||||
*
|
||||
* This handler may only be used in conjunction with data selection based Views
|
||||
* tables or other base tables using a query plugin that supports data
|
||||
* selection.
|
||||
*
|
||||
* @see entity_views_field_definition()
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class entity_views_handler_field_uri extends views_handler_field_url {
|
||||
|
||||
/**
|
||||
* Stores the entity type of the result entities.
|
||||
*/
|
||||
public $entity_type;
|
||||
|
||||
/**
|
||||
* Stores the result entities' metadata wrappers.
|
||||
*/
|
||||
public $wrappers = array();
|
||||
|
||||
/**
|
||||
* Stores the current value when rendering list fields.
|
||||
*/
|
||||
public $current_value;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
parent::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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide options for this handler.
|
||||
*/
|
||||
public function option_definition() {
|
||||
return parent::option_definition() + EntityFieldHandlerHelper::option_definition($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a options form for this handler.
|
||||
*/
|
||||
public function options_form(&$form, &$form_state) {
|
||||
parent::options_form($form, $form_state);
|
||||
EntityFieldHandlerHelper::options_form($this, $form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return parent::render($values);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the entity_views_handler_relationship class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Relationship handler for data selection tables.
|
||||
*
|
||||
* This handler may only be used in conjunction with data selection based Views
|
||||
* tables or other base tables using a query plugin that supports data
|
||||
* selection.
|
||||
*
|
||||
* @see entity_views_field_definition()
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class entity_views_handler_relationship extends views_handler_relationship {
|
||||
|
||||
/**
|
||||
* Slightly modify the options form provided by the parent handler.
|
||||
*/
|
||||
public function options_form(&$form, &$form_state) {
|
||||
parent::options_form($form, $form_state);
|
||||
// This won't work with data selector-based relationships, as we only
|
||||
// inspect those *after* the results are known.
|
||||
$form['required']['#access'] = FALSE;
|
||||
// Notify the user of our restrictions regarding lists of entities, if
|
||||
// appropriate.
|
||||
if (!empty($this->definition['multiple'])) {
|
||||
$form['multiple_note'] = array(
|
||||
'#markup' => t('<strong>Note:</strong> 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'];
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains the entity_views_handler_relationship_by_bundle class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Relationship handler for entity relationships that may limit the join to one or more bundles.
|
||||
*
|
||||
* This handler is only applicable for entities that are using bundle entities,
|
||||
* i.e. entities having the 'bundle of' entity info key set.
|
||||
*
|
||||
* For example, this allows a relationship from users to profiles of a certain
|
||||
* profile type.
|
||||
*
|
||||
* @see entity_crud_hook_entity_info()
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class entity_views_handler_relationship_by_bundle extends views_handler_relationship {
|
||||
function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
$options['bundle_types'] = array('default' => 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['bundle 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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Row style plugin for displaying the results as entities.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Plugin class for displaying Views results with entity_view.
|
||||
*/
|
||||
class entity_views_plugin_row_entity_view extends views_plugin_row {
|
||||
|
||||
protected $entity_type, $entities;
|
||||
|
||||
public function init(&$view, &$display, $options = NULL) {
|
||||
parent::init($view, $display, $options);
|
||||
|
||||
// Initialize the entity-type used.
|
||||
$table_data = views_fetch_data($this->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)) {
|
||||
$render = $this->rendered_content[entity_id($this->entity_type, $entity)];
|
||||
return drupal_render($render);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user