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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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('&#x2611;');
$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'))));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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