updated admin_menu, entity_translation, addressfield, addressfield_token, autocomplete_deluxe

This commit is contained in:
Bachir Soussi Chiadmi 2019-05-13 17:41:56 +02:00
parent 472762edfa
commit 33210e10f2
65 changed files with 3124 additions and 700 deletions

View File

@ -1,65 +1,84 @@
CONTENTS OF THIS FILE
---------------------
-- SUMMARY --
* Introduction
* Requirements
* Installation
* Configuration
* Customization
* Troubleshooting
* FAQ
* Maintainers
INTRODUCTION
------------
The Administration menu module displays the entire administrative menu tree (and
most local tasks) in a drop-down menu, providing administrators one- or
two-click access to most pages. Other modules may also add menu links to the
menu using hook_admin_menu_output_alter().
For a full description of the module, visit the project page:
http://drupal.org/project/admin_menu
For a full description of the project visit the project page:
http://drupal.org/project/admin_menu
To submit bug reports and feature suggestions, or to track changes:
http://drupal.org/project/issues/admin_menu
http://drupal.org/project/issues/admin_menu
-- REQUIREMENTS --
REQUIREMENTS
------------
None.
No special requirements
-- INSTALLATION --
INSTALLATION
------------
* Install as usual, see http://drupal.org/node/895232 for further information.
Install as you would normally install a contributed Drupal. See:
https://drupal.org/documentation/install/modules-themes/modules-7 for further
information.
* You likely want to disable Toolbar module, since its output clashes with
Administration menu.
* You likely want to disable Toolbar module, since its output clashes with
Administration menu.
-- CONFIGURATION --
CONFIGURATION
-------------
* Configure user permissions in Administration » People » Permissions:
* Configure user permissions in Administration » People » Permissions:
- Use the administration pages and help (System module)
- Use the administration pages and help (System module)
The top-level administration categories require this permission to be
accessible. The administration menu will be empty unless this permission is
granted.
The top-level administration categories require this permission to be
accessible. The administration menu will be empty unless this permission is
granted.
- Access administration menu
- Access administration menu
Users in roles with the "Access administration menu" permission will see
the administration menu at the top of each page.
Users in roles with the "Access administration menu" permission will see
the administration menu at the top of each page.
- Display Drupal links
- Display Drupal links
Users in roles with the "Display drupal links" permission will receive
links to drupal.org issue queues for all enabled contributed modules. The
issue queue links appear under the administration menu icon.
Users in roles with the "Display drupal links" permission will receive
links to drupal.org issue queues for all enabled contributed modules. The
issue queue links appear under the administration menu icon.
Note that the menu items displayed in the administration menu depend on the
actual permissions of the viewing user. For example, the "People" menu item
is not displayed to a user who is not a member of a role with the "Administer
users" permission.
Note that the menu items displayed in the administration menu depend on the
actual permissions of the viewing user. For example, the "People" menu item
is not displayed to a user who is not a member of a role with the
"Administer users" permission.
* Customize the menu settings in Administration » Configuration and modules »
Administration » Administration menu.
* Customize the menu settings in Administration » Configuration and modules »
Administration » Administration menu.
* To prevent administrative menu items from appearing twice, you may hide the
"Management" menu block.
* To prevent administrative menu items from appearing twice, you may hide the
"Management" menu block.
-- CUSTOMIZATION --
CUSTOMIZATION
-------------
* To override the default administration menu icon, you may:
@ -82,12 +101,13 @@ None.
body #admin-menu { font-size: 10px; }
-- TROUBLESHOOTING --
TROUBLESHOOTING
-------------
* If the menu does not display, check the following:
- Are the "Access administration menu" and "Use the administration pages and help"
permissions enabled for the appropriate roles?
- Are the "Access administration menu" and "Use the administration pages and
help" permissions enabled for the appropriate roles?
- Does html.tpl.php of your theme output the $page_bottom variable?
@ -99,90 +119,102 @@ None.
See http://drupal.org/node/195386 for further information.
-- FAQ --
FAQ
---
Q: When the administration menu module is enabled, blank space is added to the
bottom of my theme. Why?
Q: When the administration menu module is enabled, blank space is added to the
bottom of my theme. Why?
A: This is caused by a long list of links to module issue queues at Drupal.org.
Use Administer >> User management >> Permissions to disable the "display
drupal links" permission for all appropriate roles. Note that since UID 1
automatically receives all permissions, the list of issue queue links cannot
be disabled for UID 1.
A: This is caused by a long list of links to module issue queues at Drupal.org.
Use Administer >> User management >> Permissions to disable the "display
drupal links" permission for all appropriate roles. Note that since UID 1
automatically receives all permissions, the list of issue queue links cannot
be disabled for UID 1.
Q: After upgrading to 6.x-1.x, the menu disappeared. Why?
Q: After upgrading to 6.x-1.x, the menu disappeared. Why?
A: You may need to regenerate your menu. Visit
http://example.com/admin/build/modules to regenerate your menu (substitute
your site name for example.com).
A: You may need to regenerate your menu. Visit
http://example.com/admin/build/modules to regenerate your menu (substitute
your site name for example.com).
Q: Can I configure the administration menu module to display another menu (like
the Navigation menu, for instance)?
Q: Can I configure the administration menu module to display another menu (like
the Navigation menu, for instance)?
A: No. As the name implies, administration menu module is for administrative
menu links only. However, you can copy and paste the contents of
admin_menu.css into your theme's stylesheet and replace #admin-menu with any
other menu block id (#block-menu-1, for example).
A: No. As the name implies, administration menu module is for administrative
menu links only. However, you can copy and paste the contents of
admin_menu.css into your theme's stylesheet and replace #admin-menu with any
other menu block id (#block-menu-1, for example).
Q: Sometimes, the user counter displays a lot of anonymous users, but no spike
of users or requests appear in Google Analytics or other tracking tools.
Q: Sometimes, the user counter displays a lot of anonymous users, but no spike
of users or requests appear in Google Analytics or other tracking tools.
A: If your site was concurrently spidered by search-engine robots, it may have
a significant number of anonymous users for a short time. Most web tracking
tools like Google Analytics automatically filter out these requests.
A: If your site was concurrently spidered by search-engine robots, it may have
a significant number of anonymous users for a short time. Most web tracking
tools like Google Analytics automatically filter out these requests.
Q: I enabled "Aggregate and compress CSS files", but admin_menu.css is still
there. Is this normal?
Q: I enabled "Aggregate and compress CSS files", but admin_menu.css is still
there. Is this normal?
A: Yes, this is the intended behavior. the administration menu module only loads
its stylesheet as needed (i.e., on page requests by logged-on, administrative
users).
A: Yes, this is the intended behavior. the administration menu module only
loads its stylesheet as needed (i.e., on page requests by logged-on,
administrative users).
Q: Why are sub-menus not visible in Opera?
Q: Why are sub-menus not visible in Opera?
A: In the Opera browser preferences under "web pages" there is an option to fit
to width. By disabling this option, sub-menus in the administration menu
should appear.
A: In the Opera browser preferences under "web pages" there is an option to fit
to width. By disabling this option, sub-menus in the administration menu
should appear.
Q: How can the administration menu be hidden on certain pages?
Q: How can the administration menu be hidden on certain pages?
A: You can suppress it by simply calling the following function in PHP:
A: You can suppress it by simply calling the following function in PHP:
module_invoke('admin_menu', 'suppress');
module_invoke('admin_menu', 'suppress');
However, this needs to happen as early as possible in the page request, so
placing it in the theming layer (resp. a page template file) is too late.
Ideally, the function is called in hook_init() in a custom module. If you do
not have a custom module, placing it into some conditional code at the top of
template.php may work out, too.
However, this needs to happen as early as possible in the page request, so
placing it in the theming layer (resp. a page template file) is too late.
Ideally, the function is called in hook_init() in a custom module. If you
do not have a custom module, placing it into some conditional code at the
top of template.php may work out, too.
-- CONTACT --
Q: What does the "Administration Development Tools" module do?
A: The Administration Development Tools adds a jQuery Debugger which allows
a developer to debug and inspect arbitrary data/variables in Firebug's
console, and also to access them again in the global window object
(optionally using a named identifier, e.g. window.debug.myValue).
Chainable via jQuery. Especially useful for re-accessing and debugging
selected data via Firebug's console.
MAINTAINERS
-----------
Current maintainers:
* Daniel F. Kudwien (sun) - http://drupal.org/user/54136
* Peter Wolanin (pwolanin) - http://drupal.org/user/49851
* Stefan M. Kudwien (smk-ka) - http://drupal.org/user/48898
* Dave Reid (Dave Reid) - http://drupal.org/user/53892
* Daniel F. Kudwien (sun) - http://drupal.org/user/54136
* Peter Wolanin (pwolanin) - http://drupal.org/user/49851
* Stefan M. Kudwien (smk-ka) - http://drupal.org/user/48898
* Dave Reid (Dave Reid) - http://drupal.org/user/53892
* Truls S. Yggeseth (truls1502) - http://drupal.org/user/325866
* Sebastian Siemssen (fubhy) - https://www.drupal.org/user/761344
Major rewrite for Drupal 6 by Peter Wolanin (pwolanin).
This project has been sponsored by:
* UNLEASHED MIND
Specialized in consulting and planning of Drupal powered sites, UNLEASHED
MIND offers installation, development, theming, customization, and hosting
to get you started. Visit http://www.unleashedmind.com for more information.
* UNLEASHED MIND
Specialized in consulting and planning of Drupal powered sites, UNLEASHED
MIND offers installation, development, theming, customization, and hosting
to get you started. Visit http://www.unleashedmind.com for more information.
* Lullabot
Friendly Drupal experts providing professional consulting & education
services. Visit http://www.lullabot.com for more information.
* Acquia
Commercially Supported Drupal. Visit http://acquia.com for more information.
* Lullabot
Friendly Drupal experts providing professional consulting & education
services. Visit http://www.lullabot.com for more information.
* Acquia
Commercially Supported Drupal. Visit http://acquia.com for more information.

View File

@ -4,9 +4,8 @@ package = Administration
core = 7.x
scripts[] = admin_devel.js
; Information added by Drupal.org packaging script on 2014-12-19
version = "7.x-3.0-rc5"
; Information added by Drupal.org packaging script on 2018-12-03
version = "7.x-3.0-rc6"
core = "7.x"
project = "admin_menu"
datestamp = "1419029284"
datestamp = "1543859284"

View File

@ -1,4 +1,3 @@
#admin-menu {
text-align: right;
}
@ -25,7 +24,7 @@
border-right: 0;
}
#admin-menu .dropdown .admin-menu-tab a {
border-left: 1px solid #52565E;
border-left: 1px solid #52565e;
border-right: 0;
}
#admin-menu .dropdown li li a {
@ -42,13 +41,13 @@
/* Second-level lists */
#admin-menu .dropdown li ul {
left: auto;
right: -999em;
right: auto;
}
/* Third-and-above-level lists */
#admin-menu .dropdown li li.expandable ul {
margin-left: 0;
margin-right: 160px;
margin-left: 0 !important;
margin-right: 160px !important;
}
/* Lists nested under hovered list items */

View File

@ -1,4 +1,8 @@
(function($) {
/**
* @file
*/
(function ($) {
/**
* Live preview of Administration menu components.
@ -45,9 +49,9 @@ Drupal.behaviors.adminMenuPermissionsSetupHelp = {
// Figure out which is the other, check whether it still disabled,
// and if so, ask whether to auto-enable it.
var other = (this == $admin[index] ? $menu[index] : $admin[index]);
if (!other.checked && confirm(Drupal.t('Also allow !name role to !permission?', {
'!name': $roles[index].textContent,
'!permission': (this == $admin[index] ? menuPermission : adminPermission)
if (!other.checked && confirm(Drupal.t('Also allow @name role to @permission?', {
'@name': $roles[index].textContent,
'@permission': (this == $admin[index] ? menuPermission : adminPermission)
}))) {
other.checked = 'checked';
}

View File

@ -1,4 +1,3 @@
/**
* @file
* Administration menu color override.
@ -17,7 +16,7 @@
border-right-color: #a91f1f;
}
#admin-menu ul li.admin-menu-tab a {
border-right-color: #52565E;
border-right-color: #52565e;
}
#admin-menu li li a {
border-top-color: #801f1f;

View File

@ -1,4 +1,3 @@
/**
* @file
* Administration menu.
@ -16,6 +15,7 @@
position: absolute;
text-align: left;
top: 0;
height: 30px;
width: 100%;
}
#admin-menu-wrapper {
@ -62,7 +62,7 @@ body.admin-menu {
#admin-menu li > span {
background: transparent none;
border: none;
color: #EEE;
color: #eee;
font-weight: normal;
text-align: left; /* LTR */
text-decoration: none;
@ -74,7 +74,7 @@ body.admin-menu {
padding: 4px 8px;
}
#admin-menu .dropdown .admin-menu-tab a {
border-right: 1px solid #52565E; /* LTR */
border-right: 1px solid #52565e; /* LTR */
}
#admin-menu .dropdown li li a {
border-right: none; /* LTR */
@ -147,7 +147,7 @@ body.admin-menu {
/* Second-and-more-level hovering */
#admin-menu .dropdown li li.expandable {
background: #45454A url(images/arrow.png) no-repeat 145px 6px;
background: #45454a url(images/arrow.png) no-repeat 145px 6px;
}
#admin-menu .dropdown li li:hover {
background-color: #111;
@ -155,19 +155,19 @@ body.admin-menu {
#admin-menu .dropdown li li:hover a,
#admin-menu .dropdown li li:hover li:hover a,
#admin-menu .dropdown li li:hover li:hover li:hover a {
color: #FFF;
color: #fff;
}
#admin-menu .dropdown li li.expandable:hover a,
#admin-menu .dropdown li li.expandable:hover li.expandable:hover a {
border-color: #444;
color: #EEE;
color: #eee;
}
#admin-menu .dropdown li li.expandable:hover li a,
#admin-menu .dropdown li li.expandable:hover li.expandable:hover li a {
border-color: #323232;
}
#admin-menu .dropdown li li:hover li a {
color: #EEE;
color: #eee;
}
/* Search form */

View File

