FINAL suepr merge step : added all modules to this super repos

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-19 16:46:59 +02:00
7585 changed files with 1723356 additions and 18 deletions

View File

@@ -0,0 +1,132 @@
Title 7.x-1.x, xxxx-xx-xx
-------------------------
#1991712 by milesw: Fixed Title displays wrong revision using Revisioning
module.
#1907078 by sylus: Fixed Undefined index: field_name() in
title_field_replacement_enabled().
#1961810 by peximo | fcortez: Fixed Wrap tag classes are not being inserted.
#1850866 by B-Prod: Fixed Undefined index 'format' in
title_field_text_with_summary_sync_set().
#1269076 by plach, das-peter, danielnolde | renat: Fixed Translated
title_field replaces node->title with translated value.
Title 7.x-1.0-alpha7, 2013-03-18
--------------------------------
#1919640 by peximo: Fixed Empty tag shown when the title field is displayed.
#1869438 by jsacksick: Fixed hook_field_attach_create_bundle() implementation
isn't correct.
#1280962 by mvc: Fixed Better description of field replacement dialogue.
#1900282: Fixed wrong files[] directive in the title_test module.
#1898618 by muka: Fixed Handle case when title property is not set (avoiding
error).
#1885356 by peximo: Fixed Empty HTML class in Linked and wrapped format.
#1865604 by GaëlG, plach: Fixed Title module overrides label callback (prevents
term label localization).
Title 7.x-1.0-alpha5, 2012-12-18
--------------------------------
#1869228 by plach: Updated system dependencies.
#1062814 by Andreas Radloff, jucallme | Scott J: Added Formatters.
#1849344 by peximo: Fixed Duplicate description element in taxonomy display
form.
#1482052 by peximo | joel rotelli, epieddy: Fixed Undefined property:
stdClass::$type dans title_tokens_alter().
#1761142 by joel_osc, plach | no2e: Fixed Replaced taxonomy description:
can't delete content (but editing works).
#1736476 by guillaumev: Made sure entity is accurate in title_entity_language().
#1748008 by vasike: Replaced fields should inherit the legacy field access.
#1808454 by plach: Fixed help and permissions for the admin settings.
#1835486 by plach: Fixed Automated tests are failing.
#1808454 by peximo: Overhauled the admin settings form.
#1773514 by peximo: Added Module's configuration page.
#1396450 by logaritmisk, plach | mgifford: Fixed Notice: Undefined index:
fieldable in title_entity_info_alter() (line 28 of title/title.module).
Title 7.x-1.0-alpha4, 2012-08-11
--------------------------------
#1708046 by plach | steinmb: Fixed Term description is overwritten.
#1709938 by Amitaibu: Prevented notice in title_field_attach_create_bundle().
#1541414 by peximo, plach | int_ua: Added No 'Link this field to the original
piece of content' option in Views UI for fields replaced with Title module.
#1704536 by RoySegall, Amitaibu, plach: Attach title field automatically when a
new entity bundle is created.
#1699092 by plach: Fixed Notices when submitting an empty taxonomy term
description.
#1665006 by mikey_p: Fixed Doesn't work when legacy title field is NULL.
#1613514 by steven.wichers, Amitaibu: Fixed Importing nodes in bulk fail to
create titles for all nodes after the first one.
#1644736 by plach: Fixed When initializing replaced fields all bundles are
processed.
#1631958 by plach | Goekmen: Fixed Undefined index: base path in
EntityTranslationDefaultHandler.
#1635006 by nikosnikos: Fixed Description summary desappear in taxonomy term.
#1528590 by torrance123, JonMcL: Fixed Reference to $form_state['values'] is
altering Views Bulk Operations modify action form submit.
#1445848 by danielnolde: Added Retain the original legacy field value to entity
in sync.
#1620986 by plach: Perform reverse synchronization more reliably.
#1519930 by plach, steinmb: Use the upcoming entity_language() function to
determine the entity language.
#1586002 by plach, Pisco: (follow-up) Fixed notices when changing node language.
Title 7.x-1.0-alpha3, 2012-06-05
--------------------------------
#1586002 by plach, Pisco | liquidcms: Fixed Entity language handling broken.
#1608980 by helior: Fixed Possible undefined indexes in
title_field_replacement_info().
#1362790 by colan, amateescu: Fixed Undefined property: stdClass::$title in
title_entity_update().
#1433060 by plach: Added Allow to use entity label fields in the entity content
area.
#1367118 by plach | BrightBold: Fixed Wrong menu item declaration for the field
replacement callback.
#1357220 by colan: Fixed Module should be listed in the Fields package.
#1323288 by das-peter: Fixed entitycache integration.
Title 7.x-1.0-alpha2, 2011-09-09
--------------------------------
#1229892 by plach | chrisdolby: Fixed Strict warning: Only variables should be
assigned by reference in title_field_attach_submit() - line 482 of
title.module.
#1219860 by plach, claudiu.cristea: Fixed Undefined index 'format'.
#1210670 by claudiu.cristea, das-peter | vasike: Fixed Entity translation errors
for Taxonomies entities.
Title 7.x-1.0-alpha1, 2011-09-07
--------------------------------
#1200320 by das-peter: Fixed Call field_attach_presave() before
field_attach_update() in field replacement batch.
#1155128 by das-peter | Dave Reid, plach: Enhanced support for tokens.
#1169394 by plach: Synchronized code after core fixes.
#1146724 by plach | das-peter, joostvdl: Fixed Replacing field values are not
initialized.
#1157438 by das-peter: Fixed Reset sync cache on entitycache reset.
#1116586 by plach | tte: Fixed Exception when a node's title is not converted
immediately.
#1138646 by plach | joostvdl, sun: Fixed Notice: Undefined index: label in
entity_get_info().
#1141674 by plach | manveru: Fixed Notices about missing $description.
#924968 by plach: Updated the change log.
#924968 by plach: Improved comments/PHP docs.
#924968 by plach: Introduced tests for field replacement UI.
#924968 by plach: Fixed field replacement checkbox broken.
#924968 by plach: Polished API as title_field_replacement_toggle does not belong
to title.admin.inc.
#924968 by plach: Fixed comments/PHP docs.
#924968 by plach: Disabled entity_label() support until #1096446 is fixed.
#924968 by plach: Introduced tests for field replacement workflow.
#924968 by plach: Moved administration code into title.admin.inc.
#924968 by plach: Added entity_label() support.
#924968 by plach: Fixed resave needed if field values altered before save.
#924968 by plach: Fixed entity forms to support nested subforms.
#924968 by plach: Added PHP docs.
#924968 by plach, das-peter, sun, fago, klonos: Introduced field replacement API
and UI.
by sun: Initial baseline of module files.

View File

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

View File

@@ -0,0 +1,37 @@
-- SUMMARY --
Title module allows entity titles/labels to be fully translatable.
For a full description of the module, visit the project page:
http://drupal.org/project/title
To submit bug reports and feature suggestions, or to track changes:
http://drupal.org/project/issues/title
-- REQUIREMENTS --
* @todo
-- INSTALLATION --
* Install as usual, see http://drupal.org/node/70151 for further information.
-- CONFIGURATION --
* @todo
-- USAGE --
* @todo
-- CONTACT --
Current maintainers:
* Francesco Placella (plach) - http://drupal.org/user/183211
* Daniel F. Kudwien (sun) - http://drupal.org/user/54136

View File

