webform_localization.module in Webform Localization 7.4
Same filename and directory in other branches
Webform localization module.
This module provides Localization Features to forms and questionnaires generated by the Webform Module.
i18n_string integration based on patch http://drupal.org/node/245424#comment-5244256 by Calin Marian.
Further development sponsored by Riot Games.
@author German Martin <gmartin.php@gmail.com>
File
webform_localization.moduleView source
<?php
/**
* @file
* Webform localization module.
*
* This module provides Localization Features to forms and questionnaires
* generated by the Webform Module.
*
* i18n_string integration based on patch
* http://drupal.org/node/245424#comment-5244256 by Calin Marian.
*
* Further development sponsored by Riot Games.
*
* @author German Martin <gmartin.php@gmail.com>
*/
/**
* Implements hook_help().
*/
function webform_localization_help($section = 'admin/help#webform_localization', $arg = NULL) {
$output = '';
switch ($section) {
case 'admin/help#webform_localization':
$output = '<p>' . t('The Webform Localization module provides multilingual features to the Webform Module. Special options in the webform and component configuration let you enable different ways to manage translation of forms and questionnaires.') . '</p>';
$output .= '<p>' . t('You can choose two different ways to manage localization that cover this scenarios:') . '</p>';
$output .= '<p>' . t('A) <strong>If you want to keep a single webform across all nodes in a translation set:</strong><br />Use i18n_string integration to translate webform strings. This module expose webform properties, components and emails strings through the i18n module. All submissions results are related to the original node only.<br />(You have a "<em>localization by string translation</em>" fieldset in the form settings to enable this)') . '</p>';
$output .= '<p>' . t('B)<strong> If you want to keep a webform per node per language but synchronized:</strong><br />The entire webform structure is replicated when a translated node is created then you can customize it at will. You can add specific options or components per language and choose to keep sync: webform properties, components properties, roles and emails recipients. In this scenario make no sense of having results attached to one node since each webform could have a different structure with only a few components in sync.<br />(You have a "<em>localization by sync</em>" fieldset in the form settings to enable this)') . '</p>';
break;
}
return $output;
}
/**
* Implements hook_i18n_string_info().
*/
function webform_localization_i18n_string_info() {
$groups['webform'] = array(
'title' => t('Webform Localization'),
'description' => t('Localizable properties of webforms, like title, select options, and others.'),
// This group doesn't have strings with format.
'format' => FALSE,
// This group cannot list all strings.
'list' => FALSE,
'refresh callback' => 'webform_localization_i18n_string_refresh',
);
return $groups;
}
/**
* Update / create / delete translation source for components.
*
* Refresh callback that regenerates all the translatable properties of the
* components of the matching webforms configuration.
*/
function webform_localization_i18n_string_refresh($group) {
if ($group == 'webform') {
global $language;
$languages = language_list();
$source_language = $languages[i18n_string_source_language()];
// Refresh the strings using source language.
$language = $source_language;
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
// In case updating before UUID support.
if (module_exists('uuid') && !variable_get('webform_localization_using_uuid', FALSE)) {
webform_localization_uuid_update_strings(FALSE);
}
// Get components configured as translatable.
$query = db_select('webform_component', 'wc');
$query
->fields('wc');
$query
->condition('wl.expose_strings', 0, '>');
$query
->innerJoin('webform_localization', 'wl', 'wc.nid = wl.nid');
$components = $query
->execute()
->fetchAll();
foreach ($components as $component) {
$component = (array) $component;
$component['extra'] = unserialize($component['extra']);
webform_localization_component_update_translation_strings($component);
$component['extra'] = serialize($component['extra']);
drupal_write_record('webform_component', $component, array(
'nid',
'cid',
));
}
// Get emails configured as translatable.
$query = db_select('webform_localization', 'wl');
$query
->fields('wl', array(
'nid',
));
$query
->condition('wl.expose_strings', 0, '>');
$nid_list = $query
->execute()
->fetchAllAssoc('nid');
// @todo: Find a more eficient way to manage webform translatable
// properties.
$nodes = node_load_multiple(array_keys($nid_list));
module_load_include('inc', 'webform_localization', 'includes/webform_localization.sync');
foreach ($nid_list as $nid => $value) {
$emails = _webform_localization_emails_load($nid);
webform_localization_emails_translation_string_refresh($emails, $nid);
$node = $nodes[$nid];
webform_localization_translate_strings($node, TRUE);
}
// NOTE: Delete string for webforms that has disabled i18n translation.
// This is the only moment when we deleted translation for disabled
// webforms. This way we provide the feature to temporally disable the
// webform i18n string without losing custom translated texts.
webform_localization_delete_all_strings();
return TRUE;
}
}
/**
* Load a component file into memory.
*
* @see webform_component_include()
*
* @param string $component_type
* The string machine name of a component.
*/
function webform_localization_component_include($component_type) {
static $included = array();
// No need to load components that have already been added once.
if (!isset($included[$component_type])) {
$included[$component_type] = TRUE;
module_load_include('inc', 'webform_localization', 'components/' . $component_type);
}
}
/**
* Invoke a component callback.
*
* @see webform_component_invoke()
*
* @param string $type
* The component type as a string.
* @param string $callback
* The callback to execute.
* @param ...
* Any additional parameters required by the $callback.
*/
function webform_localization_component_invoke($type, $callback) {
$args = func_get_args();
$type = array_shift($args);
$callback = array_shift($args);
$function = '_webform_localization_' . $callback . '_' . $type;
webform_localization_component_include($type);
if (function_exists($function)) {
return call_user_func_array($function, $args);
}
}
/**
* Implements hook_webform_component_insert().
*/
function webform_localization_webform_component_insert($component) {
// Gets webform localization options that match this node ID.
$wl_options = webform_localization_get_config($component['nid']);
// Create translation source for i18n_string for all the translatable
// properties.
if ($wl_options['expose_strings']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
webform_localization_component_update_translation_strings($component);
$component['extra'] = serialize($component['extra']);
drupal_write_record('webform_component', $component, array(
'nid',
'cid',
));
}
if ($wl_options['sync_components'] && _webform_localization_sync()) {
if (!module_exists('translation')) {
// Enable the translation module as it is needed for localization sync.
module_enable('translation');
}
// Turn Off Sync.
_webform_localization_sync(FALSE);
// Get all versions of the node.
$node = node_load($component['nid']);
$translations = translation_node_get_translations($node->tnid);
if ($translations) {
unset($translations[$node->language]);
foreach ($translations as $trans_c) {
$new_component = $component;
$new_component['nid'] = $trans_c->nid;
webform_component_insert($new_component);
}
}
// Turn On Sync.
_webform_localization_sync(TRUE);
}
}
/**
* Implements hook_webform_component_presave().
*/
function webform_localization_webform_component_presave(&$component) {
// If this is an insert, skip handling as this is handled by
// hook_webform_component_insert().
if (empty($component['cid'])) {
return;
}
// Gets webform localization options that match this node ID.
$wl_options = webform_localization_get_config($component['nid']);
// Create translation source for i18n_string for all the translatable
// properties.
if ($wl_options['expose_strings']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
webform_localization_component_update_translation_strings($component);
}
if ($wl_options['sync_components'] && _webform_localization_sync()) {
if (!module_exists('translation')) {
// Enable the translation module as it is needed for localization sync.
module_enable('translation');
}
$test_node = node_load($component['nid']);
if ($test_node->tnid > 0) {
// Turn Off Sync.
_webform_localization_sync(FALSE);
module_load_include('inc', 'webform_localization', 'includes/webform_localization.component.sync');
// Get all versions of the component across all translations.
$translations = webform_localization_component_get_translations($component);
unset($translations[$component['nid']]);
// Sync the changed component with it's translations versions.
webform_localization_component_sync($component, $translations);
foreach ($translations as $trans_c) {
webform_component_update($trans_c);
}
// Turn On Sync.
_webform_localization_sync(TRUE);
}
else {
debug('configuration error: webform_localization_sync enabled AND Keep a single webform across a translation set. but no tnid, to resolve this either disable <Keep a single webform across a translation set> by unchecking this option, or enable node translation on the webform', t('debug'));
}
}
}
/**
* Implements hook_webform_component_delete().
*/
function webform_localization_webform_component_delete($component) {
// Gets webform localization options that match this node ID.
$wl_options = webform_localization_get_config($component['nid']);
if ($wl_options['expose_strings']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
webform_localization_component_delete_translation_strings($component);
}
if ($wl_options['sync_components'] && _webform_localization_sync()) {
if (!module_exists('translation')) {
// Enable the translation module as it is needed for localization sync.
module_enable('translation');
}
module_load_include('inc', 'webform_localization', 'includes/webform_localization.component.sync');
webform_localization_synchronizable_properties_delete($component);
// Turn Off Sync.
_webform_localization_sync(FALSE);
// Get all versions of the node.
$node = node_load($component['nid']);
$translations = translation_node_get_translations($node->tnid);
if ($translations) {
unset($translations[$node->language]);
foreach ($translations as $trans_c) {
$component_version = webform_localization_component_load($trans_c->nid, $component['cid']);
webform_component_delete($trans_c, $component_version);
}
}
// Turn On Sync.
_webform_localization_sync(TRUE);
}
}
/**
* A menu to_arg handler explicitly invoked by webform_menu_to_arg().
*
* If a single webform is used across all translations, use the appropriate
* node ID for webform paths.
*/
function webform_localization_webform_menu_to_arg($arg, $map, $index) {
if ($node = node_load($arg)) {
if ($nid = webform_localization_single_webform_nid($node)) {
$node = node_load($nid);
}
}
return $node ? $node->nid : $arg;
}
/**
* Find nid of node containing the 'single webform' for this translation set.
*/
function webform_localization_single_webform_nid($node) {
$cache =& drupal_static(__FUNCTION__, array());
if (!array_key_exists($node->nid, $cache)) {
// Select all webforms that match the localization configuration.
$query = db_select('webform', 'w');
$query
->innerJoin('webform_localization', 'wl', 'w.nid = wl.nid');
$query
->fields('w', array(
'nid',
));
$query
->condition('wl.single_webform', 0, '<>');
$query
->condition('wl.single_webform', $node->tnid, '=');
$query
->condition('w.nid', $node->nid, '<>');
$cache[$node->nid] = $query
->execute()
->fetchField();
}
return $cache[$node->nid];
}
/**
* Implements hook_node_view().
*/
function webform_localization_node_view($node, $view_mode) {
if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
return;
}
if ($nid = webform_localization_single_webform_nid($node)) {
// NOTE:
// Perhaps not most eficient way.. a possible alternative
// @todo rewrite the webform load and view process as a
// independent function to reuse.
$source_node = node_load($nid);
// We replace the webform with the node translation source.
$node->webform = $source_node->webform;
// This fool webform_node_view to avoid errors with drafts in between pages.
$translation_nid = $node->nid;
if (($node->webform['allow_draft'] || $node->webform['auto_save']) && $user->uid != 0) {
$node->nid = $source_node->nid;
}
// Call node view implementation to update the $node->content.
webform_node_view($node, $view_mode);
// Reset the nid if we used the drafts fix.
$node->nid = $translation_nid;
}
}
/**
* Implements hook_node_load().
*/
function webform_localization_node_load($nodes, $types) {
// Quick check to see if we need to do anything at all for these nodes.
$webform_types = webform_variable_get('webform_node_types');
if (count(array_intersect($types, $webform_types)) == 0) {
return;
}
foreach ($nodes as $nid => &$node) {
// Gets webform localization options that match this node ID.
$wl_options = webform_localization_get_config($nid);
if ($wl_options['expose_strings']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
// Translate custom strings.
webform_localization_translate_strings($node);
webform_localization_email_translate_strings($node);
}
}
}
/**
* Implements hook_webform_submission_update().
*/
function webform_localization_webform_submission_insert($node, $submission) {
// NOTE:
// We have 2 options here: to use the node language or the language of
// the page... for now make more sense use the node as language source.
$language = $node->language;
// Update language field when a submission is updated.
db_update('webform_submissions')
->fields(array(
'language' => $language,
))
->condition('sid', $submission->sid)
->execute();
}
/**
* Implements hook_webform_submission_load().
*/
function webform_localization_webform_submission_load(&$submissions) {
if (empty($submissions)) {
return;
}
$query = db_select('webform_submissions', 's');
$query
->fields('s', array(
'language',
'sid',
));
$query
->condition('s.sid', array_keys($submissions), 'IN');
$s_languages = $query
->execute()
->fetchAllAssoc('sid');
foreach ($submissions as $sid => $submission) {
$submissions[$sid]->language = $s_languages[$sid]->language;
}
}
/**
* Implements hook_webform_submission_presave().
*/
function webform_localization_webform_submission_presave(&$node, &$submission) {
if (isset($node->tnid) && $node->tnid != 0) {
$submission->serial = _webform_submission_serial_next_value($node->tnid);
}
}
/**
* Implements hook_node_delete().
*/
function webform_localization_node_delete($node) {
if (!in_array($node->type, webform_variable_get('webform_node_types')) || empty($node->webform['components'])) {
return;
}
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
// Deletion of webform and emails translatable properties.
webform_localization_delete_translate_strings($node);
// Delete webform localization configuration record.
db_delete('webform_localization')
->condition('nid', $node->nid)
->execute();
}
/**
* Implements hook_webform_component_render_alter().
*/
function webform_localization_webform_component_render_alter(&$element, $component) {
// Gets webform localization options that match this node ID.
$wl_options = webform_localization_get_config($component['nid']);
// Translate the translatable properties.
if ($wl_options['expose_strings']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
_webform_localization_translate_component($element, $component);
if (!empty($element['#attributes']['placeholder'])) {
$name = webform_localization_i18n_string_name($component['nid'], $component['cid'], '#placeholder');
$element['#attributes']['placeholder'] = i18n_string($name, $element['#attributes']['placeholder']);
}
}
}
/**
* Implements hook_webform_component_display_alter().
*/
function webform_localization_webform_component_display_alter(&$display_element, $component) {
// Gets webform localization options that match this node ID.
$wl_options = webform_localization_get_config($component['nid']);
// Translate the translatable properties.
if ($wl_options['expose_strings']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
_webform_localization_translate_component($display_element, $component);
}
}
/**
* Implements hook_webform_analysis_component_data_alter().
*/
function webform_localization_webform_analysis_component_data_alter(&$data, $node, &$component) {
// Gets webform localization options that match this node ID.
$wl_options = webform_localization_get_config($node->nid);
// Translate the translatable properties.
if ($wl_options['expose_strings']) {
// Translate component name, this is not a component spesific property.
foreach ($component['extra']['translated_strings'] as $name) {
$name_list = explode(':', $name);
// Translate component name from title property.
if ($name_list[3] == '#title') {
$component['name'] = i18n_string($name, $component['name']);
break;
}
}
// Translate data array.
$result = webform_localization_component_invoke($component['type'], 'analysis_data', $data, $node, $component);
if (!empty($result)) {
$data = $result;
}
}
}
/**
* Implements hook_webform_csv_header_alter().
*/
function webform_localization_webform_csv_header_alter(&$header, $component) {
// Gets webform localization options that match this node ID.
$wl_options = webform_localization_get_config($component['nid']);
// Translate the translatable properties.
if ($wl_options['expose_strings']) {
$result = webform_localization_component_invoke($component['type'], 'csv_header', $header, $component);
if (!empty($result)) {
$header = $result;
}
}
}
/**
* Implements hook_webform_csv_data_alter().
*/
function webform_localization_webform_csv_data_alter(&$data, $component, $submission) {
// Gets webform localization options that match this node ID.
$wl_options = webform_localization_get_config($component['nid']);
// Translate the translatable properties.
if ($wl_options['expose_strings']) {
$result = webform_localization_component_invoke($component['type'], 'csv_data', $data, $component, $submission);
if (!empty($result)) {
$data = $result;
}
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add specific localization options to Webform Configure Form.
*/
function webform_localization_form_webform_configure_form_alter(&$form, &$form_state, $form_id) {
// @see https://api.drupal.org/comment/52443#comment-52443
$enabled_languages = locale_language_list('name');
// Gets webform localization options that match this node ID.
$webform_localization_options = webform_localization_get_config($form['nid']['#value']);
if ($webform_localization_options['expose_strings'] && count($enabled_languages) > 1) {
// Avoid caching for translatable element values.
entity_get_controller('node')
->resetCache(array(
$form['nid']['#value'],
));
$form['#node'] = node_load($form['nid']['#value']);
$form['submission']['confirmation']['#default_value'] = $form['#node']->webform['confirmation'];
if ($form['submission']['redirection']['redirect']['#default_value'] == 'url') {
$form['submission']['redirection']['redirect_url']['#default_value'] = $form['#node']->webform['redirect_url'];
}
$form['advanced']['submit_text']['#default_value'] = $form['#node']->webform['submit_text'];
// Add friendly translation link.
$name = webform_localization_i18n_string_name($form['#node']->nid, 'confirmation');
$string_source = i18n_string_get_string($name);
if (isset($string_source->lid) && $string_source->lid != FALSE) {
$link = l(t('here'), 'admin/config/regional/translate/edit/' . $string_source->lid, array(
'query' => drupal_get_destination(),
));
$description = t('Click !here to translate this string.', array(
'!here' => $link,
));
}
else {
$description = t('<i>The message must be filled in before it can be translated.</i>');
}
$form['preview']['settings']['preview_message']['#default_value'] = $form['#node']->webform['preview_message'];
$name = webform_localization_i18n_string_name($form['#node']->nid, 'preview_message');
$string_source = i18n_string_get_string($name);
if (isset($string_source->lid) && $string_source->lid != FALSE) {
$link = l(t('here'), 'admin/config/regional/translate/edit/' . $string_source->lid, array(
'query' => drupal_get_destination(),
));
$description = t('Click !here to translate this string.', array(
'!here' => $link,
));
}
else {
$description = t('<i>The message must be filled in before it can be translated.</i>');
}
$form['preview']['settings']['preview_message']['#description'] .= ' ' . $description;
$form['submission']['confirmation']['#description'] .= ' ' . $description;
}
$single_webform = 0;
if ($webform_localization_options['single_webform'] > 0) {
$single_webform = 1;
}
$form['localization_by_string'] = array(
'#type' => 'fieldset',
'#title' => t('Localization by String Translation'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => -6,
'#description' => '<p>' . t('This feature implements an i18n_string integration and let you keep a single webform across several nodes in a translation set.This feature is useful when you want <em>one single webform / node acrossdifferent languages</em>.', array(
'html' => TRUE,
)) . '</p>',
);
$form['localization_by_string']['expose_strings'] = array(
'#type' => 'checkbox',
'#title' => t('Expose webform component strings suitable for translation.'),
'#default_value' => $webform_localization_options['expose_strings'],
'#description' => '<p>' . t('Use the i18n module to translate webform component strings.', array(
'html' => TRUE,
)) . '</p>',
);
$form['localization_by_string']['single_webform'] = array(
'#type' => 'checkbox',
'#title' => t('Keep a single webform across a translation set.'),
'#default_value' => $single_webform,
'#description' => '<p>' . t('When you use the i18n module to translate webform component strings, you may like to keep a single webform to attach on each node from a translation set.', array(
'html' => TRUE,
)) . '</p>',
);
$form['localization_by_sync'] = array(
'#type' => 'fieldset',
'#title' => t('Localization by Sync'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => -5,
'#description' => '<p>' . t('This feature let you have a webform per language and keeping all of them synchronized at different levels.You can choose to synchronize webform properties, submission roles, e-mail recipients and component properties.This feature is useful when you want <em>a different webform /node per language</em> but keeping some settings synchronized.', array(
'html' => TRUE,
)) . '</p>',
);
$form['localization_by_sync']['webform_properties_header'] = array(
'#type' => 'markup',
'#prefix' => t('Synchronize webform properties across node translations.'),
'#markup' => '<div class="description"><p>' . t('You can choose to synchronize specific properties of the webform.') . '</p></div>',
);
$form['localization_by_sync']['webform_properties'] = array(
'#type' => 'checkboxes',
'#options' => $webform_localization_options['webform_properties_structure'],
'#default_value' => $webform_localization_options['webform_properties'],
'#description' => '',
);
$form['localization_by_sync']['sync_components'] = array(
'#type' => 'checkbox',
'#title' => t('Synchronize webform components across node translations.'),
'#default_value' => $webform_localization_options['sync_components'],
'#description' => '<p>' . t('Copy the entire webform structure when a node is translated and synchronize changes on selected components properties.', array(
'html' => TRUE,
)) . '</p>',
);
$form['localization_by_sync']['sync_roles'] = array(
'#type' => 'checkbox',
'#title' => t('Synchronize webform submission access roles across node translations.'),
'#default_value' => $webform_localization_options['sync_roles'],
'#description' => '<p>' . t('Keep the roles that can submit a webform synchronized in a translation set.') . '</p>',
);
$form['localization_by_sync']['sync_emails'] = array(
'#type' => 'checkbox',
'#title' => t('Synchronize webform e-mail recipients across node translations.'),
'#default_value' => $webform_localization_options['sync_emails'],
'#description' => '<p>' . t('Keep the webform e-mail recipients synchronized in a translation set.') . '</p>',
);
$form['#submit'][] = '_webform_localization_webform_configure_form_submit';
if ($webform_localization_options['expose_strings']) {
// Using i18n string we need to tweak values before submit.
$submit_array = $form['#submit'];
array_unshift($submit_array, '_webform_localization_webform_configure_form_submit_i18n_tweaks');
$form['#submit'] = $submit_array;
}
}
/**
* Handle specific localization options in Webform Configure Form.
*/
function _webform_localization_webform_configure_form_submit($form, &$form_state) {
$webform_properties = $form_state['values']['webform_properties'];
foreach ($webform_properties as $key => $value) {
if (!is_string($value)) {
unset($webform_properties[$key]);
}
}
if (count($webform_properties) == 0) {
$webform_properties = '';
}
else {
$webform_properties = serialize($webform_properties);
}
if ($form_state['values']['single_webform'] > 0) {
$form_state['values']['single_webform'] = $form_state['values']['nid'];
}
$webform_localization_options = array(
'nid' => $form_state['values']['nid'],
'expose_strings' => $form_state['values']['expose_strings'],
'sync_components' => $form_state['values']['sync_components'],
'sync_roles' => $form_state['values']['sync_roles'],
'sync_emails' => $form_state['values']['sync_emails'],
'single_webform' => $form_state['values']['single_webform'],
'webform_properties' => $webform_properties,
);
$prev_options = webform_localization_get_config($form_state['values']['nid']);
if (isset($prev_options['no_persistent'])) {
drupal_write_record('webform_localization', $webform_localization_options);
}
else {
drupal_write_record('webform_localization', $webform_localization_options, array(
'nid',
));
}
module_load_include('inc', 'webform_localization', 'includes/webform_localization.sync');
webform_localization_webform_properties_sync($form_state['values']['nid']);
$webform_localization_options = webform_localization_get_config($form_state['values']['nid']);
if ($webform_localization_options['sync_roles']) {
webform_localization_roles_sync($form_state['values']['nid']);
}
if ($webform_localization_options['expose_strings']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
webform_localization_update_translation_strings($form_state['values']);
}
}
/**
* Handle translated element tweaks in Webform Configure Form.
*/
function _webform_localization_webform_configure_form_submit_i18n_tweaks($form, &$form_state) {
global $language;
$source_language = i18n_string_source_language();
if ($source_language != $language->language) {
// Webform Configure Form not in source language.
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
// Collect the form elements that are translatable.
$form_elements = array(
'confirmation',
'submit_text',
'preview_message',
);
// Add redirect url if it's not set to none or the default
// confirmation page.
if (!in_array($form_state['values']['redirect_url'], array(
'<confirmation>',
'<none>',
'<front>',
))) {
$form_elements[] = 'redirect_url';
}
foreach ($form_elements as $element) {
// Get i18n string for $element.
$name = webform_localization_i18n_string_name($form['#node']->webform['nid'], $element);
$i18n_string = i18n_string_get_string($name);
// Get submitted value for $element (the translation).
$string_translation = $element == 'confirmation' || $element == 'preview_message' ? $form_state['values'][$element]['value'] : $form_state['values'][$element];
if ($i18n_string->lid) {
// There is an i18n string source.
$string_source = $i18n_string
->get_string();
// We reset the source string value before saving the form.
if ($element == 'confirmation' || $element == 'preview_message') {
$form_state['values'][$element]['value'] = $string_source;
}
else {
$form_state['values'][$element] = $string_source;
}
}
else {
// No i18n string source found: use the current translation as the
// string source.
$string_source = $string_translation;
}
// We save the translated string using i18n string.
i18n_string_translation_update($name, $string_translation, $language->language, $string_source);
}
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add a Submit function to handle localization features.
*/
function webform_localization_form_webform_email_edit_form_alter(&$form, &$form_state, $form_id) {
$form['#submit'][] = '_webform_localization_webform_email_edit_form_submit';
}
/**
* Handle emails sync on individual email change.
*/
function _webform_localization_webform_email_edit_form_submit($form, &$form_state) {
$node = $form['#node'];
$webform_localization_options = webform_localization_get_config($node->nid);
if ($webform_localization_options['sync_emails']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.sync');
webform_localization_emails_sync($node->nid);
}
if (isset($form_state['values']['node'])) {
// Above isset avoids problem when using entity_translation.
// @see https://www.drupal.org/node/2482521
if ($webform_localization_options['expose_strings']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
webform_localization_emails_update_translation_string($form_state['values'] + array(
'node' => $node,
));
}
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add a Submit function to handle sync feature.
*/
function webform_localization_form_webform_email_delete_form_alter(&$form, &$form_state, $form_id) {
$form['#submit'][] = '_webform_localization_webform_email_delete_form_submit';
}
/**
* Handle emails localization cleanup / sync on email deletion.
*/
function _webform_localization_webform_email_delete_form_submit($form, &$form_state) {
$node = $form['node']['#value'];
$webform_localization_options = webform_localization_get_config($node->nid);
if ($webform_localization_options['sync_emails']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.sync');
webform_localization_emails_sync($node->nid);
}
if (isset($form_state['values']['node'])) {
// Above isset avoids problem when using entity_translation.
// @see https://www.drupal.org/node/2482521
if ($webform_localization_options['expose_strings']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
webform_localization_emails_delete_translation_string($form_state['values']['email']['eid'], $node->nid);
}
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add specific localization options to Webform Component Edit Form.
*/
function webform_localization_form_webform_component_edit_form_alter(&$form, &$form_state, $form_id) {
$component = $form_state['build_info']['args'][1];
if (!isset($component['cid'])) {
$component['cid'] = -1;
}
// Gets webform localization options that match this node ID.
$webform_localization_options = webform_localization_get_config($component['nid']);
if ($webform_localization_options['sync_components']) {
if (!module_exists('translation')) {
// Enable the translation module as it is needed for localization sync.
module_enable('translation');
}
module_load_include('inc', 'webform_localization', 'includes/webform_localization.component.sync');
$select_options = webform_localization_synchronizable_properties($component);
$form['localization'] = array(
'#type' => 'fieldset',
'#title' => t('Localization by Sync / Component settings'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#weight' => -4,
'#description' => t('Here you can specified what properties need to be sync for thiscomponent across the several nodes in a translation set. If youare seeing this means that you have enabled the <em>"Synchronizewebform components across node translations"</em> option in thewebform that contains this component.', array(
'html' => TRUE,
)),
);
$form['localization']['standar_properties'] = array(
'#type' => 'checkboxes',
'#title' => t('Select properties to synchronize across translations.'),
'#options' => $select_options['standar'],
'#default_value' => $select_options['standar_values'],
'#description' => t('Common properties that applies to all types of components.'),
);
$form['localization']['extra_properties'] = array(
'#type' => 'checkboxes',
'#title' => t('Select especial properties to synchronize across translations.'),
'#options' => $select_options['extra'],
'#default_value' => $select_options['extra_values'],
'#description' => t('Special properties that applies only for this type of component.'),
);
// NOTE:
// First we save the sync options to know what to do with the changes.
array_unshift($form['#submit'], '_webform_localization_webform_component_edit_form_submit');
}
}
/**
* Handle specific localization options in Webform Component Edit Form.
*/
function _webform_localization_webform_component_edit_form_submit($form, &$form_state) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.component.sync');
$options = array(
'nid' => $form_state['values']['nid'],
'cid' => $form_state['values']['cid'],
'type' => $form_state['values']['type'],
'standar_properties' => serialize($form_state['values']['localization']['standar_properties']),
'extra_properties' => serialize($form_state['values']['localization']['extra_properties']),
);
$prev_options = webform_localization_synchronizable_properties($options);
if (isset($prev_options['no_persistent'])) {
drupal_write_record('webform_component_localization', $options);
}
else {
drupal_write_record('webform_component_localization', $options, array(
'nid',
'cid',
));
}
// We reload cached configuration for this component.
webform_localization_synchronizable_properties($options, TRUE);
}
/**
* Implements hook_field_attach_prepare_translation_alter().
*/
function webform_localization_field_attach_prepare_translation_alter(&$entity, $context) {
if ($context['entity_type'] == 'node') {
if (isset($context['source_entity']->webform)) {
$webform_localization_options = webform_localization_get_config($context['source_entity']->nid);
// Copy all Webform settings over to translated versions of this node
// if the configuration match.
if ($webform_localization_options['sync_components']) {
if (!module_exists('translation')) {
// Enable the translation module as it is needed for localization sync.
module_enable('translation');
}
// NOTE:
// Perhaps could be interesting to copy only specific properties
// but for now the entire webform make more sense.
$entity->webform = $context['source_entity']->webform;
}
}
}
}
/**
* Implements hook_form_alter().
*/
function webform_localization_form_alter(&$form, &$form_state, $form_id) {
// n.b. We are not using hook_form_BASE_FORM_ID_alter(), as we need
// to interact closely with other hook_form_alter() implementations.
// @see webform_localization_module_implements_alter()
if (strpos($form_id, 'webform_client_form_') !== 0) {
return;
}
// Enhance webform's mollom support, to handle our 'single_webform' option.
if (module_exists('mollom')) {
_webform_localization_mollom_form_alter($form, $form_state, $form_id);
}
}
/**
* Interaction with mollom_form_alter() for 'single_webform' localization.
*
* If the translation source node's webform is protected by mollom, and
* uses our 'single_webform' setting, then we must also protect the other
* nodes in the translation set.
*
* This is because each node in the translation set will still have a
* unique client form_id (based on the nid, not the tnid), but mollom will
* only know about one of those form_ids.
*
* @see webform_localization_module_implements_alter()
*/
function _webform_localization_mollom_form_alter(&$form, &$form_state, $form_id) {
// Establish that the current node has a (different) translation source.
// (If this is the translation source, mollom will already know about it).
$node = $form['#node'];
if (empty($node->tnid) || $node->tnid == $node->nid) {
return;
}
// Prime the static mollom form cache (for manipulation).
mollom_form_cache();
$mollom_cache =& drupal_static('mollom_form_cache');
$protected =& $mollom_cache['protected'];
// If the source node's webform is not protected by mollom, we can bail
// out immediately.
$source_form_id = 'webform_client_form_' . $node->tnid;
if (!array_key_exists($source_form_id, $protected)) {
return;
}
// Check that the 'single_webform' option is configured for the source.
$source_wl_options = webform_localization_get_config($node->tnid);
if (empty($source_wl_options['single_webform'])) {
return;
}
// Protect this form using the settings for the single webform.
// Firstly we take care of the mollom_form_cache() static cache.
$protected[$form_id] = $protected[$source_form_id];
// Then, if necessary, we update the mollom_form_load() cache.
$mollom_form = mollom_form_load($form_id);
if (!$mollom_form) {
$source_mollom_form = mollom_form_load($source_form_id);
$mollom_form = $source_mollom_form;
$mollom_form['form_id'] = $form_id;
$cid = 'mollom:form:' . $form_id;
cache_set($cid, $mollom_form);
}
}
/**
* Implements hook_module_implements_alter().
*
* We need our hook_form_alter() to run before mollom's, so that we can
* prime and modify its cache before mollom uses it.
*
* @see webform_localization_form_alter()
* @see mollom_form_alter()
*/
function webform_localization_module_implements_alter(&$implementations, $hook) {
if ($hook != 'form_alter') {
return;
}
// If mollom isn't enabled, do nothing.
if (!module_exists('mollom')) {
return;
}
// If our code will run before mollom's, do nothing.
$pos = array_flip(array_keys($implementations));
if ($pos['webform_localization'] < $pos['mollom']) {
return;
}
// Make it so our hook implementation runs before mollom's.
$webform_localization = array(
'webform_localization' => $implementations['webform_localization'],
);
$implementations = array_diff_key($implementations, $webform_localization);
$implementations = array_merge(array_slice($implementations, 0, $pos['mollom'], TRUE), $webform_localization, array_slice($implementations, $pos['mollom'], NULL, TRUE));
}
/**
* Gets webform localization options that match a node ID.
*
* @staticvar array $webform_localization_options
* An array of webform localization options group by nid.
*
* @param int $nid
* A node Id.
* @param bool $clear_cache
* A flag to force a database reading in case that properties are cached.
*
* @return array
* Webform localization options that match the nid.
*/
function webform_localization_get_config($nid, $clear_cache = FALSE) {
static $webform_localization_options = array();
if ($clear_cache || !isset($webform_localization_options[$nid])) {
$defaults = array_keys(webform_node_defaults());
$webform_properties = array();
foreach ($defaults as $key) {
$webform_properties[$key] = $key;
}
unset($webform_properties['components']);
unset($webform_properties['roles']);
unset($webform_properties['emails']);
unset($webform_properties['record_exists']);
// If webform_uuid is being used since $component['nid'] is really a UUID
// get the nid.
if (module_exists('webform_uuid') && variable_get('webform_localization_using_uuid', FALSE)) {
$record = entity_get_id_by_uuid('node', array(
$nid,
));
$nid = !empty($record) ? array_pop($record) : $nid;
}
// Select webform localization options that match this node ID.
$options = db_select('webform_localization')
->fields('webform_localization')
->condition('nid', $nid, '=')
->execute()
->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
if (count($options) == 0) {
$webform_localization_options[$nid] = array(
'nid' => $nid,
'expose_strings' => 0,
'single_webform' => 0,
'sync_components' => 0,
'sync_roles' => 0,
'sync_emails' => 0,
'webform_properties' => array(),
'webform_properties_structure' => $webform_properties,
'no_persistent' => TRUE,
);
}
else {
$options[$nid]['webform_properties_structure'] = $webform_properties;
if (empty($options[$nid]['webform_properties'])) {
$options[$nid]['webform_properties'] = array();
}
else {
$options[$nid]['webform_properties'] = unserialize($options[$nid]['webform_properties']);
}
$webform_localization_options[$nid] = $options[$nid];
}
}
return $webform_localization_options[$nid];
}
/**
* Global switch to enable / disable syncing.
*
* This function also check whether we are synching at the moment.
*
* @return bool
* TRUE if we need to run sync operations. FALSE during syncing
* so we don't have recursion.
*/
function _webform_localization_sync($status = NULL) {
static $current = TRUE;
if (isset($status)) {
$current = $status;
}
return $current;
}
/**
* Translate webform confirmation field.
*/
function webform_localization_preprocess_webform_confirmation(&$vars) {
if (empty($vars['node']->tnid)) {
return;
}
// Select all webforms that match the localization configuration.
$query = db_select('webform', 'w');
$query
->innerJoin('webform_localization', 'wl', 'w.nid = wl.nid');
$query
->fields('w', array(
'nid',
));
$query
->condition('wl.single_webform', $vars['node']->tnid, '=');
$query
->condition('w.nid', $vars['node']->nid, '<>');
$result = $query
->execute()
->fetchField();
if ($result) {
$source_node = node_load($result);
// We replace the webform with the node translation source.
$vars['node']->webform = $source_node->webform;
}
else {
return;
}
$confirmation = check_markup($vars['node']->webform['confirmation'], $vars['node']->webform['confirmation_format'], '', TRUE);
// Strip out empty tags added by WYSIWYG editors if needed.
$vars['confirmation_message'] = drupal_strlen(trim(strip_tags($confirmation))) ? $confirmation : '';
}
/**
* Implements hook_form_webform_client_form_alter().
*/
function webform_localization_form_webform_client_form_alter(&$form, &$form_state, $form_id) {
if (!isset($form['#node']->webform['nid'])) {
return;
}
$webform_localization_options = webform_localization_get_config($form['#node']->webform['nid']);
if ($webform_localization_options['single_webform']) {
if (isset($form['details']['nid']['#value']) && $form['#node']->webform['nid'] == $form['#node']->tnid) {
// We keep current language node nid.
$form['details']['current_language_nid'] = array(
'#type' => 'value',
'#value' => $form['details']['nid']['#value'],
);
// Nid from the source webform for webform_validation.
$form['details']['nid']['#value'] = $form['#node']->webform['nid'];
}
}
}
/**
* Implements hook_modules_disabled().
*/
function webform_localization_modules_disabled($modules) {
if (in_array('uuid', $modules)) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
webform_localization_uuid_update_strings(TRUE);
}
}
/**
* Implements hook_modules_enabled().
*/
function webform_localization_modules_enabled($modules) {
if (in_array('uuid', $modules)) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
webform_localization_uuid_update_strings(FALSE);
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add related translations to translation form.
*/
function webform_localization_form_i18n_string_locale_translate_edit_form_alter(&$form, &$form_state, $form_id) {
if ($form['textgroup']['#value'] == 'webform') {
if (isset($form['context']['#markup']) && preg_match('/#title/', $form['context']['#markup'])) {
list($id, $cid, $name) = explode(':', $form['context']['#markup']);
$form['#submit'][] = 'webform_localization_i18n_string_locale_translate_edit_form_submit';
$rel_key = 'webform:' . $id . ':' . $cid . '%';
$related = db_query('SELECT lid, source, context, textgroup, location FROM {locales_source} WHERE location LIKE :key', array(
':key' => $rel_key,
));
$langs_installed = language_list();
$langs_enabled = language_list('enabled');
$langs_enabled = $langs_enabled[1];
if (isset($form['translations'])) {
foreach ($form['translations'] as $key => $value) {
if (array_key_exists($key, $langs_installed) && !array_key_exists($key, $langs_enabled)) {
unset($form['translations'][$key]);
}
}
}
$form['items'] = array(
'#type' => 'fieldset',
'#title' => t('Related strings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => 2,
);
foreach ($related as $source) {
if ($source->context != $form['context']['#markup']) {
$lid = $source->lid;
// Add original text to the top and some values for form altering.
$form['items'][$lid]['original'] = array(
'#type' => 'item',
'#title' => t('Original text'),
'#markup' => check_plain(wordwrap($source->source, 0)),
);
if (!empty($source->context)) {
$form['items'][$lid]['context'] = array(
'#type' => 'item',
'#title' => t('Context'),
'#markup' => check_plain($source->context),
);
}
$form['items'][$lid]['textgroup'] = array(
'#type' => 'value',
'#value' => $source->textgroup,
);
$form['items'][$lid]['location'] = array(
'#type' => 'value',
'#value' => $source->location,
);
$languages = $langs_enabled;
// We don't need the default language value, that value is in $source.
$omit = $source->textgroup == 'default' ? 'en' : i18n_string_source_language();
unset($languages[$omit]);
$form['items'][$lid]['translations-' . $lid] = array(
'#tree' => TRUE,
);
// Approximate the number of rows to use in the default textarea.
$rows = min(ceil(str_word_count($source->source) / 12), 10);
foreach ($languages as $langcode => $language) {
$form['items'][$lid]['translations-' . $lid][$langcode] = array(
'#type' => 'textarea',
'#title' => t($language->name),
'#rows' => $rows,
'#default_value' => '',
);
}
// Fetch translations and fill in default values in the form.
$result = db_query("SELECT DISTINCT translation, language FROM {locales_target} WHERE lid = :lid AND language <> :omit", array(
':lid' => $lid,
':omit' => $omit,
));
foreach ($result as $translation) {
$form['items'][$lid]['translations-' . $lid][$translation->language]['#default_value'] = $translation->translation;
}
}
}
}
}
}
/**
* Submit handler for translations form.
*/
function webform_localization_i18n_string_locale_translate_edit_form_submit($form, &$form_state) {
foreach ($form_state['values'] as $key => $value) {
if (preg_match("/translations-(.*)/", $key, $lid)) {
foreach ($value as $lang => $translation) {
$existing = db_query("SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language", array(
':lid' => $lid[1],
':language' => $lang,
))
->fetchField();
if (!empty($translation)) {
if (!empty($existing)) {
db_update('locales_target')
->fields(array(
'translation' => $translation,
'i18n_status' => I18N_STRING_STATUS_CURRENT,
))
->condition('lid', $lid[1])
->condition('language', $lang)
->execute();
}
else {
db_insert('locales_target')
->fields(array(
'lid' => $lid[1],
'translation' => $translation,
'language' => $lang,
))
->execute();
}
}
}
}
}
}
/**
* Implements hook_preprocess_webform_components_form().
*
* Adds a translate link to each webform component.
*/
function webform_localization_preprocess_webform_components_form(&$variables) {
$form = $variables['form'];
if (!isset($form['#node']->webform['nid'])) {
return;
}
$header = $variables['header'];
$rows = $variables['rows'];
$node = $form['#node'];
$enabled_languages = locale_language_list('name');
$webform_localization_options = webform_localization_get_config($form['#node']->webform['nid']);
$row_data = array();
if ($webform_localization_options['expose_strings'] && count($enabled_languages) > 1) {
// Change colspan of header and footer.
$footer = array_pop($rows);
$header[6]['colspan'] = 4;
$footer['data'][6]['colspan'] = 4;
// Add translate link to rows.
foreach ($rows as $key => $row) {
$row_data[$key] = $row;
if (isset($row['data-cid'])) {
$name = webform_localization_i18n_string_name($node->nid, $row['data-cid'], '#title');
$string_source = i18n_string_get_string($name);
if (isset($string_source->lid) && !empty($string_source->lid)) {
$row_data[$key]['data'][] = l(t('Translate'), 'admin/config/regional/translate/edit/' . $string_source->lid, array(
'query' => drupal_get_destination(),
));
}
else {
// Markup components do not have a translatable title, look for markup instead
$name = webform_localization_i18n_string_name($node->nid, $row['data-cid'], '#markup');
$string_source = i18n_string_get_string($name);
if (isset($string_source->lid) && !empty($string_source->lid)) {
$row_data[$key]['data'][] = l(t('Translate'), 'admin/config/regional/translate/edit/' . $string_source->lid, array(
'query' => drupal_get_destination(),
));
}
else {
// this component does not have anything translatable, so don't show a link
$row_data[$key]['data'][] = '';
}
}
}
}
$variables['rows'] = $row_data;
$variables['rows'][] = $footer;
$variables['header'] = $header;
$variables['form'] = $form;
}
}
/**
* Implements hook_js_alter().
*/
function webform_localization_js_alter(&$javascript) {
global $language;
// Only react on webform nodes.
if ($node = menu_get_object()) {
if (isset($node->type) && in_array($node->type, webform_variable_get('webform_node_types'))) {
// Only react when we are not on the source node.
if ($node->nid != $node->tnid && $node->tnid > 0) {
// Gets webform localization options that match this node ID.
$webform_localization_options = webform_localization_get_config($node->tnid);
// Only react when keep a single webform across a translation set.
if ($webform_localization_options['single_webform'] > 0) {
foreach ($javascript['settings']['data'] as &$setting) {
if (isset($setting['webform']['conditionals']['webform-client-form-' . $node->tnid])) {
$setting['webform']['conditionals']['webform-client-form-' . $node->nid] = $setting['webform']['conditionals']['webform-client-form-' . $node->tnid];
unset($setting['webform']['conditionals']['webform-client-form-' . $node->tnid]);
}
}
}
}
$do_conditionals_for_block_regular_workflow = TRUE;
if (module_exists('entity_translation')) {
//Multiple types and translation modes possible for webforms.
if (entity_translation_enabled_bundle('node', $node->type)) {
//Bundle is using entity translation, see drupal.org/node/2829894 .
$do_conditionals_for_block_regular_workflow = FALSE;
}
}
if ($do_conditionals_for_block_regular_workflow) {
// For cases when webform is displayed within a block, but not a page itself.
foreach ($javascript['settings']['data'] as &$setting) {
if (isset($setting['webform']['conditionals'])) {
foreach ($setting['webform']['conditionals'] as $k => $data) {
if (preg_match("/^webform-client-form-(.*)/", $k, $matches) && isset($matches[1])) {
$translations = translation_node_get_translations($matches[1]);
$correct_translation = isset($translations[$language->language]) ? $translations[$language->language] : FALSE;
if (!empty($correct_translation)) {
$setting['webform']['conditionals']['webform-client-form-' . $correct_translation->nid] = $setting['webform']['conditionals'][$k];
unset($setting['webform']['conditionals'][$k]);
}
}
}
}
}
}
}
}
}
/**
* Implements hook_entitycache_node_load().
*/
function webform_localization_entitycache_node_load($nodes) {
$webform_types = webform_variable_get('webform_node_types');
foreach ($nodes as $nid => &$node) {
if (in_array($node->type, $webform_types)) {
// Gets webform localization options that match this node ID.
$wl_options = webform_localization_get_config($nid);
if ($wl_options['expose_strings']) {
module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
// Translate custom strings.
webform_localization_translate_strings($node);
webform_localization_email_translate_strings($node);
}
}
}
}