updated core to 7.58 (right after the site was hacked)

This commit is contained in:
2018-04-20 23:48:40 +02:00
parent 18f4aba146
commit 9344a61b61
711 changed files with 99690 additions and 480 deletions

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -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,
),
);
}
}

View File

@@ -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".
*/

View File

@@ -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"

View File

@@ -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;
}

View File

@@ -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.');
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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.');
}
}
}
}
}

View File

@@ -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.');
}
}

View File

@@ -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;
}
}

View File

@@ -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"

View File

@@ -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.');
}
}

View File

@@ -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');
}

View File

@@ -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);
}

View File

@@ -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'))));
}
}

View File

@@ -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.')));
}
}
}