@@ -0,0 +1,453 @@
<?php
/**
* @file
* Tests for the Title module.
*/
/**
* Tests for legacy field replacement.
*/
class TitleFieldReplacementTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Field replacement',
'description' => 'Test field replacement.',
'group' => 'Title',
);
}
function setUp() {
parent::setUp('entity', 'field_test', 'title', 'title_test');
}
/**
* Test field replacement API and workflow.
*/
function testFieldReplacementWorkflow() {
$info = entity_get_info('test_entity');
$label_key = $info['entity keys']['label'];
$field_name = $label_key . '_field';
// Enable field replacement for the test entity.
title_field_replacement_toggle('test_entity', 'test_bundle', $label_key);
$i = 0;
$entity = field_test_create_stub_entity(FALSE, FALSE);
while ($i++ <= 1) {
// The first time the entity gets created the second time gets updated.
title_test_entity_save($entity);
// Check that the replacing field value has been synchronized on save.
$query = db_select('test_entity', 'te');
$query->addJoin('INNER', 'field_data_' . $field_name, 'f', 'te.ftid = f.entity_id');
$record = $query
->fields('te')
->fields('f')
->condition('ftid', $entity->ftid)
->execute()
->fetch();
$phase = $entity->is_new ? 'insert' : 'update';
$this->assertIdentical($record->{$label_key}, $record->{$field_name . '_value'}, t('Field synchronization is correctly performed on %phase.', array('%phase' => $phase)));
unset($entity->is_new);
}
// Store a dummy value in the legacy field.
while (($label = $this->randomName()) == $entity->{$label_key});
db_update('test_entity')
->fields(array($label_key => $label))
->execute();
$record = db_select('test_entity', 'te')
->fields('te')
->condition('ftid', $entity->ftid)
->execute()
->fetch();
$this->assertNotIdentical($record->{$label_key}, $entity->{$label_key}, t('Entity label has been changed.'));
// Clear field cache so synchronization can be performed on field attach
// load.
cache_clear_all('*', 'cache_field');
drupal_static_reset();
// Check that the replacing field value is correctly synchronized on load
// and view.
$entity = title_test_entity_test_load($entity);
title_test_phase_check('after_load', $entity);
$build = entity_view('test_entity', array($entity->ftid => $entity));
foreach (title_test_phase_store() as $phase => $value) {
$this->assertTrue($value, t('Field synchronization is correctly performed on %phase.', array('%phase' => $phase)));
}
// Change the value stored into the label field to check entity_label().
if (isset($info['label callback'])) {
$label = $this->randomName();
$entity->{$field_name}[LANGUAGE_NONE][0]['value'] = $label;
$this->assertIdentical(entity_label('test_entity', $entity), $label, t('entity_label() returns the expected value.'));
}
}
/**
* Test field replacement UI.
*/
function testFieldReplacementUI() {
$admin_user = $this->drupalCreateUser(array('access administration pages', 'view the administration theme', 'administer content types', 'administer taxonomy', 'administer comments'));
$this->drupalLogin($admin_user);
foreach (entity_get_info() as $entity_type => $entity_info) {
if (!empty($entity_info['field replacement'])) {
foreach ($entity_info['bundles'] as $bundle => $bundle_info) {
if (isset($bundle_info['admin']['path'])) {
$admin_path = _field_ui_bundle_admin_path($entity_type, $bundle) . '/fields';
foreach ($entity_info['field replacement'] as $legacy_field => $info) {
$path = $admin_path . '/replace/' . $legacy_field;
$xpath = '//a[@href=:url and text()=:label]';
$args = array(':url' => url($path), ':label' => t('replace'));
$targs = array('%legacy_field' => $legacy_field, '%entity_type' => $entity_type, '%bundle' => $bundle);
$field_name = $info['field']['field_name'];
// Check that the current legacy field has a "replace" operation.
$this->drupalGet($admin_path);
$link = $this->xpath($xpath, $args);
$this->assertEqual(count($link), 1, t('Replace link found for the field %legacy_field of the bundle %bundle of the entity %entity_type.', $targs));
// Check that the legacy field has correctly been replaced through
// field replacement UI.
$this->drupalPost($path, array('enabled' => TRUE), t('Save settings'));
_field_info_collate_fields(TRUE);
$link = $this->xpath($xpath, $args);
$this->assertTrue(empty($link) && title_field_replacement_enabled($entity_type, $bundle, $legacy_field), t('%legacy_field successfully replaced for the bundle %bundle of the entity %entity_type.', $targs));
// Check that the enabled status cannot be changed unless the
// field instance is removed.
$this->drupalGet($path);
$this->assertFieldByXPath('//form//input[@name="enabled" and @checked="checked" and @disabled="disabled"]', NULL, t('Field replacement for %legacy_field cannot be disabled unless the replacing field instance is deleted.', array('%legacy_field' => $legacy_field)));
$this->drupalPost($path, array(), t('Save settings'));
_field_info_collate_fields(TRUE);
$this->assertTrue(title_field_replacement_enabled($entity_type, $bundle, $legacy_field), t('Submitting the form does not alter field replacement settings.'));
// Delete the field instance and check that the "replace"
// operation is available again.
$this->drupalPost($admin_path . '/' . $field_name . '/delete', array(), t('Delete'));
$link = $this->xpath($xpath, $args);
$this->assertEqual(count($link), 1, t('Replace link found for the field %legacy_field of the bundle %bundle of the entity %entity_type.', $targs));
// Check that field replacement can be enabled again.
$this->drupalGet($path);
$this->assertFieldByXPath('//form//input[@name="enabled" and not(@checked) and not(@disabled)]', NULL, t('Field replacement for %legacy_field cannot be disabled unless the replacing field instance is deleted.', array('%legacy_field' => $legacy_field)));
}
}
}
}
}
}
}
/**
* Tests for legacy field replacement.
*/
class TitleAdminSettingsTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Admin settings',
'description' => 'Test the administration settings.',
'group' => 'Title',
);
}
function setUp() {
parent::setUp('field_test', 'title', 'title_test');
$admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer taxonomy'));
$this->drupalLogin($admin_user);
}
/**
* Check for automated title_field attachment.
*/
function testAutomatedFieldAttachement() {
$this->assertAutomatedFieldAttachement(TRUE);
$this->assertAutomatedFieldAttachement(FALSE);
}
/**
* Check that the fields are replaced or skipped depdening on the given value.
*/
function assertAutomatedFieldAttachement($enabled) {
$edit = array(
'title_taxonomy_term[auto_attach][name]' => $enabled,
'title_taxonomy_term[auto_attach][description]' => $enabled,
);
$this->drupalPost('admin/config/content/title', $edit, t('Save configuration'));
$edit = array(
'name' => $this->randomName(),
'machine_name' => drupal_strtolower($this->randomName()),
'description' => $this->randomString(16),
);
$this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
$entity_type = 'taxonomy_term';
$bundle = $edit['machine_name'];
field_info_cache_clear();
$this->assertTrue(title_field_replacement_enabled($entity_type, $bundle, 'name') == $enabled, 'Name field correctly processed.');
$this->assertTrue(title_field_replacement_enabled($entity_type, $bundle, 'description') == $enabled, 'Description field correctly processed.');
}
}
/**
* Tests for legacy field replacement.
*/
class TitleTranslationTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Replaced fields translation',
'description' => 'Test replaced field translation.',
'group' => 'Title',
);
}
protected function setUp() {
parent::setUp('locale', 'entity_translation', 'title', 'field_test', 'title_test');
// Create a power user.
$admin_user = $this->drupalCreateUser(array('administer modules', 'view the administration theme', 'administer languages', 'administer taxonomy', 'administer entity translation', 'translate any entity'));
$this->drupalLogin($admin_user);
// Enable a translation language.
$edit = array('langcode' => 'it');
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
$this->assertTrue(drupal_multilingual(), t('Italian language installed.'));
// Enable URL language negotiation.
$name = 'language_content[enabled][locale-url]';
$edit = array($name => 1);
$this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
$this->assertFieldByName($name, 1, t('URL language negotiation enabled.'));
// Enable taxonomy translation.
$name = 'entity_translation_entity_types[taxonomy_term]';
$edit = array($name => 1);
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
$this->assertFieldByName($name, 'taxonomy_term', t('Taxonomy translation enabled.'));
// Create a new vocabulary.
$name = drupal_strtolower($this->randomName());
$edit = array(
'name' => $this->randomString(),
'machine_name' => $name,
);
$this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
$this->vocabulary = taxonomy_vocabulary_machine_name_load($name);
$this->assertTrue($this->vocabulary, t('Vocabulary created.'));
// Replace both taxonomy term legacy fields.
$entity_type = 'taxonomy_term';
foreach (title_field_replacement_info($entity_type) as $legacy_field => $info) {
title_field_replacement_toggle($entity_type, $name, $legacy_field);
$t_args = array('%legacy_field' => $legacy_field);
$this->assertTrue(field_info_instance($entity_type, $info['field']['field_name'], $name), t('The %legacy_field field has been correctly replaced.', $t_args));
}
// Ensure static caches do not interfere with API calls.
drupal_static_reset();
}
/**
* Tests taxonomy programmatic translation workflow.
*/
public function testProgrammaticTranslationWorkflow() {
// Create a taxonomy term and assign it an original language different from
// the default language.
$langcode = 'it';
$original_values = array(
'name' => $langcode . '_' . $this->randomName(),
'description' => $langcode . '_' . $this->randomName(),
);
$term = (object) ($original_values + array(
'format' => 'filtered_html',
'vocabulary_machine_name' => $this->vocabulary->machine_name,
'vid' => $this->vocabulary->vid,
));
entity_translation_get_handler('taxonomy_term', $term)->setOriginalLanguage($langcode);
taxonomy_term_save($term);
$this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
$term = $this->termLoad($term->tid);
$this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), 'Replacing field values correctly created from the legacy field values.');
// Pollute synchronization cache to ensure the expected values are stored
// anyway.
title_entity_sync('taxonomy_term', $term, $langcode);
// Create a translation using the default language.
$translation_langcode = language_default()->language;
$translated_values = array(
'name' => $translation_langcode . '_' . $this->randomName(),
'description' => $translation_langcode . '_' . $this->randomName(),
);
foreach ($translated_values as $name => $value) {
$term->{$name} = $value;
}
$translation = array(
'language' => $translation_langcode,
'source' => $langcode,
'uid' => $this->loggedInUser->uid,
'status' => 1,
'translate' => 0,
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
);
entity_translation_get_handler('taxonomy_term', $term)->setTranslation($translation);
taxonomy_term_save($term);
$this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
$term = $this->termLoad($term->tid, $translation_langcode);
$this->assertTrue($this->checkFieldValues($term, $translated_values, $translation_langcode), 'Replacing field translations correctly created.');
$this->assertTrue($this->checkFieldValues($term, $original_values, $langcode, FALSE), 'Replacing field original values correctly preserved.');
// Delete the translation.
entity_translation_get_handler('taxonomy_term', $term)->removeTranslation($translation_langcode);
taxonomy_term_save($term);
$this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
$term = $this->termLoad($term->tid, $langcode);
$this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), 'Replacing field translations correctly deleted.');
// Make the term language neutral.
entity_translation_get_handler('taxonomy_term', $term)->setOriginalLanguage(LANGUAGE_NONE);
foreach ($original_values as $name => $value) {
$field_name = $name . '_field';
$term->{$field_name}[LANGUAGE_NONE] = $term->{$field_name}[$langcode];
$term->{$field_name}[$langcode] = array();
}
taxonomy_term_save($term);
$this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
$term = $this->termLoad($term->tid);
$this->assertTrue($this->checkFieldValues($term, $original_values, LANGUAGE_NONE), 'Term original language correctly changed to the former translation language.');
// Change the term language to the former translation language.
entity_translation_get_handler('taxonomy_term', $term)->setOriginalLanguage($translation_langcode);
foreach ($original_values as $name => $value) {
$field_name = $name . '_field';
$term->{$field_name}[$translation_langcode] = $term->{$field_name}[LANGUAGE_NONE];
$term->{$field_name}[LANGUAGE_NONE] = array();
}
taxonomy_term_save($term);
$this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
$term = $this->termLoad($term->tid, $translation_langcode);
$this->assertTrue($this->checkFieldValues($term, $original_values, $translation_langcode), 'Term original language correctly changed to language neutral.');
// Make a replacing field untranslatable and change its value.
$field_name = 'name_field';
$field = field_info_field($field_name);
$field['translatable'] = FALSE;
field_update_field($field);
$original_values['name'] = LANGUAGE_NONE . '_' . $this->randomName();
$term->name = $original_values['name'];
taxonomy_term_save($term);
$this->assertTrue($this->checkLegacyValues($term, $original_values), 'Legacy field values correctly stored.');
$term = $this->termLoad($term->tid);
$this->assertEqual($term->{$field_name}[LANGUAGE_NONE][0]['value'], $original_values['name'], 'Untranslatable replacing field on translatable entity correctly handled.');
}
/**
* Tests taxonomy form translation workflow.
*/
public function testFormTranslationWorkflow() {
// Create a taxonomy term and check that legacy fields are properly
// populated.
$original_values = array(
'name' => $this->randomName(),
'description' => $this->randomName(),
);
$langcode = 'en';
$edit = $this->editValues($original_values, $langcode);
$this->drupalPost('admin/structure/taxonomy/' . $this->vocabulary->machine_name . '/add', $edit, t('Save'));
$term = current(entity_load('taxonomy_term', FALSE, array('name' => $original_values['name']), TRUE));
$this->assertEqual($term->description, $original_values['description'], t('Taxonomy term created.'));
// Translate the taxonomy term and check that both the original values and
// the translations were correctly stored.
$translated_values = array(
'name' => $this->randomName(),
'description' => $this->randomName(),
);
$translation_langcode = 'it';
$edit = $this->editValues($translated_values, 'it');
$this->drupalPost($translation_langcode . '/taxonomy/term/' . $term->tid . '/edit/add/' . $langcode . '/' . $translation_langcode, $edit, t('Save'));
$term = $this->termLoad($term->tid);
$this->assertTrue($this->checkFieldValues($term, $translated_values, $translation_langcode, FALSE), t('Taxonomy term translation created.'));
$this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), t('Taxonomy term original values preserved.'));
// Check that legacy fields have the correct values.
$this->assertEqual($term->name, $original_values['name'], t('Taxonomy term name correctly stored.'));
$this->assertEqual($term->description, $original_values['description'], t('Taxonomy term description correctly stored.'));
// Updated the taxonomy term translation and check that both the original
// values and the translations were correctly stored.
$translated_values = array(
'name' => $this->randomName(),
'description' => $this->randomName(),
);
$edit = $this->editValues($translated_values, $translation_langcode);
$this->drupalPost($translation_langcode . '/taxonomy/term/' . $term->tid . '/edit/' . $translation_langcode, $edit, t('Save'));
$term = $this->termLoad($term->tid);
$this->assertTrue($this->checkFieldValues($term, $translated_values, $translation_langcode, FALSE), t('Taxonomy term translation updated.'));
$this->assertTrue($this->checkFieldValues($term, $original_values, $langcode), t('Taxonomy term original values preserved.'));
// Check that legacy fields have the correct values.
$this->assertEqual($term->name, $original_values['name'], t('Taxonomy term name correctly stored.'));
$this->assertEqual($term->description, $original_values['description'], t('Taxonomy term description correctly stored.'));
}
/**
* Loads a term using the given language as active language.
*/
protected function termLoad($tid, $langcode = NULL) {
drupal_static_reset();
title_active_language($langcode);
return current(entity_load('taxonomy_term', array($tid), array(), TRUE));
}
/**
* Returns the drupalPost() $edit array corresponding to the given values.
*/
protected function editValues($values, $langcode) {
$edit = array();
foreach ($values as $name => $value) {
$edit["{$name}_field[{$langcode}][0][value]"] = $value;
}
return $edit;
}
/**
* Checks that the field values and optionally the legacy ones match the given values.
*/
protected function checkFieldValues($term, $values, $langcode, $legacy_match = TRUE) {
foreach ($values as $name => $value) {
$field_value = $term->{$name . '_field'}[$langcode][0]['value'];
if ($field_value != $value || ($legacy_match !== ($field_value == $term->{$name}))) {
return FALSE;
}
}
return TRUE;
}
/**
* Checks that the legacy field values stored in the database match the given values.
*/
protected function checkLegacyValues($term, $values) {
$record = db_query('SELECT * FROM {taxonomy_term_data} t WHERE t.tid = :tid', array(':tid' => $term->tid))->fetchAssoc();
foreach ($values as $name => $value) {
if ($record[$name] != $value) {
return FALSE;
}
}
return TRUE;
}
}

