updated core to 7.58 (right after the site was hacked)
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
.tmgmt-entity-sources-wrapper .form-item {
|
||||
float: left;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.tmgmt-entity-sources-wrapper #edit-search-submit {
|
||||
margin: 26px 10px 0 10px;
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
name = "Entity source plugin tests"
|
||||
description = "Support module for entity source testing."
|
||||
package = Testing
|
||||
core = 7.x
|
||||
hidden = TRUE
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Tests module for the Entity Source plugin.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info_alter().
|
||||
*/
|
||||
function tmgmt_entity_test_entity_info_alter(&$entity_info) {
|
||||
if (isset($entity_info['taxonomy_term']) && empty($entity_info['taxonomy_term']['translation'])) {
|
||||
$entity_info['taxonomy_term']['translation'] = array(
|
||||
'tmgmt_entity_test_translation' => array(
|
||||
'base path' => 'taxonomy/term/%taxonomy_term',
|
||||
'alias' => TRUE,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Entity Translation Management module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup tmgmt_source
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allows to alter $query used to list entities on specific entity type overview
|
||||
* pages.
|
||||
*
|
||||
* @see TMGMTEntityDefaultSourceUIController
|
||||
*/
|
||||
function hook_tmgmt_entity_type_list_query_alter(EntityFieldQuery $query) {
|
||||
$query->entityCondition('type', array('article', 'page'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup tmgmt_source".
|
||||
*/
|
@@ -0,0 +1,27 @@
|
||||
name = Entity Source
|
||||
description = Entity source plugin for the Translation Management system.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
|
||||
dependencies[] = tmgmt
|
||||
dependencies[] = tmgmt_field
|
||||
dependencies[] = entity
|
||||
dependencies[] = entity_translation
|
||||
|
||||
test_dependencies[] = pathauto
|
||||
test_dependencies[] = file_entity
|
||||
test_dependencies[] = entityreference
|
||||
|
||||
files[] = tmgmt_entity.source.test
|
||||
files[] = tmgmt_entity.source.none.test
|
||||
files[] = tmgmt_entity.pathauto.test
|
||||
files[] = tmgmt_entity.suggestions.test
|
||||
files[] = tmgmt_entity.plugin.inc
|
||||
files[] = tmgmt_entity.ui.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Source plugin for the Translation Management system that handles entities.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_plugin_info().
|
||||
*/
|
||||
function tmgmt_entity_tmgmt_source_plugin_info() {
|
||||
$info['entity'] = array(
|
||||
'label' => t('Entity'),
|
||||
'description' => t('Source handler for entities.'),
|
||||
'plugin controller class' => 'TMGMTEntitySourcePluginController',
|
||||
'item types' => array(),
|
||||
);
|
||||
|
||||
$entity_types = array_filter(variable_get('entity_translation_entity_types', array()));
|
||||
|
||||
foreach ($entity_types as $entity_key) {
|
||||
$entity_info = entity_get_info($entity_key);
|
||||
$info['entity']['item types'][$entity_key] = $entity_info['label'];
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_ID_alter().
|
||||
*
|
||||
* Alters comment node type select box to filter out comment types that belongs
|
||||
* to non entity translatable node types.
|
||||
*/
|
||||
function tmgmt_entity_form_tmgmt_ui_entity_source_comment_overview_form_alter(&$form, &$form_state) {
|
||||
|
||||
if (!isset($form['search_wrapper']['search']['node_type'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Change the select name to "type" as in the query submitted value will be
|
||||
// passed into node.type condition.
|
||||
$form['search_wrapper']['search']['type'] = $form['search_wrapper']['search']['node_type'];
|
||||
unset($form['search_wrapper']['search']['node_type']);
|
||||
|
||||
// Set new default value.
|
||||
$form['search_wrapper']['search']['type']['#default_value'] = isset($_GET['type']) ? $_GET['type'] : NULL;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get entity translatable bundles.
|
||||
*
|
||||
* Note that for comment entity type it will return the same as for node as
|
||||
* comment bundles have no use (i.e. in queries).
|
||||
*
|
||||
* @param string $entity_type
|
||||
* Drupal entity type.
|
||||
*
|
||||
* @return array
|
||||
* Array of key => values, where key is type and value its label.
|
||||
*/
|
||||
function tmgmt_entity_get_translatable_bundles($entity_type) {
|
||||
|
||||
// If given entity type does not have entity translations enabled, no reason
|
||||
// to continue.
|
||||
if (!in_array($entity_type, variable_get('entity_translation_entity_types', array()))) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$entity_info = entity_get_info($entity_type);
|
||||
$translatable_bundle_types = array();
|
||||
|
||||
foreach ($entity_info['bundles'] as $bundle_type => $bundle_definition) {
|
||||
|
||||
if ($entity_type == 'comment') {
|
||||
$bundle_type = str_replace('comment_node_', '', $bundle_type);
|
||||
if (variable_get('language_content_type_' . $bundle_type) == ENTITY_TRANSLATION_ENABLED) {
|
||||
$translatable_bundle_types[$bundle_type] = $bundle_definition['label'];
|
||||
}
|
||||
}
|
||||
elseif ($entity_type == 'node') {
|
||||
if (variable_get('language_content_type_' . $bundle_type) == ENTITY_TRANSLATION_ENABLED) {
|
||||
$translatable_bundle_types[$bundle_type] = $bundle_definition['label'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$translatable_bundle_types[$bundle_type] = $bundle_definition['label'];
|
||||
}
|
||||
}
|
||||
|
||||
return $translatable_bundle_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets translatable entities of a given type.
|
||||
*
|
||||
* Additionally you can specify entity property conditions, pager and limit.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* Drupal entity type.
|
||||
* @param array $property_conditions
|
||||
* Entity properties. There is no value processing so caller must make sure
|
||||
* the provided entity property exists for given entity type and its value
|
||||
* is processed.
|
||||
* @param bool $pager
|
||||
* Flag to determine if pager will be used.
|
||||
*
|
||||
* @return array
|
||||
* Array of translatable entities.
|
||||
*/
|
||||
function tmgmt_entity_get_translatable_entities($entity_type, $property_conditions = array(), $pager = FALSE) {
|
||||
|
||||
if (!in_array($entity_type, variable_get('entity_translation_entity_types', array()))) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$languages = drupal_map_assoc(array_keys(language_list()));
|
||||
|
||||
$entity_info = entity_get_info($entity_type);
|
||||
$label_key = isset($entity_info['entity keys']['label']) ? $entity_info['entity keys']['label'] : NULL;
|
||||
|
||||
$id_key = $entity_info['entity keys']['id'];
|
||||
$query = db_select($entity_info['base table'], 'e');
|
||||
$query->addTag('tmgmt_entity_get_translatable_entities');
|
||||
$query->addField('e', $id_key);
|
||||
|
||||
// Language neutral entities are not translatable. Filter them out. To do
|
||||
// that: join {entity_translation} table, but only records with source column
|
||||
// empty. The {entity_translation}.language will represent the original entity
|
||||
// language in that case.
|
||||
$source_table_alias = $query->leftJoin('entity_translation', NULL, "%alias.entity_type = :entity_type AND %alias.entity_id = e.$id_key AND %alias.source = ''", array(':entity_type' => $entity_type));
|
||||
$query->condition("$source_table_alias.language", LANGUAGE_NONE, '<>');
|
||||
|
||||
// Searching for sources with missing translation.
|
||||
if (!empty($property_conditions['target_status']) && !empty($property_conditions['target_language']) && in_array($property_conditions['target_language'], $languages)) {
|
||||
|
||||
$translation_table_alias = db_escape_field('et_' . $property_conditions['target_language']);
|
||||
$query->leftJoin('entity_translation', $translation_table_alias, "%alias.entity_type = :entity_type AND %alias.entity_id = e.$id_key AND %alias.language = :language",
|
||||
array(':entity_type' => $entity_type, ':language' => $property_conditions['target_language']));
|
||||
|
||||
// Exclude entities with having source language same as the target language
|
||||
// we search for.
|
||||
$query->condition('e.language', $property_conditions['target_language'], '<>');
|
||||
|
||||
if ($property_conditions['target_status'] == 'untranslated_or_outdated') {
|
||||
$or = db_or();
|
||||
$or->isNull("$translation_table_alias.language");
|
||||
$or->condition("$translation_table_alias.translate", 1);
|
||||
$query->condition($or);
|
||||
}
|
||||
elseif ($property_conditions['target_status'] == 'outdated') {
|
||||
$query->condition("$translation_table_alias.translate", 1);
|
||||
}
|
||||
elseif ($property_conditions['target_status'] == 'untranslated') {
|
||||
$query->isNull("$translation_table_alias.language");
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the condition so we do not try to add it again below.
|
||||
unset($property_conditions['target_language']);
|
||||
unset($property_conditions['target_status']);
|
||||
|
||||
// Searching for the source label.
|
||||
if (!empty($label_key) && isset($property_conditions[$label_key])) {
|
||||
$search_tokens = explode(' ', $property_conditions[$label_key]);
|
||||
$or = db_or();
|
||||
|
||||
foreach ($search_tokens as $search_token) {
|
||||
$search_token = trim($search_token);
|
||||
if (strlen($search_token) > 2) {
|
||||
$or->condition($label_key, "%$search_token%", 'LIKE');
|
||||
}
|
||||
}
|
||||
|
||||
if ($or->count() > 0) {
|
||||
$query->condition($or);
|
||||
}
|
||||
|
||||
unset($property_conditions[$label_key]);
|
||||
}
|
||||
|
||||
// Searching by taxonomy bundles - we need to switch to vid as the bundle key.
|
||||
if ($entity_type == 'taxonomy_term' && !empty($property_conditions['vocabulary_machine_name'])) {
|
||||
$property_name = 'vid';
|
||||
$vocabulary = taxonomy_vocabulary_machine_name_load($property_conditions['vocabulary_machine_name']);
|
||||
$property_value = $vocabulary->vid;
|
||||
$query->condition('e.' . $property_name, $property_value);
|
||||
// Remove the condition so we do not try to add it again below.
|
||||
unset($property_conditions['vocabulary_machine_name']);
|
||||
}
|
||||
// Searching by the node bundles - that applies for node entities as well as
|
||||
// comment.
|
||||
elseif (in_array($entity_type, array('comment', 'node'))) {
|
||||
$node_table_alias = 'e';
|
||||
|
||||
// For comments join node table so that we can filter based on type.
|
||||
if ($entity_type == 'comment') {
|
||||
$query->join('node', 'n', 'e.nid = n.nid');
|
||||
$node_table_alias = 'n';
|
||||
}
|
||||
|
||||
// Get translatable node types and check if it is worth to continue.
|
||||
$translatable_node_types = array_keys(tmgmt_entity_get_translatable_bundles('node'));
|
||||
if (empty($translatable_node_types)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// If we have type property add condition.
|
||||
if (isset($property_conditions['type'])) {
|
||||
$query->condition($node_table_alias . '.type', $property_conditions['type']);
|
||||
// Remove the condition so we do not try to add it again below.
|
||||
unset($property_conditions['type']);
|
||||
}
|
||||
// If not, query db only for translatable node types.
|
||||
else {
|
||||
$query->condition($node_table_alias . '.type', $translatable_node_types);
|
||||
}
|
||||
}
|
||||
|
||||
// Add remaining query conditions which are expected to be handled in a
|
||||
// generic way.
|
||||
foreach ($property_conditions as $property_name => $property_value) {
|
||||
$query->condition('e.' . $property_name, $property_value);
|
||||
}
|
||||
|
||||
if ($pager) {
|
||||
$query = $query->extend('PagerDefault')->limit(variable_get('tmgmt_source_list_limit', 20));
|
||||
}
|
||||
else {
|
||||
$query->range(0, variable_get('tmgmt_source_list_limit', 20));
|
||||
}
|
||||
|
||||
$query->orderBy($entity_info['entity keys']['id'], 'DESC');
|
||||
$entity_ids = $query->execute()->fetchCol();
|
||||
$entities = array();
|
||||
|
||||
if (!empty($entity_ids)) {
|
||||
$entities = entity_load($entity_type, $entity_ids);
|
||||
}
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_suggestions()
|
||||
*/
|
||||
function tmgmt_entity_tmgmt_source_suggestions(array $items, TMGMTJob $job) {
|
||||
$suggestions = array();
|
||||
// Get all translatable entity types.
|
||||
$entity_types = array_filter(variable_get('entity_translation_entity_types', array()));
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (($item instanceof TMGMTJobItem) && ($item->plugin == 'entity') || ($item->plugin == 'node')) {
|
||||
// Load the entity and extract the bundle name to get all fields from the
|
||||
// current entity.
|
||||
$entity = entity_load_single($item->item_type, $item->item_id);
|
||||
list(, , $bundle) = entity_extract_ids($item->item_type, $entity);
|
||||
$field_instances = field_info_instances($item->item_type, $bundle);
|
||||
|
||||
|
||||
// Loop over all fields, check if they are NOT translatable. Only if a
|
||||
// field is not translatable we may suggest a referenced entity. If so,
|
||||
// check for a supported field type (image and file currently here).
|
||||
foreach ($field_instances as $instance) {
|
||||
$field = field_info_field($instance['field_name']);
|
||||
$field_type = $field['type'];
|
||||
$field_name = $field['field_name'];
|
||||
switch ($field_type) {
|
||||
case 'file':
|
||||
case 'image':
|
||||
// 'File' (and images) must be translatable entity types.
|
||||
// Other files we not suggest here. Get all field items from the
|
||||
// current field and suggest them as translatable.
|
||||
if (isset($entity_types['file']) && ($field_items = field_get_items($item->item_type, $entity, $field_name))) {
|
||||
// Add all files as a suggestion.
|
||||
foreach ($field_items as $field_item) {
|
||||
$file_entity = entity_load_single('file', $field_item['fid']);
|
||||
|
||||
// Check if there is already a translation available for this
|
||||
// file. If so, just continue with the next file.
|
||||
$handler = entity_translation_get_handler('file', $file_entity);
|
||||
if ($handler instanceof EntityTranslationHandlerInterface) {
|
||||
$translations = $handler->getTranslations();
|
||||
if (isset($translations->data[$job->target_language])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the translation as a suggestion.
|
||||
$suggestions[] = array(
|
||||
'job_item' => tmgmt_job_item_create('entity', 'file', $file_entity->fid),
|
||||
'reason' => t('Field @label', array('@label' => $instance['label'])),
|
||||
'from_item' => $item->tjiid,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'entityreference':
|
||||
$target_type = $field['settings']['target_type'];
|
||||
// Make sure only tranlatable entity types are suggested.
|
||||
if (isset($entity_types[$target_type]) && ($field_items = field_get_items($item->item_type, $entity, $field_name))) {
|
||||
// Add all referenced entities as suggestion.
|
||||
foreach ($field_items as $field_item) {
|
||||
$ref_entity = entity_load_single($target_type, $field_item['target_id']);
|
||||
|
||||
// Check if there is already a translation available for this
|
||||
// entity. If so, just continue with the next one.
|
||||
$handler = entity_translation_get_handler($target_type, $ref_entity);
|
||||
if ($handler instanceof EntityTranslationHandlerInterface) {
|
||||
$translations = $handler->getTranslations();
|
||||
if (isset($translations->data[$job->target_language])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add suggestion.
|
||||
$suggestions[] = array(
|
||||
'job_item' => tmgmt_job_item_create('entity', $target_type, $field_item['target_id']),
|
||||
'reason' => t('Field @label', array('@label' => $instance['label'])),
|
||||
'from_item' => $item->tjiid,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $suggestions;
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Tests integration with pathauto.
|
||||
*/
|
||||
class TMGMTEntitySourcePathAutoTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Source Pathauto tests',
|
||||
'description' => 'Verifies that the correct aliases are generated for entity transations',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('entity_translation', 'pathauto'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_entity', 'entity_translation', 'pathauto'));
|
||||
$this->loginAsAdmin();
|
||||
$this->createNodeType('article', 'Article', ENTITY_TRANSLATION_ENABLED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that pathauto aliases are correctly created.
|
||||
*/
|
||||
function testAliasCreation() {
|
||||
$this->setEnvironment('de');
|
||||
|
||||
// Create a translation job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
// Create a node.
|
||||
$node = $this->createNode('article');
|
||||
// Create a job item for this node and add it to the job.
|
||||
$job->addItem('entity', 'node', $node->nid);
|
||||
|
||||
// Translate the job.
|
||||
$job->requestTranslation();
|
||||
|
||||
// Check the translated job items.
|
||||
foreach ($job->getItems() as $item) {
|
||||
$item->acceptTranslation();
|
||||
}
|
||||
|
||||
// Make sure that the correct url aliases were created.
|
||||
$aliases = db_query('SELECT * FROM {url_alias} where source = :source', array(':source' => 'node/' . $node->nid))->fetchAllAssoc('language');
|
||||
$this->assertEqual(2, count($aliases));
|
||||
$this->assertTrue(isset($aliases['en']), 'English alias created.');
|
||||
$this->assertTrue(isset($aliases['de']), 'German alias created.');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the Entity source controller.
|
||||
*/
|
||||
|
||||
class TMGMTEntitySourcePluginController extends TMGMTDefaultSourcePluginController {
|
||||
|
||||
public function getLabel(TMGMTJobItem $job_item) {
|
||||
if ($entity = entity_load_single($job_item->item_type, $job_item->item_id)) {
|
||||
return entity_label($job_item->item_type, $entity);
|
||||
}
|
||||
}
|
||||
|
||||
public function getUri(TMGMTJobItem $job_item) {
|
||||
if ($entity = entity_load_single($job_item->item_type, $job_item->item_id)) {
|
||||
return entity_uri($job_item->item_type, $entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Returns the data from the fields as a structure that can be processed by
|
||||
* the Translation Management system.
|
||||
*/
|
||||
public function getData(TMGMTJobItem $job_item) {
|
||||
$entity = entity_load_single($job_item->item_type, $job_item->item_id);
|
||||
if (!$entity) {
|
||||
throw new TMGMTException(t('Unable to load entity %type with id %id', array('%type' => $job_item->item_type, $job_item->item_id)));
|
||||
}
|
||||
if (entity_language($job_item->item_type, $entity) == LANGUAGE_NONE) {
|
||||
throw new TMGMTException(t('Entity %entity could not be translated because it is language neutral', array('%entity' => entity_label($job_item->item_type, $entity))));
|
||||
}
|
||||
return tmgmt_field_get_source_data($job_item->item_type, $entity, $job_item->getJob()->source_language, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveTranslation(TMGMTJobItem $job_item) {
|
||||
$entity = entity_load_single($job_item->item_type, $job_item->item_id);
|
||||
$job = tmgmt_job_load($job_item->tjid);
|
||||
|
||||
tmgmt_field_populate_entity($job_item->item_type, $entity, $job->target_language, $job_item->getData());
|
||||
|
||||
// Change the active language of the entity to the target language.
|
||||
$handler = entity_translation_get_handler($job_item->item_type, $entity);
|
||||
$handler->setFormLanguage($job_item->getJob()->target_language);
|
||||
|
||||
entity_save($job_item->item_type, $entity);
|
||||
|
||||
$translation = array(
|
||||
// @todo Improve hardcoded values.
|
||||
'translate' => 0,
|
||||
'status' => TRUE,
|
||||
'language' => $job_item->getJob()->target_language,
|
||||
'source' => $job_item->getJob()->source_language,
|
||||
);
|
||||
$handler->setTranslation($translation);
|
||||
$handler->saveTranslations();
|
||||
$job_item->accepted();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType(TMGMTJobItem $job_item) {
|
||||
if ($entity = entity_load_single($job_item->item_type, $job_item->item_id)) {
|
||||
$bundles = tmgmt_entity_get_translatable_bundles($job_item->item_type);
|
||||
$info = entity_get_info($job_item->item_type);
|
||||
list(, , $bundle) = entity_extract_ids($job_item->item_type, $entity);
|
||||
// Display entity type and label if we have one and the bundle isn't
|
||||
// the same as the entity type.
|
||||
if (isset($bundles[$bundle]) && $bundle != $job_item->item_type) {
|
||||
return t('@type (@bundle)', array('@type' => $info['label'], '@bundle' => $bundles[$bundle]));
|
||||
}
|
||||
// Otherwise just display the entity type label.
|
||||
elseif (isset($info['label'])) {
|
||||
return $info['label'];
|
||||
}
|
||||
return parent::getType($job_item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourceLangCode(TMGMTJobItem $job_item) {
|
||||
$entity = entity_load_single($job_item->item_type, $job_item->item_id);
|
||||
return isset($entity->translations->original) ? $entity->translations->original : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExistingLangCodes(TMGMTJobItem $job_item) {
|
||||
if ($entity = entity_load_single($job_item->item_type, $job_item->item_id)) {
|
||||
$entity_info = entity_get_info($job_item->item_type);
|
||||
if (isset($entity_info['entity keys']['translations'])){
|
||||
$translations_key = $entity_info['entity keys']['translations'];
|
||||
return array_keys($entity->{$translations_key}->data);
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
}
|
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Entity source LANGUAGE_NONE tests.
|
||||
*
|
||||
* Splitted into a separate test because of https://www.drupal.org/node/2675230.
|
||||
*/
|
||||
class TMGMTEntitySourceLanguageNoneTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
public $vocabulary;
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Source Neutral tests',
|
||||
'description' => 'Tests that LANGUAGE_NONE entities can not be translated',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('entity_translation'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_entity', 'taxonomy', 'entity_translation'));
|
||||
|
||||
// Admin user to perform settings on setup.
|
||||
$this->loginAsAdmin(array('administer entity translation'));
|
||||
|
||||
$this->vocabulary = $this->createTaxonomyVocab(strtolower($this->randomName()), $this->randomName(), array(FALSE, TRUE, TRUE, TRUE));
|
||||
|
||||
// Enable entity translations for taxonomy.
|
||||
$edit['entity_translation_entity_types[taxonomy_term]'] = 1;
|
||||
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if language neutral entities are not allowed for translation.
|
||||
*
|
||||
* That behaviour is described in the entity_translation documentation:
|
||||
* https://www.drupal.org/node/1280934
|
||||
*/
|
||||
function testLanguageNeutral() {
|
||||
$this->setEnvironment('de');
|
||||
|
||||
// Structure: array({entity-type} => array({source-langcode} => {entity}))
|
||||
$test_data = array();
|
||||
|
||||
$this->createNodeType('article', 'Article', ENTITY_TRANSLATION_ENABLED);
|
||||
$test_data['node'][LANGUAGE_NONE] = $this->createNode('article', LANGUAGE_NONE);
|
||||
$test_data['node']['en'] = $this->createNode('article', 'en');
|
||||
$test_data['node']['de'] = $this->createNode('article', 'de');
|
||||
|
||||
$test_data['taxonomy_term'][LANGUAGE_NONE] = $this->createTaxonomyTerm($this->vocabulary, LANGUAGE_NONE);
|
||||
$test_data['taxonomy_term']['en'] = $this->createTaxonomyTerm($this->vocabulary, 'en');
|
||||
$test_data['taxonomy_term']['de'] = $this->createTaxonomyTerm($this->vocabulary, 'de');
|
||||
|
||||
// Test if tmgmt_entity_get_translatable_entities() function excludes
|
||||
// language neutral entities.
|
||||
foreach ($test_data as $entity_type => $entities) {
|
||||
$translatable_entities = tmgmt_entity_get_translatable_entities($entity_type);
|
||||
foreach ($entities as $langcode => $entity) {
|
||||
list($id, , ) = entity_extract_ids($entity_type, $entity);
|
||||
if ($langcode == LANGUAGE_NONE) {
|
||||
$this->assert(!isset($translatable_entities[$id]), "Language neutral $entity_type entity does not exist in the translatable entities list.");
|
||||
}
|
||||
else {
|
||||
$this->assert(isset($translatable_entities[$id]), "$langcode $entity_type entity exists in the translatable entities list.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test if language neutral entities can't be added to a translation job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
foreach ($test_data as $entity_type => $entities) {
|
||||
foreach ($entities as $langcode => $entity) {
|
||||
list($id, , ) = entity_extract_ids($entity_type, $entity);
|
||||
try {
|
||||
$job->addItem('entity', $entity_type, $id);
|
||||
if ($langcode == LANGUAGE_NONE) {
|
||||
$this->fail("Adding of language neutral $entity_type entity to a translation job did not fail.");
|
||||
}
|
||||
else {
|
||||
$this->pass("Adding of $langcode $entity_type entity node to a translation job did not fail.");
|
||||
}
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
if ($langcode == LANGUAGE_NONE) {
|
||||
$this->pass("Adding of language neutral $entity_type entity to a translation job did fail.");
|
||||
}
|
||||
else {
|
||||
$this->fail("Adding of $langcode $entity_type entity node to a translation job did fail.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$GLOBALS['TMGMT_DEBUG'] = FALSE;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic Entity Source tests.
|
||||
*/
|
||||
class TMGMTEntitySourceTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
public $vocabulary;
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Source tests',
|
||||
'description' => 'Exporting source data from entities and saving translations back to entities.',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('entity_translation'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_entity', 'taxonomy', 'entity_translation'));
|
||||
|
||||
// Admin user to perform settings on setup.
|
||||
$this->loginAsAdmin(array('administer entity translation'));
|
||||
|
||||
$this->vocabulary = $this->createTaxonomyVocab(strtolower($this->randomName()), $this->randomName(), array(FALSE, TRUE, TRUE, TRUE));
|
||||
|
||||
// Enable entity translations for taxonomy.
|
||||
$edit['entity_translation_entity_types[taxonomy_term]'] = 1;
|
||||
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests nodes field translation.
|
||||
*/
|
||||
function testEntitySourceNode() {
|
||||
$this->setEnvironment('de');
|
||||
|
||||
$this->createNodeType('article', 'Article', ENTITY_TRANSLATION_ENABLED);
|
||||
|
||||
// Create a translation job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
// Create some nodes.
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$node = $this->createNode('article');
|
||||
// Create a job item for this node and add it to the job.
|
||||
$item = $job->addItem('entity', 'node', $node->nid);
|
||||
$this->assertEqual(t('@type (@bundle)', array('@type' => t('Node'), '@bundle' => 'Article')), $item->getSourceType());
|
||||
}
|
||||
|
||||
// Translate the job.
|
||||
$job->requestTranslation();
|
||||
|
||||
// Check the translated job items.
|
||||
foreach ($job->getItems() as $item) {
|
||||
// The source is available only for en.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
$item->acceptTranslation();
|
||||
$this->assertTrue($item->isAccepted());
|
||||
$entity = entity_load_single($item->item_type, $item->item_id);
|
||||
$data = $item->getData();
|
||||
$this->checkTranslatedData($entity, $data, 'de');
|
||||
$this->checkUntranslatedData($entity, $this->field_names['node']['article'], $data, 'de');
|
||||
// The source is now available for both en and de.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests taxonomy terms field translation.
|
||||
*/
|
||||
function testEntitySourceTerm() {
|
||||
$this->setEnvironment('de');
|
||||
|
||||
// Create the job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$term = NULL;
|
||||
|
||||
//Create some terms.
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$term = $this->createTaxonomyTerm($this->vocabulary);
|
||||
// Create the item and assign it to the job.
|
||||
$item = $job->addItem('entity', 'taxonomy_term', $term->tid);
|
||||
$this->assertEqual(t('@type (@bundle)', array('@type' => t('Taxonomy term'), '@bundle' => $this->vocabulary->name)), $item->getSourceType());
|
||||
}
|
||||
// Request the translation and accept it.
|
||||
$job->requestTranslation();
|
||||
|
||||
// Check if the fields were translated.
|
||||
foreach ($job->getItems() as $item) {
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
$item->acceptTranslation();
|
||||
$entity = entity_load_single($item->item_type, $item->item_id);
|
||||
$data = $item->getData();
|
||||
$this->checkTranslatedData($entity, $data, 'de');
|
||||
$this->checkUntranslatedData($entity, $this->field_names['taxonomy_term'][$this->vocabulary->machine_name], $data, 'de');
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
}
|
||||
|
||||
function testAddingJobItemsWithEmptySourceText() {
|
||||
$this->setEnvironment('de');
|
||||
|
||||
// Create term with empty texts.
|
||||
$empty_term = new stdClass();
|
||||
$empty_term->name = $this->randomName();
|
||||
$empty_term->description = $this->randomName();
|
||||
$empty_term->vid = $this->vocabulary->vid;
|
||||
taxonomy_term_save($empty_term);
|
||||
|
||||
// Create the job.
|
||||
$job = tmgmt_job_create('en', NULL);
|
||||
try {
|
||||
$job->addItem('entity', 'taxonomy_term', $empty_term->tid);
|
||||
$this->fail('Job item added with empty source text.');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$this->assert(empty($job->tjid), 'After adding a job item with empty source text its tjid has to be unset.');
|
||||
}
|
||||
|
||||
// Create term with populated source content.
|
||||
$populated_content_term = $this->createTaxonomyTerm($this->vocabulary);
|
||||
|
||||
// Lets reuse the last created term with populated source content.
|
||||
$job->addItem('entity', 'taxonomy_term', $populated_content_term->tid);
|
||||
$this->assert(!empty($job->tjid), 'After adding another job item with populated source text its tjid must be set.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the source is able to pull content in requested language.
|
||||
*/
|
||||
function testRequestDataForSpecificLanguage() {
|
||||
$this->setEnvironment('de');
|
||||
$this->setEnvironment('cs');
|
||||
|
||||
$this->createNodeType('article', 'Article', ENTITY_TRANSLATION_ENABLED);
|
||||
|
||||
// Create a translation job.
|
||||
$job = $this->createJob('en', 'de');
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$node = $this->createNode('article', 'cs');
|
||||
$node->body['en'][0]['value'] = 'en translation';
|
||||
node_save($node);
|
||||
$job->addItem('entity', 'node', $node->nid);
|
||||
|
||||
$data = $job->getData();
|
||||
$this->assertEqual($data[1]['body'][0]['value']['#text'], 'en translation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the data from an entity with the translated data.
|
||||
*
|
||||
* @param $tentity
|
||||
* The translated entity object.
|
||||
* @param $data
|
||||
* An array with the translated data.
|
||||
* @param $langcode
|
||||
* The code of the target language.
|
||||
*/
|
||||
function checkTranslatedData($tentity, $data, $langcode) {
|
||||
foreach (element_children($data) as $field_name) {
|
||||
foreach (element_children($data[$field_name]) as $delta) {
|
||||
foreach (element_children($data[$field_name][$delta]) as $column) {
|
||||
$column_value = $data[$field_name][$delta][$column];
|
||||
if (!empty($column_value['#translate'])) {
|
||||
$this->assertEqual($tentity->{$field_name}[$langcode][$delta][$column], $column_value['#translation']['#text'], format_string('The field %field:%delta has been populated with the proper translated data.', array('%field' => $field_name, 'delta' => $delta)));
|
||||
}
|
||||
else {
|
||||
$this->assertEqual($tentity->{$field_name}[$langcode][$delta][$column], $column_value['#text'], format_string('The field %field:%delta has been populated with the proper untranslated data.', array('%field' => $field_name, 'delta' => $delta)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the fields that should not be translated.
|
||||
*
|
||||
* @param $tentity
|
||||
* The translated entity object.
|
||||
* @param $fields
|
||||
* An array with the field names to check.
|
||||
* @param $translation
|
||||
* An array with the translated data.
|
||||
* @param $langcode
|
||||
* The code of the target language.
|
||||
*/
|
||||
function checkUntranslatedData($tentity, $fields, $data, $langcode) {
|
||||
foreach ($fields as $field_name) {
|
||||
$field_info = field_info_field($field_name);
|
||||
if (!$field_info['translatable']) {
|
||||
// Avoid some PHP warnings.
|
||||
if (isset($data[$field_name])) {
|
||||
$this->assertNull($data[$field_name]['#translation']['#text'], 'The not translatable field was not translated.');
|
||||
}
|
||||
if (isset($tentity->{$field_name}[$langcode])) {
|
||||
$this->assertNull($tentity->{$field_name}[$langcode], 'The entity has translated data in a field that is translatable.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,274 @@
|
||||
<?php
|
||||
/*
|
||||
* @file
|
||||
* Contains tests for Translation management
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic Source-Suggestions tests.
|
||||
*/
|
||||
class TMGMTSuggestionsTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Suggestions tests',
|
||||
'description' => 'Tests suggestion implementation for the entity source plugin',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array(
|
||||
'file_entity',
|
||||
'entityreference',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp(array('file_entity', 'tmgmt_entity', 'tmgmt_ui', 'entityreference', 'tmgmt_i18n_string', 'i18n_menu'));
|
||||
$this->loginAsAdmin(array('administer entity translation'));
|
||||
|
||||
$this->setEnvironment('de');
|
||||
|
||||
// Enable entity translations for nodes and comments.
|
||||
$edit = array();
|
||||
$edit['entity_translation_entity_types[node]'] = 1;
|
||||
$edit['entity_translation_entity_types[file]'] = 1;
|
||||
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a node to get suggestions from.
|
||||
*
|
||||
* Creates a node with two file fields. The first one is not translatable,
|
||||
* the second one is. Both fields got two files attached, where one has
|
||||
* translatable content (title and atl-text) and the other one not.
|
||||
*
|
||||
* @return object
|
||||
* The node which is prepared with all needed fields for the suggestions.
|
||||
*/
|
||||
protected function prepareTranslationSuggestions() {
|
||||
// Create a content type with fields.
|
||||
// Only the first field is a translatable reference.
|
||||
$type = $this->drupalCreateContentType();
|
||||
|
||||
$field1 = field_create_field(array(
|
||||
'field_name' => 'field1',
|
||||
'type' => 'file',
|
||||
'cardinality' => -1,
|
||||
));
|
||||
$field2 = field_create_field(array(
|
||||
'field_name' => 'field2',
|
||||
'type' => 'file',
|
||||
'cardinality' => -1,
|
||||
'translatable' => TRUE,
|
||||
));
|
||||
$field3 = field_create_field(array(
|
||||
'field_name' => 'field3',
|
||||
'type' => 'entityreference',
|
||||
'cardinality' => -1,
|
||||
'settings' => array(
|
||||
'target_type' => 'node',
|
||||
'handler' => 'base',
|
||||
'handler_settings' => array(
|
||||
'target_bundles' => array($type->type => $type->type),
|
||||
'sort' => array('type' => 'none'),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Create field instances on the content type.
|
||||
field_create_instance(array(
|
||||
'field_name' => $field1['field_name'],
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type->type,
|
||||
'label' => 'Field 1',
|
||||
'widget' => array('type' => 'file'),
|
||||
'settings' => array(),
|
||||
));
|
||||
field_create_instance(array(
|
||||
'field_name' => $field2['field_name'],
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type->type,
|
||||
'label' => 'Field 2',
|
||||
'widget' => array('type' => 'file'),
|
||||
'settings' => array(),
|
||||
));
|
||||
field_create_instance(array(
|
||||
'field_name' => $field3['field_name'],
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type->type,
|
||||
'label' => 'Field 3',
|
||||
'settings' => array(),
|
||||
'widget' => array('type' => 'entityreference_autocomplete_tags'),
|
||||
));
|
||||
|
||||
// Make the body field translatable from node.
|
||||
$info = field_info_field('body');
|
||||
$info['translatable'] = TRUE;
|
||||
field_update_field($info);
|
||||
|
||||
// Make the file entity fields translatable.
|
||||
$info = field_info_field('field_file_image_alt_text');
|
||||
$info['translatable'] = TRUE;
|
||||
field_update_field($info);
|
||||
|
||||
$info = field_info_field('field_file_image_title_text');
|
||||
$info['translatable'] = TRUE;
|
||||
field_update_field($info);
|
||||
|
||||
// Create and save files - two with some text and two with no text.
|
||||
list($file1, $file2, $file3, $file4) = $this->drupalGetTestFiles('image');
|
||||
$file2->field_file_image_alt_text['en'][0] = array(
|
||||
'value' => $this->randomName(),
|
||||
'type' => 'plain_text',
|
||||
);
|
||||
$file2->field_file_image_title_text['en'][0] = array(
|
||||
'value' => $this->randomName() . ' ' . $this->randomName(),
|
||||
'type' => 'plain_text',
|
||||
);
|
||||
|
||||
$file4->field_file_image_alt_text['en'][0] = array(
|
||||
'value' => $this->randomName(),
|
||||
'type' => 'plain_text',
|
||||
);
|
||||
$file4->field_file_image_title_text['en'][0] = array(
|
||||
'value' => $this->randomName() . ' ' . $this->randomName(),
|
||||
'type' => 'plain_text',
|
||||
);
|
||||
|
||||
file_save($file1);
|
||||
file_save($file2);
|
||||
file_save($file3);
|
||||
file_save($file4);
|
||||
|
||||
// Create a dummy node that will be referenced
|
||||
$referenced_node = $this->drupalCreateNode(array(
|
||||
'type' => $type->type,
|
||||
'language' => 'en',
|
||||
'body' => array(
|
||||
'en' => array(
|
||||
array('value' => $this->randomName() . ' ' . $this->randomName()),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Create a node with two translatable and two non-translatable files.
|
||||
$node = $this->drupalCreateNode(array(
|
||||
'type' => $type->type,
|
||||
'language' => 'en',
|
||||
'body' => array('en' => array(
|
||||
array(
|
||||
'value' => $this->randomName(),
|
||||
),
|
||||
)),
|
||||
$field1['field_name'] => array(LANGUAGE_NONE => array(
|
||||
array(
|
||||
'fid' => $file1->fid,
|
||||
'display' => 1,
|
||||
'description' => '',
|
||||
),
|
||||
array(
|
||||
'fid' => $file2->fid,
|
||||
'display' => 1,
|
||||
'description' => '',
|
||||
),
|
||||
)),
|
||||
$field2['field_name'] => array(LANGUAGE_NONE => array(
|
||||
array(
|
||||
'fid' => $file3->fid,
|
||||
'display' => 1,
|
||||
'description' => '',
|
||||
),
|
||||
array(
|
||||
'fid' => $file4->fid,
|
||||
'display' => 1,
|
||||
'description' => '',
|
||||
),
|
||||
)),
|
||||
$field3['field_name'] => array(LANGUAGE_NONE => array(
|
||||
array('target_id' => $referenced_node->nid),
|
||||
)),
|
||||
));
|
||||
|
||||
// Create a translatable menu.
|
||||
$config = array(
|
||||
'menu_name' => 'translatable-menu',
|
||||
'title' => 'Translatable menu',
|
||||
'description' => $this->randomName(),
|
||||
'i18n_mode' => I18N_MODE_MULTIPLE,
|
||||
);
|
||||
menu_save($config);
|
||||
$menu = menu_load($config['menu_name']);
|
||||
|
||||
// Create a menu link for the node.
|
||||
$menu_link = array(
|
||||
'link_path' => 'node/' . $node->nid,
|
||||
'link_title' => 'Menu link one',
|
||||
// i18n_menu_link::get_title() uses the title, set that too.
|
||||
'title' => 'Menu link one',
|
||||
'menu_name' => $menu['menu_name'],
|
||||
'customized' => TRUE,
|
||||
);
|
||||
$node->link = menu_link_load(menu_link_save($menu_link));
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test suggested entities from a translation job.
|
||||
*/
|
||||
public function testSuggestions() {
|
||||
// Prepare a job and a node for testing.
|
||||
$job = $this->createJob();
|
||||
$node = $this->prepareTranslationSuggestions();
|
||||
$item = $job->addItem('entity', 'node', $node->nid);
|
||||
|
||||
// Get all suggestions and clean the list.
|
||||
$suggestions = $job->getSuggestions();
|
||||
$job->cleanSuggestionsList($suggestions);
|
||||
|
||||
// Check for suggestions.
|
||||
$this->assertEqual(count($suggestions), 4, 'Found four suggestions.');
|
||||
|
||||
// Check for valid attributes on the suggestions.
|
||||
foreach ($suggestions as $suggestion) {
|
||||
switch ($suggestion['reason']) {
|
||||
case 'Field Field 1':
|
||||
$this->assertEqual($suggestion['job_item']->getWordCount(), 3, 'Three translatable words in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->plugin, 'entity', 'Got an entity as plugin in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_type, 'file', 'Got a file in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_id, $node->field1[LANGUAGE_NONE][1]['fid'], 'File id match between node and suggestion.');
|
||||
break;
|
||||
case 'Field Field 2':
|
||||
$this->assertEqual($suggestion['job_item']->getWordCount(), 3, 'Three translatable words in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->plugin, 'entity', 'Got an entity as plugin in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_type, 'file', 'Got a file in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_id, $node->field2[LANGUAGE_NONE][1]['fid'], 'File id match between node and suggestion.');
|
||||
break;
|
||||
case 'Field Field 3':
|
||||
$this->assertEqual($suggestion['job_item']->getWordCount(), 2, 'Two translatable words in the suggestion');
|
||||
$this->assertEqual($suggestion['job_item']->plugin, 'entity', 'Got an entity as plugin in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_type, 'node', 'Got a node in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_id, $node->field3[LANGUAGE_NONE][0]['target_id'], 'File id match between node and suggestion.');
|
||||
break;
|
||||
case 'Menu link Menu link one':
|
||||
$this->assertEqual($suggestion['job_item']->getWordCount(), 3, 'Three translatable words in the suggestion' . $suggestion['job_item']->plugin . $suggestion['job_item']->item_type);
|
||||
$this->assertEqual($suggestion['job_item']->plugin, 'i18n_string', 'Got a string as plugin in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_type, 'menu_link', 'Got a menu link in the suggestion.');
|
||||
$this->assertEqual($suggestion['job_item']->item_id, 'menu:item:'. $node->link['mlid'], 'Menu link id match between menu link and suggestion.');
|
||||
break;
|
||||
default:
|
||||
$this->fail('Found an invalid suggestion.');
|
||||
break;
|
||||
}
|
||||
$this->assertEqual($suggestion['from_item'], $item->tjiid);
|
||||
$job->addExistingItem($suggestion['job_item']);
|
||||
}
|
||||
|
||||
// Re-get all suggestions.
|
||||
$suggestions = $job->getSuggestions();
|
||||
$job->cleanSuggestionsList($suggestions);
|
||||
|
||||
// Check for no more suggestions.
|
||||
$this->assertEqual(count($suggestions), 0, 'Found no more suggestion.');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Abstract entity ui controller class for source plugin that provides
|
||||
* getEntity() method to retrieve list of entities of specific type. It also
|
||||
* allows to implement alter hook to alter the entity query for a specific type.
|
||||
*
|
||||
* @ingroup tmgmt_source
|
||||
*/
|
||||
abstract class TMGMTEntityDefaultSourceUIController extends TMGMTDefaultSourceUIController {
|
||||
|
||||
/**
|
||||
* Entity source list items limit.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $pagerLimit = 25;
|
||||
|
||||
/**
|
||||
* Gets entities data of provided type needed to build overview form list.
|
||||
*
|
||||
* @param $type
|
||||
* Entity type for which to get list of entities.
|
||||
* @param array $property_conditions
|
||||
* Array of key => $value pairs passed into
|
||||
* tmgmt_entity_get_translatable_entities() as the second parameter.
|
||||
*
|
||||
* @return array
|
||||
* Array of entities.
|
||||
*/
|
||||
public function getEntitiesTranslationData($type, $property_conditions = array()) {
|
||||
|
||||
$return_value = array();
|
||||
$entities = tmgmt_entity_get_translatable_entities($type, $property_conditions, TRUE);
|
||||
|
||||
$entity_info = entity_get_info($type);
|
||||
$bundles = tmgmt_entity_get_translatable_bundles($type);
|
||||
|
||||
// For retrieved entities add translation specific data.
|
||||
foreach ($entities as $entity) {
|
||||
|
||||
list($entity_id, , $bundle) = entity_extract_ids($type, $entity);
|
||||
$entity_uri = entity_uri($type, $entity);
|
||||
|
||||
// This occurs on user entity type.
|
||||
if (empty($entity_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var EntityTranslationDefaultHandler $handler
|
||||
*/
|
||||
$handler = entity_translation_get_handler($type, $entity);
|
||||
|
||||
// Get existing translations and current job items for the entity
|
||||
// to determine translation statuses
|
||||
$translations = $handler->getTranslations();
|
||||
$source_lang = entity_language($type, $entity);
|
||||
$current_job_items = tmgmt_job_item_load_latest('entity', $type, $entity_id, $source_lang);
|
||||
|
||||
// Load basic entity data.
|
||||
$return_value[$entity_id] = array(
|
||||
'entity_type' => $type,
|
||||
'entity_id' => $entity_id,
|
||||
'entity_label' => entity_label($type, $entity),
|
||||
'entity_uri' => $entity_uri['path'],
|
||||
);
|
||||
|
||||
if (count($bundles) > 1) {
|
||||
$return_value[$entity_id]['bundle'] = isset($bundles[$bundle]) ? $bundles[$bundle] : t('Unknown');
|
||||
}
|
||||
|
||||
// Load entity translation specific data.
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
|
||||
$translation_status = 'current';
|
||||
|
||||
if ($langcode == $source_lang) {
|
||||
$translation_status = 'original';
|
||||
}
|
||||
elseif (!isset($translations->data[$langcode])) {
|
||||
$translation_status = 'missing';
|
||||
}
|
||||
elseif (!empty($translations->data[$langcode]['translate'])) {
|
||||
$translation_status = 'outofdate';
|
||||
}
|
||||
|
||||
$return_value[$entity_id]['current_job_items'][$langcode] = isset($current_job_items[$langcode]) ? $current_job_items[$langcode]: NULL;
|
||||
$return_value[$entity_id]['translation_statuses'][$langcode] = $translation_status;
|
||||
}
|
||||
}
|
||||
|
||||
return $return_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds search form for entity sources overview.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*
|
||||
* @return array
|
||||
* Drupal form array.
|
||||
*/
|
||||
public function overviewSearchFormPart($form, &$form_state, $type) {
|
||||
|
||||
// Add search form specific styling.
|
||||
drupal_add_css(drupal_get_path('module', 'tmgmt_entity') . '/css/tmgmt_entity.admin.entity_source_search_form.css');
|
||||
|
||||
$form = array();
|
||||
// Add entity type value into form array so that it is available in
|
||||
// the form alter hook.
|
||||
$form_state['entity_type'] = $type;
|
||||
|
||||
$form['search_wrapper'] = array(
|
||||
'#prefix' => '<div class="tmgmt-sources-wrapper tmgmt-entity-sources-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => -15,
|
||||
);
|
||||
$form['search_wrapper']['search'] = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
$form['search_wrapper']['search_submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Search'),
|
||||
'#weight' => 10,
|
||||
);
|
||||
$form['search_wrapper']['search_cancel'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Cancel'),
|
||||
'#weight' => 11,
|
||||
);
|
||||
|
||||
$entity_info = entity_get_info($type);
|
||||
|
||||
$label_key = isset($entity_info['entity keys']['label']) ? $entity_info['entity keys']['label'] : NULL;
|
||||
|
||||
if (!empty($label_key)) {
|
||||
$form['search_wrapper']['search'][$label_key] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('@entity_name title', array('@entity_name' => $entity_info['label'])),
|
||||
'#size' => 25,
|
||||
'#default_value' => isset($_GET[$label_key]) ? $_GET[$label_key] : NULL,
|
||||
);
|
||||
}
|
||||
|
||||
$language_options = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$language_options[$langcode] = $language->name;
|
||||
}
|
||||
|
||||
$form['search_wrapper']['search']['language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Source Language'),
|
||||
'#options' => $language_options,
|
||||
'#empty_option' => t('All'),
|
||||
'#default_value' => isset($_GET['language']) ? $_GET['language'] : NULL,
|
||||
);
|
||||
|
||||
$bundle_key = $entity_info['entity keys']['bundle'];
|
||||
$bundle_options = tmgmt_entity_get_translatable_bundles($type);
|
||||
|
||||
if (count($bundle_options) > 1) {
|
||||
$form['search_wrapper']['search'][$bundle_key] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('@entity_name type', array('@entity_name' => $entity_info['label'])),
|
||||
'#options' => $bundle_options,
|
||||
'#empty_option' => t('All'),
|
||||
'#default_value' => isset($_GET[$bundle_key]) ? $_GET[$bundle_key] : NULL,
|
||||
);
|
||||
}
|
||||
// In case entity translation is not enabled for any of bundles
|
||||
// display appropriate message.
|
||||
elseif (count($bundle_options) == 0) {
|
||||
drupal_set_message(t('Entity translation is not enabled for any of existing content types. To use this functionality go to Content types administration and enable entity translation for desired content types.'), 'warning');
|
||||
unset($form['search_wrapper']);
|
||||
}
|
||||
|
||||
$options = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$options[$langcode] = $language->name;
|
||||
}
|
||||
|
||||
$form['search_wrapper']['search']['target_language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target language'),
|
||||
'#options' => $options,
|
||||
'#empty_option' => t('Any'),
|
||||
'#default_value' => isset($_GET['target_language']) ? $_GET['target_language'] : NULL,
|
||||
);
|
||||
$form['search_wrapper']['search']['target_status'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target status'),
|
||||
'#options' => array(
|
||||
'untranslated_or_outdated' => t('Untranslated or outdated'),
|
||||
'untranslated' => t('Untranslated'),
|
||||
'outdated' => t('Outdated'),
|
||||
),
|
||||
'#default_value' => isset($_GET['target_status']) ? $_GET['target_status'] : NULL,
|
||||
'#states' => array(
|
||||
'invisible' => array(
|
||||
':input[name="search[target_language]"]' => array('value' => ''),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs redirect with search params appended to the uri.
|
||||
*
|
||||
* In case of triggering element is edit-search-submit it redirects to
|
||||
* current location with added query string containing submitted search form
|
||||
* values.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*/
|
||||
public function overviewSearchFormRedirect($form, &$form_state, $type) {
|
||||
if ($form_state['triggering_element']['#id'] == 'edit-search-cancel') {
|
||||
drupal_goto($_GET['q']);
|
||||
}
|
||||
elseif ($form_state['triggering_element']['#id'] == 'edit-search-submit') {
|
||||
|
||||
$query = array();
|
||||
|
||||
foreach ($form_state['values']['search'] as $key => $value) {
|
||||
$query[$key] = $value;
|
||||
}
|
||||
|
||||
drupal_goto($_GET['q'], array('query' => $query));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_menu() {
|
||||
$items = parent::hook_menu();
|
||||
if (isset($items['admin/tmgmt/sources/entity_node'])) {
|
||||
// We assume that nodes are the most important overview if enabled, so
|
||||
// make sure they show up first.
|
||||
$items['admin/tmgmt/sources/entity_node']['weight'] = -20;
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
name = Entity Source User Interface
|
||||
description = User Interface for the entity translation source plugin.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
|
||||
dependencies[] = tmgmt_entity
|
||||
dependencies[] = tmgmt_ui
|
||||
dependencies[] = views_bulk_operations
|
||||
|
||||
files[] = tmgmt_entity_ui.test
|
||||
files[] = tmgmt_entity_ui.list.test
|
||||
files[] = tmgmt_entity_ui.ui.inc
|
||||
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
|
||||
class TMGMTEntitySourceListTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
protected $nodes = array();
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Source List tests',
|
||||
'description' => 'Tests the user interface for entity translation lists.',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_entity_ui', 'translation', 'comment', 'taxonomy'));
|
||||
$this->loginAsAdmin(array('administer entity translation'));
|
||||
|
||||
$this->setEnvironment('de');
|
||||
$this->setEnvironment('fr');
|
||||
|
||||
// Enable entity translations for nodes and comments.
|
||||
$edit = array();
|
||||
$edit['entity_translation_entity_types[comment]'] = 1;
|
||||
$edit['entity_translation_entity_types[node]'] = 1;
|
||||
$edit['entity_translation_entity_types[taxonomy_term]'] = 1;
|
||||
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
|
||||
|
||||
$this->createNodeType('article', 'Article', ENTITY_TRANSLATION_ENABLED);
|
||||
$this->createNodeType('page', 'Page', TRANSLATION_ENABLED);
|
||||
|
||||
// Create nodes that will be used during tests.
|
||||
// NOTE that the order matters as results are read by xpath based on
|
||||
// position in the list.
|
||||
$this->nodes['page']['en'][] = $this->createNode('page');
|
||||
$this->nodes['article']['de'][0] = $this->createNode('article', 'de');
|
||||
$this->nodes['article']['fr'][0] = $this->createNode('article', 'fr');
|
||||
$this->nodes['article']['en'][3] = $this->createNode('article', 'en');
|
||||
$this->nodes['article']['en'][2] = $this->createNode('article', 'en');
|
||||
$this->nodes['article']['en'][1] = $this->createNode('article', 'en');
|
||||
$this->nodes['article']['en'][0] = $this->createNode('article', 'en');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the term bundle filter works.
|
||||
*/
|
||||
function testTermBundleFilter() {
|
||||
|
||||
$vocabulary1 = entity_create('taxonomy_vocabulary', array(
|
||||
'machine_name' => 'vocab1',
|
||||
'name' => $this->randomName(),
|
||||
));
|
||||
taxonomy_vocabulary_save($vocabulary1);
|
||||
|
||||
$term1 = entity_create('taxonomy_term', array(
|
||||
'name' => $this->randomName(),
|
||||
'vid' => $vocabulary1->vid,
|
||||
));
|
||||
taxonomy_term_save($term1);
|
||||
|
||||
$vocabulary2 = (object) array(
|
||||
'machine_name' => 'vocab2',
|
||||
'name' => $this->randomName(),
|
||||
);
|
||||
taxonomy_vocabulary_save($vocabulary2);
|
||||
|
||||
$term2 = entity_create('taxonomy_term', array(
|
||||
'name' => $this->randomName(),
|
||||
'vid' => $vocabulary2->vid,
|
||||
));
|
||||
taxonomy_term_save($term2);
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_taxonomy_term');
|
||||
// Both terms should be displayed with their bundle.
|
||||
$this->assertText($term1->name);
|
||||
$this->assertText($term2->name);
|
||||
$this->assertTrue($this->xpath('//td[text()=@vocabulary]', array('@vocabulary' => $vocabulary1->name)));
|
||||
$this->assertTrue($this->xpath('//td[text()=@vocabulary]', array('@vocabulary' => $vocabulary2->name)));
|
||||
|
||||
// Limit to the first vocabulary.
|
||||
$edit = array();
|
||||
$edit['search[vocabulary_machine_name]'] = $vocabulary1->machine_name;
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
// Only term 1 should be displayed now.
|
||||
$this->assertText($term1->name);
|
||||
$this->assertNoText($term2->name);
|
||||
$this->assertTrue($this->xpath('//td[text()=@vocabulary]', array('@vocabulary' => $vocabulary1->name)));
|
||||
$this->assertFalse($this->xpath('//td[text()=@vocabulary]', array('@vocabulary' => $vocabulary2->name)));
|
||||
|
||||
}
|
||||
|
||||
function testAvailabilityOfEntityLists() {
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_comment');
|
||||
// Check if we are at comments page.
|
||||
$this->assertText(t('Comment overview (Entity)'));
|
||||
// No comments yet - empty message is expected.
|
||||
$this->assertText(t('No entities matching given criteria have been found.'));
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
// Check if we are at nodes page.
|
||||
$this->assertText(t('Node overview (Entity)'));
|
||||
// We expect article title as article node type is entity translatable.
|
||||
$this->assertText($this->nodes['article']['en'][0]->title);
|
||||
// Page node type should not be listed as it is not entity translatable.
|
||||
$this->assertNoText($this->nodes['page']['en'][0]->title);
|
||||
}
|
||||
|
||||
function testTranslationStatuses() {
|
||||
|
||||
// Test statuses: Source, Missing.
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
$langstatus_en = $this->xpath('//table[@id="tmgmt-entities-list"]/tbody/tr[1]/td[@class="langstatus-en"]');
|
||||
$langstatus_de = $this->xpath('//table[@id="tmgmt-entities-list"]/tbody/tr[1]/td[@class="langstatus-de"]');
|
||||
|
||||
$this->assertEqual($langstatus_en[0]->div['title'], t('Source language'));
|
||||
$this->assertEqual($langstatus_de[0]->div['title'], t('Not translated'));
|
||||
|
||||
// Test status: Active job item.
|
||||
$job = $this->createJob('en', 'de');
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$job->addItem('entity', 'node', $this->nodes['article']['en'][0]->nid);
|
||||
$job->requestTranslation();
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
$langstatus_de = $this->xpath('//table[@id="tmgmt-entities-list"]/tbody/tr[1]/td[@class="langstatus-de"]/a');
|
||||
|
||||
$items = $job->getItems();
|
||||
$wrapper = entity_metadata_wrapper('tmgmt_job_item', array_shift($items));
|
||||
$label = t('Active job item: @state', array('@state' => $wrapper->state->label()));
|
||||
|
||||
$this->assertEqual($langstatus_de[0]->div['title'], $label);
|
||||
|
||||
// Test status: Current
|
||||
foreach ($job->getItems() as $job_item) {
|
||||
$job_item->acceptTranslation();
|
||||
}
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
$langstatus_de = $this->xpath('//table[@id="tmgmt-entities-list"]/tbody/tr[1]/td[@class="langstatus-de"]');
|
||||
|
||||
$this->assertEqual($langstatus_de[0]->div['title'], t('Translation up to date'));
|
||||
}
|
||||
|
||||
function testTranslationSubmissions() {
|
||||
|
||||
// Simple submission.
|
||||
$nid = $this->nodes['article']['en'][0]->nid;
|
||||
$edit = array();
|
||||
$edit["items[$nid]"] = 1;
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Request translation'));
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
|
||||
// Submission of two entities of the same source language.
|
||||
$nid1 = $this->nodes['article']['en'][0]->nid;
|
||||
$nid2 = $this->nodes['article']['en'][1]->nid;
|
||||
$edit = array();
|
||||
$edit["items[$nid1]"] = 1;
|
||||
$edit["items[$nid2]"] = 1;
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Request translation'));
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
|
||||
// Submission of several entities of different source languages.
|
||||
$nid1 = $this->nodes['article']['en'][0]->nid;
|
||||
$nid2 = $this->nodes['article']['en'][1]->nid;
|
||||
$nid3 = $this->nodes['article']['en'][2]->nid;
|
||||
$nid4 = $this->nodes['article']['en'][3]->nid;
|
||||
$nid5 = $this->nodes['article']['de'][0]->nid;
|
||||
$nid6 = $this->nodes['article']['fr'][0]->nid;
|
||||
$edit = array();
|
||||
$edit["items[$nid1]"] = 1;
|
||||
$edit["items[$nid2]"] = 1;
|
||||
$edit["items[$nid3]"] = 1;
|
||||
$edit["items[$nid4]"] = 1;
|
||||
$edit["items[$nid5]"] = 1;
|
||||
$edit["items[$nid6]"] = 1;
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Request translation'));
|
||||
$this->assertText(t('@count jobs need to be checked out.', array('@count' => '3')));
|
||||
}
|
||||
|
||||
function testNodeEntityListings() {
|
||||
|
||||
// Turn off the entity translation.
|
||||
$edit = array();
|
||||
$edit['language_content_type'] = TRANSLATION_ENABLED;
|
||||
$this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
|
||||
|
||||
// Check if we have appropriate message in case there are no entity
|
||||
// translatable content types.
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
$this->assertText(t('Entity translation is not enabled for any of existing content types. To use this functionality go to Content types administration and enable entity translation for desired content types.'));
|
||||
|
||||
// Turn on the entity translation for both - article and page - to test
|
||||
// search form.
|
||||
$edit = array();
|
||||
$edit['language_content_type'] = ENTITY_TRANSLATION_ENABLED;
|
||||
$this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
|
||||
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
// Create page node after entity translation is enabled.
|
||||
$page_node_translatable = $this->createNode('page');
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/entity_node');
|
||||
// We have both listed - one of articles and page.
|
||||
$this->assertText($this->nodes['article']['en'][0]->title);
|
||||
$this->assertText($page_node_translatable->title);
|
||||
|
||||
// Try the search by content type.
|
||||
$edit = array();
|
||||
$edit['search[type]'] = 'article';
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Search'));
|
||||
// There should be article present.
|
||||
$this->assertText($this->nodes['article']['en'][0]->title);
|
||||
// The page node should not be listed.
|
||||
$this->assertNoText($page_node_translatable->title);
|
||||
|
||||
// Try cancel button - despite we do post content type search value
|
||||
// we should get nodes of botch content types.
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Cancel'));
|
||||
$this->assertText($this->nodes['article']['en'][0]->title);
|
||||
$this->assertText($page_node_translatable->title);
|
||||
}
|
||||
|
||||
function testEntitySourceListSearch() {
|
||||
|
||||
// We need a node with title composed of several words to test
|
||||
// "any words" search.
|
||||
$title_part_1 = $this->randomName('4');
|
||||
$title_part_2 = $this->randomName('4');
|
||||
$title_part_3 = $this->randomName('4');
|
||||
|
||||
$this->nodes['article']['en'][0]->title = "$title_part_1 $title_part_2 $title_part_3";
|
||||
node_save($this->nodes['article']['en'][0]);
|
||||
|
||||
// Submit partial node title and see if we have a result.
|
||||
$edit = array();
|
||||
$edit['search[title]'] = "$title_part_1 $title_part_3";
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_node', $edit, t('Search'));
|
||||
$this->assertText("$title_part_1 $title_part_2 $title_part_3", 'Searching on partial node title must return the result.');
|
||||
|
||||
// Check if there is only one result in the list.
|
||||
$search_result_rows = $this->xpath('//table[@id="tmgmt-entities-list"]/tbody/tr');
|
||||
$this->assert(count($search_result_rows) == 1, 'The search result must return only one row.');
|
||||
|
||||
// To test if other entity types work go for simple comment search.
|
||||
$comment = new stdClass();
|
||||
$comment->comment_body[LANGUAGE_NONE][0]['value'] = $this->randomName();
|
||||
$comment->subject = $this->randomName();
|
||||
// We need to associate the comment with entity translatable node object.
|
||||
$comment->nid = $this->nodes['article']['en'][0]->nid;
|
||||
// Set defaults - without these we will get Undefined property notices.
|
||||
$comment->is_anonymous = TRUE;
|
||||
$comment->cid = 0;
|
||||
$comment->pid = 0;
|
||||
$comment->uid = 0;
|
||||
// Will add further comment variables.
|
||||
$comment = comment_submit($comment);
|
||||
comment_save($comment);
|
||||
// Do search for the comment.
|
||||
$edit = array();
|
||||
$edit['search[subject]'] = $comment->subject;
|
||||
$this->drupalPost('admin/tmgmt/sources/entity_comment', $edit, t('Search'));
|
||||
$this->assertText($comment->subject, 'Searching for a comment subject.');
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Main module file for the translation management entity source plugin user
|
||||
* interface.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_page_alter().
|
||||
*/
|
||||
function tmgmt_entity_ui_page_alter(&$page) {
|
||||
if (entity_access('create', 'tmgmt_job')) {
|
||||
// Translation tabs for nodes.
|
||||
if (isset($page['content']['system_main']['entity_translation_overview'])) {
|
||||
module_load_include('inc', 'tmgmt_entity_ui', 'tmgmt_entity_ui.pages');
|
||||
$page['content']['system_main']['entity_translation_overview'] = drupal_get_form('tmgmt_entity_ui_translate_form', $page['content']['system_main']);
|
||||
}
|
||||
// Support the context module: when context is used for placing the
|
||||
// system_main block, then block contents appear nested one level deeper
|
||||
// under another 'content' key.
|
||||
elseif (isset($page['content']['system_main']['content']['entity_translation_overview'])) {
|
||||
module_load_include('inc', 'tmgmt_entity_ui', 'tmgmt_entity_ui.pages');
|
||||
$page['content']['system_main']['content']['entity_translation_overview'] = drupal_get_form('tmgmt_entity_ui_translate_form', $page['content']['system_main']['content']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements tmgmt_entity_tmgmt_source_plugin_info_alter().
|
||||
*/
|
||||
function tmgmt_entity_ui_tmgmt_source_plugin_info_alter(&$info) {
|
||||
|
||||
// Define ui controller class to handle Drupal entities.
|
||||
$info['entity']['ui controller class'] = 'TMGMTEntitySourceUIController';
|
||||
|
||||
// Alter file and file path info so that tmgmt_entity_ui module is targeted
|
||||
// for page callback.
|
||||
$info['entity']['file'] = 'tmgmt_entity_ui.pages.inc';
|
||||
$info['entity']['file path'] = drupal_get_path('module', 'tmgmt_entity_ui');
|
||||
}
|
||||
|
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides page and form callbacks for the Translation Management Tool Entity
|
||||
* Source User Interface module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Entity translation overview form.
|
||||
*/
|
||||
function tmgmt_entity_ui_translate_form($form, &$form_state, $build) {
|
||||
// Store the entity in the form state so we can easily create the job in the
|
||||
// submit handler.
|
||||
$form_state['entity_type'] = $build['#entity_type'];
|
||||
$form_state['entity'] = $build['#entity'];
|
||||
|
||||
$overview = $build['entity_translation_overview'];
|
||||
|
||||
list($id, $vid, $bundle) = entity_extract_ids($form_state['entity_type'], $form_state['entity']);
|
||||
|
||||
$form['top_actions']['#type'] = 'actions';
|
||||
$form['top_actions']['#weight'] = -10;
|
||||
tmgmt_ui_add_cart_form($form['top_actions'], $form_state, 'entity', $build['#entity_type'], $id);
|
||||
|
||||
// Inject our additional column into the header.
|
||||
array_splice($overview['#header'], -1, 0, array(t('Pending Translations')));
|
||||
// Make this a tableselect form.
|
||||
$form['languages'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $overview['#header'],
|
||||
'#options' => array(),
|
||||
);
|
||||
$languages = language_list();
|
||||
// Check if there is a job / job item that references this translation.
|
||||
$entity_language = entity_language($form_state['entity_type'], $form_state['entity']);
|
||||
$items = tmgmt_job_item_load_latest('entity', $form_state['entity_type'], $id, $entity_language);
|
||||
foreach ($languages as $langcode => $language) {
|
||||
if ($langcode == LANGUAGE_NONE) {
|
||||
// Never show language neutral on the overview.
|
||||
continue;
|
||||
}
|
||||
// Since the keys are numeric and in the same order we can shift one element
|
||||
// after the other from the original non-form rows.
|
||||
$option = array_shift($overview['#rows']);
|
||||
if ($langcode == $entity_language) {
|
||||
$additional = '<strong>' . t('Source') . '</strong>';
|
||||
// This is the source object so we disable the checkbox for this row.
|
||||
$form['languages'][$langcode] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
}
|
||||
elseif (isset($items[$langcode])) {
|
||||
$item = $items[$langcode];
|
||||
$uri = $item->uri();
|
||||
$wrapper = entity_metadata_wrapper('tmgmt_job_item', $item);
|
||||
$additional = l($wrapper->state->label(), $uri['path'], array('query' => array('destination' => current_path())));
|
||||
// Disable the checkbox for this row since there is already a translation
|
||||
// in progress that has not yet been finished. This way we make sure that
|
||||
// we don't stack multiple active translations for the same item on top
|
||||
// of each other.
|
||||
$form['languages'][$langcode] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
}
|
||||
else {
|
||||
// There is no translation job / job item for this target language.
|
||||
$additional = t('None');
|
||||
}
|
||||
// Inject the additional column into the array.
|
||||
|
||||
// The generated form structure has changed, support both an additional
|
||||
// 'data' key (that is not supported by tableselect) and the old version
|
||||
// without.
|
||||
if (isset($option['data'])) {
|
||||
array_splice($option['data'], -1, 0, array($additional));
|
||||
// Append the current option array to the form.
|
||||
$form['languages']['#options'][$langcode] = $option['data'];
|
||||
}
|
||||
else {
|
||||
array_splice($option, -1, 0, array($additional));
|
||||
// Append the current option array to the form.
|
||||
$form['languages']['#options'][$langcode] = $option;
|
||||
}
|
||||
}
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['request'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Request translation'),
|
||||
'#submit' => array('tmgmt_entity_ui_translate_form_submit'),
|
||||
'#validate' => array('tmgmt_entity_ui_translate_form_validate'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation callback for the entity translation overview form.
|
||||
*/
|
||||
function tmgmt_entity_ui_translate_form_validate($form, &$form_state) {
|
||||
$selected = array_filter($form_state['values']['languages']);
|
||||
if (empty($selected)) {
|
||||
form_set_error('languages', t('You have to select at least one language for requesting a translation.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit callback for the entity translation overview form.
|
||||
*/
|
||||
function tmgmt_entity_ui_translate_form_submit($form, &$form_state) {
|
||||
$entity = $form_state['entity'];
|
||||
$entity_type = $form_state['entity_type'];
|
||||
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
$values = $form_state['values'];
|
||||
$jobs = array();
|
||||
foreach (array_keys(array_filter($values['languages'])) as $langcode) {
|
||||
// Create the job object.
|
||||
$job = tmgmt_job_create(entity_language($entity_type, $entity), $langcode, $GLOBALS['user']->uid);
|
||||
try {
|
||||
// Add the job item.
|
||||
$job->addItem('entity', $entity_type, $id);
|
||||
// Append this job to the array of created jobs so we can redirect the user
|
||||
// to a multistep checkout form if necessary.
|
||||
$jobs[$job->tjid] = $job;
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
watchdog_exception('tmgmt', $e);
|
||||
$languages = language_list();
|
||||
$target_lang_name = $languages[$langcode]->language;
|
||||
drupal_set_message(t('Unable to add job item for target language %name. Make sure the source content is not empty.', array('%name' => $target_lang_name)), 'error');
|
||||
}
|
||||
}
|
||||
tmgmt_ui_job_checkout_and_redirect($form_state, $jobs);
|
||||
}
|
@@ -0,0 +1,328 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic Node Source tests.
|
||||
*
|
||||
*/
|
||||
class TMGMTEntitySourceUITestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Entity Source UI tests',
|
||||
'description' => 'Tests the user interface for entity translation sources.',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('entity_translation'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_entity_ui', 'block', 'comment'));
|
||||
variable_set('language_content_type_page', ENTITY_TRANSLATION_ENABLED);
|
||||
variable_set('language_content_type_article', ENTITY_TRANSLATION_ENABLED);
|
||||
|
||||
$this->loginAsAdmin(array(
|
||||
'create translation jobs',
|
||||
'submit translation jobs',
|
||||
'accept translation jobs',
|
||||
'administer blocks',
|
||||
'administer entity translation',
|
||||
'toggle field translatability',
|
||||
));
|
||||
|
||||
$this->setEnvironment('de');
|
||||
$this->setEnvironment('fr');
|
||||
$this->setEnvironment('es');
|
||||
$this->setEnvironment('el');
|
||||
|
||||
$this->createNodeType('page', st('Page'), ENTITY_TRANSLATION_ENABLED);
|
||||
$this->createNodeType('article', st('Article'), ENTITY_TRANSLATION_ENABLED);
|
||||
|
||||
// Enable path locale detection.
|
||||
$edit = array(
|
||||
'language[enabled][locale-url]' => TRUE,
|
||||
'language_content[enabled][locale-interface]' => TRUE,
|
||||
);
|
||||
$this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
|
||||
|
||||
// @todo Re-enable this when switching to testing profile.
|
||||
// Enable the main page content block for hook_page_alter() to work.
|
||||
$edit = array(
|
||||
'blocks[system_main][region]' => 'content',
|
||||
);
|
||||
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testNodeTranslateTabSingleCheckout() {
|
||||
|
||||
$this->loginAsTranslator(array('translate node entities'));
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->createNode('page', 'en');
|
||||
// Create a nodes that will not be translated to test the missing
|
||||
// translation filter.
|
||||
$node_not_translated = $this->createNode('page', 'en');
|
||||
$node_german = $this->createNode('page', 'de');
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
$this->assertText($node->title);
|
||||
|
||||
// Submit.
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('German'))));
|
||||
|
||||
// Verify that the pending translation is shown.
|
||||
$this->clickLink(t('Needs review'));
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $node->title)));
|
||||
|
||||
// German node should now be listed and be clickable.
|
||||
// @todo Improve detection of the link, e.g. use xpath on the table or the
|
||||
// title module to get a better title.
|
||||
$this->clickLink('view', 1);
|
||||
$this->assertText('de_' . $node->body['en'][0]['value']);
|
||||
|
||||
// Test that the destination query argument does not break the redirect
|
||||
// and we are redirected back to the correct page.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate', array('query' => array('destination' => 'node')));
|
||||
|
||||
// Request a spanish translation.
|
||||
$edit = array(
|
||||
'languages[es]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the checkout page.
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the originally defined destination URL.
|
||||
$this->assertEqual(url('node', array('absolute' => TRUE)), $this->getUrl());
|
||||
|
||||
// Test the missing translation filter.
|
||||
$this->drupalGet('admin/tmgmt/sources');
|
||||
$this->assertText($node->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
$this->drupalPost(NULL, array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'untranslated',
|
||||
), t('Search'));
|
||||
$this->assertNoText($node->title);
|
||||
$this->assertNoText($node_german->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
// Update the the translate flag of the translated node and test if it is
|
||||
// listed among sources with missing translation.
|
||||
db_update('entity_translation')->fields(array('translate' => 1))
|
||||
->condition('entity_type', 'node')->condition('entity_id', $node->nid)->execute();
|
||||
$this->drupalPost(NULL, array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'outdated',
|
||||
), t('Search'));
|
||||
$this->assertText($node->title);
|
||||
$this->assertNoText($node_german->title);
|
||||
$this->assertNoText($node_not_translated->title);
|
||||
|
||||
$this->drupalPost(NULL, array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'untranslated_or_outdated',
|
||||
), t('Search'));
|
||||
$this->assertText($node->title);
|
||||
$this->assertNoText($node_german->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testNodeTranslateTabMultipeCheckout() {
|
||||
// Allow auto-accept.
|
||||
$default_translator = tmgmt_translator_load('test_translator');
|
||||
$default_translator->settings = array(
|
||||
'auto_accept' => TRUE,
|
||||
);
|
||||
$default_translator->save();
|
||||
|
||||
$this->loginAsTranslator(array('translate node entities'));
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->createNode('page', 'en');
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
'languages[es]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('2 jobs need to be checked out.'));
|
||||
|
||||
// Submit all jobs.
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator and continue'));
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertNoText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('Spanish'))));
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $node->title)));
|
||||
|
||||
// Translated nodes should now be listed and be clickable.
|
||||
// @todo Use links on translate tab.
|
||||
$this->drupalGet('de/node/' . $node->nid);
|
||||
$this->assertText('de_' . $node->body['en'][0]['value']);
|
||||
|
||||
$this->drupalGet('es/node/' . $node->nid);
|
||||
$this->assertText('es_' . $node->body['en'][0]['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test translating comments.
|
||||
*
|
||||
* @todo: Disabled pending resolution of http://drupal.org/node/1760270.
|
||||
*/
|
||||
function dtestCommentTranslateTab() {
|
||||
|
||||
// Login as admin to be able to submit config page.
|
||||
$this->loginAsAdmin(array('administer entity translation'));
|
||||
// Enable comment translation.
|
||||
$edit = array(
|
||||
'entity_translation_entity_types[comment]' => TRUE
|
||||
);
|
||||
$this->drupalPost('admin/config/regional/entity_translation', $edit, t('Save configuration'));
|
||||
|
||||
// Change comment_body field to be translatable.
|
||||
$comment_body = field_info_field('comment_body');
|
||||
$comment_body['translatable'] = TRUE;
|
||||
field_update_field($comment_body);
|
||||
|
||||
// Create a user that is allowed to translate comments.
|
||||
$permissions = array('translate comment entities', 'create translation jobs', 'submit translation jobs', 'accept translation jobs', 'post comments', 'skip comment approval', 'edit own comments', 'access comments');
|
||||
$entity_translation_permissions = entity_translation_permission();
|
||||
// The new translation edit form of entity_translation requires a new
|
||||
// permission that does not yet exist in older versions. Add it
|
||||
// conditionally.
|
||||
if (isset($entity_translation_permissions['edit original values'])) {
|
||||
$permissions[] = 'edit original values';
|
||||
}
|
||||
$this->loginAsTranslator($permissions, TRUE);
|
||||
|
||||
// Create an english source term.
|
||||
$node = $this->createNode('article', 'en');
|
||||
|
||||
// Add a comment.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$edit = array(
|
||||
'subject' => $this->randomName(),
|
||||
'comment_body[en][0][value]' => $this->randomName(),
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('Your comment has been posted.'));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->clickLink('edit');
|
||||
$this->assertTrue(preg_match('|comment/(\d+)/edit$|', $this->getUrl(), $matches), 'Comment found');
|
||||
$comment = comment_load($matches[1]);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $comment->subject)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
'languages[es]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('2 jobs need to be checked out.'));
|
||||
|
||||
// Submit all jobs.
|
||||
$this->assertText($comment->subject);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator and continue'));
|
||||
$this->assertText($comment->subject);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('comment/' . $comment->cid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertNoText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $comment->subject, '@language' => t('Spanish'))));
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $comment->subject)));
|
||||
|
||||
// @todo Use links on translate tab.
|
||||
$this->drupalGet('de/comment/' . $comment->cid);
|
||||
$this->assertText('de_' . $comment->comment_body['en'][0]['value']);
|
||||
|
||||
// @todo Use links on translate tab.
|
||||
$this->drupalGet('es/node/' . $comment->cid);
|
||||
$this->assertText('es_' . $comment->comment_body['en'][0]['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the entity source specific cart functionality.
|
||||
*/
|
||||
function testCart() {
|
||||
$this->loginAsTranslator(array('translate node entities'));
|
||||
|
||||
$nodes = array();
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$nodes[$i] = $this->createNode('page');
|
||||
}
|
||||
|
||||
// Test the source overview.
|
||||
$this->drupalGet('admin/tmgmt/sources/entity');
|
||||
$this->drupalPost('admin/tmgmt/sources/entity', array(
|
||||
'items[' . $nodes[1]->nid . ']' => TRUE,
|
||||
'items[' . $nodes[2]->nid . ']' => TRUE,
|
||||
), t('Add to cart'));
|
||||
|
||||
$this->drupalGet('admin/tmgmt/cart');
|
||||
$this->assertText($nodes[1]->title);
|
||||
$this->assertText($nodes[2]->title);
|
||||
|
||||
// Test the translate tab.
|
||||
$this->drupalGet('node/' . $nodes[3]->nid . '/translate');
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a>.',
|
||||
array('@count' => 2, '@url' => url('admin/tmgmt/cart'))));
|
||||
|
||||
$this->drupalPost(NULL, array(), t('Add to cart'));
|
||||
$this->assertRaw(t('@count content source was added into the <a href="@url">cart</a>.', array('@count' => 1, '@url' => url('admin/tmgmt/cart'))));
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a> including the current item.',
|
||||
array('@count' => 3, '@url' => url('admin/tmgmt/cart'))));
|
||||
}
|
||||
}
|
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Generic entity ui controller class for source plugin.
|
||||
*
|
||||
* @ingroup tmgmt_source
|
||||
*/
|
||||
class TMGMTEntitySourceUIController extends TMGMTEntityDefaultSourceUIController {
|
||||
|
||||
/**
|
||||
* Gets overview form header.
|
||||
*
|
||||
* @return array
|
||||
* Header array definition as expected by theme_tablesort().
|
||||
*/
|
||||
public function overviewFormHeader($type) {
|
||||
$languages = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$languages['langcode-' . $langcode] = array(
|
||||
'data' => check_plain($language->name),
|
||||
);
|
||||
}
|
||||
|
||||
$entity_info = entity_get_info($type);
|
||||
|
||||
$header = array(
|
||||
'title' => array('data' => t('Title (in source language)')),
|
||||
);
|
||||
|
||||
// Show the bundle if there is more than one for this entity type.
|
||||
if (count(tmgmt_entity_get_translatable_bundles($type)) > 1) {
|
||||
$header['bundle'] = array('data' => t('@entity_name type', array('@entity_name' => $entity_info['label'])));
|
||||
}
|
||||
|
||||
$header += $languages;
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a table row for overview form.
|
||||
*
|
||||
* @param array $data
|
||||
* Data needed to build the list row.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function overviewRow($data) {
|
||||
$label = $data['entity_label'] ? $data['entity_label'] : t('@type: @id', array('@type' => $data['entity_type'], '@id' => $data['entity_id']));
|
||||
|
||||
$row = array(
|
||||
'id' => $data['entity_id'],
|
||||
'title' => l($label, $data['entity_uri']),
|
||||
);
|
||||
|
||||
if (isset($data['bundle'])) {
|
||||
$row['bundle'] = $data['bundle'];
|
||||
}
|
||||
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$row['langcode-' . $langcode] = array(
|
||||
'data' => theme('tmgmt_ui_translation_language_status_single', array(
|
||||
'translation_status' => $data['translation_statuses'][$langcode],
|
||||
'job_item' => isset($data['current_job_items'][$langcode]) ? $data['current_job_items'][$langcode] : NULL,
|
||||
)),
|
||||
'class' => array('langstatus-' . $langcode),
|
||||
);
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewForm($form, &$form_state, $type) {
|
||||
|
||||
$form += $this->overviewSearchFormPart($form, $form_state, $type);
|
||||
|
||||
$form['items'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $this->overviewFormHeader($type),
|
||||
'#empty' => t('No entities matching given criteria have been found.'),
|
||||
'#attributes' => array('id' => 'tmgmt-entities-list'),
|
||||
);
|
||||
|
||||
// Load search property params which will be passed into
|
||||
$search_property_params = array();
|
||||
$exclude_params = array('q', 'page');
|
||||
foreach ($_GET as $key => $value) {
|
||||
// Skip exclude params, and those that have empty values, as these would
|
||||
// make it into query condition instead of being ignored.
|
||||
if (in_array($key, $exclude_params) || $value === '') {
|
||||
continue;
|
||||
}
|
||||
$search_property_params[$key] = $value;
|
||||
}
|
||||
|
||||
foreach ($this->getEntitiesTranslationData($type, $search_property_params) as $data) {
|
||||
$form['items']['#options'][$data['entity_id']] = $this->overviewRow($data);
|
||||
}
|
||||
|
||||
$form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewFormValidate($form, &$form_state, $type) {
|
||||
if (!empty($form_state['values']['search']['target_language']) && $form_state['values']['search']['language'] == $form_state['values']['search']['target_language']) {
|
||||
form_set_error('search[target_language]', t('The source and target languages must not be the same.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewFormSubmit($form, &$form_state, $type) {
|
||||
|
||||
// Handle search redirect.
|
||||
$this->overviewSearchFormRedirect($form, $form_state, $type);
|
||||
|
||||
$jobs = array();
|
||||
$entities = entity_load($type, $form_state['values']['items']);
|
||||
$source_lang_registry = array();
|
||||
|
||||
// Loop through entities and create individual jobs for each source language.
|
||||
foreach ($entities as $entity) {
|
||||
/**
|
||||
* @var EntityTranslationDefaultHandler $handler
|
||||
*/
|
||||
$handler = entity_translation_get_handler($type, $entity);
|
||||
$source_lang = entity_language($type, $entity);
|
||||
list($entity_id, ,) = entity_extract_ids($type, $entity);
|
||||
|
||||
try {
|
||||
|
||||
// For given source lang no job exists yet.
|
||||
if (!isset($source_lang_registry[$source_lang])) {
|
||||
// Create new job.
|
||||
$job = tmgmt_job_create($source_lang, NULL, $GLOBALS['user']->uid);
|
||||
// Add initial job item.
|
||||
$job->addItem('entity', $type, $entity_id);
|
||||
// Add job identifier into registry
|
||||
$source_lang_registry[$source_lang] = $job->tjid;
|
||||
// Add newly created job into jobs queue.
|
||||
$jobs[$job->tjid] = $job;
|
||||
}
|
||||
// We have a job for given source lang, so just add new job item for the
|
||||
// existing job.
|
||||
else {
|
||||
$jobs[$source_lang_registry[$source_lang]]->addItem('entity', $type, $entity_id);
|
||||
}
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
watchdog_exception('tmgmt', $e);
|
||||
$entity_label = entity_label($type, $entity);
|
||||
drupal_set_message(t('Unable to add job item for entity %name: %error.', array('%name' => $entity_label, '%error' => $e->getMessage())), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// If necessary, do a redirect.
|
||||
$redirects = tmgmt_ui_job_checkout_multiple($jobs);
|
||||
if ($redirects) {
|
||||
tmgmt_ui_redirect_queue_set($redirects, current_path());
|
||||
$form_state['redirect'] = tmgmt_ui_redirect_queue_dequeue();
|
||||
|
||||
drupal_set_message(format_plural(count($redirects), t('One job needs to be checked out.'), t('@count jobs need to be checked out.')));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* API documentation for this module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Alter source field data before being saved in a translation job.
|
||||
*
|
||||
* @param array $data
|
||||
* Source field data.
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param object $entity
|
||||
* An entity object.
|
||||
* @param string $langcode
|
||||
* The language of retrieved field values.
|
||||
*/
|
||||
function hook_tmgmt_field_source_data_alter(&$data, $entity_type, $entity, $langcode) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract translatable text elements from a field.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of $entity.
|
||||
* @param $entity
|
||||
* The entity being extracted.
|
||||
* @param $field
|
||||
* The field structure.
|
||||
* @param $instance
|
||||
* The field instance.
|
||||
* @param $langcode
|
||||
* The language associated with $items.
|
||||
* @param $items
|
||||
* Array of values for this field.
|
||||
*
|
||||
* @return
|
||||
* An array of translatable text elements, keyed by the schema name of the
|
||||
* field.
|
||||
*
|
||||
* @see text_tmgmt_source_translation_structure()
|
||||
*
|
||||
* @ingroup tmgmt_source
|
||||
*/
|
||||
function hook_tmgmt_source_translation_structure($entity_type, $entity, $field, $instance, $langcode, $items) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts data on the entity of the field type owned by the module.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of $entity.
|
||||
* @param $entity
|
||||
* The entity being extracted.
|
||||
* @param $field
|
||||
* The field structure.
|
||||
* @param $instance
|
||||
* The field instance.
|
||||
* @param $langcode
|
||||
* The language associated with $items.
|
||||
* @param $data
|
||||
* Translated data array.
|
||||
* @param $use_field_translation
|
||||
* TRUE if field translation is being used.
|
||||
*
|
||||
* @see tmgmt_field_populate_entity()
|
||||
*/
|
||||
function hook_tmgmt_field_type_populate_entity($entity_type, $entity, $field, $instance, $langcode, $data, $use_field_translation) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter an entity object before populating its field with translations.
|
||||
*
|
||||
* @param array $data
|
||||
* Source field data.
|
||||
* @param object $entity
|
||||
* An entity object.
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param string $langcode
|
||||
* The language of retrieved field values.
|
||||
*/
|
||||
function hook_tmgmt_field_pre_populate_entity_alter(&$data, $entity, $entity_type, $langcode) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter an entity object after populating its field with translations.
|
||||
*
|
||||
* @param object $entity
|
||||
* An entity object.
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
* @param array $data
|
||||
* Source field data.
|
||||
* @param string $langcode
|
||||
* The language of retrieved field values.
|
||||
*/
|
||||
function hook_tmgmt_field_post_populate_entity_alter(&$entity, $entity_type, $data, $langcode) {
|
||||
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
name = Translation Management Field
|
||||
description = Implements some hooks on behalf of the core Field modules that are required in the Translation Management module.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
test_dependencies[] = field_collection
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Main module file for the Translation Management Source Field module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_translation_structure().
|
||||
*
|
||||
* This hook is implemented on behalf of the core text module.
|
||||
*/
|
||||
function text_tmgmt_source_translation_structure($entity_type, $entity, $field, $instance, $langcode, $items) {
|
||||
$structure = array();
|
||||
if (!empty($items)) {
|
||||
$structure['#label'] = check_plain($instance['label']);
|
||||
foreach ($items as $delta => $value) {
|
||||
$structure[$delta]['#label'] = t('Delta #@delta', array('@delta' => $delta));
|
||||
$structure[$delta]['value'] = array(
|
||||
'#label' => $structure['#label'],
|
||||
'#text' => $value['value'],
|
||||
'#translate' => TRUE,
|
||||
);
|
||||
// Add format.
|
||||
$structure[$delta]['format'] = array(
|
||||
'#label' => '',
|
||||
'#text' => $value['format'],
|
||||
'#translate' => FALSE,
|
||||
);
|
||||
if ($field['type'] == 'text_with_summary' && !empty($value['summary'])) {
|
||||
$structure[$delta]['summary'] = array(
|
||||
'#label' => t('Summary'),
|
||||
'#text' => $value['summary'],
|
||||
'#translate' => TRUE,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for retrieving all translatable field values from an entity.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The entity type.
|
||||
* @param $entity
|
||||
* An entity object.
|
||||
* @param $langcode
|
||||
* The language of retrieved field values.
|
||||
* @param $only_translatable
|
||||
* If TRUE, only the fields which are flagged as translatable are returned.
|
||||
* Defaults to FALSE, which is usually used for node translation, where the
|
||||
* field translatability does not matter.
|
||||
*
|
||||
* @return array
|
||||
* The structured field data for all translatable fields
|
||||
*/
|
||||
function tmgmt_field_get_source_data($entity_type, $entity, $langcode, $only_translatable = FALSE) {
|
||||
try {
|
||||
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
watchdog_exception('tmgmt field', $e);
|
||||
return array();
|
||||
}
|
||||
|
||||
$fields = array();
|
||||
foreach (field_info_instances($entity_type, $bundle) as $field_name => $instance) {
|
||||
$field = field_info_field($field_name);
|
||||
$items = field_get_items($entity_type, $entity, $field_name, $langcode);
|
||||
if ((!$only_translatable || $field['translatable']) && $items) {
|
||||
if ($data = module_invoke($field['module'], 'tmgmt_source_translation_structure', $entity_type, $entity, $field, $instance, $langcode, $items)) {
|
||||
$fields[$field_name] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drupal_alter('tmgmt_field_source_data', $fields, $entity_type, $entity, $langcode);
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates a field on an object with the provided field values.
|
||||
*
|
||||
* @param $entity_type
|
||||
* The type of $entity.
|
||||
* @param $entity
|
||||
* The object to be populated.
|
||||
* @param $langcode
|
||||
* The field language.
|
||||
* @param $data
|
||||
* An array of values.
|
||||
* @param $use_field_translation
|
||||
* TRUE if field translation is being used.
|
||||
*/
|
||||
function tmgmt_field_populate_entity($entity_type, $entity, $langcode, $data, $use_field_translation = TRUE) {
|
||||
drupal_alter('tmgmt_field_pre_populate_entity', $data, $entity, $entity_type, $langcode);
|
||||
|
||||
foreach (element_children($data) as $field_name) {
|
||||
if ($field = field_info_field($field_name)) {
|
||||
$function = $field['module'] . '_field_type_tmgmt_populate_entity';
|
||||
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
$instance = field_info_instance($entity_type, $field_name, $bundle);
|
||||
if (function_exists($function)) {
|
||||
$function($entity_type, $entity, $field, $instance, $langcode, $data, $use_field_translation);
|
||||
}
|
||||
else {
|
||||
$field_langcode = $field['translatable'] ? $langcode : LANGUAGE_NONE;
|
||||
// When not using field translation, make sure we're not storing
|
||||
// multiple languages.
|
||||
if (!$use_field_translation) {
|
||||
$entity->{$field_name} = array($field_langcode => array());
|
||||
}
|
||||
|
||||
foreach (element_children($data[$field_name]) as $delta) {
|
||||
$columns = array();
|
||||
foreach (element_children($data[$field_name][$delta]) as $column) {
|
||||
if (isset($data[$field_name][$delta][$column]['#translation']['#text'])) {
|
||||
$columns[$column] = $data[$field_name][$delta][$column]['#translation']['#text'];
|
||||
}
|
||||
// For elements which are not translatable, keep using the original
|
||||
// value.
|
||||
elseif (isset($data[$field_name][$delta][$column]['#translate']) && $data[$field_name][$delta][$column]['#translate'] == FALSE) {
|
||||
$columns[$column] = $data[$field_name][$delta][$column]['#text'];
|
||||
}
|
||||
}
|
||||
// Make sure the array_merge() gets an array as a first parameter.
|
||||
if (!isset($entity->{$field_name}[$field_langcode][$delta])) {
|
||||
$entity->{$field_name}[$field_langcode][$delta] = array();
|
||||
}
|
||||
$entity->{$field_name}[$field_langcode][$delta] = array_merge($entity->{$field_name}[$field_langcode][$delta], $columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drupal_alter('tmgmt_field_post_populate_entity', $entity, $entity_type, $data, $langcode);
|
||||
}
|
||||
|
@@ -0,0 +1,20 @@
|
||||
name = i18n String Source
|
||||
description = i18n String source plugin for the Translation Management system.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
dependencies[] = tmgmt_ui
|
||||
dependencies[] = i18n_string
|
||||
# List variable as a dependency so that it gets picked up testbot.
|
||||
# See http://drupal.org/node/1440484
|
||||
dependencies[] = i18n
|
||||
dependencies[] = variable
|
||||
files[] = tmgmt_i18n_string.plugin.inc
|
||||
files[] = tmgmt_i18n_string.test
|
||||
files[] = tmgmt_i18n_string.ui.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Source plugin for the Translation Management system that handles i18n strings.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_plugin_info().
|
||||
*/
|
||||
function tmgmt_i18n_string_tmgmt_source_plugin_info() {
|
||||
$info['i18n_string'] = array(
|
||||
'label' => t('i18n String'),
|
||||
'description' => t('Source handler for i18n strings.'),
|
||||
'plugin controller class' => 'TMGMTI18nStringSourcePluginController',
|
||||
'ui controller class' => 'TMGMTI18nStringDefaultSourceUIController',
|
||||
);
|
||||
foreach (i18n_object_info() as $object_type => $object_info) {
|
||||
// Only consider object types that have string translation information.
|
||||
if (isset($object_info['string translation'])) {
|
||||
$info['i18n_string']['item types'][$object_type] = $object_info['title'];
|
||||
}
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets i18n strings for given type and label.
|
||||
*
|
||||
* @param string $type
|
||||
* i18n object type.
|
||||
* @param string $search_label
|
||||
* Label to search for.
|
||||
* @param string $target_language
|
||||
* Target language.
|
||||
* @param string $target_status
|
||||
* Target status.
|
||||
*
|
||||
* @return array
|
||||
* List of i18n strings data.
|
||||
*/
|
||||
function tmgmt_i18n_string_get_strings($type, $search_label = NULL, $target_language = NULL, $target_status = 'untranslated_or_outdated') {
|
||||
$info = i18n_object_info($type);
|
||||
|
||||
$languages = drupal_map_assoc(array_keys(language_list()));
|
||||
$select = db_select('i18n_string', 'i18n_s');
|
||||
|
||||
$select->addTag('tmgmt_sources_search');
|
||||
$select->addMetaData('plugin', 'i18n_string');
|
||||
$select->addMetaData('type', $type);
|
||||
|
||||
$select->condition('i18n_s.textgroup', $info['string translation']['textgroup']);
|
||||
|
||||
if (!empty($target_language) && in_array($target_language, $languages)) {
|
||||
if ($target_status == 'untranslated_or_outdated') {
|
||||
$or = db_or();
|
||||
$or->isNull("lt_$target_language.language");
|
||||
$or->condition("lt_$target_language.i18n_status", I18N_STRING_STATUS_UPDATE);
|
||||
$select->condition($or);
|
||||
}
|
||||
elseif ($target_status == 'outdated') {
|
||||
$select->condition("lt_$target_language.i18n_status", I18N_STRING_STATUS_UPDATE);
|
||||
}
|
||||
elseif ($target_status == 'untranslated') {
|
||||
$select->isNull("lt_$target_language.language");
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($info['string translation']['type'])) {
|
||||
$select->condition('i18n_s.type', $info['string translation']['type']);
|
||||
}
|
||||
elseif ($type == 'field' || $type == 'field_instance') {
|
||||
// Fields and field instances share the same textgroup. Use list of bundles
|
||||
// to include/exclude field_instances.
|
||||
$bundles = array();
|
||||
foreach (entity_get_info() as $entity_info) {
|
||||
$bundles = array_merge($bundles, array_keys($entity_info['bundles']));
|
||||
}
|
||||
$select->condition('i18n_s.objectid', $bundles, $type == 'field_instance' ? 'IN' : 'NOT IN');
|
||||
}
|
||||
|
||||
$select->join('locales_source', 'ls', 'ls.lid = i18n_s.lid');
|
||||
$select->addField('ls', 'source');
|
||||
if (!empty($search_label)) {
|
||||
$select->condition('ls.source', "%$search_label%", 'LIKE');
|
||||
}
|
||||
|
||||
foreach ($languages as $langcode) {
|
||||
$langcode = str_replace('-', '', $langcode);
|
||||
$select->leftJoin('locales_target', "lt_$langcode", "i18n_s.lid = %alias.lid AND %alias.language = '$langcode'");
|
||||
$select->addField("lt_$langcode", 'language', "lang_$langcode");
|
||||
}
|
||||
$select->fields("i18n_s", array('lid', 'textgroup', 'context', 'type', 'objectid'));
|
||||
|
||||
$select->addExpression("concat(i18n_s.textgroup, ':', i18n_s.type, ':', i18n_s.objectid)", 'job_item_id');
|
||||
|
||||
$select->orderBy('i18n_s.context');
|
||||
|
||||
$select->groupBy('type');
|
||||
$select->groupBy('objectid');
|
||||
|
||||
$select = $select->extend('PagerDefault')->limit(variable_get('tmgmt_source_list_limit', 20));
|
||||
|
||||
return $select->execute()->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_ID_alter().
|
||||
*
|
||||
* Adds request translation capabilities into i18n translate tab.
|
||||
*/
|
||||
function tmgmt_i18n_string_form_i18n_string_translate_page_overview_form_alter(&$form, &$form_state) {
|
||||
$object = $form['object']['#value'];
|
||||
|
||||
// Create the id: textgroup:type:objectid.
|
||||
$id = $object->get_textgroup() . ':' . implode(':', $object->get_string_context());
|
||||
$source_language = variable_get_value('i18n_string_source_language');
|
||||
|
||||
$existing_items = tmgmt_job_item_load_latest('i18n_string', $object->get_type(), $id, $source_language);
|
||||
|
||||
$form['top_actions']['#type'] = 'actions';
|
||||
$form['top_actions']['#weight'] = -10;
|
||||
tmgmt_ui_add_cart_form($form['top_actions'], $form_state, 'i18n_string', $object->get_type(), $id);
|
||||
|
||||
$form['languages']['#type'] = 'tableselect';
|
||||
|
||||
// Append lang code so that we can use it
|
||||
foreach ($form['languages']['#rows'] as $lang => $row) {
|
||||
|
||||
if (isset($existing_items[$lang])) {
|
||||
|
||||
$states = tmgmt_job_item_states();
|
||||
$row['status'] = $states[$existing_items[$lang]->state];
|
||||
|
||||
if ($existing_items[$lang]->isNeedsReview()) {
|
||||
$row['operations'] .= ' | ' . l(t('review'), 'admin/tmgmt/items/' . $existing_items[$lang]->tjiid, array('query' => array('destination' => $_GET['q'])));
|
||||
}
|
||||
elseif ($existing_items[$lang]->isActive()) {
|
||||
$row['operations'] .= ' | ' . l(t('in progress'), 'admin/tmgmt/items/' . $existing_items[$lang]->tjiid, array('query' => array('destination' => $_GET['q'])));
|
||||
}
|
||||
}
|
||||
|
||||
$form['languages']['#options'][$id . ':' . $lang] = $row;
|
||||
|
||||
if ($lang == $source_language || isset($existing_items[$lang])) {
|
||||
$form['languages'][$id . ':' . $lang] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unset($form['languages']['#rows'], $form['languages']['#theme']);
|
||||
|
||||
$form['actions']['request_translation'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Request translation'),
|
||||
'#submit' => array('tmgmt_i18n_string_translate_form_submit'),
|
||||
'#validate' => array('tmgmt_i18n_string_translate_form_validate'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation callback for the entity translation overview form.
|
||||
*/
|
||||
function tmgmt_i18n_string_translate_form_validate($form, &$form_state) {
|
||||
$selected = array_filter($form_state['values']['languages']);
|
||||
if (empty($selected)) {
|
||||
form_set_error('languages', t('You have to select at least one language for requesting a translation.'));
|
||||
}
|
||||
}
|
||||
|
||||
function tmgmt_i18n_string_translate_form_submit($form, &$form_state) {
|
||||
|
||||
$items = array_filter($form_state['values']['languages']);
|
||||
$type = $form_state['values']['object']->get_type();
|
||||
$source_lang = variable_get_value('i18n_string_source_language');
|
||||
|
||||
$jobs = array();
|
||||
$target_lang_registry = array();
|
||||
|
||||
// Loop through entities and create individual jobs for each source language.
|
||||
foreach ($items as $item) {
|
||||
|
||||
$item_parts = explode(':', $item);
|
||||
$target_lang = array_pop($item_parts);
|
||||
$key = implode(':', $item_parts);
|
||||
|
||||
// For given source lang no job exists yet.
|
||||
if (!isset($target_lang_registry[$target_lang])) {
|
||||
// Create new job.
|
||||
$job = tmgmt_job_create($source_lang, $target_lang, $GLOBALS['user']->uid);
|
||||
// Add initial job item.
|
||||
$job->addItem('i18n_string', $type, $key);
|
||||
// Add job identifier into registry
|
||||
$target_lang_registry[$target_lang] = $job->tjid;
|
||||
// Add newly created job into jobs queue.
|
||||
$jobs[$job->tjid] = $job;
|
||||
}
|
||||
// We have a job for given source lang, so just add new job item for the
|
||||
// existing job.
|
||||
else {
|
||||
$jobs[$target_lang_registry[$target_lang]]->addItem('i18n_string', $type, $key);
|
||||
}
|
||||
}
|
||||
tmgmt_ui_job_checkout_and_redirect($form_state, $jobs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_i18n_object_info_alter().
|
||||
*/
|
||||
function tmgmt_i18n_string_i18n_object_info_alter(&$info) {
|
||||
$entity_info = entity_get_info();
|
||||
// Add a entity key to the object info if neither load callback nor entity
|
||||
// keys are set and the object is an entity_type.
|
||||
// @todo: Add this as default in EntityDefaultI18nStringController.
|
||||
foreach ($info as $name => &$object) {
|
||||
if (!isset($object['load callback']) && !isset($object['entity']) && isset($entity_info[$name])) {
|
||||
$object['entity'] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the i18n wrapper object.
|
||||
*
|
||||
* I18N objects with one or two keys are supported.
|
||||
*
|
||||
* @param string $type
|
||||
* I18n object type.
|
||||
* @param object $i18n_string
|
||||
* Object with type and objectid properties.
|
||||
*
|
||||
* @return i18n_string_object_wrapper
|
||||
*/
|
||||
function tmgmt_i18n_string_get_wrapper($type, $i18n_string) {
|
||||
$object_key = i18n_object_info($type, 'key');
|
||||
|
||||
// Special handling for i18nviews.
|
||||
if ($type == 'views') {
|
||||
// The construct method needs the full view object.
|
||||
$view = views_get_view($i18n_string->objectid);
|
||||
$wrapper = i18n_get_object($type, $i18n_string->objectid, $view);
|
||||
return $wrapper;
|
||||
}
|
||||
|
||||
// Special handling for i18n_panels.
|
||||
$panels_objects = array(
|
||||
'pane_configuration' => 'panels_pane',
|
||||
'display_configuration' => 'panels_display',
|
||||
);
|
||||
if (in_array($type, array_keys($panels_objects))) {
|
||||
ctools_include('export');
|
||||
$wrapper = FALSE;
|
||||
switch ($type) {
|
||||
case 'display_configuration':
|
||||
$object_array = ctools_export_load_object($panels_objects[$type], 'conditions', array('uuid' => $i18n_string->objectid));
|
||||
$wrapper = i18n_get_object($type, $i18n_string->objectid, $object_array[$i18n_string->objectid]);
|
||||
break;
|
||||
|
||||
case 'pane_configuration':
|
||||
$obj = db_query("SELECT * FROM {panels_pane} WHERE uuid = :uuid", array(':uuid' => $i18n_string->objectid))->fetchObject();
|
||||
if ($obj) {
|
||||
$pane = ctools_export_unpack_object($panels_objects[$type], $obj);
|
||||
$translation = i18n_panels_get_i18n_translation_object($pane);
|
||||
$translation->uuid = $pane->uuid;
|
||||
$wrapper = i18n_get_object($type, $i18n_string->objectid, $translation);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return $wrapper;
|
||||
}
|
||||
|
||||
// Special handling if the object has two keys. Assume that they
|
||||
// mean type and object id.
|
||||
if ($type == 'field') {
|
||||
// Special case for fields which expect the type to be the identifier.
|
||||
$wrapper = i18n_get_object($type, $i18n_string->type);
|
||||
return $wrapper;
|
||||
}
|
||||
elseif ($type == 'field_instance') {
|
||||
// Special case for field instances, which use the field name as type and
|
||||
// bundle as object id. We don't know the entity_type, so we loop over all
|
||||
// entity_types to search for the bundle. This will clash if different
|
||||
// entity types have bundles with the same names.
|
||||
foreach (entity_get_info() as $entity_type => $entity_info) {
|
||||
if (isset($entity_info['bundles'][$i18n_string->objectid])) {
|
||||
list($type_key, $objectid_key) = $object_key;
|
||||
$wrapper = i18n_get_object($type, array(
|
||||
$type_key => $i18n_string->type,
|
||||
$objectid_key => $i18n_string->objectid
|
||||
), field_info_instance($entity_type, $i18n_string->type, $i18n_string->objectid));
|
||||
return $wrapper;
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (count($object_key) == 2) {
|
||||
list($type_key, $objectid_key) = $object_key;
|
||||
$wrapper = i18n_get_object($type, array(
|
||||
$type_key => $i18n_string->type,
|
||||
$objectid_key => $i18n_string->objectid
|
||||
));
|
||||
return $wrapper;
|
||||
}
|
||||
else {
|
||||
// Otherwise, use the object id.
|
||||
$wrapper = i18n_get_object($type, $i18n_string->objectid);
|
||||
return $wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_suggestions()
|
||||
*/
|
||||
function tmgmt_i18n_string_tmgmt_source_suggestions(array $items, TMGMTJob $job) {
|
||||
$suggestions = array();
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (($item instanceof TMGMTJobItem) && ($item->item_type == 'node')) {
|
||||
// Load translatable menu items related to this node.
|
||||
$query = db_select('menu_links', 'ml')
|
||||
->condition('ml.link_path', 'node/' . $item->item_id)
|
||||
->fields('ml', array('mlid'));
|
||||
$query->join('menu_custom', 'mc', 'ml.menu_name = mc.menu_name AND mc.i18n_mode = ' . I18N_MODE_MULTIPLE);
|
||||
$results = $query->execute()->fetchAllAssoc('mlid');
|
||||
foreach ($results as $result) {
|
||||
$menu_link = menu_link_load($result->mlid);
|
||||
// Add suggestion.
|
||||
$suggestions[] = array(
|
||||
'job_item' => tmgmt_job_item_create('i18n_string', 'menu_link', "menu:item:{$result->mlid}"),
|
||||
'reason' => t('Menu link @title', array('@title' => $menu_link['link_title'])),
|
||||
'from_item' => $item->tjiid,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the i18n string source controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Translation plugin controller for i18n strings.
|
||||
*/
|
||||
class TMGMTI18nStringSourcePluginController extends TMGMTDefaultSourcePluginController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData(TMGMTJobItem $job_item) {
|
||||
$i18n_object = $this->getI18nObjectWrapper($job_item);
|
||||
$structure = array();
|
||||
$languages = language_list();
|
||||
|
||||
if ($i18n_object instanceof i18n_string_object_wrapper) {
|
||||
$i18n_strings = $i18n_object->get_strings();
|
||||
$source_language = $job_item->getJob()->source_language;
|
||||
foreach ($i18n_strings as $string_id => $string) {
|
||||
// If the job source language is different from the i18n source language
|
||||
// try to load an existing translation for the language and use it as
|
||||
// the source.
|
||||
if ($source_language != i18n_string_source_language()) {
|
||||
$translation = $string->get_translation($source_language);
|
||||
|
||||
if (empty($translation)) {
|
||||
throw new TMGMTException(t('Unable to load %language translation for the string %title',
|
||||
array('%language' => $languages[$source_language]->name, '%title' => $string->title)));
|
||||
}
|
||||
// If '#label' is empty theme_tmgmt_ui_translator_review_form() fails.
|
||||
$structure[$string_id] = array(
|
||||
'#label' => !empty($string->title) ? $string->title : $string->property,
|
||||
'#text' => $translation,
|
||||
'#translate' => TRUE
|
||||
);
|
||||
}
|
||||
else {
|
||||
// If '#label' is empty theme_tmgmt_ui_translator_review_form() fails.
|
||||
$structure[$string_id] = array(
|
||||
'#label' => !empty($string->title) ? $string->title : $string->property,
|
||||
'#text' => $string->string,
|
||||
'#translate' => TRUE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveTranslation(TMGMTJobItem $job_item) {
|
||||
$job = tmgmt_job_load($job_item->tjid);
|
||||
$data = array_filter(tmgmt_flatten_data($job_item->getData()), '_tmgmt_filter_data');
|
||||
foreach ($data as $i18n_string => $item) {
|
||||
if (isset($item['#translation']['#text'])) {
|
||||
i18n_string_translation_update($i18n_string, $item['#translation']['#text'], $job->target_language);
|
||||
}
|
||||
}
|
||||
|
||||
// We just saved the translation, set the state of the job item to
|
||||
// 'finished'.
|
||||
$job_item->accepted();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel(TMGMTJobItem $job_item) {
|
||||
if ($i18n_object = $this->getI18nObjectWrapper($job_item)) {
|
||||
// Get the label, default to the get_title() method, fall back to the
|
||||
// first string if that is empty.
|
||||
$title = t('Unknown');
|
||||
if ($i18n_object->get_title()) {
|
||||
$title = $i18n_object->get_title();
|
||||
}
|
||||
elseif ($strings = $i18n_object->get_strings(array('empty' => TRUE))) {
|
||||
$title = reset($strings)->get_string();
|
||||
}
|
||||
return t('@title (@id)', array('@title' => strip_tags(drupal_substr($title, 0, 64)), '@id' => $job_item->item_id));
|
||||
}
|
||||
return parent::getLabel($job_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUri(TMGMTJobItem $job_item) {
|
||||
if ($wrapper = $this->getI18nObjectWrapper($job_item)) {
|
||||
return array(
|
||||
'path' => $wrapper->get_path(),
|
||||
'options' => array(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType(TMGMTJobItem $job_item) {
|
||||
if ($label = $this->getItemTypeLabel($job_item->item_type)) {
|
||||
return $label;
|
||||
}
|
||||
return parent::getType($job_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourceLangCode(TMGMTJobItem $job_item) {
|
||||
return i18n_string_source_language();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExistingLangCodes(TMGMTJobItem $job_item) {
|
||||
$existing_lang_codes = array();
|
||||
$languages = language_list();
|
||||
|
||||
if ($object = $this->getI18nObjectWrapper($job_item)) {
|
||||
$existing_lang_codes = array_keys($languages);
|
||||
foreach ($object->load_strings() as $string) {
|
||||
foreach ($languages as $language) {
|
||||
if ($language->language == $this->getSourceLangCode($job_item)) {
|
||||
continue;
|
||||
}
|
||||
// Remove languages for which we fail to find translation.
|
||||
if (in_array($language->language, $existing_lang_codes) && !$string->get_translation($language->language)) {
|
||||
$existing_lang_codes = array_diff($existing_lang_codes, array($language->language));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $existing_lang_codes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get i18n_object_wrapper for given job item.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
*
|
||||
* @return i18n_string_object_wrapper
|
||||
*/
|
||||
protected function getI18nObjectWrapper(TMGMTJobItem $job_item) {
|
||||
list(, $type, $object_id) = explode(':', $job_item->item_id, 3);
|
||||
return tmgmt_i18n_string_get_wrapper($job_item->item_type, (object) array('type' => $type, 'objectid' => $object_id));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,496 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic i18n String Source tests.
|
||||
*/
|
||||
class TMGMTI18nStringSourceTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'i18n String Source tests',
|
||||
'description' => 'Exporting source data from i18n string and saving translations back',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('i18n_string'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_ui', 'tmgmt_i18n_string', 'taxonomy', 'i18n_taxonomy', 'i18n_block', 'i18n_field', 'list', 'i18n_menu'));
|
||||
$this->setEnvironment('de');
|
||||
$this->translator = $this->createTranslator();
|
||||
}
|
||||
|
||||
function testI18nStringSourceTaxonomy() {
|
||||
// Test translation of a vocabulary.
|
||||
/////////////////////////////////////
|
||||
$config = array(
|
||||
'name' => $this->randomName(),
|
||||
'machine_name' => 'test_vocab',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
);
|
||||
$vocabulary = entity_create('taxonomy_vocabulary', $config);
|
||||
taxonomy_vocabulary_save($vocabulary);
|
||||
|
||||
$string_object_name = "taxonomy:vocabulary:" . $vocabulary->vid;
|
||||
$source_text = $vocabulary->name;
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item1 = $job->addItem('i18n_string', 'taxonomy_vocabulary', $string_object_name);
|
||||
$this->assertEqual(t('Vocabulary'), $item1->getSourceType());
|
||||
$job->requestTranslation();
|
||||
|
||||
foreach ($job->getItems() as $item) {
|
||||
/* @var $item TMGMTJobItem */
|
||||
$item->acceptTranslation();
|
||||
}
|
||||
|
||||
// Check the structure of the imported data.
|
||||
$this->assertEqual($item1->item_id, $string_object_name, 'i18n Strings object correctly saved');
|
||||
|
||||
// Check string translation.
|
||||
$this->assertEqual(i18n_string_translate('taxonomy:vocabulary:' . $vocabulary->vid . ':name', $source_text, array('langcode' => $job->target_language)), $job->target_language . '_' . $source_text);
|
||||
|
||||
// Test translation of a taxonomy term.
|
||||
/////////////////////////////////////
|
||||
$term = entity_create('taxonomy_term', array(
|
||||
'vid' => $vocabulary->vid,
|
||||
'name' => $this->randomName(),
|
||||
'description' => $this->randomName(),
|
||||
));
|
||||
taxonomy_term_save($term);
|
||||
|
||||
$string_object_name = "taxonomy:term:" . $term->tid;
|
||||
$source_text_name = $term->name;
|
||||
$source_text_description = $term->description;
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item1 = $job->addItem('i18n_string', 'taxonomy_term', $string_object_name);
|
||||
$this->assertEqual(t('Taxonomy term'), $item1->getSourceType());
|
||||
$job->requestTranslation();
|
||||
|
||||
/* @var $item TMGMTJobItem */
|
||||
foreach ($job->getItems() as $item) {
|
||||
// The source is available only in en.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
$item->acceptTranslation();
|
||||
// The source should be now available in de and en.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
|
||||
// Check the structure of the imported data.
|
||||
$this->assertEqual($item1->item_id, $string_object_name);
|
||||
|
||||
// Check string translation.
|
||||
$this->assertEqual(i18n_string_translate('taxonomy:term:' . $term->tid . ':name', $source_text_name,
|
||||
array('langcode' => $job->target_language)), $job->target_language . '_' . $source_text_name);
|
||||
$this->assertEqual(i18n_string_translate('taxonomy:term:' . $term->tid . ':description', $source_text_description,
|
||||
array('langcode' => $job->target_language)), $job->target_language . '_' . $source_text_description);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the source is able to pull content in requested language.
|
||||
*/
|
||||
function testRequestDataForSpecificLanguage() {
|
||||
$this->setEnvironment('es');
|
||||
$this->setEnvironment('cs');
|
||||
|
||||
$config = array(
|
||||
'name' => $this->randomName(),
|
||||
'machine_name' => 'test_vocab',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
);
|
||||
$vocabulary = entity_create('taxonomy_vocabulary', $config);
|
||||
taxonomy_vocabulary_save($vocabulary);
|
||||
|
||||
$string_object_name = "taxonomy:vocabulary:" . $vocabulary->vid;
|
||||
|
||||
i18n_string_translation_update($string_object_name . ':name', 'de translation', 'de');
|
||||
|
||||
// Create new job item with a source language for which the translation
|
||||
// exits.
|
||||
$job = $this->createJob('de', 'cs');
|
||||
$job->save();
|
||||
$job->addItem('i18n_string', 'taxonomy_vocabulary', $string_object_name);
|
||||
|
||||
$data = $job->getData();
|
||||
$this->assertEqual($data[1][$string_object_name . ':name']['#text'], 'de translation');
|
||||
|
||||
// Create new job item with a source language for which the translation
|
||||
// does not exit.
|
||||
$job = $this->createJob('es', 'cs');
|
||||
$job->save();
|
||||
try {
|
||||
$job->addItem('i18n_string', 'taxonomy_vocabulary', $string_object_name);
|
||||
$this->fail('The job item should not be added as there is no translation for language "es"');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$languages = language_list();
|
||||
$this->assertEqual(t('Unable to load %language translation for the string %title',
|
||||
array('%language' => $languages['es']->name, '%title' => 'Name')), $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
function testI18nStringSourceMenu() {
|
||||
drupal_static_reset('_tmgmt_plugin_info');
|
||||
drupal_static_reset('_tmgmt_plugin_controller');
|
||||
|
||||
// Test translation of a menu.
|
||||
/////////////////////////////////////
|
||||
$config = array(
|
||||
'menu_name' => $this->randomName(),
|
||||
'title' => $this->randomName(),
|
||||
'description' => $this->randomName(),
|
||||
'i18n_mode' => I18N_MODE_MULTIPLE,
|
||||
);
|
||||
menu_save($config);
|
||||
$menu = menu_load($config['menu_name']);
|
||||
|
||||
$source_text = $menu['title'];
|
||||
$string_name = 'menu:menu:' . $menu['menu_name'];
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->translator->name;
|
||||
$job->settings = array();
|
||||
|
||||
$item1 = $job->addItem('i18n_string', 'menu', $string_name);
|
||||
$this->assertEqual(t('Menu'), $item1->getSourceType());
|
||||
$job->requestTranslation();
|
||||
/* @var $item TMGMTJobItem */
|
||||
foreach ($job->getItems() as $item) {
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
$item->acceptTranslation();
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
|
||||
$data = $item1->getData();
|
||||
$this->assertEqual($data['menu:menu:' . $menu['menu_name'] . ':title']['#text'], $config['title']);
|
||||
$this->assertEqual($data['menu:menu:' . $menu['menu_name'] . ':description']['#text'], $config['description']);
|
||||
|
||||
// Check the structure of the imported data.
|
||||
$this->assertEqual($item1->item_id, $string_name, 'String is correctly saved');
|
||||
|
||||
// Check string translation.
|
||||
$this->assertEqual(i18n_string_translate($string_name . ':title', $source_text, array('langcode' => $job->target_language)), $job->target_language . '_' . $source_text);
|
||||
|
||||
// Test translation of a menu item.
|
||||
/////////////////////////////////////
|
||||
$source_text = $this->randomName();
|
||||
$menu_link = array(
|
||||
'link_path' => '<front>',
|
||||
'link_title' => $source_text,
|
||||
// i18n_menu_link::get_title() uses the title, set that too.
|
||||
'title' => $source_text,
|
||||
'menu_name' => $menu['menu_name'],
|
||||
'customized' => TRUE,
|
||||
);
|
||||
menu_link_save($menu_link);
|
||||
$string_name = 'menu:item:' . $menu_link['mlid'];
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->translator->name;
|
||||
$job->settings = array();
|
||||
|
||||
$item1 = $job->addItem('i18n_string', 'menu_link', $string_name);
|
||||
$this->assertEqual(t('Menu link'), $item1->getSourceType());
|
||||
$job->requestTranslation();
|
||||
/* @var $item TMGMTJobItem */
|
||||
foreach ($job->getItems() as $item) {
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
$item->acceptTranslation();
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
|
||||
$data = $item1->getData();
|
||||
$this->assertEqual($data[$string_name . ':title']['#text'], $source_text);
|
||||
|
||||
// Check the structure of the imported data.
|
||||
$this->assertEqual($item1->item_id, $string_name);
|
||||
|
||||
// Check string translation.
|
||||
$this->assertEqual(i18n_string_translate($string_name . ':title', $source_text, array('langcode' => $job->target_language)), $job->target_language . '_' . $source_text);
|
||||
|
||||
}
|
||||
|
||||
function testI18nStringSourceLangCodes() {
|
||||
$config = array(
|
||||
'name' => $this->randomName(),
|
||||
'description' => 'description_' . $this->randomName(),
|
||||
'machine_name' => 'test_vocab',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
);
|
||||
$vocabulary = entity_create('taxonomy_vocabulary', $config);
|
||||
taxonomy_vocabulary_save($vocabulary);
|
||||
|
||||
$string_object_name = "taxonomy:vocabulary:" . $vocabulary->vid;
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item = $job->addItem('i18n_string', 'taxonomy_vocabulary', $string_object_name);
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
|
||||
i18n_string_translation_update($string_object_name . ':description', 'de_' . $config['description'], 'de');
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
|
||||
i18n_string_translation_update($string_object_name . ':name', 'de_' . $config['name'], 'de');
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en', 'de'));
|
||||
}
|
||||
|
||||
function testI18nStringPluginUI() {
|
||||
|
||||
$this->loginAsAdmin(array('administer taxonomy', 'translate interface', 'translate user-defined strings'));
|
||||
|
||||
$vocab_data = array(
|
||||
'name' => $this->randomName(),
|
||||
'machine_name' => 'test_vocab',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
);
|
||||
$term_data = array(
|
||||
'name' => $this->randomName(),
|
||||
);
|
||||
$vocab_data_not_translated = array(
|
||||
'name' => $this->randomName(),
|
||||
'machine_name' => 'test_vocab3',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
);
|
||||
|
||||
// Configure taxonomy and create vocab + term.
|
||||
$this->drupalPost('admin/structure/taxonomy/add', $vocab_data, t('Save'));
|
||||
$this->drupalGet('admin/structure/taxonomy');
|
||||
$this->clickLink(t('add terms'));
|
||||
$this->drupalPost(NULL, $term_data, t('Save'));
|
||||
$this->drupalPost('admin/structure/taxonomy/add', $vocab_data_not_translated, t('Save'));
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_taxonomy_vocabulary');
|
||||
$this->assertText($vocab_data['name']);
|
||||
|
||||
// Request translation via i18n source tab
|
||||
$this->drupalPost(NULL, array('items[taxonomy:vocabulary:1]' => 1), t('Request translation'));
|
||||
// Test for the job checkout url.
|
||||
$this->assertTrue(strpos($this->getUrl(), 'admin/tmgmt/jobs') !== FALSE);
|
||||
entity_get_controller('tmgmt_job')->resetCache();
|
||||
$jobs = entity_load('tmgmt_job', FALSE);
|
||||
/** @var TMGMTJob $job */
|
||||
$job = array_pop($jobs);
|
||||
$this->assertFieldByName('label', $job->label());
|
||||
|
||||
// Request translation via translate tab of i18n.
|
||||
$this->drupalPost('admin/structure/taxonomy/test_vocab/translate', array('languages[taxonomy:vocabulary:1:de]' => 1), t('Request translation'));
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Verify that the job item status is shown.
|
||||
$this->assertText(t('Needs review'));
|
||||
$this->clickLink(t('review'));
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
$this->assertText(t('The translation for @label has been accepted.', array('@label' => $job->label())));
|
||||
|
||||
// Test the missing translation filter.
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_taxonomy_vocabulary');
|
||||
// Check that the source language has been removed from the target language
|
||||
// select box.
|
||||
$elements = $this->xpath('//select[@name=:name]//option[@value=:option]', array(':name' => 'search[target_language]', ':option' => i18n_string_source_language()));
|
||||
$this->assertTrue(empty($elements));
|
||||
$edit = array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'untranslated',
|
||||
);
|
||||
$this->drupalPost('admin/tmgmt/sources/i18n_string_taxonomy_vocabulary', $edit, t('Search'));
|
||||
// The vocabulary name is translated to "de" therefore it must not show up
|
||||
// in the list.
|
||||
$this->assertNoText($vocab_data['name']);
|
||||
$this->assertText($vocab_data_not_translated['name']);
|
||||
|
||||
$edit = array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'untranslated',
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
$this->assertNoText($vocab_data['name']);
|
||||
$this->assertText($vocab_data_not_translated['name']);
|
||||
|
||||
// Update the string status to I18N_STRING_STATUS_UPDATE.
|
||||
$lid = db_select('locales_source', 's')->fields('s', array('lid'))->condition('source', $vocab_data['name'])->execute()->fetchField();
|
||||
db_update('locales_target')->fields(array('i18n_status' => I18N_STRING_STATUS_UPDATE))->condition('lid', $lid)->execute();
|
||||
|
||||
$edit = array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'outdated',
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
$this->assertText($vocab_data['name']);
|
||||
$this->assertNoText($vocab_data_not_translated['name']);
|
||||
|
||||
$edit = array(
|
||||
'search[target_language]' => 'de',
|
||||
'search[target_status]' => 'untranslated_or_outdated',
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
$this->assertText($vocab_data['name']);
|
||||
$this->assertText($vocab_data_not_translated['name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translation of blocks through the user interface.
|
||||
*/
|
||||
function testI18nStringPluginUIBlock() {
|
||||
|
||||
$this->loginAsAdmin(array('administer blocks', 'translate interface', 'translate user-defined strings'));
|
||||
|
||||
// Make some blocks translatable.
|
||||
$navigation_edit = array(
|
||||
'title' => $this->randomName(),
|
||||
'i18n_mode' => 1,
|
||||
);
|
||||
$this->drupalPost('admin/structure/block/manage/system/navigation/configure', $navigation_edit, t('Save block'));
|
||||
$powered_edit = array(
|
||||
'title' => $this->randomName(),
|
||||
'i18n_mode' => 1,
|
||||
);
|
||||
$this->drupalPost('admin/structure/block/manage/system/powered-by/configure', $powered_edit, t('Save block'));
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_block');
|
||||
$this->assertText($navigation_edit['title']);
|
||||
$this->assertText($powered_edit['title']);
|
||||
|
||||
// Request translation via i18n source tab.
|
||||
$edit = array(
|
||||
'items[blocks:system:powered-by]' => 1,
|
||||
'items[blocks:system:navigation]' => 1,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
$this->assertText($navigation_edit['title']);
|
||||
$this->assertText($powered_edit['title']);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
$this->assertRaw(t('Active job item: Needs review'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translation of fields through the user interface.
|
||||
*/
|
||||
function testI18nStringPluginUIField() {
|
||||
$this->loginAsAdmin(array('translate interface', 'translate user-defined strings'));
|
||||
$type = $this->drupalCreateContentType(array('type' => $type = $this->randomName()));
|
||||
|
||||
// Create a field.
|
||||
$field = array(
|
||||
'field_name' => 'list_test',
|
||||
'type' => 'list_text',
|
||||
);
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$field['settings']['allowed_values'][$this->randomName()] = $this->randomString();
|
||||
}
|
||||
field_create_field($field);
|
||||
|
||||
// Create an instance of the previously created field.
|
||||
$instance = array(
|
||||
'field_name' => 'list_test',
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type->type,
|
||||
'label' => $this->randomName(10),
|
||||
'description' => $this->randomString(30),
|
||||
);
|
||||
field_create_instance($instance);
|
||||
|
||||
// The body field doesn't have anything that can be translated on the field
|
||||
// level, so it shouldn't show up in the field overview.
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_field');
|
||||
$this->assertNoText(t('Body'));
|
||||
// @todo: Label doesn't work here?
|
||||
$this->assertText('field:list_test:#allowed_values');
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_field_instance');
|
||||
$this->assertUniqueText(t('Body'));
|
||||
$this->assertUniqueText($instance['label']);
|
||||
|
||||
// Request translation.
|
||||
$edit = array(
|
||||
'items[field:body:' . $type->type . ']' => 1,
|
||||
'items[field:list_test:' . $type->type . ']' => 1,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
$this->assertText(t('Body'));
|
||||
$this->assertText($instance['label']);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
$this->assertRaw(t('Active job item: Needs review'));
|
||||
|
||||
// Review the first item.
|
||||
$this->clickLink(t('reviewed'));
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
|
||||
// The overview should now have a translated field and a pending job item.
|
||||
$this->drupalGet('admin/tmgmt/sources/i18n_string_field_instance');
|
||||
$this->assertRaw(t('Translation up to date'));
|
||||
$this->assertRaw(t('Active job item: Needs review'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the i18n specific cart functionality.
|
||||
*/
|
||||
function testCart() {
|
||||
$vocabulary1 = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->randomName(),
|
||||
'description' => 'description_' . $this->randomName(),
|
||||
'machine_name' => 'test_vocab1',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
));
|
||||
taxonomy_vocabulary_save($vocabulary1);
|
||||
$string1 = "taxonomy:vocabulary:" . $vocabulary1->vid;
|
||||
|
||||
$vocabulary2 = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->randomName(),
|
||||
'description' => 'description_' . $this->randomName(),
|
||||
'machine_name' => 'test_vocab2',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
));
|
||||
taxonomy_vocabulary_save($vocabulary2);
|
||||
$string2 = "taxonomy:vocabulary:" . $vocabulary2->vid;
|
||||
|
||||
$vocabulary3 = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->randomName(),
|
||||
'description' => 'description_' . $this->randomName(),
|
||||
'machine_name' => 'test_vocab3',
|
||||
'i18n_mode' => I18N_MODE_LOCALIZE,
|
||||
));
|
||||
taxonomy_vocabulary_save($vocabulary3);
|
||||
|
||||
$this->loginAsAdmin(array_merge($this->translator_permissions, array('translate interface', 'translate user-defined strings')));
|
||||
|
||||
// Test source overview.
|
||||
$this->drupalPost('admin/tmgmt/sources/i18n_string_taxonomy_vocabulary', array(
|
||||
'items[' . $string1 . ']' => TRUE,
|
||||
'items[' . $string2 . ']' => TRUE,
|
||||
), t('Add to cart'));
|
||||
$this->drupalGet('admin/tmgmt/cart');
|
||||
$this->assertText($vocabulary1->name);
|
||||
$this->assertText($vocabulary2->name);
|
||||
|
||||
// Test translate tab.
|
||||
$this->drupalGet('admin/structure/taxonomy/test_vocab3/translate');
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a>.',
|
||||
array('@count' => 2, '@url' => url('admin/tmgmt/cart'))));
|
||||
|
||||
$this->drupalPost(NULL, array(), t('Add to cart'));
|
||||
$this->assertRaw(t('@count content source was added into the <a href="@url">cart</a>.', array('@count' => 1, '@url' => url('admin/tmgmt/cart'))));
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a> including the current item.',
|
||||
array('@count' => 3, '@url' => url('admin/tmgmt/cart'))));
|
||||
}
|
||||
}
|
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the I18nString source controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class TMGMTI18nStringDefaultSourceUIController
|
||||
*
|
||||
* UI Controller fo i18n strings translation jobs.
|
||||
*/
|
||||
class TMGMTI18nStringDefaultSourceUIController extends TMGMTDefaultSourceUIController {
|
||||
/**
|
||||
* Gets overview form header.
|
||||
*
|
||||
* @return array
|
||||
* Header array definition as expected by theme_tablesort().
|
||||
*/
|
||||
public function overviewFormHeader() {
|
||||
$languages = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$langcode = str_replace('-', '', $langcode);
|
||||
$languages['langcode-' . $langcode] = array(
|
||||
'data' => check_plain($language->name),
|
||||
);
|
||||
}
|
||||
|
||||
$header = array(
|
||||
'title' => array('data' => t('Label (in source language)')),
|
||||
'type' => array('data' => t('Type')),
|
||||
) + $languages;
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewForm($form, &$form_state, $type) {
|
||||
$form += $this->overviewSearchFormPart($form, $form_state, $type);
|
||||
|
||||
$form['items'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $this->overviewFormHeader($type),
|
||||
'#empty' => t('No strings matching given criteria have been found.')
|
||||
);
|
||||
|
||||
$search_data = $this->getSearchFormSubmittedParams();
|
||||
|
||||
$i18n_strings = tmgmt_i18n_string_get_strings($type, $search_data['label'], $search_data['target_language'], $search_data['target_status']);
|
||||
|
||||
foreach ($this->getTranslationData($i18n_strings, $form_state['item_type']) as $id => $data) {
|
||||
$form['items']['#options'][$id] = $this->overviewRow($type, $data);
|
||||
}
|
||||
|
||||
$form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create translation data list for the sources page list.
|
||||
*
|
||||
* @param array $i18n_strings
|
||||
* Result of the search query returned by tmgmt_i18n_string_get_strings().
|
||||
* @param string $type
|
||||
* I18n object type.
|
||||
*
|
||||
* @return array
|
||||
* Structured array with translation data.
|
||||
*/
|
||||
protected function getTranslationData($i18n_strings, $type) {
|
||||
$objects = array();
|
||||
$source_language = variable_get_value('i18n_string_source_language');
|
||||
|
||||
foreach ($i18n_strings as $i18n_string) {
|
||||
$wrapper = tmgmt_i18n_string_get_wrapper($type, $i18n_string);
|
||||
|
||||
if ($wrapper instanceof i18n_string_object_wrapper) {
|
||||
$id = $i18n_string->job_item_id;
|
||||
|
||||
// Get existing translations and current job items for the entity
|
||||
// to determine translation statuses
|
||||
$current_job_items = tmgmt_job_item_load_latest('i18n_string', $wrapper->get_type(), $id, $source_language);
|
||||
|
||||
$objects[$id] = array(
|
||||
'id' => $id,
|
||||
'object' => $wrapper->get_strings(array('empty' => TRUE)),
|
||||
'wrapper' => $wrapper,
|
||||
);
|
||||
// Load entity translation specific data.
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$langcode = str_replace('-', '', $langcode);
|
||||
|
||||
$translation_status = 'current';
|
||||
|
||||
if ($langcode == $source_language) {
|
||||
$translation_status = 'original';
|
||||
}
|
||||
elseif ($i18n_string->{'lang_' . $langcode} === NULL) {
|
||||
$translation_status = 'missing';
|
||||
}
|
||||
|
||||
$objects[$id]['current_job_items'][$langcode] = isset($current_job_items[$langcode]) ? $current_job_items[$langcode] : NULL;
|
||||
$objects[$id]['translation_statuses'][$langcode] = $translation_status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds search form for entity sources overview.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*
|
||||
* @return array
|
||||
* Drupal form array.
|
||||
*/
|
||||
public function overviewSearchFormPart($form, &$form_state, $type) {
|
||||
|
||||
$options = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$options[$langcode] = $language->name;
|
||||
}
|
||||
|
||||
$default_values = $this->getSearchFormSubmittedParams();
|
||||
|
||||
$form['search_wrapper'] = array(
|
||||
'#prefix' => '<div class="tmgmt-sources-wrapper tmgmt-i18n_string-sources-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => -15,
|
||||
);
|
||||
$form['search_wrapper']['search'] = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
$form['search_wrapper']['search']['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Label in source language'),
|
||||
'#default_value' => isset($default_values['label']) ? $default_values['label'] : NULL,
|
||||
);
|
||||
|
||||
// Unset the source language as it should not be listed among target
|
||||
// languages.
|
||||
unset($options[i18n_string_source_language()]);
|
||||
|
||||
$form['search_wrapper']['search']['target_language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target language'),
|
||||
'#options' => $options,
|
||||
'#empty_option' => t('Any'),
|
||||
'#default_value' => isset($default_values['target_language']) ? $default_values['target_language'] : NULL,
|
||||
);
|
||||
$form['search_wrapper']['search']['target_status'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target status'),
|
||||
'#options' => array(
|
||||
'untranslated_or_outdated' => t('Untranslated or outdated'),
|
||||
'untranslated' => t('Untranslated'),
|
||||
'outdated' => t('Outdated'),
|
||||
),
|
||||
'#default_value' => isset($default_values['target_status']) ? $default_values['target_status'] : NULL,
|
||||
'#states' => array(
|
||||
'invisible' => array(
|
||||
':input[name="search[target_language]"]' => array('value' => ''),
|
||||
),
|
||||
),
|
||||
);
|
||||
$form['search_wrapper']['search_submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Search'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets submitted search params.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSearchFormSubmittedParams() {
|
||||
$params = array(
|
||||
'label' => NULL,
|
||||
'target_language' => NULL,
|
||||
'target_status' => NULL,
|
||||
);
|
||||
|
||||
if (isset($_GET['label'])) {
|
||||
$params['label'] = $_GET['label'];
|
||||
}
|
||||
if (isset($_GET['target_language'])) {
|
||||
$params['target_language'] = $_GET['target_language'];
|
||||
}
|
||||
if (isset($_GET['target_status'])) {
|
||||
$params['target_status'] = $_GET['target_status'];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a table row for overview form.
|
||||
*
|
||||
* @param string $type
|
||||
* i18n type.
|
||||
* @param array $data
|
||||
* Data needed to build the list row.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function overviewRow($type, $data) {
|
||||
// Set the default item key, assume it's the first.
|
||||
$item_title = reset($data['object']);
|
||||
|
||||
$type_label = i18n_object_info($type, 'title');
|
||||
|
||||
$row = array(
|
||||
'id' => $data['id'],
|
||||
'title' => $item_title->get_string() ? t('@title (@id)', array('@title' => $item_title->get_string(), '@id' => $data['id'])) : $data['id'],
|
||||
'type' => empty($type_label) ? t('Unknown') : $type_label,
|
||||
);
|
||||
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$langcode = str_replace('-', '', $langcode);
|
||||
$row['langcode-' . $langcode] = theme('tmgmt_ui_translation_language_status_single', array(
|
||||
'translation_status' => $data['translation_statuses'][$langcode],
|
||||
'job_item' => isset($data['current_job_items'][$langcode]) ? $data['current_job_items'][$langcode] : NULL,
|
||||
));
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function overviewFormSubmit($form, &$form_state, $type) {
|
||||
|
||||
// Handle search redirect.
|
||||
$this->overviewSearchFormRedirect($form, $form_state, $type);
|
||||
$items = array_filter($form_state['values']['items']);
|
||||
$type = $form_state['item_type'];
|
||||
|
||||
$source_lang = variable_get_value('i18n_string_source_language');
|
||||
|
||||
// Create only single job for all items as the source language is just
|
||||
// the same for all.
|
||||
$job = tmgmt_job_create($source_lang, NULL, $GLOBALS['user']->uid);
|
||||
|
||||
// Loop through entities and create individual jobs for each source language.
|
||||
foreach ($items as $item) {
|
||||
$job->addItem('i18n_string', $type, $item);
|
||||
}
|
||||
|
||||
$form_state['redirect'] = array('admin/tmgmt/jobs/' . $job->tjid,
|
||||
array('query' => array('destination' => current_path())));
|
||||
drupal_set_message(t('One job needs to be checked out.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs redirect with search params appended to the uri.
|
||||
*
|
||||
* In case of triggering element is edit-search-submit it redirects to
|
||||
* current location with added query string containing submitted search form
|
||||
* values.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*/
|
||||
public function overviewSearchFormRedirect($form, &$form_state, $type) {
|
||||
if ($form_state['triggering_element']['#id'] == 'edit-search-submit') {
|
||||
|
||||
$query = array();
|
||||
|
||||
foreach ($form_state['values']['search'] as $key => $value) {
|
||||
$query[$key] = $value;
|
||||
}
|
||||
|
||||
drupal_goto($_GET['q'], array('query' => $query));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Drupal 7\\n"
|
||||
"MIME-Version: 1.0\\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\\n"
|
||||
"Content-Transfer-Encoding: 8bit\\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
|
||||
|
||||
msgid "Hello World"
|
||||
msgstr "Hallo Welt"
|
||||
|
||||
msgid "Example"
|
||||
msgstr "Beispiel"
|
@@ -0,0 +1,17 @@
|
||||
name = Locales Source
|
||||
description = Locales source plugin for the Translation Management system.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
dependencies[] = tmgmt
|
||||
dependencies[] = locale
|
||||
files[] = tmgmt_locale.plugin.inc
|
||||
files[] = tmgmt_locale.test
|
||||
files[] = tmgmt_locale.ui.inc
|
||||
files[] = tmgmt_locale.ui.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Installation hooks for tmgmt_locale module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Update existing {locales_target}.l10n_status if any.
|
||||
*/
|
||||
function tmgmt_locale_update_7000() {
|
||||
if (module_exists('l10n_update')) {
|
||||
module_load_include('inc', 'l10n_update');
|
||||
$query = db_select('tmgmt_job_item', 'ji')
|
||||
->condition('ji.plugin', 'locale')
|
||||
->condition('ji.state', TMGMT_JOB_ITEM_STATE_ACCEPTED);
|
||||
$query->innerJoin('tmgmt_job', 'j', 'j.tjid = ji.tjid');
|
||||
$query->addField('ji', 'item_id', 'lid');
|
||||
$query->addField('j', 'target_language', 'language');
|
||||
foreach ($query->execute() as $row) {
|
||||
db_update('locales_target')
|
||||
->condition('lid', $row->lid)
|
||||
->condition('language', $row->language)
|
||||
->fields(array('l10n_status' => L10N_UPDATE_STRING_CUSTOM))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Source plugin for the Translation Management system that handles locale strings.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_plugin_info().
|
||||
*
|
||||
* @see TMGMTLocaleSourcePluginController
|
||||
*/
|
||||
function tmgmt_locale_tmgmt_source_plugin_info() {
|
||||
$info['locale'] = array(
|
||||
'label' => t('Locale source'),
|
||||
'description' => t('Source handler for locale strings.'),
|
||||
'plugin controller class' => 'TMGMTLocaleSourcePluginController',
|
||||
'ui controller class' => 'TMGMTLocaleSourceUIController',
|
||||
'item types' => array(
|
||||
'default' => t('Locale'),
|
||||
),
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the locale source controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Translation plugin controller for locale strings.
|
||||
*/
|
||||
class TMGMTLocaleSourcePluginController extends TMGMTDefaultSourcePluginController {
|
||||
|
||||
/**
|
||||
* Updates translation associated to a specific locale source.
|
||||
*
|
||||
* @param string $lid
|
||||
* The Locale ID.
|
||||
* @param string $target_language
|
||||
* Target language to update translation.
|
||||
* @param string $translation
|
||||
* Translation value.
|
||||
*
|
||||
* @return bool
|
||||
* Success or not updating the locale translation.
|
||||
*/
|
||||
protected function updateTranslation($lid, $target_language, $translation) {
|
||||
|
||||
$languages = locale_language_list('name', TRUE);
|
||||
if (!$lid || !array_key_exists($target_language, $languages) || !$translation) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$exists = db_query("SELECT COUNT(lid) FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $target_language))
|
||||
->fetchField();
|
||||
|
||||
$fields = array(
|
||||
'translation' => $translation,
|
||||
);
|
||||
if (module_exists('l10n_update')) {
|
||||
module_load_include('inc', 'l10n_update');
|
||||
$fields += array(
|
||||
'l10n_status' => L10N_UPDATE_STRING_CUSTOM,
|
||||
);
|
||||
}
|
||||
|
||||
// @todo Only singular strings are managed here, we should take care of
|
||||
// plural information of processed string.
|
||||
if (!$exists) {
|
||||
$fields += array(
|
||||
'lid' => $lid,
|
||||
'language' => $target_language,
|
||||
);
|
||||
db_insert('locales_target')
|
||||
->fields($fields)
|
||||
->execute();
|
||||
}
|
||||
else {
|
||||
db_update('locales_target')
|
||||
->fields($fields)
|
||||
->condition('lid', $lid)
|
||||
->condition('language', $target_language)
|
||||
->execute();
|
||||
}
|
||||
// Clear locale caches.
|
||||
_locale_invalidate_js($target_language);
|
||||
cache_clear_all('locale:' . $target_language, 'cache');
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to obtain a locale object for given job item.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
*
|
||||
* @return locale object
|
||||
*/
|
||||
protected function getLocaleObject(TMGMTJobItem $job_item) {
|
||||
$locale_lid = $job_item->item_id;
|
||||
|
||||
// Check existence of assigned lid.
|
||||
$exists = db_query("SELECT COUNT(lid) FROM {locales_source} WHERE lid = :lid", array(':lid' => $locale_lid))->fetchField();
|
||||
if (!$exists) {
|
||||
throw new TMGMTException(t('Unable to load locale with id %id', array('%id' => $job_item->item_id)));
|
||||
}
|
||||
|
||||
// This is necessary as the method is also used in the getLabel() callback
|
||||
// and for that case the job is not available in the cart.
|
||||
if (!empty($job_item->tjid)) {
|
||||
$source_language = $job_item->getJob()->source_language;
|
||||
}
|
||||
else {
|
||||
$source_language = $job_item->getSourceLangCode();
|
||||
}
|
||||
|
||||
if ($source_language == 'en') {
|
||||
$query = db_select('locales_source', 'ls');
|
||||
$query
|
||||
->fields('ls')
|
||||
->condition('ls.lid', $locale_lid);
|
||||
$locale_object = $query
|
||||
->execute()
|
||||
->fetchObject();
|
||||
|
||||
$locale_object->language = 'en';
|
||||
|
||||
if (empty($locale_object)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$locale_object->origin = 'source';
|
||||
}
|
||||
else {
|
||||
$query = db_select('locales_target', 'lt');
|
||||
$query->join('locales_source', 'ls', 'ls.lid = lt.lid');
|
||||
$query
|
||||
->fields('lt')
|
||||
->fields('ls')
|
||||
->condition('lt.lid', $locale_lid)
|
||||
->condition('lt.language', $source_language);
|
||||
$locale_object = $query
|
||||
->execute()
|
||||
->fetchObject();
|
||||
|
||||
if (empty($locale_object)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$locale_object->origin = 'target';
|
||||
}
|
||||
|
||||
return $locale_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel(TMGMTJobItem $job_item) {
|
||||
if ($locale_object = $this->getLocaleObject($job_item)) {
|
||||
if ($locale_object->origin == 'source') {
|
||||
$label = $locale_object->source;
|
||||
}
|
||||
else {
|
||||
$label = $locale_object->translation;
|
||||
}
|
||||
return truncate_utf8(strip_tags($label), 30, FALSE, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [@inheritdoc}
|
||||
*/
|
||||
public function getType(TMGMTJobItem $job_item) {
|
||||
return $this->getItemTypeLabel($job_item->item_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getData(TMGMTJobItem $job_item) {
|
||||
$locale_object = $this->getLocaleObject($job_item);
|
||||
if (empty($locale_object)) {
|
||||
$languages = language_list();
|
||||
throw new TMGMTException(t('Unable to load %language translation for the locale %id',
|
||||
array('%language' => $languages[$job_item->getJob()->source_language]->name, '%id' => $job_item->item_id)));
|
||||
}
|
||||
|
||||
if ($locale_object->origin == 'source') {
|
||||
$text = $locale_object->source;
|
||||
}
|
||||
else {
|
||||
$text = $locale_object->translation;
|
||||
}
|
||||
|
||||
// Identify placeholders that need to be escaped. Assume that placeholders
|
||||
// consist of alphanumeric characters and _,- only and are delimited by
|
||||
// non-alphanumeric characters. There are cases that don't match, for
|
||||
// example appended SI units like "@valuems", there only @value is the
|
||||
// actual placeholder.
|
||||
$escape = array();
|
||||
if (preg_match_all('/([@!%][a-zA-Z0-9_-]+)/', $text, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
foreach ($matches[0] as $match) {
|
||||
$escape[$match[1]]['string'] = $match[0];
|
||||
}
|
||||
}
|
||||
$structure['singular'] = array(
|
||||
'#label' => t('Singular'),
|
||||
'#text' => (string) $text,
|
||||
'#translate' => TRUE,
|
||||
'#escape' => $escape,
|
||||
);
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveTranslation(TMGMTJobItem $job_item) {
|
||||
$job = tmgmt_job_load($job_item->tjid);
|
||||
$data = $job_item->getData();
|
||||
if (isset($data['singular'])) {
|
||||
$translation = $data['singular']['#translation']['#text'];
|
||||
// Update the locale string in the system.
|
||||
// @todo: Send error message to user if update fails.
|
||||
if ($this->updateTranslation($job_item->item_id, $job->target_language, $translation)) {
|
||||
$job_item->accepted();
|
||||
}
|
||||
}
|
||||
|
||||
// @todo: Temporary backwards compability with existing jobs, remove in next
|
||||
// release.
|
||||
if (isset($data[$job_item->item_id])) {
|
||||
$translation = $data[$job_item->item_id]['#translation']['#text'];
|
||||
// Update the locale string in the system.
|
||||
// @todo: Send error message to user if update fails.
|
||||
if ($this->updateTranslation($job_item->item_id, $job->target_language, $translation)) {
|
||||
$job_item->accepted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourceLangCode(TMGMTJobItem $job_item) {
|
||||
// For the locale source English is always the source language.
|
||||
return 'en';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExistingLangCodes(TMGMTJobItem $job_item) {
|
||||
$query = db_select('locales_target', 'lt');
|
||||
$query->fields('lt', array('language'));
|
||||
$query->condition('lt.lid', $job_item->item_id);
|
||||
|
||||
$existing_lang_codes = array('en');
|
||||
foreach ($query->execute() as $language) {
|
||||
$existing_lang_codes[] = $language->language;
|
||||
}
|
||||
|
||||
return $existing_lang_codes;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic Locale Source tests.
|
||||
*/
|
||||
class TMGMTLocaleSourceTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Locale Source tests',
|
||||
'description' => 'Exporting source data from locale and saving translations back',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_locale'));
|
||||
$this->langcode = 'de';
|
||||
$this->context = 'default';
|
||||
$file = new stdClass();
|
||||
$file->uri = drupal_realpath(drupal_get_path('module', 'tmgmt_locale') . '/tests/test.xx.po');
|
||||
$this->pofile = file_save($file);
|
||||
$this->setEnvironment($this->langcode);
|
||||
$this->setEnvironment('es');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translation of a locale singular term.
|
||||
*/
|
||||
function testSingularTerm() {
|
||||
// Load PO file to create a locale structure in the database.
|
||||
_locale_import_po($this->pofile, $this->langcode, LOCALE_IMPORT_OVERWRITE, $this->context);
|
||||
|
||||
// Obtain one locale string with translation.
|
||||
$locale_object = db_query('SELECT * FROM {locales_source} WHERE source = :source LIMIT 1', array(':source' => 'Hello World'))->fetchObject();
|
||||
$source_text = $locale_object->source;
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item1 = $job->addItem('locale', 'default', $locale_object->lid);
|
||||
|
||||
// Check the structure of the imported data.
|
||||
$this->assertEqual($item1->item_id, $locale_object->lid, 'Locale Strings object correctly saved');
|
||||
$this->assertEqual('Locale', $item1->getSourceType());
|
||||
$this->assertEqual('Hello World', $item1->getSourceLabel());
|
||||
$job->requestTranslation();
|
||||
|
||||
foreach ($job->getItems() as $item) {
|
||||
/* @var $item TMGMTJobItem */
|
||||
$item->acceptTranslation();
|
||||
$this->assertTrue($item->isAccepted());
|
||||
// The source is now available in en and de.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en', 'de'));
|
||||
}
|
||||
|
||||
// Check string translation.
|
||||
$expected_translation = $job->target_language . '_' . $source_text;
|
||||
$this->assertTranslation($locale_object->lid, 'de', $expected_translation);
|
||||
|
||||
// Translate the german translation to spanish.
|
||||
$target_langcode = 'es';
|
||||
$job = $this->createJob('de', $target_langcode);
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item1 = $job->addItem('locale', 'default', $locale_object->lid);
|
||||
$this->assertEqual('Locale', $item1->getSourceType());
|
||||
$this->assertEqual($expected_translation, $item1->getSourceLabel());
|
||||
$job->requestTranslation();
|
||||
|
||||
foreach ($job->getItems() as $item) {
|
||||
/* @var $item TMGMTJobItem */
|
||||
$item->acceptTranslation();
|
||||
$this->assertTrue($item->isAccepted());
|
||||
|
||||
// The source should be now available for en, de and es languages.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en', 'de', 'es'));
|
||||
}
|
||||
|
||||
// Check string translation.
|
||||
$this->assertTranslation($locale_object->lid, $target_langcode, $job->target_language . '_' . $expected_translation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the source is able to pull content in requested language.
|
||||
*/
|
||||
function testRequestDataForSpecificLanguage() {
|
||||
$this->setEnvironment('cs');
|
||||
|
||||
_locale_import_po($this->pofile, $this->langcode, LOCALE_IMPORT_OVERWRITE, $this->context);
|
||||
$locale_object = db_query('SELECT * FROM {locales_source} WHERE source = :source LIMIT 1', array(':source' => 'Hello World'))->fetchObject();
|
||||
|
||||
$plugin = new TMGMTLocaleSourcePluginController('locale', 'locale');
|
||||
$reflection_plugin = new ReflectionClass('TMGMTLocaleSourcePluginController');
|
||||
$updateTranslation = $reflection_plugin->getMethod('updateTranslation');
|
||||
$updateTranslation->setAccessible(TRUE);
|
||||
|
||||
$updateTranslation->invoke($plugin, $locale_object->lid, 'de', 'de translation');
|
||||
|
||||
// Create the new job and job item.
|
||||
$job = $this->createJob('de', 'cs');
|
||||
$job->save();
|
||||
$job->addItem('locale', 'default', $locale_object->lid);
|
||||
|
||||
$data = $job->getData();
|
||||
$this->assertEqual($data[1]['singular']['#text'], 'de translation');
|
||||
|
||||
// Create new job item with a source language for which the translation
|
||||
// does not exit.
|
||||
$job = $this->createJob('es', 'cs');
|
||||
$job->save();
|
||||
try {
|
||||
$job->addItem('locale', 'default', $locale_object->lid);
|
||||
$this->fail('The job item should not be added as there is no translation for language "es"');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$languages = language_list();
|
||||
$this->assertEqual(t('Unable to load %language translation for the locale %id',
|
||||
array('%language' => $languages['es']->name, '%id' => $locale_object->lid)), $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that strings that need escaping are correctly identified.
|
||||
*/
|
||||
function testEscaping() {
|
||||
$lid = db_insert('locales_source')
|
||||
->fields(array(
|
||||
'source' => '@place-holders need %to be !esc_aped.',
|
||||
'textgroup' => 'default',
|
||||
'context' => '',
|
||||
))
|
||||
->execute();
|
||||
$job = $this->createJob('en', 'de');
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
$item = $job->addItem('locale', 'default', $lid);
|
||||
$data = $item->getData();
|
||||
$expected_escape = array(
|
||||
0 => array('string' => '@place-holders'),
|
||||
20 => array('string' => '%to'),
|
||||
27 => array('string' => '!esc_aped'),
|
||||
);
|
||||
$this->assertEqual($data['singular']['#escape'], $expected_escape);
|
||||
|
||||
// Invalid patterns that should be ignored.
|
||||
$lid = db_insert('locales_source')
|
||||
->fields(array(
|
||||
'source' => '@ % ! example',
|
||||
'textgroup' => 'default',
|
||||
'context' => '',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$item = $job->addItem('locale', 'default', $lid);
|
||||
$data = $item->getData();
|
||||
$this->assertTrue(empty($data[$lid]['#escape']));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that system behaves correctly with an non-existing locales.
|
||||
*/
|
||||
function testInexistantSource() {
|
||||
// Create inexistant locale object.
|
||||
$locale_object = new stdClass();
|
||||
$locale_object->lid = 0;
|
||||
|
||||
// Create the job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
// Create the job item.
|
||||
try {
|
||||
$job->addItem('locale', 'default', $locale_object->lid);
|
||||
$this->fail('Job item add with an inexistant locale.');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$this->pass('Exception thrown when trying to translate non-existing locale string');
|
||||
}
|
||||
|
||||
// Try to translate a source string without translation from german to
|
||||
// spanish.
|
||||
$lid = db_insert('locales_source')
|
||||
->fields(array(
|
||||
'source' => 'No translation',
|
||||
'textgroup' => 'default',
|
||||
'context' => '',
|
||||
))
|
||||
->execute();
|
||||
$job = $this->createJob('de', 'fr');
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
try {
|
||||
$job->addItem('locale', 'default', $lid);
|
||||
$this->fail('Job item add with an non-existing locale did not fail.');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$this->pass('Job item add with an non-existing locale did fail.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts a locale translation.
|
||||
*
|
||||
* @param int $lid
|
||||
* The locale source id.
|
||||
* @param string $target_langcode
|
||||
* The target language code.
|
||||
* @param string $expected_translation
|
||||
* The expected translation.
|
||||
*/
|
||||
public function assertTranslation($lid, $target_langcode, $expected_translation) {
|
||||
$actual_translation = db_query('SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language', array(
|
||||
':lid' => $lid,
|
||||
':language' => $target_langcode
|
||||
))->fetchField();
|
||||
$this->assertEqual($actual_translation, $expected_translation);
|
||||
}
|
||||
}
|
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the I18nString source controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class TMGMTI18nStringDefaultSourceUIController
|
||||
*
|
||||
* UI Controller fo i18n strings translation jobs.
|
||||
*/
|
||||
class TMGMTLocaleSourceUIController extends TMGMTDefaultSourceUIController {
|
||||
|
||||
/**
|
||||
* Gets locale strings.
|
||||
*
|
||||
* @param string $textgroup
|
||||
* The locale textgroup.
|
||||
* @param string $search_label
|
||||
* Label to search for.
|
||||
* @param string $missing_target_language
|
||||
* Missing translation language.
|
||||
*
|
||||
* @return array
|
||||
* List of i18n strings data.
|
||||
*/
|
||||
function getStrings($textgroup, $search_label = NULL, $missing_target_language = NULL) {
|
||||
$languages = drupal_map_assoc(array_keys(language_list()));
|
||||
$select = db_select('locales_source', 'ls')
|
||||
->fields('ls', array('lid', 'source'));
|
||||
|
||||
$select->addTag('tmgmt_sources_search');
|
||||
$select->addMetaData('plugin', 'locale');
|
||||
$select->addMetaData('type', $textgroup);
|
||||
|
||||
$select->condition('ls.textgroup', $textgroup);
|
||||
if (!empty($search_label)) {
|
||||
$select->condition('ls.source', "%$search_label%", 'LIKE');
|
||||
}
|
||||
if (!empty($missing_target_language) && in_array($missing_target_language, $languages)) {
|
||||
$select->isNull("lt_$missing_target_language.language");
|
||||
}
|
||||
|
||||
// Join locale targets for each language.
|
||||
// We want all joined fields to be named as langcodes, but langcodes could
|
||||
// contain hyphens in their names, which is not allowed by the most database
|
||||
// engines. So we create a langcode-to-filed_alias map, and rename fields
|
||||
// later.
|
||||
$langcode_to_filed_alias_map = array();
|
||||
foreach ($languages as $langcode) {
|
||||
$table_alias = $select->leftJoin('locales_target', db_escape_field("lt_$langcode"), "ls.lid = %alias.lid AND %alias.language = '$langcode'");
|
||||
$langcode_to_filed_alias_map[$langcode] = $select->addField($table_alias, 'language');
|
||||
}
|
||||
|
||||
$select = $select->extend('PagerDefault')->limit(variable_get('tmgmt_source_list_limit', 20));
|
||||
$rows = $select->execute()->fetchAll();
|
||||
foreach ($rows as $row) {
|
||||
foreach ($langcode_to_filed_alias_map as $langcode => $field_alias) {
|
||||
$row->{$langcode} = $row->{$field_alias};
|
||||
unset($row->{$field_alias});
|
||||
}
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets overview form header.
|
||||
*
|
||||
* @return array
|
||||
* Header array definition as expected by theme_tablesort().
|
||||
*/
|
||||
public function overviewFormHeader() {
|
||||
$languages = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$languages['langcode-' . $langcode] = array(
|
||||
'data' => check_plain($language->name),
|
||||
);
|
||||
}
|
||||
|
||||
$header = array(
|
||||
'source' => array('data' => t('Source text')),
|
||||
) + $languages;
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements TMGMTSourceUIControllerInterface::overviewForm().
|
||||
*/
|
||||
public function overviewForm($form, &$form_state, $type) {
|
||||
$form += $this->overviewSearchFormPart($form, $form_state, $type);
|
||||
|
||||
$form['items'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $this->overviewFormHeader($type),
|
||||
'#empty' => t('No strings matching given criteria have been found.')
|
||||
);
|
||||
|
||||
$search_data = $this->getSearchFormSubmittedParams();
|
||||
|
||||
$strings = $this->getStrings($type, $search_data['label'], $search_data['missing_target_language']);
|
||||
|
||||
foreach ($this->getTranslationData($strings, $type) as $id => $data) {
|
||||
$form['items']['#options'][$id] = $this->overviewRow($type, $data);
|
||||
}
|
||||
|
||||
$form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create translation data list for the sources page list.
|
||||
*
|
||||
* @param array $strings
|
||||
* Result of the search query returned by tmgmt_i18n_string_get_strings().
|
||||
* @param string $type
|
||||
* I18n object type.
|
||||
*
|
||||
* @return array
|
||||
* Structured array with translation data.
|
||||
*/
|
||||
protected function getTranslationData($strings, $type) {
|
||||
$objects = array();
|
||||
// Source language of locale strings is always english.
|
||||
$source_language = 'en';
|
||||
|
||||
foreach ($strings as $string) {
|
||||
$id = $string->lid;
|
||||
|
||||
// Get existing translations and current job items for the entity
|
||||
// to determine translation statuses
|
||||
$current_job_items = tmgmt_job_item_load_latest('locale', $type, $id, $source_language);
|
||||
|
||||
$objects[$id] = array(
|
||||
'id' => $id,
|
||||
'object' => $string
|
||||
);
|
||||
// Load entity translation specific data.
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$translation_status = 'current';
|
||||
|
||||
if ($langcode == $source_language) {
|
||||
$translation_status = 'original';
|
||||
}
|
||||
elseif ($string->{$langcode} === NULL) {
|
||||
$translation_status = 'missing';
|
||||
}
|
||||
|
||||
$objects[$id]['current_job_items'][$langcode] = isset($current_job_items[$langcode]) ? $current_job_items[$langcode] : NULL;
|
||||
$objects[$id]['translation_statuses'][$langcode] = $translation_status;
|
||||
}
|
||||
}
|
||||
|
||||
return $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds search form for entity sources overview.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*
|
||||
* @return array
|
||||
* Drupal form array.
|
||||
*/
|
||||
public function overviewSearchFormPart($form, &$form_state, $type) {
|
||||
|
||||
$options = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$options[$langcode] = $language->name;
|
||||
}
|
||||
|
||||
$default_values = $this->getSearchFormSubmittedParams();
|
||||
|
||||
$form['search_wrapper'] = array(
|
||||
'#prefix' => '<div class="tmgmt-sources-wrapper tmgmt-i18n_string-sources-wrapper">',
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => -15,
|
||||
);
|
||||
$form['search_wrapper']['search'] = array(
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
$form['search_wrapper']['search']['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Source text'),
|
||||
'#default_value' => isset($default_values['label']) ? $default_values['label'] : NULL,
|
||||
);
|
||||
|
||||
// Unset English as it is the source language for all locale strings.
|
||||
unset($options['en']);
|
||||
|
||||
$form['search_wrapper']['search']['missing_target_language'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Not translated to'),
|
||||
'#options' => $options,
|
||||
'#empty_option' => '--',
|
||||
'#default_value' => isset($default_values['missing_target_language']) ? $default_values['missing_target_language'] : NULL,
|
||||
);
|
||||
$form['search_wrapper']['search_submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Search'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets submitted search params.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSearchFormSubmittedParams() {
|
||||
$params = array(
|
||||
'label' => NULL,
|
||||
'missing_target_language' => NULL,
|
||||
);
|
||||
|
||||
if (isset($_GET['label'])) {
|
||||
$params['label'] = $_GET['label'];
|
||||
}
|
||||
if (isset($_GET['missing_target_language'])) {
|
||||
$params['missing_target_language'] = $_GET['missing_target_language'];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a table row for overview form.
|
||||
*
|
||||
* @param string $type
|
||||
* i18n type.
|
||||
* @param array $data
|
||||
* Data needed to build the list row.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function overviewRow($type, $data) {
|
||||
// Set the default item key, assume it's the first.
|
||||
$source = $data['object'];
|
||||
|
||||
$row = array(
|
||||
'id' => $data['id'],
|
||||
'source' => check_plain($source->source),
|
||||
);
|
||||
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$row['langcode-' . $langcode] = theme('tmgmt_ui_translation_language_status_single', array(
|
||||
'translation_status' => $data['translation_statuses'][$langcode],
|
||||
'job_item' => isset($data['current_job_items'][$langcode]) ? $data['current_job_items'][$langcode] : NULL,
|
||||
));
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements TMGMTSourceUIControllerInterface::overviewFormSubmit().
|
||||
*/
|
||||
public function overviewFormSubmit($form, &$form_state, $type) {
|
||||
// Handle search redirect.
|
||||
$this->overviewSearchFormRedirect($form, $form_state, $type);
|
||||
$items = array_filter($form_state['values']['items']);
|
||||
$type = $form_state['item_type'];
|
||||
|
||||
$source_lang = 'en';
|
||||
|
||||
// Create only single job for all items as the source language is just
|
||||
// the same for all.
|
||||
$job = tmgmt_job_create($source_lang, NULL, $GLOBALS['user']->uid);
|
||||
|
||||
// Loop through entities and create individual jobs for each source language.
|
||||
foreach ($items as $item) {
|
||||
$job->addItem('locale', $type, $item);
|
||||
}
|
||||
|
||||
$form_state['redirect'] = array('admin/tmgmt/jobs/' . $job->tjid,
|
||||
array('query' => array('destination' => current_path())));
|
||||
drupal_set_message(t('One job needs to be checked out.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs redirect with search params appended to the uri.
|
||||
*
|
||||
* In case of triggering element is edit-search-submit it redirects to
|
||||
* current location with added query string containing submitted search form
|
||||
* values.
|
||||
*
|
||||
* @param array $form
|
||||
* Drupal form array.
|
||||
* @param $form_state
|
||||
* Drupal form_state array.
|
||||
* @param $type
|
||||
* Entity type.
|
||||
*/
|
||||
public function overviewSearchFormRedirect($form, &$form_state, $type) {
|
||||
if ($form_state['triggering_element']['#id'] == 'edit-search-submit') {
|
||||
|
||||
$query = array();
|
||||
|
||||
foreach ($form_state['values']['search'] as $key => $value) {
|
||||
$query[$key] = $value;
|
||||
}
|
||||
|
||||
drupal_goto($_GET['q'], array('query' => $query));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic Locale Source tests.
|
||||
*/
|
||||
class TMGMTLocaleSourceUiTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Locale Source UI tests',
|
||||
'description' => 'Tests the locale source overview',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_locale', 'tmgmt_ui'));
|
||||
$this->langcode = 'de';
|
||||
$this->context = 'default';
|
||||
$file = new stdClass();
|
||||
$file->uri = drupal_realpath(drupal_get_path('module', 'tmgmt_locale') . '/tests/test.xx.po');
|
||||
$this->pofile = file_save($file);
|
||||
$this->setEnvironment($this->langcode);
|
||||
$this->setEnvironment('gsw-berne');
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function testOverview() {
|
||||
// Load PO file to create a locale structure in the database.
|
||||
_locale_import_po($this->pofile, $this->langcode, LOCALE_IMPORT_OVERWRITE, $this->context);
|
||||
|
||||
$this->loginAsTranslator();
|
||||
$this->drupalGet('admin/tmgmt/sources/locale_default');
|
||||
|
||||
$this->assertText('Hello World');
|
||||
$this->assertText('Example');
|
||||
$rows = $this->xpath('//tbody/tr');
|
||||
foreach ($rows as $row) {
|
||||
if ($row->td[1] == 'Hello World') {
|
||||
$this->assertEqual((string) $row->td[3]->div['title'], t('Translation up to date'));
|
||||
$this->assertEqual((string) $row->td[4]->div['title'], t('Not translated'));
|
||||
}
|
||||
}
|
||||
|
||||
// Filter on the label.
|
||||
$edit = array('search[label]' => 'Hello');
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
|
||||
$this->assertText('Hello World');
|
||||
$this->assertNoText('Example');
|
||||
|
||||
$locale_object = db_query('SELECT * FROM {locales_source} WHERE source = :source LIMIT 1', array(':source' => 'Hello World'))->fetchObject();
|
||||
|
||||
// First add source to the cart to test its functionality.
|
||||
$edit = array(
|
||||
'items[' . $locale_object->lid . ']' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Add to cart'));
|
||||
$this->assertRaw(t('@count content source was added into the <a href="@url">cart</a>.', array('@count' => 1, '@url' => url('admin/tmgmt/cart'))));
|
||||
$edit['target_language[]'] = array('gsw-berne');
|
||||
$this->drupalPost('admin/tmgmt/cart', $edit, t('Request translation'));
|
||||
|
||||
// Assert that the job item is displayed.
|
||||
$this->assertText('Hello World');
|
||||
$this->assertText(t('Locale'));
|
||||
$this->assertText('2');
|
||||
$this->drupalPost(NULL, array('target_language' => 'gsw-berne'), t('Submit to translator'));
|
||||
|
||||
// Test for the translation flag title.
|
||||
$this->drupalGet('admin/tmgmt/sources/locale_default');
|
||||
$this->assertRaw(t('Active job item: Needs review'));
|
||||
|
||||
// Review and accept the job item.
|
||||
$job_items = tmgmt_job_item_load_latest('locale', 'default', $locale_object->lid, 'en');
|
||||
$this->drupalGet('admin/tmgmt/items/' . $job_items['gsw-berne']->tjiid);
|
||||
$this->assertRaw('gsw-berne_Hello World');
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
$this->drupalGet('admin/tmgmt/sources/locale_default');
|
||||
|
||||
$this->assertNoRaw(t('Active job item: Needs review'));
|
||||
$rows = $this->xpath('//tbody/tr');
|
||||
foreach ($rows as $row) {
|
||||
if ($row->td[1] == 'Hello World') {
|
||||
$this->assertEqual((string) $row->td[3]->div['title'], t('Translation up to date'));
|
||||
$this->assertEqual((string) $row->td[4]->div['title'], t('Translation up to date'));
|
||||
}
|
||||
}
|
||||
|
||||
// Test the missing translation filter.
|
||||
$this->drupalGet('admin/tmgmt/sources/locale_default');
|
||||
// Check that the source language (en) has been removed from the target language
|
||||
// select box.
|
||||
$elements = $this->xpath('//select[@name=:name]//option[@value=:option]', array(':name' => 'search[target_language]', ':option' => 'en'));
|
||||
$this->assertTrue(empty($elements));
|
||||
|
||||
// Filter on the "Not translated to".
|
||||
$edit = array('search[missing_target_language]' => 'gsw-berne');
|
||||
$this->drupalPost(NULL, $edit, t('Search'));
|
||||
// Hello World is translated to "gsw-berne" therefore it must not show up in the
|
||||
// list.
|
||||
$this->assertNoText('Hello World');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the node source plugin for TMGMT.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Alter the created node translation.
|
||||
*
|
||||
* @param object
|
||||
* $tnode translated node
|
||||
* @param object
|
||||
* $node source node
|
||||
* @param TMGMTJobItem
|
||||
* $job_item
|
||||
*/
|
||||
function hook_tmgmt_before_update_node_translation_alter($tnode, $node, $job_item) {
|
||||
// Always store new translations as a new revision.
|
||||
$tnode->revision = 1;
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
name = Content Source
|
||||
description = Content Translation source plugin for the Translation Management system.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
|
||||
dependencies[] = tmgmt
|
||||
dependencies[] = tmgmt_field
|
||||
dependencies[] = translation
|
||||
|
||||
files[] = tmgmt_node.plugin.inc
|
||||
files[] = tmgmt_node.ui.inc
|
||||
files[] = tmgmt_node.test
|
||||
|
||||
; Views integration and handlers
|
||||
files[] = views/tmgmt_node.views.inc
|
||||
files[] = views/handlers/tmgmt_node_handler_field_translation_language_status.inc
|
||||
files[] = views/handlers/tmgmt_node_handler_field_translation_language_status_single.inc
|
||||
files[] = views/handlers/tmgmt_node_handler_filter_node_translatable_types.inc
|
||||
files[] = views/handlers/tmgmt_node_handler_filter_missing_translation.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Source plugin for the Translation Management system that handles nodes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_source_plugin_info().
|
||||
*/
|
||||
function tmgmt_node_tmgmt_source_plugin_info() {
|
||||
$info['node'] = array(
|
||||
'label' => t('Node'),
|
||||
'description' => t('Source handler for nodes.'),
|
||||
'plugin controller class' => 'TMGMTNodeSourcePluginController',
|
||||
'ui controller class' => 'TMGMTNodeSourceUIController',
|
||||
'views controller class' => 'TMGMTNodeSourceViewsController',
|
||||
'item types' => array(),
|
||||
);
|
||||
foreach (node_type_get_names() as $type => $name) {
|
||||
if (translation_supported_type($type)) {
|
||||
$info['node']['item types'][$type] = $name;
|
||||
}
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validator for the missing target language views field handler.
|
||||
*/
|
||||
function tmgmt_node_views_exposed_target_language_validate($form, &$form_state) {
|
||||
if (!empty($form_state['values']['tmgmt_node_missing_translation']) && $form_state['values']['language_1'] == $form_state['values']['tmgmt_node_missing_translation']) {
|
||||
form_set_error('tmgmt_node_missing_translation', t('The source and target languages must not be the same.'));
|
||||
}
|
||||
}
|
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the node source plugin controller.
|
||||
*/
|
||||
|
||||
class TMGMTNodeSourcePluginController extends TMGMTDefaultSourcePluginController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Returns the data from the fields as a structure that can be processed by
|
||||
* the Translation Management system.
|
||||
*/
|
||||
public function getData(TMGMTJobItem $job_item) {
|
||||
$node = node_load($job_item->item_id);
|
||||
$source_language = $job_item->getJob()->source_language;
|
||||
$languages = language_list();
|
||||
|
||||
// If the node language is not the same as the job source language try to
|
||||
// load its translation for the job source language.
|
||||
if ($node->language != $source_language) {
|
||||
$translation_loaded = FALSE;
|
||||
foreach (translation_node_get_translations($node->nid) as $language => $translation) {
|
||||
if ($language == $source_language) {
|
||||
$node = node_load($translation->nid);
|
||||
$translation_loaded = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$translation_loaded) {
|
||||
throw new TMGMTException(t('Unable to load %language translation for the node %title',
|
||||
array('%language' => $languages[$source_language]->name, '%title' => $node->title)));
|
||||
}
|
||||
}
|
||||
|
||||
$type = node_type_get_type($node);
|
||||
// Get all the fields that can be translated and arrange their values into
|
||||
// a specific structure.
|
||||
$structure = tmgmt_field_get_source_data('node', $node, $job_item->getJob()->source_language);
|
||||
$structure['node_title']['#label'] = $type->title_label;
|
||||
$structure['node_title']['#text'] = $node->title;
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveTranslation(TMGMTJobItem $job_item) {
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
$job = $job_item->getJob();
|
||||
if (empty($node->tnid)) {
|
||||
// We have no translation source nid, this is a new set, so create it.
|
||||
$node->tnid = $node->nid;
|
||||
node_save($node);
|
||||
}
|
||||
$translations = translation_node_get_translations($node->tnid);
|
||||
if (isset($translations[$job->target_language])) {
|
||||
// We have already a translation for the source node for the target
|
||||
// language, so load it.
|
||||
$tnode = node_load($translations[$job->target_language]->nid);
|
||||
}
|
||||
else {
|
||||
// We don't have a translation for the source node yet, so create one.
|
||||
$tnode = clone $node;
|
||||
unset($tnode->nid, $tnode->vid, $tnode->uuid, $tnode->vuuid);
|
||||
$tnode->language = $job->target_language;
|
||||
$tnode->translation_source = $node;
|
||||
}
|
||||
|
||||
// Allow modules and translator plugins to alter, for example in the
|
||||
// case of creating revisions for translated nodes, or altering
|
||||
// properties of the tnode before saving.
|
||||
drupal_alter('tmgmt_before_update_node_translation', $tnode, $node, $job_item);
|
||||
|
||||
// Time to put the translated data into the node.
|
||||
$data = $job_item->getData();
|
||||
// Special case for the node title.
|
||||
if (isset($data['node_title']['#translation']['#text'])) {
|
||||
$tnode->title = $data['node_title']['#translation']['#text'];
|
||||
unset($data['node_title']);
|
||||
}
|
||||
tmgmt_field_populate_entity('node', $tnode, $job->target_language, $data, FALSE);
|
||||
// Reset translation field, which determines outdated status.
|
||||
$tnode->translation['status'] = 0;
|
||||
node_save($tnode);
|
||||
|
||||
// We just saved the translation, set the sate of the job item to
|
||||
// 'finished'.
|
||||
$job_item->accepted();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel(TMGMTJobItem $job_item) {
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
return entity_label('node', $node);
|
||||
}
|
||||
return parent::getLabel($job_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUri(TMGMTJobItem $job_item) {
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
return entity_uri('node', $node);
|
||||
}
|
||||
return parent::getUri($job_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType(TMGMTJobItem $job_item) {
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
return node_type_get_name($node);
|
||||
}
|
||||
return parent::getType($job_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourceLangCode(TMGMTJobItem $job_item) {
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
return entity_language('node', $node);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExistingLangCodes(TMGMTJobItem $job_item) {
|
||||
$existing_lang_codes = array();
|
||||
if ($node = node_load($job_item->item_id)) {
|
||||
$existing_lang_codes = array(entity_language('node', $node));
|
||||
}
|
||||
if ($translations = translation_node_get_translations($job_item->item_id)) {
|
||||
$existing_lang_codes = array_unique(array_merge($existing_lang_codes, array_keys($translations)));
|
||||
}
|
||||
|
||||
return $existing_lang_codes;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* Basic Node Source tests.
|
||||
*/
|
||||
class TMGMTNodeSourceTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Node Source tests',
|
||||
'description' => 'Exporting source data from nodes and saving translations back to nodes',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_node', 'translation'));
|
||||
$this->loginAsAdmin();
|
||||
$this->setEnvironment('de');
|
||||
$this->createNodeType('page', 'Basic page', TRANSLATION_ENABLED, FALSE);
|
||||
$this->attachFields('node', 'page', array(TRUE, TRUE, FALSE, FALSE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests nodes field translation.
|
||||
*/
|
||||
function testNodeSource() {
|
||||
|
||||
// Create a translation job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $this->default_translator->name;
|
||||
$job->settings = array();
|
||||
$job->save();
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$node = $this->createNode('page');
|
||||
// Create a job item for this node and add it to the job.
|
||||
$item = $job->addItem('node', 'node', $node->nid);
|
||||
$this->assertEqual('Basic page', $item->getSourceType());
|
||||
}
|
||||
|
||||
// Translate the job.
|
||||
$job->requestTranslation();
|
||||
|
||||
foreach ($job->getItems() as $item) {
|
||||
// The source is only available in en.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('en'));
|
||||
|
||||
$item->acceptTranslation();
|
||||
$node = node_load($item->item_id);
|
||||
// Check if the tnid attribute is bigger than 0.
|
||||
$this->assertTrue($node->tnid > 0, 'The source node is part of a translation set.');
|
||||
// The translations may be statically cached, so make make sure
|
||||
// to reset the cache before loading the node translations.
|
||||
$cached_translations = & drupal_static('translation_node_get_translations', array());
|
||||
unset($cached_translations[$node->tnid]);
|
||||
// Load the translation set of the source node.
|
||||
$translations = translation_node_get_translations($node->tnid);
|
||||
$this->assertNotNull($translations['de'], 'Translation found for the source node.');
|
||||
if (isset($translations['de'])) {
|
||||
$tnode = node_load($translations['de']->nid, NULL, TRUE);
|
||||
$this->checkTranslatedData($tnode, $item->getData(), 'de');
|
||||
}
|
||||
|
||||
// The source should be now available for en and de.
|
||||
$this->assertJobItemLangCodes($item, 'en', array('de', 'en'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the source is able to pull content in requested language.
|
||||
*/
|
||||
function testRequestDataForSpecificLanguage() {
|
||||
$this->setEnvironment('sk');
|
||||
$this->setEnvironment('es');
|
||||
$content_type = $this->drupalCreateContentType();
|
||||
|
||||
$node = $this->drupalCreateNode(array(
|
||||
'title' => $this->randomName(),
|
||||
'language' => 'sk',
|
||||
'body' => array('sk' => array(array())),
|
||||
'type' => $content_type->type,
|
||||
));
|
||||
|
||||
$this->drupalCreateNode(array(
|
||||
'title' => 'en translation',
|
||||
'language' => 'en',
|
||||
'tnid' => $node->nid,
|
||||
'body' => array('en' => array(array())),
|
||||
'type' => $content_type->type,
|
||||
));
|
||||
|
||||
// Create a translation job.
|
||||
$job = $this->createJob('en', 'de');
|
||||
$job->save();
|
||||
$job->addItem('node', 'node', $node->nid);
|
||||
|
||||
$data = $job->getData();
|
||||
$this->assertEqual($data[1]['node_title']['#text'], 'en translation');
|
||||
|
||||
// Create new job item with a source language for which the translation
|
||||
// does not exit.
|
||||
$job = $this->createJob('es', 'cs');
|
||||
$job->save();
|
||||
try {
|
||||
$job->addItem('node', 'node', $node->nid);
|
||||
$this->fail('The job item should not be added as there is no translation for language "es"');
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
$languages = language_list();
|
||||
$this->assertEqual(t('Unable to load %language translation for the node %title',
|
||||
array('%language' => $languages['es']->name, '%title' => $node->title)), $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the data from an entity with the translated data.
|
||||
*
|
||||
* @param $node
|
||||
* The translated node object.
|
||||
* @param $data
|
||||
* An array with the translated data.
|
||||
* @param $langcode
|
||||
* The code of the target language.
|
||||
*/
|
||||
function checkTranslatedData($node, $data, $langcode) {
|
||||
foreach (element_children($data) as $field_name) {
|
||||
if ($field_name == 'node_title') {
|
||||
$this->assertEqual($node->title, $data['node_title']['#translation']['#text'], 'The title of the translated node matches the translated data.');
|
||||
continue;
|
||||
}
|
||||
foreach (element_children($data[$field_name]) as $delta) {
|
||||
$field_langcode = field_is_translatable('node', field_info_field($field_name)) ? $langcode : LANGUAGE_NONE;
|
||||
foreach (element_children($data[$field_name][$delta]) as $column) {
|
||||
$column_value = $data[$field_name][$delta][$column];
|
||||
if (!isset($column_value['#translate']) || $column_value['#translate']) {
|
||||
$this->assertEqual($node->{$field_name}[$field_langcode][$delta][$column], $column_value['#translation']['#text'], format_string('The translatable field %field:%delta has been populated with the proper translated data.', array(
|
||||
'%field' => $field_name,
|
||||
'delta' => $delta
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the node source ui controller.
|
||||
*/
|
||||
|
||||
class TMGMTNodeSourceUIController extends TMGMTDefaultSourceUIController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_menu() {
|
||||
// The node source overview is a View using Views Bulk Operations. Therefore
|
||||
// we don't need to provide any menu items.
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_forms() {
|
||||
// The node source overview is a View using Views Bulk Operations. Therefore
|
||||
// we don't need to provide any forms.
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_views_default_views() {
|
||||
return _tmgmt_load_exports('tmgmt_node', 'views', 'view.inc', 'view');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
name = Content Source User Interface
|
||||
description = User Interface for the content translation source plugin.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
|
||||
dependencies[] = tmgmt_node
|
||||
dependencies[] = tmgmt_ui
|
||||
dependencies[] = views_bulk_operations
|
||||
|
||||
files[] = tmgmt_node_ui.test
|
||||
files[] = tmgmt_node_ui.overview.test
|
||||
|
||||
; Views handlers
|
||||
files[] = views/tmgmt_node_ui_handler_filter_node_translatable_types.inc
|
||||
files[] = views/tmgmt_node_ui_handler_field_jobs.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Main module file for the translation management node source plugin user
|
||||
* interface.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_page_alter().
|
||||
*/
|
||||
function tmgmt_node_ui_page_alter(&$page) {
|
||||
// Translation tabs for nodes.
|
||||
if (($node = menu_get_object()) && entity_access('create', 'tmgmt_job')) {
|
||||
if (isset($page['content']['system_main']['translation_node_overview'])) {
|
||||
module_load_include('inc', 'tmgmt_node_ui', 'tmgmt_node_ui.pages');
|
||||
$page['content']['system_main']['translation_node_overview'] = drupal_get_form('tmgmt_node_ui_node_form', $node, $page['content']['system_main']['translation_node_overview']);
|
||||
}
|
||||
// Support the context module: when context is used for placing the
|
||||
// system_main block, then block contents appear nested one level deeper
|
||||
// under another 'content' key.
|
||||
elseif (isset($page['content']['system_main']['content']['translation_node_overview'])) {
|
||||
module_load_include('inc', 'tmgmt_node_ui', 'tmgmt_node_ui.pages');
|
||||
$page['content']['system_main']['content']['translation_node_overview'] = drupal_get_form('tmgmt_node_ui_node_form', $node, $page['content']['system_main']['content']['translation_node_overview']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_action_info().
|
||||
*/
|
||||
function tmgmt_node_ui_action_info() {
|
||||
return array(
|
||||
'tmgmt_node_ui_checkout_multiple_action' => array(
|
||||
'type' => 'node',
|
||||
'label' => t('Request translations'),
|
||||
'configurable' => false,
|
||||
'aggregate' => true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to do multistep checkout for translations.
|
||||
*
|
||||
* @param array $nodes
|
||||
* Array of Drupal nodes.
|
||||
* @param $info
|
||||
* Action info - not used.
|
||||
*
|
||||
*/
|
||||
function tmgmt_node_ui_checkout_multiple_action($nodes, $info) {
|
||||
$jobs = array();
|
||||
$source_lang_registry = array();
|
||||
|
||||
// Loop through entities and create individual jobs for each source language.
|
||||
foreach ($nodes as $node) {
|
||||
|
||||
try {
|
||||
|
||||
// For given source lang no job exists yet.
|
||||
if (!isset($source_lang_registry[$node->language])) {
|
||||
// Create new job.
|
||||
$job = tmgmt_job_create($node->language, NULL, $GLOBALS['user']->uid);
|
||||
// Add initial job item.
|
||||
$job->addItem('node', 'node', $node->nid);
|
||||
// Add job identifier into registry
|
||||
$source_lang_registry[$node->language] = $job->tjid;
|
||||
// Add newly created job into jobs queue.
|
||||
$jobs[$job->tjid] = $job;
|
||||
}
|
||||
// We have a job for given source lang, so just add new job item for the
|
||||
// existing job.
|
||||
else {
|
||||
$jobs[$source_lang_registry[$node->language]]->addItem('node', 'node', $node->nid);
|
||||
}
|
||||
}
|
||||
catch (TMGMTException $e) {
|
||||
watchdog_exception('tmgmt', $e);
|
||||
drupal_set_message(t('Unable to add job item for node %name. Make sure the source content is not empty.', array('%name' => $node->title)), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// If necessary, do a redirect.
|
||||
$redirects = tmgmt_ui_job_checkout_multiple($jobs);
|
||||
if ($redirects) {
|
||||
tmgmt_ui_redirect_queue_set($redirects, current_path());
|
||||
drupal_set_message(format_plural(count($redirects), t('One job needs to be checked out.'), t('@count jobs need to be checked out.')));
|
||||
drupal_goto(tmgmt_ui_redirect_queue_dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Content Overview Tests
|
||||
*/
|
||||
class TMGMTNodeSourceUIOverviewTestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Node Source UI Overview tests',
|
||||
'description' => 'Tests the user interface for node overviews.',
|
||||
'group' => 'Translation Management',
|
||||
'dependencies' => array('rules'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_node_ui'));
|
||||
|
||||
$this->loginAsAdmin();
|
||||
|
||||
$this->setEnvironment('de');
|
||||
$this->setEnvironment('fr');
|
||||
$this->setEnvironment('es');
|
||||
$this->setEnvironment('el');
|
||||
|
||||
$this->createNodeType('page', 'Page', TRANSLATION_ENABLED, FALSE);
|
||||
// 1 means that the node type can have a language but is not translatable.
|
||||
$this->createNodeType('untranslated', 'Untranslated', 1, FALSE);
|
||||
|
||||
$this->checkPermissions(array(), TRUE);
|
||||
|
||||
// Allow auto-accept.
|
||||
$default_translator = tmgmt_translator_load('test_translator');
|
||||
$default_translator->settings = array(
|
||||
'auto_accept' => TRUE,
|
||||
);
|
||||
$default_translator->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests translating through the content source overview.
|
||||
*/
|
||||
function testNodeSourceOverview() {
|
||||
|
||||
// Login as translator to translate nodes.
|
||||
$this->loginAsTranslator(array(
|
||||
'translate content',
|
||||
'edit any page content',
|
||||
'create page content',
|
||||
));
|
||||
|
||||
// Create a bunch of english nodes.
|
||||
$node1 = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
$node2 = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
$node3 = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
$node4 = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Create a node with an undefined language.
|
||||
$node5 = $this->drupalCreateNode(array('type' => 'page'));
|
||||
|
||||
// Create a node of an untranslatable content type.
|
||||
$node6 = $this->drupalCreateNode(array('type' => 'untranslated', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the overview page and make sure the nodes are there.
|
||||
$this->drupalGet('admin/tmgmt/sources/node');
|
||||
|
||||
// Make sure that valid nodes are shown.
|
||||
$this->assertText($node1->title);
|
||||
$this->assertText($node2->title);
|
||||
$this->assertText($node3->title);
|
||||
$this->assertText($node4->title);
|
||||
// Nodes without a language must not be shown.
|
||||
$this->assertNoText($node5->title);
|
||||
// Node with a type that is not enabled for translation must not be shown.
|
||||
$this->assertNoText($node6->title);
|
||||
|
||||
// Now translate them.
|
||||
$edit = array(
|
||||
'views_bulk_operations[0]' => TRUE,
|
||||
'views_bulk_operations[1]' => TRUE,
|
||||
'views_bulk_operations[2]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translations'));
|
||||
|
||||
// Some assertions on the submit form.
|
||||
$this->assertText(t('@title and 2 more (English to ?, Unprocessed)', array('@title' => $node1->title)));
|
||||
$this->assertText($node1->title);
|
||||
$this->assertText($node2->title);
|
||||
$this->assertText($node3->title);
|
||||
$this->assertNoText($node4->title);
|
||||
|
||||
// Translate
|
||||
$edit = array(
|
||||
'target_language' => 'de',
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Submit to translator'));
|
||||
$this->assertNoText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node1->title, '@language' => t('German'))));
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $node1->title)));
|
||||
$this->assertNoText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node2->title, '@language' => t('German'))));
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $node1->title)));
|
||||
$this->assertNoText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node3->title, '@language' => t('German'))));
|
||||
$this->assertText(t('The translation for @title has been accepted.', array('@title' => $node1->title)));
|
||||
|
||||
// Check the translated node.
|
||||
$this->clickLink($node1->title);
|
||||
$this->clickLink(t('Translate'));
|
||||
$this->assertText('de_' . $node1->title);
|
||||
|
||||
// Test for the source list limit set in the views export.
|
||||
$view = views_get_view('tmgmt_node_source_overview');
|
||||
$view->execute_display('default');
|
||||
$this->assertEqual($view->get_items_per_page(), variable_get('tmgmt_source_list_limit', 20));
|
||||
|
||||
// Test the missing translation filter.
|
||||
|
||||
// Create nodes needed to test the missing translation filter here so that
|
||||
// VBO order is not affected.
|
||||
$node_not_translated = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
$node_de = $this->drupalCreateNode(array('type' => 'page', 'language' => 'de', 'body' => array('de' => array(array()))));
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/node');
|
||||
$this->assertText($node1->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
$this->assertText($node_de->title);
|
||||
|
||||
// Submitting the search form will not work. After the form submission the
|
||||
// page does gets redirected to url without query parameters. So we simply
|
||||
// access the page with desired query.
|
||||
$this->drupalGet('admin/tmgmt/sources/node', array('query' => array(
|
||||
'tmgmt_node_missing_translation' => 'de',
|
||||
'target_status' => 'untranslated',
|
||||
)));
|
||||
$this->assertNoText($node1->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
$this->assertNoText($node_de->title);
|
||||
|
||||
// Update the the translate flag of the translated node and test if it is
|
||||
// listed among sources with missing translation.
|
||||
db_update('node')->fields(array('translate' => 1))
|
||||
->condition('nid', $node1->nid)->execute();
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/node', array('query' => array(
|
||||
'tmgmt_node_missing_translation' => 'de',
|
||||
'target_status' => 'outdated',
|
||||
)));
|
||||
$this->assertText($node1->title);
|
||||
$this->assertNoText($node_not_translated->title);
|
||||
$this->assertNoText($node_de->title);
|
||||
|
||||
$this->drupalGet('admin/tmgmt/sources/node', array('query' => array(
|
||||
'tmgmt_node_missing_translation' => 'de',
|
||||
'target_status' => 'untranslated_or_outdated',
|
||||
)));
|
||||
$this->assertText($node1->title);
|
||||
$this->assertText($node_not_translated->title);
|
||||
$this->assertNoText($node_de->title);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides page and form callbacks for the Translation Management Tool Node
|
||||
* Source User Interface module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Node translation overview form. This form overrides the Drupal core or i18n
|
||||
* Content Translation page with a tableselect form.
|
||||
*/
|
||||
function tmgmt_node_ui_node_form($form, &$form_state, $node, $original) {
|
||||
// Store the node in the form state so we can easily create the job in the
|
||||
// submit handler.
|
||||
$form_state['node'] = $node;
|
||||
|
||||
$form['top_actions']['#type'] = 'actions';
|
||||
$form['top_actions']['#weight'] = -10;
|
||||
tmgmt_ui_add_cart_form($form['top_actions'], $form_state, 'node', 'node', $node->nid);
|
||||
|
||||
// Inject our additional column into the header.
|
||||
array_splice($original['#header'], -1, 0, array(t('Pending Translations')));
|
||||
// Make this a tableselect form.
|
||||
$form['languages'] = array(
|
||||
'#type' => 'tableselect',
|
||||
'#header' => $original['#header'],
|
||||
'#options' => array(),
|
||||
);
|
||||
$languages = module_exists('i18n_node') ? i18n_node_language_list($node) : language_list();
|
||||
// Check if there is a job / job item that references this translation.
|
||||
$items = tmgmt_job_item_load_latest('node', 'node', $node->nid, $node->language);
|
||||
foreach ($languages as $langcode => $language) {
|
||||
if ($langcode == LANGUAGE_NONE) {
|
||||
// Never show language neutral on the overview.
|
||||
continue;
|
||||
}
|
||||
// Since the keys are numeric and in the same order we can shift one element
|
||||
// after the other from the original non-form rows.
|
||||
$option = array_shift($original['#rows']);
|
||||
if ($langcode == $node->language) {
|
||||
$additional = '<strong>' . t('Source') . '</strong>';
|
||||
// This is the source object so we disable the checkbox for this row.
|
||||
$form['languages'][$langcode] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
}
|
||||
elseif (isset($items[$langcode])) {
|
||||
/** @var TMGMTJobItem $item */
|
||||
$item = $items[$langcode];
|
||||
if ($item->getJob()->isUnprocessed()) {
|
||||
$uri = $item->getJob()->uri();
|
||||
$additional = l(t('Unprocessed'), $uri['path']);
|
||||
}
|
||||
else {
|
||||
$wrapper = entity_metadata_wrapper('tmgmt_job_item', $item);
|
||||
$uri = $item->uri();
|
||||
$additional = l($wrapper->state->label(), $uri['path']);
|
||||
}
|
||||
// Disable the checkbox for this row since there is already a translation
|
||||
// in progress that has not yet been finished. This way we make sure that
|
||||
// we don't stack multiple active translations for the same item on top
|
||||
// of each other.
|
||||
$form['languages'][$langcode] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#disabled' => TRUE,
|
||||
);
|
||||
}
|
||||
else {
|
||||
// There is no translation job / job item for this target language.
|
||||
$additional = t('None');
|
||||
}
|
||||
// Inject the additional column into the array.
|
||||
array_splice($option, -1, 0, array($additional));
|
||||
// Append the current option array to the form.
|
||||
$form['languages']['#options'][$langcode] = $option;
|
||||
}
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['request'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Request translation'),
|
||||
'#submit' => array('tmgmt_node_ui_translate_form_submit'),
|
||||
'#validate' => array('tmgmt_node_ui_translate_form_validate'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation callback for the node translation overview form.
|
||||
*/
|
||||
function tmgmt_node_ui_translate_form_validate($form, &$form_state) {
|
||||
$selected = array_filter($form_state['values']['languages']);
|
||||
if (empty($selected)) {
|
||||
form_set_error('languages', t('You have to select at least one language for requesting a translation.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit callback for the node translation overview form.
|
||||
*/
|
||||
function tmgmt_node_ui_translate_form_submit($form, &$form_state) {
|
||||
$node = $form_state['node'];
|
||||
$values = $form_state['values'];
|
||||
$jobs = array();
|
||||
foreach (array_keys(array_filter($values['languages'])) as $langcode) {
|
||||
// Create the job object.
|
||||
$job = tmgmt_job_create($node->language, $langcode, $GLOBALS['user']->uid);
|
||||
// Add the job item.
|
||||
$job->addItem('node', 'node', $node->nid);
|
||||
// Append this job to the array of created jobs so we can redirect the user
|
||||
// to a multistep checkout form if necessary.
|
||||
$jobs[$job->tjid] = $job;
|
||||
}
|
||||
tmgmt_ui_job_checkout_and_redirect($form_state, $jobs);
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains default rules.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_default_rules_configuration().
|
||||
*/
|
||||
function tmgmt_node_ui_default_rules_configuration() {
|
||||
$data = '{ "tmgmt_node_ui_request_translation" : {
|
||||
"LABEL" : "Request translation",
|
||||
"PLUGIN" : "rule",
|
||||
"REQUIRES" : [ "tmgmt" ],
|
||||
"USES VARIABLES" : { "nodes" : { "label" : "Nodes", "type" : "list\u003Cnode\u003E" } },
|
||||
"DO" : [
|
||||
{ "tmgmt_get_first_from_node_list" : {
|
||||
"USING" : { "list" : [ "nodes" ] },
|
||||
"PROVIDE" : { "first_node" : { "first_node" : "Node" } }
|
||||
}
|
||||
},
|
||||
{ "tmgmt_rules_create_job" : {
|
||||
"USING" : { "source_language" : [ "first-node:language" ] },
|
||||
"PROVIDE" : { "job" : { "job" : "Job" } }
|
||||
}
|
||||
},
|
||||
{ "LOOP" : {
|
||||
"USING" : { "list" : [ "nodes" ] },
|
||||
"ITEM" : { "node" : "Node" },
|
||||
"DO" : [
|
||||
{ "tmgmt_rules_job_add_item" : {
|
||||
"job" : [ "job" ],
|
||||
"plugin" : "node",
|
||||
"item_type" : "node",
|
||||
"item_id" : [ "node:nid" ]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{ "tmgmt_rules_job_checkout" : { "job" : [ "job" ] } }
|
||||
]
|
||||
}
|
||||
}';
|
||||
$rule = rules_import($data);
|
||||
$configs[$rule->name] = $rule;
|
||||
return $configs;
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
#edit-tmgmt-node-missing-translation-wrapper .form-item-target-status,
|
||||
#edit-tmgmt-node-missing-translation-wrapper .form-item-tmgmt-node-missing-translation {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#edit-tmgmt-node-missing-translation-wrapper .form-item-target-status {
|
||||
margin: 0 0 0 55px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
top: -19px;
|
||||
}
|
||||
|
||||
#edit-tmgmt-node-missing-translation-wrapper .form-item-target-status select {
|
||||
margin-top: 9px;
|
||||
}
|
@@ -0,0 +1,450 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Basic Node Source UI tests.
|
||||
*/
|
||||
class TMGMTNodeSourceUITestCase extends TMGMTEntityTestCaseUtility {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Node Source UI tests',
|
||||
'description' => 'Tests the user interface for node translation sources.',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_node_ui', 'block'));
|
||||
|
||||
// We need the administer blocks permission.
|
||||
$this->loginAsAdmin(array('administer blocks'));
|
||||
|
||||
$this->setEnvironment('de');
|
||||
$this->setEnvironment('fr');
|
||||
$this->setEnvironment('es');
|
||||
$this->setEnvironment('el');
|
||||
|
||||
// @todo Re-enable this when switching to testing profile.
|
||||
// Enable the main page content block for hook_page_alter() to work.
|
||||
$edit = array(
|
||||
'blocks[system_main][region]' => 'content',
|
||||
);
|
||||
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
|
||||
|
||||
$this->createNodeType('page', 'Page', TRANSLATION_ENABLED, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the create, submit and accept permissions.
|
||||
*/
|
||||
function testPermissions() {
|
||||
|
||||
$no_permissions = $this->drupalCreateUser();
|
||||
$this->drupalLogin($no_permissions);
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Test with a user that is only allowed to create jobs.
|
||||
$create_user = $this->drupalCreateUser(array('access administration pages', 'translate content', 'create translation jobs'));
|
||||
$this->drupalLogin($create_user);
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
$this->assertText(t('One job has been created.'));
|
||||
// Verify that we are still on the translate tab.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
|
||||
// The job is unprocessed, check the status flag in the source list.
|
||||
$this->drupalGet('admin/tmgmt/sources');
|
||||
$links = $this->xpath('//a[contains(@title, :title)]', array(':title' => t('Active job item: @state', array('@state' => t('Unprocessed')))));
|
||||
$attributes = $links[0]->attributes();
|
||||
// Check if the found link points to the job checkout page instead of the
|
||||
// job item review form.
|
||||
$this->assertEqual($attributes['href'], url('admin/tmgmt/jobs/1', array('query' => array('destination' => 'admin/tmgmt/sources'))));
|
||||
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
$this->assertResponse(200);
|
||||
$this->assertLink(t('manage'));
|
||||
$this->assertNoLink(t('submit'));
|
||||
$this->assertNoLink(t('delete'));
|
||||
$this->assertText(t('@title', array('@title' => $node->title)));
|
||||
$this->clickLink(t('manage'));
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoRaw(t('Submit to translator'));
|
||||
|
||||
// Try to access the delete page directly.
|
||||
$this->drupalGet($this->getUrl() . '/delete');
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Log in as user with only submit permission.
|
||||
$submit_user = $this->drupalCreateUser(array('access administration pages', 'translate content', 'submit translation jobs'));
|
||||
$this->drupalLogin($submit_user);
|
||||
|
||||
// Go to the translate tab, verify that there is no request translation
|
||||
// button.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
$this->assertNoRaw(t('Request translation'));
|
||||
|
||||
// Go to the overview and submit the job.
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
$this->assertResponse(200);
|
||||
$this->assertLink(t('submit'));
|
||||
$this->assertNoLink(t('manage'));
|
||||
$this->assertNoLink(t('delete'));
|
||||
$this->assertText(t('@title', array('@title' => $node->title)));
|
||||
|
||||
// Check VBO actions - "submit translation job" has the right to cancel
|
||||
// translation only.
|
||||
$element = $this->xpath('//select[@id=:id]/option/@value', array(':id' => 'edit-operation'));
|
||||
$options = array();
|
||||
foreach ($element as $option) {
|
||||
$options[] = (string) $option;
|
||||
}
|
||||
$this->assertTrue(in_array('rules_component::rules_tmgmt_job_abort_translation', $options));
|
||||
|
||||
// Go to the job checkout page and submit it.
|
||||
$this->clickLink('submit');
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
// After submit the redirect goes back to the job overview.
|
||||
$this->assertUrl('admin/tmgmt');
|
||||
|
||||
// Make sure that the job is active now.
|
||||
$this->assertText(t('Active'));
|
||||
// Click abort link and check if we are at the job abort confirm page.
|
||||
$this->clickLink(t('abort'));
|
||||
$this->assertText(t('This will send a request to the translator to abort the job. After the action the job translation process will be aborted and only remaining action will be resubmitting it.'));
|
||||
// Return back to job overview and test the manage link.
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
$this->clickLink(t('manage'));
|
||||
$this->assertText(t('Needs review'));
|
||||
$this->assertNoLink(t('review'));
|
||||
|
||||
// Now log in as user with only accept permission and review the job.
|
||||
$accept_user = $this->drupalCreateUser(array('access administration pages', 'accept translation jobs'));
|
||||
$this->drupalLogin($accept_user);
|
||||
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
|
||||
// Check VBO actions - "accept translation jobs" has the right to accept
|
||||
// translation only.
|
||||
$element = $this->xpath('//select[@id=:id]/option/@value', array(':id' => 'edit-operation'));
|
||||
$options = array();
|
||||
foreach ($element as $option) {
|
||||
$options[] = (string) $option;
|
||||
}
|
||||
$this->assertTrue(in_array('rules_component::rules_tmgmt_job_accept_translation', $options));
|
||||
|
||||
$this->clickLink('manage');
|
||||
$this->clickLink('review');
|
||||
|
||||
$this->drupalPost(NULL, array(), '✓');
|
||||
// Verify that the accepted character is shown.
|
||||
$this->assertText('☑');
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
$this->assertText(t('Accepted'));
|
||||
$this->assertText('1/0/0');
|
||||
|
||||
$create_user = $this->loginAsAdmin();
|
||||
$this->drupalLogin($create_user);
|
||||
|
||||
$this->drupalGet('admin/tmgmt');
|
||||
// Check VBO actions - "administer tmgmt" has rights for all actions.
|
||||
$element = $this->xpath('//select[@id=:id]/option/@value', array(':id' => 'edit-operation'));
|
||||
$options = array();
|
||||
foreach ($element as $option) {
|
||||
$options[] = (string) $option;
|
||||
}
|
||||
$this->assertTrue(in_array('rules_component::rules_tmgmt_job_accept_translation', $options));
|
||||
$this->assertTrue(in_array('rules_component::rules_tmgmt_job_abort_translation', $options));
|
||||
$this->assertTrue(in_array('rules_component::rules_tmgmt_job_delete', $options));
|
||||
|
||||
// Go to the translate tab, verify that there is no request translation
|
||||
// button.
|
||||
//$this->drupalGet('node/' . $node->nid);
|
||||
//$this->clickLink('Translate');
|
||||
//$this->assertNoRaw(t('Request translation'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testTranslateTabSingleCheckout() {
|
||||
|
||||
// Create a user that is allowed to translate nodes.
|
||||
$translater = $this->drupalCreateUser(array('translate content', 'create translation jobs', 'submit translation jobs', 'accept translation jobs'));
|
||||
$this->drupalLogin($translater);
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
$this->assertText($node->title);
|
||||
|
||||
// Go to the translate tab and check if the pending translation label is
|
||||
// "Unprocessed" and links to the job checkout page.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->assertLink(t('Unprocessed'));
|
||||
$this->clickLink(t('Unprocessed'));
|
||||
|
||||
// Submit.
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('German'))));
|
||||
|
||||
// Review.
|
||||
$this->clickLink(t('Needs review'));
|
||||
|
||||
// @todo Review job throuh the UI.
|
||||
$items = tmgmt_job_item_load_latest('node', 'node', $node->nid, 'en');
|
||||
$items['de']->acceptTranslation();
|
||||
|
||||
// German node should now be listed and be clickable.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->clickLink('de_' . $node->title);
|
||||
|
||||
// Test that the destination query argument does not break the redirect
|
||||
// and we are redirected back to the correct page.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate', array('query' => array('destination' => 'node')));
|
||||
|
||||
// Request a spanish translation.
|
||||
$edit = array(
|
||||
'languages[es]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the checkout page.
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the originally defined destination URL.
|
||||
$this->assertEqual(url('node', array('absolute' => TRUE)), $this->getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testTranslateTabMultipeCheckout() {
|
||||
// Create a user that is allowed to translate nodes.
|
||||
$translater = $this->drupalCreateUser(array('translate content', 'create translation jobs', 'submit translation jobs', 'accept translation jobs'));
|
||||
$this->drupalLogin($translater);
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
'languages[es]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('2 jobs need to be checked out.'));
|
||||
|
||||
// Submit all jobs.
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator and continue'));
|
||||
$this->assertText($node->title);
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('Spanish'))));
|
||||
|
||||
// Review.
|
||||
$this->clickLink(t('Needs review'));
|
||||
|
||||
// @todo Review job throuh the UI.
|
||||
$items = tmgmt_job_item_load_latest('node', 'node', $node->nid, 'en');
|
||||
$items['de']->acceptTranslation();
|
||||
$items['es']->acceptTranslation();
|
||||
|
||||
// Translated nodes should now be listed and be clickable.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->clickLink('de_' . $node->title);
|
||||
|
||||
// Translated nodes should now be listed and be clickable.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->clickLink('es_' . $node->title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testTranslateTabAutomatedCheckout() {
|
||||
// Hide settings on the test translator.
|
||||
$default_translator = tmgmt_translator_load('test_translator');
|
||||
$default_translator->settings = array(
|
||||
'expose_settings' => FALSE,
|
||||
);
|
||||
$default_translator->save();
|
||||
|
||||
// Create a user that is allowed to translate nodes.
|
||||
$translater = $this->drupalCreateUser(array('translate content', 'create translation jobs', 'submit translation jobs', 'accept translation jobs'));
|
||||
$this->drupalLogin($translater);
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertNoText(t('One job needs to be checked out.'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('German'))));
|
||||
|
||||
// Review.
|
||||
$this->clickLink(t('Needs review'));
|
||||
|
||||
// @todo Review job throuh the UI.
|
||||
$items = tmgmt_job_item_load_latest('node', 'node', $node->nid, 'en');
|
||||
$items['de']->acceptTranslation();
|
||||
|
||||
// German node should now be listed and be clickable.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->clickLink('de_' . $node->title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the translate tab for a single checkout.
|
||||
*/
|
||||
function testTranslateTabDisabledQuickCheckout() {
|
||||
variable_set('tmgmt_quick_checkout', FALSE);
|
||||
|
||||
// Hide settings on the test translator.
|
||||
$default_translator = tmgmt_translator_load('test_translator');
|
||||
$default_translator->settings = array(
|
||||
'expose_settings' => FALSE,
|
||||
);
|
||||
$default_translator->save();
|
||||
|
||||
// Create a user that is allowed to translate nodes.
|
||||
$translater = $this->drupalCreateUser(array('translate content', 'create translation jobs', 'submit translation jobs', 'accept translation jobs'));
|
||||
$this->drupalLogin($translater);
|
||||
|
||||
// Create an english source node.
|
||||
$node = $this->drupalCreateNode(array('type' => 'page', 'language' => 'en', 'body' => array('en' => array(array()))));
|
||||
|
||||
// Go to the translate tab.
|
||||
$this->drupalGet('node/' . $node->nid);
|
||||
$this->clickLink('Translate');
|
||||
|
||||
// Assert some basic strings on that page.
|
||||
$this->assertText(t('Translations of @title', array('@title' => $node->title)));
|
||||
$this->assertText(t('Pending Translations'));
|
||||
|
||||
// Request a translation for german.
|
||||
$edit = array(
|
||||
'languages[de]' => TRUE,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Request translation'));
|
||||
|
||||
// Verify that we are on the translate tab.
|
||||
$this->assertText(t('One job needs to be checked out.'));
|
||||
$this->assertText($node->title);
|
||||
|
||||
// Submit.
|
||||
$this->drupalPost(NULL, array(), t('Submit to translator'));
|
||||
|
||||
// Make sure that we're back on the translate tab.
|
||||
$this->assertEqual(url('node/' . $node->nid . '/translate', array('absolute' => TRUE)), $this->getUrl());
|
||||
$this->assertText(t('Test translation created.'));
|
||||
$this->assertText(t('The translation of @title to @language is finished and can now be reviewed.', array('@title' => $node->title, '@language' => t('German'))));
|
||||
|
||||
// Review.
|
||||
$this->clickLink(t('Needs review'));
|
||||
|
||||
// @todo Review job throuh the UI.
|
||||
$items = tmgmt_job_item_load_latest('node', 'node', $node->nid, 'en');
|
||||
$items['de']->acceptTranslation();
|
||||
|
||||
// German node should now be listed and be clickable.
|
||||
$this->drupalGet('node/' . $node->nid . '/translate');
|
||||
$this->clickLink('de_' . $node->title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the node source specific cart functionality.
|
||||
*/
|
||||
function testCart() {
|
||||
$nodes = array();
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$nodes[] = $this->createNode('page');
|
||||
}
|
||||
|
||||
$this->loginAsAdmin(array_merge($this->translator_permissions, array('translate content')));
|
||||
|
||||
// Test the source overview.
|
||||
$this->drupalPost('admin/tmgmt/sources/node', array(
|
||||
'views_bulk_operations[0]' => TRUE,
|
||||
'views_bulk_operations[1]' => TRUE,
|
||||
), t('Add to cart'));
|
||||
$this->drupalGet('admin/tmgmt/cart');
|
||||
$this->assertText($nodes[0]->title);
|
||||
$this->assertText($nodes[1]->title);
|
||||
|
||||
// Test the translate tab.
|
||||
$this->drupalGet('node/' . $nodes[3]->nid . '/translate');
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a>.',
|
||||
array('@count' => 2, '@url' => url('admin/tmgmt/cart'))));
|
||||
|
||||
$this->drupalPost(NULL, array(), t('Add to cart'));
|
||||
$this->assertRaw(t('@count content source was added into the <a href="@url">cart</a>.', array('@count' => 1, '@url' => url('admin/tmgmt/cart'))));
|
||||
$this->assertRaw(t('There are @count items in the <a href="@url">translation cart</a> including the current item.',
|
||||
array('@count' => 3, '@url' => url('admin/tmgmt/cart'))));
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Field handler which shows all jobs which contains a node.
|
||||
*
|
||||
* @TODO: This could probably abstracted into a more generic handler,
|
||||
* or even api function.
|
||||
*/
|
||||
class tmgmt_node_ui_handler_field_jobs extends views_handler_field_prerender_list {
|
||||
function pre_render(&$values) {
|
||||
$nids = array();
|
||||
foreach ($values as $row) {
|
||||
$nid = $this->get_value($row);
|
||||
$nids[] = $nid;
|
||||
}
|
||||
|
||||
$select = db_select('tmgmt_job', 'tj');
|
||||
$select->join('tmgmt_job_item', 'tji', "tj.id = %alias.tjid");
|
||||
$select->join('node', 'n', "tji.item_type = 'node' AND tji.plugin = 'node' AND tji.item_id = node.nid");
|
||||
$select->addField('n', 'nid');
|
||||
$select->addExpression('MAX(tj.id)');
|
||||
|
||||
dpq($select);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Field handler to display the status of all languages.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class tmgmt_node_handler_field_translation_language_status extends views_handler_field {
|
||||
|
||||
/**
|
||||
* @var views_plugin_query_default
|
||||
*/
|
||||
var $query;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $language_items;
|
||||
|
||||
/**
|
||||
* Array of active job items.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $active_job_items = array();
|
||||
|
||||
function init(&$view, &$options) {
|
||||
parent::init($view, $options);
|
||||
$this->view->init_style();
|
||||
$this->additional_fields['nid'] = 'nid';
|
||||
|
||||
/**
|
||||
* Dynamically add new fields so they are used
|
||||
*/
|
||||
$languages = language_list('language');
|
||||
foreach ($languages as $langcode => $lang_info) {
|
||||
$handler = views_get_handler($this->table, $this->field . '_single', 'field');
|
||||
if ($handler) {
|
||||
$id = $options['id'] . '_single_' . $langcode;
|
||||
$this->view->display_handler->handlers['field'][$id] = $handler;
|
||||
$info = array(
|
||||
'id' => $id,
|
||||
'table' => $this->table,
|
||||
'field' => $this->field . '_single',
|
||||
'label' => $lang_info->name,
|
||||
);
|
||||
$handler->langcode = $langcode;
|
||||
$handler->main_field = $options['id'];
|
||||
$handler->init($this->view, $info);
|
||||
$this->language_handlers[$langcode] = $handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pre_render(&$values) {
|
||||
$nids = array();
|
||||
foreach ($values as $value) {
|
||||
$tnid = $this->get_value($value);
|
||||
$tnid = !empty($tnid) ? $tnid : $this->get_value($value, 'nid');
|
||||
$this->active_job_items[$tnid] = tmgmt_job_item_load_latest('node', 'node', $tnid, $value->node_language);
|
||||
$nids[] = $tnid;
|
||||
}
|
||||
if ($nodes = node_load_multiple($nids)) {
|
||||
$result = db_select('node', 'n')
|
||||
->fields('n', array('tnid', 'language', 'translate'))
|
||||
->condition('tnid', $nids)
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
$this->language_items = array();
|
||||
foreach ($result as $tnode) {
|
||||
// The translate flag is set if the translation node is outdated, revert
|
||||
// to have FALSE for outdated translations.
|
||||
$this->language_items[$tnode->tnid][$tnode->language] = !$tnode->translate;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @todo What is this?
|
||||
*/
|
||||
class tmgmt_node_handler_field_translation_language_status_single extends views_handler_field {
|
||||
|
||||
/**
|
||||
* @var tmgmt_node_handler_field_translation_language_status
|
||||
*/
|
||||
var $main_handler;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
var $langcode;
|
||||
|
||||
function init(&$view, &$options) {
|
||||
parent::init($view, $options);
|
||||
$this->additional_fields['nid'] = array(
|
||||
'table' => 'node',
|
||||
'field' => 'nid',
|
||||
);
|
||||
}
|
||||
|
||||
function render($values) {
|
||||
$nid = $values->nid;
|
||||
$langcode = $this->langcode;
|
||||
|
||||
// Check if this is the source language.
|
||||
if ($langcode == $values->node_language) {
|
||||
$translation_status = 'original';
|
||||
}
|
||||
// Check if there is a translation.
|
||||
elseif (!isset($this->view->field[$this->main_field]->language_items[$nid][$langcode])) {
|
||||
$translation_status = 'missing';
|
||||
}
|
||||
// Check if the translation is outdated.
|
||||
elseif (!$this->view->field[$this->main_field]->language_items[$nid][$langcode]) {
|
||||
$translation_status = 'outofdate';
|
||||
}
|
||||
else {
|
||||
$translation_status = 'current';
|
||||
}
|
||||
|
||||
$job_item = NULL;
|
||||
|
||||
if (isset($this->view->field[$this->main_field]->active_job_items[$nid][$langcode])) {
|
||||
$job_item = $this->view->field[$this->main_field]->active_job_items[$nid][$langcode];
|
||||
}
|
||||
|
||||
return theme('tmgmt_ui_translation_language_status_single', array(
|
||||
'translation_status' => $translation_status,
|
||||
'job_item' => $job_item,
|
||||
));
|
||||
}
|
||||
|
||||
function query() {
|
||||
$this->add_additional_fields();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of tmgmt_node_handler_filter_missing_translation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filter by language.
|
||||
*
|
||||
* @ingroup views_filter_handlers
|
||||
*/
|
||||
class tmgmt_node_handler_filter_missing_translation extends views_handler_filter {
|
||||
|
||||
/**
|
||||
* The target status to use for the query.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $target_status = 'untranslated_or_outdated';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function query() {
|
||||
$this->ensure_my_table();
|
||||
|
||||
// Don't do anything if no language was selected.
|
||||
if (!$this->value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$join = new views_join();
|
||||
$join->definition['left_table'] = $this->table_alias;
|
||||
$join->definition['left_field'] = $this->real_field;
|
||||
$join->definition['table'] = 'node';
|
||||
$join->definition['field'] = 'tnid';
|
||||
$join->definition['type'] = 'LEFT';
|
||||
$join->construct();
|
||||
|
||||
$join->extra = array(array(
|
||||
'field' => 'language',
|
||||
'value' => $this->value,
|
||||
));
|
||||
|
||||
$table_alias = $this->query->add_table('node', $this->relationship, $join);
|
||||
|
||||
$this->query->add_where_expression($this->options['group'], "{$this->table_alias}.language != :language", array(':language' => $this->value));
|
||||
|
||||
if ($this->target_status == 'untranslated_or_outdated') {
|
||||
$this->query->add_where_expression($this->options['group'], "($table_alias.nid IS NULL OR {$this->table_alias}.translate = 1)");
|
||||
}
|
||||
elseif ($this->target_status == 'outdated') {
|
||||
$this->query->add_where_expression($this->options['group'], "{$this->table_alias}.translate = 1");
|
||||
}
|
||||
elseif ($this->target_status == 'untranslated') {
|
||||
$this->query->add_where_expression($this->options['group'], "$table_alias.nid IS NULL");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function value_form(&$form, &$form_state) {
|
||||
$options = array();
|
||||
foreach (language_list() as $langcode => $language) {
|
||||
$options[$langcode] = $language->name;
|
||||
}
|
||||
|
||||
$identifier = $this->options['expose']['identifier'];
|
||||
|
||||
$form['value'][$identifier] = array(
|
||||
'#type' => 'select',
|
||||
'#options' => $options,
|
||||
'#empty_option' => t('Any'),
|
||||
'#id' => 'tmgmt_node_missing_target_language',
|
||||
'#element_validate' => array('tmgmt_node_views_exposed_target_language_validate'),
|
||||
);
|
||||
// Attach css to style the target_status element inline.
|
||||
$form['#attached']['css'][] = drupal_get_path('module', 'tmgmt_node_ui') . '/tmgmt_node_ui.source_overview.css';
|
||||
$form['value']['target_status'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Target status'),
|
||||
'#options' => array(
|
||||
'untranslated_or_outdated' => t('Untranslated or outdated'),
|
||||
'untranslated' => t('Untranslated'),
|
||||
'outdated' => t('Outdated'),
|
||||
),
|
||||
'#states' => array(
|
||||
'invisible' => array(
|
||||
':input[id="tmgmt_node_missing_target_language"]' => array('value' => ''),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function accept_exposed_input($input) {
|
||||
$return = parent::accept_exposed_input($input);
|
||||
if ($return && isset($input['target_status'])) {
|
||||
$this->target_status = $input['target_status'];
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains tmgmt_node_ui_handler_filter_node_translatable_types.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Limits node types to those enabled for content translation.
|
||||
*/
|
||||
class tmgmt_node_ui_handler_filter_node_translatable_types extends views_handler_filter {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function query() {
|
||||
$this->ensure_my_table();
|
||||
$valid_types = array_keys(tmgmt_source_translatable_item_types('node'));
|
||||
if ($valid_types) {
|
||||
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field", array_values($valid_types), 'IN');
|
||||
}
|
||||
else {
|
||||
// There are no valid translatable node types, do not return any results.
|
||||
$this->query->add_where_expression($this->options['group'], '1 = 0');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function admin_summary() { }
|
||||
}
|
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Please supply a file description.
|
||||
*/
|
||||
|
||||
class TMGMTNodeSourceViewsController extends TMGMTDefaultSourceViewsController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function views_data() {
|
||||
// Relationships between job items and nodes.
|
||||
$data['tmgmt_job_item']['job_item_to_node'] = array(
|
||||
'title' => t('Content'),
|
||||
'help' => t('Content that is associated with this job item.'),
|
||||
'real field' => 'item_id',
|
||||
'relationship' => array(
|
||||
'label' => t('Content'),
|
||||
'base' => 'node',
|
||||
'base field' => 'vid',
|
||||
'relationship field' => 'item_id',
|
||||
'extra' => array(
|
||||
array(
|
||||
'table' => 'tmgmt_job_item',
|
||||
'field' => 'item_type',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
array(
|
||||
'table' => 'tmgmt_job_item',
|
||||
'field' => 'plugin',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
$data['node']['node_to_job_item'] = array(
|
||||
'title' => t('Translation job item'),
|
||||
'help' => t('Job items of this node.'),
|
||||
'relationship' => array(
|
||||
'real field' => 'vid',
|
||||
'label' => t('Translation job item'),
|
||||
'base' => 'tmgmt_job_item',
|
||||
'base field' => 'item_id',
|
||||
'extra' => array(
|
||||
array(
|
||||
'field' => 'item_type',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
array(
|
||||
'field' => 'plugin',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
$data['node']['tmgmt_translatable_types_all'] = array(
|
||||
'group' => t('Content translation'),
|
||||
'title' => t('All translatable types'),
|
||||
'help' => t('Enforces that only nodes from node types which are translatable are '),
|
||||
'filter' => array(
|
||||
'handler' => 'tmgmt_node_ui_handler_filter_node_translatable_types',
|
||||
'real field' => 'type',
|
||||
),
|
||||
);
|
||||
$data['node']['tmgmt_node_missing_translation'] = array(
|
||||
'group' => t('Content translation'),
|
||||
'title' => t('Missing translation'),
|
||||
'help' => t('Enables the search for nodes with missing translation ofr the specified language'),
|
||||
'filter' => array(
|
||||
'handler' => 'tmgmt_node_handler_filter_missing_translation',
|
||||
'real field' => 'nid',
|
||||
),
|
||||
);
|
||||
$data['node']['tmgmt_jobs'] = array(
|
||||
'title' => t('Translation jobs'),
|
||||
'help' => t('Shows all translation jobs which contains this node'),
|
||||
'field' => array(
|
||||
'handler' => 'tmgmt_node_ui_handler_field_jobs',
|
||||
'real field' => 'nid',
|
||||
),
|
||||
);
|
||||
$data['node']['tmgmt_job_item'] = array(
|
||||
'title' => t('Job item'),
|
||||
'real field' => 'vid',
|
||||
'relationship' => array(
|
||||
'title' => t('Translation job item'),
|
||||
'label' => t('Translation job item'),
|
||||
'base' => 'tmgmt_job_item',
|
||||
'base field' => 'item_id',
|
||||
'extra' => array(
|
||||
array(
|
||||
'field' => 'item_type',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
array(
|
||||
'field' => 'plugin',
|
||||
'operator' => '=',
|
||||
'value' => 'node',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
$data['node']['translation_language_status'] = array(
|
||||
'group' => t('Content translation'),
|
||||
'title' => t('All translation languages'),
|
||||
'help' => t('Display all target lanuages.'),
|
||||
'real field' => 'tnid',
|
||||
'field' => array(
|
||||
'handler' => 'tmgmt_node_handler_field_translation_language_status',
|
||||
),
|
||||
);
|
||||
$data['node']['translation_language_status_single'] = array(
|
||||
'title' => t('All translation languages (single)'),
|
||||
'help' => t("Don't use this in the user interface."),
|
||||
'field' => array(
|
||||
'handler' => 'tmgmt_node_handler_field_translation_language_status_single',
|
||||
),
|
||||
);
|
||||
$data['node']['tmgmt_translatable_types_select'] = array(
|
||||
'group' => t('Content translation'),
|
||||
'title' => t('Select translatable content types'),
|
||||
'help' => t('Allows to filter on specific translatable types.'),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_in_operator',
|
||||
'real field' => 'type',
|
||||
'options callback' => 'tmgmt_source_translatable_item_types',
|
||||
'options arguments' => array($this->pluginType),
|
||||
),
|
||||
);
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
|
||||
$view = new view();
|
||||
$view->name = 'tmgmt_node_source_overview';
|
||||
$view->description = 'Node source overview for bulk operations.';
|
||||
$view->tag = 'Translation Management';
|
||||
$view->base_table = 'node';
|
||||
$view->human_name = 'Node Source Overview';
|
||||
$view->core = 7;
|
||||
$view->api_version = '3.0';
|
||||
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
|
||||
|
||||
/* Display: Master */
|
||||
$handler = $view->new_display('default', 'Master', 'default');
|
||||
$handler->display->display_options['title'] = 'Content overview';
|
||||
$handler->display->display_options['use_more_always'] = FALSE;
|
||||
$handler->display->display_options['group_by'] = TRUE;
|
||||
$handler->display->display_options['access']['type'] = 'perm';
|
||||
$handler->display->display_options['access']['perm'] = 'create translation jobs';
|
||||
$handler->display->display_options['cache']['type'] = 'none';
|
||||
$handler->display->display_options['query']['type'] = 'views_query';
|
||||
$handler->display->display_options['query']['options']['query_comment'] = FALSE;
|
||||
$handler->display->display_options['exposed_form']['type'] = 'basic';
|
||||
$handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
|
||||
$handler->display->display_options['pager']['type'] = 'full';
|
||||
$handler->display->display_options['pager']['options']['items_per_page'] = variable_get('tmgmt_source_list_limit', 20);
|
||||
$handler->display->display_options['pager']['options']['offset'] = '0';
|
||||
$handler->display->display_options['pager']['options']['id'] = '0';
|
||||
$handler->display->display_options['pager']['options']['quantity'] = '9';
|
||||
$handler->display->display_options['style_plugin'] = 'table';
|
||||
$handler->display->display_options['style_options']['columns'] = array(
|
||||
'title' => 'title',
|
||||
);
|
||||
$handler->display->display_options['style_options']['default'] = '-1';
|
||||
$handler->display->display_options['style_options']['info'] = array(
|
||||
'title' => array(
|
||||
'sortable' => 0,
|
||||
'default_sort_order' => 'asc',
|
||||
'align' => '',
|
||||
'separator' => '',
|
||||
'empty_column' => 0,
|
||||
),
|
||||
);
|
||||
/* No results behavior: Global: Text area */
|
||||
$handler->display->display_options['empty']['area']['id'] = 'area';
|
||||
$handler->display->display_options['empty']['area']['table'] = 'views';
|
||||
$handler->display->display_options['empty']['area']['field'] = 'area';
|
||||
$handler->display->display_options['empty']['area']['content'] = 'There are no nodes that match the specified filter criteria.';
|
||||
$handler->display->display_options['empty']['area']['format'] = 'filtered_html';
|
||||
/* Relationship: User */
|
||||
$handler->display->display_options['relationships']['uid']['id'] = 'uid';
|
||||
$handler->display->display_options['relationships']['uid']['table'] = 'node';
|
||||
$handler->display->display_options['relationships']['uid']['field'] = 'uid';
|
||||
$handler->display->display_options['relationships']['uid']['ui_name'] = 'User';
|
||||
$handler->display->display_options['relationships']['uid']['label'] = 'User';
|
||||
/* Field: Bulk operations */
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['id'] = 'views_bulk_operations';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['table'] = 'node';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['field'] = 'views_bulk_operations';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['ui_name'] = 'Bulk operations';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['label'] = '<!--views-bulk-operations-select-all-->';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['element_label_colon'] = FALSE;
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['display_type'] = '1';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['enable_select_all_pages'] = 1;
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['force_single'] = 0;
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['entity_load_capacity'] = '10';
|
||||
$handler->display->display_options['fields']['views_bulk_operations']['vbo_operations'] = array(
|
||||
'rules_component::tmgmt_node_ui_tmgmt_nodes_add_items_to_cart' => array(
|
||||
'selected' => 1,
|
||||
'skip_confirmation' => 1,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_assign_owner_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::views_bulk_operations_delete_item' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::views_bulk_operations_script_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_make_sticky_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_make_unsticky_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::views_bulk_operations_modify_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
'settings' => array(
|
||||
'show_all_tokens' => 1,
|
||||
'display_values' => array(
|
||||
'_all_' => '_all_',
|
||||
),
|
||||
),
|
||||
),
|
||||
'action::views_bulk_operations_argument_selector_action' => array(
|
||||
'selected' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
'settings' => array(
|
||||
'url' => '',
|
||||
),
|
||||
),
|
||||
'action::node_promote_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_publish_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_unpromote_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'rules_component::tmgmt_node_ui_request_translation' => array(
|
||||
'selected' => 0,
|
||||
'skip_confirmation' => 1,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::tmgmt_node_ui_checkout_multiple_action' => array(
|
||||
'selected' => 1,
|
||||
'skip_confirmation' => 1,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_save_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::system_send_email_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_unpublish_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
'action::node_unpublish_by_keyword_action' => array(
|
||||
'selected' => 0,
|
||||
'postpone_processing' => 0,
|
||||
'skip_confirmation' => 0,
|
||||
'override_label' => 0,
|
||||
'label' => '',
|
||||
),
|
||||
);
|
||||
/* Field: Title */
|
||||
$handler->display->display_options['fields']['title']['id'] = 'title';
|
||||
$handler->display->display_options['fields']['title']['table'] = 'node';
|
||||
$handler->display->display_options['fields']['title']['field'] = 'title';
|
||||
$handler->display->display_options['fields']['title']['ui_name'] = 'Title';
|
||||
$handler->display->display_options['fields']['title']['label'] = 'Title (in source language)';
|
||||
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
|
||||
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
|
||||
/* Field: Type */
|
||||
$handler->display->display_options['fields']['type']['id'] = 'type';
|
||||
$handler->display->display_options['fields']['type']['table'] = 'node';
|
||||
$handler->display->display_options['fields']['type']['field'] = 'type';
|
||||
$handler->display->display_options['fields']['type']['ui_name'] = 'Type';
|
||||
/* Field: All translation languages */
|
||||
$handler->display->display_options['fields']['translation_language_status_1']['id'] = 'translation_language_status_1';
|
||||
$handler->display->display_options['fields']['translation_language_status_1']['table'] = 'node';
|
||||
$handler->display->display_options['fields']['translation_language_status_1']['field'] = 'translation_language_status';
|
||||
$handler->display->display_options['fields']['translation_language_status_1']['ui_name'] = 'All translation languages';
|
||||
$handler->display->display_options['fields']['translation_language_status_1']['exclude'] = TRUE;
|
||||
/* Field: Author */
|
||||
$handler->display->display_options['fields']['name']['id'] = 'name';
|
||||
$handler->display->display_options['fields']['name']['table'] = 'users';
|
||||
$handler->display->display_options['fields']['name']['field'] = 'name';
|
||||
$handler->display->display_options['fields']['name']['relationship'] = 'uid';
|
||||
$handler->display->display_options['fields']['name']['ui_name'] = 'Author';
|
||||
$handler->display->display_options['fields']['name']['label'] = 'Author';
|
||||
/* Field: Updated date */
|
||||
$handler->display->display_options['fields']['changed']['id'] = 'changed';
|
||||
$handler->display->display_options['fields']['changed']['table'] = 'node';
|
||||
$handler->display->display_options['fields']['changed']['field'] = 'changed';
|
||||
$handler->display->display_options['fields']['changed']['ui_name'] = 'Updated date';
|
||||
$handler->display->display_options['fields']['changed']['date_format'] = 'short';
|
||||
/* Sort criterion: Post date */
|
||||
$handler->display->display_options['sorts']['created']['id'] = 'created';
|
||||
$handler->display->display_options['sorts']['created']['table'] = 'node';
|
||||
$handler->display->display_options['sorts']['created']['field'] = 'created';
|
||||
$handler->display->display_options['sorts']['created']['ui_name'] = 'Post date';
|
||||
$handler->display->display_options['sorts']['created']['order'] = 'DESC';
|
||||
/* Filter criterion: Content: Title */
|
||||
$handler->display->display_options['filters']['title']['id'] = 'title';
|
||||
$handler->display->display_options['filters']['title']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['title']['field'] = 'title';
|
||||
$handler->display->display_options['filters']['title']['operator'] = 'word';
|
||||
$handler->display->display_options['filters']['title']['group'] = 1;
|
||||
$handler->display->display_options['filters']['title']['exposed'] = TRUE;
|
||||
$handler->display->display_options['filters']['title']['expose']['operator_id'] = 'title_op';
|
||||
$handler->display->display_options['filters']['title']['expose']['label'] = 'Node title';
|
||||
$handler->display->display_options['filters']['title']['expose']['operator'] = 'title_op';
|
||||
$handler->display->display_options['filters']['title']['expose']['identifier'] = 'title';
|
||||
/* Filter criterion: Published */
|
||||
$handler->display->display_options['filters']['status']['id'] = 'status';
|
||||
$handler->display->display_options['filters']['status']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['status']['field'] = 'status';
|
||||
$handler->display->display_options['filters']['status']['ui_name'] = 'Published';
|
||||
$handler->display->display_options['filters']['status']['value'] = '1';
|
||||
$handler->display->display_options['filters']['status']['group'] = 1;
|
||||
$handler->display->display_options['filters']['status']['exposed'] = TRUE;
|
||||
$handler->display->display_options['filters']['status']['expose']['operator_id'] = '';
|
||||
$handler->display->display_options['filters']['status']['expose']['label'] = 'Published';
|
||||
$handler->display->display_options['filters']['status']['expose']['operator'] = 'status_op';
|
||||
$handler->display->display_options['filters']['status']['expose']['identifier'] = 'status';
|
||||
$handler->display->display_options['filters']['status']['expose']['required'] = TRUE;
|
||||
/* Filter criterion: Source translation */
|
||||
$handler->display->display_options['filters']['source_translation']['id'] = 'source_translation';
|
||||
$handler->display->display_options['filters']['source_translation']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['source_translation']['field'] = 'source_translation';
|
||||
$handler->display->display_options['filters']['source_translation']['ui_name'] = 'Source translation';
|
||||
$handler->display->display_options['filters']['source_translation']['operator'] = '1';
|
||||
$handler->display->display_options['filters']['source_translation']['group'] = 1;
|
||||
/* Filter criterion: Content: Language */
|
||||
$handler->display->display_options['filters']['language']['id'] = 'language';
|
||||
$handler->display->display_options['filters']['language']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['language']['field'] = 'language';
|
||||
$handler->display->display_options['filters']['language']['operator'] = 'not in';
|
||||
$handler->display->display_options['filters']['language']['value'] = array(
|
||||
'und' => 'und',
|
||||
);
|
||||
$handler->display->display_options['filters']['language']['group'] = 1;
|
||||
/* Filter criterion: Content: Language */
|
||||
$handler->display->display_options['filters']['language_1']['id'] = 'language_1';
|
||||
$handler->display->display_options['filters']['language_1']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['language_1']['field'] = 'language';
|
||||
$handler->display->display_options['filters']['language_1']['group'] = 1;
|
||||
$handler->display->display_options['filters']['language_1']['exposed'] = TRUE;
|
||||
$handler->display->display_options['filters']['language_1']['expose']['operator_id'] = 'language_1_op';
|
||||
$handler->display->display_options['filters']['language_1']['expose']['label'] = 'Source language';
|
||||
$handler->display->display_options['filters']['language_1']['expose']['operator'] = 'language_1_op';
|
||||
$handler->display->display_options['filters']['language_1']['expose']['identifier'] = 'language_1';
|
||||
/* Filter criterion: Content translation: Select translatable content types */
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['id'] = 'tmgmt_translatable_types_select';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['field'] = 'tmgmt_translatable_types_select';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['exposed'] = TRUE;
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['expose']['operator_id'] = 'tmgmt_translatable_types_select_op';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['expose']['label'] = 'Content type';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['expose']['operator'] = 'tmgmt_translatable_types_select_op';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_select']['expose']['identifier'] = 'tmgmt_translatable_types_select';
|
||||
/* Filter criterion: Content translation: All translatable types */
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_all']['id'] = 'tmgmt_translatable_types_all';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_all']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['tmgmt_translatable_types_all']['field'] = 'tmgmt_translatable_types_all';
|
||||
/* Filter criterion: Content translation: Missing translation */
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['id'] = 'tmgmt_node_missing_translation';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['table'] = 'node';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['field'] = 'tmgmt_node_missing_translation';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['exposed'] = TRUE;
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['expose']['operator_id'] = 'tmgmt_node_missing_translation_op';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['expose']['label'] = 'Target language';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['expose']['operator'] = 'tmgmt_node_missing_translation_op';
|
||||
$handler->display->display_options['filters']['tmgmt_node_missing_translation']['expose']['identifier'] = 'tmgmt_node_missing_translation';
|
||||
|
||||
/* Display: Page */
|
||||
$handler = $view->new_display('page', 'Page', 'page');
|
||||
$handler->display->display_options['path'] = 'admin/tmgmt/sources/node';
|
||||
$handler->display->display_options['menu']['type'] = 'tab';
|
||||
$handler->display->display_options['menu']['title'] = 'Content';
|
||||
$handler->display->display_options['menu']['weight'] = -20;
|
||||
$handler->display->display_options['menu']['context'] = 0;
|
||||
$translatables['tmgmt_node_source_overview'] = array(
|
||||
t('Master'),
|
||||
t('Content overview'),
|
||||
t('more'),
|
||||
t('Search'),
|
||||
t('Reset'),
|
||||
t('Sort by'),
|
||||
t('Asc'),
|
||||
t('Desc'),
|
||||
t('Items per page'),
|
||||
t('- All -'),
|
||||
t('Offset'),
|
||||
t('« first'),
|
||||
t('‹ previous'),
|
||||
t('next ›'),
|
||||
t('last »'),
|
||||
t('There are no nodes that match the specified filter criteria.'),
|
||||
t('User'),
|
||||
t('<!--views-bulk-operations-select-all-->'),
|
||||
t('Title (in source language)'),
|
||||
t('Type'),
|
||||
t('All translation languages'),
|
||||
t('Author'),
|
||||
t('Updated date'),
|
||||
t('Node title'),
|
||||
t('Published'),
|
||||
t('Source language'),
|
||||
t('Content type'),
|
||||
t('Page'),
|
||||
);
|
Reference in New Issue
Block a user