@ -97,7 +97,7 @@ function admin_menu_tree_dynamic(array $expand_map) {
$db_or = db_or();
foreach ($plids as $path_plids) {
$db_and = db_and();
// plids with value 0 may be ignored.
// Plids with value 0 may be ignored.
foreach (array_filter($path_plids) as $column => $plid) {
$db_and->condition($column, $plid);
}
@ -204,7 +204,7 @@ function admin_menu_merge_tree(array &$tree, array $tree_dynamic, array $expand_
foreach ($load_functions as $index => $function) {
if ($function) {
if (is_array($function)) {
list($function,) = each($function);
$function = key($function);
}
// Add the loader function name minus "_load".
$placeholder = '%' . substr($function, 0, -5);
@ -269,7 +269,7 @@ function admin_menu_translate($router_item, $map) {
// replace any other.
// @todo Doing this instead leads to plenty of duplicate links below
// admin/structure/menu; likely a hidden recursion problem.
// $router_item['mlid'] = $router_item['href'] . $router_item['mlid'];
// $router_item['mlid'] = $router_item['href'] . $router_item['mlid'];.
$router_item['mlid'] = $router_item['href'];
// Turn menu callbacks into regular menu items to make them visible.
if ($router_item['type'] == MENU_CALLBACK) {
@ -278,10 +278,12 @@ function admin_menu_translate($router_item, $map) {
// @see _menu_tree_check_access()
$key = (50000 + $router_item['weight']) . ' ' . $router_item['title'] . ' ' . $router_item['mlid'];
return array($key => array(
'link' => $router_item,
'below' => array(),
));
return array(
$key => array(
'link' => $router_item,
'below' => array(),
),
);
}
return array();
@ -461,20 +463,22 @@ function admin_menu_links_icon() {
'#access' => user_access('display drupal links'),
'#href' => 'http://drupal.org',
);
// Add links to project issue queues.
foreach (module_list(FALSE, TRUE) as $module) {
$info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info');
if (!isset($info['project']) || isset($links['icon']['drupal.org'][$info['project']])) {
continue;
if (variable_get('admin_menu_issue_queues', TRUE)) {
// Add links to project issue queues.
foreach (module_list(FALSE, TRUE) as $module) {
$info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info');
if (!isset($info['project']) || isset($links['icon']['drupal.org'][$info['project']])) {
continue;
}
$links['icon']['drupal.org'][$info['project']] = array(
'#title' => t('@project issue queue', array('@project' => $info['name'])),
'#weight' => ($info['project'] == 'drupal' ? -10 : 0),
'#href' => 'http://drupal.org/project/issues/' . $info['project'],
'#options' => array(
'query' => array('version' => (isset($info['core']) ? $info['core'] : 'All')),
),
);
}
$links['icon']['drupal.org'][$info['project']] = array(
'#title' => t('@project issue queue', array('@project' => $info['name'])),
'#weight' => ($info['project'] == 'drupal' ? -10 : 0),
'#href' => 'http://drupal.org/project/issues/' . $info['project'],
'#options' => array(
'query' => array('version' => (isset($info['core']) ? $info['core'] : 'All')),
),
);
}
// Add items to flush caches.
$links['icon']['flush-cache'] = array(
@ -570,7 +574,7 @@ function admin_menu_links_users() {
'#description' => t('Current anonymous / authenticated users'),
'#weight' => -90,
'#attributes' => array('class' => array('admin-menu-action', 'admin-menu-users')),
'#href' => (user_access('administer users') ? 'admin/people/people' : 'user'),
'#href' => (user_access('administer users') ? 'admin/people' : 'user'),
);
return $links;
}
@ -658,7 +662,7 @@ function admin_menu_theme_settings() {
'#default_value' => variable_get('admin_menu_tweak_modules', 0),
);
if (module_exists('util')) {
$form['tweaks']['admin_menu_tweak_modules']['#description'] .= '<br /><strong>' . t('If the Utility module was installed for this purpose, it can be safely disabled and uninstalled.') . '</strong>';
$form['tweaks']['admin_menu_tweak_modules']['#description'] = '<br /><strong>' . t('If the Utility module was installed for this purpose, it can be safely disabled and uninstalled.') . '</strong>';
}
$form['tweaks']['admin_menu_tweak_permissions'] = array(
'#type' => 'checkbox',
@ -685,6 +689,11 @@ function admin_menu_theme_settings() {
'#title' => t('Cache menu in client-side browser'),
'#default_value' => variable_get('admin_menu_cache_client', 1),
);
$form['performance']['admin_menu_issue_queues'] = array(
'#type' => 'checkbox',
'#title' => t('Show Issue Queue links in icon menu'),
'#default_value' => variable_get('admin_menu_issue_queues', 1),
);
return system_settings_form($form);
}
@ -763,18 +772,20 @@ function admin_menu_flush_cache($name = NULL) {
if (!isset($caches[$name])) {
return MENU_NOT_FOUND;
}
$message = t('@title cache cleared.', array('@title' => $caches[$name]['title']));
}
else {
$caches[$name] = array(
'title' => t('Every'),
'callback' => 'drupal_flush_all_caches',
);
$message = t('All caches cleared.');
}
// Pass the cache to flush forward to the callback.
$function = $caches[$name]['callback'];
$function($name);
drupal_set_message(t('!title cache cleared.', array('!title' => $caches[$name]['title'])));
drupal_set_message($message);
// The JavaScript injects a destination request parameter pointing to the
// originating page, so the user is redirected back to that page. Without
@ -907,4 +918,3 @@ function template_preprocess_admin_menu_icon(&$variables) {
function theme_admin_menu_icon($variables) {
return '<img class="admin-menu-icon" src="' . $variables['src'] . '" width="16" height="16" alt="' . $variables['alt'] . '" />';
}

View File

@ -8,9 +8,8 @@ configure = admin/config/administration/admin_menu
dependencies[] = system (>7.10)
files[] = tests/admin_menu.test
; Information added by Drupal.org packaging script on 2014-12-19
version = "7.x-3.0-rc5"
; Information added by Drupal.org packaging script on 2018-12-03
version = "7.x-3.0-rc6"
core = "7.x"
project = "admin_menu"
datestamp = "1419029284"
datestamp = "1543859284"

View File

@ -1,4 +1,8 @@
(function($) {
/**
* @file
*/
(function ($) {
Drupal.admin = Drupal.admin || {};
Drupal.admin.behaviors = Drupal.admin.behaviors || {};
@ -139,7 +143,7 @@ Drupal.admin.getCache = function (hash, onSuccess) {
*
* @see toolbar.js
*/
Drupal.admin.height = function() {
Drupal.admin.height = function () {
var $adminMenu = $('#admin-menu');
var height = $adminMenu.outerHeight();
// In IE, Shadow filter adds some extra height, so we need to remove it from
@ -161,7 +165,7 @@ Drupal.admin.height = function() {
Drupal.admin.attachBehaviors = function (context, settings, $adminMenu) {
if ($adminMenu.length) {
$adminMenu.addClass('admin-menu-processed');
$.each(Drupal.admin.behaviors, function() {
$.each(Drupal.admin.behaviors, function () {
this(context, settings, $adminMenu);
});
}
@ -206,7 +210,7 @@ Drupal.admin.behaviors.replacements = function (context, settings, $adminMenu) {
*/
Drupal.admin.behaviors.destination = function (context, settings, $adminMenu) {
if (settings.admin_menu.destination) {
$('a.admin-menu-destination', $adminMenu).each(function() {
$('a.admin-menu-destination', $adminMenu).each(function () {
this.search += (!this.search.length ? '?' : '&') + Drupal.settings.admin_menu.destination;
});
}

View File

@ -80,7 +80,14 @@ function field_ui_admin_menu_map() {
'access callback' => 'user_access',
'access arguments' => array('administer site configuration'),
);
if (!call_user_func_array($bundle_info['admin']['access callback'], $bundle_info['admin']['access arguments'])) {
$access_arguments = $bundle_info['admin']['access arguments'];
if (isset($bundle_info['admin']['real path'])) {
$menu_item = menu_get_item($bundle_info['admin']['real path']);
if (isset($menu_item['map'])) {
$access_arguments = menu_unserialize(serialize($access_arguments), $menu_item['map']);
}
}
if (!call_user_func_array($bundle_info['admin']['access callback'], $access_arguments)) {
continue;
}

View File

@ -63,7 +63,7 @@ function admin_menu_theme() {
function admin_menu_menu() {
// AJAX callback.
// @see http://drupal.org/project/js
$items['js/admin_menu/cache'] = array(
$items['js/admin_menu/cache/%'] = array(
'page callback' => 'admin_menu_js_cache',
'delivery callback' => 'admin_menu_deliver',
'access arguments' => array('access administration menu'),
@ -78,7 +78,7 @@ function admin_menu_menu() {
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
);
$items['admin/config/administration/admin_menu'] = array(
$items['admin/config/administration/admin-menu'] = array(
'title' => 'Administration menu',
'description' => 'Adjust administration menu settings.',
'page callback' => 'drupal_get_form',
@ -211,7 +211,7 @@ function admin_menu_page_build(&$page) {
// @todo Drupal.behaviors.adminMenuMarginTop is obsolete, but
// hook_page_build() does not allow to set a CSS class on the body yet.
// @see http://drupal.org/node/1473548, http://drupal.org/node/1194528
//$page['#attributes']['class'][] = 'admin-menu';
// $page['#attributes']['class'][] = 'admin-menu';
}
if ($setting = variable_get('admin_menu_position_fixed', 1)) {
$settings['position_fixed'] = $setting;
@ -230,7 +230,7 @@ function admin_menu_page_build(&$page) {
if ($_GET['q'] == 'admin/modules' || strpos($_GET['q'], 'admin/modules/list') === 0) {
$settings['tweak_modules'] = variable_get('admin_menu_tweak_modules', 0);
}
if ($_GET['q'] == 'admin/people/permissions' || $_GET['q'] == 'admin/people/permissions/list') {
if (strpos($_GET['q'], 'admin/people/permissions') === 0) {
$settings['tweak_permissions'] = variable_get('admin_menu_tweak_permissions', 0);
}
@ -502,7 +502,6 @@ function admin_menu_output($complete = FALSE) {
// @todo Move the below callbacks into hook_admin_menu_build()
// implementations (and $module.admin_menu.inc).
// Add administration menu.
if (!empty($components['admin_menu.menu']) || $complete) {
$content['menu'] = admin_menu_links_menu(admin_menu_tree('management'));
@ -548,7 +547,10 @@ function admin_menu_output($complete = FALSE) {
// Store the new hash for this user.
if (!empty($_COOKIE['has_js']) && !$complete) {
admin_menu_cache_set($cid, md5($content));
$cache = cache_get($cid, 'cache_admin_menu');
if (!$cache || !isset($cache->data)) {
admin_menu_cache_set($cid, md5($content));
}
}
return $content;
@ -603,11 +605,13 @@ function admin_menu_admin_menu_output_build(&$content) {
* Implements hook_admin_menu_output_alter().
*/
function admin_menu_admin_menu_output_alter(&$content) {
foreach ($content['menu'] as $key => $link) {
// Move local tasks on 'admin' into icon menu.
if ($key == 'admin/tasks' || $key == 'admin/index') {
$content['icon']['icon'][$key] = $link;
unset($content['menu'][$key]);
if (!empty($content['menu'])) {
foreach ($content['menu'] as $key => $link) {
// Move local tasks on 'admin' into icon menu.
if ($key == 'admin/tasks' || $key == 'admin/index') {
$content['icon']['icon'][$key] = $link;
unset($content['menu'][$key]);
}
}
}
}
@ -677,6 +681,13 @@ function theme_admin_menu_links($variables) {
$elements[$path]['#options']['attributes']['class'][] = 'admin-menu-destination';
}
// If the path has an alias replace the href with the alias.
if (module_exists('path')) {
if ($alias = drupal_get_path_alias($elements[$path]['#href'])) {
$elements[$path]['#href'] = $alias;
}
}
$link = l($elements[$path]['#title'], $elements[$path]['#href'], $elements[$path]['#options']);
}
// Handle plain text items, but do not interfere with menu additions.
@ -751,7 +762,7 @@ function admin_menu_translated_menu_link_alter(&$item, $map) {
}
}
// Don't waste cycles altering items that are not visible
// Don't waste cycles altering items that are not visible.
if (!$item['access']) {
return;
}
@ -795,8 +806,8 @@ function admin_menu_flush_caches($uid = NULL) {
cache_clear_all($cid, 'cache_menu', TRUE);
// Flush client-side cache hashes.
drupal_static_reset('admin_menu_cache_get');
// db_table_exists() required for SimpleTest.
if (db_table_exists('cache_admin_menu')) {
// If cache_admin_menu is not empty, flush it.
if (!cache_is_empty('cache_admin_menu')) {
cache_clear_all(isset($uid) ? $cid : '*', 'cache_admin_menu', TRUE);
}
}

View File

@ -1,4 +1,3 @@
/**
* @file
* Administration menu color override for uid1.

View File

@ -0,0 +1,4 @@
#admin-menu > div > .dropdown > li > a,
#admin-menu > div > .dropdown > li > span {
border-left: 0;
}

View File

@ -1,4 +1,3 @@
/**
* @file
* Toolbar style for Administration menu.
@ -63,8 +62,9 @@ body div#toolbar.toolbar {
background: url(toolbar.png) no-repeat 0 -45px;
text-indent: -9999px;
}
#admin-menu > div > .dropdown > li > a {
border-right: 0;
#admin-menu > div > .dropdown > li > a,
#admin-menu > div > .dropdown > li > span {
border-right: 0; /* LTR */
margin-bottom: 4px;
padding: 2px 10px 3px;
}
@ -142,4 +142,3 @@ body div#toolbar.toolbar {
#admin-menu .shortcut-toolbar a {
display: block;
}

View File

@ -4,9 +4,8 @@ package = Administration
core = 7.x
dependencies[] = admin_menu
; Information added by Drupal.org packaging script on 2014-12-19
version = "7.x-3.0-rc5"
; Information added by Drupal.org packaging script on 2018-12-03
version = "7.x-3.0-rc6"
core = "7.x"
project = "admin_menu"
datestamp = "1419029284"
datestamp = "1543859284"

View File

@ -34,4 +34,3 @@ function admin_menu_toolbar_update_6300() {
->condition('name', 'admin_menu_toolbar')
->execute();
}

View File

@ -115,4 +115,3 @@ function admin_menu_toolbar_admin_menu_output_alter(&$content) {
$content['account']['account']['#options']['html'] = TRUE;
}
}

View File

@ -16,7 +16,10 @@ class AdminMenuWebTestCase extends DrupalWebTestCase {
'admin_menu' => 'access administration menu',
);
function setUp() {
/**
*
*/
public function setUp() {
// Enable admin menu module and any other modules.
$modules = func_get_args();
$modules = isset($modules[0]) ? $modules[0] : $modules;
@ -108,12 +111,17 @@ class AdminMenuWebTestCase extends DrupalWebTestCase {
$xpath = '//div[@id="admin-menu"]/div/ul' . implode('/parent::li/ul', $xpath);
$this->assertNoElementByXPath($xpath, $args, $message . ' link not found.');
}
}
/**
* Tests menu links depending on user permissions.
*/
class AdminMenuPermissionsTestCase extends AdminMenuWebTestCase {
/**
*
*/
public static function getInfo() {
return array(
'name' => 'Menu link access permissions',
@ -122,14 +130,17 @@ class AdminMenuPermissionsTestCase extends AdminMenuWebTestCase {
);
}
function setUp() {
/**
*
*/
public function setUp() {
parent::setUp(array('node'));
}
/**
* Test that the links are added to the page (no JS testing).
*/
function testPermissions() {
public function testPermissions() {
module_enable(array('contact'));
$this->resetAll();
@ -140,7 +151,7 @@ class AdminMenuPermissionsTestCase extends AdminMenuWebTestCase {
// Create a user who
// - can access content overview
// - cannot access drupal.org links
// - cannot administer Contact module
// - cannot administer Contact module.
$permissions = $this->basePermissions + array(
'access content overview',
);
@ -156,7 +167,7 @@ class AdminMenuPermissionsTestCase extends AdminMenuWebTestCase {
// Create a user "reversed" to the above; i.e., who
// - cannot access content overview
// - can access drupal.org links
// - can administer Contact module
// - can administer Contact module.
$permissions = $this->basePermissions + array(
'display drupal links',
'administer contact forms',
@ -172,7 +183,7 @@ class AdminMenuPermissionsTestCase extends AdminMenuWebTestCase {
/**
* Tests handling of links pointing to category/overview pages.
*/
function testCategories() {
public function testCategories() {
// Create a user with minimum permissions.
$admin_user = $this->drupalCreateUser($this->basePermissions);
$this->drupalLogin($admin_user);
@ -201,7 +212,7 @@ class AdminMenuPermissionsTestCase extends AdminMenuWebTestCase {
/**
* Tests that user role and permission changes are properly taken up.
*/
function testPermissionChanges() {
public function testPermissionChanges() {
// Create a user who is able to change permissions.
$permissions = $this->basePermissions + array(
'administer permissions',
@ -253,12 +264,17 @@ class AdminMenuPermissionsTestCase extends AdminMenuWebTestCase {
// Verify that Structure » Content types does not appear.
$this->assertNoLinkTrailByTitle(array(t('Structure'), t('Content types')));
}
}
/**
* Tests appearance, localization, and escaping of dynamic links.
*/
class AdminMenuDynamicLinksTestCase extends AdminMenuWebTestCase {
/**
*
*/
public static function getInfo() {
return array(
'name' => 'Dynamic links',
@ -267,14 +283,17 @@ class AdminMenuDynamicLinksTestCase extends AdminMenuWebTestCase {
);
}
function setUp() {
/**
*
*/
public function setUp() {
parent::setUp(array('node'));
}
/**
* Tests node type links.
*/
function testNode() {
public function testNode() {
$type = $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
// Create a content-type with special characters.
$type = $this->drupalCreateContentType(array('type' => 'special', 'name' => 'Cool & Special'));
@ -324,7 +343,7 @@ class AdminMenuDynamicLinksTestCase extends AdminMenuWebTestCase {
/**
* Tests Add content links.
*/
function testNodeAdd() {
public function testNodeAdd() {
$type = $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
// Verify that "Add content" does not appear for unprivileged users.
@ -359,12 +378,17 @@ class AdminMenuDynamicLinksTestCase extends AdminMenuWebTestCase {
t('Add content'),
));
}
}
/**
* Tests appearance of different types of links.
*/
class AdminMenuLinkTypesTestCase extends AdminMenuWebTestCase {
/**
*
*/
public static function getInfo() {
return array(
'name' => 'Link types',
@ -373,7 +397,10 @@ class AdminMenuLinkTypesTestCase extends AdminMenuWebTestCase {
);
}
function setUp() {
/**
*
*/
public function setUp() {
parent::setUp(array('help'));
$permissions = module_invoke_all('permission');
@ -385,7 +412,7 @@ class AdminMenuLinkTypesTestCase extends AdminMenuWebTestCase {
/**
* Tests appearance of different router item link types.
*/
function testLinkTypes() {
public function testLinkTypes() {
// Verify that MENU_NORMAL_ITEMs appear.
$this->assertLinkTrailByTitle(array(
t('Configuration'),
@ -420,12 +447,17 @@ class AdminMenuLinkTypesTestCase extends AdminMenuWebTestCase {
':title' => t('Index'),
), "Icon » Index link found.");
}
}
/**
* Tests customized menu links.
*/
class AdminMenuCustomizedTestCase extends AdminMenuWebTestCase {
/**
*
*/
public static function getInfo() {
return array(
'name' => 'Customized links',
@ -434,7 +466,10 @@ class AdminMenuCustomizedTestCase extends AdminMenuWebTestCase {
);
}
function setUp() {
/**
*
*/
public function setUp() {
parent::setUp(array('menu'));
$this->admin_user = $this->drupalCreateUser($this->basePermissions + array(
@ -446,7 +481,7 @@ class AdminMenuCustomizedTestCase extends AdminMenuWebTestCase {
/**
* Test disabled custom links.
*/
function testCustomDisabled() {
public function testCustomDisabled() {
$type = $this->drupalCreateContentType();
$node = $this->drupalCreateNode(array('type' => $type->type));
$text = $this->randomName();
@ -488,7 +523,7 @@ class AdminMenuCustomizedTestCase extends AdminMenuWebTestCase {
/**
* Tests external links.
*/
function testCustomExternal() {
public function testCustomExternal() {
// Add a custom link to the node to the menu.
$edit = array(
'link_path' => 'http://example.com',
@ -516,5 +551,5 @@ class AdminMenuCustomizedTestCase extends AdminMenuWebTestCase {
':path' => $path,
))->fetchField();
}
}
}

View File

@ -465,12 +465,33 @@ function entity_translation_export_import_import_translation($data){
$bundle = $data['Bundle (Not Editable)'];
$entity_id = $data['Entity ID (Not Editable)'];
$entities = entity_load($entity_type, array($entity_id));
if(empty($entity_id)) {
if ($entity_type == 'taxonomy_term') {
// Handle taxonomy term entity.
$vocab = taxonomy_vocabulary_machine_name_load($bundle);
$term = new stdClass();
$term->name = '';
$term->vid = $vocab->vid;
taxonomy_term_save($term);
// If cannot load entity.
if(empty($entities)){
drupal_set_message(t('Entity with id @id could not be found in database.', array('@id' => $entity_id)));
return FALSE;
$entities = entity_load($entity_type, array($term->tid));
}
else if($entity_type == 'node') {
// Handle node entity.
$node = new stdClass();
$node->type = $bundle;
node_object_prepare($node); //Set some default values
$node->status = 1;
$node->promote = 0;
$node->sticky = 0;
$node->uid = $user->uid;
node_save($node);
$entities = entity_load($entity_type, array($node->nid));
}
}
else {
$entities = entity_load($entity_type, array($entity_id));
}
// Get the entity.

View File

@ -5,9 +5,9 @@ package = Multilingual - Entity Translation
dependencies[] = entity
dependencies[] = entity_translation
; Information added by drupal.org packaging script on 2013-09-30
version = "7.x-1.0-beta3+0-dev"
; Information added by Drupal.org packaging script on 2017-02-08
version = "7.x-1.0-beta4"
core = "7.x"
project = "entity_translation_export_import"
datestamp = "1380577025"
datestamp = "1486567689"

View File

@ -1,13 +1,13 @@
<?php
/**
* @file
* Webform Component information for an address field type
* Webform Component information for an address field type.
*/
/**
* Specify the default properties of a component.
*
* @return
* @return array
* An array defining the default structure of a component.
*/
function _webform_defaults_addressfield() {
@ -23,6 +23,8 @@ function _webform_defaults_addressfield() {
'attributes' => array(),
'description' => '',
'available_countries' => array(),
'default_country' => '',
'format_handlers' => array(),
'csv_separate' => 0,
),
);
@ -30,21 +32,22 @@ function _webform_defaults_addressfield() {
/**
* Generate the form for editing a component.
*
* Create a set of form elements to be displayed on the form for editing this
* component. Use care naming the form items, as this correlates directly to the
* database schema. The component "Name" and "Description" fields are added to
* every component type and are not necessary to specify here (although they
* may be overridden if desired).
*
* @param $component
* @param mixed $component
* A Webform component array.
*
* @return
* @return array
* An array of form items to be displayed on the edit component page
*/
function _webform_edit_addressfield($component) {
$form = array();
$form['extra']['available_countries'] = array(
'#type' => 'select',
'#multiple' => TRUE,
@ -53,6 +56,21 @@ function _webform_edit_addressfield($component) {
'#options' => _addressfield_country_options_list(),
'#default_value' => $component['extra']['available_countries'],
);
$form['extra']['default_country'] = array(
'#type' => 'select',
'#multiple' => FALSE,
'#title' => t('Default country'),
'#description' => t('Select which country should be selected as the default.'),
'#options' => array_merge(array(0 => t('- None -')), _addressfield_country_options_list()),
'#default_value' => $component['extra']['default_country'],
);
$form['extra']['format_handlers'] = array(
'#type' => 'checkboxes',
'#title' => t('Format handlers'),
'#options' => addressfield_format_plugins_options(),
'#required' => TRUE,
'#default_value' => !empty($component['extra']['format_handlers']) ? $component['extra']['format_handlers'] : array('address'),
);
$form['extra']['csv_separate'] = array(
'#type' => 'radios',
'#title' => t('CSV download'),
@ -69,18 +87,21 @@ function _webform_edit_addressfield($component) {
/**
* Render a Webform component to be part of a form.
*
* @param $component
* @param mixed $component
* A Webform component array.
* @param $value
* @param mixed $value
* If editing an existing submission or resuming a draft, this will contain
* an array of values to be shown instead of the default in the component
* configuration. This value will always be an array, keyed numerically for
* each value saved in this field.
* @param $filter
* @param bool $filter
* Whether or not to filter the contents of descriptions and values when
* rendering the component. Values need to be unfiltered to be editable by
* Form Builder.
*
* @return array
* Form element.
*
* @see _webform_client_form_add_component()
*/
function _webform_render_addressfield($component, $value = NULL, $filter = TRUE) {
@ -98,55 +119,98 @@ function _webform_render_addressfield($component, $value = NULL, $filter = TRUE)
'description',
),
);
$available = !empty($component['extra']['available_countries']) ? $component['extra']['available_countries'] : NULL;
// Get the current address
if (!empty($value[0])) {
if (is_string($value[0])) {
$address = unserialize($value[0]);
// Get the current address.
$address = _addressfield_tokens_expand_value($value);
if (empty($address)) {
if (!empty($component['value'])) {
$address = $component['value'];
}
else {
$address = $value[0];
$address = _webform_addressfield($component['cid']);
}
}
elseif (!empty($component['value'])) {
$address = $component['value'];
}
else {
$address = _webform_addressfield($component['cid']);
}
if (empty($address)) {
$address = addressfield_default_values($available);
$address = _webform_addressfield_default_values($available, $component);
}
// Generate the address form.
$context = array(
'mode' => 'form',
'instance' => array(
'required' => $component['required'],
),
'form_key' => $component['form_key'],
);
$element += addressfield_generate($address, array('address'), $context);
if (!empty($available)) {
$element['country']['#options'] = array_intersect_key($element['country']['#options'], $available);
$handlers = !empty($component['extra']['format_handlers']) ? $component['extra']['format_handlers'] : array('address');
$element += addressfield_generate($address, $handlers, $context);
if (empty($address['country'])) {
$element['street_block']['#access'] = FALSE;
$element['locality_block']['#access'] = FALSE;
}
$element['country']['#element_validate'] = array('_webform_addressfield_country_validate');
$element['country']['#cid'] = $component['cid'];
$element['country']['#limit_validation_errors'] = array();
if (isset($element['country'])) {
if (!empty($available)) {
$element['country']['#options'] = array_intersect_key($element['country']['#options'], $available);
// Hide the country element only if there is one option and the whole field
// is required, otherwise there will always be an additional None option.
// @see addressfield_format_address_hide_country()
if (!empty($handlers['address-hide-country']) && count($element['country']['#options']) == 1 && $component['required']) {
$element['country']['#access'] = FALSE;
}
}
$element['country']['#default_value'] = $address['country'];
$element['country']['#element_validate'] = array('_webform_addressfield_country_validate');
$element['country']['#cid'] = $component['cid'];
$element['country']['#limit_validation_errors'] = array();
}
$form_state = array();
drupal_alter('field_widget_addressfield_standard_form', $element, $form_state, $context);
drupal_alter('field_widget_addressfield_standard_form', $element, $form_state, $context);
return $element;
}
function _webform_addressfield_default_values($available, $component) {
$default_country = !empty($component['extra']['default_country'])
? $component['extra']['default_country']
: addressfield_tokens_default_country();
$default_values = array(
'country' => $default_country,
'name_line' => '',
'first_name' => '',
'last_name' => '',
'organisation_name' => '',
'administrative_area' => '',
'sub_administrative_area' => '',
'locality' => '',
'dependent_locality' => '',
'postal_code' => '',
'thoroughfare' => '',
'premise' => '',
'sub_premise' => '',
'data' => '',
);
return ($default_values);
}
/**
* Stores an addressfield submitted in a webform component. Ideally we should store
* it in the $form_state instead, but there appears to be no way to get it to actually
* pass through to _webform_render_addressfield().
*
* @param $cid integer The ID of the webform component.
* @param $address array If set, this address will be stored with the given $cid.
* @return array The address stored with the given $cid, if there is one; otherwise, NULL.
* Stores an addressfield submitted in a webform component.
*
* Ideally store it in the $form_state instead, but there appears to be no way
* to get it to actually pass through to _webform_render_addressfield().
*
* @param int $cid
* The ID of the webform component.
* @param mixed $address
* If set, this address will be stored with the given $cid.
*
* @return array
* The address stored with the given $cid, if there is one; otherwise, NULL.
*/
function _webform_addressfield($cid, $address = NULL) {
$out = &drupal_static(__FUNCTION__, array());
@ -160,40 +224,41 @@ function _webform_addressfield($cid, $address = NULL) {
}
/**
* Validates a country, and if it changes, rebuilds the form for the new country
* Validates a country, and if changed, rebuilds the form for the new country.
*/
function _webform_addressfield_country_validate(&$element, &$form_state) {
// If the country was changed, rebuild the form.
if ($element['#default_value'] != $element['#value']) {
if (!isset($element['#default_value']) || $element['#default_value'] != $element['#value']) {
$form_state['rebuild'] = TRUE;
}
$cid = $element['#cid'];
$parents = $element['#parents'];
array_pop($parents);
// Search through the form values to find the current address.
$address = drupal_array_get_nested_value($form_state['values'], $parents);
_webform_addressfield($cid, $address);
}
/**
* Display the result of a submission for a component.
*
* The output of this function will be displayed under the "Results" tab then
* "Submissions". This should output the saved data in some reasonable manner.
*
* @param $component
* @param mixed $component
* A Webform component array.
* @param $value
* @param mixed $value
* An array of information containing the submission result, directly
* correlating to the webform_submitted_data database table schema.
* @param $format
* @param string $format
* Either 'html' or 'text'. Defines the format that the content should be
* returned as. Make sure that returned content is run through check_plain()
* or other filtering functions when returning HTML.
*
* @return
* @return array
* A renderable element containing at the very least these properties:
* - #title
* - #weight
@ -205,27 +270,23 @@ function _webform_addressfield_country_validate(&$element, &$form_state) {
* (such as wrapping the text) or as HTML (ensuring consistent output).
*/
function _webform_display_addressfield($component, $value, $format = 'html') {
$address = NULL;
if (isset($value[0])) {
$address = $value[0];
if (is_string($address)) {
$address = unserialize($address);
}
}
$address = _addressfield_tokens_expand_value($value);
return array(
'#title' => $component['name'],
'#weight' => $component['weight'],
'#theme' => $format == 'html' ? 'addressfield_formatter' : 'addressfield_formatter__linear',
'#theme_wrappers' => $format == 'html' ? array('webform_element' ) : array('webform_element_text'),
'#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
'#post_render' => array('webform_element_wrapper'),
'#component' => $component,
'#format' => $format,
'#address' => $address,
'#address' => $address ? $address : NULL,
'#handlers' => $component['extra']['format_handlers'],
);
}
/**
* A hook for changing the input values before saving to the database.
*
* Webform expects a component to consist of a single field, or a single array
* of fields. If you have a component that requires a deeper form tree
* you must flatten the data into a single array using this callback
@ -234,12 +295,12 @@ function _webform_display_addressfield($component, $value, $format = 'html') {
* Note that Webform will save the result of this function directly into the
* database.
*
* @param $component
* @param mixed $component
* A Webform component array.
* @param $value
* @param mixed $value
* The POST data associated with the user input.
*
* @return
* @return array
* An array of values to be saved into the database. Note that this should be
* a numerically keyed array.
*/
@ -249,88 +310,92 @@ function _webform_submit_addressfield($component, $value) {
/**
* Calculate and returns statistics about results for this component.
*
* This takes into account all submissions to this webform. The output of this
* function will be displayed under the "Results" tab then "Analysis".
*
* @param $component
* @param mixed $component
* An array of information describing the component, directly correlating to
* the webform_component database schema.
* @param $sids
* @param mixed $sids
* An optional array of submission IDs (sid). If supplied, the analysis will
* be limited to these sids.
* @param $single
* @param bool $single
* Boolean flag determining if the details about a single component are being
* shown. May be used to provided detailed information about a single
* component's analysis, such as showing "Other" options within a select list.
*
* @return
* @return array
* An array of data rows, each containing a statistic for this component's
* submissions.
*/
function _webform_analysis_addressfield($component, $sids = array(), $single = FALSE) {
// TODO Update this function
// @todo Update this function
// Generate the list of options and questions.
$query = db_select('webform_submitted_data', 'wsd')
->fields('wsd', array('data'))
->condition('nid', $component['nid'])
->condition('cid', $component['cid']);
if ( count($sids) ) {
if (count($sids)) {
$query->condition('sid', $sids, 'IN');
}
$non_blanks = 0;
$submissions = 0;
$results = $query->execute();
foreach ($results as $row) {
if ( drupal_strlen(trim($row->data)) > 0 ) {
if (drupal_strlen(trim($row->data)) > 0) {
$non_blanks++;
}
$submissions++;
}
$rows[0] = array(
t('Left Blank'),
( $submissions - $non_blanks )
($submissions - $non_blanks),
);
$rows[1] = array(
t('User entered value'),
$non_blanks
$non_blanks,
);
return $rows;
return array('table_rows' => $rows);
}
/**
* Return the result of a component value for display in a table.
*
* The output of this function will be displayed under the "Results" tab then
* "Table".
*
* @param $component
* @param mixed $component
* A Webform component array.
* @param $value
* @param mixed $value
* An array of information containing the submission result, directly
* correlating to the webform_submitted_data database schema.
*
* @return
* @return string
* Textual output formatted for human reading.
*/
function _webform_table_addressfield($component, $value) {
if (!empty($value[0])) {
return theme('addressfield_formatter', array( 'address' => $value[0] ));
$address = _addressfield_tokens_expand_value($value);
if ($address) {
return theme('addressfield_formatter', array('address' => $address));
}
return '';
}
/**
* Return the header for this component to be displayed in a CSV file.
*
* The output of this function will be displayed under the "Results" tab then
* "Download".
*
* @param $component
* @param mixed $component
* A Webform component array.
* @param $export_options
* @param mixed $export_options
* An array of options that may configure export of this field.
*
* @return
* @return array
* An array of data to be displayed in the first three rows of a CSV file, not
* including either prefixed or trailing commas.
*/
@ -340,7 +405,6 @@ function _webform_csv_headers_addressfield($component, $export_options) {
$header[0] = array();
$header[1] = array();
$header[2] = array();
foreach (addressfield_tokens_property_names() as $key => $name) {
$header[0][] = '';
$header[1][] = (empty($header[1])) ? $component['name'] : '';
@ -349,7 +413,7 @@ function _webform_csv_headers_addressfield($component, $export_options) {
}
else {
$header[0] = array('');
$header[1] = array();
$header[1] = array('');
$header[2] = array($component['name']);
}
return $header;
@ -357,34 +421,56 @@ function _webform_csv_headers_addressfield($component, $export_options) {
/**
* Format the submitted data of a component for CSV downloading.
*
* The output of this function will be displayed under the "Results" tab then
* "Download".
*
* @param $component
* @param mixed $component
* A Webform component array.
* @param $export_options
* @param mixed $export_options
* An array of options that may configure export of this field.
* @param $value
* @param mixed $value
* An array of information containing the submission result, directly
* correlating to the webform_submitted_data database schema.
*
* @return
* @return array
* An array of items to be added to the CSV file. Each value within the array
* will be another column within the file. This function is called once for
* every row of data.
*/
function _webform_csv_data_addressfield($component, $export_options, $value) {
$address = _addressfield_tokens_expand_value($value);
if (!empty($component['extra']['csv_separate']) && $component['extra']['csv_separate'] == 1) {
// Each address component should be in a separate column.
$return = array();
foreach (addressfield_tokens_property_names() as $key => $name) {
$return[] = (isset($value[0][$key])) ? $value[0][$key] : '';
$return[] = (isset($address[$key])) ? $address[$key] : '';
}
return $return;
}
else {
if (!empty($value[0])) {
return theme('addressfield_formatter__linear', array( 'address' => $value[0] ));
// The entire address should be displayed in the one column.
if ($address) {
return theme('addressfield_formatter__linear', array('address' => $address));
}
return '';
}
}
/**
* Expand a raw address submission as loaded from the database to an array.
*
* @param string $value
* An array of information containing the submission result, directly
* correlating to the {webform_submitted_data} database schema.
*
* @return array|false
* An associative array of address fields, or FALSE on failure.
*/
function _addressfield_tokens_expand_value($value) {
if (isset($value[0]) && is_string($value[0])) {
return unserialize($value[0]);
}
return FALSE;
}

View File

@ -1,16 +1,13 @@
name = Address Field Tokens
description = Creates tokens for address fields, adds new addressfield renders, and adds webform integration.
core = 7.x
package = Fields
dependencies[] = addressfield
dependencies[] = entity_token
dependencies[] = token
name = Address Field Tokens
description = Creates tokens for address fields, adds new addressfield renders, and adds webform integration.
core = 7.x
package = Fields
dependencies[] = addressfield
dependencies[] = entity_token
dependencies[] = token
; Information added by Drupal.org packaging script on 2014-10-29
version = "7.x-1.5"
; Information added by Drupal.org packaging script on 2018-10-11
version = "7.x-1.13"
core = "7.x"
project = "addressfield_tokens"
datestamp = "1414599829"
datestamp = "1539260884"

View File

@ -4,9 +4,12 @@
* Main components.
*/
/**
* Implements hook_menu().
*/
function addressfield_tokens_menu() {
$items = array();
$items['admin/config/regional/address'] = array(
'title' => 'Addresses',
'description' => 'Settings for address fields and tokens',
@ -16,7 +19,7 @@ function addressfield_tokens_menu() {
'file' => 'addressfield_tokens.admin.inc',
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
@ -34,19 +37,31 @@ function addressfield_tokens_theme($existing, $type, $theme, $path) {
'file' => 'addressfield_tokens.theme.inc',
),
'addressfield_formatter__linear' => array(
'variables' => array('address' => NULL, 'premise' => TRUE, 'organisation_name' => TRUE, 'name_line' => TRUE),
'variables' => array(
'address' => NULL,
'premise' => TRUE,
'organisation_name' => TRUE,
'name_line' => TRUE,
),
'file' => 'addressfield_tokens.theme.inc',
),
'addressfield_formatter__components' => array(
'variables' => array(
'address' => NULL,
'components' => array('thoroughfare', 'premise', 'locality', 'administrative_area', 'postal_code', 'country'),
'components' => array(
'thoroughfare',
'premise',
'locality',
'administrative_area',
'postal_code',
'country',
),
'separator' => ', ',
),
'file' => 'addressfield_tokens.theme.inc',
),
);
return $theme;
}
@ -75,7 +90,14 @@ function addressfield_tokens_field_formatter_info() {
'label' => t('Address components'),
'field types' => array('addressfield'),
'settings' => array(
'components' => array('thoroughfare', 'premise', 'locality', 'administrative_area', 'postal_code', 'country'),
'components' => array(
'thoroughfare',
'premise',
'locality',
'administrative_area',
'postal_code',
'country',
),
'separator' => ', ',
),
),
@ -103,14 +125,13 @@ function addressfield_tokens_field_formatter_settings_form($field, $instance, $v
$element['separator'] = array(
'#type' => 'textfield',
'#title' => t('Separator'),
'#description' => t('The separator to use between components. Use \n for a line break.'),
'#description' => t('The separator to use between components. Use br tag for a line break.'),
'#default_value' => $settings['separator'],
);
}
return $element;
}
/**
* Implements hook_field_formatter_settings_summary().
*/
@ -132,7 +153,7 @@ function addressfield_tokens_field_formatter_settings_summary($field, $instance,
*/
function addressfield_tokens_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array();
switch ($display['type']) {
case 'addressfield_citystate':
$theme = array('addressfield_formatter__citystate', 'addressfield_formatter');
@ -147,7 +168,7 @@ function addressfield_tokens_field_formatter_view($entity_type, $entity, $field,
);
}
break;
case 'addressfield_linear':
$theme = array('addressfield_formatter__linear', 'addressfield_formatter');
@ -161,7 +182,7 @@ function addressfield_tokens_field_formatter_view($entity_type, $entity, $field,
);
}
break;
case 'addressfield_country':
foreach ($items as $delta => $item) {
if (!empty($item['country'])) {
@ -175,11 +196,11 @@ function addressfield_tokens_field_formatter_view($entity_type, $entity, $field,
}
}
break;
case 'addressfield_state':
foreach ($items as $delta => $item) {
if (!empty($item['country']) && !empty($item['administrative_area'])) {
$state = _addressfield_tokens_state($item['country'], $item['administrative_area']);
$state = addressfield_tokens_state($item['country'], $item['administrative_area']);
$element[$delta] = array(
'#type' => 'markup',
'#markup' => filter_xss($state),
@ -189,19 +210,19 @@ function addressfield_tokens_field_formatter_view($entity_type, $entity, $field,
}
}
break;
case 'addressfield_components':
$theme = array('addressfield_formatter__components', 'addressfield_formatter');
$settings = $display['settings'];
foreach ($items as $delta => $item) {
if (!empty($item['country'])) {
array_unshift($theme, 'addressfield_formatter__components__' . $item['country']);
}
}
$element[$delta] = array(
'#theme' => $theme,
'#address' => array_map('filter_xss', $item),
'#components' => $settings['components'],
'#separator' => filter_xss($settings['separator']),
'#separator' => filter_xss($settings['separator'], array('br')),
);
}
break;
@ -230,11 +251,31 @@ function addressfield_tokens_property_names() {
return $names;
}
/**
* Generates token components.
*
* @return mixed
* Array of components.
*/
function addressfield_tokens_components() {
$comps = &drupal_static(__FUNCTION__, array());
if (empty($comps)) {
$names = addressfield_tokens_property_names();
foreach (array('first_name', 'last_name', 'name_line', 'organisation_name', 'thoroughfare', 'premise', 'locality', 'dependent_locality', 'administrative_area', 'sub_administrative_area', 'postal_code', 'country') as $key) {
$fields = array(
'first_name',
'last_name',
'name_line',
'organisation_name',
'thoroughfare',
'premise',
'locality',
'dependent_locality',
'administrative_area',
'sub_administrative_area',
'postal_code',
'country',
);
foreach ($fields as $key) {
$comps[$key] = $names[$key];
if (in_array($key, array('administrative_area', 'country'))) {
$comps[$key . '_full'] = t('@name (full)', array(
@ -248,14 +289,15 @@ function addressfield_tokens_components() {
/**
* Gets the list of countries from the locale settings in Drupal.
*
* @return array An array of countries. The keys are the 2-letter
*
* @return array
* An array of countries. The keys are the 2-letter
* abbreviations and the values are the full country names.
*/
function _addressfield_tokens_countries() {
$countries = &drupal_static(__FUNCTION__);
if (empty($countries)) {
include_once('includes/locale.inc');
require_once DRUPAL_ROOT . '/includes/locale.inc';
$countries = country_get_list();
}
return $countries;
@ -263,14 +305,17 @@ function _addressfield_tokens_countries() {
/**
* Gets the name of the country with the given abbreviation.
*
* @param string $country The 2-letter abbreviation of the country.
* @return string The name of the country, or FALSE.
*
* @param string $country
* The 2-letter abbreviation of the country.
*
* @return string
* The name of the country, or FALSE.
*/
function _addressfield_tokens_country($country) {
$countries = _addressfield_tokens_countries();
// Country abbreviations will always be two uppercase letters.
// Country abbreviations will always be two uppercase letters.
$country = drupal_strtoupper($country);
if (!empty($country) && isset($countries[$country])) {
return check_plain($countries[$country]);
@ -279,11 +324,12 @@ function _addressfield_tokens_country($country) {
}
/**
* Gets the abbreviation of the country with the given name
*
* @param string
* Gets the abbreviation of the country with the given name.
*
* @param string $country
* The name of the country.
* @return string $country
*
* @return string
* The 2-letter abbreviation of the country, or FALSE.
*/
function _addressfield_tokens_country_abbr($country) {
@ -297,55 +343,63 @@ function _addressfield_tokens_country_abbr($country) {
/**
* Gets the list of states in the given country.
*
* @param string $country The 2-letter abbreviation of the country.
*
* @return array An array of countries. The keys are the 2-letter
*
* @param string $country
* The 2-letter abbreviation of the country.
*
* @return array
* An array of countries. The keys are the 2-letter
* abbreviations and the values are the full country names.
*/
function _addressfield_tokens_states($country) {
function addressfield_tokens_states($country) {
global $language;
$langcode = $language->language;
$states = &drupal_static(__FUNCTION__);
$country = drupal_strtoupper($country);
if (!isset($states[$country])) {
$cache = cache_get('addressfield_tokens_states');
$cache = cache_get('addressfield_tokens_states:' . $langcode);
if ($cache) {
$states = $cache->data;
}
}
if (!isset($states[$country])) {
$format = addressfield_generate(array('country' => $country), array('address'), array('mode' => 'render'));
if (isset($format['locality_block']['administrative_area']['#options'])) {
$states[$country] = $format['locality_block']['administrative_area']['#options'];
}
else {
$states[$country] = array();
}
cache_set('addressfield_tokens_states', $states);
cache_set('addressfield_tokens_states:' . $langcode, $states);
}
return $states[$country];
}
/**
* Gets the name of the state with the given abbreviation.
*
* @param string $country The 2-letter abbreviation of the country.
* @param string $state The 2-letter abbreviation of the state.
* @return string The name of the state, or FALSE.
*
* @param string $country
* The 2-letter abbreviation of the country.
* @param string $state
* The 2-letter abbreviation of the state.
*
* @return string
* The name of the state, or FALSE.
*/
function _addressfield_tokens_state($country, $state) {
$states = _addressfield_tokens_states($country);
// State abbreviations will usually be two uppercase letters.
$state = drupal_strtoupper($state);
if (!empty($state) && !empty($states[$state])) {
function addressfield_tokens_state($country, $state) {
$states = addressfield_tokens_states($country);
// State abbreviations will usually be two uppercase letters.
$state_upper = drupal_strtoupper($state);
if (!empty($state_upper) && !empty($states[$state_upper])) {
return check_plain($states[$state]);
}
return check_plain($state);
}
/**
* Implements hook_webform_component_info().
/**
* Implements hook_webform_component_info().
*/
function addressfield_tokens_webform_component_info() {
$components = array();
@ -387,6 +441,15 @@ function addressfield_tokens_webform_component_info() {
return $components;
}
/**
* Retrieves node components based on Node ID.
* @param int $nid
* Node ID.
*
* @return mixed
* Components.
*/
function _addressfield_tokens_webform_components($nid) {
$components = &drupal_static(__FUNCTION__, array());
if (!isset($components[$nid])) {
@ -402,13 +465,14 @@ function _addressfield_tokens_webform_components($nid) {
/**
* Implements hook_webform_submission_load().
*/
* Commented out to solve issue https://www.drupal.org/project/addressfield_tokens/issues/2674752
*
function addressfield_tokens_webform_submission_load(&$submissions) {
$submissions_reset = reset($submissions);
$nid = $submissions_reset->nid;
$components = _addressfield_tokens_webform_components($nid);
foreach ($submissions as $sid => $submission) {
foreach ($components as $cid => $component) {
if (!empty($submission->data[$cid])) {
@ -421,17 +485,40 @@ function addressfield_tokens_webform_submission_load(&$submissions) {
$data = empty($data) ? array() : unserialize($data);
$addresses[$delta] = $data;
}
drupal_array_set_nested_value($submission->data, $parents, $addresses);
if (count($addresses)) {
drupal_array_set_nested_value($submission->data, $parents, $addresses);
}
}
}
}
}
**/
/**
* Implements hook_webform_validator_alter().
/**
* Implements hook_webform_validator_alter().
*/
function addressfield_tokens_webform_validator_alter(&$validators) {
$validators['unique']['component_types'][] = 'addressfield';
$validators['oneoftwo']['component_types'][] = 'addressfield';
$validators['oneofseveral']['component_types'][] = 'addressfield';
}
/**
* Implements hook_webform_hints_element_alter().
*/
function addressfield_tokens_webform_hints_element_alter(&$element, &$required_label)
{
if ($element['#type'] == 'addressfield_container' || $element['#type'] == 'fieldset') {
$addressfield_tokens_fields_info = addressfield_tokens_field_formatter_info();
foreach ($addressfield_tokens_fields_info['addressfield_components']['settings']['components'] as $component) {
if (isset($element[$component])) {
if (isset($element[$component]['#options'])) {
$element[$component]['#empty_option'] = '- ' . $element[$component]['#title'] . $required_label . ' -';
}
$element[$component]['#attributes']['placeholder'] = $element[$component]['#title'] . $required_label;
$element[$component]['#title_display'] = 'invisible';
}
}
}
}

View File

@ -11,7 +11,13 @@
*/
function theme_addressfield_formatter($vars) {
$address = $vars['address'];
if (isset($address['addressfield'])) {
$address = $address['addressfield'];
}
$handlers = $vars['handlers'];
if (empty($handlers)) {
$handlers = array('address');
}
$out = addressfield_generate($address, $handlers, array('mode' => 'render'));
return '<div class="addressfield">' . render($out) . '</div>';
@ -39,7 +45,7 @@ function theme_addressfield_formatter__citystate($vars) {
// If there's no location, render an alternate
if (empty($out)) {
$out[] = 'Undefined';
return '';
}
// Render the location components
@ -56,6 +62,18 @@ function theme_addressfield_formatter__citystate($vars) {
function theme_addressfield_formatter__linear($vars) {
$loc = $vars['address'];
// If single line name is empty, construct it from first and last name.
if (empty($loc['name_line'])) {
$parts = array();
if (!empty($loc['first_name'])) {
$parts[] = $loc['first_name'];
}
if (!empty($loc['last_name'])) {
$parts[] = $loc['last_name'];
}
$loc['name_line'] = join(' ', $parts);
}
// Determine which location components to render
$out = array();
if (!empty($loc['name_line']) && $vars['name_line']) {
@ -103,9 +121,9 @@ function theme_addressfield_formatter__components($vars) {
$out[$key] = _addressfield_tokens_country($loc['country']);
}
elseif ($key == 'administrative_area_full' && !empty($loc['country']) && !empty($loc['administrative_area'])) {
$out[$key] = _addressfield_tokens_state($loc['country'], $loc['administrative_area']);
$out[$key] = addressfield_tokens_state($loc['country'], $loc['administrative_area']);
}
}
return implode($separator, $out);
}
}

View File

@ -1,36 +1,37 @@
<?php
/**
* @file Provides token replacements for address fields.
*/
* @file
* Provides token replacements for address fields.
*/
/**
* Implements hook_token_info_alter().
*
* Attaches tokens to all addressfield properties. The default names of each
* addressfield component can be altered by administrators according to the site's locale.
*
* @param array $info The existing token info.
*
* Attaches tokens to all addressfield properties. The default names of each
* addressfield component can be altered by administrators according to the
* site's locale.
*/
function addressfield_tokens_token_info_alter(&$info) {
// Define the address field token types
// Define the address field token types.
$info['types']['addressfield'] = array(
'name' => t('Address field'),
'description' => t('An address associated with an entity.'),
'needs-data' => 'addressfield',
);
// Add tokens for each component of the address
$info['tokens'] += array( 'addressfield' => array() );
// Add tokens for each component of the address.
$info['tokens'] += array('addressfield' => array());
$props = addressfield_data_property_info();
$names = addressfield_tokens_property_names();
$params = array(
'@default_country' => addressfield_tokens_default_country(),
);
foreach ($props as $field => $data) {
$fieldtoken = str_replace('_', '-', $field);
if (!empty($names[$field])) {
$name = $names[$field];
$descr = $data['label'];
@ -38,7 +39,7 @@ function addressfield_tokens_token_info_alter(&$info) {
else {
$name = $data['label'];
$descr = $name;
$matches = array();
if (preg_match('/^(.*)\s+\(i\.e\.\s+(.*)\)$/', $name, $matches)) {
$name = $matches[1];
@ -54,7 +55,7 @@ function addressfield_tokens_token_info_alter(&$info) {
}
$info['tokens']['addressfield']['administrative-area']['name'] .= ' (abbreviation)';
$info['tokens']['addressfield']['country']['name'] .= ' (abbreviation)';
// Add tokens for the formatted address and text-only version.
$info['tokens']['addressfield'] += array(
'full' => array(
@ -83,7 +84,7 @@ function addressfield_tokens_token_info_alter(&$info) {
'type' => 'text',
),
);
// Add user tokens that are useful for MailChimp.
if (module_exists('mailchimp')) {
$info['tokens']['addressfield'] += array(
@ -95,9 +96,23 @@ function addressfield_tokens_token_info_alter(&$info) {
);
}
// Attach tokens to all address fields
// Add extra text to webform submission values help.
if (array_key_exists('submission', $info['tokens'])) {
$info['tokens']['submission']['values']['description'] .= '
<div>
' . t('For addressfield components you can also choose individual elements of the address using the syntax field_key:element, For example:') . '
<ul>
<li>[submission:values:address:thoroughfare]</li>
<li>[submission:values:address:locality]</li>
<li>[submission:values:address:administrative_area]</li>
<li>[submission:values:address:postal_code]</li>
<li>[submission:values:address:country]</li>
</ul>
</div>';
}
// Attach tokens to all address fields.
$valid_types = entity_token_types();
$entity_info = entity_get_info();
foreach ($valid_types as $token_type => $type) {
foreach (entity_get_all_property_info($type) as $name => $property) {
@ -116,14 +131,6 @@ function addressfield_tokens_token_info_alter(&$info) {
/**
* Implements hook_tokens().
*
* @param string $type The type of tokens to replace. We are looking for 'addressfield', but can also chain
* addressfields onto entities that have addressfields as properties.
* @param array $tokens The tokens to replace.
* @param array $data The data to use as replacements. We are looking for an 'addressfield' property.
* @param array $options Additional options for the tokenizer.
*
* @return array The replaced values.
*/
function addressfield_tokens_tokens($type, $tokens, array $data = array(), array $options = array()) {
$url_options = array();
@ -140,27 +147,49 @@ function addressfield_tokens_tokens($type, $tokens, array $data = array(), array
$replacements = array();
$last_original = NULL;
// Process address field tokens
if ($type == 'addressfield' && !empty($data['addressfield'])) {
// Process webform submission addressfield tokens.
if ($type == 'submission' && !empty($data['webform-submission'])) {
$submission = $data['webform-submission'];
$node = isset($data['node']) ? $data['node'] : node_load($submission->nid);
$value_tokens = token_find_with_prefix($tokens, 'values');
// Loop through the components of the webform looking for addressfield
// components with the expected form_key.
foreach ($node->webform['components'] as $cid => $component) {
if ($component['type'] == 'addressfield' && isset($submission->data[$cid][0])) {
$address = $submission->data[$cid][0];
if (is_string($address)) {
$address = unserialize($address);
}
$address_tokens = token_find_with_prefix($value_tokens, $component['form_key']);
foreach ($address_tokens as $token => $original) {
if (isset($address[$token])) {
$replacements[$original] = $sanitize ? filter_xss($address[$token]) : $address[$token];
}
}
}
}
}
// Process address field tokens.
elseif ($type == 'addressfield' && !empty($data['addressfield'])) {
foreach ($tokens as $name => $original) {
$last_original = $original;
$name = str_replace('-', '_', $name);
$address = $data['addressfield'];
// If the address field exists, use it.
if (isset($address[$name])) {
$replacements[$original] = $sanitize ? filter_xss($address[$name]) : $address[$name];
}
else {
// Otherwise, it's a special token
// Otherwise, it's a special token.
switch ($name) {
case 'full':
$render = addressfield_generate($address, array('address'), array(
'mode' => 'render',
'mode' => 'render',
));
$replacements[$original] = $sanitize ? filter_xss(drupal_render($render)) : drupal_render($render);
break;
case 'text':
$out = array();
if (!empty($address['thoroughfare'])) {
@ -182,7 +211,7 @@ function addressfield_tokens_tokens($type, $tokens, array $data = array(), array
}
$replacements[$original] = $sanitize ? filter_xss(implode("\n", $out)) : implode("\n", $out);
break;
case 'city_state':
$out = array();
if (!empty($address['locality'])) {
@ -194,20 +223,20 @@ function addressfield_tokens_tokens($type, $tokens, array $data = array(), array
if (!empty($address['country']) && $address['country'] != addressfield_tokens_default_country()) {
$out[] = _addressfield_tokens_country($address['country']);
}
$replacements[$original] = $sanitize ? filter_xss(implode(", ", $out)) : implode(", ", $out);
$replacements[$original] = $sanitize ? filter_xss(implode(", ", $out)) : implode(", ", $out);
break;
case 'state_name':
if (!empty($address['administrative_area']) && !empty($address['country'])) {
if ($sanitize) {
$replacements[$original] = filter_xss(_addressfield_tokens_state($address['country'], $address['administrative_area']));
$replacements[$original] = filter_xss(addressfield_tokens_state($address['country'], $address['administrative_area']));
}
else {
$replacements[$original] = _addressfield_tokens_state($address['country'], $address['administrative_area']);
$replacements[$original] = addressfield_tokens_state($address['country'], $address['administrative_area']);
}
}
break;
case 'country_name':
if (!empty($address['country'])) {
if ($sanitize) {
@ -218,16 +247,23 @@ function addressfield_tokens_tokens($type, $tokens, array $data = array(), array
}
}
break;
case 'mc_address':
$address_components = array('thoroughfare', 'premise', 'locality', 'administrative_area', 'postal_code', 'country');
$address_components = array(
'thoroughfare',
'premise',
'locality',
'administrative_area',
'postal_code',
'country',
);
$mc_address = array();
foreach ($address_components as $component) {
if (!empty($address[$component])) {
$mc_address[] = check_plain($address[$component]);
}
}
// MailChimp requires the address to be a string of double-space
// MailChimp requires the address to be a string of double-space
// delimited address fields. (http://kb.mailchimp.com/article/how-do-i-set-up-the-address-field-type-for-import)
$replacements[$original] = !empty($mc_address) ? implode(' ', $mc_address) : '';
break;
@ -242,7 +278,7 @@ function addressfield_tokens_tokens($type, $tokens, array $data = array(), array
$token_types = entity_token_types();
$info = token_info();
if (isset($info['tokens'][$type])) {
// Otherwise, chain address fields attached to other entities
// Otherwise, chain address fields attached to other entities.
foreach ($info['tokens'][$type] as $name => $token_info) {
if (isset($token_info['type']) && $token_info['type'] == 'addressfield') {
if ($chained_tokens = token_find_with_prefix($tokens, $name)) {
@ -261,6 +297,6 @@ function addressfield_tokens_tokens($type, $tokens, array $data = array(), array
}
}
}
return $replacements;
}
}

View File

@ -3,7 +3,7 @@
/**
* Contains the predefined address formats.
*
* Derived from Google's dataset: https://i18napis.appspot.com/address.
* Derived from Google's dataset: https://chromium-i18n.appspot.com/ssl-address.
*/
/**
@ -40,12 +40,13 @@ function addressfield_get_address_format($country_code) {
// These formats differ from the default only by the presence of the
// postal code in 'used_fields'.
$countries_with_optional_postal_code = array(
'AC', 'AD', 'AL', 'AZ', 'BA', 'BB', 'BD', 'BG', 'BH', 'BM', 'BN', 'BT',
'CR', 'CY', 'DO', 'DZ', 'EC', 'EH', 'ET', 'FO', 'GE', 'GN', 'GT',
'GW', 'HR', 'HT', 'IL', 'IS', 'JO', 'KE', 'KG', 'KH', 'KW', 'LA',
'LA', 'LB', 'LK', 'LR', 'LS', 'MA', 'MC', 'MD', 'ME', 'MG', 'MK', 'MM',
'MT', 'MU', 'MV', 'NE', 'NP', 'OM', 'PK', 'PY', 'RO', 'RS', 'SA', 'SI',
'SN', 'SZ', 'TA', 'TJ', 'TM', 'TN', 'VA', 'VC', 'VG', 'XK', 'ZM',
'AC', 'AD', 'AF', 'AI', 'AL', 'AZ', 'BA', 'BB', 'BD', 'BG', 'BH', 'BM',
'BN', 'BT', 'CU', 'CR', 'CY', 'DO', 'DZ', 'EC', 'EH', 'ET', 'FO', 'GE',
'GN', 'GT', 'GW', 'HR', 'HM', 'HT', 'IL', 'IS', 'JO', 'KE', 'KG', 'KH',
'KP', 'KW', 'LA', 'LB', 'LK', 'LR', 'LS', 'MA', 'MC', 'MD', 'ME', 'MG',
'MK', 'MM', 'MT', 'MU', 'MV', 'NE', 'NP', 'OM', 'PK', 'PY', 'RO', 'RS',
'SA', 'SD', 'SI', 'SN', 'SZ', 'TA', 'TJ', 'TM', 'TN', 'TZ', 'VA', 'VC',
'VG', 'XK', 'ZM',
);
foreach ($countries_with_optional_postal_code as $code) {
$address_formats[$code] = array(
@ -71,6 +72,8 @@ function addressfield_get_address_format($country_code) {
$address_formats['AE'] = array(
'used_fields' => array('administrative_area'),
'administrative_area_label' => t('Emirate'),
'render_administrative_area_value' => TRUE,
'required_fields' => array('administrative_area'),
);
$address_formats['AM'] = array(
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
@ -114,7 +117,7 @@ function addressfield_get_address_format($country_code) {
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
);
$address_formats['CL'] = array(
'used_fields' => array('dependent_locality', 'locality', 'administrative_area', 'postal_code'),
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
'administrative_area_label' => t('State', array(), array('context' => 'Territory of a country')),
'render_administrative_area_value' => TRUE,
);
@ -122,9 +125,11 @@ function addressfield_get_address_format($country_code) {
'used_fields' => array('dependent_locality', 'locality', 'administrative_area', 'postal_code'),
'required_fields' => array('locality', 'administrative_area'),
'dependent_locality_label' => t('District'),
'render_administrative_area_value' => TRUE,
);
$address_formats['CO'] = array(
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
'required_fields' => array('locality', 'administrative_area'),
'administrative_area_label' => t('Department', array(), array('context' => 'Territory of a country')),
);
$address_formats['CV'] = array(
@ -142,6 +147,7 @@ function addressfield_get_address_format($country_code) {
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
'required_fields' => array('locality', 'postal_code'),
'administrative_area_label' => t('County'),
'render_administrative_area_value' => TRUE,
);
$address_formats['ES'] = array(
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
@ -155,7 +161,7 @@ function addressfield_get_address_format($country_code) {
'postal_code_label' => t('ZIP code'),
);
$address_formats['GB'] = array(
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
'used_fields' => array('locality', 'postal_code'),
'required_fields' => array('locality', 'postal_code'),
'locality_label' => t('Town/City'),
'administrative_area_label' => t('County'),
@ -165,8 +171,8 @@ function addressfield_get_address_format($country_code) {
'used_fields' => array('postal_code'),
);
$address_formats['GU'] = array(
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
'required_fields' => array('locality', 'administrative_area', 'postal_code'),
'used_fields' => array('locality', 'postal_code'),
'required_fields' => array('locality', 'postal_code'),
'administrative_area_label' => t('State', array(), array('context' => 'Territory of a country')),
'postal_code_label' => t('ZIP code'),
);
@ -182,13 +188,15 @@ function addressfield_get_address_format($country_code) {
);
$address_formats['ID'] = array(
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
'required_fields' => array('administrative_area'),
'locality_label' => t('City/Regency'),
'render_administrative_area_value' => TRUE,
);
$address_formats['IE'] = array(
'used_fields' => array('locality', 'administrative_area'),
'used_fields' => array('dependent_locality', 'locality', 'administrative_area', 'postal_code'),
'locality_label' => t('Town/City'),
'administrative_area_label' => t('County'),
'postal_code_label' => t('Eircode'),
'render_administrative_area_value' => TRUE,
);
$address_formats['IN'] = array(
@ -293,7 +301,7 @@ function addressfield_get_address_format($country_code) {
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
);
$address_formats['NG'] = array(
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
'used_fields' => array('dependent_locality', 'locality', 'administrative_area', 'postal_code'),
'administrative_area_label' => t('State', array(), array('context' => 'Territory of a country')),
);
$address_formats['NI'] = array(
@ -330,6 +338,7 @@ function addressfield_get_address_format($country_code) {
);
$address_formats['PH'] = array(
'used_fields' => array('dependent_locality', 'locality', 'administrative_area', 'postal_code'),
'render_administrative_area_value' => TRUE,
);
$address_formats['PR'] = array(
'used_fields' => array('locality', 'postal_code'),
@ -344,7 +353,7 @@ function addressfield_get_address_format($country_code) {
);
$address_formats['RU'] = array(
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
'required_fields' => array('locality', 'postal_code'),
'required_fields' => array('locality', 'administrative_area', 'postal_code'),
'render_administrative_area_value' => TRUE,
);
$address_formats['SC'] = array(
@ -389,7 +398,7 @@ function addressfield_get_address_format($country_code) {
);
$address_formats['UA'] = array(
'used_fields' => array('locality', 'administrative_area', 'postal_code'),
'required_fields' => array('locality', 'postal_code'),
'required_fields' => array('locality', 'administrative_area', 'postal_code'),
'administrative_area_label' => t('Region', array(), array('context' => 'Territory of a country')),
);
$address_formats['UM'] = array(

View File

@ -132,7 +132,7 @@ function _addressfield_get_administrative_areas_defaults() {
'AT' => 'Atacama',
'BI' => 'Biobío',
'CO' => 'Coquimbo',
'LI' => 'Libertador General Bernardo O\'Higgins',
'LI' => "Libertador General Bernardo O'Higgins",
'LL' => 'Los Lagos',
'LR' => 'Los Ríos',
'MA' => 'Magallanes y de la Antártica Chilena',
@ -449,112 +449,112 @@ function _addressfield_get_administrative_areas_defaults() {
'AL' => 'Alessandria',
'AN' => 'Ancona',
'AO' => 'Aosta',
'AP' => 'Ascoli Piceno',
'AQ' => "L'Aquila",
'AR' => 'Arezzo',
'AP' => 'Ascoli Piceno',
'AT' => 'Asti',
'AV' => 'Avellino',
'BA' => 'Bari',
'BG' => 'Bergamo',
'BI' => 'Biella',
'BT' => 'Barletta-Andria-Trani',
'BL' => 'Belluno',
'BN' => 'Benevento',
'BG' => 'Bergamo',
'BI' => 'Biella',
'BO' => 'Bologna',
'BR' => 'Brindisi',
'BS' => 'Brescia',
'BT' => 'Barletta-Andria-Trani',
'BZ' => 'Bolzano/Bozen',
'BS' => 'Brescia',
'BR' => 'Brindisi',
'CA' => 'Cagliari',
'CB' => 'Campobasso',
'CE' => 'Caserta',
'CH' => 'Chieti',
'CI' => 'Carbonia-Iglesias',
'CL' => 'Caltanissetta',
'CN' => 'Cuneo',
'CO' => 'Como',
'CR' => 'Cremona',
'CS' => 'Cosenza',
'CB' => 'Campobasso',
'CI' => 'Carbonia-Iglesias',
'CE' => 'Caserta',
'CT' => 'Catania',
'CZ' => 'Catanzaro',
'CH' => 'Chieti',
'CO' => 'Como',
'CS' => 'Cosenza',
'CR' => 'Cremona',
'KR' => 'Crotone',
'CN' => 'Cuneo',
'EN' => 'Enna',
'FC' => 'Forlì-Cesena',
'FE' => 'Ferrara',
'FG' => 'Foggia',
'FI' => 'Firenze',
'FM' => 'Fermo',
'FE' => 'Ferrara',
'FI' => 'Firenze',
'FG' => 'Foggia',
'FC' => 'Forlì-Cesena',
'FR' => 'Frosinone',
'GE' => 'Genova',
'GO' => 'Gorizia',
'GR' => 'Grosseto',
'IM' => 'Imperia',
'IS' => 'Isernia',
'KR' => 'Crotone',
'LC' => 'Lecco',
'AQ' => "L'Aquila",
'SP' => 'La Spezia',
'LT' => 'Latina',
'LE' => 'Lecce',
'LC' => 'Lecco',
'LI' => 'Livorno',
'LO' => 'Lodi',
'LT' => 'Latina',
'LU' => 'Lucca',
'MB' => 'Monza e Brianza',
'MC' => 'Macerata',
'ME' => 'Messina',
'MI' => 'Milano',
'MN' => 'Mantova',
'MO' => 'Modena',
'MS' => 'Massa-Carrara',
'MT' => 'Matera',
'VS' => 'Medio Campidano',
'ME' => 'Messina',
'MI' => 'Milano',
'MO' => 'Modena',
'MB' => 'Monza e Brianza',
'NA' => 'Napoli',
'NO' => 'Novara',
'NU' => 'Nuoro',
'OG' => 'Ogliastra',
'OR' => 'Oristano',
'OT' => 'Olbia-Tempio',
'PA' => 'Palermo',
'PC' => 'Piacenza',
'OR' => 'Oristano',
'PD' => 'Padova',
'PE' => 'Pescara',
'PG' => 'Perugia',
'PI' => 'Pisa',
'PN' => 'Pordenone',
'PO' => 'Prato',
'PA' => 'Palermo',
'PR' => 'Parma',
'PT' => 'Pistoia',
'PU' => 'Pesaro e Urbino',
'PV' => 'Pavia',
'PG' => 'Perugia',
'PU' => 'Pesaro e Urbino',
'PE' => 'Pescara',
'PC' => 'Piacenza',
'PI' => 'Pisa',
'PT' => 'Pistoia',
'PN' => 'Pordenone',
'PZ' => 'Potenza',
'PO' => 'Prato',
'RG' => 'Ragusa',
'RA' => 'Ravenna',
'RC' => 'Reggio Calabria',
'RE' => 'Reggio Emilia',
'RG' => 'Ragusa',
'RI' => 'Rieti',
'RM' => 'Roma',
'RN' => 'Rimini',
'RM' => 'Roma',
'RO' => 'Rovigo',
'SA' => 'Salerno',
'SI' => 'Siena',
'SO' => 'Sondrio',
'SP' => 'La Spezia',
'SR' => 'Siracusa',
'SS' => 'Sassari',
'SV' => 'Savona',
'SI' => 'Siena',
'SR' => 'Siracusa',
'SO' => 'Sondrio',
'TA' => 'Taranto',
'TE' => 'Teramo',
'TN' => 'Trento',
'TR' => 'Terni',
'TO' => 'Torino',
'TP' => 'Trapani',
'TR' => 'Terni',
'TS' => 'Trieste',
'TN' => 'Trento',
'TV' => 'Treviso',
'TS' => 'Trieste',
'UD' => 'Udine',
'VA' => 'Varese',
'VE' => 'Venezia',
'VB' => 'Verbano-Cusio-Ossola',
'VC' => 'Vercelli',
'VE' => 'Venezia',
'VI' => 'Vicenza',
'VR' => 'Verona',
'VS' => 'Medio Campidano',
'VT' => 'Viterbo',
'VV' => 'Vibo Valentia',
'VI' => 'Vicenza',
'VT' => 'Viterbo',
);
$administrative_areas['JM'] = array(
'13' => 'Clarendon',
@ -663,11 +663,11 @@ function _addressfield_get_administrative_areas_defaults() {
'BCN' => 'Baja California',
'BCS' => 'Baja California Sur',
'CAM' => 'Campeche',
'CMX' => 'Ciudad de México',
'COA' => 'Coahuila',
'COL' => 'Colima',
'CHP' => 'Chiapas',
'CHH' => 'Chihuahua',
'DIF' => 'Distrito Federal',
'DUG' => 'Durango',
'MEX' => 'Estado de México',
'GUA' => 'Guanajuato',
@ -709,7 +709,7 @@ function _addressfield_get_administrative_areas_defaults() {
'13' => t('Sarawak'),
'10' => t('Selangor'),
'11' => t('Terengganu'),
);
);
$administrative_areas['PE'] = array(
'AMA' => 'Amazonas',
'ANC' => 'Ancash',
@ -737,6 +737,90 @@ function _addressfield_get_administrative_areas_defaults() {
'TUM' => 'Tumbes',
'UCA' => 'Ucayali',
);
$administrative_areas['PH'] = array(
'ABR' => 'Abra',
'AGN' => 'Agusan del Norte',
'AGS' => 'Agusan del Sur',
'AKL' => 'Aklan',
'ALB' => 'Albay',
'ANT' => 'Antique',
'APA' => 'Apayao',
'AUR' => 'Aurora',
'BAS' => 'Basilan',
'BAN' => 'Bataan',
'BTN' => 'Batanes',
'BTG' => 'Batangas',
'BEN' => 'Benguet',
'BIL' => 'Biliran',
'BOH' => 'Bohol',
'BUK' => 'Bukidnon',
'BUL' => 'Bulacan',
'CAG' => 'Cagayan',
'CAN' => 'Camarines Norte',
'CAS' => 'Camarines Sur',
'CAM' => 'Camiguin',
'CAP' => 'Capiz',
'CAT' => 'Catanduanes',
'CAV' => 'Cavite',
'CEB' => 'Cebu',
'COM' => 'Compostela Valley',
'NCO' => 'Cotabato',
'DAV' => 'Davao del Norte',
'DAS' => 'Davao del Sur',
'a9d' => 'Davao Occidental',
'DAO' => 'Davao Oriental',
'DIN' => 'Dinagat Islands',
'EAS' => 'Eastern Samar',
'GUI' => 'Guimaras',
'IFU' => 'Ifugao',
'ILN' => 'Ilocos Norte',
'ILS' => 'Ilocos Sur',
'ILI' => 'Iloilo',
'ISA' => 'Isabela',
'KAL' => 'Kalinga',
'LUN' => 'La Union',
'LAG' => 'Laguna',
'LAN' => 'Lanao del Norte',
'LAS' => 'Lanao del Sur',
'LEY' => 'Leyte',
'MAG' => 'Maguindanao',
'MAD' => 'Marinduque',
'MAS' => 'Masbate',
'00' => 'Metro Manila',
'MDC' => 'Mindoro Occidental',
'MDR' => 'Mindoro Oriental',
'MSC' => 'Misamis Occidental',
'MSR' => 'Misamis Oriental',
'MOU' => 'Mountain Province',
'NEC' => 'Negros Occidental',
'NER' => 'Negros Oriental',
'NSA' => 'Northern Samar',
'NUE' => 'Nueva Ecija',
'NUV' => 'Nueva Vizcaya',
'PLW' => 'Palawan',
'PAM' => 'Pampanga',
'PAN' => 'Pangasinan',
'QUE' => 'Quezon Province',
'QUI' => 'Quirino',
'RIZ' => 'Rizal',
'ROM' => 'Romblon',
'WSA' => 'Samar',
'SAR' => 'Sarangani',
'SIG' => 'Siquijor',
'SOR' => 'Sorsogon',
'SCO' => 'South Cotabato',
'SLE' => 'Southern Leyte',
'SUK' => 'Sultan Kudarat',
'SLU' => 'Sulu',
'SUN' => 'Surigao del Norte',
'SUR' => 'Surigao del Sur',
'TAR' => 'Tarlac',
'TAW' => 'Tawi-Tawi',
'ZMB' => 'Zambales',
'ZAN' => 'Zamboanga del Norte',
'ZAS' => 'Zamboanga del Sur',
'ZSI' => 'Zamboanga Sibuguey',
);
$administrative_areas['RU'] = array(
'MOW' => t('Moskva'),
'SPE' => t('Sankt-Peterburg'),
@ -931,31 +1015,31 @@ function _addressfield_get_administrative_areas_defaults() {
);
$administrative_areas['UA'] = array(
'43' => t('Crimea'),
'05' => t('Vinnyts\'ka oblast'),
'07' => t('Volyns\'ka oblast'),
'05' => t("Vinnyts'ka oblast"),
'07' => t("Volyns'ka oblast"),
'12' => t('Dnipropetrovsk Oblast'),
'14' => t('Donetsk Oblast'),
'18' => t('Zhytomyrs\'ka oblast'),
'21' => t('Zakarpats\'ka oblast'),
'23' => t('Zaporiz\'ka oblast'),
'26' => t('Ivano-Frankivs\'ka oblast'),
'30' => t('Kiev Oblast'),
'35' => t('Kirovohrads\'ka oblast'),
'09' => t('Luhans\'ka oblast'),
'18' => t("Zhytomyrs'ka oblast"),
'21' => t("Zakarpats'ka oblast"),
'23' => t("Zaporiz'ka oblast"),
'26' => t("Ivano-Frankivs'ka oblast"),
'30' => t('Kyiv Oblast'),
'35' => t("Kirovohrads'ka oblast"),
'09' => t("Luhans'ka oblast"),
'46' => t('Lviv Oblast'),
'48' => t('Mykolaivs\'ka oblast'),
'48' => t("Mykolaivs'ka oblast"),
'51' => t('Odessa Oblast'),
'53' => t('Poltavs\'ka oblast'),
'56' => t('Rivnens\'ka oblast'),
'40' => t('Sevastopol\' city'),
'59' => t('Sums\'ka oblast'),
'61' => t('Ternopil\'s\'ka oblast'),
'53' => t("Poltavs'ka oblast"),
'56' => t("Rivnens'ka oblast"),
'40' => t("Sevastopol' city"),
'59' => t("Sums'ka oblast"),
'61' => t("Ternopil's'ka oblast"),
'63' => t('Kharkiv Oblast'),
'65' => t('Khersons\'ka oblast'),
'68' => t('Khmel\'nyts\'ka oblast'),
'71' => t('Cherkas\'ka oblast'),
'77' => t('Chernivets\'ka oblast'),
'74' => t('Chernihivs\'ka oblast'),
'65' => t("Khersons'ka oblast"),
'68' => t("Khmel'nyts'ka oblast"),
'71' => t("Cherkas'ka oblast"),
'77' => t("Chernivets'ka oblast"),
'74' => t("Chernihivs'ka oblast"),
);
$administrative_areas['US'] = array(
'AL' => t('Alabama'),

View File

@ -41,9 +41,9 @@ function _addressfield_sample_addresses() {
if (!isset($fields)) {
$filepath = DRUPAL_ROOT . '/' . drupal_get_path('module', 'addressfield');
$fields = array();
if ($handle = @fopen("$filepath/addresses.txt",'r')) {
if ($handle = @fopen("$filepath/addresses.txt", 'r')) {
if (is_resource($handle)) {
while (($buffer = fgets($handle)) !== false) {
while (($buffer = fgets($handle)) !== FALSE) {
list($country, $administrative_area, $sub_administrative_area, $locality, $dependent_locality, $postal_code, $thoroughfare, $premise, $sub_premise) = explode("\t", $buffer);
$fields[] = array(
'country' => ($country == 'NULL') ? NULL : trim($country),

View File

@ -36,10 +36,20 @@ function addressfield_feeds_processor_targets_alter(&$targets, $entity_type, $bu
* A string identifying the target on the node.
* @param $values
* The value to populate the target with.
* @param array $mapping
* Associative array of the mapping settings from the per mapping
* configuration form.
*/
function addressfield_set_target($source, $entity, $target, $values) {
function addressfield_set_target($source, $entity, $target, $values, $mapping) {
$language = $mapping['language'];
list($field_name, $sub_field) = explode(':', $target, 2);
// Field info and instance are required for setting default values.
$entity_type = $source->importer->processor->entityType();
list(, , $bundle_name) = entity_extract_ids($entity_type, $entity);
$info = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle_name);
// Convert the values into an array if it isn't one already to correspond to
// Drupal's handling of field value arrays.
if (!is_array($values)) {
@ -48,12 +58,16 @@ function addressfield_set_target($source, $entity, $target, $values) {
// If the field is already set on the given entity, update the existing value
// array. Otherwise start with a fresh field value array.
$field = isset($entity->$field_name) ? $entity->$field_name : array();
$field = isset($entity->{$field_name}) ? $entity->{$field_name} : array();
// Loop over the field values array...
foreach ($values as $delta => $value) {
$field[LANGUAGE_NONE][$delta][$sub_field] = $value;
// Set defaults for new values.
if (!isset($field[$language][$delta])) {
$field[$language][$delta] = addressfield_default_values($info, $instance);
}
$field[$language][$delta][$sub_field] = $value;
}
$entity->$field_name = $field;
$entity->{$field_name} = $field;
}

View File

@ -10,9 +10,8 @@ files[] = views/addressfield_views_handler_field_administrative_area.inc
files[] = views/addressfield_views_handler_field_country.inc
files[] = views/addressfield_views_handler_filter_country.inc
; Information added by Drupal.org packaging script on 2015-10-07
version = "7.x-1.2"
; Information added by Drupal.org packaging script on 2018-10-26
version = "7.x-1.3"
core = "7.x"
project = "addressfield"
datestamp = "1444254070"
datestamp = "1540579391"

View File

@ -5,6 +5,11 @@
* Base integration with the Migrate API class.
*/
// Avoid issues when migrate module is disabled.
if (!class_exists('MigrateFieldHandler')) {
return;
}
/**
* Implements hook_migrate_api().
*/

View File

@ -52,6 +52,37 @@ function addressfield_module_implements_alter(&$implementations, $hook) {
}
}
/**
* Returns a list of address fields optionally filtered by entity type.
*
* @param string $entity_type
* Optional machine-name of an entity type to filter the returned array by.
*
* @return array
* An array of address field mapping data.
*/
function addressfield_get_address_fields($entity_type = '') {
$fields = &drupal_static(__FUNCTION__ . '_' . $entity_type);
if (isset($fields)) {
return $fields;
}
// Get mapping data for all address fields.
$fields = array_filter(field_info_field_map(), 'addressfield_field_map_filter');
// Filter the list of fields by entity type if specified.
if (!empty($fields) && !empty($entity_type)) {
foreach ($fields as $field_name => $field) {
if (!isset($field['bundles'][$entity_type])) {
unset($fields[$field_name]);
}
}
}
return $fields;
}
/**
* Returns TRUE if a field map array value represents an addressfield.
*
@ -305,7 +336,14 @@ function theme_addressfield_container($variables) {
if (strlen($element['#children']) > 0) {
$output = '<' . $element['#tag'] . drupal_attributes($element['#attributes']) . '>';
$output .= $element['#children'];
$output .= '</' . $element['#tag'] . ">";
$output .= '</' . $element['#tag'] . '>';
// Add a linebreak to the HTML after a div. This is invisible on the
// rendered page but improves the appearance of address field output when
// HTML tags are stripped, such as by Views Data Export.
if ($element['#tag'] == 'div') {
$output .= PHP_EOL;
}
return $output;
}
else {
@ -446,7 +484,7 @@ function addressfield_field_presave($entity_type, $entity, $field, $instance, $l
// spaces to single spaces.
foreach ($item as $key => &$value) {
if (!in_array($key, array('data')) && is_string($value)) {
$value = trim(str_replace(' ', ' ', $value));
$value = trim(preg_replace('/[[:blank:]]{2,}/u', ' ', $value));
}
}
}

View File

@ -89,11 +89,13 @@ function addressfield_token_info() {
*/
function addressfield_token_info_alter(&$data) {
// Loop over every address field on the site.
foreach (array_filter(field_info_field_map(), 'addressfield_field_map_filter') as $field_name => $field) {
foreach ($data['tokens'] as $group => $token){
if (isset($data['tokens'][$group][$field_name]) && is_array($data['tokens'][$group][$field_name])) {
// Set the token type for the field to use the addressfield child tokens.
$data['tokens'][$group][$field_name]['type'] = 'address-field';
foreach (addressfield_get_address_fields() as $field_name => $field) {
foreach (array($field_name, strtr($field_name, '_', '-')) as $name) {
foreach ($data['tokens'] as $group => $token) {
if (isset($data['tokens'][$group][$name]) && is_array($data['tokens'][$group][$name])) {
// Set the token type for the field to use the addressfield child tokens.
$data['tokens'][$group][$name]['type'] = 'address-field';
}
}
}
}
@ -212,19 +214,27 @@ function addressfield_tokens($type, $tokens, array $data = array(), array $optio
// and helps us avoid having to do the entity detection ourselves.
if ($type == 'entity') {
// Loop over the address fields defined on the site.
foreach (array_filter(field_info_field_map(), 'addressfield_field_map_filter') as $field_name => $field) {
// If there are any address field tokens in the token list...
if ($addressfield_tokens = token_find_with_prefix($tokens, $field_name)) {
// If the current field is on the matching entity type...
if (!empty($field['bundles'][$data['entity_type']])) {
// Extract the format handlers selected in a representative instance
// settings form for use in formatting tokens.
$instance = field_info_instance($data['entity_type'], $field_name, reset($field['bundles'][$data['entity_type']]));
$format_handlers = $instance['widget']['settings']['format_handlers'];
foreach (addressfield_get_address_fields($data['entity_type']) as $field_name => $field) {
// If the current field is on the matching entity type...
if (!empty($field['bundles'][$data['entity_type']])) {
// Extract the format handlers selected in a representative instance
// settings form for use in formatting tokens.
$instance = field_info_instance($data['entity_type'], $field_name, reset($field['bundles'][$data['entity_type']]));
$format_handlers = $instance['widget']['settings']['format_handlers'];
}
foreach (array($field_name, strtr($field_name, '_', '-')) as $prefix) {
// If there are any address field tokens in the token list...
$addressfield_tokens = token_find_with_prefix($tokens, $prefix);
if (!$addressfield_tokens) {
continue;
}
// Generate the necessary address field tokens for the entity.
$replacements += token_generate('address-field', $addressfield_tokens, array('address-field' => $data['entity']->$field_name, 'format_handlers' => $format_handlers), $options);
if (property_exists($data['entity'], $field_name)) {
// Generate the necessary address field tokens for the entity.
$replacements += token_generate('address-field', $addressfield_tokens, array('address-field' => $data['entity']->$field_name, 'format_handlers' => $format_handlers), $options);
}
}
}
}

View File

@ -7,9 +7,8 @@ hidden = TRUE
dependencies[] = ctools
dependencies[] = addressfield
; Information added by Drupal.org packaging script on 2015-10-07
version = "7.x-1.2"
; Information added by Drupal.org packaging script on 2018-10-26
version = "7.x-1.3"
core = "7.x"
project = "addressfield"
datestamp = "1444254070"
datestamp = "1540579391"

View File

@ -0,0 +1,24 @@
<?php
/**
* @file
* Hide the administrative area field.
*/
$plugin = array(
'title' => t('Hide the administrative area'),
'format callback' => 'addressfield_format_address_hide_administrative_area',
'type' => 'address',
'weight' => -84,
);
/**
* Format callback.
*
* @see CALLBACK_addressfield_format_callback()
*/
function addressfield_format_address_hide_administrative_area(&$format, $address, $context = array()) {
if (isset($format['locality_block']['administrative_area'])) {
$format['locality_block']['administrative_area']['#access'] = FALSE;
}
}

View File

@ -20,7 +20,7 @@ $plugin = array(
function addressfield_format_address_hide_country(&$format, $address, $context = array()) {
// Hide the country element only if the whole field is required, otherwise
// there will always be an additional None option.
if ($context['mode'] == 'form' && $context['instance']['required']) {
if ($context['mode'] == 'form' && (empty($context['instance']) || $context['instance']['required'])) {
if (!empty($format['country']['#options']) && count($format['country']['#options']) == 1) {
$format['country']['#access'] = FALSE;
}

View File

@ -0,0 +1,24 @@
<?php
/**
* @file
* Hide the locality field.
*/
$plugin = array(
'title' => t('Hide the locality'),
'format callback' => 'addressfield_format_address_hide_locality',
'type' => 'address',
'weight' => -84,
);
/**
* Format callback.
*
* @see CALLBACK_addressfield_format_callback()
*/
function addressfield_format_address_hide_locality(&$format, $address, $context = array()) {
if (isset($format['locality_block']['locality'])) {
$format['locality_block']['locality']['#access'] = FALSE;
}
}

View File

@ -240,7 +240,12 @@ function addressfield_format_address_generate(&$format, $address, $context = arr
if (in_array($address['country'], $countries_postal_code_after_locality)) {
// Take the widget out of the array.
$postal_code_widget = $format['locality_block']['postal_code'];
$postal_code_widget['#prefix'] = ' ';
// If the postal code not in its own row, add a separating space.
if (empty($postal_code_widget['#tag'])) {
$postal_code_widget['#prefix'] = ' ';
}
unset($format['locality_block']['postal_code']);
// Add it back.

View File

View File

@ -1,5 +1,4 @@
<?php
// $Id$
/**
* @file
@ -12,24 +11,26 @@
* When you want to use the Autocomplete Deluxe element, you have to choose
* between two types sources for the suggestion data: Ajax Callbacks or Lists.
* You can set the source type by using the appropriate options:
* - #autocomplete_deluxe_path expects a string with an url, that points to the ajax
* callback. The response should be encoded as json(like for the core
* - #autocomplete_deluxe_path expects a string with an url, that points to the
* ajax callback. The response should be encoded as json (like for the core
* autocomplete).
* - #autocomplete_options needs an array in the form of an array(similar to #options in core
* for selects or checkboxes): array('a', 'b', 'c') or array(1 => 'a', 2 =>
* 'b', 3 => 'c').
* - #autocomplete_options needs an array in the form of an array (similar to
* #options in core for selects or checkboxes): array('a', 'b', 'c') or
* array(1 => 'a', 2 => 'b', 3 => 'c').
*
* Besides this two, there are three other options, wich autocomplete deluxe
* Besides these two, there are four other options which autocomplete deluxe
* accepts:
* - #multiple Indicates whether the user may select more than one item. Expects
* TRUE or FALSE, by default it is set to FALSE.
* - #autocomplete_multiple_delimiter If #multiple is TRUE, then you can use
* this option to set a seperator for multiple values. By default a string
* with the follwing content will be used: ', '.
* - #autocomplete_min_length Indicates how many characters must be entered
* until, the suggesion list can be opened. Especially helpfull, when your
* - #delimiter If #multiple is TRUE, then you can use this option to set a
* seperator for multiple values. By default a string with the following
* content will be used: ', '.
* - #min_length Indicates how many characters must be entered
* until, the suggesion list can be opened. Especially helpful, when your
* ajax callback returns only valid suggestion for a minimum characters.
* The default is 0.
* - #not_found_message A message text which will be displayed, if the entered
* term was not found.
*/
function somefunction() {
switch ($type) {
@ -38,16 +39,18 @@ function somefunction() {
'#type' => 'autocomplete_deluxe',
'#autocomplete_options' => $options,
'#multiple' => FALSE,
'#autocomplete_min_length' => 0,
'#min_length' => 0,
);
break;
case 'ajax':
$element = array(
'#type' => 'autocomplete_deluxe',
'#autocomplete_deluxe_path' => url('some_uri', array('absolute' => TRUE)),
'#multiple' => TRUE,
'#autocomplete_min_length' => 1,
'#autocomplete_multiple_delimiter' => '|',
'#min_length' => 1,
'#delimiter' => '|',
'#not_found_message' => "The term '@term' will be added.",
);
break;
}

View File

@ -5,9 +5,9 @@ core = 7.x
files[] = autocomplete_deluxe.module
dependencies[] = taxonomy
; Information added by Drupal.org packaging script on 2017-01-11
version = "7.x-2.2"
; Information added by Drupal.org packaging script on 2017-07-25
version = "7.x-2.3"
core = "7.x"
project = "autocomplete_deluxe"
datestamp = "1484128687"
datestamp = "1501005546"

View File

@ -1,9 +1,83 @@
Entity Translation 7.x-1.x, xxxx-xx-xx
--------------------------------------
#2189567 by stefanos.petrakis, jmuzz: Fixed problems enabling and disabling translation on
a field with revisions enabled.
#2798721 by gnucifer: Path scheme not initialized on delete translation confirmation form.
#3025770 by sudheeshps, patelmayank7552, stefanos.petrakis: PHP 7.0
Compatibility
#2536292 by plopesc, stefanos.petrakis: "Warning: array_merge(): Argument #2 is
not an array in entity_translation_menu_alter" When 'page arguments' or
'access arguments' are not defined in the original item
#2921298 by Stevel, stefanos.petrakis: Missing test dependencies
Entity Translation 7.x-1.0, 2018-04-01
--------------------------------------
(no change)
Entity Translation 7.x-1.0-rc1, 2018-03-21
------------------------------------------
#2927815 by Skabbkladden, dawehner: Infinite loop with language neutral alias
and pathauto.
#2890683 by tobiberlin, stefanos.petrakis: entity_translation_field_attach_form()
adding unwanted fields to form.
#2907366 by swati.nuna: Invalid hook hook_translation_info() mentioned in
docblock for entity_translation_edit_form_info().
#1799770 by plach, bforchhammer: Update id and bundle when setting a wrapped
entity.
#2339315 by plach: Added documentation for
hook_entity_translation_source_field_state_alter().
#1506054 by plach, Owen Barton: Taxonomy term reference: language-aware widget
and autocomplete.
#2907275 by jalpesh: CSS class name misspelled in entity_translation_overview().
Entity Translation 7.x-1.0-beta7, 2017-08-21
--------------------------------------------
#2903195 by stefanos.petrakis, BAHbKA, plach: Enabling translation for not
empty taxonomy vocabulary.
#2425825 by jamesrward: Update from before beta4 fails.
#2900094 by joseph.olstad: code sniffer drupal.org recommended code syntax
fixes.
#2883805 by mikran: entity_translation_update_7008 is broken when taxonomy
module is not enabled.
#2877074 by plach, stefanos.petrakis, joseph.olstad: Refactor the
entity_translation_language() callback to make it bundle-specific.
#2743685 by stefanos.petrakis, bwaindwain: Pathauto update for all translations
for a single node.
#2899658 by joseph.olstad, stefanos.petrakis: entity_translation_upgrade_do
needs to automatically create the entity_translation row for the source
language in entity_translation.
#1800158 by meichr: Entity Translation Upgrade, drush extension.
#2885858 by StephaneQ: Can't delete taxonomy term translation since beta6
update.
#2572203 by mattew: Wrong query generated when several Translations
relationships are used.
#2824255 by ccarrascal, plach: Notice: Undefined index for language in
setTranslation().
#2750179 by wadmiraal: Allowed default translation handler to be completely
overridden.
#2307937 by meichr, joseph.olstad: Batch API needs $context['results'] handled
as an array, not a value.
#1175170 by hgoto, Pisco, alberto56: Optionally enable disabled languages for
entity translation.
#2870524 by stefanos.petrakis, joseph.olstad: Field copy fails with content
translation for fields with entity translation enabled.
Entity Translation 7.x-1.0-beta6, 2017-03-03
--------------------------------------------
#2849464 by plach, czigor, stefanos.petrakis: Added an API to set the active
language.
#2438615 by David_Rothstein, matthiasm11: New nodes are always created using
LANGUAGE_NONE (only changed to the correct language during form submission).
#2820454 by Dylan Donkersgoed: PHP notice when attaching single field
with field_attach_form.
#2826297 by Thomas Cys, dwebpoint, stefanos.petrakis: 'clone' is a reserved
keyword introduced in PHP version 5.0 and cannot be invoked as a function.
#2189567 by stefanos.petrakis, jmuzz: Fixed problems enabling and disabling
translation on a field with revisions enabled.
#2798721 by gnucifer: Path scheme not initialized on delete translation
confirmation form.
#2215771 by colan, stefanos.petrakis: Fixed "Undefined index: translate in
EntityTranslationDefaultHandler->entityForm()".
#1829636 by bforchhammer, stefanos.petrakis: Fixed "All languages" suffix not

View File

@ -9,8 +9,9 @@ Project maintainers
The Entity translation maintainers oversee the development of the project as a
whole. The project maintainers for Entity translation are:
- Francesco Placella 'plach' <http://drupal.org/user/183211>
- Daniel F. Kudwien 'sun' <http://drupal.org/user/54136>
- Francesco Placella 'plach' <https://www.drupal.org/u/plach>
- Daniel F. Kudwien 'sun' <https://www.drupal.org/u/sun>
- Stefanos Petrakis 'stefanos.petrakis' <https://www.drupal.org/u/stefanospetrakis>
Component maintainers
@ -25,14 +26,15 @@ http://drupal.org/node/363367 to find out how to become a component maintainer.
Current component maintainers for Entity translation:
Base system
- Francesco Placella 'plach' <http://drupal.org/user/183211>
- Benedikt Forchhammer 'bforchhammer' <http://drupal.org/user/216396>
- Francesco Placella 'plach' <https://www.drupal.org/u/plach>
- Benedikt Forchhammer 'bforchhammer' <https://www.drupal.org/u/bforchhammer>
- Stefanos Petrakis 'stefanos.petrakis' <https://www.drupal.org/u/stefanospetrakis>
Menu integration
- Benedikt Forchhammer 'bforchhammer' <http://drupal.org/user/216396>
- Benedikt Forchhammer 'bforchhammer' <https://www.drupal.org/u/bforchhammer>
Views integration
- Fabian Sörqvist 'fabsor' <http://drupal.org/user/255704>
- Fabian Sörqvist 'fabsor' <https://www.drupal.org/u/fabsor>
Node translation upgrade
- Francesco Placella 'plach' <http://drupal.org/user/183211>
- Francesco Placella 'plach' <https://www.drupal.org/u/plach>

View File

@ -18,6 +18,13 @@ function entity_translation_admin_form($form, $form_state) {
'#default_value' => variable_get('locale_field_language_fallback', TRUE),
);
$form['entity_translation_languages_enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Use only enabled languages'),
'#description' => t('If checked, disabled languages will not show up as available languages. This setting does not apply to content types that are configured to use the Multilingual content (i18n_node) module as this module provides its own configuration.'),
'#default_value' => variable_get('entity_translation_languages_enabled', FALSE),
);
$form['entity_translation_show_fallback_on_overview_pages'] = array(
'#type' => 'checkbox',
'#title' => t('Show fallback statuses on overview pages'),
@ -45,7 +52,7 @@ function entity_translation_admin_form($form, $form_state) {
);
$_null = NULL;
list($items, ) = menu_router_build();
list($items,) = menu_router_build();
_entity_translation_validate_path_schemes($_null, FALSE, $items);
foreach (entity_get_info() as $entity_type => $info) {
@ -316,7 +323,7 @@ function entity_translation_overview($entity_type, $entity, $callback = NULL) {
$link = isset($add_links->links[$langcode]['href']) ? $add_links->links[$langcode] : array('href' => $add_path, 'language' => $language);
$link['query'] = isset($_GET['destination']) ? drupal_get_destination() : FALSE;
$options[] = $translatable ? l(t('add'), $link['href'], $link) : t('No translatable fields');
$classes[] = $translatable ? '' : 'non-traslatable';
$classes[] = $translatable ? '' : 'non-translatable';
}
$status = t('Not translated');
// Show fallback information if required.
@ -389,7 +396,7 @@ function _entity_translation_label($entity_type, $entity, $langcode = NULL) {
/**
* Theme wrapper for the entity translation language page.
*/
function theme_entity_translation_overview($variables){
function theme_entity_translation_overview($variables) {
$rows = $variables['rows'];
return theme('table', array(
'rows' => $rows,
@ -400,7 +407,7 @@ function theme_entity_translation_overview($variables){
/**
* Theme wrapper for the entity translation language outdated translation.
*/
function theme_entity_translation_overview_outdated($variables){
function theme_entity_translation_overview_outdated($variables) {
$message = $variables['message'];
return ' - <span class="marker">' . $message . '</span>';
}
@ -410,7 +417,7 @@ function theme_entity_translation_overview_outdated($variables){
*/
function entity_translation_delete_confirm($form, $form_state, $entity_type, $entity, $langcode) {
$handler = entity_translation_get_handler($entity_type, $entity);
$handler->setFormLanguage($langcode);
$handler->setActiveLanguage($langcode);
$handler->initPathScheme();
$languages = language_list();
@ -452,7 +459,7 @@ function entity_translation_delete_confirm_submit($form, &$form_state) {
$form_state['redirect'] = $handler->getTranslatePath();
}
/*
/**
* Confirm form for changing field translatability.
*/
function entity_translation_translatable_form($form, &$form_state, $field_name) {
@ -547,7 +554,7 @@ function entity_translation_translatable_form_submit($form, $form_state) {
batch_set($batch);
}
/*
/**
* Toggle translatability of the given field.
*
* This is called from a batch operation, but should only run once per field.
@ -602,12 +609,28 @@ function entity_translation_translatable_batch($translatable, $field_name, $copy
->range($offset, $limit)
->execute();
foreach ($result as $entity_type => $revisions) {
foreach ($revisions as $revision) {
// $revision is a partial entity object that will be used as an array of
// conditions.
$conditions = (array) $revision;
$entity = reset(entity_load($entity_type, FALSE, $conditions));
foreach ($result as $entity_type => $partial_entities) {
// Load all revisionable entities' revisions.
if (EntityTranslationDefaultHandler::isEntityTypeRevisionable($entity_type)) {
$entities = array();
// Extract the revision identifier from the entity's definition.
$entity_info = entity_get_info($entity_type);
$revision_id_key = $entity_info['entity keys']['revision'];
// Load each revisionable entity's revision using $conditions, which
// should include the revision id information.
foreach ($partial_entities as $partial_entity) {
$conditions = (array) $partial_entity;
$revision_condition = array_intersect_key($conditions, array($revision_id_key => $revision_id_key));
$entity_revisions = entity_load($entity_type, FALSE, $revision_condition);
$entities[] = reset($entity_revisions);
}
}
else {
$entities = entity_load($entity_type, array_keys($partial_entities));
}
foreach ($entities as $entity) {
$context['sandbox']['progress']++;
$handler = entity_translation_get_handler($entity_type, $entity);
$langcode = $handler->getLanguage();
@ -626,9 +649,10 @@ function entity_translation_translatable_batch($translatable, $field_name, $copy
if ($translatable && isset($entity->{$field_name}[LANGUAGE_NONE])) {
// If the field is being switched to translatable and has data for
// LANGUAGE_NONE then we need to move the data to the right language.
// In case the 'Copy translations' option was selected, move the
// available LANGUAGE_NONE field data into all existing translation
// languages, otherwise only into the entity's language.
$translations = $handler->getTranslations();
if ($copy_all_languages && !empty($translations->data)) {
foreach ($translations->data as $translation) {
$entity->{$field_name}[$translation['language']] = $entity->{$field_name}[LANGUAGE_NONE];

View File

@ -164,3 +164,17 @@ function hook_entity_translation_delete($entity_type, $entity, $langcode) {
*/
function hook_entity_translation_delete_revision($entity_type, $entity, $langcode) {
}
/**
* Allows to sets the right values in the form state when adding a translation.
*/
function hook_entity_translation_source_field_state_alter(&$field_state) {
if (isset($field_state['entity'])) {
module_load_include('inc', 'entity', 'includes/entity.ui');
foreach ($field_state['entity'] as $delta => $entity) {
if ($entity instanceof FieldCollectionItemEntity) {
$field_state['entity'][$delta] = entity_ui_clone_entity('field_collection_item', $entity);
}
}
}
}

View File

@ -5,6 +5,9 @@ core = 7.x
configure = admin/config/regional/entity_translation
dependencies[] = locale (>7.14)
test_dependencies[] = pathauto:pathauto
test_dependencies[] = title:title
files[] = includes/translation.handler_factory.inc
files[] = includes/translation.handler.inc
files[] = includes/translation.handler.comment.inc
@ -23,9 +26,8 @@ files[] = views/entity_translation_handler_filter_language.inc
files[] = views/entity_translation_handler_filter_translation_exists.inc
files[] = views/entity_translation_handler_field_field.inc
; Information added by Drupal.org packaging script on 2016-09-28
version = "7.x-1.0-beta5+15-dev"
; Information added by Drupal.org packaging script on 2019-01-20
version = "7.x-1.0+5-dev"
core = "7.x"
project = "entity_translation"
datestamp = "1475057941"
datestamp = "1548022384"

View File

@ -175,6 +175,9 @@ function entity_translation_install() {
// Enable revision support for entity translation.
variable_set('entity_translation_revision_enabled', TRUE);
// Enable taxonomy autocomplete support.
variable_set('entity_translation_taxonomy_autocomplete', TRUE);
}
/**
@ -356,7 +359,11 @@ function entity_translation_update_7006() {
'not null' => FALSE,
'description' => 'The entity revision id this translation relates to',
);
db_add_field('entity_translation', 'revision_id', $spec);
// Create revision id column if it doesn't exist already.
if (!db_field_exists('entity_translation', 'revision_id')) {
db_add_field('entity_translation', 'revision_id', $spec);
}
// Create the entity translation revision schema.
$table = array(
@ -429,7 +436,11 @@ function entity_translation_update_7006() {
'primary key' => array('entity_type', 'revision_id', 'language'),
'indexes'=> array('revision_id' => array('revision_id')),
);
db_create_table('entity_translation_revision', $table);
// Create entity translation revision table if it doesn't exist already.
if (!db_table_exists('entity_translation_revision')) {
db_create_table('entity_translation_revision', $table);
}
}
/**
@ -452,6 +463,9 @@ function entity_translation_update_7007() {
* through it.
*/
function entity_translation_update_7008() {
if (!module_exists('taxonomy')) {
return;
}
// According to EXPLAIN joining {taxonomy_vocabulary} here makes the query
// perform way worse, so we just split into two quick ones.
$query = "SELECT t.vid

View File

@ -244,6 +244,13 @@ function entity_translation_menu() {
'file' => 'entity_translation.admin.inc',
);
$items['entity_translation/taxonomy_term/autocomplete'] = array(
'title' => 'Entity translation autocomplete',
'page callback' => 'entity_translation_taxonomy_term_autocomplete',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
@ -457,7 +464,11 @@ function entity_translation_menu_alter(&$items) {
// Replace the main edit callback with our proxy implementation to set
// form language to the current language and check access.
$entity_position = array_search($scheme['path wildcard'], $edit_path_parts);
$original_item = $edit_item;
// Make sure incoming page and access arguments are arrays.
$original_item = $edit_item + array(
'page arguments' => array(),
'access arguments' => array(),
);
$args = array($entity_type, $entity_position, FALSE, $original_item);
$edit_item['page callback'] = 'entity_translation_edit_page';
$edit_item['page arguments'] = array_merge($args, $original_item['page arguments']);
@ -550,7 +561,7 @@ function entity_translation_edit_page() {
$handler = entity_translation_get_handler($entity_type, $entity);
$handler->initPathScheme();
$langcode = entity_translation_get_existing_language($entity_type, $entity, $langcode);
$handler->setFormLanguage($langcode);
$handler->setActiveLanguage($langcode);
// Display the entity edit form.
return _entity_translation_callback($edit_form_item['page callback'], $args, $edit_form_item);
@ -693,7 +704,7 @@ function entity_translation_add_page() {
$handler = entity_translation_get_handler($entity_type, $entity);
$handler->initPathScheme();
$handler->setFormLanguage($langcode);
$handler->setActiveLanguage($langcode);
$handler->setSourceLanguage($source);
// Display the entity edit form.
@ -918,7 +929,7 @@ function entity_translation_field_language_alter(&$display_language, $context) {
if (isset($translations->data[$context['language']]) && !entity_translation_access($entity_type, $translations->data[$context['language']])) {
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
$instances = field_info_instances($entity_type, $bundle);
$entity = clone($entity);
$entity = clone $entity;
foreach ($translations->data as $langcode => $translation) {
if ($langcode == $context['language'] || !entity_translation_access($entity_type, $translations->data[$langcode])) {
@ -1194,7 +1205,7 @@ function entity_translation_field_attach_form($entity_type, $entity, &$form, &$f
if (empty($form['#entity_translation_source_form']) && entity_translation_enabled($entity_type, $bundle)) {
$handler = entity_translation_get_handler($entity_type, $entity);
$langcode = !empty($langcode) ? $langcode : $handler->getLanguage();
$form_langcode = $handler->getFormLanguage();
$form_langcode = $handler->getActiveLanguage();
$translations = $handler->getTranslations();
$update_langcode = $form_langcode && ($form_langcode != $langcode);
$source = $handler->getSourceLanguage();
@ -1217,7 +1228,7 @@ function entity_translation_field_attach_form($entity_type, $entity, &$form, &$f
// language information from source to target language, this way the
// user can find the form items already populated with the source values
// while the field form element holds the correct language information.
if ($field['translatable']) {
if ($field['translatable'] && isset($form[$field_name])) {
$element = &$form[$field_name];
$element['#entity_type'] = $entity_type;
$element['#entity'] = $entity;
@ -1253,11 +1264,14 @@ function entity_translation_field_attach_form($entity_type, $entity, &$form, &$f
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
foreach (field_info_instances($entity_type, $bundle) as $instance) {
$field_name = $instance['field_name'];
$field = field_info_field($field_name);
// If access is not set or is granted we check whether the user has
// access to shared fields.
$form[$field_name]['#access'] = (!isset($form[$field_name]['#access']) || $form[$field_name]['#access']) && ($field['translatable'] || $shared_access);
$form[$field_name]['#multilingual'] = (boolean) $field['translatable'];
// Check if a field is part of the form array.
if (isset($form[$field_name])) {
$field = field_info_field($field_name);
// If access is not set or is granted we check whether the user has
// access to shared fields.
$form[$field_name]['#access'] = (!isset($form[$field_name]['#access']) || $form[$field_name]['#access']) && ($field['translatable'] || $shared_access);
$form[$field_name]['#multilingual'] = (boolean) $field['translatable'];
}
}
}
@ -1491,7 +1505,7 @@ function entity_translation_form_alter(&$form, &$form_state) {
if (!$handler->isNewEntity()) {
$handler->entityForm($form, $form_state);
$translations = $handler->getTranslations();
$form_langcode = $handler->getFormLanguage();
$form_langcode = $handler->getActiveLanguage();
if (!isset($translations->data[$form_langcode]) || count($translations->data) > 1) {
// Hide shared form elements if the user is not allowed to edit them.
$handler->entityFormSharedElements($form);
@ -1520,7 +1534,7 @@ function entity_translation_form_alter(&$form, &$form_state) {
function entity_translation_entity_form_source_language_submit($form, &$form_state) {
$handler = entity_translation_entity_form_get_handler($form, $form_state);
$langcode = $form_state['values']['source_language']['language'];
$path = "{$handler->getEditPath()}/add/$langcode/{$handler->getFormLanguage()}";
$path = "{$handler->getEditPath()}/add/$langcode/{$handler->getActiveLanguage()}";
$options = array();
if (isset($_GET['destination'])) {
$options['query'] = drupal_get_destination();
@ -1536,7 +1550,7 @@ function entity_translation_entity_form_source_language_submit($form, &$form_sta
*/
function entity_translation_entity_form_delete_translation_submit($form, &$form_state) {
$handler = entity_translation_entity_form_get_handler($form, $form_state);
$path = "{$handler->getTranslatePath()}/delete/{$handler->getFormLanguage()}";
$path = "{$handler->getTranslatePath()}/delete/{$handler->getActiveLanguage()}";
$options = array();
if (isset($_GET['destination'])) {
$options['query'] = drupal_get_destination();
@ -1564,7 +1578,7 @@ function entity_translation_entity_form_language_update($element, &$form_state,
// needed when responding to an AJAX request where the languages cannot be set
// from the usual page callback.
if (!empty($form_state['entity_translation']['form_langcode'])) {
$handler->setFormLanguage($form_state['entity_translation']['form_langcode']);
$handler->setActiveLanguage($form_state['entity_translation']['form_langcode']);
}
// When responding to an AJAX request we should ignore any change in the
// language widget as it may alter the field language expected by the AJAX
@ -1597,6 +1611,9 @@ function entity_translation_field_attach_submit($entity_type, $entity, $form, &$
// Update the wrapped entity with the submitted values.
$handler->setEntity($entity);
$handler->entityFormSubmit($form, $form_state);
// Process in-place translations for the taxonomy autocomplete widget.
entity_translation_taxonomy_term_field_attach_submit($entity_type, $entity, $form, $form_state);
}
}
@ -1671,7 +1688,7 @@ function theme_entity_translation_language_tabs($variables) {
* Adds an option to enable field synchronization.
* Enable a selector to choose whether a field is translatable.
*/
function entity_translation_form_field_ui_field_edit_form_alter(&$form, $form_state) {
function entity_translation_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
$instance = $form['#instance'];
$entity_type = $instance['entity_type'];
$field_name = $instance['field_name'];
@ -1691,7 +1708,8 @@ function entity_translation_form_field_ui_field_edit_form_alter(&$form, $form_st
$label = t('Field translation');
$title = t('Users may translate all occurrences of this field:') . _entity_translation_field_desc($field);
if (field_has_data($field)) {
$form_state['field_has_data'] = field_has_data($field);
if ($form_state['field_has_data']) {
$path = "admin/config/regional/entity_translation/translatable/$field_name";
$status = $translatable ? $title : (t('All occurrences of this field are untranslatable:') . _entity_translation_field_desc($field));
$link_title = !$translatable ? t('Enable translation') : t('Disable translation');
@ -1719,6 +1737,11 @@ function entity_translation_form_field_ui_field_edit_form_alter(&$form, $form_st
'#default_value' => $translatable,
);
}
$function = 'entity_translation_form_field_ui_field_edit_' . $instance['widget']['type'] . '_form_alter';
if (function_exists($function)) {
$function($form, $form_state);
}
}
/**
@ -1821,32 +1844,37 @@ function entity_translation_settings($entity_type, $bundle) {
* Entity language callback.
*
* This callback changes the entity language from the actual one to the active
* form language. This overriding allows to obtain language dependent form
* widgets where multilingual values are supported (e.g. field or path alias
* widgets) even if the code was not originally written with supporting multiple
* values per language in mind.
* language. This overriding allows to obtain language dependent form widgets
* where multilingual values are supported (e.g. field or path alias widgets)
* even if the code was not originally written with supporting multiple values
* per language in mind.
*
* The main drawback of this approach is that code needing to access the actual
* language in the entity form build/validation/submit workflow cannot rely on
* the entity_language() function. On the other hand in these scenarios assuming
* the presence of Entity translation should be safe, thus being able to rely on
* the EntityTranslationHandlerInterface::getLanguage() method.
* the presence of Entity translation should be safe, thus developers can rely
* on the EntityTranslationHandlerInterface::getLanguage() method.
*
* @param $entity_type
* @param string $entity_type
* The the type of the entity.
* @param $entity
* @param object $entity
* The entity whose language has to be returned.
*
* @return
* @return string
* A valid language code.
*/
function entity_translation_language($entity_type, $entity) {
$handler = entity_translation_get_handler($entity_type, $entity);
if (empty($handler)) {
if (!$handler) {
return LANGUAGE_NONE;
}
$langcode = $handler->getFormLanguage();
return !empty($langcode) ? $langcode : $handler->getLanguage();
if (entity_translation_enabled($entity_type, $entity)) {
$langcode = $handler->getActiveLanguage();
return $langcode ? $langcode : $handler->getLanguage();
}
else {
return $handler->getLanguage();
}
}
/**
@ -1862,15 +1890,8 @@ function entity_translation_language($entity_type, $entity) {
* A class implementing EntityTranslationHandlerInterface.
*/
function entity_translation_get_handler($entity_type = NULL, $entity = NULL) {
if (class_exists('EntityTranslationHandlerFactory')) {
$factory = EntityTranslationHandlerFactory::getInstance();
return empty($entity) ? $factory->getLastHandler($entity_type) : $factory->getHandler($entity_type, $entity);
}
// @todo BC layer. Remove before the first stable release.
elseif (!empty($entity_type) && is_object($entity)) {
$entity_info = entity_get_info($entity_type);
return new EntityTranslationDefaultHandler($entity_type, $entity_info, $entity);
}
$factory = EntityTranslationHandlerFactory::getInstance();
return empty($entity) ? $factory->getLastHandler($entity_type) : $factory->getHandler($entity_type, $entity);
}
/**
@ -1915,7 +1936,7 @@ function entity_translation_current_form_get_handler() {
}
/**
* Returns an array of edit form info as defined in hook_translation_info().
* Returns an array of edit form info as defined in hook_entity_info().
*
* @param $form
* The entity edit form.
@ -2027,7 +2048,7 @@ function entity_translation_entity_uuid_presave(&$entity, $entity_type) {
}
/**
* Implement hook_pathauto_alias_alter().
* Implements hook_pathauto_alias_alter().
*
* When bulk-updating aliases for nodes automatically create a path for every
* translation.
@ -2037,17 +2058,20 @@ function entity_translation_pathauto_alias_alter(&$alias, array &$context) {
$entity_type = $context['module'];
// Ensure that we are dealing with a bundle having entity translation enabled.
if ($context['op'] == 'bulkupdate' && !empty($info[$entity_type]['token type']) && !empty($context['data'][$info[$entity_type]['token type']])) {
if (in_array($context['op'], array('bulkupdate', 'update')) && !empty($info[$entity_type]['token type']) && !empty($context['data'][$info[$entity_type]['token type']])) {
$entity = $context['data'][$info[$entity_type]['token type']];
if (entity_translation_enabled($entity_type, $entity)) {
$translations = entity_translation_get_handler($entity_type, $entity)->getTranslations();
// Only create extra aliases if we are working on the original language to
// avoid infinite recursion.
if ($context['language'] == $translations->original) {
// To avoid infinite recursion, remember the starting language.
static $pathauto_start_language;
if (!$pathauto_start_language) {
$pathauto_start_language = $context['language'];
}
if ($context['language'] == $pathauto_start_language && $context['language'] != LANGUAGE_NONE) {
foreach ($translations->data as $language => $translation) {
// We already have an alias for the original language, so let's not
// We already have an alias for the starting language, so let's not
// create another one.
if ($language == $translations->original) {
if ($language == $pathauto_start_language) {
continue;
}
pathauto_create_alias($entity_type, $context['op'], $context['source'], $context['data'], $context['type'], $language);
@ -2085,3 +2109,11 @@ function entity_translation_entity_save($entity_type, $entity) {
field_attach_update($entity_type, $entity);
}
}
/**
* Implements hook_field_attach_prepare_translation_alter().
*/
function entity_translation_field_attach_prepare_translation_alter(&$entity, $context) {
$handler = entity_translation_get_handler('node', $entity);
$handler->setActiveLanguage($context['langcode']);
}

View File

@ -40,6 +40,9 @@ function entity_translation_taxonomy_term_menu_alter(&$items, $backup) {
$items['taxonomy/term/%taxonomy_term/translate']['access callback'] = 'entity_translation_taxonomy_term_tab_access';
$items['taxonomy/term/%taxonomy_term/translate']['file'] = 'entity_translation.admin.inc';
$items['taxonomy/term/%taxonomy_term/translate']['module'] = 'entity_translation';
// Delete translation callback.
$items['taxonomy/term/%taxonomy_term/translate/delete/%entity_translation_language']['access arguments'] = $access_arguments;
}
/**
@ -94,3 +97,472 @@ function entity_translation_form_taxonomy_form_vocabulary_submit($form, &$form_s
variable_set('entity_translation_taxonomy', $info);
}
/**
* Returns a translated label for the specified taxonomy term.
*
* @param object $term
* A taxonomy term object.
* @param string $langcode
* The language the label should be translated in.
*
* @return string
* The translated taxonomy term label.
*
* @internal This is supposed to be used only by the ET taxonomy integration
* code, as it might be removed/replaced in any moment of the ET lifecycle.
*/
function _entity_translation_taxonomy_label($term, $langcode) {
$entity_type = 'taxonomy_term';
if (function_exists('title_entity_label')) {
$label = title_entity_label($term, $entity_type, $langcode);
}
else {
$label = entity_label($entity_type, $term);
}
return (string) $label;
}
/**
* Implements entity_translation_form_field_ui_field_edit_WIDGET_TYPE_form_alter().
*
* {@inheritdoc}
*/
function entity_translation_form_field_ui_field_edit_taxonomy_autocomplete_form_alter(&$form, &$form_state) {
$key = 'entity_translation_taxonomy_autocomplete_translate';
$instance = $form['#instance'];
$field_name = $instance['field_name'];
$entity_type = $instance['entity_type'];
$field = field_info_field($field_name);
$translatable = field_is_translatable($entity_type, $field);
$bundle = !empty($field['settings']['allowed_values'][0]['vocabulary']) ? $field['settings']['allowed_values'][0]['vocabulary'] : NULL;
$access = variable_get('entity_translation_taxonomy_autocomplete', FALSE);
// Add a checkbox to toggle in-place translation for autocomplete widgets.
$form['instance']['settings'][$key] = array(
'#type' => 'checkbox',
'#title' => t('Enable in-place translation of terms'),
'#description' => t('Check this option if you wish to use translation forms to perform in-place translation for terms entered in the original language.'),
'#default_value' => !$translatable && !empty($form['#instance']['settings'][$key]),
'#access' => $access && (!$form_state['field_has_data'] || !$translatable) && entity_translation_enabled('taxonomy_term', $bundle),
'#states' => array(
'visible' => array(':input[name="field[translatable]"]' => array('checked' => FALSE)),
),
'#weight' => -8,
);
}
/**
* Checks whether in-place translation is enabled for the autocomplete widget.
*
* @param array $element
* The widget form element.
* @param array $form_state
* The form state array.
*
* @return bool
* TRUE if in-place translation is enabled, FALSE otherwise.
*/
function _entity_translation_taxonomy_autocomplete_translation_enabled($element, $form_state) {
$field = field_info_field($element['#field_name']);
if (field_is_translatable($element['#entity_type'], $field)) {
return FALSE;
}
list($id, , $bundle) = entity_extract_ids($element['#entity_type'], $element['#entity']);
if (!$id) {
return FALSE;
}
$entity_type = 'taxonomy_term';
$parent_handler = entity_translation_get_handler($element['#entity_type'], $element['#entity']);
$active_langcode = $parent_handler->getActiveLanguage();
$translations = $parent_handler->getTranslations();
$entity_langcode = isset($translations->original) ? $translations->original : LANGUAGE_NONE;
$instance = field_info_instance($element['#entity_type'], $field['field_name'], $bundle);
// We need to make sure that we are not dealing with a translation form.
// However checking the active language is not enough, because the user may
// have changed the entity language.
return
(isset($form_state['entity_translation']['is_translation']) ?
$form_state['entity_translation']['is_translation'] : ($active_langcode != $entity_langcode)) &&
!empty($instance['settings']['entity_translation_taxonomy_autocomplete_translate']) &&
(user_access('translate any entity') || user_access("translate $entity_type entities"));
}
/**
* Implements hook_field_widget_WIDGET_TYPE_form_alter().
*
* {@inheritdoc}
*/
function entity_translation_field_widget_taxonomy_autocomplete_form_alter(&$element, &$form_state, $context) {
// The autocomplete widget is also displayed in the field configuration form,
// in which case we do not need to perform any alteration. To preserve BC, by
// default we enable our taxonomy autocomplete override only on new sites.
if (!isset($element['#entity']) || !variable_get('entity_translation_taxonomy_autocomplete', FALSE)) {
return;
}
// We will need to translate term names, if Title is enabled and configured
// for this vocabulary.
$entity_type = 'taxonomy_term';
$field = field_widget_field($element, $form_state);
$bundle = !empty($field['settings']['allowed_values'][0]['vocabulary']) ? $field['settings']['allowed_values'][0]['vocabulary'] : NULL;
if ($bundle && entity_translation_enabled($entity_type, $bundle)) {
$parent_handler = entity_translation_get_handler($element['#entity_type'], $element['#entity']);
$langcode = $parent_handler->getActiveLanguage();
$terms = array_values(_entity_translation_taxonomy_autocomplete_widget_get_terms($element));
// If we are using the regular autocomplete behavior also in translation
// forms, we need to set our custom callback.
if (!_entity_translation_taxonomy_autocomplete_translation_enabled($element, $form_state)) {
$element['#autocomplete_path'] = 'entity_translation/' . $entity_type . '/autocomplete/' . $langcode . '/' . $element['#field_name'];
$translations = $parent_handler->getTranslations();
if (isset($translations->original) && $translations->original != $langcode) {
$labels = array();
foreach ($terms as $delta => $term) {
$labels[] = _entity_translation_taxonomy_label($term, $langcode);
}
$element['#default_value'] = implode(', ', $labels);
}
}
// Otherwise we just provide the in-place translation widget.
else {
$element['#type'] = 'fieldset';
$element['#description'] = t('Enter one translation for each term');
$element['#access'] = (bool) $terms;
foreach ($terms as $delta => $term) {
$element[$delta] = array(
'#type' => 'textfield',
'#default_value' => _entity_translation_taxonomy_label($term, $langcode),
'#required' => TRUE,
'#tid' => $term->tid,
);
}
$element['#process'][] = 'entity_translation_taxonomy_autocomplete_process';
}
// The native term save logic is performed at widget validation level, so we
// just replace the validation handler to provide our logic instead.
$element['#element_validate'] = array_values(array_diff($element['#element_validate'], array('taxonomy_autocomplete_validate')));
$element['#element_validate'][] = 'entity_translation_taxonomy_autocomplete_validate';
}
}
/**
* Returns the terms referenced by the taxonomy autocomplete widget field.
*
* @param array $element
* The taxonomy autocomplete form element.
*
* @return object[]
* An associative array of taxonomy term object keyed by their identifiers.
*/
function _entity_translation_taxonomy_autocomplete_widget_get_terms($element) {
$items = isset($element['#entity']->{$element['#field_name']}[$element['#language']]) ?
$element['#entity']->{$element['#field_name']}[$element['#language']] : array();
$tids = array_map(function ($item) { return $item['tid']; }, $items);
return taxonomy_term_load_multiple($tids);
}
/**
* Process callback for the ET taxonomy autocomplete widget.
*
* {@inheritdoc}
*/
function entity_translation_taxonomy_autocomplete_process($element) {
// The in-place translation widget makes sense only for untranslatable field,
// which may have the "(all languages)" label suffix. In this case it would be
// confusing so we need to revert that.
$instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
$element['#title'] = check_plain($instance['label']);
return $element;
}
/**
* Entity translation taxonomy autocomplete callback.
*
* @param string $langcode
* The input language.
* @param string $field_name
* The name of the term reference field.
* @param string $tags_typed
* (optional) A comma-separated list of term names entered in the
* autocomplete form element. Only the last term is used for autocompletion.
* Defaults to an empty string.
*
* @see taxonomy_autocomplete()
*/
function entity_translation_taxonomy_term_autocomplete($langcode = NULL, $field_name = '', $tags_typed = '') {
// If the request has a '/' in the search text, then the menu system will have
// split it into multiple arguments, recover the intended $tags_typed.
$args = func_get_args();
// Shift off the $langcode and $field_name arguments.
array_shift($args);
array_shift($args);
$tags_typed = implode('/', $args);
// Make sure the field exists and is a taxonomy field.
if (!($field = field_info_field($field_name)) || $field['type'] !== 'taxonomy_term_reference') {
// Error string. The JavaScript handler will realize this is not JSON and
// will display it as debugging information.
print t('Taxonomy field @field_name not found.', array('@field_name' => $field_name));
exit;
}
// The user enters a comma-separated list of tags. We only autocomplete the
// last tag.
$tags_typed = drupal_explode_tags($tags_typed);
$tag_last = drupal_strtolower(array_pop($tags_typed));
$term_matches = array();
if ($tag_last != '') {
if (!isset($langcode) || $langcode == LANGUAGE_NONE) {
$langcode = $GLOBALS['language_content']->language;
}
// Part of the criteria for the query come from the field's own settings.
$vocabulary = _entity_translation_taxonomy_reference_get_vocabulary($field);
$entity_type = 'taxonomy_term';
$query = new EntityFieldQuery();
$query->addTag('taxonomy_term_access');
$query->entityCondition('entity_type', $entity_type);
// If the Title module is enabled and the taxonomy term name is replaced for
// the current bundle, we can look for translated names, otherwise we fall
// back to the regular name property.
if (module_invoke('title', 'field_replacement_enabled', $entity_type, $vocabulary->machine_name, 'name')) {
$name_field = 'name_field';
$language_group = 0;
// Do not select already entered terms.
$column = 'value';
if (!empty($tags_typed)) {
$query->fieldCondition($name_field, $column, $tags_typed, 'NOT IN', NULL, $language_group);
}
$query->fieldCondition($name_field, $column, $tag_last, 'CONTAINS', NULL, $language_group);
$query->fieldLanguageCondition($name_field, array($langcode, LANGUAGE_NONE), NULL, NULL, $language_group);
}
else {
$name_field = 'name';
// Do not select already entered terms.
if (!empty($tags_typed)) {
$query->propertyCondition($name_field, $tags_typed, 'NOT IN');
}
$query->propertyCondition($name_field, $tag_last, 'CONTAINS');
}
// Select rows that match by term name.
$query->propertyCondition('vid', $vocabulary->vid);
$query->range(0, 10);
$result = $query->execute();
// Populate the results array.
$prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : '';
$terms = !empty($result[$entity_type]) ? taxonomy_term_load_multiple(array_keys($result[$entity_type])) : array();
foreach ($terms as $tid => $term) {
$name = _entity_translation_taxonomy_label($term, $langcode);
$n = $name;
// Term names containing commas or quotes must be wrapped in quotes.
if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) {
$n = '"' . str_replace('"', '""', $name) . '"';
}
$term_matches[$prefix . $n] = check_plain($name);
}
}
drupal_json_output($term_matches);
}
/**
* Returns the vocabulary enabled for the specified taxonomy reference field.
*
* @param array $field
* A field definition.
*
* @return object|null
* A vocabulary object or NULL if none is found.
*/
function _entity_translation_taxonomy_reference_get_vocabulary($field) {
$vocabulary = NULL;
if (!empty($field['settings']['allowed_values'])) {
$vids = array();
$vocabularies = taxonomy_vocabulary_get_names();
foreach ($field['settings']['allowed_values'] as $tree) {
$vids[] = $vocabularies[$tree['vocabulary']]->vid;
}
$vocabulary = taxonomy_vocabulary_load(reset($vids));
}
return $vocabulary;
}
/**
* Form element validate handler for taxonomy term autocomplete element.
*
* {@inheritdoc}
*
* @see taxonomy_autocomplete_validate()
*/
function entity_translation_taxonomy_autocomplete_validate($element, &$form_state) {
$value = array();
list($id) = entity_extract_ids($element['#entity_type'], $element['#entity']);
$is_new = !isset($id);
// This is the language of the parent entity, that we will be applying to new
// terms.
$parent_handler = entity_translation_get_handler($element['#entity_type'], $element['#entity']);
$langcode = !empty($form_state['entity_translation']['form_langcode']) ?
$form_state['entity_translation']['form_langcode'] : $parent_handler->getActiveLanguage();
// Handle in-place translation.
if (_entity_translation_taxonomy_autocomplete_translation_enabled($element, $form_state)) {
// The referenced terms cannot change, so we just need to collect their term
// identifiers. We also build a map of the corresponding deltas for later
// use.
$deltas = array();
foreach (element_children($element) as $delta) {
$tid = $element[$delta]['#tid'];
$deltas[$tid] = $delta;
$value[$delta]['tid'] = $tid;
}
// Save term translations.
$entity_type = 'taxonomy_term';
$name_field = 'name_field';
$source_langcode = $parent_handler->getSourceLanguage();
// This is a validation handler, so we must defer the actual save to the
// submit phase.
$terms_to_save = &$form_state['entity_translation']['taxonomy_autocomplete'][$element['#entity_type']][$id][$element['#field_name']];
foreach (taxonomy_term_load_multiple(array_keys($deltas)) as $term) {
// This is also the right context to perform validation.
$term_translation = $element[$deltas[$term->tid]]['#value'];
if (!$term_translation) {
$instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
drupal_set_message(t('The translations for %field_label cannot be empty.', array('%field_label' => $instance['label'])), 'error', FALSE);
continue;
}
$handler = entity_translation_get_handler($entity_type, $term);
$translations = $handler->getTranslations();
$term_langcode = $handler->getLanguage();
$typed_langcode = $term_langcode != LANGUAGE_NONE ? $langcode : $term_langcode;
// Create a new translation in the active language, if it is missing.
if (!isset($translations->data[$typed_langcode]) && $typed_langcode != LANGUAGE_NONE) {
$translation = array(
'language' => $typed_langcode,
'source' => $source_langcode,
'uid' => $GLOBALS['user']->uid,
'status' => 1,
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
);
$translation_values = array(
$name_field => array($typed_langcode => array(array('value' => $term_translation))),
);
$handler->setTranslation($translation, $translation_values);
$terms_to_save[] = $term;
}
// Otherwise we just update the existing translation, if it has changed.
// If the term is language-neutral, we just update its main value. This is
// expected to happen normally, but could when referencing existing terms.
elseif ($term_translation != _entity_translation_taxonomy_label($term, $typed_langcode)) {
$term->{$name_field}[$typed_langcode][0]['value'] = $term_translation;
$terms_to_save[] = $term;
}
}
}
// Autocomplete widgets do not send their tids in the form, so we must detect
// them here and process them independently.
elseif ($tags = $element['#value']) {
$entity_type = 'taxonomy_term';
$field = field_widget_field($element, $form_state);
$vocabulary = _entity_translation_taxonomy_reference_get_vocabulary($field);
$typed_tags = drupal_explode_tags($tags);
// Collect existing terms by name.
$existing_terms = array();
foreach (_entity_translation_taxonomy_autocomplete_widget_get_terms($element) as $term) {
$name = _entity_translation_taxonomy_label($term, $langcode);
$existing_terms[$name] = $term;
}
// Select terms that match by the (translated) name.
$query = new EntityFieldQuery();
$query->addTag('taxonomy_term_access');
$query->entityCondition('entity_type', $entity_type);
$query->propertyCondition('vid', $vocabulary->vid);
if ($langcode != LANGUAGE_NONE && module_invoke('title', 'field_replacement_enabled', $entity_type, $vocabulary->machine_name, 'name')) {
$language_group = 0;
// Do not select already entered terms.
$name_field = 'name_field';
$column = 'value';
$query->fieldCondition($name_field, $column, $typed_tags, NULL, NULL, $language_group);
// When we are creating a new entity, we cannot filter by active language,
// as that may have not be applied to the autocomplete query.
if (!$is_new) {
$query->fieldLanguageCondition($name_field, array($langcode, LANGUAGE_NONE), NULL, NULL, $language_group);
}
}
else {
$query->propertyCondition('name', $typed_tags);
}
$result = $query->execute();
// When we are creating a new entity, the language used for the autocomplete
// query is the current content language, so we should use that to update
// the map of existing terms.
if (!empty($result[$entity_type])) {
$typed_langcode = !$is_new ? $langcode : $GLOBALS['language_content']->language;
foreach (taxonomy_term_load_multiple(array_keys($result[$entity_type])) as $term) {
$name = _entity_translation_taxonomy_label($term, $typed_langcode);
$existing_terms[$name] = $term;
}
}
// Now collect the identifiers for the various terms and update the taxonomy
// reference field values.
foreach ($typed_tags as $delta => $typed_tag) {
// See if the term exists in the chosen vocabulary and return the tid.
// Otherwise create a new 'autocreate' term for insert/update.
if (isset($existing_terms[$typed_tag])) {
$term = $existing_terms[$typed_tag];
}
else {
$term = (object) array(
'tid' => 'autocreate',
'vid' => $vocabulary->vid,
'name' => $typed_tag,
'vocabulary_machine_name' => $vocabulary->machine_name,
);
$handler = entity_translation_get_handler($entity_type, $term);
$handler->setOriginalLanguage($langcode);
$handler->initTranslations();
}
$value[] = (array) $term;
}
}
form_set_value($element, $value, $form_state);
}
/**
* Term-specific implementation of hook_field_attach_submit().
*/
function entity_translation_taxonomy_term_field_attach_submit($entity_type, $entity, $form, &$form_state) {
// Finally save in-place translations
if (!empty($form_state['entity_translation']['taxonomy_autocomplete'])) {
foreach ($form_state['entity_translation']['taxonomy_autocomplete'] as $entity_type => $entity_type_data) {
foreach ($entity_type_data as $id => $field_name_data) {
foreach ($field_name_data as $field_name => $term_data) {
if (!is_array($term_data)) {
continue;
}
foreach ($term_data as $term) {
entity_translation_entity_save('taxonomy_term', $term);
}
}
}
}
}
}

View File

@ -7,9 +7,8 @@ dependencies[] = i18n
dependencies[] = i18n_menu
files[] = entity_translation_i18n_menu.test
; Information added by Drupal.org packaging script on 2016-09-28
version = "7.x-1.0-beta5+15-dev"
; Information added by Drupal.org packaging script on 2019-01-20
version = "7.x-1.0+5-dev"
core = "7.x"
project = "entity_translation"
datestamp = "1475057941"
datestamp = "1548022384"

View File

@ -164,7 +164,7 @@ function entity_translation_i18n_menu_form(&$form, &$form_state) {
$default = isset($source_menu['language']) && $source_menu['language'] != LANGUAGE_NONE;
$languages = language_list();
$handler = entity_translation_entity_form_get_handler($form, $form_state);
$langcode = $handler->getFormLanguage();
$langcode = $handler->getActiveLanguage();
$language_name = isset($languages[$langcode]) ? t($languages[$langcode]->name) : t('current');
$form['menu']['#multilingual'] = TRUE;

View File

@ -125,23 +125,36 @@ class EntityTranslationMenuTranslationTestCase extends EntityTranslationTestCase
$this->assertText($link_title, 'New menu link title found: ' . $link_title);
}
/**
* Asserts a link with a given label and title is found in the page.
*/
function assertLinkWithTitleAttribute($label, $title) {
$links = $this->xpath('//a[normalize-space(text())=:label and normalize-space(@title)=:title]', array(
':label' => $label,
':title' => $title,
));
$this->assert(isset($links[0]));
}
/**
* Test if menu localization works.
*/
function testMenuLocalization() {
// Create Basic page in English.
$link_title_en = $this->randomName();
$node = $this->createPage($link_title_en, NULL, 'en');
$link_description_en = $this->randomName() . ' & htmlentity';
$node = $this->createPage($link_title_en, $link_description_en, 'en');
// Submit translation in Spanish.
$link_title_es = $this->randomName();
$node_translation = $this->createTranslation($node, $link_title_es, NULL, 'es');
$link_description_es = $this->randomName() . ' & htmlentity';
$node_translation = $this->createTranslation($node, $link_title_es, $link_description_es, 'es');
// Check menu links in both languages.
$this->get('en', '<front>');
$this->assertText($link_title_en);
$this->assertLinkWithTitleAttribute($link_title_en, $link_description_en);
$this->get('es', '<front>');
$this->assertText($link_title_es);
$this->assertLinkWithTitleAttribute($link_title_es, $link_description_es);
// Edit English menu link.
$link_title_en2 = $this->randomName();
@ -149,15 +162,19 @@ class EntityTranslationMenuTranslationTestCase extends EntityTranslationTestCase
// Check that Spanish menu link has not changed.
$this->get('es', '<front>');
$this->assertText($link_title_es);
$this->assertLinkWithTitleAttribute($link_title_es, $link_description_es);
// Edit Spanish menu link.
$link_title_es2 = $this->randomName();
$this->editPage($node, $link_title_es, $link_title_es2, 'es');
// Check that English menu link has not changed.
// Check that English menu link has changed.
$this->get('en', '<front>');
$this->assertText($link_title_en2);
$this->assertLinkWithTitleAttribute($link_title_en2, $link_description_en);
// Check that Spanish menu link has changed.
$this->get('es', '<front>');
$this->assertLinkWithTitleAttribute($link_title_es2, $link_description_es);
// Delete Spanish translation and check that the respective menu item has
// been deleted as well.

View File

@ -34,8 +34,8 @@ function entity_translation_upgrade_start($types) {
*/
function entity_translation_upgrade_end($success, $results, $operations, $elapsed) {
if (!empty($results)) {
$message = format_plural($results, '1 node translation successfully upgraded.', '@count node translations successfully upgraded.');
watchdog('entity translation upgrade', '@count node translations successfully upgraded.', array('@count' => $results), WATCHDOG_INFO);
$message = format_plural($results['total'], '1 node translation successfully upgraded.', '@count node translations successfully upgraded.');
watchdog('entity translation upgrade', '@count node translations successfully upgraded.', array('@count' => $results['total']), WATCHDOG_INFO);
}
else {
$message = t('No node translation available for the upgrade.');
@ -91,6 +91,10 @@ function entity_translation_upgrade_do($types, &$context) {
$node = $nodes[$nid];
$original = $nodes[$node->tnid];
$handler = entity_translation_get_handler('node', $original);
// Instantiate the data and original properties of the translations.
if (empty($handler->getTranslations()->data)) {
$handler->initTranslations();
}
if (!isset($instances[$node->type])) {
$instances[$node->type] = field_info_instances('node', $node->type);
@ -185,7 +189,7 @@ function entity_translation_upgrade_do($types, &$context) {
$context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
if ($context['finished'] >= 1) {
$context['results'] = $context['sandbox']['total'];
$context['results']['total'] = $context['sandbox']['total'];
}
}
}

View File

@ -0,0 +1,117 @@
<?php
/**
* Implements hook_drush_command().
*/
function entity_translation_upgrade_drush_command() {
$items = array();
$items['entity-translation-upgrade'] = array(
'description' => "Upgrades all nodes of the specified content type from Content Translation to Entity Translation.",
'arguments' => array(
'content_type' => 'Content type of nodes to be upgraded.',
),
'examples' => array(
'drush entity-translation-upgrade article' => 'Upgrades all nodes of content type "article" from Content Translation to Entity Translation.',
),
'aliases' => array('etu'),
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
);
return $items;
}
/**
* Implements hook_drush_help().
*
* @param
* A string with the help section
*
* @return
* A string with the help text for the entity-translation-upgrade command
*/
function entity_translation_upgrade_drush_help($section) {
switch ($section) {
case 'drush:entity-translation-upgrade':
return dt("Brief help for Drush command entity-translation-upgrade.");
case 'meta:entity_translation_upgrade:title':
return dt("Entity Translation Upgrade commands");
case 'meta:entity_translation_upgrade:summary':
return dt("Upgrading nodes to Entity Translation.");
}
}
/**
* Implements drush_hook_COMMAND().
*
* @param
* The content type of which the nodes shall be upgraded
*
* Runs the batch upgrading nodes of the specified content_type to Entity
* Translation. Lets user choose content type from a list, if argument has
* not been provided.
*/
function drush_entity_translation_upgrade($content_type = "") {
// Get all installed content types.
$available_types = array();
$available_types_chose = array();
$available_types_str = '';
foreach (node_type_get_types() as $type) {
$available_types[$type->type] = $type->type;
$available_types_chose[$type->type] = $type->name;
if (strlen($available_types_str) > 0) {
$available_types_str .= ', ';
}
$available_types_str .= $type->type;
}
// If argument content_type is empty, prompt user for content type.
if (!$content_type) {
$content_type = drush_choice($available_types_chose, dt('Choose the content type of the nodes to be upgraded to Entity Translation:'));
}
// Do content type argument checks.
if (!$content_type) {
return TRUE;
}
if (strlen($available_types_str) == 0) {
return drush_set_error(dt('Entity Translation Upgrade cannot run as no content type has been installed.'));
}
if (!in_array($content_type, $available_types)) {
return drush_set_error(dt('"@content_type" is not a valid content type machine name. Please use one of these installed content types as argument: @available_types_str.', array('@content_type' => $content_type, '@available_types_str' => $available_types_str)));
}
// Start batch to upgrade nodes of the specified content type.
$types = array($content_type => $content_type);
$batch = array(
'operations' => array(
array('entity_translation_upgrade_do', array($types)),
array('entity_translation_upgrade_complete', array()),
),
'finished' => 'entity_translation_upgrade_drush_end',
'file' => drupal_get_path('module', 'entity_translation_upgrade') . '/entity_translation_upgrade.admin.inc',
'progressive' => FALSE,
);
batch_set($batch);
drush_backend_batch_process();
}
/**
* This is the 'finished' batch callback, drush version.
*/
function entity_translation_upgrade_drush_end($success, $results, $operations, $elapsed) {
// Print result messages.
if (!empty($results)) {
$message = format_plural($results['total'], '1 node translation successfully upgraded.', '@count node translations successfully upgraded.');
$severity = 'ok';
watchdog('Entity Translation upgrade', '@count node translations successfully upgraded.', array('@count' => $results['total']), WATCHDOG_INFO);
}
else {
$message = t('No node translation available for the upgrade.');
$severity = 'warning';
}
drush_log($message, $severity);
}

View File

@ -3,10 +3,10 @@ description = Provides an upgrade path from node-based translation to field-base
package = Multilingual - Entity Translation
core = 7.x
dependencies[] = entity_translation
files[] = entity_translation_upgrade.test
; Information added by Drupal.org packaging script on 2016-09-28
version = "7.x-1.0-beta5+15-dev"
; Information added by Drupal.org packaging script on 2019-01-20
version = "7.x-1.0+5-dev"
core = "7.x"
project = "entity_translation"
datestamp = "1475057941"
datestamp = "1548022384"

View File

@ -0,0 +1,163 @@
<?php
/**
* @file
* Tests for Entity Translation module.
*/
/**
* Tests for the upgrade translation process.
*/
class EntityTranslationUpgradeTestCase extends EntityTranslationTestCase {
/**
* Return the test information.
*/
public static function getInfo() {
return array(
'name' => 'Translation upgrade',
'description' => 'Tests for the upgrade from Content Translation to Entity Translation.',
'group' => 'Entity translation',
'dependencies' => array(),
);
}
function setUp() {
parent::setUp('locale', 'translation', 'translation_test', 'entity_translation', 'entity_translation_upgrade');
$this->getAdminUser(array(
'toggle field translatability',
'administer entity translation',
));
$this->getTranslatorUser(array(
'translate content',
));
$this->login($this->getAdminUser());
$this->addLanguage('en');
$this->addLanguage('es');
$this->configureContentTypeForContentTranslation();
$this->enableUrlLanguageDetection();
$this->login($this->getTranslatorUser());
}
/**
* Configure the "Basic page" content type for entity translation tests.
*/
public function configureContentTypeForContentTranslation() {
// Configure the "Basic page" content type to use multilingual support with
// content translation.
$edit = array();
$edit['language_content_type'] = TRANSLATION_ENABLED;
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
$this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
}
/**
* Toggle body field's translatability.
*/
public function makeBodyFieldTranslatable() {
$edit = array();
$this->drupalGet('admin/structure/types/manage/page/fields/body');
$this->clickLink('Enable translation');
$this->drupalPost(NULL, array(), t('Confirm'));
$this->assertRaw(t('Data successfully processed.'), t('Body field have been made translatable.'));
}
/**
* @see TranslationTestCase::createPage
*/
function createContentTranslationPage($title, $body, $language = NULL) {
$edit = array();
$langcode = LANGUAGE_NONE;
$edit["title"] = $title;
$edit["body[$langcode][0][value]"] = $body;
if (!empty($language)) {
$edit['language'] = $language;
}
$this->drupalPost('node/add/page', $edit, t('Save'));
$this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), 'Basic page created.');
// Check to make sure the node was created.
$node = $this->drupalGetNodeByTitle($title);
$this->assertTrue($node, 'Node found in database.');
return $node;
}
/**
* @see TranslationTestCase::createTranslation
*/
function createContentTranslationTranslation($node, $title, $body, $language) {
$this->drupalGet('node/add/page', array('query' => array('translation' => $node->nid, 'target' => $language)));
$langcode = LANGUAGE_NONE;
$body_key = "body[$langcode][0][value]";
$this->assertFieldByXPath('//input[@id="edit-title"]', $node->title, "Original title value correctly populated.");
$this->assertFieldByXPath("//textarea[@name='$body_key']", $node->body[LANGUAGE_NONE][0]['value'], "Original body value correctly populated.");
$edit = array();
$edit["title"] = $title;
$edit[$body_key] = $body;
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), 'Translation created.');
// Check to make sure that translation was successful.
$translation = $this->drupalGetNodeByTitle($title);
$this->assertTrue($translation, 'Node found in database.');
$this->assertTrue($translation->tnid == $node->nid, 'Translation set id correctly stored.');
return $translation;
}
/**
* Tests copying of source node's body value in the add translation form page.
*/
public function testUpgradeContentToEntityTranslation() {
// Create Basic page in English.
$node_title = $this->randomName();
$node_body = $this->randomName();
$node = $this->createContentTranslationPage($node_title, $node_body, 'en');
// Submit translation in Spanish.
$this->drupalGet('node/' . $node->nid . '/translate');
$node_translation_title = $this->randomName();
$node_translation_body = $this->randomName();
$node_translation = $this->createContentTranslationTranslation($node, $node_translation_title, $node_translation_body, 'es');
// Make Body field translatable before we run the upgrade.
$this->login($this->getAdminUser());
$this->makeBodyFieldTranslatable();
// Run the upgrade for all Page nodes.
$edit = array(
'types[page]' => 'page',
);
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Upgrade'));
// Switch to our translator user.
$this->login($this->getTranslatorUser());
// Check that the unpublished target node triggers a redirect.
$this->drupalGet('node/' . $node_translation->nid);
$headers = $this->drupalGetHeaders(TRUE);
list(, $status) = explode(' ', $headers[0][':status'], 3);
$this->assertEqual($status, 301, 'Expected response code was sent.');
$languages = language_list();
$this->assertEqual($this->getUrl(), url('node/' . $node->nid, array('absolute' => TRUE, 'language' => $languages['es'])), 'entity_translation_upgrade_redirect() redirected to expected URL.');
// Check that the body is displayed when the active language is English.
$this->drupalGet('node/' . $node->nid);
$this->assertRaw($node_body, t('Body field displayed correctly in the source language.'));
// Check that the translated body is displayed when the active language is Spanish.
$this->drupalGet('es/node/' . $node->nid);
$this->assertRaw($node_translation_body, t('Body field displayed correctly in the target language.'));
// Check that the edit forms are initialized correctly in the target language.
$this->drupalGet('node/' . $node->nid . '/edit');
$this->assertFieldByXPath("//textarea[@name='body[en][0][value]']", $node_body, "Body field correctly instantiated with the value of the source language.");
$this->drupalGet('es/node/' . $node->nid . '/edit');
$this->assertFieldByXPath("//textarea[@name='body[es][0][value]']", $node_translation_body, "Body field correctly instantiated with the value of the target language.");
}
}

View File

@ -98,6 +98,62 @@ interface EntityTranslationHandlerInterface {
*/
public function getLanguage();
/**
* Sets the active language.
*
* This is the language that determines which translation should be considered
* "active" for the wrapped entity. The "Entity Translation" module uses this
* information to implement the UI-based translation workflow. Other modules
* can rely on it to implement their own entity translation-related logic.
*
* This will affect which language is returned by the core "entity_language()"
* function.
*
* @param string $langcode
* The active language code.
*
* @see entity_language()
* @see entity_translation_language()
* @see ::getActiveLanguage()
*/
public function setActiveLanguage($langcode);
/**
* Returns the active language.
*
* @return string
* The active language for the wrapped entity.
*
* @see ::setActiveLanguage()
*/
public function getActiveLanguage();
/**
* Sets the active form language.
*
* @param string $langcode
* The active form language code.
*
* @deprecated in 7.x-1.0-beta6, will be removed before 7.x-1.0. Use
* ::setActiveLanguage() instead.
*
* @see ::setActiveLanguage()
*/
public function setFormLanguage($langcode);
/**
* Retrieves the active form language.
*
* @return string
* The active form language code.
*
* @deprecated in 7.x-1.0-beta6, will be removed before 7.x-1.0. Use
* ::getActiveLanguage() instead.
*
* @see ::getActiveLanguage()
*/
public function getFormLanguage();
/**
* Returns the translation object key for the wrapped entity type.
*/
@ -109,7 +165,7 @@ interface EntityTranslationHandlerInterface {
public function getDefaultLanguage();
/**
* Sets the language of the orginal translation.
* Sets the language of the original translation.
*
* @param $langcode
* The language code of the original content values.
@ -272,16 +328,6 @@ interface EntityTranslationHandlerInterface {
*/
public function isAliasEnabled();
/**
* Sets the active form language.
*/
public function setFormLanguage($langcode);
/**
* Retrieves the active form language.
*/
public function getFormLanguage();
/**
* Sets the source language for the translation being created.
*/
@ -364,19 +410,19 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
*/
protected $children = array();
private $entityForm;
private $translating;
private $outdated;
private $formLanguage;
private $sourceLanguage;
protected $entityForm;
protected $translating;
protected $outdated;
protected $activeLanguage;
protected $sourceLanguage;
private $pathScheme;
private $pathWildcard;
private $basePath;
private $editPath;
private $translatePath;
private $viewPath;
private $routerMap;
protected $pathScheme;
protected $pathWildcard;
protected $basePath;
protected $editPath;
protected $translatePath;
protected $viewPath;
protected $routerMap;
/**
* Initializes an instance of the translation handler.
@ -396,7 +442,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
$this->entityForm = FALSE;
$this->translating = FALSE;
$this->outdated = FALSE;
$this->formLanguage = FALSE;
$this->activeLanguage = FALSE;
$this->sourceLanguage = FALSE;
$this->pathScheme = 'default';
$this->routerMap = array();
@ -481,7 +527,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
public function addChild($entity_type, $entity) {
if (!empty($this->factory)) {
$handler = $this->factory->getHandler($entity_type, $entity);
$handler->setFormLanguage($this->getFormLanguage());
$handler->setActiveLanguage($this->getActiveLanguage());
$handler->setSourceLanguage($this->getSourceLanguage());
// Avoid registering more than one child handler for each entity.
$hid = $this->factory->getHandlerId($entity_type, $entity);
@ -632,6 +678,12 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
throw new Exception('Invalid translation language');
}
// $args will be used later on in a call to notifyChildren(). We need
// to call func_get_args() before any modifications to the function's
// arguments take place. This is due to changes in PHP 7.0 and onwards.
// @see http://php.net/manual/en/function.func-get-args.php#refsect1-function.func-get-args-notes
$args = func_get_args();
$translations = $this->getTranslations();
$langcode = $translation['language'];
@ -658,13 +710,12 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
foreach (field_info_instances($this->entityType, $this->bundle) as $instance) {
$field_name = $instance['field_name'];
$field = field_info_field($field_name);
if ($field['translatable'] && isset($values[$field_name])) {
if ($field['translatable'] && isset($values[$field_name][$langcode])) {
$this->entity->{$field_name}[$langcode] = $values[$field_name][$langcode];
}
}
}
$args = func_get_args();
$this->notifyChildren(__FUNCTION__, $args);
}
@ -803,6 +854,55 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
}
}
/**
* {@inheritdoc}
*/
public function setActiveLanguage($langcode) {
// @todo To fully preserve BC, we proxy the call to the deprecated
// ::setFormLanguage method. This will keep things working even when it
// has been overridden. Inline its implementation here upon removal.
$this->setFormLanguage($langcode);
}
/**
* {@inheritdoc}
*/
public function getActiveLanguage() {
// @todo To fully preserve BC, we proxy the call to the deprecated
// ::getFormLanguage method. This will keep things working even when it
// has been overridden. Inline its implementation here upon removal.
return $this->getFormLanguage();
}
/**
* {@inheritdoc}
*/
public function setFormLanguage($langcode) {
$this->activeLanguage = $langcode;
$args = func_get_args();
$this->notifyChildren(__FUNCTION__, $args);
}
/**
* {@inheritdoc}
*/
public function getFormLanguage() {
if (!empty($this->activeLanguage)) {
return $this->activeLanguage;
}
// For new entities the active language should match the default language.
// The language stored with the entity itself (for example, $node->language)
// may not be reliable since the function creating the entity object will
// not know which language "Entity Translation" is configured to create the
// entity in.
elseif ($this->isNewEntity()) {
return $this->getDefaultLanguage();
}
else {
return $this->getLanguage();
}
}
/**
* @see EntityTranslationHandlerInterface::getLanguageKey()
*/
@ -952,7 +1052,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
if ($outdated) {
$translations = $this->getTranslations();
foreach ($translations->data as $langcode => &$translation) {
if ($langcode != $this->getFormLanguage()) {
if ($langcode != $this->getActiveLanguage()) {
$translation['translate'] = 1;
}
}
@ -1097,7 +1197,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
*/
public function getSharedFieldsAccess() {
$settings = entity_translation_settings($this->entityType, $this->bundle);
return ($settings['shared_fields_original_only'] == FALSE || $this->getLanguage() == $this->getFormLanguage()) &&
return ($settings['shared_fields_original_only'] == FALSE || $this->getLanguage() == $this->getActiveLanguage()) &&
(!entity_translation_workflow_enabled() || user_access('edit translation shared fields') || user_access("edit {$this->entityType} translation shared fields"));
}
@ -1108,22 +1208,6 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
return !empty($this->entityInfo['translation']['entity_translation']['alias']);
}
/**
* @see EntityTranslationHandlerInterface::setFormLanguage()
*/
public function setFormLanguage($langcode) {
$this->formLanguage = $langcode;
$args = func_get_args();
$this->notifyChildren(__FUNCTION__, $args);
}
/**
* @see EntityTranslationHandlerInterface::getFormLanguage()
*/
public function getFormLanguage() {
return !empty($this->formLanguage) ? $this->formLanguage : $this->getLanguage();
}
/**
* @see EntityTranslationHandlerInterface::setSourceLanguage()
*/
@ -1161,7 +1245,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
public function entityForm(&$form, &$form_state) {
$this->entityForm = TRUE;
$translations = $this->getTranslations();
$form_langcode = $this->getFormLanguage();
$form_langcode = $this->getActiveLanguage();
$langcode = $this->getLanguage();
$is_translation = $this->isTranslationForm();
$new_translation = !isset($translations->data[$form_langcode]);
@ -1461,7 +1545,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
$language_key = $this->getLanguageKey();
if (isset($form_state['values'][$language_key]) && !$this->isTranslationForm()) {
$langcode = $form_state['values'][$language_key];
$this->setFormLanguage($langcode);
$this->setActiveLanguage($langcode);
}
}
@ -1474,7 +1558,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
}
$this->updateFormLanguage($form_state);
$form_langcode = $this->getFormLanguage();
$form_langcode = $this->getActiveLanguage();
foreach (field_info_instances($this->entityType, $this->bundle) as $instance) {
$field_name = $instance['field_name'];
@ -1496,7 +1580,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
* @see EntityTranslationHandlerInterface::entityFormSubmit()
*/
public function entityFormSubmit($form, &$form_state) {
$form_langcode = $this->getFormLanguage();
$form_langcode = $this->getActiveLanguage();
$translations = $this->getTranslations();
$is_translation = !empty($form_state['entity_translation']['is_translation']);
$new_translation = !isset($translations->data[$form_langcode]);
@ -1551,7 +1635,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
if (count($translations->data) > 0) {
$languages = language_list();
$form_langcode = $this->getFormLanguage();
$form_langcode = $this->getActiveLanguage();
$language_tabs = array();
if ($this->getSourceLanguage()) {
@ -1624,7 +1708,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
* Returns TRUE if an entity translation is being edited.
*/
protected function isTranslationForm() {
return !$this->isNewEntity() && $this->getFormLanguage() != $this->getLanguage();
return !$this->isNewEntity() && $this->getActiveLanguage() != $this->getLanguage();
}
/**
@ -1653,7 +1737,7 @@ class EntityTranslationDefaultHandler implements EntityTranslationHandlerInterfa
*
* @throws Exception
*/
private function initPathVariables() {
protected function initPathVariables() {
if (empty($this->pathScheme) || !isset($this->entityInfo['translation']['entity_translation']['path schemes'][$this->pathScheme])) {
throw new Exception("Cannot initialize entity translation path variables (invalid path scheme).");
}

View File

@ -87,10 +87,7 @@ class EntityTranslationHandlerFactory {
if (!isset($this->handlers[$entity_type][$id])) {
$entity_info = entity_get_info($entity_type);
$class = $entity_info['translation']['entity_translation']['class'];
// @todo Remove the fourth parameter once 3rd-party translation handlers
// have been fixed and no longer require the deprecated entity_id
// parameter.
$handler = new $class($entity_type, $entity_info, $entity, NULL);
$handler = new $class($entity_type, $entity_info, $entity);
$handler->setFactory($this);
$this->handlers[$entity_type][$id] = $handler;
}

View File

@ -14,6 +14,9 @@ class EntityTranslationTestCase extends DrupalWebTestCase {
protected $admin_user;
protected $translator_user;
/**
* {@inheritdoc}
*/
function setUp() {
$args = func_get_args();
call_user_func_array(array('parent', 'setUp'), $args);
@ -133,6 +136,19 @@ class EntityTranslationTestCase extends DrupalWebTestCase {
}
}
/**
* Disable a language which is in the language list.
*
* @param string $langcode
* The code of the language to disable, which must exist.
*/
function disableLanguage($langcode) {
$edit = array(
'enabled[' . $langcode . ']' => FALSE,
);
$this->drupalPost('admin/config/regional/language', $edit, 'Save configuration');
}
/**
* Install a specified language if it has not been already, otherwise make sure that the language is enabled.
*
@ -190,7 +206,7 @@ class EntityTranslationTestCase extends DrupalWebTestCase {
// Check if the setting works.
$this->drupalGet('node/add/page');
$this->assertFieldById('edit-body-und-add-more', t('Add another item'), t('Add another item button found.'));
$this->assertFieldById('edit-body-en-add-more', t('Add another item'), t('Add another item button found.'));
}
/**
@ -205,9 +221,8 @@ class EntityTranslationTestCase extends DrupalWebTestCase {
*/
function createPage($title, $body, $langcode) {
$edit = array();
$language_none = LANGUAGE_NONE;
$edit["title"] = $title;
$edit["body[$language_none][0][value]"] = $body;
$edit["body[$langcode][0][value]"] = $body;
$edit['language'] = $langcode;
$this->drupalPost('node/add/page', $edit, t('Save'));
$this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), t('Basic page created.'));
@ -270,10 +285,27 @@ class EntityTranslationTranslationTestCase extends EntityTranslationTestCase {
$this->login($this->getAdminUser());
$this->addLanguage('en');
$this->addLanguage('es');
$this->addLanguage('fr');
$this->disableLanguage('fr');
$this->configureContentType();
$this->login($this->getTranslatorUser());
}
/**
* Test disabled languages.
*
* Make sure disabled languages are not accessible in the language list when
* the option entity_translation_languages_enabled is enabled.
*/
function testDisabledLanguages() {
$this->drupalGet('node/add/page');
$this->assertRaw('value="fr"', 'French is available even if the language is disabled');
variable_set('entity_translation_languages_enabled', TRUE);
$this->drupalGet('node/add/page');
$this->assertNoRaw('value="fr"', 'French is not available when the language is disabled and the option entity_translation_languages_enabled is enabled.');
}
/**
* Test if field based translation works.
*
@ -525,3 +557,788 @@ class EntityTranslationHookTestCase extends EntityTranslationTestCase {
return $info;
}
}
/**
* Tests that entity translation handler hierarchy works properly.
*/
class EntityTranslationHierarchyTestCase extends EntityTranslationTestCase {
/**
* Return the test information.
*/
public static function getInfo() {
return array(
'name' => 'Entity translation hierarchy',
'description' => 'Tests that entity translation handler hierarchy works properly.',
'group' => 'Entity translation',
);
}
/**
* {@inheritdoc}
*/
function setUp() {
parent::setUp('locale', 'entity_translation');
}
/**
* Tests the handler hierarchy.
*/
public function testHierarchy() {
$entity_type = 'node';
$node = $this->drupalCreateNode();
$factory = EntityTranslationHandlerFactory::getInstance();
$handler = $factory->getHandler($entity_type, $node);
$children = array();
foreach (range(0, 4) as $index) {
$children[$index] = $this->drupalCreateNode();
$handler->addChild($entity_type, $children[$index]);
}
$langcode = 'it';
$handler->setActiveLanguage($langcode);
foreach ($children as $child) {
$child_handler = $factory->getHandler($entity_type, $child);
$this->assertEqual($child_handler->getActiveLanguage(), $langcode);
}
$rm_index = mt_rand(0, count($children) - 1);
$handler->removeChild($entity_type, $children[$rm_index]);
$langcode = 'fr';
$handler->setActiveLanguage($langcode);
foreach ($children as $index => $child) {
$child_handler = $factory->getHandler($entity_type, $child);
$this->assertEqual($child_handler->getActiveLanguage() == $langcode, $index != $rm_index);
}
// @todo Test the other properties.
}
}
/**
* Basic tests for nodes using both content and entity translation.
*/
class EntityTranslationContentTranslationTestCase extends EntityTranslationTestCase {
/**
* Return the test information.
*/
public static function getInfo() {
return array(
'name' => 'Content and entity translation',
'description' => 'Basic tests for nodes using both content and entity translatio.',
'group' => 'Entity translation',
);
}
/**
* {@inheritdoc}
*/
public function setUp() {
// Activate modules and unset users.
parent::setUp('locale', 'translation', 'translation_test', 'entity_translation');
// Create admin and translator users with one extra permission,
// namely the 'translate content' permission.
// These getters works also as setters.
$this->getAdminUser(array(
'translate content',
));
$this->getTranslatorUser(array(
'translate content',
));
$this->login($this->getAdminUser());
$this->addLanguage('en');
$this->addLanguage('es');
$this->enableUrlLanguageDetection();
$this->configureContentType();
$this->login($this->getTranslatorUser());
}
/**
* Configure the "Basic page" content type for entity translation tests.
*/
public function configureContentType() {
// Configure the "Basic page" content type to use multilingual support with
// content translation.
$edit = array();
$edit['language_content_type'] = TRANSLATION_ENABLED;
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
$this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
// Toggle body field's translatability.
$edit = array();
$edit['field[translatable]'] = 1;
$this->drupalPost('admin/structure/types/manage/page/fields/body', $edit, t('Save settings'));
$this->assertRaw(t('Saved %field configuration.', array('%field' => 'Body')), t('Body field settings have been updated.'));
}
/**
* @see TranslationTestCase::createPage()
*/
function createPage($title, $body, $language = NULL) {
$edit = array();
$langcode = LANGUAGE_NONE;
$edit["title"] = $title;
$edit["body[$langcode][0][value]"] = $body;
if (!empty($language)) {
$edit['language'] = $language;
}
$this->drupalPost('node/add/page', $edit, t('Save'));
$this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), 'Basic page created.');
// Check to make sure the node was created.
$node = $this->drupalGetNodeByTitle($title);
$this->assertTrue($node, 'Node found in database.');
return $node;
}
/**
* Tests copying of source node's body value in the add translation form page.
*/
public function testCopyFieldsUsingContentTranslation() {
// Create Basic page in English.
$node_title = $this->randomName();
$node_body = $this->randomName();
$node = $this->createPage($node_title, $node_body, 'en');
// Check that the edit form correctly copies over the field's values from
// the source node.
$target_language = 'es';
$this->drupalGet('node/add/page', array('query' => array('translation' => $node->nid, 'target' => $target_language)));
$body_key = "body[${target_language}][0][value]";
$this->assertFieldByXPath("//textarea[@name='$body_key']", $node_body, "Body field correctly instantiated with the value of the source language.");
}
}
/**
* Tests for integration of Entity Translation with other modules.
*/
class EntityTranslationIntegrationTestCase extends EntityTranslationTestCase {
/**
* Return the test information.
*/
public static function getInfo() {
return array(
'name' => 'Integration with other modules',
'description' => 'Tests for integration of Entity Translation with other modules.',
'group' => 'Entity translation',
// We need to add this to the test_dependencies[] as well.
'dependencies' => array('pathauto'),
);
}
/**
* {@inheritdoc}
*/
public function setUp() {
// Activate modules.
parent::setUp('locale', 'entity_translation');
// Create admin and translator users with one extra permission,
// namely the 'administer content' permission for the admin, to
// allow enabling the pathauto module during testing. The
// Translator user needs to be able to create url aliases.
$this->getAdminUser(array(
'administer modules',
));
$this->getTranslatorUser(array(
'create url aliases',
));
$this->login($this->getAdminUser());
$this->addLanguage('en');
$this->addLanguage('es');
$this->enableUrlLanguageDetection();
$this->configureContentType();
$this->login($this->getTranslatorUser());
}
/**
* Returns the role id of an $account object.
*/
protected function getUserRole($account) {
return reset($account->roles);
}
/**
* Tests Pathauto integration.
*/
public function testPathautoIntegration() {
$languages = language_list();
// Enable the path module to add aliases manually.
$this->login($this->getAdminUser());
if (!module_exists('path')) {
module_enable(array('path'));
}
$this->login($this->getTranslatorUser());
// Create Basic page in English.
$node_title = $this->randomName();
$node_body = $this->randomName();
$node = $this->createPage($node_title, $node_body, 'en');
$node_alias = $this->randomName();
$edit = array(
'path[alias]' => $node_alias,
);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
// Submit translation in Spanish.
$node_translation_body = $this->randomName();
$this->createTranslation($node, $node_title, $node_translation_body, 'es');
$node_translation_alias = $this->randomName();
$edit = array(
'path[alias]' => $node_translation_alias,
);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'), array('language' => $languages['es']));
// Enable the pathauto module.
$this->login($this->getAdminUser());
if (!module_exists('pathauto')) {
module_enable(array('pathauto'));
}
$admin_rid = $this->getUserRole($this->getAdminUser());
user_role_grant_permissions($admin_rid, array('administer url aliases', 'administer pathauto'));
$translator_rid = $this->getUserRole($this->getTranslatorUser());
user_role_grant_permissions($translator_rid, array('create url aliases'));
$this->login($this->getTranslatorUser());
// Create pathauto alias for source node.
$edit = array(
'path[pathauto]' => TRUE,
);
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
// Clear the static caches in case they interfere.
drupal_lookup_path('wipe');
$node_pathauto_alias = pathauto_node_update_alias($node, 'return');
$node_translation_pathauto_alias = pathauto_node_update_alias($node, 'return', array('language' => 'es'));
// Check that the new alias for the translation matches the
// pathauto's logic.
$this->assertEqual($this->getUrl(), url(drupal_get_path_alias($node_pathauto_alias), array('absolute' => TRUE)));
// Check that a pathauto alias was created for the source and
// matches the pathauto's logic.
$this->drupalGet('node/' . $node->nid, array('language' => $languages['es']));
$this->assertEqual($this->getUrl(), url(drupal_get_path_alias($node_translation_pathauto_alias), array('absolute' => TRUE, 'language' => $languages['es'])));
// Delete the two aliases.
path_delete(array('source' => 'node/' . $node->nid));
// Create pathauto alias for the translation of the node.
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'), array('language' => $languages['es']));
// Clear the static caches in case they interfere.
drupal_lookup_path('wipe');
$node_pathauto_alias = pathauto_node_update_alias($node, 'return');
$node_translation_pathauto_alias = pathauto_node_update_alias($node, 'return', array('language' => 'es'));
// Check that the new alias for the translation matches the
// pathauto's logic.
$this->assertEqual($this->getUrl(), url(drupal_get_path_alias($node_translation_pathauto_alias), array('absolute' => TRUE, 'language' => $languages['es'])));
// Check that a pathauto alias was created for the source and
// matches the pathauto's logic.
$this->drupalGet('node/' . $node->nid);
$this->assertEqual($this->getUrl(), url(drupal_get_path_alias($node_pathauto_alias), array('absolute' => TRUE)));
}
}
/**
* Tests for enabling fields to use Entity Translation or disabling them.
*/
class EntityTranslationToggleFieldsTranslatabilityTestCase extends EntityTranslationTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Fields translatability toggling',
'description' => 'Tests for enabling fields to use Entity Translation or disabling them.',
'group' => 'Entity translation',
);
}
/**
* {@inheritdoc}
*/
public function setUp() {
// Activate modules.
parent::setUp('locale', 'taxonomy', 'entity_translation', 'entity_translation_test');
$this->login($this->getAdminUser(array(
'administer taxonomy',
'toggle field translatability',
)));
$this->login($this->getTranslatorUser(array(
'administer taxonomy',
)));
}
/**
* Configure the "Basic page" content type for entity translation tests.
*/
protected function configureContentTypeForRevisions() {
// Configure the "Basic page" content type to use revisions.
$edit = array(
'node_options[revision]' => 1,
);
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
$this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
}
/**
* Create a "Basic page" in the specified language.
*
* @param $title
* Title of the basic page in the specified language.
* @param $body
* Body of the basic page in the specified language.
*/
protected function createUntranslatedPage($title, $body) {
$edit = array(
'title' => $title,
'body[und][0][value]' => $body,
);
$this->drupalPost('node/add/page', $edit, t('Save'));
$this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), t('Basic page created.'));
// Check to make sure the node was created.
$node = $this->drupalGetNodeByTitle($title);
$this->assertTrue($node, t('Node found in database.'));
return $node;
}
/**
* Create a "Tags" term in the specified language.
*
* @param $name
* Name of the term.
* @param $description
* Description of the term.
* @param $text
* Content for the field_simple_text field.
*/
protected function createUntranslatedTag($name, $description, $text) {
$edit = array(
'name' => $name,
'description[value]' => $description,
"field_simple_text[und][0][value]" => $text,
);
$this->drupalPost('admin/structure/taxonomy/tags/add', $edit, t('Save'));
// Check to make sure the term was created.
$term = current(entity_load('taxonomy_term', FALSE, array('name' => $name), TRUE));
$this->assertTrue($term, t('Term found in database.'));
return $term;
}
/**
* Tests toggling translatability on fields with data (non-revisionable).
*/
public function testTogglingFieldsWithDataNonRevisionable() {
// Create an untranslated Basic page.
$node_title = $this->randomName();
$node_body = $this->randomName();
$node = $this->createUntranslatedPage($node_title, $node_body);
$this->assert(isset($node->body[LANGUAGE_NONE]), t('Found body field data in LANGUAGE_NONE as expected.'));
$this->assertEqual($node->body[LANGUAGE_NONE][0]['value'], $node_body);
// Create an untranslated Tags term.
$term_name = $this->randomName();
$term_description = $this->randomName();
$term_simple_text = $this->randomName();
$term = $this->createUntranslatedTag($term_name, $term_description, $term_simple_text);
$this->assert(isset($term->field_simple_text[LANGUAGE_NONE]), t('Found field data in LANGUAGE_NONE as expected.'));
$this->assertEqual($term->field_simple_text[LANGUAGE_NONE][0]['value'], $term_simple_text);
// Enable translation for field body and check field migration.
$this->login($this->getAdminUser());
$this->drupalGet('admin/structure/types/manage/page/fields/body');
$this->clickLink('Enable translation');
$this->drupalPost(NULL, array(), t('Confirm'));
$node = current(entity_load('node', FALSE, array('title' => $node_title), TRUE));
$this->assert(isset($node->body['en']), t('Found field data in English as expected.'));
$this->assertEqual($node->body['en'][0]['value'], $node_body);
$this->assert(!isset($node->body[LANGUAGE_NONE]), t('No field data in LANGUAGE_NONE found.'));
// Disable translation for body field and check field reverse migration.
$this->drupalGet('admin/structure/types/manage/page/fields/body');
$this->clickLink('Disable translation');
$this->drupalPost(NULL, array(), t('Confirm'));
$node = current(entity_load('node', FALSE, array('title' => $node_title), TRUE));
$this->assert(isset($node->body[LANGUAGE_NONE]), t('Found field data in LANGUAGE_NONE as expected.'));
$this->assertEqual($node->body[LANGUAGE_NONE][0]['value'], $node_body);
$this->assert(!isset($node->body['en']), t('No field data in English found.'));
// Enable translation for field_simple_text and check field migration.
$this->drupalGet('admin/structure/taxonomy/tags/fields/field_simple_text');
$this->clickLink('Enable translation');
$this->drupalPost(NULL, array(), t('Confirm'));
// Clear the field cache in order to load current field data.
field_info_cache_clear();
// Load the term and check that the fields data are under 'en'.
$term = current(entity_load('taxonomy_term', FALSE, array('name' => $term_name), TRUE));
$this->assert(isset($term->field_simple_text['en']), t('Found field data in English as expected.'));
$this->assertEqual($term->field_simple_text['en'][0]['value'], $term_simple_text);
$this->assert(!isset($term->field_simple_text[LANGUAGE_NONE]), t('No field data in LANGUAGE_NONE found.'));
// Disable translation for field_simple_text.
$this->drupalGet('admin/structure/taxonomy/tags/fields/field_simple_text');
$this->clickLink('Disable translation');
$this->drupalPost(NULL, array(), t('Confirm'));
// Load the term and check that the fields data are under LANGUAGE_NONE.
$term = current(entity_load('taxonomy_term', FALSE, array('name' => $term_name), TRUE));
$this->assert(isset($term->field_simple_text[LANGUAGE_NONE]), t('Found field data in LANGUAGE_NONE as expected.'));
$this->assertEqual($term->field_simple_text[LANGUAGE_NONE][0]['value'], $term_simple_text);
$this->assert(!isset($term->field_simple_text['en']), t('No field data in English found.'));
}
/**
* Tests toggling translatability on fields with data (revisionable).
*/
public function testTogglingFieldsWithDataRevisionable() {
// Enable revisions for Basic pages.
$this->login($this->getAdminUser());
$this->configureContentTypeForRevisions();
// Create an untranslated Basic page.
$this->login($this->getTranslatorUser());
$node_title = $this->randomName();
$node_body = $this->randomName();
$node = $this->createUntranslatedPage($node_title, $node_body);
$this->assert(isset($node->body[LANGUAGE_NONE]), t('Found field data in LANGUAGE_NONE as expected.'));
$this->assertEqual($node->body[LANGUAGE_NONE][0]['value'], $node_body);
// Create a new revision for the page.
$edit_revision = array(
'title' => $this->randomName(),
);
$this->drupalPost('node/' . $node->nid . '/edit', $edit_revision, t('Save'));
$node = node_load($node->nid, NULL, TRUE);
$this->assert($node->vid == $node->nid + 1, t('Correct vid attached to the node object.'));
// Enable translation for field body and check field migration on all
// revisions.
$this->login($this->getAdminUser());
$this->drupalGet('admin/structure/types/manage/page/fields/body');
$this->clickLink('Enable translation');
$this->drupalPost(NULL, array(), t('Confirm'));
$node_current_revision = current(entity_load('node', FALSE, array('vid' => $node->vid), TRUE));
$this->assert(isset($node_current_revision->body['en']), t('Found field data in English as expected.'));
$this->assertEqual($node_current_revision->body['en'][0]['value'], $node_body);
$this->assert(!isset($node_current_revision->body[LANGUAGE_NONE]), t('No field data in LANGUAGE_NONE found.'));
$node_previous_revision = current(entity_load('node', FALSE, array('vid' => ($node->vid - 1)), TRUE));
$this->assert(isset($node_previous_revision->body['en']), t('Found field data in English as expected.'));
$this->assertEqual($node_previous_revision->body['en'][0]['value'], $node_body);
$this->assert(!isset($node_previous_revision->body[LANGUAGE_NONE]), t('No field data in LANGUAGE_NONE found.'));
// Disable translation for field_body.
$this->drupalGet('admin/structure/types/manage/page/fields/body');
$this->clickLink('Disable translation');
$this->drupalPost(NULL, array(), t('Confirm'));
// Disable translation for field body and check field reverse migration on
// all revisions.
$node_current_revision = current(entity_load('node', FALSE, array('vid' => $node->vid), TRUE));
$this->assert(isset($node_current_revision->body[LANGUAGE_NONE]), t('Found field data in LANGUAGE_NONE as expected.'));
$this->assertEqual($node_current_revision->body[LANGUAGE_NONE][0]['value'], $node_body);
$this->assert(!isset($node_current_revision->body['en']), t('No field data in English found.'));
$node_previous_revision = current(entity_load('node', FALSE, array('vid' => ($node->vid - 1)), TRUE));
$this->assert(isset($node_previous_revision->body[LANGUAGE_NONE]), t('Found field data in LANGUAGE_NONE as expected.'));
$this->assertEqual($node_previous_revision->body[LANGUAGE_NONE][0]['value'], $node_body);
$this->assert(!isset($node_previous_revision->body['en']), t('No field data in English found.'));
}
}
/**
* Tests for the taxonomy autocomplete translation modes.
*/
class EntityTranslationTaxonomyAutocompleteTestCase extends EntityTranslationTestCase {
/**
* Returns the test information.
*/
public static function getInfo() {
return array(
'name' => 'Entity translation taxonomy autocomplete',
'description' => 'Tests for the taxonomy autocomplete translation modes.',
'group' => 'Entity translation',
'dependencies' => array('title'),
);
}
/**
* {@inheritdoc}
*/
function setUp() {
parent::setUp('locale', 'entity_translation', 'taxonomy', 'title');
$this->login($this->getAdminUser(array(
'administer taxonomy',
'administer entity translation',
)));
$this->addLanguage('en');
$this->addLanguage('it');
$this->addLanguage('fr');
$this->enableUrlLanguageDetection();
$this->configureVocabulary();
$this->configureContentType();
}
/**
* Makes the "Tags" vocabulary translatable.
*/
function configureVocabulary() {
$edit = array(
'entity_translation_entity_types[taxonomy_term]' => TRUE,
);
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
$edit = array(
'entity_translation_taxonomy' => TRUE,
);
$this->drupalPost('admin/structure/taxonomy/tags/edit', $edit, t('Save'));
$edit = array(
'enabled' => TRUE,
);
$this->drupalPost('admin/structure/taxonomy/tags/fields/replace/name', $edit, t('Save settings'));
$edit = array(
'enabled' => TRUE,
);
$this->drupalPost('admin/structure/taxonomy/tags/fields/replace/description', $edit, t('Save settings'));
}
/**
* {@inheritdoc}
*/
function configureContentType() {
parent::configureContentType();
// Create an untranslatable term reference field with unlimited cardinality.
$edit = array(
'fields[_add_new_field][label]' => 'Test tags',
'fields[_add_new_field][field_name]' => 'test_tags',
'fields[_add_new_field][type]' => 'taxonomy_term_reference',
'fields[_add_new_field][widget_type]' => 'taxonomy_autocomplete',
);
$this->drupalPost('admin/structure/types/manage/page/fields', $edit, t('Save'));
$edit = array(
'field[settings][allowed_values][0][vocabulary]' => 'tags',
);
$this->drupalPost('admin/structure/types/manage/page/fields/field_test_tags/field-settings', $edit, t('Save field settings'));
// Verify the in-place translation option is available.
$this->drupalGet('admin/structure/types/manage/page/fields/field_test_tags');
$this->assertRaw(t('Enable in-place translation of terms'));
$edit = array(
'field[cardinality]' => FIELD_CARDINALITY_UNLIMITED,
);
$this->drupalPost(NULL, $edit, t('Save settings'));
// Ensure entity info is up-to-date.
drupal_flush_all_caches();
}
/**
* Enables in-place translation.
*/
function enableInPlaceTranslation() {
$edit = array(
'instance[settings][entity_translation_taxonomy_autocomplete_translate]' => TRUE,
);
$this->drupalPost('admin/structure/types/manage/page/fields/field_test_tags', $edit, t('Save settings'));
}
/**
* Tests that in-place translation works as expected.
*/
function testInPlaceTranslation() {
$this->enableInPlaceTranslation();
$this->login($this->getTranslatorUser(array(
'administer taxonomy',
)));
$values = array(
'Red' => 'Rosso',
'Green' => 'Verde',
'Blue' => 'Blu',
);
// Create an English node with a few new tags.
$edit = array(
'title' => 'Test 1',
'field_test_tags[' . LANGUAGE_NONE . ']' => implode(', ', array_keys($values)),
'language' => 'en',
);
$this->drupalPost('node/add/page', $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit['title']);
// Create an Italian translation and translate the English tags.
$this->drupalGet('node/' . $node->nid . '/translate');
$this->clickLink('add', 1);
$edit = array();
foreach (array_values($values) as $delta => $value) {
$edit['field_test_tags[' . LANGUAGE_NONE . '][' . $delta . ']'] = $value;
}
$this->drupalPost(NULL, $edit, t('Save'));
// Verify that the Italian values are correctly stored/displayed.
foreach ($values as $original => $translation) {
$this->assertRaw($translation);
}
// Verify that the original English values were correctly retained.
$this->drupalGet('node/' . $node->nid);
foreach ($values as $original => $translation) {
$this->assertRaw($original);
}
}
/**
* That the autocomplete works with translated terms.
*/
function testTranslatedAutocomplete() {
$this->login($this->getTranslatorUser(array(
'administer taxonomy',
)));
$vocabulary = taxonomy_vocabulary_machine_name_load('tags');
$entity_type = 'taxonomy_term';
$existing_values = array();
$translated_values = array(
'en' => array(
'Red' => 'Rosso',
'Green' => 'Verde',
),
'it' => array(
'Blu' => 'Blue',
),
);
$langcodes = array_keys($translated_values);
// Create a few existing tags with different original language and translate
// them accordingly.
foreach ($translated_values as $langcode => $values) {
title_active_language($langcode);
$translation_langcode = current(array_diff($langcodes, array($langcode)));
foreach ($values as $original => $translation) {
$term = (object) array(
'vid' => $vocabulary->vid,
'vocabulary_machine_name' => $vocabulary->machine_name,
'name' => $original,
'name_field' => array(
$langcode => array(array('value' => $original)),
$translation_langcode => array(array('value' => $translation)),
),
);
$translation = array(
'language' => $translation_langcode,
'source' => $langcode,
'status' => TRUE,
);
$handler = entity_translation_get_handler($entity_type, $term);
$handler->setOriginalLanguage($langcode);
$handler->initTranslations();
$handler->setTranslation($translation);
taxonomy_term_save($term);
$existing_values[$term->name_field['en'][0]['value']] = $term->name_field['it'][0]['value'];
}
}
// Verify that the English autocomplete route returns results for terms
// originally created in English.
$this->autocompleteGet('en', 'Re');
$this->assertRaw('Red');
$this->assertRaw('Green');
// Verify that the English autocomplete route returns results for terms
// translated into English.
$this->autocompleteGet('en', 'Blu');
$this->assertRaw('Blue');
// Verify that the Italian autocomplete route returns results for terms
// originally created in Italian.
$this->autocompleteGet('it', 'Blu');
$this->assertRaw('Blu');
$this->assertNoRaw('Blue');
// Verify that the Italian autocomplete route returns results for terms
// translated into Italian.
$this->autocompleteGet('it', 'R');
$this->assertRaw('Rosso');
$this->assertRaw('Verde');
// Verify that existing tags are correctly referenced and new tags are
// correctly created, when saving an English node.
$new_values = array(
'Cyan' => 'Ciano',
'Magenta' => 'Magenta',
'Yellow' => 'Giallo',
'Black' => 'Nero',
);
$all_values = $existing_values + $new_values;
$edit = array(
'title' => 'Test 1',
'field_test_tags[' . LANGUAGE_NONE . ']' => implode(', ', array_keys($all_values)),
'language' => 'en',
);
$this->drupalPost('node/add/page', $edit, t('Save'));
foreach ($all_values as $original => $translation) {
$this->assertRaw($original);
}
// Verify that existing translated tags are correctly referenced and new
// tags are correctly created, when translated the node into Italian.
$node = $this->drupalGetNodeByTitle($edit['title']);
$this->drupalGet('node/' . $node->nid . '/translate');
$this->clickLink('add', 1);
$edit = array(
'field_test_tags[' . LANGUAGE_NONE . ']' => implode(', ', $all_values),
);
$this->drupalPost(NULL, $edit, t('Save'));
foreach ($all_values as $original => $translation) {
$this->assertRaw($translation);
}
// Verify that existing (translated) tags were preserved, while new Italian
// tags replaced the corresponding English versions.
$this->drupalGet('node/' . $node->nid);
foreach ($existing_values as $original => $translation) {
$this->assertRaw($original);
}
foreach ($new_values as $original => $translation) {
$this->assertRaw($translation);
}
}
/**
* Performs a GET request to the autocomplete path.
*
* @param string $langcode
* The language to use to query results.
* @param string $query
* The search query string.
*/
protected function autocompleteGet($langcode, $query) {
$path = 'entity_translation/taxonomy_term/autocomplete/' . $langcode . '/field_test_tags/' . $query;
$languages = language_list();
$this->drupalGet($path, array('language' => $languages[$langcode]));
}
}

View File

@ -4,11 +4,11 @@ core = 7.x
package = Testing
hidden = TRUE
dependencies[] = entity_translation
files[] = entity_translation_test.module
; Information added by Drupal.org packaging script on 2016-09-28
version = "7.x-1.0-beta5+15-dev"
; Information added by Drupal.org packaging script on 2019-01-20
version = "7.x-1.0+5-dev"
core = "7.x"
project = "entity_translation"
datestamp = "1475057941"
datestamp = "1548022384"

View File

@ -5,3 +5,34 @@
* Installation functionality for Entity Translation testing module.
*/
/**
* Implements hook_install().
*/
function entity_translation_test_install() {
// Create a simple text field, attached to taxonomy_terms.
field_info_cache_clear();
$field = array(
'field_name' => 'field_simple_text',
'type' => 'text',
'cardinality' => 1,
);
field_create_field($field);
$instance = array(
'field_name' => $field['field_name'],
'label' => ucfirst(str_replace('_', ' ', $field['field_name'])),
'entity_type' => 'taxonomy_term',
'bundle' => 'tags',
'widget' => array(
'type' => 'text_textfield',
),
'display' => array(
'default' => array(
'type' => 'text_default',
),
),
);
field_create_instance($instance);
}

View File

@ -1,7 +1,8 @@
<?php
/**
* @file
* Contains the relationship plugin for relating entities to translation metadata.
* Contains a views plugin for relating entities to translation metadata.
*/
/**
@ -27,7 +28,10 @@ class entity_translation_handler_relationship extends views_handler_relationship
$alias = $def['table'] . '_' . $this->table;
// We need to add a condition on entity type to the join to avoid getting
// relationships to entities with other types.
$join->extra = "$alias.entity_type = '{$def['entity type']}'";
$join->extra = array(
array('field' => 'entity_type', 'value' => $def['entity type']),
);
$this->alias = $this->query->add_relationship($alias, $join, 'entity_translation', $this->relationship);
}
}