View File

@@ -0,0 +1,15 @@
name = Title Test
description = Testing module for Title module functionality. Do not enable.
core = 7.x
package = Testing
hidden = TRUE
dependencies[] = title
dependencies[] = entity
dependencies[] = entity_translation
; Information added by drupal.org packaging script on 2013-07-24
version = "7.x-1.0-alpha7+5-dev"
core = "7.x"
project = "title"
datestamp = "1374690078"

View File

@@ -0,0 +1,7 @@
<?php
/**
* @file
* Installation functionality for Title Test module.
*/

View File

@@ -0,0 +1,130 @@
<?php
/**
* @file
* Testing functionality for Title module.
*/
/**
* Implements hook_entity_info().
*/
function title_test_entity_info() {
$info = array();
$field = array(
'type' => 'text',
'cardinality' => 1,
'translatable' => TRUE,
);
$instance = array(
'required' => TRUE,
'settings' => array(
'text_processing' => 0,
),
'widget' => array(
'weight' => -5,
),
);
$info['test_entity'] = array(
'entity keys' => array(
'label' => 'ftlabel',
),
'field replacement' => array(
'ftlabel' => array(
'field' => $field,
'instance' => array(
'label' => t('Title'),
'description' => t('A field replacing node title.'),
) + $instance,
),
),
'controller class' => 'EntityAPIController',
);
return $info;
}
/**
* Save the given test entity.
*/
function title_test_entity_save($entity) {
// field_test_entity_save does not invoke hook_entity_presave().
module_invoke_all('entity_presave', $entity, 'test_entity');
field_test_entity_save($entity);
// field_test_entity_save does not invoke hook_entity_insert().
$hook = $entity->is_new ? 'entity_insert' : 'entity_update';
module_invoke_all($hook, $entity, 'test_entity');
}
/**
* Load the given test entity.
*/
function title_test_entity_test_load($entity) {
$entity = field_test_entity_test_load($entity->ftid);
// field_test_entity_load does not invoke hook_entity_load().
module_invoke_all('entity_load', array($entity), 'test_entity');
return $entity;
}
/**
* Store a value for the given phase.
*/
function title_test_phase_store($phase = NULL, $value = NULL) {
$store = &drupal_static(__FUNCTION__, array());
if (isset($phase)) {
$store[$phase] = $value;
}
return $store;
}
/**
* Check the entity label at a give phase.
*/
function title_test_phase_check($phase, $entity) {
$info = entity_get_info('test_entity');
$label_key = $info['entity keys']['label'];
$field_name = $label_key . '_field';
$value = $entity->{$label_key} == $entity->{$field_name}[LANGUAGE_NONE][0]['value'];
title_test_phase_store($phase, $value);
return $value;
}
/**
* Implements hook_entity_presave().
*/
function title_test_entity_presave($entity, $type) {
if ($type == 'test_entity') {
$info = entity_get_info('test_entity');
$label_key = $info['entity keys']['label'];
$entity->{$label_key} = DrupalWebTestCase::randomName();
}
}
/**
* Implements hook_field_attach_load().
*/
function title_test_field_attach_load($entity_type, $entities, $age, $options) {
if ($entity_type == 'test_entity') {
title_test_phase_check('field_attach_load', current($entities));
}
}
/**
* Implements hook_entity_load().
*/
function title_test_entity_load($entities, $type) {
if ($type == 'test_entity') {
title_test_phase_check('entity_load', current($entities));
}
}
/**
* Implements hook_entity_prepare_view().
*/
function title_test_entity_prepare_view($entities, $type, $langcode = NULL) {
if ($type == 'test_entity') {
title_test_phase_check('entity_prepare_view', current($entities));
}
}

View File

@@ -0,0 +1,118 @@
<?php
/**
* @file
* Admin page callbacks for the Title module.
*/
/**
* Provide settings to enable title field.
*/
function title_form_field_ui_overview(&$form, &$form_state) {
$entity_info = entity_get_info($form['#entity_type']);
if (!empty($entity_info['field replacement'])) {
$field_replacement_info = $entity_info['field replacement'];
$admin_path = _field_ui_bundle_admin_path($form['#entity_type'], $form['#bundle']);
$form['fields']['#header'][6]['colspan'] += 1;
foreach (element_children($form['fields']) as $field_name) {
if (isset($field_replacement_info[$field_name])) {
$form['fields'][$field_name]['field_replacement'] = array(
'#type' => 'link',
'#title' => t('replace'),
'#href' => $admin_path . '/fields/replace/' . $field_name,
'#options' => array('attributes' => array('title' => t('Replace %field with a customizable field instance that can be translated.', array('%field' => $field_name)))),
);
}
else {
$form['fields'][$field_name]['field_replacement'] = array();
}
}
}
}
/**
* Generate a field replacement form.
*/
function title_field_replacement_form($form, $form_state, $entity_type, $bundle, $field_name) {
$bundle_name = field_extract_bundle($entity_type, $bundle);
$entity_info = entity_get_info($entity_type);
$info = $entity_info['field replacement'][$field_name];
$instance = field_info_instance($entity_type, $info['field']['field_name'], $bundle_name);
$enabled = !empty($instance);
$form['#entity_type'] = $entity_type;
$form['#bundle'] = $bundle_name;
$form['#field_name'] = $field_name;
$form['enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Replace %field with a field instance', array('%field' => $field_name)),
'#description' => t('If this is enabled the %field will be replaced with a customizable field that can be translated.', array('%field' => $field_name)),
'#default_value' => $enabled,
'#disabled' => $enabled,
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save settings'));
return $form;
}
/**
* Process field replacement form subissions.
*/
function title_field_replacement_form_submit($form, &$form_state) {
if ($form_state['values']['enabled'] != $form['enabled']['#default_value']) {
if (title_field_replacement_toggle($form['#entity_type'], $form['#bundle'], $form['#field_name'])) {
drupal_set_message(t('%field replaced with a field instance.', array('%field' => $form['#field_name'])));
title_field_replacement_batch_set($form['#entity_type'], $form['#bundle'], $form['#field_name']);
}
else {
drupal_set_message(t('Field replacement removed.'));
}
}
$form_state['redirect'] = _field_ui_bundle_admin_path($form['#entity_type'], $form['#bundle']) . '/fields';
}
/**
* Form settings for automated title_field attachement.
*/
function title_admin_settings_form() {
$form['tabs'] = array(
'#type' => 'vertical_tabs',
);
foreach (entity_get_info() as $entity_type => $info) {
if (empty($info['field replacement'])) {
continue;
}
$form['settings']['title_' . $entity_type] = array(
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#tree' => TRUE,
'#group' => 'tabs',
'#title' => $info['label'],
);
$options = array();
foreach (array_keys($info['field replacement']) as $replacement) {
$options[$replacement] = drupal_ucfirst($replacement);
}
$default = variable_get('title_' . $entity_type, array());
$form['settings']['title_' . $entity_type]['auto_attach'] = array(
'#type' => 'checkboxes',
'#title' => t('Automatic field replacement'),
'#options' => $options,
'#default_value' => !empty($default['auto_attach']) ? $default['auto_attach'] : array(),
'#description' => t('Automatically replace the selected field(s) when creating a new bundle.'),
);
$form['settings']['title_' . $entity_type]['hide_label'] = _title_hide_label_widget($default, $info['label']);
}
return system_settings_form($form);
}

View File

@@ -0,0 +1,7 @@
<?php
/**
* @file
* API documentation for Title module.
*/

View File

@@ -0,0 +1,215 @@
<?php
/**
* @file
* Provide field replacement information for core entities and type specific
* callbacks.
*/
/**
* Implements hook_entity_info().
*/
function title_entity_info() {
$info = array();
$field = array(
'type' => 'text',
'cardinality' => 1,
'translatable' => TRUE,
);
$instance = array(
'required' => TRUE,
'settings' => array(
'text_processing' => 0,
),
'widget' => array(
'weight' => -5,
),
'display' => array(
'default' => array(
'type' => 'hidden',
),
),
);
$info['node'] = array(
'field replacement' => array(
'title' => array(
'field' => $field,
'instance' => array(
'label' => t('Title'),
'description' => '',
) + $instance,
),
),
'efq bundle conditions' => TRUE,
);
if (module_exists('taxonomy')) {
$info['taxonomy_term'] = array(
'field replacement' => array(
'name' => array(
'field' => $field,
'instance' => array(
'label' => t('Name'),
'description' => '',
) + $instance,
'preprocess_key' => 'term_name',
),
'description' => array(
'field' => array(
'type' => 'text_with_summary',
) + $field,
'instance' => array(
'required' => FALSE,
'label' => t('Description'),
'description' => '',
'settings' => array(
'text_processing' => 1,
),
) + $instance,
'callbacks' => array(
'submit' => 'title_field_term_description_submit',
),
'additional keys' => array(
'format' => 'format',
),
),
),
);
}
if (module_exists('comment')) {
$info['comment'] = array(
'field replacement' => array(
'subject' => array(
'field' => $field,
'instance' => array(
'label' => t('Subject'),
'description' => '',
) + $instance,
'preprocess_key' => 'title',
),
),
);
}
return $info;
}
/**
* Submit callback for the taxonomy term description.
*/
function title_field_term_description_submit($entity_type, $entity, $legacy_field, $info, $langcode, &$values) {
if (!isset($values['description'])) {
$values['description'] = array();
}
foreach (array('value', 'format') as $key) {
if (isset($entity->{$info['field']['field_name']}[$langcode][0][$key])) {
$values['description'][$key] = $entity->{$info['field']['field_name']}[$langcode][0][$key];
}
// If the keys are not defined an empty value has been submitted, hence we
// need to update the term description accordingly.
else {
$values['description'][$key] = ($key == 'value') ? '' : filter_default_format();
}
}
}
/**
* Sync callback for the text field type.
*/
function title_field_text_sync_get($entity_type, $entity, $legacy_field, $info, $langcode) {
$value = NULL;
$field_name = $info['field']['field_name'];
if (!empty($entity->{$field_name}[$langcode]) && is_array($entity->{$field_name}[$langcode])) {
$items = $entity->{$field_name}[$langcode];
$value = !empty($items[0]['value']) ? $items[0]['value'] : NULL;
}
return array($legacy_field => $value);
}
/**
* Sync back callback for the text field type.
*/
function title_field_text_sync_set($entity_type, $entity, $legacy_field, $info, $langcode) {
$entity->{$info['field']['field_name']}[$langcode][0]['value'] = $entity->{$legacy_field};
}
/**
* Sync callback for the text with summary field type.
*/
function title_field_text_with_summary_sync_get($entity_type, $entity, $legacy_field, $info, $langcode) {
$value = NULL;
$format = NULL;
$format_key = $info['additional keys']['format'];
$field_name = $info['field']['field_name'];
// Return values only if there is any available to process for the current
// language.
if (!empty($entity->{$field_name}[$langcode]) && is_array($entity->{$field_name}[$langcode])) {
$items = $entity->{$field_name}[$langcode];
$value = !empty($items[0]['value']) ? $items[0]['value'] : NULL;
$format = $entity->{$field_name}[$langcode][0]['format'];
}
return array(
$legacy_field => $value,
$format_key => $format,
);
}
/**
* Sync back callback for the text with summary field type.
*/
function title_field_text_with_summary_sync_set($entity_type, $entity, $legacy_field, $info, $langcode) {
foreach (array('value' => $legacy_field, 'format' => $info['additional keys']['format']) as $column => $name) {
if (isset($entity->{$name})) {
$entity->{$info['field']['field_name']}[$langcode][0][$column] = $entity->{$name};
}
}
}
/**
* Process variables for page.tpl.php.
*/
function title_process_page(&$variables) {
// Ugly but necessary: there is no standardized way to tell if the current
// page is an entity view page. This information should be injected here in
// some form by entity-defining modules.
$entity_types = array(
'comment' => 1,
'node' => 1,
'taxonomy_term' => 2,
);
foreach ($entity_types as $entity_type => $position) {
if ($entity = menu_get_object($entity_type, $position)) {
break;
}
}
if ($entity) {
title_field_replacement_hide_label($entity_type, $entity, $variables, TRUE);
}
}
/**
* Process variables for node.tpl.php.
*/
function title_process_node(&$variables) {
title_field_replacement_hide_label('node', $variables['node'], $variables);
}
/**
* Process variables for taxonomy-term.tpl.php.
*/
function title_process_taxonomy_term(&$variables) {
title_field_replacement_hide_label('taxonomy_term', $variables['term'], $variables);
}
/**
* Process variables for comment.tpl.php.
*/
function title_process_comment(&$variables) {
title_field_replacement_hide_label('comment', $variables['comment'], $variables);
}

View File

@@ -0,0 +1,148 @@
<?php
/**
* @file
* Implement a title field formater.
*/
/**
* Implements hook_field_formatter_info().
*/
function title_field_formatter_info() {
return array(
'title_linked' => array(
'label' => t('Linked and wrapped'),
'field types' => array('text'),
'settings' => array('title_style' => '', 'title_link' => '', 'title_class' => ''),
),
);
}
/**
* Implements hook_field_formatter_settings_form().
*/
function title_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
$settings = $instance['display'][$view_mode]['settings'];
$element = array();
$wrap_tags = array(
'_none' => t('- None -'),
'div' => t('DIV'),
'h1' => t('H1'),
'h2' => t('H2'),
'h3' => t('H3'),
'h4' => t('H4'),
'h5' => t('H5'),
'h6' => t('H6'),
'span' => t('SPAN'),
);
$element['title_style'] = array(
'#title' => t('Wrap title in tag'),
'#type' => 'select',
'#default_value' => !empty($settings['title_style']) ? $settings['title_style'] : '_none',
'#options' => $wrap_tags,
);
$link_types = array(
'content' => t('Content'),
);
$element['title_link'] = array(
'#title' => t('Link title to'),
'#type' => 'select',
'#default_value' => $settings['title_link'],
'#empty_option' => t('Nothing'),
'#options' => $link_types,
);
$element['title_class'] = array(
'#title' => t('Tag classes'),
'#type' => 'textfield',
'#description' => t('A CSS class to use in the wrapper tag for the title.'),
'#default_value' => $settings['title_class'],
'#element_validate' => array('_title_validate_class'),
);
return $element;
}
/**
* Implements hook_field_formatter_settings_summary().
*/
function title_field_formatter_settings_summary($field, $instance, $view_mode) {
$settings = $instance['display'][$view_mode]['settings'];
$summary = array();
$tag = isset($settings['title_style']) && $settings['title_style'] != '' && $settings['title_style'] != '_none' ? $settings['title_style'] : t('- None -');
$summary[] = t('Title wrap tag: @tag', array('@tag' => $tag));
$link_types = array(
'content' => t('Linked to content'),
);
// Display this setting only if field is linked.
if (isset($link_types[$settings['title_link']])) {
$summary[] = $link_types[$settings['title_link']];
}
// Display this setting only if wrapper has a class.
if (isset($settings['title_class']) && $settings['title_class'] != '_none' && $settings['title_class'] != '') {
$summary[] = t('Wrap tag classes: @classes', array('@classes' => $settings['title_class']));
}
return implode('<br />', $summary);
}
/**
* Implements hook_field_formatter_view().
*/
function title_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$settings = $display['settings'];
$output = isset($items[0]) ? $items[0]['safe_value'] : '';
if (!empty($output) && $settings['title_link'] == 'content') {
$uri = entity_uri($entity_type, $entity);
$output = l($output, $uri['path'], array('html' => TRUE));
}
$wrap_tag = empty($settings['title_style']) ? '_none' : $settings['title_style'];
if ($wrap_tag != '_none') {
$variables = array(
'element' => array(
'#tag' => $wrap_tag,
'#value' => $output,
),
);
if (!empty($settings['title_class'])) {
$variables['element']['#attributes'] = array('class' => $settings['title_class']);
}
$output = theme('html_tag', $variables);
}
$element = array(
array(
'#markup' => $output,
),
);
return $element;
}
/**
* Validate that a space-separated list of values are lowercase and appropriate for use as HTML classes.
*
* @see title_field_formatter_settings_form()
*/
function _title_validate_class($element, &$form_state) {
$value = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
$classes = explode(' ', $value);
foreach ($classes as $class) {
if ($class != drupal_html_class($class)) {
form_error($element, t('Wrapper classes contain illegal characters; classes should be lowercase and may contain letters, numbers, and dashes.'));
return;
}
}
}

View File

@@ -0,0 +1,17 @@
name = Title
description = Replaces entity legacy fields with regular fields.
core = 7.x
package = Fields
configure = admin/config/content/title
dependencies[] = system (>7.14)
files[] = title.module
files[] = views/views_handler_title_field.inc
files[] = tests/title.test
; Information added by drupal.org packaging script on 2013-07-24
version = "7.x-1.0-alpha7+5-dev"
core = "7.x"
project = "title"
datestamp = "1374690078"

View File

@@ -0,0 +1,55 @@
<?php
/**
* @file
* Installation functions for the Title module.
*/
/**
* Helper function.
*/
function _title_install_set_weight($weight) {
db_update('system')
->fields(array('weight' => $weight))
->condition('name', 'title')
->execute();
}
/**
* Implements hook_install().
*/
function title_install() {
// Make (reasonably) sure that title_module_implements_alter() is invoked as
// last so we can determine the priority of our hook implementations reliably.
_title_install_set_weight(100);
}
/**
* Implements hook_update_N.
*
* Make sure Title has a very high weight to be able to perform reverse
* synchronization reliably.
*/
function title_update_7001() {
_title_install_set_weight(100);
}
/**
* Implements hook_update_N.
*
* Update title_auto_attach variables to the new format.
*/
function title_update_7002() {
$variables = array();
foreach (variable_get('title_auto_attach', array()) as $variable) {
$pieces = explode(':', $variable);
$variables['title_' . $pieces[0]]['auto_attach'][$pieces[1]] = $pieces[1];
}
foreach ($variables as $name => $value) {
variable_set($name, $value);
}
variable_del('title_auto_attach');
}

View File

@@ -0,0 +1,954 @@
<?php
/**
* @file
* Replaces entity legacy fields with regular fields.
*
* Provides an API and a basic UI to replace legacy pseudo-fields with regular
* fields. The API only offers synchronization between the two data storage
* systems and data replacement on entity load/save. Field definitions have to
* be provided by the modules exploiting the API.
*
* Title implements its own entity description API to describe core legacy
* pseudo-fields:
* - Node: title
* - Taxonomy Term: name, description
* - Comment: subject
*
* @todo: API PHPdocs
*/
module_load_include('inc', 'title', 'title.core');
module_load_include('inc', 'title', 'title.field');
/**
* Implements hook_module_implements_alter().
*/
function title_module_implements_alter(&$implementations, $hook) {
if (isset($implementations['title'])) {
$group = $implementations['title'];
unset($implementations['title']);
switch ($hook) {
// The following hook implementations should be executed as last ones.
case 'entity_info_alter':
case 'entity_presave':
case 'field_attach_presave':
$implementations['title'] = $group;
break;
// Normally Title needs to act as first module to perform synchronization.
default:
$implementations = array('title' => $group) + $implementations;
}
}
}
/**
* Implements hook_entity_info_alter().
*/
function title_entity_info_alter(&$info) {
foreach ($info as $entity_type => $entity_info) {
if (!empty($entity_info['fieldable']) && !empty($info[$entity_type]['field replacement'])) {
foreach ($info[$entity_type]['field replacement'] as $legacy_field => $data) {
// Provide defaults for the replacing field name.
$fr_info = &$info[$entity_type]['field replacement'][$legacy_field];
if (empty($fr_info['field']['field_name'])) {
$fr_info['field']['field_name'] = $legacy_field . '_field';
}
$fr_info['instance']['field_name'] = $fr_info['field']['field_name'];
// Provide defaults for the sync callbacks.
$type = $fr_info['field']['type'];
if (empty($fr_info['callbacks'])) {
$fr_info['callbacks'] = array();
}
$fr_info['callbacks'] += array(
'sync_get' => "title_field_{$type}_sync_get",
'sync_set' => "title_field_{$type}_sync_set",
);
// Support add explicit support for entity_label().
if (isset($entity_info['entity keys']['label']) && $entity_info['entity keys']['label'] == $legacy_field) {
// Store the original label callback for compatibility reasons.
if (isset($info[$entity_type]['label callback'])) {
$info[$entity_type]['label fallback']['title'] = $info[$entity_type]['label callback'];
}
$info[$entity_type]['label callback'] = 'title_entity_label';
$fr_info += array('preprocess_key' => $info[$entity_type]['entity keys']['label']);
}
}
}
}
}
/**
* Return field replacement specific information.
*
* @param $entity_type
* The name of the entity type.
* @param $legacy_field
* (Otional) The legacy field name to be replaced.
*/
function title_field_replacement_info($entity_type, $legacy_field = NULL) {
$info = entity_get_info($entity_type);
if (empty($info['field replacement'])) {
return FALSE;
}
if (isset($legacy_field)) {
return isset($info['field replacement'][$legacy_field]) ? $info['field replacement'][$legacy_field] : FALSE;
}
else {
return $info['field replacement'];
}
}
/**
* Return an entity label value.
*
* @param $entity
* The entity whose label has to be displayed.
* @param $type
* The name of the entity type.
* @param $langcode
* (Optional) The language the entity label has to be displayed in.
*
* @return
* The entity label as a string value.
*/
function title_entity_label($entity, $type, $langcode = NULL) {
$entity_info = entity_get_info($type);
$legacy_field = $entity_info['entity keys']['label'];
$info = $entity_info['field replacement'][$legacy_field];
list(, , $bundle) = entity_extract_ids($type, $entity);
// If field replacement is enabled we use the replacing field value.
if (title_field_replacement_enabled($type, $bundle, $legacy_field)) {
$langcode = field_language($type, $entity, $info['field']['field_name'], $langcode);
$values = $info['callbacks']['sync_get']($type, $entity, $legacy_field, $info, $langcode);
return $values[$legacy_field];
}
// Otherwise if we have a fallback defined we use the original label callback.
elseif (isset($entity_info['label fallback']['title']) && function_exists($entity_info['label fallback']['title'])) {
return $entity_info['label fallback']['title']($entity, $type, $langcode);
}
else {
return (property_exists($entity, $legacy_field)) ? $entity->{$legacy_field} : NULL;
}
}
/**
* Implements hook_entity_presave().
*/
function title_entity_presave($entity, $entity_type) {
$entity_langcode = title_entity_language($entity_type, $entity);
$langcode = $entity_langcode;
// If Entity Translation is enabled and the entity type is transltable,we need
// to check if we have a translation for the current active language. If so we
// need to synchronize the legacy field values into the replacing field
// translations in the active language.
if (module_invoke('entity_translation', 'enabled', $entity_type)) {
$langcode = title_active_language();
$translations = entity_translation_get_handler($entity_type, $entity)->getTranslations();
// If we are removing a translation for the active language we need to skip
// reverse synchronization, as we would store empty values in the original
// replacing fields immediately afterwards.
if (!isset($translations->data[$langcode])) {
$langcode = isset($translations->hook[$langcode]['hook']) && $translations->hook[$langcode]['hook'] == 'delete' ? FALSE : $entity_langcode;
}
}
// Perform reverse synchronization to retain any change in the legacy field
// values. We must avoid doing this twice as we might overwrite the already
// synchronized values, if we are updating an existing entity.
if ($langcode) {
title_entity_sync($entity_type, $entity, $langcode, TRUE);
}
// If we are not dealing with the entity language, we need to synchronize the
// original values into the legacy fields to ensure they are always stored in
// the entity table.
if ($entity_langcode != $langcode) {
list($id, , ) = entity_extract_ids($entity_type, $entity);
$sync = &drupal_static('title_entity_sync', array());
unset($sync[$entity_type][$id]);
title_entity_sync($entity_type, $entity, $entity_langcode);
}
}
/**
* Implements hook_field_attach_update().
*/
function title_field_attach_update($entity_type, $entity) {
// Reset the field_attach_presave static cache so that subsequent saves work
// correctly.
$sync = &drupal_static('title_field_attach_presave', array());
list($id, , ) = entity_extract_ids($entity_type, $entity);
unset($sync[$entity_type][$id]);
// Immediately after saving the entity we need to ensure that the legacy field
// holds a value corresponding to the current active language, as it were
// just loaded.
title_entity_sync($entity_type, $entity);
}
/**
* Implements hook_field_attach_load().
*
* Synchronization must be performed as early as possible to prevent other code
* from accessing replaced fields before they get their actual value.
*
* @see title_entity_load()
*/
function title_field_attach_load($entity_type, $entities, $age, $options) {
// Allow values to re-sync when field_attach_load_revision() is called.
if ($age == FIELD_LOAD_REVISION) {
title_entity_sync_static_reset($entity_type, array_keys($entities));
}
title_entity_load($entities, $entity_type);
}
/**
* Implements hook_entity_load().
*
* Since the result of field_attach_load() is cached, synchronization must be
* performed also here to ensure that there is always the correct value in the
* replaced fields.
*/
function title_entity_load($entities, $type) {
foreach ($entities as &$entity) {
// Synchronize values from the regular field unless we are intializing it.
title_entity_sync($type, $entity, NULL, !empty($GLOBALS['title_field_replacement_init']));
}
}
/**
* Implements hook_entitycache_load().
*
* Entity cache might cache the entire $entity object, in which case
* synchronization will not be performed on entity load.
*/
function title_entitycache_load($entities, $type) {
title_entity_load($entities, $type);
}
/**
* Implements hook_entitycache_reset().
*
* When the entity cache is reset the field sync has to be done again.
*/
function title_entitycache_reset($ids, $entity_type) {
title_entity_sync_static_reset($entity_type, $ids);
}
/**
* Implements hook_entity_prepare_view().
*
* On load synchronization is performed using the current display language. A
* different language might be specified while viewing the entity in which case
* synchronization must be performed again.
*/
function title_entity_prepare_view($entities, $type, $langcode) {
foreach ($entities as &$entity) {
title_entity_sync($type, $entity, $langcode);
}
}
/**
* Check whether field replacement is enabled for the given field.
*
* @param $entity_type
* The type of $entity.
* @param $bundle
* The bundle the legacy field belongs to.
* @param $legacy_field
* The name of the legacy field to be replaced.
*
* @return
* TRUE if field replacement is enabled for the given field, FALSE otherwise.
*/
function title_field_replacement_enabled($entity_type, $bundle, $legacy_field) {
$info = title_field_replacement_info($entity_type, $legacy_field);
if (!empty($info['field']['field_name'])) {
$instance = field_info_instance($entity_type, $info['field']['field_name'], $bundle);
}
return !empty($instance);
}
/**
* Toggle field replacement for the given field.
*
* @param $entity_type
* The name of the entity type.
* @param $bundle
* The bundle the legacy field belongs to.
* @param $legacy_field
* The name of the legacy field to be replaced.
*/
function title_field_replacement_toggle($entity_type, $bundle, $legacy_field) {
$info = title_field_replacement_info($entity_type, $legacy_field);
if (!$info) {
return;
}
$field_name = $info['field']['field_name'];
$instance = field_info_instance($entity_type, $field_name, $bundle);
if (empty($instance)) {
$options = variable_get('title_' . $entity_type, array());
$field = field_info_field($field_name);
if (empty($field)) {
field_create_field($info['field']);
}
$info['instance']['entity_type'] = $entity_type;
$info['instance']['bundle'] = $bundle;
$info['instance']['settings']['hide_label']['page'] = isset($options['hide_label']['page']) ? $options['hide_label']['page'] : FALSE;
$info['instance']['settings']['hide_label']['entity'] = isset($options['hide_label']['entity']) ? $options['hide_label']['entity'] : FALSE;
field_create_instance($info['instance']);
return TRUE;
}
else {
field_delete_instance($instance);
return FALSE;
}
}
/**
* Set a batch process to initialize replacing field values.
*
* @param $entity_type
* The type of $entity.
* @param $bundle
* The bundle the legacy field belongs to.
* @param $legacy_field
* The name of the legacy field to be replaced.
*/
function title_field_replacement_batch_set($entity_type, $bundle, $legacy_field) {
$batch = array(
'title' => t('Replacing field values for %field', array('%field' => $legacy_field)),
'operations' => array(
array('title_field_replacement_batch', array($entity_type, $bundle, $legacy_field)),
),
);
batch_set($batch);
}
/**
* Batch operation: initialize a batch of replacing field values.
*/
function title_field_replacement_batch($entity_type, $bundle, $legacy_field, &$context) {
$info = entity_get_info($entity_type);
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', $entity_type);
// There is no general way to tell if an entity supports bundle conditions
// (for instance taxonomy terms and comments do not), hence we may need to
// loop over all the entities of the given type.
if (!empty($info['efq bundle conditions'])) {
$query->entityCondition('bundle', $bundle);
}
if (empty($context['sandbox'])) {
$count_query = clone $query;
$total = $count_query
->count()
->execute();
$context['sandbox']['steps'] = 0;
$context['sandbox']['progress'] = 0;
$context['sandbox']['total'] = $total;
}
$step = variable_get('title_field_replacement_batch_size', 5);
$start = $step * $context['sandbox']['steps']++;
$results = $query
->entityCondition('entity_type', $entity_type)
->range($start, $step)
->execute();
if (!empty($results[$entity_type])) {
$ids = array_keys($results[$entity_type]);
title_field_replacement_init($entity_type, $bundle, $legacy_field, $ids);
$context['sandbox']['progress'] += count($ids);
}
if ($context['sandbox']['progress'] != $context['sandbox']['total']) {
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['total'];
}
}
/**
* Initialize a batch of replacing field values.
*
* @param $entity_type
* The type of $entity.
* @param $bundle
* The bundle the legacy field belongs to.
* @param $legacy_field
* The name of the legacy field to be replaced.
* @param $ids
* An array of entity IDs.
*
* @return
* The number of entities processed.
*/
function title_field_replacement_init($entity_type, $bundle, $legacy_field, $ids) {
$GLOBALS['title_field_replacement_init'] = TRUE;
$entities = entity_load($entity_type, $ids);
foreach ($entities as $id => $entity) {
list(, , $entity_bundle) = entity_extract_ids($entity_type, $entity);
if ($entity_bundle == $bundle) {
field_attach_presave($entity_type, $entity);
field_attach_update($entity_type, $entity);
}
}
unset($GLOBALS['title_field_replacement_init']);
}
/**
* Synchronize replaced fields with the regular field values.
*
* @param $entity_type
* The name of the entity type.
* @param $entity
* The entity to work with.
* @param $set
* Specifies the direction synchronization must be performed.
*/
function title_entity_sync($entity_type, &$entity, $langcode = NULL, $set = FALSE) {
$sync = &drupal_static(__FUNCTION__, array());
list($id, , $bundle) = entity_extract_ids($entity_type, $entity);
if (!isset($langcode)) {
$langcode = $set ? title_entity_language($entity_type, $entity) : title_active_language();
}
// We do not need to perform synchronization more than once.
if (!$set && !empty($id) && !empty($sync[$entity_type][$id][$langcode][$set])) {
return;
}
$sync[$entity_type][$id][$langcode][$set] = TRUE;
$fr_info = title_field_replacement_info($entity_type);
if ($fr_info) {
foreach ($fr_info as $legacy_field => $info) {
if (title_field_replacement_enabled($entity_type, $bundle, $legacy_field)) {
$function = 'title_field_sync_' . ($set ? 'set' : 'get');
$function($entity_type, $entity, $legacy_field, $info, $langcode);
}
}
}
}
/**
* Reset the list of entities whose fields have already been synchronized.
*
* @param $entity_type
* The name of the entity type.
* @param $entity_ids
* Either an array of entity IDs to reset or NULL to reset all.
*/
function title_entity_sync_static_reset($entity_type, $entity_ids = NULL) {
$sync = &drupal_static('title_entity_sync', array());
if (is_array($entity_ids)) {
foreach ($entity_ids as $id) {
unset($sync[$entity_type][$id]);
}
}
else {
unset($sync[$entity_type]);
}
}
/**
* Synchronize a single legacy field with its regular field value.
*
* @param $entity_type
* The name of the entity type.
* @param $entity
* The entity to work with.
* @param $legacy_field
* The name of the legacy field to be replaced.
* @param $field_name
* The regular field to use as source value.
* @param $info
* Field replacement information for the given entity.
* @param $langcode
* The field language to use for the source value.
*/
function title_field_sync_get($entity_type, $entity, $legacy_field, $info, $langcode = NULL) {
if (property_exists($entity, $legacy_field)) {
// Save the legacy field value to LEGACY_FIELD_NAME_original.
$entity->{$legacy_field . '_original'} = $entity->{$legacy_field};
// Find out the actual language to use (field might be untranslatable).
$langcode = field_language($entity_type, $entity, $info['field']['field_name'], $langcode);
$values = $info['callbacks']['sync_get']($entity_type, $entity, $legacy_field, $info, $langcode);
foreach ($values as $name => $value) {
$entity->{$name} = $value;
}
}
}
/**
* Synchronize a single regular field from its legacy field value.
*
* @param $entity_type
* The name of the entity type.
* @param $entity
* The entity to work with.
* @param $legacy_field
* The name of the legacy field to be replaced.
* @param $field_name
* The regular field to use as source value.
* @param $info
* Field replacement information for the given entity.
* @param $langcode
* The field language to use for the target value.
*/
function title_field_sync_set($entity_type, $entity, $legacy_field, $info, $langcode) {
if (property_exists($entity, $legacy_field)) {
// Find out the actual language to use (field might be untranslatable).
$field = field_info_field($info['field']['field_name']);
$langcode = field_is_translatable($entity_type, $field) ? $langcode : LANGUAGE_NONE;
$info['callbacks']['sync_set']($entity_type, $entity, $legacy_field, $info, $langcode);
}
}
/**
* Returns and optionally stores the active language.
*
* @param string $langcode
* (optional) The active language to be set. If none is provided the active
* language is just returned.
*
* @return string
* The active language code. Defaults to the current content language.
*/
function title_active_language($langcode = NULL) {
static $drupal_static_fast;
if (!isset($drupal_static_fast)) {
$drupal_static_fast['active_language'] = &drupal_static(__FUNCTION__);
}
$active_langcode = &$drupal_static_fast['active_language'];
if (isset($langcode)) {
$active_langcode = $langcode;
}
if (empty($active_langcode)) {
$active_langcode = $GLOBALS['language_content']->language;
}
return $active_langcode;
}
/**
* Provide the original entity language.
*
* If a language property is defined for the current entity we synchronize the
* field value using the entity language, otherwise we fall back to
* LANGUAGE_NONE.
*
* @param $entity_type
* @param $entity
*
* @return
* A language code
*/
function title_entity_language($entity_type, $entity) {
if (module_exists('entity_translation') && entity_translation_enabled($entity_type)) {
$handler = entity_translation_get_handler($entity_type, $entity, TRUE);
$langcode = $handler->getLanguage();
}
else {
$langcode = entity_language($entity_type, $entity);
}
return !empty($langcode) ? $langcode : LANGUAGE_NONE;
}
/**
* Implements hook_field_attach_form().
*
* Hide legacy field widgets on the assumption that this is always called on
* fieldable entity forms.
*/
function title_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
$fr_info = title_field_replacement_info($entity_type);
if (!empty($fr_info)) {
foreach ($fr_info as $legacy_field => $info) {
if (isset($form[$legacy_field]) && title_field_replacement_enabled($entity_type, $bundle, $legacy_field)) {
// Inherit the access from the title widget, so that other modules
// restricting access to it keep working.
if (isset($form[$legacy_field]['#access'])) {
$form[$info['field']['field_name']]['#access'] = $form[$legacy_field]['#access'];
}
// Restrict access to the legacy field form element and mark it as
// replaced.
$form[$legacy_field]['#access'] = FALSE;
$form[$legacy_field]['#field_replacement'] = TRUE;
}
}
}
}
/**
* Implements hook_field_attach_submit().
*
* Synchronize submitted field values into the corresponding legacy fields.
*/
function title_field_attach_submit($entity_type, $entity, $form, &$form_state) {
$fr_info = title_field_replacement_info($entity_type);
if (!empty($fr_info)) {
// We copy (rather than reference) the values from $form_state because the
// subsequent call to drupal_array_get_nested_value() is destructive and
// will affect other hooks relying on data in $form_state. At the end, we
// copy any modified value back into the $form_state array using
// drupal_array_set_nested_value().
$values = $form_state['values'];
$values = drupal_array_get_nested_value($values, $form['#parents']);
$langcode = entity_language($entity_type, $entity);
foreach ($fr_info as $legacy_field => $info) {
if (!empty($form[$legacy_field]['#field_replacement'])) {
$field_name = $info['field']['field_name'];
// Give a chance to operate on submitted values either.
if (!empty($info['callbacks']['submit'])) {
$info['callbacks']['submit']($entity_type, $entity, $legacy_field, $info, $langcode, $values);
}
drupal_static_reset('field_language');
title_field_sync_get($entity_type, $entity, $legacy_field, $info, $langcode);
}
}
drupal_array_set_nested_value($form_state['values'], $form['#parents'], $values);
}
}
/**
* Implements of hook_menu().
*/
function title_menu() {
$items = array();
foreach (entity_get_info() as $entity_type => $entity_info) {
if (!empty($entity_info['field replacement'])) {
foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
// Blindly taken from field_ui_menu().
if (isset($bundle_info['admin'])) {
$path = $bundle_info['admin']['path'];
if (isset($bundle_info['admin']['bundle argument'])) {
$bundle_arg = $bundle_info['admin']['bundle argument'];
}
else {
$bundle_arg = $bundle_name;
}
$access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
$access += array(
'access callback' => 'user_access',
'access arguments' => array('administer site configuration'),
);
$path = "$path/fields/replace/%";
$field_arg = count(explode('/', $path)) - 1;
$items[$path] = array(
'load arguments' => array(),
'title' => 'Replace fields',
'page callback' => 'drupal_get_form',
'page arguments' => array('title_field_replacement_form', $entity_type, $bundle_arg, $field_arg),
'file' => 'title.admin.inc',
) + $access;
}
}
}
}
$items['admin/config/content/title'] = array(
'title' => 'Title settings',
'description' => 'Settings for the Title module.',
'page callback' => 'drupal_get_form',
'page arguments' => array('title_admin_settings_form'),
'access arguments' => array('administer site configuration'),
'file' => 'title.admin.inc',
);
return $items;
}
/**
* Implements hook help.
*/
function title_help($path, $arg) {
switch ($path) {
case 'admin/config/content/title':
return '<p>' . t('The settings below allow to configure the <em>default</em> settings to be used when creating new replacing fields. It is even possibile to configure them so that the selected fields are created automatically when a new bundle is created.') . '</p>';
}
}
/**
* Implements hook_field_extra_fields_alter().
*/
function title_field_extra_fields_alter(&$info) {
$entity_info = entity_get_info();
foreach ($info as $entity_type => $bundles) {
foreach ($bundles as $bundle_name => $bundle) {
if (!empty($entity_info[$entity_type]['field replacement'])) {
foreach ($entity_info[$entity_type]['field replacement'] as $field_name => $field_replacement_info) {
if (title_field_replacement_enabled($entity_type, $bundle_name, $field_name)) {
// Remove the replaced legacy field.
unset($info[$entity_type][$bundle_name]['form'][$field_name], $info[$entity_type][$bundle_name]['display'][$field_name]);
}
}
}
}
}
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function title_form_field_ui_field_overview_form_alter(&$form, &$form_state) {
module_load_include('inc', 'title', 'title.admin');
title_form_field_ui_overview($form, $form_state);
}
/**
* Implements hook_tokens_alter().
*
* Make sure tokens are properly translated.
*/
function title_tokens_alter(array &$replacements, array $context) {
$mapping = &drupal_static(__FUNCTION__);
if (empty($mapping)) {
foreach (entity_get_info() as $entity_type => $info) {
if (!empty($info['token type'])) {
$mapping[$info['token type']] = $entity_type;
}
}
}
if (isset($mapping[$context['type']])) {
$entity_type = $mapping[$context['type']];
$fr_info = title_field_replacement_info($entity_type);
if ($fr_info && !empty($context['data'][$context['type']])) {
$entity = $context['data'][$context['type']];
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
$options = $context['options'];
// Since Title tokens are mostly used in storage contexts we default to
// the current working language, that is the entity language. Modules
// using Title tokens in display contexts need to specify the current
// display language.
$langcode = isset($options['language']) ? $options['language']->language : entity_language($entity_type, $entity);
if ($fr_info) {
foreach ($fr_info as $legacy_field => $info) {
if (title_field_replacement_enabled($entity_type, $bundle, $legacy_field)) {
if (isset($context['tokens'][$legacy_field])) {
$langcode = field_language($entity_type, $entity, $info['field']['field_name'], $langcode);
$values = $info['callbacks']['sync_get']($entity_type, $entity, $legacy_field, $info, $langcode);
$item = $values[$legacy_field];
if (!empty($item)) {
if (is_array($item)) {
$item = reset($item);
}
$replacements[$context['tokens'][$legacy_field]] = $item;
}
}
}
}
}
}
}
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function title_form_field_ui_field_edit_form_alter(&$form, $form_state) {
$instance = $form['#instance'];
$entity_type = $instance['entity_type'];
if (title_field_replacement_is_label($entity_type, $instance['field_name'])) {
$info = entity_get_info($entity_type);
$form['instance']['settings']['hide_label'] = _title_hide_label_widget($instance['settings'], $info['label']);
}
}
/**
* Returns the hide label form widget.
*/
function _title_hide_label_widget($default, $entity_label) {
return array(
'#type' => 'checkboxes',
'#title' => t('Label replacement'),
'#description' => t('Check these options if you wish to hide the main page title or each label when displaying multiple items of type %entity_label.', array('%entity_label' => $entity_label)),
'#default_value' => !empty($default['hide_label']) ? $default['hide_label'] : array(),
'#options' => array(
'page' => t('Hide page title'),
'entity' => t('Hide label in %entity_label listings', array('%entity_label' => drupal_strtolower($entity_label))),
),
);
}
/**
* Checks whether the given field name is a replaced entity label.
*
* @param $entity_type
* The name of the entity type.
* @param $field_name
* The replacing field name.
*
* @return
* TRUE id the give field is replacing the entity label, FALSE otherwise.
*/
function title_field_replacement_is_label($entity_type, $field_name) {
$label = FALSE;
$legacy_field = title_field_replacement_get_legacy_field($entity_type, $field_name);
if ($legacy_field) {
$info = entity_get_info($entity_type);
$label = $legacy_field == $info['entity keys']['label'];
}
return $label;
}
/**
* Returns the legacy field replaced by the given field name.
*
* @param $entity_type
* The name of the entity type.
* @param $field_name
* The replacing field name.
*
* @return
* The replaced legacy field name or FALSE if none available.
*/
function title_field_replacement_get_legacy_field($entity_type, $field_name) {
$result = FALSE;
$fr_info = title_field_replacement_info($entity_type);
if ($fr_info) {
foreach ($fr_info as $legacy_field => $info) {
if ($info['field']['field_name'] == $field_name) {
$result = $legacy_field;
break;
}
}
}
return $result;
}
/**
* Returns the field instance replacing the given entity type's label.
*
* @param $entity_type
* The name of the entity type.
* @param $bundle
* The name of the bundle the instance is attached to.
*
* @return
* The field instance replacing the label or FALSE if none available.
*/
function title_field_replacement_get_label_field($entity_type, $bundle) {
$instance = FALSE;
$info = entity_get_info($entity_type);
if (!empty($info['field replacement'])) {
$fr_info = $info['field replacement'];
$legacy_field = $info['entity keys']['label'];
if (!empty($fr_info[$legacy_field]['field'])) {
$instance = field_info_instance($entity_type, $fr_info[$legacy_field]['field']['field_name'], $bundle);
}
}
return $instance;
}
/**
* Hides the label from the given variables.
*
* @param $entity_type
* The name of the entity type.
* @param $entity
* The entity to work with.
* @param $vaiables
* A reference to the variables array related to the template being processed.
* @param $page
* (optional) The current render phase: page or entity. Defaults to entity.
*/
function title_field_replacement_hide_label($entity_type, $entity, &$variables, $page = FALSE) {
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
$instance = title_field_replacement_get_label_field($entity_type, $bundle);
$settings_key = $page ? 'page' : 'entity';
if (!empty($instance['settings']['hide_label'][$settings_key])) {
// If no key is passed default to the label one.
if ($page) {
$key = 'title';
}
else {
$info = entity_get_info($entity_type);
$key = $info['field replacement'][$info['entity keys']['label']]['preprocess_key'];
}
// We cannot simply unset the variable value since this may cause templates
// to throw notices.
$variables[$key] = FALSE;
}
}
/**
* Implements hook_views_api().
*/
function title_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'title') . '/views',
);
}
/**
* Implements hook_field_attach_create_bundle().
*
* Automatically attach the replacement field to the new bundle.
*/
function title_field_attach_create_bundle($entity_type, $bundle) {
$entity_info = entity_get_info($entity_type);
if (empty($entity_info['field replacement'])) {
return;
}
$options = variable_get('title_' . $entity_type, array());
foreach (array_keys($entity_info['field replacement']) as $legacy_field) {
if (empty($options['auto_attach'][$legacy_field])) {
continue;
}
// Do not continue if the replacement field already exists.
$field_name = $entity_info['field replacement'][$legacy_field]['field']['field_name'];
if (field_info_instance($entity_type, $field_name, $bundle)) {
continue;
}
title_field_replacement_toggle($entity_type, $bundle, $legacy_field);
$instance = field_info_instance($entity_type, $field_name, $bundle);
if ($instance) {
$params = array(
'@entity_label' => drupal_strtolower($entity_info['label']),
'%field_name' => $instance['label'],
);
drupal_set_message(t('The @entity_label %field_name field was automatically replaced.', $params));
}
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* Implements hook_views_data_alter().
*
* Replace field default handler (views_handler_field_field).
*/
function title_field_views_data_alter(&$data) {
foreach (entity_get_info() as $entity_type => $entity_info) {
$replacements = title_field_replacement_info($entity_type);
if ($replacements) {
foreach ($replacements as $replacement) {
$field = field_info_field($replacement['field']['field_name']);
$table = _field_sql_storage_tablename($field);
if (isset($data[$table][$field['field_name']])) {
$data[$table][$field['field_name']]['field']['handler'] = 'views_handler_title_field';
}
}
}
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* Field handler to present field title with link to the entity.
*
* @ingroup views_field_handlers
*/
class views_handler_title_field extends views_handler_field_field {
function option_definition() {
$options = parent::option_definition();
$options['link_to_entity'] = array('default' => isset($this->definition['link_to_entity default']) ? $this->definition['link_to_entity default'] : FALSE);
return $options;
}
/**
* Provide link to entity option.
*/
function options_form(&$form, &$form_state) {
$form['link_to_entity'] = array(
'#title' => t('Link this field to the original entity'),
'#description' => t("Enable to override this field's links."),
'#type' => 'checkbox',
'#default_value' => !empty($this->options['link_to_entity']),
);
parent::options_form($form, $form_state);
}
function advanced_render($values) {
$this->original_values = $values;
return parent::advanced_render($values);
}
function render_item($count, $item) {
if (!empty($this->options['link_to_entity'])) {
$values = $this->original_values;
$entity_type = $this->definition['entity_tables'][$this->base_table];
$entity_info = entity_get_info($entity_type);
$key = $entity_info['entity keys']['id'];
if (!empty($values->_field_data[$key]['entity'])) {
$entity = $values->_field_data[$key]['entity'];
$uri = entity_uri($entity_type, $entity);
$this->options['alter']['make_link'] = TRUE;
$this->options['alter']['path'] = $uri['path'];
}
}
return parent::render_item($count, $item);
}
}