updated core to 7.58 (right after the site was hacked)
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xml:lang="de-CH" lang="de-CH" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<meta name="JobID" content="<?php echo $tjid; ?>" />
|
||||
<meta name="languageSource" content="<?php echo $source_language; ?>" />
|
||||
<meta name="languageTarget" content="<?php echo $target_language; ?>" />
|
||||
|
||||
<title>Job ID <?php echo $tjid; ?></title>
|
||||
</head>
|
||||
<body>
|
||||
<?php foreach ($items as $item_key => $item): ?>
|
||||
<div class="asset" id="<?php echo $item_key; ?>">
|
||||
<?php foreach ($item as $field_key => $field): ?>
|
||||
<div class="atom" id="<?php echo $field_key; ?>"><?php echo $field['#text']; ?></div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</body>
|
||||
</html>
|
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* API and hook documentation for the File Translator module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provide information about available file format to export to and import from.
|
||||
*
|
||||
* @return
|
||||
* An array of available file format plugin definitions. The key is the file
|
||||
* extension for that format. It is therefore currently not possible to have
|
||||
* two file formats which share the same file extension as there needs to be
|
||||
* a way to identify them for the import. Each plugin info array then consists
|
||||
* of a label and a plugin controller class, which needs to implement
|
||||
* TMGMTFileFormatInterface.
|
||||
*
|
||||
* @see hook_tmgmt_file_format_plugin_info_alter()
|
||||
*/
|
||||
function hook_tmgmt_file_format_plugin_info() {
|
||||
return array(
|
||||
'xlf' => array(
|
||||
'label' => t('XLIFF'),
|
||||
'plugin controller class' => 'TMGMTFileFormatXLIFF',
|
||||
),
|
||||
'html' => array(
|
||||
'label' => t('HTML'),
|
||||
'plugin controller class' => 'TMGMTFileFormatHTML',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide information about available text processors.
|
||||
*
|
||||
* @return array
|
||||
* An array of available text processor definitions. The key is the text
|
||||
* processor name.
|
||||
*/
|
||||
function hook_tmgmt_file_text_processor_plugin_info() {
|
||||
return array(
|
||||
'mask_html_for_xliff' => array(
|
||||
'label' => t('Escape HTML'),
|
||||
'processor class' => 'TMGMTFileXLIFFMaskHTMLProcessor',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter file format plugins provided by other modules.
|
||||
*
|
||||
* @see hook_tmgmt_file_format_plugin_info()
|
||||
*/
|
||||
function hook_tmgmt_file_format_plugin_info_alter($file_formats) {
|
||||
// Switch the used HTML plugin controller class.
|
||||
$file_formats['html']['plugin controller class'] = 'MyModuleCustomizedHTML';
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Drush integration for tmgmt_file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_drush_command().
|
||||
*/
|
||||
function tmgmt_file_drush_command() {
|
||||
$items = array();
|
||||
|
||||
$items['tmgmt_translate_import'] = array(
|
||||
'description' => 'Import XLIFF translation files',
|
||||
'arguments' => array(
|
||||
'name' => 'Directory path that is search for *.xlf files or a file name',
|
||||
),
|
||||
'aliases' => array('tmti'),
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import XLIFF files from a directory or single file.
|
||||
*/
|
||||
function drush_tmgmt_file_tmgmt_translate_import($name = NULL) {
|
||||
if (!$name) {
|
||||
return drush_set_error(dt('You need to provide a directory path or filename.'));
|
||||
}
|
||||
|
||||
if (!file_exists($name)) {
|
||||
// Drush changes the current working directory to the drupal root directory.
|
||||
// Also check the current directory.
|
||||
if (!file_exists(drush_cwd() . '/' . $name)) {
|
||||
return drush_set_error(dt('@name does not exists or is not accessible.', array('@name' => $name)));
|
||||
}
|
||||
else {
|
||||
// The path is relative to the current directory, update the variable.
|
||||
$name = drush_cwd() . '/' . $name;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dir($name)) {
|
||||
drush_log(dt('Scanning dir @dir.', array('@dir' => $name)), 'success');
|
||||
$files = file_scan_directory($name, '/.*\.xlf$/');
|
||||
if (empty($files)) {
|
||||
drush_set_error(dt('No files found to import in @name.', array('@name' => $name)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Create the structure expected by the loop below.
|
||||
$files = array($name => (object)array('name' => basename($name)));
|
||||
}
|
||||
|
||||
$controller = tmgmt_file_format_controller('xlf');
|
||||
foreach ($files as $path => $info) {
|
||||
$job = $controller->validateImport($path);
|
||||
if (empty($job)) {
|
||||
drush_log(dt('No translation job found for @filename.', array('@filename' => $info->name)), 'error');
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($job->isFinished()) {
|
||||
drush_log(dt('Skipping @filename for finished job @name (#@id).', array('@filename' => $info->name, '@name' => $job->label(), '@id' => $job->tjid)), 'warning');
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Validation successful, start import.
|
||||
$job->addTranslatedData($controller->import($path));
|
||||
drush_log(dt('Successfully imported file @filename for translation job @name (#@id).', array('@filename' => $info->name, '@name' => $job->label(), '@id' => $job->tjid)), 'success');
|
||||
}
|
||||
catch (Exception $e) {
|
||||
drush_log(dt('Failed importing file @filename: @error', array('@filename' => $info->name, '@error' => $e->getMessage())), 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Export into HTML.
|
||||
*/
|
||||
class TMGMTFileFormatHTML implements TMGMTFileFormatInterface {
|
||||
|
||||
/**
|
||||
* Returns base64 encoded data that is safe for use in xml ids.
|
||||
*/
|
||||
protected function encodeIdSafeBase64($data) {
|
||||
// Prefix with a b to enforce that the first character is a letter.
|
||||
return 'b' . rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns decoded id safe base64 data.
|
||||
*/
|
||||
protected function decodeIdSafeBase64($data) {
|
||||
// Remove prefixed b.
|
||||
$data = substr($data, 1);
|
||||
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function export(TMGMTJob $job, $conditions = array()) {
|
||||
$items = array();
|
||||
foreach ($job->getItems($conditions) as $item) {
|
||||
$data = array_filter(tmgmt_flatten_data($item->getData()), '_tmgmt_filter_data');
|
||||
foreach ($data as $key => $value) {
|
||||
$items[$item->tjiid][$this->encodeIdSafeBase64($item->tjiid . '][' . $key)] = $value;
|
||||
}
|
||||
}
|
||||
return theme('tmgmt_file_html_template', array(
|
||||
'tjid' => $job->tjid,
|
||||
'source_language' => $job->getTranslator()->mapToRemoteLanguage($job->source_language),
|
||||
'target_language' => $job->getTranslator()->mapToRemoteLanguage($job->target_language),
|
||||
'items' => $items,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function import($imported_file, $is_file = TRUE) {
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadHTMLFile($imported_file);
|
||||
$xml = simplexml_import_dom($dom);
|
||||
|
||||
$data = array();
|
||||
foreach ($xml->xpath("//div[@class='atom']") as $atom) {
|
||||
// Assets are our strings (eq fields in nodes).
|
||||
$key = $this->decodeIdSafeBase64((string) $atom['id']);
|
||||
$data[$key]['#text'] = (string) $atom;
|
||||
}
|
||||
return tmgmt_unflatten_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateImport($imported_file) {
|
||||
$dom = new DOMDocument();
|
||||
if (!$dom->loadHTMLFile($imported_file)) {
|
||||
return FALSE;
|
||||
}
|
||||
$xml = simplexml_import_dom($dom);
|
||||
|
||||
// Collect meta information.
|
||||
$meta_tags = $xml->xpath('//meta');
|
||||
$meta = array();
|
||||
foreach ($meta_tags as $meta_tag) {
|
||||
$meta[(string) $meta_tag['name']] = (string) $meta_tag['content'];
|
||||
}
|
||||
|
||||
// Check required meta tags.
|
||||
foreach (array('JobID', 'languageSource', 'languageTarget') as $name) {
|
||||
if (!isset($meta[$name])) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to load the job.
|
||||
if (!$job = tmgmt_job_load($meta['JobID'])) {
|
||||
drupal_set_message(t('The imported file job id @file_tjid is not available.', array(
|
||||
'@file_tjid' => $job->tjid,
|
||||
)), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Check language.
|
||||
if ($meta['languageSource'] != $job->getTranslator()->mapToRemoteLanguage($job->source_language) ||
|
||||
$meta['languageTarget'] != $job->getTranslator()->mapToRemoteLanguage($job->target_language)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Validation successful.
|
||||
return $job;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Interface for exporting to a given file format.
|
||||
*/
|
||||
interface TMGMTFileFormatInterface {
|
||||
|
||||
/**
|
||||
* Return the file content for the job data.
|
||||
*
|
||||
* @param $job
|
||||
* The translation job object to be exported.
|
||||
* @param array $conditions
|
||||
* (optional) An array containing list of conditions.
|
||||
*
|
||||
* @return
|
||||
* String with the file content.
|
||||
*/
|
||||
function export(TMGMTJob $job, $conditions = array());
|
||||
|
||||
/**
|
||||
* Validates that the given file is valid and can be imported.
|
||||
*
|
||||
* @todo this function should NOT return a job. We need a import processor
|
||||
* instance instead to deal with the import context.
|
||||
*
|
||||
* @param string $imported_file
|
||||
* File path to the file to be imported.
|
||||
*
|
||||
* @return TMGMTJob
|
||||
* Returns the corresponding translation job entity if the import file is
|
||||
* valid, FALSE otherwise.
|
||||
*/
|
||||
public function validateImport($imported_file);
|
||||
|
||||
/**
|
||||
* Converts an exported file content back to the translated data.
|
||||
*
|
||||
* @param string $imported_file
|
||||
* Path to a file or an XML string to import.
|
||||
* @param bool $is_file
|
||||
* (optional) Whether $imported_file is the path to a file or not.
|
||||
*
|
||||
* @return
|
||||
* Translated data array.
|
||||
*/
|
||||
function import($imported_file, $is_file = TRUE);
|
||||
}
|
@@ -0,0 +1,635 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Export to XLIFF format.
|
||||
*
|
||||
* The XLIFF processor follows this specification:
|
||||
* @link http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2-cd02.html
|
||||
*
|
||||
* The purpose of this class is to mask or process HTML elements in the source
|
||||
* and target elements so that translation tools are able to understand which
|
||||
* content needs to be translated and ignored.
|
||||
*
|
||||
* On the other hand we need to properly unmask the XLIFF markup back to HTML on
|
||||
* the translation import. So the process is bidirectional and prior to running
|
||||
* the unmasking process we try to validate the integrity in the
|
||||
* validateJobTranslationUponImport() method. Currently the integrity check
|
||||
* involves only a counter of XLIFF elements that have been created during
|
||||
* source processing and has to mach number of XLIFF elements being imported
|
||||
* with the translation.
|
||||
*
|
||||
* To process the content DOMDocument object is used due to its ability to
|
||||
* read broken HTML. This also implies that if broken HTML is in the source
|
||||
* content the translation content will be fixed into the extend of DOMDocument
|
||||
* abilities.
|
||||
*
|
||||
* Following is implemented:
|
||||
* - All pair tags get escaped using <bpt><ept> markup.
|
||||
* - <br> tags are marked with <x ctype="lb">.
|
||||
* - <img> tags are marked with <ph ctype="image"> tags. The title and alt
|
||||
* attributes should have been extracted into <sub> elements, however are not
|
||||
* as Trados studio triggers a fatal error in case there are two <sub>
|
||||
* elements at the same level.
|
||||
*
|
||||
* Not implemented:
|
||||
* - Attributes of <img> element are written only as attributes of <ph> element
|
||||
* instead of using x-html: prefix. This results in conflict with own <ph>
|
||||
* element's attributes such as "id". The reason why x-html prefix has not
|
||||
* been used is that Trados studio triggered fatal error on xml validation.
|
||||
* - Translatable attributes like title and alt.
|
||||
* @link http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2-cd02.html#elem_img
|
||||
* - Forms - this is big part
|
||||
* @link http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2-cd02.html#HTMLForms
|
||||
* - <pre> elements
|
||||
* @link http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2-cd02.html#Elem_preformatted
|
||||
*/
|
||||
class TMGMTFileformatXLIFF extends XMLWriter implements TMGMTFileFormatInterface {
|
||||
|
||||
/**
|
||||
* Contains a reference to the currently being exported job.
|
||||
*
|
||||
* @var TMGMTJob
|
||||
*/
|
||||
protected $job;
|
||||
|
||||
protected $importedXML;
|
||||
protected $importedTransUnits;
|
||||
|
||||
/**
|
||||
* Adds a job item to the xml export.
|
||||
*
|
||||
* @param $item
|
||||
* The job item entity.
|
||||
*/
|
||||
protected function addItem(TMGMTJobItem $item) {
|
||||
$this->startElement('group');
|
||||
$this->writeAttribute('id', $item->tjiid);
|
||||
|
||||
// Add a note for the source label.
|
||||
$this->writeElement('note', $item->getSourceLabel());
|
||||
|
||||
// @todo: Write in nested groups instead of flattening it.
|
||||
$data = array_filter(tmgmt_flatten_data($item->getData()), '_tmgmt_filter_data');
|
||||
foreach ($data as $key => $element) {
|
||||
$this->addTransUnit($item->tjiid . '][' . $key, $element, $this->job);
|
||||
}
|
||||
$this->endElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single translation unit for a data element.
|
||||
*
|
||||
* @param $key
|
||||
* The unique identifier for this data element.
|
||||
* @param $element
|
||||
* Array with the properties #text and optionally #label.
|
||||
* @param TMGMTJob $job
|
||||
* Translation job.
|
||||
*/
|
||||
protected function addTransUnit($key, $element, TMGMTJob $job) {
|
||||
|
||||
$key_array = tmgmt_ensure_keys_array($key);
|
||||
|
||||
$this->startElement('trans-unit');
|
||||
$this->writeAttribute('id', $key);
|
||||
$this->writeAttribute('resname', $key);
|
||||
|
||||
$this->startElement('source');
|
||||
$this->writeAttribute('xml:lang', $this->job->getTranslator()->mapToRemoteLanguage($this->job->source_language));
|
||||
|
||||
if ($job->getSetting('xliff_cdata')) {
|
||||
$this->writeCdata(trim($element['#text']));
|
||||
}
|
||||
elseif ($job->getSetting('xliff_processing')) {
|
||||
$this->writeRaw($this->processForExport($element['#text'], $key_array));
|
||||
}
|
||||
else {
|
||||
$this->text($element['#text']);
|
||||
}
|
||||
|
||||
$this->endElement();
|
||||
$this->startElement('target');
|
||||
$this->writeAttribute('xml:lang', $this->job->getTranslator()->mapToRemoteLanguage($this->job->target_language));
|
||||
|
||||
if (!empty($element['#translation']['#text'])) {
|
||||
if ($job->getSetting('xliff_processing')) {
|
||||
$this->writeRaw($this->processForExport($element['#translation']['#text'], $key_array));
|
||||
}
|
||||
else {
|
||||
$this->text($element['#translation']['#text']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->endElement();
|
||||
if (isset($element['#label'])) {
|
||||
$this->writeElement('note', $element['#label']);
|
||||
}
|
||||
$this->endElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function export(TMGMTJob $job, $conditions = array()) {
|
||||
|
||||
$this->job = $job;
|
||||
|
||||
$this->openMemory();
|
||||
$this->setIndent(true);
|
||||
$this->setIndentString(' ');
|
||||
$this->startDocument('1.0', 'UTF-8');
|
||||
|
||||
// Root element with schema definition.
|
||||
$this->startElement('xliff');
|
||||
$this->writeAttribute('version', '1.2');
|
||||
$this->writeAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2');
|
||||
$this->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
|
||||
$this->writeAttribute('xsi:schemaLocation', 'urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-strict.xsd');
|
||||
|
||||
// File element.
|
||||
$this->startElement('file');
|
||||
$this->writeAttribute('original', 'xliff-core-1.2-strict.xsd');
|
||||
$this->writeAttribute('source-language', $job->getTranslator()->mapToRemoteLanguage($job->source_language));
|
||||
$this->writeAttribute('target-language', $job->getTranslator()->mapToRemoteLanguage($job->target_language));
|
||||
$this->writeAttribute('datatype', 'plaintext');
|
||||
// Date needs to be in ISO-8601 UTC
|
||||
$this->writeAttribute('date', date('Y-m-d\Th:m:i\Z'));
|
||||
|
||||
$this->startElement('header');
|
||||
$this->startElement('phase-group');
|
||||
$this->startElement('phase');
|
||||
$this->writeAttribute('tool-id', 'tmgmt');
|
||||
$this->writeAttribute('phase-name', 'extraction');
|
||||
$this->writeAttribute('process-name', 'extraction');
|
||||
$this->writeAttribute('job-id', $job->tjid);
|
||||
|
||||
$this->endElement();
|
||||
$this->endElement();
|
||||
$this->startElement('tool');
|
||||
$this->writeAttribute('tool-id', 'tmgmt');
|
||||
$this->writeAttribute('tool-name', 'Drupal Translation Management Tools');
|
||||
$this->endElement();
|
||||
$this->endElement();
|
||||
|
||||
$this->startElement('body');
|
||||
|
||||
foreach ($job->getItems($conditions) as $item) {
|
||||
$this->addItem($item);
|
||||
}
|
||||
|
||||
// End the body, file and xliff tags.
|
||||
$this->endElement();
|
||||
$this->endElement();
|
||||
$this->endElement();
|
||||
$this->endDocument();
|
||||
return $this->outputMemory();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function import($imported_file, $is_file = TRUE) {
|
||||
if (!$this->getImportedXML($imported_file, $is_file)) {
|
||||
return FALSE;
|
||||
}
|
||||
$phase = $this->importedXML->xpath("//xliff:phase[@phase-name='extraction']");
|
||||
$phase = reset($phase);
|
||||
$job = tmgmt_job_load((string) $phase['job-id']);
|
||||
return tmgmt_unflatten_data($this->getImportedTargets($job));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateImport($imported_file) {
|
||||
// Validates imported XLIFF file.
|
||||
// Checks:
|
||||
// - Job ID
|
||||
// - Target ans source languages
|
||||
// - Content integrity.
|
||||
|
||||
if (!($xml = $this->getImportedXML($imported_file))) {
|
||||
drupal_set_message(t('The imported file is not a valid XML.'), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
// Check if our phase information is there.
|
||||
$phase = $xml->xpath("//xliff:phase[@phase-name='extraction']");
|
||||
if ($phase) {
|
||||
$phase = reset($phase);
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('The imported file is missing required XLIFF phase information.'), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Check if the job has a valid job reference.
|
||||
if (!isset($phase['job-id'])) {
|
||||
drupal_set_message(t('The imported file does not contain a job reference.'), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Attempt to load the job if none passed.
|
||||
$job = tmgmt_job_load((int) $phase['job-id']);
|
||||
if (empty($job)) {
|
||||
drupal_set_message(t('The imported file job id @file_tjid is not available.', array(
|
||||
'@file_tjid' => $phase['job-id'],
|
||||
)), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// @todo We use the $job to addMessage in case of failure. However the job
|
||||
// context is not safe at this point.
|
||||
|
||||
// Compare source language.
|
||||
if (!isset($xml->file['source-language']) || $job->getTranslator()->mapToRemoteLanguage($job->source_language) != $xml->file['source-language']) {
|
||||
$job->addMessage('The imported file source language @file_language does not match the job source language @job_language.', array(
|
||||
'@file_language' => empty($xml->file['source-language']) ? t('none') : $xml->file['source-language'],
|
||||
'@job_language' => $job->source_language,
|
||||
), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Compare target language.
|
||||
if (!isset($xml->file['target-language']) || $job->getTranslator()->mapToRemoteLanguage($job->target_language) != $xml->file['target-language']) {
|
||||
$job->addMessage('The imported file target language @file_language does not match the job target language @job_language.', array(
|
||||
'@file_language' => empty($xml->file['target-language']) ? t('none') : $xml->file['target-language'],
|
||||
'@job_language' => $job->target_language,
|
||||
), 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$targets = $this->getImportedTargets($job);
|
||||
|
||||
if (empty($targets)) {
|
||||
$job->addMessage('The imported file seems to be missing translation.', 'error');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// In case we do not do xliff processing we cannot do the elements
|
||||
// count validation.
|
||||
if (!$job->getSetting('xliff_processing')) {
|
||||
return $job;
|
||||
}
|
||||
|
||||
$reader = new XMLReader();
|
||||
$xliff_validation = $job->getSetting('xliff_validation');
|
||||
|
||||
foreach ($targets as $id => $target) {
|
||||
$array_key = tmgmt_ensure_keys_array($id);
|
||||
$job_item = tmgmt_job_item_load(array_shift($array_key));
|
||||
$count = 0;
|
||||
$reader->XML('<translation>' . $target['#text'] . '</translation>');
|
||||
while ($reader->read()) {
|
||||
if (in_array($reader->name, array('translation', '#text'))) {
|
||||
continue;
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
|
||||
if (!isset($xliff_validation[$id]) || $xliff_validation[$id] != $count) {
|
||||
$job_item->addMessage('Failed to validate semantic integrity of %key element. Please check also the HTML code of the element in the review process.',
|
||||
array('%key' => tmgmt_ensure_keys_string($array_key)));
|
||||
}
|
||||
}
|
||||
|
||||
// Validation successful.
|
||||
return $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the simple XMLElement object.
|
||||
*
|
||||
* @param string $imported_file
|
||||
* Path to a file or an XML string to import.
|
||||
* @param bool $is_file
|
||||
* (optional) Whether $imported_file is the path to a file or not.
|
||||
*
|
||||
* @return bool|\SimpleXMLElement
|
||||
* The parsed SimpleXMLElement object. FALSE in case of failed parsing.
|
||||
*/
|
||||
protected function getImportedXML($imported_file, $is_file = TRUE) {
|
||||
if (empty($this->importedXML)) {
|
||||
// It is not possible to load the file directly with simplexml as it gets
|
||||
// url encoded due to the temporary://. This is a PHP bug, see
|
||||
// https://bugs.php.net/bug.php?id=61469
|
||||
if ($is_file) {
|
||||
$imported_file = file_get_contents($imported_file);
|
||||
}
|
||||
|
||||
if (!($this->importedXML = simplexml_load_string($imported_file))) {
|
||||
return FALSE;
|
||||
}
|
||||
// Register the XLIFF namespace, required for xpath.
|
||||
$this->importedXML->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2');
|
||||
}
|
||||
|
||||
return $this->importedXML;
|
||||
}
|
||||
|
||||
protected function getImportedTargets(TMGMTJob $job) {
|
||||
if (empty($this->importedXML)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (empty($this->importedTransUnits)) {
|
||||
$reader = new XMLReader();
|
||||
foreach ($this->importedXML->xpath('//xliff:trans-unit') as $unit) {
|
||||
if (!$job->getSetting('xliff_processing')) {
|
||||
$this->importedTransUnits[(string) $unit['id']]['#text'] = (string) $unit->target;
|
||||
continue;
|
||||
}
|
||||
|
||||
$reader->XML($unit->target->asXML());
|
||||
$reader->read();
|
||||
$this->importedTransUnits[(string) $unit['id']]['#text'] =
|
||||
$this->processForImport($reader->readInnerXML(), $job);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->importedTransUnits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes trans-unit/target to rebuild back the HTML.
|
||||
*
|
||||
* @param string $translation
|
||||
* Job data array.
|
||||
* @param TMGMTJob $job
|
||||
* Translation job.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function processForImport($translation, TMGMTJob $job) {
|
||||
// In case we do not want to do xliff processing return the translation as
|
||||
// is.
|
||||
if (!$job->getSetting('xliff_processing')) {
|
||||
return $translation;
|
||||
}
|
||||
|
||||
$reader = new XMLReader();
|
||||
$reader->XML('<translation>' . $translation . '</translation>');
|
||||
$text = '';
|
||||
|
||||
while ($reader->read()) {
|
||||
// If the current element is text append it to the result text.
|
||||
if ($reader->name == '#text' || $reader->name == '#cdata-section') {
|
||||
$text .= $reader->value;
|
||||
}
|
||||
elseif ($reader->name == 'x') {
|
||||
if ($reader->getAttribute('ctype') == 'lb') {
|
||||
$text .= '<br />';
|
||||
}
|
||||
}
|
||||
elseif ($reader->name == 'ph') {
|
||||
if ($reader->getAttribute('ctype') == 'image') {
|
||||
$text .= '<img';
|
||||
while ($reader->moveToNextAttribute()) {
|
||||
// @todo - we have to use x-html: prefixes for attributes.
|
||||
if ($reader->name != 'ctype' && $reader->name != 'id') {
|
||||
$text .= " {$reader->name}=\"{$reader->value}\"";
|
||||
}
|
||||
}
|
||||
$text .= ' />';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to process the source text.
|
||||
*
|
||||
* @param string $source
|
||||
* Job data array.
|
||||
* @param array $key_array
|
||||
* The source item data key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function processForExport($source, array $key_array) {
|
||||
$tjiid = $key_array[0];
|
||||
$key_string = tmgmt_ensure_keys_string($key_array);
|
||||
// The reason why we use DOMDocument object here and not just XMLReader
|
||||
// is the DOMDocument's ability to deal with broken HTML.
|
||||
$dom = new DOMDocument();
|
||||
// We need to append the head with encoding so that special characters
|
||||
// are read correctly.
|
||||
$dom->loadHTML("<html><head><meta http-equiv='Content-type' content='text/html; charset=UTF-8' /></head><body>" . $source . '</body></html>');
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDOMIterator($dom),
|
||||
RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
$writer = new XMLWriter();
|
||||
$writer->openMemory();
|
||||
$writer->startDocument('1.0', 'UTF-8');
|
||||
$writer->startElement('wrapper');
|
||||
|
||||
$tray = array();
|
||||
$non_pair_tags = array('br', 'img');
|
||||
|
||||
if (!isset($this->job->settings['xliff_validation'])) {
|
||||
$this->job->settings['xliff_validation'] = array();
|
||||
}
|
||||
$xliff_validation = $this->job->settings['xliff_validation'];
|
||||
|
||||
/** @var DOMElement $node */
|
||||
foreach ($iterator as $node) {
|
||||
|
||||
if (in_array($node->nodeName, array('html', 'body', 'head', 'meta'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($node->nodeType === XML_ELEMENT_NODE) {
|
||||
// Increment the elements count and compose element id.
|
||||
if (!isset($xliff_validation[$key_string])) {
|
||||
$xliff_validation[$key_string] = 0;
|
||||
}
|
||||
$xliff_validation[$key_string]++;
|
||||
$id = 'tjiid' . $tjiid . '-' . $xliff_validation[$key_string];
|
||||
|
||||
$is_pair_tag = !in_array($node->nodeName, $non_pair_tags);
|
||||
|
||||
if ($is_pair_tag) {
|
||||
$this->writeBPT($writer, $node, $id);
|
||||
}
|
||||
elseif ($node->nodeName == 'img') {
|
||||
$this->writeIMG($writer, $node, $id);
|
||||
}
|
||||
elseif ($node->nodeName == 'br') {
|
||||
$this->writeBR($writer, $node, $id);
|
||||
}
|
||||
|
||||
// Add to tray new element info.
|
||||
$tray[$id] = array(
|
||||
'name' => $node->nodeName,
|
||||
'id' => $id,
|
||||
'value' => $node->nodeValue,
|
||||
'built_text' => '',
|
||||
'is_pair_tag' => $is_pair_tag,
|
||||
);
|
||||
|
||||
}
|
||||
// The current node is a text.
|
||||
elseif ($node->nodeName == '#text') {
|
||||
// Add the node value to the text output.
|
||||
$writer->writeCdata($this->toEntities($node->nodeValue));
|
||||
foreach ($tray as &$info) {
|
||||
$info['built_text'] .= $node->nodeValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse so that pair tags are closed in the expected order.
|
||||
$reversed_tray = array_reverse($tray);
|
||||
foreach ($reversed_tray as $_info) {
|
||||
// If the build_text equals to the node value and it is not a pair tag
|
||||
// add the end pair tag markup.
|
||||
if ($_info['value'] == $_info['built_text'] && $_info['is_pair_tag']) {
|
||||
// Count also for the closing elements.
|
||||
$xliff_validation[$key_string]++;
|
||||
$this->writeEPT($writer, $_info['name'], $_info['id']);
|
||||
// When the end pair tag has been written unset the element info
|
||||
// from the tray.
|
||||
unset($tray[$_info['id']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the xliff_validation data and save the job.
|
||||
$this->job->settings['xliff_validation'] = $xliff_validation;
|
||||
$this->job->save();
|
||||
|
||||
$writer->endElement();
|
||||
// Load the output with XMLReader so that we can easily get the inner xml.
|
||||
$reader = new XMLReader();
|
||||
$reader->XML($writer->outputMemory());
|
||||
$reader->read();
|
||||
return $reader->readInnerXML();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes br tag.
|
||||
*
|
||||
* @param XMLWriter $writer
|
||||
* Writer that writes the output.
|
||||
* @param DOMElement $node
|
||||
* Current node.
|
||||
* @param $id
|
||||
* Current node id.
|
||||
*/
|
||||
protected function writeBR(XMLWriter $writer, DOMElement $node, $id) {
|
||||
$writer->startElement('x');
|
||||
$writer->writeAttribute('id', $id);
|
||||
$writer->writeAttribute('ctype', 'lb');
|
||||
$writer->endElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes beginning pair tag.
|
||||
*
|
||||
* @param XMLWriter $writer
|
||||
* Writer that writes the output.
|
||||
* @param DOMElement $node
|
||||
* Current node.
|
||||
* @param $id
|
||||
* Current node id.
|
||||
*/
|
||||
protected function writeBPT(XMLWriter $writer, DOMElement $node, $id) {
|
||||
$beginning_tag = '<' . $node->nodeName;
|
||||
if ($node->hasAttributes()) {
|
||||
$attributes = array();
|
||||
/** @var DOMAttr $attribute */
|
||||
foreach ($node->attributes as $attribute) {
|
||||
$attributes[] = $attribute->name . '="' . $attribute->value . '"';
|
||||
}
|
||||
|
||||
$beginning_tag .= ' '. implode(' ', $attributes);
|
||||
}
|
||||
$beginning_tag .= '>';
|
||||
$writer->startElement('bpt');
|
||||
$writer->writeAttribute('id', $id);
|
||||
$writer->text($beginning_tag);
|
||||
$writer->endElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes ending pair tag.
|
||||
*
|
||||
* @param XMLWriter $writer
|
||||
* Writer that writes the output.
|
||||
* @param string $name
|
||||
* Ending tag name.
|
||||
* @param $id
|
||||
* Current node id.
|
||||
*/
|
||||
protected function writeEPT(XMLWriter $writer, $name, $id) {
|
||||
$writer->startElement('ept');
|
||||
$writer->writeAttribute('id', $id);
|
||||
$writer->text('</' . $name . '>');
|
||||
$writer->endElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes img tag.
|
||||
*
|
||||
* Note that alt and title attributes are not written as sub elements as
|
||||
* Trados studio is not able to deal with two sub elements at one level.
|
||||
*
|
||||
* @param XMLWriter $writer
|
||||
* Writer that writes the output.
|
||||
* @param DOMElement $node
|
||||
* Current node.
|
||||
* @param $id
|
||||
* Current node id.
|
||||
*/
|
||||
protected function writeIMG(XMLWriter $writer, DOMElement $node, $id) {
|
||||
$writer->startElement('ph');
|
||||
$writer->writeAttribute('id', $id);
|
||||
$writer->writeAttribute('ctype', 'image');
|
||||
foreach ($node->attributes as $attribute) {
|
||||
// @todo - uncomment when issue with Trados/sub elements fixed.
|
||||
/*
|
||||
if (in_array($attribute->name, array('title', 'alt'))) {
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
$writer->writeAttribute($attribute->name, $attribute->value);
|
||||
}
|
||||
/*
|
||||
if ($alt_attribute = $node->getAttribute('alt')) {
|
||||
$writer->startElement('sub');
|
||||
$writer->writeAttribute('id', $id . '-img-alt');
|
||||
$writer->writeAttribute('ctype', 'x-img-alt');
|
||||
$writer->text($alt_attribute);
|
||||
$writer->endElement();
|
||||
$this->elementsCount++;
|
||||
}
|
||||
if ($title_attribute = $node->getAttribute('title')) {
|
||||
$writer->startElement('sub');
|
||||
$writer->writeAttribute('id', $id . '-img-title');
|
||||
$writer->writeAttribute('ctype', 'x-img-title');
|
||||
$writer->text($title_attribute);
|
||||
$writer->endElement();
|
||||
$this->elementsCount++;
|
||||
}
|
||||
*/
|
||||
$writer->endElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert critical characters to HTML entities.
|
||||
*
|
||||
* DOMDocument will convert HTML entities to its actual characters. This can
|
||||
* lead into situation when not allowed characters will appear in the content.
|
||||
*
|
||||
* @param string $string
|
||||
* String to escape.
|
||||
*
|
||||
* @return string
|
||||
* Escaped string.
|
||||
*/
|
||||
protected function toEntities($string) {
|
||||
return str_replace(array('&', '>', '<'), array('&', '>', '<'), $string);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
name = Export / Import File
|
||||
description = A translator which allows you to export source data into a file and import the translated in return.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
dependencies[] = tmgmt
|
||||
configure = admin/config/regional/tmgmt_translator
|
||||
files[] = tmgmt_file.plugin.inc
|
||||
files[] = tmgmt_file.ui.inc
|
||||
files[] = tmgmt_file.format.interface.inc
|
||||
files[] = tmgmt_file.format.xliff.inc
|
||||
files[] = tmgmt_file.format.html.inc
|
||||
files[] = tmgmt_file.recursive_iterator.inc
|
||||
files[] = tmgmt_file.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Module file of the translation management test module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_translator_plugin_info().
|
||||
*/
|
||||
function tmgmt_file_tmgmt_translator_plugin_info() {
|
||||
return array(
|
||||
'file' => array(
|
||||
'label' => t('File translator'),
|
||||
'description' => t('File translator that exports and imports files.'),
|
||||
'plugin controller class' => 'TMGMTFileTranslatorPluginController',
|
||||
'ui controller class' => 'TMGMTFileTranslatorUIController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function tmgmt_file_theme() {
|
||||
return array(
|
||||
'tmgmt_file_html_template' => array(
|
||||
'path' => drupal_get_path('module', 'tmgmt_file') . '/templates',
|
||||
'template' => 'tmgmt_file_html_template',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import form submit callback.
|
||||
*/
|
||||
function tmgmt_file_import_form_submit($form, &$form_state) {
|
||||
// Ensure we have the file uploaded.
|
||||
$job = $form_state['tmgmt_job'];
|
||||
$supported_formats = array_keys(tmgmt_file_format_plugin_info());
|
||||
if ($file = file_save_upload('file', array('file_validate_extensions' => array(implode(' ', $supported_formats))))) {
|
||||
$extension = pathinfo($file->uri, PATHINFO_EXTENSION);
|
||||
$controller = tmgmt_file_format_controller($extension);
|
||||
if ($controller) {
|
||||
// Validate the file on job.
|
||||
$validated_job = $controller->validateImport($file->uri, $job);
|
||||
if (!$validated_job) {
|
||||
$job->addMessage('Failed to validate file, import aborted.', array(), 'error');
|
||||
}
|
||||
elseif ($validated_job->tjid != $job->tjid) {
|
||||
$job->addMessage('The imported file job id @file_tjid does not match the job id @job_tjid.', array(
|
||||
'@file_tjid' => $validated_job->tjid,
|
||||
'@job_tjid' => $job->tjid,
|
||||
), 'error');
|
||||
}
|
||||
else {
|
||||
try {
|
||||
// Validation successful, start import.
|
||||
$job->addTranslatedData($controller->import($file->uri));
|
||||
$job->addMessage('Successfully imported file.');
|
||||
} catch (Exception $e) {
|
||||
$job->addMessage('File import failed with the following message: @message', array('@message' => $e->getMessage()), 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($job->getMessagesSince() as $message) {
|
||||
// Ignore debug messages.
|
||||
if ($message->type == 'debug') {
|
||||
continue;
|
||||
}
|
||||
if ($text = $message->getMessage()) {
|
||||
drupal_set_message(filter_xss($text), $message->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about file format plugins.
|
||||
*
|
||||
* @param $plugin
|
||||
* (Optional) Name of a plugin/extension.
|
||||
*
|
||||
* @return array
|
||||
* If a plugin name is provided, information about that plugin, an array of
|
||||
* plugin information otherwise. The information of each plugin consists of
|
||||
* the label and plugin controller class, keyed by the plugin name which is
|
||||
* also the extension for that file format.
|
||||
*/
|
||||
function tmgmt_file_format_plugin_info($plugin = NULL) {
|
||||
return _tmgmt_plugin_info('file_format', $plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of file format plugin labels.
|
||||
*/
|
||||
function tmgmt_file_format_plugin_labels() {
|
||||
return _tmgmt_plugin_labels('file_format');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file format plugin controller.
|
||||
*
|
||||
* @param $plugin
|
||||
* (Optional) Name of a plugin/extension.
|
||||
*
|
||||
* @return TMGMTFileFormatInterface
|
||||
* Either a specific file format plugin controller instance or an array of
|
||||
* available controllers.
|
||||
*/
|
||||
function tmgmt_file_format_controller($plugin = NULL) {
|
||||
return _tmgmt_plugin_controller('file_format', $plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_file_format_info().
|
||||
*/
|
||||
function tmgmt_file_tmgmt_file_format_plugin_info() {
|
||||
return array(
|
||||
'xlf' => array(
|
||||
'label' => t('XLIFF'),
|
||||
'plugin controller class' => 'TMGMTFileFormatXLIFF',
|
||||
),
|
||||
'html' => array(
|
||||
'label' => t('HTML'),
|
||||
'plugin controller class' => 'TMGMTFileFormatHTML',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_job_delete().
|
||||
*/
|
||||
function tmgmt_file_tmgmt_job_delete(TMGMTJob $job) {
|
||||
$translator = $job->getTranslator();
|
||||
|
||||
// Ignore jobs that don't have a file translator.
|
||||
if (!$translator || $translator->plugin != 'file') {
|
||||
return;
|
||||
}
|
||||
// Check if there are any files that need to be deleted.
|
||||
// @todo There doesn't seem to be an API function for this...
|
||||
$args = array(
|
||||
':module' => 'tmgmt_file',
|
||||
':type' => 'tmgmt_job',
|
||||
':id' => $job->tjid,
|
||||
);
|
||||
$result = db_query('SELECT fid FROM {file_usage} WHERE module = :module and type = :type and id = :id', $args);
|
||||
$fids = $result->fetchCol();
|
||||
if (!empty($fids)) {
|
||||
foreach (file_load_multiple($fids) as $file) {
|
||||
file_usage_delete($file, 'tmgmt_file', 'tmgmt_job', $job->tjid);
|
||||
// It is very unlikely that these files are used anywhere else. Delete it.
|
||||
file_delete($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_file_download().
|
||||
*/
|
||||
function tmgmt_file_file_download($uri) {
|
||||
// Get the file record based on the URI. If not in the database just return.
|
||||
$files = file_load_multiple(array(), array('uri' => $uri));
|
||||
if (count($files)) {
|
||||
foreach ($files as $item) {
|
||||
// Since some database servers sometimes use a case-insensitive comparison
|
||||
// by default, double check that the filename is an exact match.
|
||||
if ($item->uri === $uri) {
|
||||
$file = $item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset($file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this file belongs to a job.
|
||||
$usage_list = file_usage_list($file);
|
||||
if (!isset($usage_list['tmgmt_file']['tmgmt_job'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (tmgmt_job_load_multiple(array_keys($usage_list['tmgmt_file']['tmgmt_job'])) as $job) {
|
||||
if (tmgmt_job_access('view', $job)) {
|
||||
// Access is granted.
|
||||
$headers = file_get_content_headers($file);
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
|
||||
// Returning nothing means access denied unless another module specifically
|
||||
// grants access.
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the file translator plugin controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* File translator plugin controller.
|
||||
*/
|
||||
class TMGMTFileTranslatorPluginController extends TMGMTDefaultTranslatorPluginController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function canTranslate(TMGMTTranslator $translator, TMGMTJob $job) {
|
||||
// Anything can be exported.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function requestTranslation(TMGMTJob $job) {
|
||||
$name = "JobID" . $job->tjid . '_' . $job->source_language . '_' . $job->target_language;
|
||||
|
||||
$export = tmgmt_file_format_controller($job->getSetting('export_format'));
|
||||
|
||||
$path = $job->getSetting('scheme') . '://tmgmt_file/' . $name . '.' . $job->getSetting('export_format');
|
||||
$dirname = dirname($path);
|
||||
if (file_prepare_directory($dirname, FILE_CREATE_DIRECTORY)) {
|
||||
$file = file_save_data($export->export($job), $path);
|
||||
file_usage_add($file, 'tmgmt_file', 'tmgmt_job', $job->tjid);
|
||||
$job->submitted('Exported file can be downloaded <a href="!link">here</a>.', array('!link' => file_create_url($file->uri)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCheckoutSettings(TMGMTJob $job) {
|
||||
return $job->getTranslator()->getSetting('allow_override');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultSettings() {
|
||||
return array(
|
||||
'export_format' => 'xlf',
|
||||
'allow_override' => TRUE,
|
||||
'scheme' => 'public',
|
||||
// Making this setting TRUE by default is more appropriate, however we
|
||||
// need to make it FALSE due to backwards compatibility.
|
||||
'xliff_processing' => FALSE,
|
||||
'xliff_cdata' => FALSE,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains RecursiveDOMIterator.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class used to iterate through DOMDocument.
|
||||
*/
|
||||
class RecursiveDOMIterator implements RecursiveIterator {
|
||||
/**
|
||||
* Current position in DOMNodeList.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $position;
|
||||
|
||||
/**
|
||||
* The DOMNodeList with all children to iterate over.
|
||||
*
|
||||
* @var DOMNodeList
|
||||
*/
|
||||
protected $nodeList;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param DOMNode $domNode
|
||||
* DOMNode to iterate over.
|
||||
*/
|
||||
public function __construct(DOMNode $domNode) {
|
||||
$this->position = 0;
|
||||
$this->nodeList = $domNode->childNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current DOMNode.
|
||||
*
|
||||
* @return DOMNode
|
||||
* Current DOMNode object.
|
||||
*/
|
||||
public function current() {
|
||||
return $this->nodeList->item($this->position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator for the current iterator entry.
|
||||
*
|
||||
* @return RecursiveDOMIterator
|
||||
* Iterator with children elements.
|
||||
*/
|
||||
public function getChildren() {
|
||||
return new self($this->current());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current element has children.
|
||||
*
|
||||
* @return bool
|
||||
* Has children.
|
||||
*/
|
||||
public function hasChildren() {
|
||||
return $this->current()->hasChildNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current position.
|
||||
*
|
||||
* @return int
|
||||
* Current position
|
||||
*/
|
||||
public function key() {
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the current position to the next element.
|
||||
*/
|
||||
public function next() {
|
||||
$this->position++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the Iterator to the first element.
|
||||
*/
|
||||
public function rewind() {
|
||||
$this->position = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid.
|
||||
*
|
||||
* @return bool
|
||||
* Is valid.
|
||||
*/
|
||||
public function valid() {
|
||||
return $this->position < $this->nodeList->length;
|
||||
}
|
||||
}
|
@@ -0,0 +1,536 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test cases for the file translator module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic tests for the file translator.
|
||||
*/
|
||||
class TMGMTFileTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'File Translator tests',
|
||||
'description' => 'Tests the file translator plugin integration.',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_file', 'tmgmt_ui'));
|
||||
$this->loginAsAdmin();
|
||||
$this->setEnvironment('de');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the content processing for XLIFF export and import.
|
||||
*/
|
||||
function testXLIFFTextProcessing() {
|
||||
$translator = $this->createTranslator();
|
||||
$translator->plugin = 'file';
|
||||
$translator->settings = array(
|
||||
'export_format' => 'xlf',
|
||||
'xliff_processing' => TRUE,
|
||||
);
|
||||
$translator->save();
|
||||
|
||||
// Get the source text.
|
||||
$source_text = trim(file_get_contents(drupal_get_path('module', 'tmgmt') . '/tests/testing_html/sample.html'));
|
||||
|
||||
// Create the reader instance, it will be used through the tests.
|
||||
$reader = new XMLReader();
|
||||
$xliff_elements = array('bpt', 'ept', 'ph', 'x', '#text', '#cdata-section', 'content');
|
||||
|
||||
// ==== First test the whole cycle ==== //
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$job->addItem('test_html_source', 'test', '1');
|
||||
|
||||
// Requesting translation will mask the html.
|
||||
$job->requestTranslation();
|
||||
$content = $this->getTransUnitsContent($job);
|
||||
// Test that the exported trans unit contains only xliff elements.
|
||||
$reader->XML('<content>' . $content[0]['source'] . '</content>');
|
||||
while ($reader->read()) {
|
||||
if (!in_array($reader->name, $xliff_elements)) {
|
||||
$this->fail(t('The source contains unexpected element %element', array('%element' => $reader->name)));
|
||||
}
|
||||
}
|
||||
$reader->XML('<content>' . $content[0]['target'] . '</content>');
|
||||
while ($reader->read()) {
|
||||
if (!in_array($reader->name, $xliff_elements)) {
|
||||
$this->fail(t('The target contains unexpected element %element', array('%element' => $reader->name)));
|
||||
}
|
||||
}
|
||||
|
||||
// Import the file, make sure all the html has been revealed and no xliff
|
||||
// elements are present in the job translation.
|
||||
$messages = $job->getMessages();
|
||||
$message = reset($messages);
|
||||
$translated_file = 'public://tmgmt_file/translated.xlf';
|
||||
$this->createTranslationFile($message->variables['!link'], 'one paragraph', 'one translated paragraph', $translated_file);
|
||||
$uri = $job->uri();
|
||||
$edit = array(
|
||||
'files[file]' => $translated_file,
|
||||
);
|
||||
$this->drupalPost($uri['path'] . '/manage', $edit, t('Import'));
|
||||
// Reset caches and reload job.
|
||||
entity_get_controller('tmgmt_job')->resetCache();
|
||||
entity_get_controller('tmgmt_job_item')->resetCache();
|
||||
$job = tmgmt_job_load($job->tjid);
|
||||
|
||||
// Do the comparison of the translation text and the source. It must be the
|
||||
// same as there was no change done to the translation.
|
||||
$item_data = $job->getData(array(1, 'dummy', 'deep_nesting'));
|
||||
$this->assertEqual(trim($item_data[1]['#translation']['#text']), str_replace('one paragraph', 'one translated paragraph', $source_text));
|
||||
$job_items = $job->getItems();
|
||||
/** @var TMGMTJobItem $job_item */
|
||||
$job_item = array_shift($job_items);
|
||||
// Job item must be in review.
|
||||
$this->assertTrue($job_item->isNeedsReview());
|
||||
|
||||
$this->assertIntegrityCheck($job, FALSE);
|
||||
|
||||
// ==== Test integrity check ==== //
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$job->addItem('test_html_source', 'test', '1');
|
||||
$job->requestTranslation();
|
||||
|
||||
$messages = $job->getMessages();
|
||||
$message = reset($messages);
|
||||
// Get the xml content and remove the element representing <br />. This will
|
||||
// result in different element counts in the source and target and should
|
||||
// trigger an error and not import the translation.
|
||||
$translated_file = 'public://tmgmt_file/translated.xlf';
|
||||
$this->createTranslationFile($message->variables['!link'], '<x id="tjiid2-4" ctype="lb"/>', '', $translated_file);
|
||||
$uri = $job->uri();
|
||||
$edit = array(
|
||||
'files[file]' => $translated_file,
|
||||
);
|
||||
$this->drupalPost($uri['path'] . '/manage', $edit, t('Import'));
|
||||
entity_get_controller('tmgmt_job')->resetCache();
|
||||
entity_get_controller('tmgmt_job_item')->resetCache();
|
||||
$job = tmgmt_job_load($job->tjid);
|
||||
|
||||
$this->assertIntegrityCheck($job);
|
||||
|
||||
// Set the XLIFF processing to FALSE and test it results in the source
|
||||
// text not being XLIFF processed.
|
||||
$translator->settings['xliff_processing'] = FALSE;
|
||||
$translator->save();
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$job->addItem('test_html_source', 'test', '1');
|
||||
$job->requestTranslation();
|
||||
$targets = $this->getTransUnitsContent($job);
|
||||
$this->assertEqual(trim(html_entity_decode($targets['0']['source'])), $source_text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the CDATA option for XLIFF export and import.
|
||||
*/
|
||||
function testXLIFFCDATA() {
|
||||
$translator = $this->createTranslator();
|
||||
$translator->plugin = 'file';
|
||||
$translator->settings = array(
|
||||
'export_format' => 'xlf',
|
||||
'xliff_cdata' => TRUE,
|
||||
);
|
||||
$translator->save();
|
||||
|
||||
// Get the source text.
|
||||
$source_text = trim(file_get_contents(drupal_get_path('module', 'tmgmt') . '/tests/testing_html/sample.html'));
|
||||
|
||||
// Create a new job.
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$job->addItem('test_html_source', 'test', '1');
|
||||
$job->requestTranslation();
|
||||
$messages = $job->getMessages();
|
||||
$message = reset($messages);
|
||||
|
||||
$download_url = $message->variables['!link'];
|
||||
// Get XLIFF content.
|
||||
$xliff = file_get_contents($download_url);
|
||||
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadXML($xliff);
|
||||
$this->assertTrue($dom->schemaValidate(drupal_get_path('module', 'tmgmt_file') . '/xliff-core-1.2-strict.xsd'));
|
||||
|
||||
// "Translate" items.
|
||||
$xml = simplexml_import_dom($dom);
|
||||
$translated_text = array();
|
||||
foreach ($xml->file->body->children() as $group) {
|
||||
foreach ($group->children() as $transunit) {
|
||||
if ($transunit->getName() == 'trans-unit') {
|
||||
// The target should be empty.
|
||||
$this->assertEqual($transunit->target, '');
|
||||
|
||||
// Update translations using CDATA.
|
||||
$node = dom_import_simplexml($transunit->target);
|
||||
$owner = $node->ownerDocument;
|
||||
$node->appendChild($owner->createCDATASection($xml->file['target-language'] . '_' . (string) $transunit->source));
|
||||
|
||||
// Store the text to allow assertions later on.
|
||||
$translated_text[(string) $group['id']][(string) $transunit['id']] = (string) $transunit->target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$translated_file = 'public://tmgmt_file/translated file.xlf';
|
||||
$xml->asXML($translated_file);
|
||||
|
||||
// Import the file and check translation for the "dummy" item.
|
||||
$uri = $job->uri();
|
||||
$edit = array(
|
||||
'files[file]' => $translated_file,
|
||||
);
|
||||
$this->drupalPost($uri['path'] . '/manage', $edit, t('Import'));
|
||||
$this->clickLink(t('review'));
|
||||
foreach ($translated_text[1] as $key => $value) {
|
||||
$this->assertText(htmlspecialchars($value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets trans-unit content from the XLIFF file that has been exported for the
|
||||
* given job as last.
|
||||
*/
|
||||
protected function getTransUnitsContent(TMGMTJob $job) {
|
||||
$messages = $job->getMessages();
|
||||
$message = reset($messages);
|
||||
$download_url = $message->variables['!link'];
|
||||
$xml_string = file_get_contents($download_url);
|
||||
$xml = simplexml_load_string($xml_string);
|
||||
|
||||
// Register the xliff namespace, required for xpath.
|
||||
$xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2');
|
||||
|
||||
$reader = new XMLReader();
|
||||
$data = array();
|
||||
$i = 0;
|
||||
foreach ($xml->xpath('//xliff:trans-unit') as $unit) {
|
||||
$reader->XML($unit->source->asXML());
|
||||
$reader->read();
|
||||
$data[$i]['source'] = $reader->readInnerXML();
|
||||
$reader->XML($unit->target->asXML());
|
||||
$reader->read();
|
||||
$data[$i]['target'] = $reader->readInnerXML();
|
||||
$i++;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests export and import for the HTML format.
|
||||
*/
|
||||
function testHTML() {
|
||||
$translator = $this->createTranslator();
|
||||
$translator->plugin = 'file';
|
||||
$translator->settings = array(
|
||||
'export_format' => 'html',
|
||||
);
|
||||
$translator->save();
|
||||
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$job->addItem('test_source', 'test', '1');
|
||||
$job->addItem('test_source', 'test', '2');
|
||||
|
||||
$job->requestTranslation();
|
||||
$messages = $job->getMessages();
|
||||
$message = reset($messages);
|
||||
|
||||
$download_url = $message->variables['!link'];
|
||||
|
||||
// "Translate" items.
|
||||
$xml = simplexml_load_file($download_url);
|
||||
$translated_text = array();
|
||||
foreach ($xml->body->children() as $group) {
|
||||
for ($i = 0; $i < $group->count(); $i++) {
|
||||
// This does not actually override the whole object, just the content.
|
||||
$group->div[$i] = (string) $xml->head->meta[3]['content'] . '_' . (string) $group->div[$i];
|
||||
// Store the text to allow assertions later on.
|
||||
$translated_text[(string) $group['id']][(string) $group->div[$i]['id']] = (string) $group->div[$i];
|
||||
}
|
||||
}
|
||||
|
||||
$translated_file = 'public://tmgmt_file/translated.html';
|
||||
$xml->asXML($translated_file);
|
||||
$this->importFile($translated_file, $translated_text, $job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests import and export for the XLIFF format.
|
||||
*/
|
||||
function testXLIFF() {
|
||||
$translator = $this->createTranslator();
|
||||
$translator->plugin = 'file';
|
||||
$translator->settings = array(
|
||||
'export_format' => 'xlf',
|
||||
);
|
||||
$translator->save();
|
||||
|
||||
// Set multiple data items for the source.
|
||||
variable_set('tmgmt_test_source_data', array(
|
||||
'dummy' => array(
|
||||
'deep_nesting' => array(
|
||||
'#text' => file_get_contents(drupal_get_path('module', 'tmgmt') . '/tests/testing_html/sample.html') . ' @id.',
|
||||
'#label' => 'Label of deep nested item @id',
|
||||
),
|
||||
),
|
||||
'another_item' => array(
|
||||
'#text' => 'Text of another item @id.',
|
||||
'#label' => 'Label of another item @id.',
|
||||
),
|
||||
));
|
||||
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$first_item = $job->addItem('test_source', 'test', '1');
|
||||
// Keep the first item data for later use.
|
||||
$first_item_data = tmgmt_flatten_data($first_item->getData());
|
||||
$job->addItem('test_source', 'test', '2');
|
||||
|
||||
$job->requestTranslation();
|
||||
$messages = $job->getMessages();
|
||||
$message = reset($messages);
|
||||
|
||||
$download_url = $message->variables['!link'];
|
||||
$xliff = file_get_contents($download_url);
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML($xliff);
|
||||
$this->assertTrue($dom->schemaValidate(drupal_get_path('module', 'tmgmt_file') . '/xliff-core-1.2-strict.xsd'));
|
||||
|
||||
// "Translate" items.
|
||||
$xml = simplexml_import_dom($dom);
|
||||
$translated_text = array();
|
||||
foreach ($xml->file->body->children() as $group) {
|
||||
foreach ($group->children() as $transunit) {
|
||||
if ($transunit->getName() == 'trans-unit') {
|
||||
// The target should be empty.
|
||||
$this->assertEqual($transunit->target, '');
|
||||
$transunit->target = $xml->file['target-language'] . '_' . (string) $transunit->source;
|
||||
// Store the text to allow assertions later on.
|
||||
$translated_text[(string) $group['id']][(string) $transunit['id']] = (string) $transunit->target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Change the job id to a non-existing one and try to import it.
|
||||
$wrong_xml = clone $xml;
|
||||
$wrong_xml->file->header->{'phase-group'}->phase['job-id'] = 500;
|
||||
$wrong_file = 'public://tmgmt_file/wrong_file.xlf';
|
||||
$wrong_xml->asXML($wrong_file);
|
||||
$uri = $job->uri();
|
||||
$edit = array(
|
||||
'files[file]' => $wrong_file,
|
||||
);
|
||||
$this->drupalPost($uri['path'] . '/manage', $edit, t('Import'));
|
||||
$this->assertText(t('Failed to validate file, import aborted.'));
|
||||
|
||||
// Change the job id to a wrong one and try to import it.
|
||||
$wrong_xml = clone $xml;
|
||||
$second_job = $this->createJob();
|
||||
$second_job->translator = $translator->name;
|
||||
// We need to add the elements count value into settings, otherwise the
|
||||
// validation will fail on integrity check.
|
||||
$second_job->settings['xliff_validation'][1] = 0;
|
||||
$second_job->settings['xliff_validation'][2] = 0;
|
||||
$second_job->save();
|
||||
$wrong_xml->file->header->{'phase-group'}->phase['job-id'] = $second_job->tjid;
|
||||
$wrong_file = 'public://tmgmt_file/wrong_file.xlf';
|
||||
$wrong_xml->asXML($wrong_file);
|
||||
$uri = $job->uri();
|
||||
$edit = array(
|
||||
'files[file]' => $wrong_file,
|
||||
);
|
||||
$this->drupalPost($uri['path'] . '/manage', $edit, t('Import'));
|
||||
$this->assertRaw(t('The imported file job id @file_tjid does not match the job id @job_tjid.', array(
|
||||
'@file_tjid' => $second_job->tjid,
|
||||
'@job_tjid' => $job->tjid,
|
||||
)));
|
||||
|
||||
$translated_file = 'public://tmgmt_file/translated file.xlf';
|
||||
$xml->asXML($translated_file);
|
||||
|
||||
// Import the file and accept translation for the "dummy" item.
|
||||
$uri = $job->uri();
|
||||
$edit = array(
|
||||
'files[file]' => $translated_file,
|
||||
);
|
||||
$this->drupalPost($uri['path'] . '/manage', $edit, t('Import'));
|
||||
$this->clickLink(t('review'));
|
||||
$this->drupalPostAJAX(NULL, NULL, array('reviewed-dummy|deep_nesting' => '✓'));
|
||||
|
||||
// Update the translation for "another" item and import.
|
||||
$xml->file->body->group[0]->{'trans-unit'}[1]->target = $xml->file->body->group[0]->{'trans-unit'}[1]->target . ' updated';
|
||||
$xml->asXML($translated_file);
|
||||
$uri = $job->uri();
|
||||
$edit = array(
|
||||
'files[file]' => $translated_file,
|
||||
);
|
||||
$this->drupalPost($uri['path'] . '/manage', $edit, t('Import'));
|
||||
|
||||
// At this point we must have the "dummy" item accepted and intact. The
|
||||
// "another" item must have updated translation.
|
||||
$this->clickLink(t('review'));
|
||||
$this->assertFieldByName('dummy|deep_nesting[translation]', 'de_' . $first_item_data['dummy][deep_nesting']['#text']);
|
||||
$this->assertFieldByName('another_item[translation]', 'de_' . $first_item_data['another_item']['#text'] . ' updated');
|
||||
|
||||
// Now finish the import/save as completed process doing another extra
|
||||
// import. The extra import will test that a duplicate import of the same
|
||||
// file does not break the process.
|
||||
$this->importFile($translated_file, $translated_text, $job);
|
||||
|
||||
$this->assertNoText(t('Import translated file'));
|
||||
|
||||
// Create a job, assign to the file translator and delete before attaching
|
||||
// a file.
|
||||
$other_job = $this->createJob();
|
||||
$other_job->translator = $translator->name;
|
||||
$other_job->save();
|
||||
$other_job->delete();
|
||||
// Make sure the file of the other job still exists.
|
||||
$response = drupal_http_request($download_url);
|
||||
$this->assertEqual(200, $response->code);
|
||||
|
||||
// Delete the job and then make sure that the file has been deleted.
|
||||
$job->delete();
|
||||
$response = drupal_http_request($download_url);
|
||||
$this->assertEqual(404, $response->code);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests storing files in the private file system.
|
||||
*/
|
||||
function testPrivate() {
|
||||
// Enable the private file system.
|
||||
variable_set('file_private_path', variable_get('file_public_path') . '/private');
|
||||
|
||||
// Create a translator using the private file system.
|
||||
// @todo: Test the configuration UI.
|
||||
$translator = $this->createTranslator();
|
||||
$translator->plugin = 'file';
|
||||
$translator->settings = array(
|
||||
'export_format' => 'xlf',
|
||||
'scheme' => 'private',
|
||||
);
|
||||
$translator->save();
|
||||
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$job->addItem('test_source', 'test', '1');
|
||||
$job->addItem('test_source', 'test', '2');
|
||||
|
||||
$job->requestTranslation();
|
||||
$messages = $job->getMessages();
|
||||
$message = reset($messages);
|
||||
|
||||
$download_url = $message->variables['!link'];
|
||||
$this->drupalGet($download_url);
|
||||
// Verify that the URL is served using the private file system and the
|
||||
// access checks work.
|
||||
$this->assertTrue(preg_match('|system/files|', $download_url));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalLogout();
|
||||
// Verify that access is now protected.
|
||||
$this->drupalGet($download_url);
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
protected function importFile($translated_file, $translated_text, TMGMTJob $job) {
|
||||
// To test the upload form functionality, navigate to the edit form.
|
||||
$uri = $job->uri();
|
||||
$edit = array(
|
||||
'files[file]' => $translated_file,
|
||||
);
|
||||
$this->drupalPost($uri['path'] . '/manage', $edit, t('Import'));
|
||||
|
||||
// Make sure the translations have been imported correctly.
|
||||
$this->assertNoText(t('In progress'));
|
||||
// @todo: Enable this assertion once new releases for views and entity
|
||||
// module are out.
|
||||
//$this->assertText(t('Needs review'));
|
||||
|
||||
// Review both items.
|
||||
$this->clickLink(t('review'));
|
||||
foreach ($translated_text[1] as $key => $value) {
|
||||
$this->assertText(check_plain($value));
|
||||
}
|
||||
foreach ($translated_text[2] as $key => $value) {
|
||||
$this->assertNoText(check_plain($value));
|
||||
}
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
// Review both items.
|
||||
$this->clickLink(t('review'));
|
||||
foreach ($translated_text[1] as $key => $value) {
|
||||
$this->assertNoText(check_plain($value));
|
||||
}
|
||||
foreach ($translated_text[2] as $key => $value) {
|
||||
$this->assertText(check_plain($value));
|
||||
}
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
// @todo: Enable this assertion once new releases for views and entity
|
||||
// module are out.
|
||||
//$this->assertText(t('Accepted'));
|
||||
$this->assertText(t('Finished'));
|
||||
$this->assertNoText(t('Needs review'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a translated XLIFF file based on the replacement definition.
|
||||
*
|
||||
* @param string $source_file
|
||||
* Source file name.
|
||||
* @param $search
|
||||
* String to search in the source.
|
||||
* @param $replace
|
||||
* String to replace it with in the target.
|
||||
* @param $translated_file
|
||||
* Name of the file to write.
|
||||
*/
|
||||
protected function createTranslationFile($source_file, $search, $replace, $translated_file) {
|
||||
$xml_string = file_get_contents($source_file);
|
||||
preg_match('/<source xml:lang="en">(.+)<\/source>/s', $xml_string, $matches);
|
||||
$target = str_replace($search, $replace, $matches[1]);
|
||||
if ($replace) {
|
||||
$this->assertTrue(strpos($target, $replace) !== FALSE, 'String replaced in translation');
|
||||
}
|
||||
$translated_xml_string = str_replace('<target xml:lang="de"/>', '<target xml:lang="de">' . $target . '</target>', $xml_string);
|
||||
file_put_contents($translated_file, $translated_xml_string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts import integrity for a job.
|
||||
*
|
||||
* @param TMGMTJob $job
|
||||
* The job to check.
|
||||
* @param bool $expected
|
||||
* (optional) If an integrity failed message is expected or not, defaults
|
||||
* to FALSE.
|
||||
*/
|
||||
protected function assertIntegrityCheck(TMGMTJob $job, $expected = TRUE) {
|
||||
$integrity_check_failed = FALSE;
|
||||
/** @var TMGMTMessage $message */
|
||||
foreach ($job->getMessages() as $message) {
|
||||
if ($message->getMessage() == t('Failed to validate semantic integrity of %key element. Please check also the HTML code of the element in the review process.', array('%key' => 'dummy][deep_nesting'))) {
|
||||
$integrity_check_failed = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Check if the message was found or not, based on the expected argument.
|
||||
if ($expected) {
|
||||
$this->assertTrue($integrity_check_failed, 'The validation of semantic integrity must fail.');
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($integrity_check_failed, 'The validation of semantic integrity must not fail.');
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Please supply a file description.
|
||||
*/
|
||||
|
||||
/**
|
||||
* File translator plugin controller.
|
||||
*/
|
||||
class TMGMTFileTranslatorUIController extends TMGMTDefaultTranslatorUIController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function pluginSettingsForm($form, &$form_state, TMGMTTranslator $translator, $busy = FALSE) {
|
||||
$form['export_format'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Export to'),
|
||||
'#options' => tmgmt_file_format_plugin_labels(),
|
||||
'#default_value' => $translator->getSetting('export_format'),
|
||||
'#description' => t('Please select the format you want to export data.'),
|
||||
);
|
||||
|
||||
$form['xliff_cdata'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('XLIFF CDATA'),
|
||||
'#description' => t('Check to use CDATA for import/export.'),
|
||||
'#default_value' => $translator->getSetting('xliff_cdata'),
|
||||
);
|
||||
|
||||
$form['xliff_processing'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Extended XLIFF processing'),
|
||||
'#description' => t('Check to further process content semantics and mask HTML tags instead just escaping it.'),
|
||||
'#default_value' => $translator->getSetting('xliff_processing'),
|
||||
);
|
||||
|
||||
$form['xliff_message'] = array(
|
||||
'#type' => 'item',
|
||||
'#markup' => t('By selecting CDATA option, XLIFF processing will be ignored.'),
|
||||
'#prefix' => '<div class="messages warning">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
|
||||
$form['allow_override'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Allow to override the format per job'),
|
||||
'#default_value' => $translator->getSetting('allow_override'),
|
||||
);
|
||||
|
||||
// Any visible, writeable wrapper can potentially be used for the files
|
||||
// directory, including a remote file system that integrates with a CDN.
|
||||
foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
|
||||
$options[$scheme] = check_plain($info['description']);
|
||||
}
|
||||
|
||||
if (!empty($options)) {
|
||||
$form['scheme'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Download method'),
|
||||
'#default_value' => $translator->getSetting('scheme'),
|
||||
'#options' => $options,
|
||||
'#description' => t('Choose the location where exported files should be stored. The usage of a protected location (e.g. private://) is recommended to prevent unauthorized access.'),
|
||||
);
|
||||
}
|
||||
|
||||
return parent::pluginSettingsForm($form, $form_state, $translator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkoutSettingsForm($form, &$form_state, TMGMTJob $job) {
|
||||
if ($job->getTranslator()->getSetting('allow_override')) {
|
||||
$form['export_format'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => t('Export to'),
|
||||
'#options' => tmgmt_file_format_plugin_labels(),
|
||||
'#default_value' => $job->getTranslator()->getSetting('export_format'),
|
||||
'#description' => t('Please select the format you want to export data.'),
|
||||
);
|
||||
}
|
||||
return parent::checkoutSettingsForm($form, $form_state, $job);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkoutInfo(TMGMTJob $job) {
|
||||
// If the job is finished, it's not possible to import translations anymore.
|
||||
if ($job->isFinished()) {
|
||||
return parent::checkoutInfo($job);
|
||||
}
|
||||
$form = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Import translated file'),
|
||||
);
|
||||
|
||||
$supported_formats = array_keys(tmgmt_file_format_plugin_info());
|
||||
$form['file'] = array(
|
||||
'#type' => 'file',
|
||||
'#title' => t('File file'),
|
||||
'#size' => 50,
|
||||
'#description' => t('Supported formats: @formats.', array('@formats' => implode(', ', $supported_formats))),
|
||||
);
|
||||
$form['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Import'),
|
||||
'#submit' => array('tmgmt_file_import_form_submit'),
|
||||
);
|
||||
return $this->checkoutInfoWrapper($job, $form);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
||||
TMGMT Local Translator
|
||||
----------------------
|
||||
|
||||
A user interface to execute the actual translation of TMGMT source elements.
|
||||
Includes management capabilities for handling translations and users.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Local Translator is a translation plugin for TMGMT and is included in TMGMT core.
|
||||
|
||||
Basic Concepts
|
||||
--------------
|
||||
|
||||
Acts as a translator plugin for TMGMT. Can be used to do manual local
|
||||
translation. In addition it offers some management capabilities to handle the
|
||||
assignment of jobs to users.
|
||||
|
||||
The local translator adds two permissions:
|
||||
|
||||
- Provide translation services
|
||||
|
||||
The user can assign jobs to himself (assuming he has the right skills) and
|
||||
execute the translation. Adds a 'Translate' link to the User menu.
|
||||
|
||||
- Administer translation tasks
|
||||
|
||||
Assign jobs to other users for translation. Adds a 'Manage Translate Tasks'
|
||||
to the User Menu.
|
||||
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
|
||||
In TMGMT, a translation job can be sent to the local translator. In the checkout
|
||||
settings, the plugin offers the possibility to assign the job to a specific user.
|
||||
Only users with the required language skills are listed at this moment. If no
|
||||
person is selected, the job will be moved to the 'unassigned' task list for later
|
||||
treatment.
|
||||
|
||||
Following the 'Translate' link in the User Menu, the user finds a listing
|
||||
of the tasks assigned to him as well as eligible tasks to assign to himself,
|
||||
depending on his skills. Assigned tasks will show a 'translate' link in the
|
||||
action column. Follow it to get a list of the task items to be translated.
|
||||
Choose to translate one item to get to the actual translation page.
|
||||
|
||||
It lists two panes for each data item contained in the task item. One showing
|
||||
the text in the original language. The second one for writing in the translation.
|
||||
Each line sports a check button to its right. Once the translation is done, the
|
||||
data item can be checked off as complete. This check is purely informational
|
||||
and has no functional consequences.
|
||||
|
||||
A translation task item can be saved at any time for later rework. It will show
|
||||
up as pending or translated depending on the state of the check mark for each
|
||||
item.
|
||||
|
||||
Once the data items are all translated, the 'Save as completed' button will
|
||||
finalize the task and send it back to TMGMT core for further processing. Please
|
||||
note: Completing a task item is independent of the state of the checkboxes.
|
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the task controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller class for the local task entity.
|
||||
*
|
||||
* @ingroup tmgmt_local_task
|
||||
*/
|
||||
class TMGMTLocalTaskController extends EntityAPIController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save($entity, DatabaseTransaction $transaction = NULL) {
|
||||
$entity->changed = REQUEST_TIME;
|
||||
return parent::save($entity, $transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($ids, $transaction = NULL) {
|
||||
parent::delete($ids, $transaction);
|
||||
|
||||
$query = new EntityFieldQuery();
|
||||
$result = $query
|
||||
->entityCondition('entity_type', 'tmgmt_local_task_item')
|
||||
->propertyCondition('tltid', $ids)
|
||||
->execute();
|
||||
if (!empty($result['tmgmt_local_task_item'])) {
|
||||
entity_delete_multiple('tmgmt_local_task_item', array_keys($result['tmgmt_local_task_item']));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the task item controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controller class for the local task entity.
|
||||
*
|
||||
* @ingroup tmgmt_local_task
|
||||
*/
|
||||
class TMGMTLocalTaskItemController extends EntityAPIController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @todo Eliminate the need to flatten and unflatten the TaskItem data.
|
||||
*/
|
||||
public function save($entity, DatabaseTransaction $transaction = NULL) {
|
||||
// Consider everything translated when the job item is translated.
|
||||
if ($entity->isCompleted()) {
|
||||
$entity->count_untranslated = 0;
|
||||
$entity->count_translated = count(tmgmt_flatten_data($entity->data));
|
||||
$entity->count_completed = 0;
|
||||
}
|
||||
// Consider everything completed if the job is completed.
|
||||
elseif ($entity->isClosed()) {
|
||||
$entity->count_untranslated = 0;
|
||||
$entity->count_translated = 0;
|
||||
$entity->count_completed = count(tmgmt_flatten_data($entity->data));
|
||||
}
|
||||
// Count the data item states.
|
||||
else {
|
||||
// Start with assuming that all data is untranslated, then go through it
|
||||
// and count translated data.
|
||||
$entity->count_untranslated = count(array_filter(tmgmt_flatten_data($entity->getJobItem()->getData()), '_tmgmt_filter_data'));
|
||||
$entity->count_translated = 0;
|
||||
$entity->count_completed = 0;
|
||||
$this->count($entity->data, $entity);
|
||||
}
|
||||
return parent::save($entity, $transaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse all data items recursively and sums up the counters for
|
||||
* accepted, translated and pending items.
|
||||
*
|
||||
* @param $item
|
||||
* The current data item.
|
||||
* @param $entity
|
||||
* The job item the count should be calculated.
|
||||
*/
|
||||
protected function count(&$item, $entity) {
|
||||
if (!empty($item['#text'])) {
|
||||
if (_tmgmt_filter_data($item)) {
|
||||
|
||||
// Set default states if no state is set.
|
||||
if (!isset($item['#status'])) {
|
||||
$item['#status'] = TMGMT_DATA_ITEM_STATE_UNTRANSLATED;
|
||||
}
|
||||
switch ($item['#status']) {
|
||||
case TMGMT_DATA_ITEM_STATE_TRANSLATED:
|
||||
$entity->count_untranslated--;
|
||||
$entity->count_translated++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach (element_children($item) as $key) {
|
||||
$this->count($item[$key], $entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Entity UI controller for the local task entity.
|
||||
*/
|
||||
class TMGMTLocalTaskUIController extends EntityDefaultUIController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_menu() {
|
||||
$id_count = count(explode('/', $this->path));
|
||||
$wildcard = isset($this->entityInfo['admin ui']['menu wildcard']) ? $this->entityInfo['admin ui']['menu wildcard'] : '%entity_object';
|
||||
$items[$this->path . '/' . $wildcard] = array(
|
||||
'title callback' => 'entity_label',
|
||||
'title arguments' => array($this->entityType, $id_count),
|
||||
'page callback' => 'tmgmt_local_task_view',
|
||||
'page arguments' => array($id_count),
|
||||
'load arguments' => array($this->entityType),
|
||||
'access callback' => 'entity_access',
|
||||
'access arguments' => array('view', $this->entityType, $id_count),
|
||||
'file' => 'tmgmt_local.pages.inc',
|
||||
'file path' => drupal_get_path('module', 'tmgmt_local') . '/includes',
|
||||
);
|
||||
$items[$this->path . '/' . $wildcard . '/delete'] = array(
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array($this->entityType . '_operation_form', $this->entityType, $id_count, $id_count + 1),
|
||||
'load arguments' => array($this->entityType),
|
||||
'access callback' => 'entity_access',
|
||||
'access arguments' => array('delete', $this->entityType, $id_count),
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
$items[$this->path . '/' . $wildcard . '/unassign'] = array(
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array($this->entityType . '_operation_form', $this->entityType, $id_count, $id_count + 1),
|
||||
'load arguments' => array($this->entityType),
|
||||
'access callback' => 'entity_access',
|
||||
'access arguments' => array('unassign', $this->entityType, $id_count),
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function operationForm($form, &$form_state, $entity, $op) {
|
||||
switch ($op) {
|
||||
case 'delete':
|
||||
$confirm_question = t('Are you sure you want to delete the translation task %label?', array('%label' => $entity->label()));
|
||||
return confirm_form($form, $confirm_question, $this->path);
|
||||
case 'unassign':
|
||||
$confirm_question = t('Are you sure you want to unassign from the translation task %label?', array('%label' => $entity->label()));
|
||||
return confirm_form($form, $confirm_question, $this->path);
|
||||
}
|
||||
drupal_not_found();
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applyOperation($op, $entity) {
|
||||
switch ($op) {
|
||||
case 'delete':
|
||||
$entity->delete();
|
||||
return t('Deleted the translation local task %label.', array('%label' => $entity->label()));
|
||||
case 'unassign':
|
||||
$entity->unassign();
|
||||
$entity->save();
|
||||
return t('Unassigned from translation local task %label.', array('%label' => $entity->label()));
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Entity UI controller for the local task item entity.
|
||||
*/
|
||||
class TMGMTLocalTaskItemUIController extends EntityDefaultUIController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hook_menu() {
|
||||
$id_count = count(explode('/', $this->path));
|
||||
$items[$this->path . '/%tmgmt_local_task/item/%tmgmt_local_task_item'] = array(
|
||||
'title callback' => 'entity_label',
|
||||
'title arguments' => array($this->entityType, $id_count + 2),
|
||||
'page callback' => 'tmgmt_local_task_item_view',
|
||||
'page arguments' => array($id_count + 2),
|
||||
'load arguments' => array($this->entityType),
|
||||
'access callback' => 'entity_access',
|
||||
'access arguments' => array('view', $this->entityType, $id_count + 2),
|
||||
'file' => 'tmgmt_local.pages.inc',
|
||||
'file path' => drupal_get_path('module', 'tmgmt_local') . '/includes',
|
||||
);
|
||||
return $items;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,387 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Entity class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Entity class for the local task entity.
|
||||
*
|
||||
* @ingroup tmgmt_local_task
|
||||
*/
|
||||
class TMGMTLocalTask extends Entity {
|
||||
|
||||
/**
|
||||
* Translation local task identifier.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $tltid;
|
||||
|
||||
/**
|
||||
* The user id of the creator of the task.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $uid;
|
||||
|
||||
/**
|
||||
* The time when the task was created as a timestamp.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $created = REQUEST_TIME;
|
||||
|
||||
/**
|
||||
* The time when the task was changed as a timestamp.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $changed;
|
||||
|
||||
/**
|
||||
* A title of this task.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $title;
|
||||
|
||||
/**
|
||||
* The user id of the assigned translator.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $tuid;
|
||||
|
||||
/**
|
||||
* Translation job.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $tjid;
|
||||
|
||||
/**
|
||||
* Current status of the task.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $status = TMGMT_LOCAL_TASK_STATUS_UNASSIGNED;
|
||||
|
||||
/**
|
||||
* Counter for how many times task was returned to translator.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $loop_count;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $values = array(), $entity_type = 'tmgmt_local_task') {
|
||||
parent::__construct($values, $entity_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultUri() {
|
||||
return array('path' => 'translate/' . $this->tltid);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defaultLabel() {
|
||||
if (empty($this->tuid)) {
|
||||
if (empty($this->title)) {
|
||||
return t('Task for @job', array('@job' => $this->getJob()->label()));
|
||||
}
|
||||
else {
|
||||
return $this->title;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (empty($this->title)) {
|
||||
return t('Task for @job assigned to @translator', array('@job' => $this->getJob()->label(), '@translator' => entity_label('user', user_load($this->tuid))));
|
||||
}
|
||||
else {
|
||||
return t('@title assigned to @translator', array('@title' => $this->title, '@translator' => entity_label('user', user_load($this->tuid))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildContent($view_mode = 'full', $langcode = NULL) {
|
||||
$content = entity_ui_get_form('tmgmt_local_task', $this);
|
||||
return entity_get_controller($this->entityType)->buildContent($this, $view_mode, $langcode, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the corresponding translation job.
|
||||
*
|
||||
* @return TMGMTJob
|
||||
*/
|
||||
public function getJob() {
|
||||
return tmgmt_job_load($this->tjid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign translation task to passed user.
|
||||
*
|
||||
* @param object $user
|
||||
* User object.
|
||||
*/
|
||||
public function assign($user) {
|
||||
$this->incrementLoopCount(TMGMT_LOCAL_TASK_STATUS_PENDING, $user->uid);
|
||||
$this->tuid = $user->uid;
|
||||
$this->status = TMGMT_LOCAL_TASK_STATUS_PENDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unassign translation task.
|
||||
*/
|
||||
public function unassign() {
|
||||
// We also need to increment loop count when unassigning.
|
||||
$this->incrementLoopCount(TMGMT_LOCAL_TASK_STATUS_UNASSIGNED, 0);
|
||||
$this->tuid = 0;
|
||||
$this->status = TMGMT_LOCAL_TASK_STATUS_UNASSIGNED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all job items attached to this task.
|
||||
*
|
||||
* @return array
|
||||
* An array of translation job items.
|
||||
*/
|
||||
public function getItems($conditions = array()) {
|
||||
$query = new EntityFieldQuery();
|
||||
$query->entityCondition('entity_type', 'tmgmt_local_task_item');
|
||||
$query->propertyCondition('tltid', $this->tltid);
|
||||
foreach ($conditions as $key => $condition) {
|
||||
if (is_array($condition)) {
|
||||
$operator = isset($condition['operator']) ? $condition['operator'] : '=';
|
||||
$query->propertyCondition($key, $condition['value'], $operator);
|
||||
}
|
||||
else {
|
||||
$query->propertyCondition($key, $condition);
|
||||
}
|
||||
}
|
||||
$results = $query->execute();
|
||||
if (!empty($results['tmgmt_local_task_item'])) {
|
||||
return entity_load('tmgmt_local_task_item', array_keys($results['tmgmt_local_task_item']));
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a task item for this task and the given job item.
|
||||
*
|
||||
* @param TMGMTJobItem $job_item
|
||||
* The job item.
|
||||
*/
|
||||
public function addTaskItem(TMGMTJobItem $job_item) {
|
||||
// Save the task to get an id.
|
||||
if (empty($this->tltid)) {
|
||||
$this->save();
|
||||
}
|
||||
|
||||
$local_task = entity_create('tmgmt_local_task_item', array(
|
||||
'tltid' => $this->identifier(),
|
||||
'tjiid' => $job_item->identifier(),
|
||||
));
|
||||
$local_task->save();
|
||||
return $local_task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status of the task. Can be one of the task status constants.
|
||||
*
|
||||
* @return int
|
||||
* The status of the task or NULL if it hasn't been set yet.
|
||||
*/
|
||||
public function getStatus() {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the status of the task.
|
||||
*
|
||||
* @param $status
|
||||
* The new status of the task. Has to be one of the task status constants.
|
||||
* @param $message
|
||||
* (Optional) The log message to be saved along with the status change.
|
||||
* @param $variables
|
||||
* (Optional) An array of variables to replace in the message on display.
|
||||
*
|
||||
* @return int
|
||||
* The updated status of the task if it could be set.
|
||||
*
|
||||
* @see TMGMTJob::addMessage()
|
||||
*/
|
||||
public function setStatus($status) {
|
||||
// Return TRUE if the status could be set. Return FALSE otherwise.
|
||||
if (array_key_exists($status, tmgmt_local_task_statuses())) {
|
||||
$this->incrementLoopCount($status, $this->tuid);
|
||||
$this->status = $status;
|
||||
$this->save();
|
||||
}
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the passed value matches the current status.
|
||||
*
|
||||
* @param $status
|
||||
* The value to check the current status against.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the passed status matches the current status, FALSE otherwise.
|
||||
*/
|
||||
public function isStatus($status) {
|
||||
return $this->getStatus() == $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the user described by $account is the author of this task.
|
||||
*
|
||||
* @param $account
|
||||
* (Optional) A user object. Defaults to the currently logged in user.
|
||||
*/
|
||||
public function isAuthor($account = NULL) {
|
||||
$account = isset($account) ? $account : $GLOBALS['user'];
|
||||
return $this->uid == $account->uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the status of this task is 'unassigned'.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the status is 'unassigned', FALSE otherwise.
|
||||
*/
|
||||
public function isUnassigned() {
|
||||
return $this->isStatus(TMGMT_LOCAL_TASK_STATUS_UNASSIGNED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the status of this task is 'pending'.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the status is 'pending', FALSE otherwise.
|
||||
*/
|
||||
public function isPending() {
|
||||
return $this->isStatus(TMGMT_LOCAL_TASK_STATUS_PENDING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the status of this task is 'completed'.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the status is 'completed', FALSE otherwise.
|
||||
*/
|
||||
public function isCompleted() {
|
||||
return $this->isStatus(TMGMT_LOCAL_TASK_STATUS_COMPLETED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the status of this task is 'rejected'.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the status is 'rejected', FALSE otherwise.
|
||||
*/
|
||||
public function isRejected() {
|
||||
return $this->isStatus(TMGMT_LOCAL_TASK_STATUS_REJECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the status of this task is 'closed'.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if the status is 'closed', FALSE otherwise.
|
||||
*/
|
||||
public function isClosed() {
|
||||
return $this->isStatus(TMGMT_LOCAL_TASK_STATUS_CLOSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of all translated data items.
|
||||
*
|
||||
* @return
|
||||
* Translated count
|
||||
*/
|
||||
public function getCountTranslated() {
|
||||
return tmgmt_local_task_statistic($this, 'count_translated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of all untranslated data items.
|
||||
*
|
||||
* @return
|
||||
* Translated count
|
||||
*/
|
||||
public function getCountUntranslated() {
|
||||
return tmgmt_local_task_statistic($this, 'count_untranslated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of all completed data items.
|
||||
*
|
||||
* @return
|
||||
* Translated count
|
||||
*/
|
||||
public function getCountCompleted() {
|
||||
return tmgmt_local_task_statistic($this, 'count_completed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums up all word counts of this task job items.
|
||||
*
|
||||
* @return
|
||||
* The sum of all accepted counts
|
||||
*/
|
||||
public function getWordCount() {
|
||||
return tmgmt_local_task_statistic($this, 'word_count');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns loop count of a task.
|
||||
*
|
||||
* @return int
|
||||
* Task loop count.
|
||||
*/
|
||||
public function getLoopCount() {
|
||||
return $this->loop_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment loop_count property depending on current status, new status and
|
||||
* new translator.
|
||||
*
|
||||
* @param int $newStatus
|
||||
* New status of task.
|
||||
* @param int $new_tuid
|
||||
* New translator uid.
|
||||
*/
|
||||
public function incrementLoopCount($newStatus, $new_tuid) {
|
||||
if ($this->status == TMGMT_LOCAL_TASK_STATUS_PENDING
|
||||
&& $newStatus == TMGMT_LOCAL_TASK_STATUS_PENDING
|
||||
&& $this->tuid != $new_tuid) {
|
||||
++$this->loop_count;
|
||||
}
|
||||
else if ($this->status != TMGMT_LOCAL_TASK_STATUS_UNASSIGNED
|
||||
&& $newStatus == TMGMT_LOCAL_TASK_STATUS_UNASSIGNED) {
|
||||
++$this->loop_count;
|
||||
}
|
||||
else if ($this->status != TMGMT_LOCAL_TASK_STATUS_UNASSIGNED
|
||||
&& $this->status != TMGMT_LOCAL_TASK_STATUS_PENDING
|
||||
&& $newStatus == TMGMT_LOCAL_TASK_STATUS_PENDING) {
|
||||
++$this->loop_count;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Entity class.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Entity class for the local task item entity.
|
||||
*
|
||||
* @ingroup tmgmt_local_task
|
||||
*/
|
||||
class TMGMTLocalTaskItem extends Entity {
|
||||
|
||||
/**
|
||||
* Translation local task item identifier.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $tltiid;
|
||||
|
||||
/**
|
||||
* The task identifier.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $tltid;
|
||||
|
||||
/**
|
||||
* Translation job item.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $tjiid;
|
||||
|
||||
/**
|
||||
* Current status of the task.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $status;
|
||||
|
||||
/**
|
||||
* Translated data and data item status.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $data = array();
|
||||
|
||||
/**
|
||||
* Counter for all untranslated data items.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $count_untranslated = 0;
|
||||
|
||||
/**
|
||||
* Counter for all translated data items.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $count_translated = 0;
|
||||
|
||||
/**
|
||||
* Counter for all completed data items.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $count_completed = 0;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $values = array(), $entity_type = 'tmgmt_local_task_item') {
|
||||
parent::__construct($values, $entity_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultUri() {
|
||||
return array('path' => 'translate/' . $this->tltid . '/item/' . $this->tltiid);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function defaultLabel() {
|
||||
if ($job_item = $this->getJobItem()) {
|
||||
return $job_item->label();
|
||||
}
|
||||
return t('Missing job item');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation task.
|
||||
*
|
||||
* @return TMGMTLocalTask
|
||||
*/
|
||||
public function getTask() {
|
||||
return entity_load_single('tmgmt_local_task', $this->tltid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation job item.
|
||||
*
|
||||
* @return TMGMTJobItem
|
||||
*/
|
||||
public function getJobItem() {
|
||||
return entity_load_single('tmgmt_job_item', $this->tjiid);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildContent($view_mode = 'full', $langcode = NULL) {
|
||||
$content = drupal_get_form('tmgmt_local_translation_form', $this);
|
||||
return entity_get_controller($this->entityType)->buildContent($this, $view_mode, $langcode, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if the local task is pending.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the local task item is untranslated.
|
||||
*/
|
||||
public function isPending() {
|
||||
return $this->status == TMGMT_LOCAL_TASK_ITEM_STATUS_PENDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if the local task is translated (fully translated).
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the local task item is translated.
|
||||
*/
|
||||
public function isCompleted() {
|
||||
return $this->status == TMGMT_LOCAL_TASK_ITEM_STATUS_COMPLETED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rreturns TRUE if the local task is closed (translated and accepted).
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the local task item is translated and accepted.
|
||||
*/
|
||||
public function isClosed() {
|
||||
return $this->status == TMGMT_LOCAL_TASK_ITEM_STATUS_CLOSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the task item status to completed.
|
||||
*/
|
||||
public function completed() {
|
||||
$this->status = TMGMT_LOCAL_TASK_ITEM_STATUS_COMPLETED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the task item status to closed.
|
||||
*/
|
||||
public function closed() {
|
||||
$this->status = TMGMT_LOCAL_TASK_ITEM_STATUS_CLOSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the values for a specific substructure in the data array.
|
||||
*
|
||||
* The values are either set or updated but never deleted.
|
||||
*
|
||||
* @param $key
|
||||
* Key pointing to the item the values should be applied.
|
||||
* The key can be either be an array containing the keys of a nested array
|
||||
* hierarchy path or a string with '][' or '|' as delimiter.
|
||||
* @param $values
|
||||
* Nested array of values to set.
|
||||
*/
|
||||
public function updateData($key, $values = array()) {
|
||||
foreach ($values as $index => $value) {
|
||||
// In order to preserve existing values, we can not aplly the values array
|
||||
// at once. We need to apply each containing value on its own.
|
||||
// If $value is an array we need to advance the hierarchy level.
|
||||
if (is_array($value)) {
|
||||
$this->updateData(array_merge(tmgmt_ensure_keys_array($key), array($index)), $value);
|
||||
}
|
||||
// Apply the value.
|
||||
else {
|
||||
drupal_array_set_nested_value($this->data, array_merge(tmgmt_ensure_keys_array($key), array($index)), $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of translations.
|
||||
*
|
||||
* The structure is similar to the form API in the way that it is a possibly
|
||||
* nested array with the following properties whose presence indicate that the
|
||||
* current element is a text that might need to be translated.
|
||||
*
|
||||
* - #text: The translated text of the corresponding entry in the job item.
|
||||
* - #status: The status of the translation.
|
||||
*
|
||||
* The key can be an alphanumeric string.
|
||||
*
|
||||
* @param array $key
|
||||
* If present, only the subarray identified by key is returned.
|
||||
* @param string $index
|
||||
* Optional index of an attribute below $key.
|
||||
*
|
||||
* @return array
|
||||
* A structured data array.
|
||||
*/
|
||||
public function getData(array $key = array(), $index = NULL) {
|
||||
if (empty($key)) {
|
||||
return $this->data;
|
||||
}
|
||||
if ($index) {
|
||||
$key = array_merge($key, array($index));
|
||||
}
|
||||
return drupal_array_get_nested_value($this->data, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of all translated data items.
|
||||
*
|
||||
* @return
|
||||
* Translated count
|
||||
*/
|
||||
public function getCountTranslated() {
|
||||
return $this->count_translated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of all untranslated data items.
|
||||
*
|
||||
* @return
|
||||
* Translated count
|
||||
*/
|
||||
public function getCountUntranslated() {
|
||||
return $this->count_untranslated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of all completed data items.
|
||||
*
|
||||
* @return
|
||||
* Translated count
|
||||
*/
|
||||
public function getCountCompleted() {
|
||||
return $this->count_completed;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains the metadata controller classes for the local task translatio
|
||||
* entity.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Metadata controller for the local task entity.
|
||||
*/
|
||||
class TMGMTLocalTaskMetadataController extends EntityDefaultMetadataController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityPropertyInfo() {
|
||||
$info = parent::entityPropertyInfo();
|
||||
// $info = _tmgmt_override_property_description($info, $this->type);
|
||||
$properties = &$info[$this->type]['properties'];
|
||||
|
||||
// Make the created and changed property appear as date.
|
||||
$properties['changed']['type'] = $properties['created']['type'] = 'date';
|
||||
|
||||
// Add the options list for the defined status constants.
|
||||
$properties['status']['options list'] = 'tmgmt_local_task_statuses';
|
||||
|
||||
// Link the job property to the corresponding job entity.
|
||||
$properties['job'] = array(
|
||||
'label' => t('Translation Job'),
|
||||
'type' => 'tmgmt_job',
|
||||
'description' => t('Corresponding job entity of the translation task.'),
|
||||
'setter callback' => 'entity_property_verbatim_set',
|
||||
'setter permission' => 'administer tmgmt',
|
||||
'required' => TRUE,
|
||||
'schema field' => 'tjid',
|
||||
);
|
||||
|
||||
// Link the author property to the corresponding user entity.
|
||||
$properties['author'] = array(
|
||||
'label' => t('Author'),
|
||||
'type' => 'user',
|
||||
'description' => t('The author of the translation task.'),
|
||||
'setter callback' => 'entity_property_verbatim_set',
|
||||
'setter permission' => 'administer tmgmt',
|
||||
'required' => TRUE,
|
||||
'schema field' => 'uid',
|
||||
);
|
||||
|
||||
// Link the author property to the corresponding user entity.
|
||||
$properties['translator'] = array(
|
||||
'label' => t('Translator'),
|
||||
'type' => 'user',
|
||||
'description' => t('The assigned translator for translation task.'),
|
||||
'setter callback' => 'entity_property_verbatim_set',
|
||||
'setter permission' => 'administer tmgmt',
|
||||
'required' => TRUE,
|
||||
'schema field' => 'tuid',
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Metadata controller for the local task entity.
|
||||
*/
|
||||
class TMGMTLocalTaskItemMetadataController extends EntityDefaultMetadataController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityPropertyInfo() {
|
||||
$info = parent::entityPropertyInfo();
|
||||
$properties = &$info[$this->type]['properties'];
|
||||
|
||||
// Add the options list for the defined status constants.
|
||||
$properties['status']['options list'] = 'tmgmt_local_task_item_statuses';
|
||||
|
||||
// Link the job property to the corresponding job entity.
|
||||
$properties['tjiid']['type'] = 'tmgmt_job_item';
|
||||
|
||||
// Link the author property to the corresponding user entity.
|
||||
$properties['tltid']['type'] = 'tmgmt_local_task';
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,515 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides page and forms callbacks.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simple page callback for viewing a task.
|
||||
*
|
||||
* @param TMGMTJob $task
|
||||
* The viewed task.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array.
|
||||
*/
|
||||
function tmgmt_local_task_view(TMGMTLocalTask $task) {
|
||||
return entity_view($task->entityType(), array($task), 'full', NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity API form the local task entity.
|
||||
*/
|
||||
function tmgmt_local_task_form($form, &$form_state, TMGMTLocalTask $task, $op = 'edit') {
|
||||
$wrapper = entity_metadata_wrapper('tmgmt_local_task', $task);
|
||||
|
||||
// Set the title of the page to the label and the current status of the task.
|
||||
drupal_set_title(t('@label (@status)', array('@label' => $task->label(), '@status' => $wrapper->status->label())));
|
||||
|
||||
// Check if the translator entity is completely new or not.
|
||||
$old = empty($task->is_new) && $op != 'clone';
|
||||
|
||||
$form['title'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Title'),
|
||||
'#default_value' => $task->title,
|
||||
'#access' => user_access('administer tmgmt') || user_access('administer translation tasks'),
|
||||
);
|
||||
|
||||
$form['status'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Status'),
|
||||
'#options' => tmgmt_local_task_statuses(),
|
||||
'#default_value' => $wrapper->status->value(),
|
||||
'#access' => user_access('administer tmgmt') || user_access('administer translation tasks'),
|
||||
);
|
||||
|
||||
$translators = tmgmt_local_translators($task->getJob()->source_language, array($task->getJob()->target_language));
|
||||
$form['tuid'] = array(
|
||||
'#title' => t('Assigned'),
|
||||
'#type' => 'select',
|
||||
'#options' => $translators,
|
||||
'#empty_option' => t('- Select user -'),
|
||||
'#default_value' => $task->tuid,
|
||||
'#access' => user_access('administer tmgmt') || user_access('administer translation tasks'),
|
||||
);
|
||||
|
||||
if ($view = views_get_view('tmgmt_local_task_items')) {
|
||||
$form['items'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => $view->get_title(),
|
||||
'#prefix' => '<div class="tmgmt-local-task-items">',
|
||||
'#markup' => $view->preview('block', array($task->tltid)),
|
||||
'#attributes' => array('class' => array('tmgmt-local-task-items')),
|
||||
'#suffix' => '</div>',
|
||||
'#weight' => 10,
|
||||
);
|
||||
}
|
||||
|
||||
// Add the buttons and action links.
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['#access'] = user_access('administer tmgmt') || user_access('administer translation tasks');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save task'),
|
||||
);
|
||||
|
||||
if ($old) {
|
||||
$form['actions']['delete'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Delete'),
|
||||
'#redirect' => 'translate/' . $task->tltid . '/delete',
|
||||
// Don't run validations, so the user can always delete the job.
|
||||
'#limit_validation_errors' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit callback for the local task form.
|
||||
*/
|
||||
function tmgmt_local_task_form_submit($form, &$form_state) {
|
||||
$task = entity_ui_form_submit_build_entity($form, $form_state);
|
||||
// If the task isn't assigned to anyone but doesn't have the unassigned status
|
||||
// update it.
|
||||
if ($task->tuid == 0 && !$task->isUnassigned()) {
|
||||
$task->unassign();
|
||||
}
|
||||
$task->save();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Simple page callback for viewing a task.
|
||||
*
|
||||
* @param TMGMTLocalTaskItem $item
|
||||
* The viewed task item
|
||||
*
|
||||
* @return array
|
||||
* A renderable array.
|
||||
*/
|
||||
function tmgmt_local_task_item_view(TMGMTLocalTaskItem $item) {
|
||||
return entity_view($item->entityType(), array($item), 'full', NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign task to current user.
|
||||
*
|
||||
* @param TMGMTLocalTask $task
|
||||
*/
|
||||
function tmgmt_local_translation_assign_to_me(TMGMTLocalTask $task) {
|
||||
$task->assign($GLOBALS['user']);
|
||||
$task->save();
|
||||
drupal_goto('translate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Form callback for translating a job item.
|
||||
*/
|
||||
function tmgmt_local_translation_form($form, &$form_state, TMGMTLocalTaskItem $task_item) {
|
||||
$form_state['task'] = $task_item->getTask();
|
||||
$form_state['task_item'] = $task_item;
|
||||
$form_state['job_item'] = $job_item = $task_item->getJobItem();
|
||||
|
||||
$job = $job_item->getJob();
|
||||
|
||||
if ($job->getSetting('job_comment')) {
|
||||
$form['job_comment'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Job comment'),
|
||||
'#markup' => filter_xss($job->getSetting('job_comment')),
|
||||
);
|
||||
}
|
||||
|
||||
$form['translation'] = array(
|
||||
'#type' => 'container',
|
||||
);
|
||||
|
||||
// Build the translation form.
|
||||
$data = $job_item->getData();
|
||||
|
||||
// Need to keep the first hierarchy. So flatten must take place inside
|
||||
// of the foreach loop.
|
||||
$zebra = 'even';
|
||||
// Reverse the order to get the correct order.
|
||||
foreach (array_reverse(element_children($data)) as $key) {
|
||||
$flattened = tmgmt_flatten_data($data[$key], $key);
|
||||
$form['translation'][$key] = tmgmt_local_translation_form_element($flattened, $task_item, $zebra);
|
||||
}
|
||||
|
||||
// Add the form actions as well.
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['save_as_completed'] = array(
|
||||
'#type' => 'submit',
|
||||
'#validate' => array('tmgmt_local_translation_form_save_as_completed_validate'),
|
||||
'#submit' => array('tmgmt_local_translation_form_save_submit', 'tmgmt_local_translation_form_save_as_completed_submit'),
|
||||
'#value' => t('Save as completed'),
|
||||
);
|
||||
$form['actions']['save'] = array(
|
||||
'#type' => 'submit',
|
||||
'#submit' => array('tmgmt_local_translation_form_save_submit'),
|
||||
'#value' => t('Save'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form validate callback for save as completed submit action.
|
||||
*
|
||||
* Verify that all items are translated.
|
||||
*/
|
||||
function tmgmt_local_translation_form_save_as_completed_validate($form, &$form_state) {
|
||||
// Loop over all data items and verify that there is a translation in there.
|
||||
foreach ($form_state['values'] as $key => $value) {
|
||||
if (is_array($value) && isset($value['translation'])) {
|
||||
if (empty($value['translation'])) {
|
||||
form_set_error($key . '[translation]', t('Missing translation.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submit callback for save as completed submit action.
|
||||
*
|
||||
* Change items to needs review state and task to completed status.
|
||||
*/
|
||||
function tmgmt_local_translation_form_save_as_completed_submit($form, &$form_state) {
|
||||
/**
|
||||
* @var TMGMTLocalTask $task.
|
||||
*/
|
||||
$task = $form_state['task'];
|
||||
|
||||
/**
|
||||
* @var TMGMTLocalTaskItem $task_item.
|
||||
*/
|
||||
$task_item = $form_state['task_item'];
|
||||
$task_item->completed();
|
||||
$task_item->save();
|
||||
|
||||
// Mark the task as completed if all assigned job items are at needs done.
|
||||
$all_done = TRUE;
|
||||
foreach ($task->getItems() as $item) {
|
||||
if ($item->isPending()) {
|
||||
$all_done = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($all_done) {
|
||||
$task->setStatus(TMGMT_LOCAL_TASK_STATUS_COMPLETED);
|
||||
// If the task is now completed, redirect back to the overview.
|
||||
$form_state['redirect'] = 'translate';
|
||||
}
|
||||
else {
|
||||
// If there are more task items, redirect back to the task.
|
||||
$uri = $task->uri();
|
||||
$form_state['redirect'] = $uri['path'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @var TMGMTJobItem $job_item.
|
||||
*/
|
||||
$job_item = $form_state['job_item'];
|
||||
|
||||
// Add the translations to the job item.
|
||||
$job_item->addTranslatedData($task_item->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submit callback for save action.
|
||||
*
|
||||
* Saves all items.
|
||||
*/
|
||||
function tmgmt_local_translation_form_save_submit($form, &$form_state) {
|
||||
/**
|
||||
* @var TMGMTTaskItem $task_item.
|
||||
*/
|
||||
$task_item = $form_state['task_item'];
|
||||
|
||||
// Write the translated data into the task item.
|
||||
form_state_values_clean($form_state);
|
||||
foreach ($form_state['values'] as $key => $value) {
|
||||
if (is_array($value) && isset($value['translation'])) {
|
||||
$update['#text'] = $value['translation'];
|
||||
$task_item->updateData($key, $update);
|
||||
}
|
||||
}
|
||||
$task_item->save();
|
||||
|
||||
$task = $form_state['task'];
|
||||
$uri = $task->uri();
|
||||
$form_state['redirect'] = $uri['path'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a translation form element.
|
||||
*/
|
||||
function tmgmt_local_translation_form_element($data, TMGMTLocalTaskItem $item, &$zebra) {
|
||||
static $flip = array(
|
||||
'even' => 'odd',
|
||||
'odd' => 'even',
|
||||
);
|
||||
|
||||
$form = array();
|
||||
|
||||
$job = $item->getJobItem()->getJob();
|
||||
$language_list = language_list();
|
||||
|
||||
foreach (element_children($data) as $key) {
|
||||
if (isset($data[$key]['#text']) && _tmgmt_filter_data($data[$key])) {
|
||||
// The char sequence '][' confuses the form API so we need to replace it.
|
||||
$target_key = str_replace('][', '|', $key);
|
||||
$zebra = $flip[$zebra];
|
||||
$form[$target_key] = array(
|
||||
'#tree' => TRUE,
|
||||
'#ajaxid' => drupal_html_id('tmgmt-local-element-' . $key),
|
||||
'#theme' => 'tmgmt_local_translation_form_element',
|
||||
'#parent_label' => $data[$key]['#parent_label'],
|
||||
'#zebra' => $zebra,
|
||||
);
|
||||
|
||||
$source_language = $language_list[$job->source_language];
|
||||
$target_language = $language_list[$job->target_language];
|
||||
|
||||
$form[$target_key]['source'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => $source_language->name,
|
||||
'#value' => $data[$key]['#text'],
|
||||
'#disabled' => TRUE,
|
||||
'#allow_focus' => TRUE,
|
||||
);
|
||||
|
||||
$form[$target_key]['translation'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => $target_language->name,
|
||||
'#default_value' => $item->getData(tmgmt_ensure_keys_array($key), '#text'),
|
||||
//'#required' => TRUE,
|
||||
);
|
||||
|
||||
$form[$target_key]['actions'] = array(
|
||||
'#type' => 'container',
|
||||
);
|
||||
$status = $item->getData(tmgmt_ensure_keys_array($key), '#status');
|
||||
$completed = $status == TMGMT_DATA_ITEM_STATE_TRANSLATED;
|
||||
if ($completed) {
|
||||
$form[$target_key]['actions']['reject-' . $target_key] = array(
|
||||
'#type' => 'submit',
|
||||
// Unicode character ✗ BALLOT X
|
||||
'#value' => '✗',
|
||||
'#attributes' => array('title' => t('Reject')),
|
||||
'#name' => 'reject-' . $target_key,
|
||||
'#submit' => array('tmgmt_local_translation_form_update_state_submit'),
|
||||
'#ajax' => array(
|
||||
'callback' => 'tmgmt_local_translation_form_update_state_ajax',
|
||||
'wrapper' => $form[$target_key]['#ajaxid'],
|
||||
),
|
||||
'#tmgmt_local_action' => 'reject',
|
||||
'#tmgmt_local_key' => str_replace('][', '|', $key),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form[$target_key]['actions']['finish-' . $target_key] = array(
|
||||
'#type' => 'submit',
|
||||
// Unicode character ✓ CHECK MARK
|
||||
'#value' => '✓',
|
||||
'#attributes' => array('title' => t('Finish')),
|
||||
'#name' => 'finish-' . $target_key,
|
||||
'#submit' => array('tmgmt_local_translation_form_update_state_submit'),
|
||||
'#ajax' => array(
|
||||
'callback' => 'tmgmt_local_translation_form_update_state_ajax',
|
||||
'wrapper' => $form[$target_key]['#ajaxid'],
|
||||
),
|
||||
'#tmgmt_local_action' => 'finish',
|
||||
'#tmgmt_local_key' => str_replace('][', '|', $key),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submit callback for the translation state update button.
|
||||
*/
|
||||
function tmgmt_local_translation_form_update_state_submit($form, &$form_state) {
|
||||
$values = $form_state['values'];
|
||||
|
||||
/**
|
||||
* @var TMGMTLocalTaskItem $item.
|
||||
*/
|
||||
$item = $form_state['task_item'];
|
||||
|
||||
$action = $form_state['triggering_element']['#tmgmt_local_action'];
|
||||
$key = $form_state['triggering_element']['#tmgmt_local_key'];
|
||||
|
||||
// Write the translated data into the job item.
|
||||
if (isset($values[$key]) && is_array($values[$key]) && isset($values[$key]['translation'])) {
|
||||
$update['#status'] = $action == 'finish' ? TMGMT_DATA_ITEM_STATE_TRANSLATED : TMGMT_DATA_ITEM_STATE_PENDING;
|
||||
$update['#text'] = $values[$key]['translation'];
|
||||
$item->updateData($key, $update);
|
||||
$item->save();
|
||||
|
||||
// We need to rebuild form so we get updated action button state.
|
||||
$form_state['rebuild'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax callback for the job item review form.
|
||||
*/
|
||||
function tmgmt_local_translation_form_update_state_ajax($form, &$form_state) {
|
||||
$key = array_slice($form_state['triggering_element']['#array_parents'], 0, 3);
|
||||
$commands = array();
|
||||
$render_data = drupal_array_get_nested_value($form, $key);
|
||||
$commands[] = ajax_command_replace(NULL, drupal_render($render_data));
|
||||
tmgmt_ui_write_request_messages($form_state['job_item']->getJob());
|
||||
$commands[] = ajax_command_html('#tmgmt-status-messages-' . strtolower($render_data['#parent_label'][0]), theme('status_messages'));
|
||||
return array('#type' => 'ajax', '#commands' => $commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Form for assigning multiple tasks to translator.
|
||||
*
|
||||
* @param array $form
|
||||
* @param array $form_state
|
||||
* @param string $tasks
|
||||
* Comma separated tasks ids.
|
||||
*
|
||||
* @return array
|
||||
* Drupal form definition.
|
||||
*/
|
||||
function tmgmt_local_translation_assign_form($form, &$form_state, $tasks) {
|
||||
$form_state['tasks'] = explode(',', $tasks);
|
||||
|
||||
$roles = tmgmt_local_translator_roles();
|
||||
if (empty($roles)) {
|
||||
drupal_set_message(t('No user role has the "provide translation services" permission. <a href="@url">Configure permissions</a> for the Local translator module.',
|
||||
array('@url' => url('admin/people/permissions'))), 'warning');
|
||||
}
|
||||
|
||||
$form['tuid'] = array(
|
||||
'#title' => t('Assign to'),
|
||||
'#type' => 'select',
|
||||
'#empty_option' => t('Select user'),
|
||||
'#options' => tmgmt_local_get_translators_for_tasks($form_state['tasks']),
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Assign tasks'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for form for assigning multiple tasks to translator.
|
||||
*/
|
||||
function tmgmt_local_translation_assign_form_submit($form, &$form_state) {
|
||||
$translator = user_load($form_state['values']['tuid']);
|
||||
|
||||
$how_many = 0;
|
||||
foreach ($form_state['tasks'] as $task_id) {
|
||||
$task = tmgmt_local_task_load($task_id);
|
||||
if ($task) {
|
||||
$task->assign($translator);
|
||||
$task->save();
|
||||
++$how_many;
|
||||
}
|
||||
}
|
||||
|
||||
drupal_set_message(t('Assigned @how_many to translator @translator_name.', array('@how_many' => $how_many, '@translator_name' => $translator->name)));
|
||||
|
||||
$form_state['redirect'] = 'manage-translate';
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to form for assigning multiple tasks to translator, but here we also
|
||||
* allow no user selection.
|
||||
*
|
||||
* @see tmgmt_local_translation_assign_form().
|
||||
*/
|
||||
function tmgmt_local_translation_reassign_form($form, &$form_state, $tasks) {
|
||||
$form_state['tasks'] = explode(',', $tasks);
|
||||
|
||||
$roles = tmgmt_local_translator_roles();
|
||||
if (empty($roles)) {
|
||||
drupal_set_message(t('No user role has the "provide translation services" permission. <a href="@url">Configure permissions</a> for the Local translator module.',
|
||||
array('@url' => url('admin/people/permissions'))), 'warning');
|
||||
}
|
||||
|
||||
$form['tuid'] = array(
|
||||
'#title' => t('Assign to'),
|
||||
'#type' => 'select',
|
||||
'#empty_option' => t('Select user'),
|
||||
'#options' => tmgmt_local_get_translators_for_tasks($form_state['tasks']),
|
||||
);
|
||||
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Reassign tasks'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler for form for reassigning multiple tasks to translator.
|
||||
*/
|
||||
function tmgmt_local_translation_reassign_form_submit($form, &$form_state) {
|
||||
|
||||
if (!empty($form_state['values']['tuid'])) {
|
||||
$translator = user_load($form_state['values']['tuid']);
|
||||
}
|
||||
else {
|
||||
$translator = (object) array('uid' => 0, 'name' => t('none'));
|
||||
}
|
||||
|
||||
$how_many = 0;
|
||||
foreach ($form_state['tasks'] as $task_id) {
|
||||
$task = tmgmt_local_task_load($task_id);
|
||||
if ($task) {
|
||||
if ($translator->uid) {
|
||||
$task->assign($translator);
|
||||
}
|
||||
else {
|
||||
$task->unassign();
|
||||
}
|
||||
$task->save();
|
||||
++$how_many;
|
||||
}
|
||||
}
|
||||
|
||||
drupal_set_message(t('Reassigned @how_many to translator @translator_name.', array('@how_many' => $how_many, '@translator_name' => $translator->name)));
|
||||
|
||||
$form_state['redirect'] = 'manage-translate';
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the user translator plugin controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Local translator plugin controller.
|
||||
*/
|
||||
class TMGMTLocalTranslatorPluginController extends TMGMTDefaultTranslatorPluginController {
|
||||
|
||||
protected $language_pairs = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function requestTranslation(TMGMTJob $job) {
|
||||
$tuid = $job->getSetting('translator');
|
||||
|
||||
// Create local task for this job.
|
||||
$local_task = tmgmt_local_task_create(array(
|
||||
'uid' => $job->uid,
|
||||
'tuid' => $tuid,
|
||||
'tjid' => $job->tjid,
|
||||
'title' => t('Task for !label', array('!label' => $job->defaultLabel())),
|
||||
));
|
||||
// If we have translator then switch to pending state.
|
||||
if ($tuid) {
|
||||
$local_task->status = TMGMT_LOCAL_TASK_STATUS_PENDING;
|
||||
}
|
||||
$local_task->save();
|
||||
|
||||
// Create task items.
|
||||
foreach ($job->getItems() as $item) {
|
||||
$local_task->addTaskItem($item);
|
||||
}
|
||||
|
||||
// The translation job has been successfully submitted.
|
||||
$job->submitted();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedTargetLanguages(TMGMTTranslator $translator, $source_language) {
|
||||
$languages = tmgmt_local_supported_target_languages($source_language);
|
||||
if ($translator->getSetting('allow_all')) {
|
||||
$languages += parent::getSupportedTargetLanguages($translator, $source_language);
|
||||
}
|
||||
return $languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedLanguagePairs(TMGMTTranslator $translator) {
|
||||
|
||||
if (!empty($this->language_pairs)) {
|
||||
return $this->language_pairs;
|
||||
}
|
||||
|
||||
$roles = user_roles(TRUE, 'provide translation services');
|
||||
|
||||
$query = db_select('field_data_tmgmt_translation_skills', 'ts');
|
||||
|
||||
$query->join('users', 'u', 'u.uid = ts.entity_id AND u.status = 1');
|
||||
|
||||
$query->addField('ts', 'tmgmt_translation_skills_language_from', 'source_language');
|
||||
$query->addField('ts', 'tmgmt_translation_skills_language_to', 'target_language');
|
||||
|
||||
$query->condition('ts.deleted', 0);
|
||||
$query->condition('ts.entity_type', 'user');
|
||||
|
||||
if (!in_array(DRUPAL_AUTHENTICATED_RID, array_keys($roles))) {
|
||||
$query->join('users_roles', 'ur', 'ur.uid = u.uid AND ur.rid');
|
||||
$or_conditions = db_or()->condition('ur.rid', array_keys($roles))->condition('u.uid', 1);
|
||||
$query->condition($or_conditions);
|
||||
}
|
||||
|
||||
foreach ($query->execute()->fetchAll() as $item) {
|
||||
$this->language_pairs[] = array(
|
||||
'source_language' => $item->source_language,
|
||||
'target_language' => $item->target_language,
|
||||
);
|
||||
}
|
||||
|
||||
return $this->language_pairs;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides the user translator UI plugin controller.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Local translator plugin UI controller.
|
||||
*/
|
||||
class TMGMTLocalTranslatorUIController extends TMGMTDefaultTranslatorUIController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkoutSettingsForm($form, &$form_state, TMGMTJob $job) {
|
||||
if ($translators = tmgmt_local_translators($job->source_language, array($job->target_language))) {
|
||||
$form['translator'] = array(
|
||||
'#title' => t('Select translator for this job'),
|
||||
'#type' => 'select',
|
||||
'#options' => array('' => t('Select user')) + $translators,
|
||||
'#default_value' => $job->getSetting('translator'),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['message'] = array(
|
||||
'#markup' => t('There are no translators available.'),
|
||||
);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function checkoutInfo(TMGMTJob $job) {
|
||||
$label = $job->getTranslator()->label();
|
||||
$form['#title'] = t('@translator translation job information', array('@translator' => $label));
|
||||
$form['#type'] = 'fieldset';
|
||||
|
||||
$tuid = $job->getSetting('translator');
|
||||
if ($tuid && $translator = user_load($tuid)) {
|
||||
$form['job_status'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Job status'),
|
||||
'#markup' => t('Translation job is assigned to %name.', array('%name' => entity_label('user', $translator))),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['job_status'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Job status'),
|
||||
'#markup' => t('Translation job is not assigned to any translator.'),
|
||||
);
|
||||
}
|
||||
|
||||
if ($job->getSetting('job_comment')) {
|
||||
$form['job_comment'] = array(
|
||||
'#type' => 'item',
|
||||
'#title' => t('Job comment'),
|
||||
'#markup' => filter_xss($job->getSetting('job_comment')),
|
||||
);
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
public function pluginSettingsForm($form, &$form_state, TMGMTTranslator $translator, $busy = FALSE) {
|
||||
$form['allow_all'] = array(
|
||||
'#title' => t('Allow translations for enabled languages even if no translator has the necessary capabilities'),
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => $translator->getSetting('allow_all'),
|
||||
);
|
||||
return $form;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Renders a tables containing a group of data items belonging to the same field.
|
||||
*/
|
||||
function theme_tmgmt_local_translation_form($variables) {
|
||||
$result = '';
|
||||
|
||||
// Get through all available translations.
|
||||
$translation = $variables['element']['translation'];
|
||||
foreach (element_children($translation) as $children_name) {
|
||||
$children = $translation[$children_name];
|
||||
$result .= drupal_render($children);
|
||||
}
|
||||
|
||||
// We have rendered translation children so lets remove them.
|
||||
unset($variables['element']['translation']);
|
||||
|
||||
// Render the rest of elements(form actions, CSRF tokens, etc.)
|
||||
$result .= drupal_render_children($variables['element']);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a table for one data item.
|
||||
*/
|
||||
function theme_tmgmt_local_translation_form_element($variables) {
|
||||
// Theme table which contains source, translation and action state button.
|
||||
$element = $variables['element'];
|
||||
|
||||
$parts = explode('|', $element['#parents'][0]);
|
||||
$header_title = ucfirst(str_replace('_', ' ', $parts[0]));
|
||||
|
||||
// Container for ajax messages.
|
||||
$result = '<div id="tmgmt-status-messages-' . strtolower($element['#parent_label'][0]) . '"></div>';
|
||||
|
||||
$result .= theme('table', array(
|
||||
'attributes' => array('id' => $element['#ajaxid'], 'class' => array($element['#zebra'])),
|
||||
'header' => array(array(
|
||||
'data' => $header_title,
|
||||
'colspan' => 3,
|
||||
)),
|
||||
'rows' => array(
|
||||
array(
|
||||
'data' => array(
|
||||
drupal_render($element['source']),
|
||||
drupal_render($element['translation']),
|
||||
drupal_render($element['actions']),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
return $result;
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
name = Translation Language capabilities
|
||||
description = Provides a field that allows users to select language combinations.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Implements hook_field_info().
|
||||
*/
|
||||
function tmgmt_language_combination_field_info() {
|
||||
$info['tmgmt_language_combination'] = array(
|
||||
'label' => t('Language combination'),
|
||||
'description' => t('Allows the definition of language combinations (e.g. "From english to german").'),
|
||||
'default_widget' => 'tmgmt_language_combination_default',
|
||||
'default_formatter' => 'tmgmt_language_combination_default',
|
||||
'settings' => array(
|
||||
'user_register_form' => 1,
|
||||
),
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_schema().
|
||||
*/
|
||||
function tmgmt_language_combination_field_schema($field) {
|
||||
$schema = array(
|
||||
'columns' => array(
|
||||
'language_from' => array(
|
||||
'description' => 'The langcode of the language from which the user is able to translate.',
|
||||
'type' => 'varchar',
|
||||
'length' => 10,
|
||||
),
|
||||
'language_to' => array(
|
||||
'description' => 'The langcode of the language to which the user is able to translate.',
|
||||
'type' => 'varchar',
|
||||
'length' => 10,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'language' => array('language_from', 'language_to'),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_info().
|
||||
*/
|
||||
function tmgmt_language_combination_field_widget_info() {
|
||||
$info['tmgmt_language_combination_default'] = array(
|
||||
'label' => t('Select list'),
|
||||
'description' => t('Default widget for allowing users to define translation combination.'),
|
||||
'field types' => array('tmgmt_language_combination'),
|
||||
'behaviors' => array(
|
||||
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
|
||||
'default value' => FIELD_BEHAVIOR_DEFAULT,
|
||||
),
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_form().
|
||||
*/
|
||||
function tmgmt_language_combination_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
|
||||
if (isset($form_state['list_all_languages'])) {
|
||||
$languages_options = tmgmt_language_combination_languages_predefined_list();
|
||||
}
|
||||
else {
|
||||
$languages_options = array();
|
||||
foreach (language_list() as $code => $language) {
|
||||
$languages_options[$code] = $language->name;
|
||||
}
|
||||
}
|
||||
|
||||
$options = array('_none' => t('- None -')) + $languages_options;
|
||||
|
||||
$element['language_from'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('From'),
|
||||
'#options' => $options,
|
||||
'#default_value' => isset($items[$delta]) ? $items[$delta]['language_from'] : '',
|
||||
'#attributes' => array('class' => array('from-language')),
|
||||
);
|
||||
|
||||
$element['language_to'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('To'),
|
||||
'#options' => $options,
|
||||
'#default_value' => isset($items[$delta]) ? $items[$delta]['language_to'] : '',
|
||||
'#attributes' => array('class' => array('to-language')),
|
||||
);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_validate().
|
||||
*/
|
||||
function tmgmt_language_combination_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
|
||||
$existing = array();
|
||||
|
||||
foreach ($items as $delta => $item) {
|
||||
$key = $item['language_from'] . ':' . $item['language_to'];
|
||||
|
||||
if (!tmgmt_language_combination_field_is_empty($item, 'tmgmt_language_combination')) {
|
||||
if ($item['language_from'] == $item['language_to']) {
|
||||
$errors[$field['field_name']][$langcode][$delta][] = array(
|
||||
'error' => 'tmgmt_language_combination_equal',
|
||||
'message' => t("%name: The 'from' and 'to' language fields can't have the same value.", array('%name' => $instance['label'])),
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($existing[$key])) {
|
||||
$errors[$field['field_name']][$langcode][$delta][] = array(
|
||||
'error' => 'tmgmt_language_combination_equal',
|
||||
'message' => t('%name: The language combination has to be unique.', array('%name' => $instance['label'])),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$existing[$key] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_error().
|
||||
*/
|
||||
function tmgmt_language_combination_field_widget_error($element, $error, $form, &$form_state) {
|
||||
form_error($element, $error['message']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_is_empty().
|
||||
*/
|
||||
function tmgmt_language_combination_field_is_empty($item, $field) {
|
||||
if (empty($item['language_from']) || empty($item['language_to']) || $item['language_from'] == '_none' || $item['language_to'] == '_none') {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_formatter_info().
|
||||
*/
|
||||
function tmgmt_language_combination_field_formatter_info() {
|
||||
$info['tmgmt_language_combination_default'] = array(
|
||||
'label' => t('Default'),
|
||||
'description' => t('Default formatter for displaying the translation combination of a user.'),
|
||||
'field types' => array('tmgmt_language_combination'),
|
||||
);
|
||||
|
||||
$info['tmgmt_language_combination_table'] = array(
|
||||
'label' => t('Table'),
|
||||
'description' => t('Formatter for displaying the translation combination of a user in a table.'),
|
||||
'field types' => array('tmgmt_language_combination'),
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_formatter_view().
|
||||
*/
|
||||
function tmgmt_language_combination_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
|
||||
$element = array();
|
||||
|
||||
switch ($display['type']) {
|
||||
case 'tmgmt_language_combination_default':
|
||||
$element['#theme'] = 'item_list';
|
||||
$element['#items'] = array();
|
||||
|
||||
foreach ($items as $delta => $item) {
|
||||
$from = tmgmt_language_combination_language_label($item['language_from']);
|
||||
$to = tmgmt_language_combination_language_label($item['language_to']);
|
||||
$element['#items'][$delta]['data'] = t('From @from to @to', array('@from' => $from, '@to' => $to));
|
||||
$element['#items'][$delta]['class'][] = drupal_html_class($from . '-' . $to) . '">';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'tmgmt_language_combination_table':
|
||||
$rows = array();
|
||||
|
||||
foreach ($items as $item) {
|
||||
$to = tmgmt_language_combination_language_label($item['language_to']);
|
||||
$from = tmgmt_language_combination_language_label($item['language_from']);
|
||||
$row[] = array(
|
||||
'data' => $from,
|
||||
'class' => array('from-language', drupal_html_class('language-' . $from)),
|
||||
);
|
||||
|
||||
$row[] = array(
|
||||
'data' => $to,
|
||||
'class' => array('to-language', drupal_html_class('language-' . $to)),
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
'data' => $row,
|
||||
'class' => array(drupal_html_class($from . '-' . $to)),
|
||||
);
|
||||
}
|
||||
|
||||
$element = array(
|
||||
'#theme' => 'table',
|
||||
'#header' => array(t('From'), t('To')),
|
||||
'#rows' => $rows,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_update().
|
||||
*/
|
||||
function tmgmt_language_combination_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
|
||||
$languages = language_list();
|
||||
$added_languages = array();
|
||||
|
||||
// In case the skill languages is not know to the system, install them.
|
||||
foreach ($items as $item) {
|
||||
if (!isset($languages[$item['language_to']]) && !isset($added_languages[$item['language_to']])) {
|
||||
locale_add_language($item['language_to']);
|
||||
$added_languages[$item['language_to']] = $item['language_to'];
|
||||
}
|
||||
if (!isset($languages[$item['language_from']]) && !isset($added_languages[$item['language_from']])) {
|
||||
locale_add_language($item['language_from']);
|
||||
$added_languages[$item['language_from']] = $item['language_from'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label of a language.
|
||||
*
|
||||
* @todo Remove this once the core language label function is fixed.
|
||||
*
|
||||
* @param $language
|
||||
* A language in ISO format.
|
||||
* @return string
|
||||
* The label of the language or an empty string if the language or its label
|
||||
* are not defined.
|
||||
*/
|
||||
function tmgmt_language_combination_language_label($language) {
|
||||
$languages = tmgmt_language_combination_languages_predefined_list();
|
||||
if (!empty($languages[$language])) {
|
||||
return $languages[$language];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a language code list for a select form item with all languages.
|
||||
*/
|
||||
function tmgmt_language_combination_languages_predefined_list() {
|
||||
$predefined = &drupal_static(__FUNCTION__);
|
||||
|
||||
if (!isset($predefined)) {
|
||||
include_once DRUPAL_ROOT . '/includes/iso.inc';
|
||||
$predefined = _locale_get_predefined_list();
|
||||
|
||||
foreach ($predefined as $key => $value) {
|
||||
|
||||
// Include native name in output, if possible
|
||||
if (count($value) > 1) {
|
||||
$tname = t($value[0]);
|
||||
$predefined[$key] = ($tname == $value[1]) ? $tname : "$tname ($value[1])";
|
||||
}
|
||||
else {
|
||||
$predefined[$key] = t($value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add custom languages that are not part of the iso.inc definition.
|
||||
$installed_languages = language_list();
|
||||
foreach ($installed_languages as $lang => $info) {
|
||||
if (!isset($predefined[$lang])) {
|
||||
$predefined[$lang] = $info->name;
|
||||
}
|
||||
}
|
||||
|
||||
asort($predefined);
|
||||
}
|
||||
|
||||
return $predefined;
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* API documentation for the tmgmt_local module.
|
||||
*/
|
||||
|
@@ -0,0 +1,37 @@
|
||||
name = Local Translator
|
||||
description = Allows local users to provide translation services.
|
||||
package = Translation Management
|
||||
core = 7.x
|
||||
|
||||
dependencies[] = tmgmt
|
||||
dependencies[] = tmgmt_language_combination
|
||||
|
||||
configure = admin/config/regional/tmgmt_translator
|
||||
|
||||
files[] = controller/tmgmt_local.controller.task.inc
|
||||
files[] = controller/tmgmt_local.controller.task_item.inc
|
||||
files[] = entity/tmgmt_local.entity.task.inc
|
||||
files[] = entity/tmgmt_local.entity.task_item.inc
|
||||
files[] = includes/tmgmt_local.info.inc
|
||||
files[] = includes/tmgmt_local.plugin.inc
|
||||
files[] = includes/tmgmt_local.plugin.ui.inc
|
||||
files[] = controller/tmgmt_local.ui_controller.task.inc
|
||||
files[] = controller/tmgmt_local.ui_controller.task_item.inc
|
||||
files[] = tmgmt_local.test
|
||||
|
||||
; Views integration and handlers
|
||||
files[] = views/tmgmt_local.views.inc
|
||||
files[] = views/handlers/tmgmt_local_task_handler_field_job_item_count.inc
|
||||
files[] = views/handlers/tmgmt_local_task_handler_field_loop_count.inc
|
||||
files[] = views/handlers/tmgmt_local_task_handler_field_operations.inc
|
||||
files[] = views/handlers/tmgmt_local_task_handler_field_progress.inc
|
||||
files[] = views/handlers/tmgmt_local_task_handler_field_item_operations.inc
|
||||
files[] = views/handlers/tmgmt_local_task_handler_field_wordcount.inc
|
||||
files[] = views/handlers/tmgmt_local_task_handler_filter_eligible.inc
|
||||
|
||||
; Information added by Drupal.org packaging script on 2016-09-21
|
||||
version = "7.x-1.0-rc2+1-dev"
|
||||
core = "7.x"
|
||||
project = "tmgmt"
|
||||
datestamp = "1474446494"
|
||||
|
@@ -0,0 +1,387 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Installation hooks for tmgmt_local module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function tmgmt_local_schema() {
|
||||
$schema['tmgmt_local_task'] = array(
|
||||
'description' => 'A tmgmt local task connects translator user with assigned job items and provide additional workflow data.',
|
||||
'fields' => array(
|
||||
'tltid' => array(
|
||||
'description' => 'The identifier of the task.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'uid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => "User's {users}.uid for task creator.",
|
||||
),
|
||||
'created' => array(
|
||||
'description' => 'The Unix timestamp when the task was created.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'changed' => array(
|
||||
'description' => 'The Unix timestamp when the task was most recently saved.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'title' => array(
|
||||
'description' => 'Task title.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
),
|
||||
'tuid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Assigned translator user {users}.uid.',
|
||||
),
|
||||
'tjid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => "Translation job {tmgmt_job}.tjid that belongs to task.",
|
||||
),
|
||||
'status' => array(
|
||||
'description' => 'The status of the task.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'loop_count' => array(
|
||||
'description' => 'Counter for how many times this task was returned to translator.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('tltid'),
|
||||
'indexes' => array(
|
||||
'tuid' => array('tuid'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'author' => array(
|
||||
'table' => 'users',
|
||||
'columns' => array('uid' => 'uid'),
|
||||
),
|
||||
'translator' => array(
|
||||
'table' => 'users',
|
||||
'columns' => array('tuid' => 'uid'),
|
||||
),
|
||||
'job' => array(
|
||||
'table' => 'tmgmt_job',
|
||||
'columns' => array('tjid' => 'tjid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$schema['tmgmt_local_task_item'] = array(
|
||||
'description' => 'A tmgmt local task item contains additional workflow data for a job item.',
|
||||
'fields' => array(
|
||||
'tltiid' => array(
|
||||
'description' => 'The identifier of the task item.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tltid' => array(
|
||||
'description' => 'Translation job task {tmgmt_local_task}.tltid that belongs to task.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tjiid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => "Translation job item {tmgmt_job_item}.tjiid that belongs to task.",
|
||||
),
|
||||
'status' => array(
|
||||
'description' => 'The status of the task.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'data' => array(
|
||||
'description' => 'Stores translations and translation statuses',
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'count_untranslated' => array(
|
||||
'description' => 'Counter for all untranslated data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'count_translated' => array(
|
||||
'description' => 'Counter for all translated data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'count_completed' => array(
|
||||
'description' => 'Counter for all completed data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('tltiid'),
|
||||
'indexes' => array(
|
||||
'tltid' => array('tltid'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'task' => array(
|
||||
'table' => 'tmgmt_local_task',
|
||||
'columns' => array('tltid' => 'tltid'),
|
||||
),
|
||||
'job_item' => array(
|
||||
'table' => 'tmgmt_job_item',
|
||||
'columns' => array('tjiid' => 'tjiid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {tmgmt_local_task_item} table.
|
||||
*/
|
||||
function tmgmt_local_update_7000() {
|
||||
$schema = array(
|
||||
'description' => 'A tmgmt local task item contains additional workflow data for a job item.',
|
||||
'fields' => array(
|
||||
'tltiid' => array(
|
||||
'description' => 'The identifier of the task item.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tltid' => array(
|
||||
'description' => 'Translation job task {tmgmt_local_task}.tltid that belongs to task.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'tjiid' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => "Translation job item {tmgmt_job_item}.tjiid that belongs to task.",
|
||||
),
|
||||
'status' => array(
|
||||
'description' => 'The status of the task.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'data' => array(
|
||||
'description' => 'Stores translations and translation statuses',
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
'size' => 'big',
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'count_untranslated' => array(
|
||||
'description' => 'Counter for all untranslated data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'count_translated' => array(
|
||||
'description' => 'Counter for all translated data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'count_completed' => array(
|
||||
'description' => 'Counter for all completed data items.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('tltiid'),
|
||||
'indexes' => array(
|
||||
'tltid' => array('tltid'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'task' => array(
|
||||
'table' => 'tmgmt_local_task',
|
||||
'columns' => array('tltid' => 'tltid'),
|
||||
),
|
||||
'job_item' => array(
|
||||
'table' => 'tmgmt_job_item',
|
||||
'columns' => array('tjiid' => 'tjiid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
db_create_table('tmgmt_local_task_item', $schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_enable().
|
||||
*/
|
||||
function tmgmt_local_enable() {
|
||||
// If our field type is not found rebuild the cache.
|
||||
if (!field_info_field_types('tmgmt_language_combination')) {
|
||||
field_cache_clear();
|
||||
}
|
||||
|
||||
// Create the language combination field if it doesn't exist yet.
|
||||
if (!field_info_field('tmgmt_translation_skills')) {
|
||||
$field = array(
|
||||
'type' => 'tmgmt_language_combination',
|
||||
'field_name' => 'tmgmt_translation_skills',
|
||||
'translatable' => FALSE,
|
||||
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
|
||||
'locked' => FALSE,
|
||||
);
|
||||
|
||||
field_create_field($field);
|
||||
}
|
||||
|
||||
// Attach the language skills field collection to the user entity.
|
||||
if (!field_info_instance('user', 'tmgmt_translation_skills', 'user')) {
|
||||
$instance = array(
|
||||
'field_name' => 'tmgmt_translation_skills',
|
||||
'entity_type' => 'user',
|
||||
'bundle' => 'user',
|
||||
'label' => t('Translation skills'),
|
||||
);
|
||||
|
||||
field_create_instance($instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function tmgmt_local_uninstall() {
|
||||
field_delete_field('tmgmt_translation_skills');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create local task items.
|
||||
*/
|
||||
function tmgmt_local_update_7001() {
|
||||
$result = db_query('SELECT lt.tltid, ji.tjiid, ji.state, ji.data from {tmgmt_local_task} lt INNER JOIN {tmgmt_job} j ON j.tjid = lt.tjid INNER JOIN {tmgmt_job_item} ji ON ji.tjid = j.tjid');
|
||||
$insert = db_insert('tmgmt_local_task_item')
|
||||
->fields(array('tltid', 'tjiid', 'status', 'data'));
|
||||
module_load_include('module', 'tmgmt');
|
||||
foreach ($result as $row) {
|
||||
$data = unserialize($row->data);
|
||||
$data_count = count(array_filter(tmgmt_flatten_data($data), '_tmgmt_filter_data'));
|
||||
$translation = (array)_tmgmt_local_translated_data_7001($data);
|
||||
$values =array(
|
||||
'tltid' => $row->tltid,
|
||||
'tjiid' => $row->tjiid,
|
||||
'count_untranslated' => 0,
|
||||
'count_translated' => 0,
|
||||
'count_translated' => 0,
|
||||
'status' => 0,
|
||||
'data' => serialize($translation),
|
||||
);
|
||||
switch ($row->state) {
|
||||
case 2:
|
||||
// Job item state needs review is task item status translated.
|
||||
$values['status'] = 1;
|
||||
$values['count_translated'] = $data_count;
|
||||
break;
|
||||
case 3:
|
||||
// Job item state accepted is task item status completed.
|
||||
$values['status'] = 3;
|
||||
$values['count_completed'] = $data_count;
|
||||
break;
|
||||
case 1:
|
||||
default:
|
||||
// Job item state active is task item status untranslated.
|
||||
$values['status'] = 0;
|
||||
$values['count_untranslated'] = $data_count;
|
||||
break;
|
||||
}
|
||||
$insert->values($values);
|
||||
}
|
||||
$insert->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data parsing helper function for tmgmt_local_update_7001().
|
||||
*
|
||||
* Copies #translation texts from the source data array to the translation
|
||||
* data array.
|
||||
*
|
||||
* @param array $source
|
||||
* The original source data array of the job item.
|
||||
*
|
||||
* @return array
|
||||
* The filled translation data array.
|
||||
*/
|
||||
function _tmgmt_local_translated_data_7001($source) {
|
||||
if (!empty($source['#translation']['#text'])) {
|
||||
$translation['#text'] = $source['#translation']['#text'];
|
||||
if ((!empty($source['#status']))) {
|
||||
$translation['#status'] = $source['#status'];
|
||||
}
|
||||
return $translation;
|
||||
}
|
||||
else {
|
||||
$translation = array();
|
||||
foreach (element_children($source) as $key) {
|
||||
if ($return = _tmgmt_local_translated_data_7001($source[$key])) {
|
||||
$translation[$key] = $return;
|
||||
}
|
||||
}
|
||||
if (!empty($translation)) {
|
||||
return $translation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the tmgmt_language_combination module is enabled.
|
||||
*/
|
||||
function tmgmt_local_update_7002() {
|
||||
// Enable the tmgmt_language_combionation module.
|
||||
module_enable(array('tmgmt_language_combination'));
|
||||
|
||||
// Remove the tmgmt_skills module.
|
||||
db_delete('system')
|
||||
->condition('name', 'tmgmt_skills')
|
||||
->condition('type', 'module')
|
||||
->execute();
|
||||
|
||||
// Create the field if it doesn't exist yet.
|
||||
if (!field_info_field_types('tmgmt_language_combination')) {
|
||||
field_cache_clear();
|
||||
}
|
||||
|
||||
// Create the language combination field if it doesn't exist yet.
|
||||
if (!field_info_field('tmgmt_translation_skills')) {
|
||||
$field = array(
|
||||
'type' => 'tmgmt_language_combination',
|
||||
'field_name' => 'tmgmt_translation_skills',
|
||||
'translatable' => FALSE,
|
||||
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
|
||||
'locked' => FALSE,
|
||||
);
|
||||
|
||||
field_create_field($field);
|
||||
}
|
||||
|
||||
// Attach the language skills field collection to the user entity.
|
||||
if (!field_info_instance('user', 'tmgmt_translation_skills', 'user')) {
|
||||
$instance = array(
|
||||
'field_name' => 'tmgmt_translation_skills',
|
||||
'entity_type' => 'user',
|
||||
'bundle' => 'user',
|
||||
'label' => t('Translation skills'),
|
||||
);
|
||||
|
||||
field_create_instance($instance);
|
||||
}
|
||||
}
|
@@ -0,0 +1,903 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Main module file for the local translation module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup tmgmt_local_task TMGMT Local Task
|
||||
* @{
|
||||
* Various local task API functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Modules should return this value from hook_tmgmt_local_translation_access()
|
||||
* to allow access to a node.
|
||||
*/
|
||||
define('TMGMT_LOCAL_TRANSLATION_ACCESS_ALLOW', 'allow');
|
||||
|
||||
/**
|
||||
* Modules should return this value from hook_tmgmt_local_translation_access()
|
||||
* to deny access to a node.
|
||||
*/
|
||||
define('TMGMT_LOCAL_TRANSLATION_ACCESS_DENY', 'deny');
|
||||
|
||||
/**
|
||||
* Modules should return this value from hook_tmgmt_local_translation_access()
|
||||
* to not affect node access.
|
||||
*/
|
||||
define('TMGMT_LOCAL_TRANSLATION_ACCESS_IGNORE', NULL);
|
||||
|
||||
/**
|
||||
* Translation task is not assigned to translator.
|
||||
*/
|
||||
define('TMGMT_LOCAL_TASK_STATUS_UNASSIGNED', 0);
|
||||
|
||||
/**
|
||||
* Translation task is pending.
|
||||
*/
|
||||
define('TMGMT_LOCAL_TASK_STATUS_PENDING', 1);
|
||||
|
||||
/**
|
||||
* Translation task is completed (all job items are translated).
|
||||
*/
|
||||
define('TMGMT_LOCAL_TASK_STATUS_COMPLETED', 2);
|
||||
|
||||
/**
|
||||
* Translation task is rejected (at least some job items are rejected).
|
||||
*/
|
||||
define('TMGMT_LOCAL_TASK_STATUS_REJECTED', 3);
|
||||
|
||||
/**
|
||||
* Translation task is closed.
|
||||
*/
|
||||
define('TMGMT_LOCAL_TASK_STATUS_CLOSED', 4);
|
||||
|
||||
/**
|
||||
* Translation task item is untranslated.
|
||||
*/
|
||||
define('TMGMT_LOCAL_TASK_ITEM_STATUS_PENDING', 0);
|
||||
|
||||
/**
|
||||
* Translation task item is translated and pending review of the job item.
|
||||
*/
|
||||
define('TMGMT_LOCAL_TASK_ITEM_STATUS_COMPLETED', 1);
|
||||
|
||||
/**
|
||||
* Translation job item has been rejected and the task needs to be updated.
|
||||
*/
|
||||
define('TMGMT_LOCAL_TASK_ITEM_STATUS_REJECTED', 2);
|
||||
|
||||
/**
|
||||
* The translation task item has been completed and closed.
|
||||
*/
|
||||
define('TMGMT_LOCAL_TASK_ITEM_STATUS_CLOSED', 3);
|
||||
|
||||
/**
|
||||
* Untranslated translation data item.
|
||||
*/
|
||||
define('TMGMT_DATA_ITEM_STATE_UNTRANSLATED', 0);
|
||||
|
||||
/**
|
||||
* @} End of "tmgmt_local_task".
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_entity_info().
|
||||
*/
|
||||
function tmgmt_local_entity_info() {
|
||||
$info = array(
|
||||
'tmgmt_local_task' => array(
|
||||
'label' => t('Translation Task'),
|
||||
'module' => 'tmgmt_local',
|
||||
'entity class' => 'TMGMTLocalTask',
|
||||
'controller class' => 'TMGMTLocalTaskController',
|
||||
'metadata controller class' => 'TMGMTLocalTaskMetadataController',
|
||||
'views controller class' => 'TMGMTLocalTaskViewsController',
|
||||
'base table' => 'tmgmt_local_task',
|
||||
'uri callback' => 'entity_class_uri',
|
||||
'label callback' => 'entity_class_label',
|
||||
'access callback' => 'tmgmt_local_task_access',
|
||||
'entity keys' => array(
|
||||
'id' => 'tltid',
|
||||
),
|
||||
'admin ui' => array(
|
||||
'controller class' => 'TMGMTLocalTaskUIController',
|
||||
'path' => 'translate',
|
||||
),
|
||||
),
|
||||
'tmgmt_local_task_item' => array(
|
||||
'label' => t('Translation Task Item'),
|
||||
'module' => 'tmgmt_local',
|
||||
'entity class' => 'TMGMTLocalTaskItem',
|
||||
'controller class' => 'TMGMTLocalTaskItemController',
|
||||
'metadata controller class' => 'TMGMTLocalTaskItemMetadataController',
|
||||
'views controller class' => 'TMGMTLocalTaskItemViewsController',
|
||||
'base table' => 'tmgmt_local_task_item',
|
||||
'uri callback' => 'entity_class_uri',
|
||||
'label callback' => 'entity_class_label',
|
||||
'access callback' => 'tmgmt_local_task_item_access',
|
||||
'entity keys' => array(
|
||||
'id' => 'tltiid',
|
||||
),
|
||||
'admin ui' => array(
|
||||
'controller class' => 'TMGMTLocalTaskItemUIController',
|
||||
'path' => 'translate',
|
||||
),
|
||||
),
|
||||
);
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function tmgmt_local_theme() {
|
||||
return array(
|
||||
'tmgmt_local_translation_form' => array(
|
||||
'render element' => 'element',
|
||||
'file' => 'includes/tmgmt_local.theme.inc',
|
||||
),
|
||||
'tmgmt_local_translation_form_element' => array(
|
||||
'render element' => 'element',
|
||||
'file' => 'includes/tmgmt_local.theme.inc',
|
||||
),
|
||||
// @todo - not implemented.
|
||||
'tmgmt_local_translation_form_element_status' => array(
|
||||
'render element' => 'status',
|
||||
'file' => 'includes/tmgmt_local.theme.inc',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function tmgmt_local_menu() {
|
||||
$items['translate/%tmgmt_local_task/assign-to-me'] = array(
|
||||
'title' => 'Assign translation task to me',
|
||||
'description' => 'Assign translation task to current translator user.',
|
||||
'page callback' => 'tmgmt_local_translation_assign_to_me',
|
||||
'page arguments' => array(1),
|
||||
'access callback' => 'tmgmt_local_translation_access',
|
||||
'access arguments' => array(1),
|
||||
'file' => 'includes/tmgmt_local.pages.inc',
|
||||
);
|
||||
$items['manage-translate/assign-tasks'] = array(
|
||||
'title' => 'Assign translation task',
|
||||
'description' => 'Assign translation tasks to specific translator.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('tmgmt_local_translation_assign_form', 2),
|
||||
'access arguments' => array('administer translation tasks'),
|
||||
'file' => 'includes/tmgmt_local.pages.inc',
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
$items['manage-translate/reassign-tasks'] = array(
|
||||
'title' => 'Reassign translation task',
|
||||
'description' => 'Ressign translation tasks to specific translator.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('tmgmt_local_translation_reassign_form', 2),
|
||||
'access arguments' => array('administer translation tasks'),
|
||||
'file' => 'includes/tmgmt_local.pages.inc',
|
||||
'type' => MENU_CALLBACK,
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_permission().
|
||||
*/
|
||||
function tmgmt_local_permission() {
|
||||
return array(
|
||||
'provide translation services' => array(
|
||||
'title' => t('Provide translation services'),
|
||||
'descriptions' => t('Root permission for translation access: Users with this permission are eligible to be granted translation access to a translation task.'),
|
||||
),
|
||||
'administer translation tasks' => array(
|
||||
'title' => t('Administer translation tasks'),
|
||||
'description' => t('Administer translation tasks.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_api().
|
||||
*/
|
||||
function tmgmt_local_views_api() {
|
||||
return array(
|
||||
'api' => 3.0,
|
||||
'path' => drupal_get_path('module', 'tmgmt_local') . '/views',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_default_views().
|
||||
*/
|
||||
function tmgmt_local_views_default_views() {
|
||||
return _tmgmt_load_exports('tmgmt_local', 'views', 'view.inc', 'view');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_translator_plugin_info().
|
||||
*/
|
||||
function tmgmt_local_tmgmt_translator_plugin_info() {
|
||||
$info['local'] = array(
|
||||
'label' => t('Local Translator'),
|
||||
'description' => t('Allows local users to process translation jobs.'),
|
||||
'plugin controller class' => 'TMGMTLocalTranslatorPluginController',
|
||||
'ui controller class' => 'TMGMTLocalTranslatorUIController',
|
||||
|
||||
'map remote languages' => FALSE,
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @addtogroup tmgmt_local
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determine whether the current user is allowed to translate a given
|
||||
* translation task.
|
||||
*
|
||||
* @param $task
|
||||
* The translation task to be translated.
|
||||
* @param $account
|
||||
* (Optional) A user object representing the user that is trying to obtain
|
||||
* translation access. Determines access for a user other than the current
|
||||
* user.
|
||||
* @return bool
|
||||
* TRUE if the user is allowed to translate the given translation job, FALSE
|
||||
* otherwise.
|
||||
*/
|
||||
function tmgmt_local_translation_access(TMGMTLocalTask $task, $account = NULL) {
|
||||
$job = $task->getJob();
|
||||
|
||||
if (!$job || !$job->isActive()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$rights = &drupal_static(__FUNCTION__);
|
||||
|
||||
// If no user object is supplied, the access check is for the current user.
|
||||
if (empty($account)) {
|
||||
$account = $GLOBALS['user'];
|
||||
}
|
||||
|
||||
// If we've already checked access for this job and user, return from cache.
|
||||
if (isset($rights[$account->uid][$job->tjid])) {
|
||||
return $rights[$account->uid][$job->tjid];
|
||||
}
|
||||
|
||||
// We grant access to the translation if both of the following conditions are
|
||||
// met:
|
||||
// - User is assigned as a translator to the given task.
|
||||
// - User has 'provide translation services' permission.
|
||||
// - No modules say to deny access.
|
||||
// - At least one module says to grant access.
|
||||
// - User has translation capabilities for this task.
|
||||
if (!user_access('provide translation services')) {
|
||||
$rights[$account->uid][$job->tjid] = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ($task->tuid == $account->uid) {
|
||||
$rights[$account->uid][$job->tjid] = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
$access = module_invoke_all('tmgmt_local_translation_access', $job, $account);
|
||||
if (in_array(TMGMT_LOCAL_TRANSLATION_ACCESS_DENY, $access, TRUE)) {
|
||||
$rights[$account->uid][$job->tjid] = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
elseif (in_array(TMGMT_LOCAL_TRANSLATION_ACCESS_ALLOW, $access, TRUE)) {
|
||||
$rights[$account->uid][$job->tjid] = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Lastly, check for the translation capabilities.
|
||||
$target_languages = tmgmt_local_supported_target_languages($job->source_language, array($account->uid));
|
||||
$rights[$account->uid][$job->tjid] = in_array($job->target_language, $target_languages);
|
||||
|
||||
return $rights[$account->uid][$job->tjid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for clearing the languages cache of all local translators.
|
||||
*
|
||||
* Can be used in oder to clear the cache for supported target languages after
|
||||
* the translation capabilities of an local have changed.
|
||||
*/
|
||||
function tmgmt_local_clear_languages_cache() {
|
||||
$query = new EntityFieldQuery();
|
||||
$query->entityCondition('entity_type', 'tmgmt_translator');
|
||||
$query->propertyCondition('plugin', 'local');
|
||||
$result = $query->execute();
|
||||
if ($result) {
|
||||
foreach (tmgmt_translator_load_multiple(array_keys($result['tmgmt_translator'])) as $translator) {
|
||||
$translator->clearLanguageCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a local translation task entity.
|
||||
*
|
||||
* @return TMGMTLocalTask
|
||||
*/
|
||||
function tmgmt_local_task_load($tltid) {
|
||||
return entity_load_single('tmgmt_local_task', $tltid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads local translation tasks entity.
|
||||
*/
|
||||
function tmgmt_local_task_load_multiple(array $tltids = array(), $conditions = array()) {
|
||||
return entity_load('tmgmt_local_task', $tltids, $conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a local translation task items entity.
|
||||
*
|
||||
* @return TMGMTLocalTaskItem
|
||||
*/
|
||||
function tmgmt_local_task_item_load($tltiid) {
|
||||
return entity_load_single('tmgmt_local_task_item', $tltiid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads local translation task items entity.
|
||||
*/
|
||||
function tmgmt_local_task_item_load_multiple(array $tltiids = array(), $conditions = array()) {
|
||||
return entity_load('tmgmt_local_task_item', $tltiids, $conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a translation task entity.
|
||||
*
|
||||
* @param $values
|
||||
* (Optional) An array of additional entity values.
|
||||
*
|
||||
* @return TMGMTLocalTask
|
||||
* The local translation task entity.
|
||||
*/
|
||||
function tmgmt_local_task_create(array $values = array()) {
|
||||
return entity_create('tmgmt_local_task', $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes multiple local tasks entities.
|
||||
*
|
||||
* @param $tltids
|
||||
* An array of local tasks IDs.
|
||||
*/
|
||||
function tmgmt_local_task_delete_multiple(array $tltids) {
|
||||
entity_get_controller('tmgmt_local_task')->delete($tltids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback for the local task entity.
|
||||
*
|
||||
* @param $op
|
||||
* The operation being performed.
|
||||
* @param $item
|
||||
* (Optional) A TMGMTLocalTask entity to check access for. If no entity is
|
||||
* given, it will be determined whether access is allowed for all entities.
|
||||
* @param $account
|
||||
* (Optional) The user to check for. Leave it to NULL to check for the global
|
||||
* user.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if access is allowed, FALSE otherwise.
|
||||
*/
|
||||
function tmgmt_local_task_access($op, $task = NULL, $account = NULL) {
|
||||
if (user_access('administer tmgmt', $account) || user_access('administer translation tasks', $account)) {
|
||||
// Administrators can do everything.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!$account) {
|
||||
global $user;
|
||||
$account = $user;
|
||||
}
|
||||
|
||||
// @todo - probably need refinement when we introduce more module permissions.
|
||||
switch ($op) {
|
||||
case 'view':
|
||||
case 'update':
|
||||
return user_access('provide translation services', $account);
|
||||
break;
|
||||
case 'unassign':
|
||||
return !empty($task->tuid) && $task->tuid == $account->uid && user_access('provide translation services', $account);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback for the local task item entity.
|
||||
*
|
||||
* @param $op
|
||||
* The operation being performed.
|
||||
* @param $item
|
||||
* (Optional) A TMGMTLocalTaskItem entity to check access for. If no entity is
|
||||
* given, it will be determined whether access is allowed for all entities.
|
||||
* @param $account
|
||||
* (Optional) The user to check for. Leave it to NULL to check for the global
|
||||
* user.
|
||||
*
|
||||
* @return boolean
|
||||
* TRUE if access is allowed, FALSE otherwise.
|
||||
*/
|
||||
function tmgmt_local_task_item_access($op, TMGMTLocalTaskItem $item = NULL, $account = NULL) {
|
||||
$task = NULL;
|
||||
if ($item) {
|
||||
$task = $item->getTask();
|
||||
}
|
||||
return entity_access($op, 'tmgmt_local_task', $task, $account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an array with the word and status statistics of a task.
|
||||
*
|
||||
* @param $tltids
|
||||
* An array of local task ids.
|
||||
*
|
||||
* @return
|
||||
* An array of objects with the keys word_count, count_pending,
|
||||
* count_accepted, count_translated and loop_count.
|
||||
*/
|
||||
function tmgmt_local_task_statistics_load(array $tltids) {
|
||||
$statistics = &drupal_static(__FUNCTION__, array());
|
||||
|
||||
// First try to get the values from the cache.
|
||||
$return = array();
|
||||
$tltids_to_load = array();
|
||||
foreach ($tltids as $tltid) {
|
||||
if (isset($statistics[$tltid])) {
|
||||
// Info exists in cache, get it from there.
|
||||
$return[$tltid] = $statistics[$tltid];
|
||||
}
|
||||
else {
|
||||
// Info doesn't exist in cache, add job to the list that needs to be
|
||||
// fetched.
|
||||
$tltids_to_load[] = $tltid;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are remaining jobs, build a query to fetch them.
|
||||
if (!empty($tltids_to_load)) {
|
||||
// Build the query to fetch the statistics.
|
||||
$query = db_select('tmgmt_local_task_item', 'tlti');
|
||||
$query->join('tmgmt_local_task', 'tlt', 'tlt.tltid = tlti.tltid');
|
||||
$query->join('tmgmt_job_item', 'tji', 'tji.tjiid = tlti.tjiid');
|
||||
$query->fields('tlt', array('tltid'));
|
||||
$query->addExpression('SUM(tji.word_count)', 'word_count');
|
||||
$query->addExpression('SUM(tlti.count_untranslated)', 'count_untranslated');
|
||||
$query->addExpression('SUM(tlti.count_translated)', 'count_translated');
|
||||
$query->addExpression('SUM(tlti.count_completed)', 'count_completed');
|
||||
$result = $query->groupBy('tlt.tltid')
|
||||
->condition('tlt.tltid', $tltids_to_load)
|
||||
->execute();
|
||||
|
||||
foreach ($result as $row) {
|
||||
$return[$row->tltid] = $statistics[$row->tltid] = $row;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific statistic of a task.
|
||||
*
|
||||
* @param $task
|
||||
* The translation task entity.
|
||||
* @param $key
|
||||
* One of word_count, loop_count, count_pending, count_accepted and
|
||||
* count_translated.
|
||||
*
|
||||
* @return
|
||||
* The requested information as an integer.
|
||||
*/
|
||||
function tmgmt_local_task_statistic(TMGMTLocalTask $task, $key) {
|
||||
$statistics = tmgmt_local_task_statistics_load(array($task->tltid));
|
||||
if (isset($statistics[$task->tltid]->$key)) {
|
||||
return $statistics[$task->tltid]->$key;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a labeled list of all available statuses.
|
||||
*
|
||||
* @return array
|
||||
* A list of all available statuses.
|
||||
*/
|
||||
function tmgmt_local_task_statuses() {
|
||||
return $statuses = array(
|
||||
TMGMT_LOCAL_TASK_STATUS_UNASSIGNED => t('Unassigned'),
|
||||
TMGMT_LOCAL_TASK_STATUS_PENDING => t('Pending'),
|
||||
TMGMT_LOCAL_TASK_STATUS_COMPLETED => t('Completed'),
|
||||
TMGMT_LOCAL_TASK_STATUS_REJECTED => t('Rejected'),
|
||||
TMGMT_LOCAL_TASK_STATUS_CLOSED => t('Closed'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a labeled list of all available statuses for task items.
|
||||
*
|
||||
* @return array
|
||||
* A list of all available statuses.
|
||||
*/
|
||||
function tmgmt_local_task_item_statuses() {
|
||||
return $statuses = array(
|
||||
TMGMT_LOCAL_TASK_ITEM_STATUS_PENDING => t('Untranslated'),
|
||||
TMGMT_LOCAL_TASK_ITEM_STATUS_COMPLETED => t('Translated'),
|
||||
TMGMT_LOCAL_TASK_ITEM_STATUS_REJECTED => t('Rejected'),
|
||||
TMGMT_LOCAL_TASK_ITEM_STATUS_CLOSED => t('Completed'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all involved language pairs for given tasks.
|
||||
*
|
||||
* @param array $tasks
|
||||
* Array of tasks ids.
|
||||
*
|
||||
* @return array
|
||||
* Array of involved languages.
|
||||
*/
|
||||
function tmgmt_local_tasks_languages($tasks) {
|
||||
$query = db_select('tmgmt_local_task', 't');
|
||||
$query->condition('tltid', $tasks);
|
||||
$query->join('tmgmt_job', 'j', 't.tjid = j.tjid');
|
||||
$query->fields('j', array('source_language', 'target_language'));
|
||||
$query->groupBy('target_language');
|
||||
$result = $query->execute();
|
||||
$languages = array();
|
||||
|
||||
foreach ($result as $row) {
|
||||
if (empty($languages[$row->source_language]) || !in_array($row->target_language, $languages[$row->source_language])) {
|
||||
$languages[$row->source_language][] = $row->target_language;
|
||||
}
|
||||
}
|
||||
|
||||
return $languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets translators able to translate all given tasks.
|
||||
*
|
||||
* @param array $tasks
|
||||
* Array of tasks ids.
|
||||
*
|
||||
* @return array
|
||||
* List of uid => name values.
|
||||
*/
|
||||
function tmgmt_local_get_translators_for_tasks($tasks) {
|
||||
$translators = array();
|
||||
|
||||
foreach (tmgmt_local_tasks_languages($tasks) as $source_language => $target_languages) {
|
||||
$translators[] = tmgmt_local_translators($source_language, $target_languages);
|
||||
}
|
||||
|
||||
if (count($translators) > 1) {
|
||||
return call_user_func_array('array_intersect', $translators);
|
||||
}
|
||||
elseif (count($translators) == 1) {
|
||||
return array_shift($translators);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup tmgmt_local_task".
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_job_item_update().
|
||||
*
|
||||
* @todo: Move this to the translator plugin API.
|
||||
*/
|
||||
function tmgmt_local_tmgmt_job_item_update(TMGMTJobItem $job_item) {
|
||||
if ($job_item->isAccepted() && !$job_item->original->isAccepted()) {
|
||||
$tltiid = db_query('SELECT tltiid FROM {tmgmt_local_task_item} ti INNER JOIN {tmgmt_local_task} t ON ti.tltid = t.tltid WHERE t.tjid = :tjid AND ti.tjiid = :tjiid', array(':tjid' => $job_item->tjid, ':tjiid' => $job_item->tjiid))->fetchField();
|
||||
if ($tltiid) {
|
||||
$task_item = tmgmt_local_task_item_load($tltiid);
|
||||
$task_item->closed();
|
||||
$task_item->save();
|
||||
|
||||
// Check if the task can be marked as closed as well.
|
||||
$query = new EntityFieldQuery();
|
||||
$unclosed_tasks = $query->entityCondition('entity_type', 'tmgmt_local_task_item')
|
||||
->propertyCondition('tltid', $task_item->tltid)
|
||||
->propertyCondition('status', TMGMT_LOCAL_TASK_ITEM_STATUS_CLOSED, '<>')
|
||||
->count()
|
||||
->execute();
|
||||
if ($unclosed_tasks == 0) {
|
||||
$task = $task_item->getTask();
|
||||
$task->setStatus(TMGMT_LOCAL_TASK_STATUS_CLOSED);
|
||||
$task->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tmgmt_job_delete().
|
||||
*/
|
||||
function tmgmt_local_tmgmt_job_delete(TMGMTJob $job) {
|
||||
$query = new EntityFieldQuery();
|
||||
$result = $query
|
||||
->entityCondition('entity_type', 'tmgmt_local_task')
|
||||
->propertyCondition('tjid', $job->tjid)
|
||||
->execute();
|
||||
if (!empty($result['tmgmt_local_task'])) {
|
||||
entity_delete_multiple('tmgmt_local_task', array_keys($result['tmgmt_local_task']));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_field_access().
|
||||
*/
|
||||
function tmgmt_local_field_access($op, $field, $entity_type, $entity = NULL, $account = NULL) {
|
||||
if ($field['field_name'] == 'tmgmt_translation_skills' && $entity_type == 'user') {
|
||||
$account = !empty($account) ? $account : $GLOBALS['user'];
|
||||
|
||||
// If the field is just being viewed, grant access.
|
||||
if ($op == 'view') {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// User can provide transl. services and is dealing with own account.
|
||||
if (!empty($entity) && $entity->uid == $account->uid) {
|
||||
return user_access('provide translation services', $entity);
|
||||
}
|
||||
|
||||
// Administrators are allowed to deal with others only.
|
||||
if (user_access('administer tmgmt', $account)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_insert().
|
||||
*/
|
||||
function tmgmt_local_field_attach_insert($entity_type, $entity) {
|
||||
if ($entity_type != 'user' || !array_intersect_key(user_roles(TRUE, 'provide translation services'), $entity->roles)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tmgmt_local_clear_languages_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_update().
|
||||
*/
|
||||
function tmgmt_local_field_attach_update($entity_type, $entity) {
|
||||
if ($entity_type != 'user' || !array_intersect_key(user_roles(TRUE, 'provide translation services'), $entity->roles)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tmgmt_local_clear_languages_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_attach_delete().
|
||||
*/
|
||||
function tmgmt_local_field_attach_delete($entity_type, $entity) {
|
||||
if ($entity_type != 'user' || !array_intersect_key(user_roles(TRUE, 'provide translation services'), $entity->roles)) {
|
||||
return;
|
||||
}
|
||||
tmgmt_local_clear_languages_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets list of language pairs.
|
||||
*
|
||||
* @param string $source_language
|
||||
* Source language code for which to limit the selection.
|
||||
* @param array $uids
|
||||
* List of user ids for whom to get the language pairs.
|
||||
*
|
||||
* @return array
|
||||
* List of language pairs where a pair is defined by associative array of
|
||||
* source_language and target_language keys.
|
||||
*/
|
||||
function tmgmt_local_supported_language_pairs($source_language = NULL, $uids = array()) {
|
||||
$language_pairs = &drupal_static(__FUNCTION__);
|
||||
$cache_key = $source_language . '_' . implode('_', $uids);
|
||||
|
||||
if (isset($language_pairs[$cache_key])) {
|
||||
return $language_pairs[$cache_key];
|
||||
}
|
||||
|
||||
$language_pairs[$cache_key] = array();
|
||||
|
||||
foreach (tmgmt_local_capabilities($source_language, NULL, $uids) as $row) {
|
||||
// Prevent duplicates.
|
||||
$pair_key = $row->tmgmt_translation_skills_language_from . '__' . $row->tmgmt_translation_skills_language_to;
|
||||
$language_pairs[$cache_key][$pair_key] = array(
|
||||
'source_language' => $row->tmgmt_translation_skills_language_from,
|
||||
'target_language' => $row->tmgmt_translation_skills_language_to,
|
||||
);
|
||||
}
|
||||
|
||||
return $language_pairs[$cache_key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets supported target languages.
|
||||
*
|
||||
* @param string $source_language
|
||||
* Source language for which to get target languages.
|
||||
* @param array $uids
|
||||
* List of user ids for whom to get the target languages.
|
||||
*
|
||||
* @return array
|
||||
* List of target languages where code is the key as well as the value.
|
||||
*/
|
||||
function tmgmt_local_supported_target_languages($source_language, $uids = array()) {
|
||||
$pairs = tmgmt_local_supported_language_pairs($source_language, $uids);
|
||||
$supported_languages = array();
|
||||
|
||||
foreach ($pairs as $pair) {
|
||||
$supported_languages[$pair['target_language']] = $pair['target_language'];
|
||||
}
|
||||
|
||||
return $supported_languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets local translator for given language combination.
|
||||
*
|
||||
* @param string $source_language
|
||||
* (optional) Source language to limit on.
|
||||
* @param array $target_languages
|
||||
* (optional) List of target languages to limit to.
|
||||
*
|
||||
* @return array
|
||||
* Array of uid => name translators or empty array if there are no translator
|
||||
* users.
|
||||
*/
|
||||
function tmgmt_local_translators($source_language = NULL, array $target_languages = array()) {
|
||||
$translators = &drupal_static(__FUNCTION__);
|
||||
|
||||
$key = $source_language . '_' . implode('_', $target_languages);
|
||||
|
||||
if (isset($translators[$key])) {
|
||||
return $translators[$key];
|
||||
}
|
||||
|
||||
// Get all capabilities keyed by uids for given source language.
|
||||
$translators_capabilities = array();
|
||||
foreach (tmgmt_local_capabilities($source_language) as $row) {
|
||||
$translators_capabilities[$row->uid][] = $row->tmgmt_translation_skills_language_to;
|
||||
}
|
||||
|
||||
// Filter out translator uids who's capabilities are not sufficient for given
|
||||
// target languages.
|
||||
$translators_uids = array();
|
||||
foreach ($translators_capabilities as $uid => $translator_capabilities) {
|
||||
// In case provided target languages exceed users capabilities exclude.
|
||||
if (!empty($target_languages) && count(array_diff($target_languages, $translator_capabilities)) > 0) {
|
||||
continue;
|
||||
}
|
||||
$translators_uids[] = $uid;
|
||||
}
|
||||
|
||||
// Finally build the translators list.
|
||||
$translators[$key] = array();
|
||||
if (!empty($translators_uids)) {
|
||||
foreach (user_load_multiple($translators_uids) as $account) {
|
||||
$translators[$key][$account->uid] = entity_label('user', $account);
|
||||
}
|
||||
}
|
||||
|
||||
return $translators[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets language capabilities.
|
||||
*
|
||||
* @param string $source_language
|
||||
* (optional) Limit the source language.
|
||||
* @param string $target_language
|
||||
* (optional) Limit the target language.
|
||||
* @param array $uids
|
||||
* (optional) Limit to specific users.
|
||||
*
|
||||
* @return array
|
||||
* Array of language capabilities with following data:
|
||||
* - tmgmt_translation_skills_language_from
|
||||
* - tmgmt_translation_skills_language_to
|
||||
* - uid
|
||||
* - name
|
||||
* - mail
|
||||
*/
|
||||
function tmgmt_local_capabilities($source_language = NULL, $target_language = NULL, $uids = array()) {
|
||||
|
||||
$roles = tmgmt_local_translator_roles();
|
||||
// If there are no roles that have the required permission, return an empty
|
||||
// array.
|
||||
if (empty($roles)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$query = db_select('field_data_tmgmt_translation_skills', 'ts')
|
||||
->fields('ts', array('tmgmt_translation_skills_language_from', 'tmgmt_translation_skills_language_to'))
|
||||
->condition('ts.deleted', 0)
|
||||
->condition('ts.entity_type', 'user');
|
||||
|
||||
if ($source_language) {
|
||||
$query->condition('ts.tmgmt_translation_skills_language_from', $source_language);
|
||||
}
|
||||
|
||||
if ($target_language) {
|
||||
$query->condition('ts.tmgmt_translation_skills_language_to', $target_language);
|
||||
}
|
||||
|
||||
// Join only active users.
|
||||
$query->innerJoin('users', 'u', 'u.uid = ts.entity_id AND u.status = 1');
|
||||
$query->fields('u', array('uid', 'name', 'mail'));
|
||||
|
||||
if (!empty($uids)) {
|
||||
$query->condition('u.uid', $uids);
|
||||
}
|
||||
|
||||
// If the authenticated user role has the required permission we do not have
|
||||
// to do the role check.
|
||||
if (!in_array('authenticated user', $roles)) {
|
||||
$query->leftJoin('users_roles', 'ur', "ur.uid = u.uid AND ur.rid IN (:roles)", array(':roles' => array_keys($roles)));
|
||||
}
|
||||
|
||||
// Add a tag so other modules can alter this query at will.
|
||||
$query->addTag('tmgmt_translation_combination');
|
||||
|
||||
return $query->execute()->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get roles with 'provide translation services' permissions.
|
||||
*
|
||||
* @return array
|
||||
* An associative array with the role id as the key and the role name as
|
||||
* value.
|
||||
*/
|
||||
function tmgmt_local_translator_roles() {
|
||||
return user_roles(TRUE, 'provide translation services');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_rules_action_info_alter().
|
||||
*/
|
||||
function tmgmt_local_rules_action_info_alter(&$actions) {
|
||||
if (isset($actions['component_rules_tmgmt_local_task_assign_to_me'])) {
|
||||
$actions['component_rules_tmgmt_local_task_assign_to_me']['access callback'] = 'tmgmt_local_rules_component_access';
|
||||
}
|
||||
if (isset($actions['component_rules_tmgmt_local_task_assign'])) {
|
||||
$actions['component_rules_tmgmt_local_task_assign']['access callback'] = 'tmgmt_local_rules_component_access';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback to translation tasks rules component actions.
|
||||
*/
|
||||
function tmgmt_local_rules_component_access($type, $name) {
|
||||
switch ($name) {
|
||||
case 'component_rules_tmgmt_local_task_assign_to_me':
|
||||
return user_access('provide translation services');
|
||||
case 'component_rules_tmgmt_local_task_assign';
|
||||
return user_access('administer translation tasks');
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Rules integration.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_rules_action_info().
|
||||
*/
|
||||
function tmgmt_local_rules_action_info() {
|
||||
$info['tmgmt_local_rules_task_assign'] = array(
|
||||
'label' => t('Assign translation task'),
|
||||
'group' => t('Translation Management'),
|
||||
'parameter' => array(
|
||||
'task' => array(
|
||||
'type' => 'tmgmt_local_task',
|
||||
'label' => t('Translation task'),
|
||||
'description' => t('The translation task that should be assigned to the configured user.'),
|
||||
),
|
||||
'user' => array(
|
||||
'type' => 'user',
|
||||
'label' => t('user'),
|
||||
'description' => t('The assigned user.'),
|
||||
),
|
||||
),
|
||||
);
|
||||
$info['tmgmt_local_rules_task_unassign'] = array(
|
||||
'label' => t('Unassign translation task'),
|
||||
'group' => t('Translation Management'),
|
||||
'parameter' => array(
|
||||
'task' => array(
|
||||
'type' => 'tmgmt_local_task',
|
||||
'label' => t('Translation task'),
|
||||
'description' => t('The translation task which will be unassigned.'),
|
||||
),
|
||||
),
|
||||
'access callback' => 'tmgmt_local_rules_task_access',
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules callback to assign a translation task to translator.
|
||||
*/
|
||||
function tmgmt_local_rules_task_assign(TMGMTLocalTask $task, stdClass $translator) {
|
||||
$task->assign($translator);
|
||||
$task->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules callback to unassign a translation task.
|
||||
*/
|
||||
function tmgmt_local_rules_task_unassign(TMGMTLocalTask $task) {
|
||||
$task->unassign();
|
||||
$task->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback to the unassign task rules action.
|
||||
*/
|
||||
function tmgmt_local_rules_task_access() {
|
||||
return user_access('administer translation tasks') || user_access('provide translation services');
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* @file
|
||||
* Contains default rules.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_default_rules_configuration().
|
||||
*/
|
||||
function tmgmt_local_default_rules_configuration() {
|
||||
$data = '{ "rules_tmgmt_local_task_assign_to_me" : {
|
||||
"LABEL" : "Assign Translation Task To Me",
|
||||
"PLUGIN" : "rule",
|
||||
"REQUIRES" : [ "tmgmt_local" ],
|
||||
"USES VARIABLES" : { "task" : { "label" : "Task", "type" : "tmgmt_local_task" } },
|
||||
"DO" : [
|
||||
{ "tmgmt_local_rules_task_assign" : { "task" : [ "task" ], "user" : [ "site:current-user" ] } }
|
||||
]
|
||||
}
|
||||
}';
|
||||
$rule = rules_import($data);
|
||||
$configs[$rule->name] = $rule;
|
||||
|
||||
$data = ' { "rules_tmgmt_local_task_assign" : {
|
||||
"LABEL" : "Assign Translation Task",
|
||||
"PLUGIN" : "rule",
|
||||
"REQUIRES" : [ "tmgmt" ],
|
||||
"USES VARIABLES" : {
|
||||
"task" : { "label" : "Task", "type" : "tmgmt_local_task" },
|
||||
"translator" : { "label" : "Translator", "type" : "user" }
|
||||
},
|
||||
"DO" : [
|
||||
{ "tmgmt_local_rules_task_assign" : {
|
||||
"task" : [ "task" ],
|
||||
"translator" : [ "translator" ]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}';
|
||||
$rule = rules_import($data);
|
||||
$configs[$rule->name] = $rule;
|
||||
|
||||
$data = ' { "rules_tmgmt_local_task_unassign" : {
|
||||
"LABEL" : "Unassign Translation Task",
|
||||
"PLUGIN" : "rule",
|
||||
"REQUIRES" : [ "tmgmt_local" ],
|
||||
"USES VARIABLES" : { "task" : { "label" : "Task", "type" : "tmgmt_local_task" } },
|
||||
"DO" : [ { "tmgmt_local_rules_task_unassign" : { "task" : [ "task" ] } } ]
|
||||
}
|
||||
}';
|
||||
$rule = rules_import($data);
|
||||
$configs[$rule->name] = $rule;
|
||||
|
||||
return $configs;
|
||||
}
|
@@ -0,0 +1,614 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test cases for the local translator module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic tests for the local translator.
|
||||
*/
|
||||
class TMGMTLocalTestCase extends TMGMTBaseTestCase {
|
||||
|
||||
/**
|
||||
* Translator user.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $local_translator;
|
||||
|
||||
protected $local_translator_permissions = array(
|
||||
'provide translation services',
|
||||
);
|
||||
|
||||
static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Local Translator tests',
|
||||
'description' => 'Tests the local translator plugin integration.',
|
||||
'group' => 'Translation Management',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp(array('tmgmt_language_combination', 'tmgmt_local', 'tmgmt_ui'));
|
||||
$this->loginAsAdmin();
|
||||
$this->setEnvironment('de');
|
||||
}
|
||||
|
||||
function testTranslatorSkillsForTasks() {
|
||||
|
||||
$this->setEnvironment('fr');
|
||||
|
||||
$translator1 = $this->drupalCreateUser($this->local_translator_permissions);
|
||||
$this->drupalLogin($translator1);
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][0][language_from]' => 'en',
|
||||
'tmgmt_translation_skills[und][0][language_to]' => 'de',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator1->uid . '/edit', $edit, t('Save'));
|
||||
|
||||
$translator2 = $this->drupalCreateUser($this->local_translator_permissions);
|
||||
$this->drupalLogin($translator2);
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][0][language_from]' => 'en',
|
||||
'tmgmt_translation_skills[und][0][language_to]' => 'de',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator2->uid . '/edit', $edit, t('Save'));
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][1][language_from]' => 'de',
|
||||
'tmgmt_translation_skills[und][1][language_to]' => 'en',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator2->uid . '/edit', $edit, t('Save'));
|
||||
|
||||
$translator3 = $this->drupalCreateUser($this->local_translator_permissions);
|
||||
$this->drupalLogin($translator3);
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][0][language_from]' => 'en',
|
||||
'tmgmt_translation_skills[und][0][language_to]' => 'de',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator3->uid . '/edit', $edit, t('Save'));
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][1][language_from]' => 'de',
|
||||
'tmgmt_translation_skills[und][1][language_to]' => 'en',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator3->uid . '/edit', $edit, t('Save'));
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][2][language_from]' => 'en',
|
||||
'tmgmt_translation_skills[und][2][language_to]' => 'fr',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator3->uid . '/edit', $edit, t('Save'));
|
||||
|
||||
$job1 = $this->createJob('en', 'de');
|
||||
$job2 = $this->createJob('de', 'en');
|
||||
$job3 = $this->createJob('en', 'fr');
|
||||
|
||||
$local_task1 = tmgmt_local_task_create(array(
|
||||
'uid' => $job1->uid,
|
||||
'tjid' => $job1->tjid,
|
||||
'title' => 'Task 1',
|
||||
));
|
||||
$local_task1->save();
|
||||
|
||||
$local_task2 = tmgmt_local_task_create(array(
|
||||
'uid' => $job2->uid,
|
||||
'tjid' => $job2->tjid,
|
||||
'title' => 'Task 2',
|
||||
));
|
||||
$local_task2->save();
|
||||
|
||||
$local_task3 = tmgmt_local_task_create(array(
|
||||
'uid' => $job3->uid,
|
||||
'tjid' => $job3->tjid,
|
||||
'title' => 'Task 3',
|
||||
));
|
||||
$local_task3->save();
|
||||
|
||||
// Test languages involved in tasks.
|
||||
$this->assertEqual(
|
||||
tmgmt_local_tasks_languages(array($local_task1->tltid, $local_task2->tltid, $local_task3->tltid)),
|
||||
array(
|
||||
'en' => array('de', 'fr'),
|
||||
'de' => array('en'),
|
||||
)
|
||||
);
|
||||
|
||||
// Test available translators for task en - de.
|
||||
$this->assertEqual(
|
||||
tmgmt_local_get_translators_for_tasks(array($local_task1->tltid)),
|
||||
array(
|
||||
$translator1->uid => entity_label('user', $translator1),
|
||||
$translator2->uid => entity_label('user', $translator2),
|
||||
$translator3->uid => entity_label('user', $translator3),
|
||||
)
|
||||
);
|
||||
|
||||
// Test available translators for tasks en - de, de - en.
|
||||
$this->assertEqual(
|
||||
tmgmt_local_get_translators_for_tasks(array($local_task1->tltid, $local_task2->tltid)),
|
||||
array(
|
||||
$translator2->uid => entity_label('user', $translator2),
|
||||
$translator3->uid => entity_label('user', $translator3),
|
||||
)
|
||||
);
|
||||
|
||||
// Test available translators for tasks en - de, de - en, en - fr.
|
||||
$this->assertEqual(
|
||||
tmgmt_local_get_translators_for_tasks(array($local_task1->tltid, $local_task2->tltid, $local_task3->tltid)),
|
||||
array(
|
||||
$translator3->uid => entity_label('user', $translator3),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the basic translation workflow
|
||||
*/
|
||||
function testBasicWorkflow() {
|
||||
$translator = tmgmt_translator_load('local');
|
||||
|
||||
// Create a job and request a local translation.
|
||||
$this->loginAsTranslator();
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$job->settings['job_comment'] = $job_comment = 'Dummy job comment';
|
||||
$job->addItem('test_source', 'test', '1');
|
||||
$job->addItem('test_source', 'test', '2');
|
||||
$job->save();
|
||||
$uri = $job->uri();
|
||||
|
||||
// Make sure that the checkout page works as expected when there are no
|
||||
// roles.
|
||||
$this->drupalGet($uri['path']);
|
||||
$this->assertText(t('@translator can not translate from @source to @target.', array('@translator' => 'Local Translator (auto created)', '@source' => 'English', '@target' => 'German')));
|
||||
|
||||
$this->local_translator = $this->drupalCreateUser($this->local_translator_permissions);
|
||||
|
||||
// The same when there is a single role.
|
||||
$this->drupalGet($uri['path']);
|
||||
$this->assertText(t('@translator can not translate from @source to @target.', array('@translator' => 'Local Translator (auto created)', '@source' => 'English', '@target' => 'German')));
|
||||
|
||||
// Create another local translator with the required capabilities.
|
||||
$other_translator_same = $this->drupalCreateUser($this->local_translator_permissions);
|
||||
|
||||
// And test again with two roles but still no capabilities.
|
||||
$this->drupalGet($uri['path']);
|
||||
$this->assertText(t('@translator can not translate from @source to @target.', array('@translator' => 'Local Translator (auto created)', '@source' => 'English', '@target' => 'German')));
|
||||
|
||||
$this->drupalLogin($other_translator_same);
|
||||
// Configure language capabilities.
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][0][language_from]' => 'en',
|
||||
'tmgmt_translation_skills[und][0][language_to]' => 'de',
|
||||
);
|
||||
$this->drupalPost('user/' . $other_translator_same->uid . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check that the user is not listed in the translator selection form.
|
||||
$this->loginAsAdmin();
|
||||
$this->drupalGet($uri['path']);
|
||||
$this->assertText(t('Select translator for this job'));
|
||||
$this->assertText($other_translator_same->name);
|
||||
$this->assertNoText($this->local_translator->name);
|
||||
|
||||
$this->drupalLogin($this->local_translator);
|
||||
// Configure language capabilities.
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][0][language_from]' => 'en',
|
||||
'tmgmt_translation_skills[und][0][language_to]' => 'de',
|
||||
);
|
||||
$this->drupalPost('user/' . $this->local_translator->uid . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check that the translator is now listed.
|
||||
$this->loginAsAdmin();
|
||||
$this->drupalGet($uri['path']);
|
||||
$this->assertText($this->local_translator->name);
|
||||
|
||||
$job->requestTranslation();
|
||||
|
||||
// Test for job comment in the job checkout info pane.
|
||||
$uri = $job->uri();
|
||||
$this->drupalGet($uri['path']);
|
||||
$this->assertText($job_comment);
|
||||
|
||||
$this->drupalLogin($this->local_translator);
|
||||
|
||||
// Create a second local translator with different language capabilities,
|
||||
// make sure that he does not see the task.
|
||||
$other_translator = $this->drupalCreateUser($this->local_translator_permissions);
|
||||
$this->drupalLogin($other_translator);
|
||||
// Configure language capabilities.
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][0][language_from]' => 'de',
|
||||
'tmgmt_translation_skills[und][0][language_to]' => 'en',
|
||||
);
|
||||
$this->drupalPost('user/' . $other_translator->uid . '/edit', $edit, t('Save'));
|
||||
$this->drupalGet('translate');
|
||||
$this->assertNoText(t('Task for @job', array('@job' => $job->label())));
|
||||
|
||||
$this->drupalLogin($this->local_translator);
|
||||
|
||||
// Check the translate overview.
|
||||
$this->drupalGet('translate');
|
||||
$this->assertText(t('Task for @job', array('@job' => $job->label())));
|
||||
// @todo: Fails, encoding problem?
|
||||
//$this->assertText(t('@from => @to', array('@from' => 'en', '@to' => 'de')));
|
||||
$edit = array(
|
||||
'views_bulk_operations[0]' => $job->tjid,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Assign to me'));
|
||||
$this->assertText(t('Performed Assign to me on 1 item.'));
|
||||
|
||||
// Unassign again.
|
||||
$edit = array(
|
||||
'views_bulk_operations[0]' => $job->tjid,
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Unassign'));
|
||||
$this->assertText(t('Performed Unassign on 1 item.'));
|
||||
|
||||
// Now test the assign link.
|
||||
$this->clickLink(t('assign'));
|
||||
|
||||
// Log in with the translator with the same capabilities, make sure that he
|
||||
// does not see the assigned task.
|
||||
$this->drupalLogin($other_translator_same);
|
||||
$this->drupalGet('translate');
|
||||
$this->assertNoText(t('Task for @job', array('@job' => $job->label())));
|
||||
|
||||
$this->drupalLogin($this->local_translator);
|
||||
|
||||
// Translate the task.
|
||||
$this->drupalGet('translate');
|
||||
$this->clickLink(t('view'));
|
||||
|
||||
// Assert created local task and task items.
|
||||
$this->assertTrue(preg_match('|translate/(\d+)|', $this->getUrl(), $matches), 'Task found');
|
||||
$task = tmgmt_local_task_load($matches[1]);
|
||||
$this->assertTrue($task->isPending());
|
||||
$this->assertEqual($task->getCountCompleted(), 0);
|
||||
$this->assertEqual($task->getCountTranslated(), 0);
|
||||
$this->assertEqual($task->getCountUntranslated(), 2);
|
||||
list($first_task_item, $second_task_item) = array_values($task->getItems());
|
||||
$this->assertTrue($first_task_item->isPending());
|
||||
$this->assertEqual($first_task_item->getCountCompleted(), 0);
|
||||
$this->assertEqual($first_task_item->getCountTranslated(), 0);
|
||||
$this->assertEqual($first_task_item->getCountUntranslated(), 1);
|
||||
|
||||
$this->assertText('test_source:test:1');
|
||||
$this->assertText('test_source:test:2');
|
||||
$this->assertText(t('Untranslated'));
|
||||
|
||||
// Translate the first item.
|
||||
$this->clickLink(t('translate'));
|
||||
|
||||
$this->assertText(t('Dummy'));
|
||||
// Job comment is present in the translate tool as well.
|
||||
$this->assertText($job_comment);
|
||||
$this->assertText('test_source:test:1');
|
||||
|
||||
// Try to complete a translation when translations are missing.
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
$this->assertText(t('Missing translation.'));
|
||||
|
||||
$edit = array(
|
||||
'dummy|deep_nesting[translation]' => $translation1 = 'German translation of source 1',
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Save as completed'));
|
||||
|
||||
// Review and accept the first item.
|
||||
entity_get_controller('tmgmt_job_item')->resetCache(array(1));
|
||||
entity_get_controller('tmgmt_local_task')->resetCache();
|
||||
entity_get_controller('tmgmt_local_task_item')->resetCache();
|
||||
drupal_static_reset('tmgmt_local_task_statistics_load');
|
||||
$item1 = tmgmt_job_item_load(1);
|
||||
$item1->acceptTranslation();
|
||||
|
||||
// The first item should be accepted now, the second still in progress.
|
||||
$this->drupalGet('translate/1');
|
||||
$this->assertText(t('Completed'));
|
||||
$this->assertText(t('Untranslated'));
|
||||
|
||||
$task = tmgmt_local_task_load($task->tltid);
|
||||
$this->assertTrue($task->isPending());
|
||||
$this->assertEqual($task->getCountCompleted(), 1);
|
||||
$this->assertEqual($task->getCountTranslated(), 0);
|
||||
$this->assertEqual($task->getCountUntranslated(), 1);
|
||||
list($first_task_item, $second_task_item) = array_values($task->getItems());
|
||||
$this->assertTrue($first_task_item->isClosed());
|
||||
$this->assertEqual($first_task_item->getCountCompleted(), 1);
|
||||
$this->assertEqual($first_task_item->getCountTranslated(), 0);
|
||||
$this->assertEqual($first_task_item->getCountUntranslated(), 0);
|
||||
$this->assertTrue($second_task_item->isPending());
|
||||
$this->assertEqual($second_task_item->getCountCompleted(), 0);
|
||||
$this->assertEqual($second_task_item->getCountTranslated(), 0);
|
||||
$this->assertEqual($second_task_item->getCountUntranslated(), 1);
|
||||
|
||||
// Translate the second item but do not mark as translated it yet.
|
||||
$this->clickLink(t('translate'));
|
||||
$edit = array(
|
||||
'dummy|deep_nesting[translation]' => $translation2 = 'German translation of source 2',
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
// The first item is still completed, the second still untranslated.
|
||||
$this->assertText(t('Completed'));
|
||||
$this->assertText(t('Untranslated'));
|
||||
|
||||
entity_get_controller('tmgmt_local_task')->resetCache();
|
||||
entity_get_controller('tmgmt_local_task_item')->resetCache();
|
||||
drupal_static_reset('tmgmt_local_task_statistics_load');
|
||||
$task = tmgmt_local_task_load($task->tltid);
|
||||
$this->assertTrue($task->isPending());
|
||||
$this->assertEqual($task->getCountCompleted(), 1);
|
||||
$this->assertEqual($task->getCountTranslated(), 0);
|
||||
$this->assertEqual($task->getCountUntranslated(), 1);
|
||||
list($first_task_item, $second_task_item) = array_values($task->getItems());
|
||||
$this->assertTrue($first_task_item->isClosed());
|
||||
$this->assertEqual($first_task_item->getCountCompleted(), 1);
|
||||
$this->assertEqual($first_task_item->getCountTranslated(), 0);
|
||||
$this->assertEqual($first_task_item->getCountUntranslated(), 0);
|
||||
$this->assertTrue($second_task_item->isPending());
|
||||
$this->assertEqual($second_task_item->getCountCompleted(), 0);
|
||||
$this->assertEqual($second_task_item->getCountTranslated(), 0);
|
||||
$this->assertEqual($second_task_item->getCountUntranslated(), 1);
|
||||
|
||||
// Mark the data item as translated but don't save the task item as
|
||||
// completed.
|
||||
$this->clickLink(t('translate'));
|
||||
$this->drupalPost(NULL, array(), t('✓'));
|
||||
|
||||
entity_get_controller('tmgmt_local_task')->resetCache();
|
||||
entity_get_controller('tmgmt_local_task_item')->resetCache();
|
||||
drupal_static_reset('tmgmt_local_task_statistics_load');
|
||||
$task = tmgmt_local_task_load($task->tltid);
|
||||
$this->assertTrue($task->isPending());
|
||||
$this->assertEqual($task->getCountCompleted(), 1);
|
||||
$this->assertEqual($task->getCountTranslated(), 1);
|
||||
$this->assertEqual($task->getCountUntranslated(), 0);
|
||||
list($first_task_item, $second_task_item) = array_values($task->getItems());
|
||||
$this->assertTrue($first_task_item->isClosed());
|
||||
$this->assertEqual($first_task_item->getCountCompleted(), 1);
|
||||
$this->assertEqual($first_task_item->getCountTranslated(), 0);
|
||||
$this->assertEqual($first_task_item->getCountUntranslated(), 0);
|
||||
$this->assertTrue($second_task_item->isPending());
|
||||
$this->assertEqual($second_task_item->getCountCompleted(), 0);
|
||||
$this->assertEqual($second_task_item->getCountTranslated(), 1);
|
||||
$this->assertEqual($second_task_item->getCountUntranslated(), 0);
|
||||
|
||||
// Check the job data.
|
||||
entity_get_controller('tmgmt_job')->resetCache(array($job->tjid));
|
||||
entity_get_controller('tmgmt_job_item')->resetCache();
|
||||
$job = tmgmt_job_load($job->tjid);
|
||||
list($item1, $item2) = array_values($job->getItems());
|
||||
// The text in the first item should be available for review, the
|
||||
// translation of the second item not.
|
||||
$this->assertEqual($item1->getData(array('dummy', 'deep_nesting', '#translation', '#text')), $translation1);
|
||||
$this->assertEqual($item2->getData(array('dummy', 'deep_nesting', '#translation', '#text')), '');
|
||||
|
||||
// Check the overview page, the task should still show in progress.
|
||||
$this->drupalGet('translate');
|
||||
$this->assertText(t('Pending'));
|
||||
|
||||
// Mark the second item as completed now.
|
||||
$this->clickLink(t('view'));
|
||||
$this->clickLink(t('translate'));
|
||||
$this->drupalPost(NULL, array(), t('Save as completed'));
|
||||
|
||||
// Review and accept the second item.
|
||||
entity_get_controller('tmgmt_job_item')->resetCache(array(2));
|
||||
entity_get_controller('tmgmt_local_task')->resetCache();
|
||||
entity_get_controller('tmgmt_local_task_item')->resetCache();
|
||||
drupal_static_reset('tmgmt_local_task_statistics_load');
|
||||
$item1 = tmgmt_job_item_load(2);
|
||||
$item1->acceptTranslation();
|
||||
|
||||
// Refresh the page.
|
||||
$this->drupalGet($this->url);
|
||||
|
||||
$task = tmgmt_local_task_load($task->tltid);
|
||||
$this->assertTrue($task->isClosed());
|
||||
$this->assertEqual($task->getCountCompleted(), 2);
|
||||
$this->assertEqual($task->getCountTranslated(), 0);
|
||||
$this->assertEqual($task->getCountUntranslated(), 0);
|
||||
list($first_task_item, $second_task_item) = array_values($task->getItems());
|
||||
$this->assertTrue($first_task_item->isClosed());
|
||||
$this->assertEqual($first_task_item->getCountCompleted(), 1);
|
||||
$this->assertEqual($first_task_item->getCountTranslated(), 0);
|
||||
$this->assertEqual($first_task_item->getCountUntranslated(), 0);
|
||||
$this->assertTrue($second_task_item->isClosed());
|
||||
$this->assertEqual($second_task_item->getCountCompleted(), 1);
|
||||
$this->assertEqual($second_task_item->getCountTranslated(), 0);
|
||||
$this->assertEqual($second_task_item->getCountUntranslated(), 0);
|
||||
|
||||
// We should have been redirect back to the overview, the task should be
|
||||
// completed now.
|
||||
$this->assertNoText($task->getJob()->label());
|
||||
$this->clickLink(t('Closed'));
|
||||
$this->assertText($task->getJob()->label());
|
||||
$this->assertText(t('Completed'));
|
||||
|
||||
entity_get_controller('tmgmt_job')->resetCache(array($job->tjid));
|
||||
entity_get_controller('tmgmt_job_item')->resetCache();
|
||||
$job = tmgmt_job_load($job->tjid);
|
||||
list($item1, $item2) = array_values($job->getItems());
|
||||
// Job was accepted and finished automatically due to the default approve
|
||||
// setting.
|
||||
$this->assertTrue($job->isFinished());
|
||||
$this->assertEqual($item1->getData(array('dummy', 'deep_nesting', '#translation', '#text')), $translation1);
|
||||
$this->assertEqual($item2->getData(array('dummy', 'deep_nesting', '#translation', '#text')), $translation2);
|
||||
|
||||
// Delete the job, make sure that the corresponding task and task items were
|
||||
// deleted.
|
||||
$job->delete();
|
||||
$this->assertFalse(tmgmt_local_task_item_load($task->tltid));
|
||||
$this->assertFalse($task->getItems());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the allow all setting.
|
||||
*/
|
||||
function testAllowAll() {
|
||||
$translator = tmgmt_translator_load('local');
|
||||
|
||||
// Create a job and request a local translation.
|
||||
$this->loginAsTranslator();
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$job->addItem('test_source', 'test', '1');
|
||||
$job->addItem('test_source', 'test', '2');
|
||||
|
||||
$this->assertFalse($job->requestTranslation(), 'Translation request was denied.');
|
||||
|
||||
// Now enable the setting.
|
||||
$translator->settings['allow_all'] = TRUE;
|
||||
$translator->save();
|
||||
|
||||
$this->assertIdentical(NULL, $job->requestTranslation(), 'Translation request was successfull');
|
||||
$this->assertTrue($job->isActive());
|
||||
}
|
||||
|
||||
function testCapabilitiesAPI() {
|
||||
|
||||
$this->setEnvironment('fr');
|
||||
$this->setEnvironment('ru');
|
||||
$this->setEnvironment('it');
|
||||
|
||||
$all_translators = array();
|
||||
|
||||
$translator1 = $this->drupalCreateUser($this->local_translator_permissions);
|
||||
$all_translators[$translator1->uid] = $translator1->name;
|
||||
$this->drupalLogin($translator1);
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][0][language_from]' => 'en',
|
||||
'tmgmt_translation_skills[und][0][language_to]' => 'de',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator1->uid . '/edit', $edit, t('Save'));
|
||||
|
||||
$translator2 = $this->drupalCreateUser($this->local_translator_permissions);
|
||||
$all_translators[$translator2->uid] = $translator2->name;
|
||||
$this->drupalLogin($translator2);
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][0][language_from]' => 'en',
|
||||
'tmgmt_translation_skills[und][0][language_to]' => 'ru',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator2->uid . '/edit', $edit, t('Save'));
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][1][language_from]' => 'en',
|
||||
'tmgmt_translation_skills[und][1][language_to]' => 'fr',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator2->uid . '/edit', $edit, t('Save'));
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][2][language_from]' => 'fr',
|
||||
'tmgmt_translation_skills[und][2][language_to]' => 'it',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator2->uid . '/edit', $edit, t('Save'));
|
||||
|
||||
$translator3 = $this->drupalCreateUser($this->local_translator_permissions);
|
||||
$all_translators[$translator3->uid] = $translator3->name;
|
||||
$this->drupalLogin($translator3);
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][0][language_from]' => 'fr',
|
||||
'tmgmt_translation_skills[und][0][language_to]' => 'ru',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator3->uid . '/edit', $edit, t('Save'));
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][1][language_from]' => 'it',
|
||||
'tmgmt_translation_skills[und][1][language_to]' => 'en',
|
||||
);
|
||||
$this->drupalPost('user/' . $translator3->uid . '/edit', $edit, t('Save'));
|
||||
|
||||
// Test target languages.
|
||||
$target_languages = tmgmt_local_supported_target_languages('fr');
|
||||
$this->assertTrue(isset($target_languages['it']));
|
||||
$this->assertTrue(isset($target_languages['ru']));
|
||||
$target_languages = tmgmt_local_supported_target_languages('en');
|
||||
$this->assertTrue(isset($target_languages['fr']));
|
||||
$this->assertTrue(isset($target_languages['ru']));
|
||||
|
||||
// Test language pairs.
|
||||
$this->assertEqual(tmgmt_local_supported_language_pairs(), array (
|
||||
'en__de' =>
|
||||
array (
|
||||
'source_language' => 'en',
|
||||
'target_language' => 'de',
|
||||
),
|
||||
'en__ru' =>
|
||||
array (
|
||||
'source_language' => 'en',
|
||||
'target_language' => 'ru',
|
||||
),
|
||||
'en__fr' =>
|
||||
array (
|
||||
'source_language' => 'en',
|
||||
'target_language' => 'fr',
|
||||
),
|
||||
'fr__it' =>
|
||||
array (
|
||||
'source_language' => 'fr',
|
||||
'target_language' => 'it',
|
||||
),
|
||||
'fr__ru' =>
|
||||
array (
|
||||
'source_language' => 'fr',
|
||||
'target_language' => 'ru',
|
||||
),
|
||||
'it__en' =>
|
||||
array (
|
||||
'source_language' => 'it',
|
||||
'target_language' => 'en',
|
||||
),
|
||||
));
|
||||
$this->assertEqual(tmgmt_local_supported_language_pairs('fr', array($translator2->uid)), array (
|
||||
'fr__it' =>
|
||||
array (
|
||||
'source_language' => 'fr',
|
||||
'target_language' => 'it',
|
||||
),
|
||||
));
|
||||
|
||||
// Test if we got all translators.
|
||||
$translators = tmgmt_local_translators();
|
||||
foreach ($all_translators as $uid => $name) {
|
||||
if (!isset($translators[$uid])) {
|
||||
$this->fail('Expected translator not present');
|
||||
}
|
||||
if (!in_array($name, $all_translators)) {
|
||||
$this->fail('Expected translator name not present');
|
||||
}
|
||||
}
|
||||
|
||||
// Only translator2 has such capabilities.
|
||||
$translators = tmgmt_local_translators('en', array('ru', 'fr'));
|
||||
$this->assertTrue(isset($translators[$translator2->uid]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test permissions for the tmgmt_local VBO actions.
|
||||
*/
|
||||
function testVBOPermissions() {
|
||||
$translator = tmgmt_translator_load('local');
|
||||
$job = $this->createJob();
|
||||
$job->translator = $translator->name;
|
||||
$job->settings['job_comment'] = $job_comment = 'Dummy job comment';
|
||||
$job->addItem('test_source', 'test', '1');
|
||||
$job->addItem('test_source', 'test', '2');
|
||||
|
||||
// Create another local translator with the required capabilities.
|
||||
$local_translator = $this->loginAsTranslator($this->local_translator_permissions);
|
||||
// Configure language capabilities.
|
||||
$edit = array(
|
||||
'tmgmt_translation_skills[und][0][language_from]' => 'en',
|
||||
'tmgmt_translation_skills[und][0][language_to]' => 'de',
|
||||
);
|
||||
$this->drupalPost('user/' . $local_translator->uid . '/edit', $edit, t('Save'));
|
||||
|
||||
$job->requestTranslation();
|
||||
|
||||
$this->drupalGet('translate');
|
||||
$this->assertFieldById('edit-rules-componentrules-tmgmt-local-task-assign-to-me', t('Assign to me'));
|
||||
$this->assertFieldById('edit-rules-componentrules-tmgmt-local-task-unassign', t('Unassign'));
|
||||
|
||||
// Login as admin and check VBO submit actions are present.
|
||||
$this->loginAsAdmin(array('administer translation tasks'));
|
||||
$this->drupalGet('manage-translate');
|
||||
$this->assertFieldById('edit-actionviews-bulk-operations-argument-selector-action--2', t('Assign to...'));
|
||||
$this->assertFieldById('edit-actionviews-bulk-operations-argument-selector-action', t('Assign to...'));
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Field handler which shows the link for translating translation task items.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class tmgmt_local_task_handler_field_item_operations extends views_handler_field_entity {
|
||||
|
||||
function render($values) {
|
||||
global $user;
|
||||
/**
|
||||
* @var TMGMTLocalTaskItem $item
|
||||
*/
|
||||
$item = $this->get_value($values);
|
||||
|
||||
$element = array();
|
||||
$element['#theme'] = 'links';
|
||||
$element['#attributes'] = array('class' => array('links', 'inline'));
|
||||
// Only allow to translate if the job is assigned to this user.
|
||||
if (entity_access('view', 'tmgmt_local_task_item', $item) && $item->getTask()->tuid == $user->uid) {
|
||||
$element['#links']['translate'] = array(
|
||||
'href' => 'translate/' . $item->tltid . '/item/' . $item->tltiid,
|
||||
'attributes' => array(
|
||||
'title' => $item->isPending() ? t('Translate') : t('View'),
|
||||
),
|
||||
'title' => $item->isPending() ? t('translate') : t('view'),
|
||||
);
|
||||
}
|
||||
return drupal_render($element);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Field handler to show the amount of job items per task.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class tmgmt_local_task_handler_field_job_item_count extends views_handler_field {
|
||||
/**
|
||||
* @var views_plugin_query_default
|
||||
*/
|
||||
var $query;
|
||||
|
||||
function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
$options['state'] = array('default' => '');
|
||||
return $options;
|
||||
}
|
||||
|
||||
function options_form(&$form, &$form_state) {
|
||||
parent::options_form($form, $form_state);
|
||||
$options = array('' => t('- All -'));
|
||||
$options += tmgmt_job_item_states();
|
||||
$form['state'] = array(
|
||||
'#title' => t('Job item state'),
|
||||
'#description' => t('Count only job items of a certain state.'),
|
||||
'#type' => 'select',
|
||||
'#options' => $options,
|
||||
'#default_value' => $this->options['state'],
|
||||
);
|
||||
}
|
||||
|
||||
function use_group_by() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
function query() {
|
||||
$this->ensure_my_table();
|
||||
|
||||
// Therefore construct the join.
|
||||
$join = new views_join();
|
||||
$join->definition['left_table'] = $this->table_alias;
|
||||
$join->definition['left_field'] = 'tjid';
|
||||
$join->definition['table'] = 'tmgmt_job_item';
|
||||
$join->definition['field'] = 'tjid';
|
||||
$join->definition['type'] = 'LEFT';
|
||||
|
||||
if (!empty($this->options['state'])) {
|
||||
$join->extra = array(array(
|
||||
'field' => 'state',
|
||||
'value' => $this->options['state']
|
||||
));
|
||||
}
|
||||
$join->construct();
|
||||
|
||||
// Add the join to the tmgmt_job_item table.
|
||||
$this->table_alias = $this->query->add_table('tmgmt_job_item', $this->relationship, $join);
|
||||
|
||||
// And finally add the count of the job items field.
|
||||
$params = array('function' => 'count');
|
||||
$this->field_alias = $this->query->add_field($this->table_alias, 'tjiid', NULL, $params);
|
||||
|
||||
$this->add_additional_fields();
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Field handler which shows the loop count.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class tmgmt_local_task_handler_field_loop_count extends views_handler_field_entity {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function render($values) {
|
||||
$object = $this->get_value($values);
|
||||
return $object->getLoopCount();
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Field handler which shows the operations for a task.
|
||||
*
|
||||
* @todo Remove this once http://drupal.org/node/1435662 is through.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class tmgmt_local_task_handler_field_operations extends views_handler_field_entity {
|
||||
|
||||
function render($values) {
|
||||
/**
|
||||
* @var TMGMTLocalTask $task
|
||||
*/
|
||||
$task = $this->get_value($values);
|
||||
|
||||
$element = array();
|
||||
$element['#theme'] = 'links';
|
||||
$element['#attributes'] = array('class' => array('links', 'inline'));
|
||||
$uri = $task->uri();
|
||||
if (entity_access('view', 'tmgmt_local_task', $task)) {
|
||||
$element['#links']['view'] = array(
|
||||
'href' => $uri['path'],
|
||||
'query' => array('destination' => current_path()),
|
||||
'title' => t('view'),
|
||||
);
|
||||
}
|
||||
if (user_access('administer translation tasks') && tmgmt_local_translation_access($task) && empty($task->tuid)) {
|
||||
$element['#links']['assign'] = array(
|
||||
'href' => 'manage-translate/assign-tasks/' . $task->tltid,
|
||||
'query' => array('destination' => current_path()),
|
||||
'attributes' => array(
|
||||
'title' => t('Assign'),
|
||||
),
|
||||
'title' => t('assign'),
|
||||
);
|
||||
}
|
||||
elseif (tmgmt_local_translation_access($task) && empty($task->tuid)) {
|
||||
$element['#links']['assign_to_me'] = array(
|
||||
'href' => 'translate/' . $task->tltid . '/assign-to-me',
|
||||
'query' => array('destination' => current_path()),
|
||||
'attributes' => array(
|
||||
'title' => t('Assign to me'),
|
||||
),
|
||||
'title' => t('assign'),
|
||||
);
|
||||
}
|
||||
elseif (tmgmt_local_translation_access($task) && empty($task->tuid)) {
|
||||
$element['#links']['assign_to_me'] = array(
|
||||
'href' => 'translate/' . $task->tltid . '/assign-to-me',
|
||||
'query' => array('destination' => current_path()),
|
||||
'attributes' => array(
|
||||
'title' => t('Assign to me'),
|
||||
),
|
||||
'title' => t('assign'),
|
||||
);
|
||||
}
|
||||
if (!empty($task->tuid) && entity_access('unassign', 'tmgmt_local_task', $task)) {
|
||||
$element['#links']['unassign'] = array(
|
||||
'href' => 'translate/' . $task->tltid . '/unassign',
|
||||
'query' => array('destination' => current_path()),
|
||||
'attributes' => array(
|
||||
'title' => t('Unassign'),
|
||||
),
|
||||
'title' => t('unassign'),
|
||||
);
|
||||
}
|
||||
if (entity_access('delete', 'tmgmt_local_task', $task)) {
|
||||
$element['#links']['delete'] = array(
|
||||
'href' => $uri['path'] . '/delete',
|
||||
'query' => array('destination' => current_path()),
|
||||
'title' => t('delete'),
|
||||
);
|
||||
}
|
||||
return drupal_render($element);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Field handler which shows the progressbar.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class tmgmt_local_task_handler_field_progress extends tmgmt_handler_field_tmgmt_progress {
|
||||
|
||||
/**
|
||||
* Prefetch statistics for all jobs.
|
||||
*/
|
||||
function pre_render(&$values) {
|
||||
parent::pre_render($values);
|
||||
|
||||
// In case of tasks, pre-fetch the statistics in a single query and add them
|
||||
// to the static cache.
|
||||
if ($this->entity_type == 'tmgmt_task') {
|
||||
$tltids = array();
|
||||
foreach ($values as $value) {
|
||||
$tltids[] = $value->tjid;
|
||||
}
|
||||
tmgmt_local_task_statistics_load($tltids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function render($values) {
|
||||
$object = $this->get_value($values);
|
||||
$counts = array(
|
||||
'@untranslated' => $object->getCountUntranslated(),
|
||||
'@translated' => $object->getCountTranslated(),
|
||||
'@completed' => $object->getCountCompleted(),
|
||||
);
|
||||
$id = $object->internalIdentifier();
|
||||
|
||||
if (module_exists('google_chart_tools')) {
|
||||
draw_chart($this->build_progressbar_settings($id, $counts));
|
||||
return '<div id="progress' . $id . '"></div>';
|
||||
}
|
||||
$title = t('Untranslated: @untranslated, translated: @translated, completed: @completed.', $counts);
|
||||
return sprintf('<span title="%s">%s</span>', $title, implode('/', $counts));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function build_progressbar_settings($id, $counts, $prefix = 'progress') {
|
||||
$settings['chart'][$prefix . $id] = array(
|
||||
'header' => array(t('Untranslated'), t('Translated'), t('Completed')),
|
||||
'rows' => array(
|
||||
array($counts['@untranslated'], $counts['@translated'], $counts['@completed']),
|
||||
),
|
||||
'columns' => array(''),
|
||||
'chartType' => 'PieChart',
|
||||
'containerId' => $prefix . $id,
|
||||
'options' => array(
|
||||
'backgroundColor' => 'transparent',
|
||||
'colors' => array('#60ff60', '#ffff00', '#6060ff'),
|
||||
'forceIFrame' => FALSE,
|
||||
'chartArea' => array(
|
||||
'left' => 0,
|
||||
'top' => 0,
|
||||
'width' => '50%',
|
||||
'height' => '100%',
|
||||
),
|
||||
'fontSize' => 9,
|
||||
'title' => t('Progress'),
|
||||
'titlePosition' => 'none',
|
||||
'width' => 60,
|
||||
'height' => 50,
|
||||
'isStacked' => TRUE,
|
||||
'legend' => array('position' => 'none'),
|
||||
'pieSliceText' => 'none',
|
||||
)
|
||||
);
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Field handler which shows the word count.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class tmgmt_local_task_handler_field_wordcount extends views_handler_field_entity {
|
||||
|
||||
/**
|
||||
* Prefetch statistics for all jobs.
|
||||
*/
|
||||
function pre_render(&$values) {
|
||||
parent::pre_render($values);
|
||||
|
||||
// In case of jobs, pre-fetch the statistics in a single query and add them
|
||||
// to the static cache.
|
||||
if ($this->entity_type == 'tmgmt_task') {
|
||||
$tltids = array();
|
||||
foreach ($values as $value) {
|
||||
$tltids[] = $value->tjid;
|
||||
}
|
||||
tmgmt_local_task_statistics_load($tltids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function render($values) {
|
||||
$object = $this->get_value($values);
|
||||
return $object->getWordCount();
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Field handler which shows the link for assign translation task to selected
|
||||
* user.
|
||||
*
|
||||
* @ingroup views_field_handlers
|
||||
*/
|
||||
class tmgmt_local_task_handler_filter_eligible extends views_handler_filter {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$this->ensure_my_table();
|
||||
$source = $this->table_alias . '.source_language';
|
||||
$target = $this->table_alias . '.target_language';
|
||||
// Add a new group for the language capabilities, which are a set of source
|
||||
// and target language combinations.
|
||||
$this->query->set_where_group('OR', 'eligible');
|
||||
// Return all language capabilities for the current user.
|
||||
foreach (tmgmt_local_supported_language_pairs(NULL, array($GLOBALS['user']->uid)) as $key => $capability) {
|
||||
$key = str_replace('-', '_', $key);
|
||||
$arguments = array(':source_' . $key => $capability['source_language'], ':target_' . $key => $capability['target_language']);
|
||||
$this->query->add_where_expression('eligible', "$source = :source_$key AND $target = :target_$key", $arguments);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Views controllers for the translation management local task module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Views controller class for the local task entity.
|
||||
*/
|
||||
class TMGMTLocalTaskViewsController extends EntityDefaultViewsController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function views_data() {
|
||||
$data = parent::views_data();
|
||||
$data['tmgmt_local_task']['operations'] = array(
|
||||
'title' => t('Operations'),
|
||||
'help' => t('Displays a list of operations which are available for a task.'),
|
||||
'real field' => 'tltid',
|
||||
'field' => array(
|
||||
'handler' => 'tmgmt_local_task_handler_field_operations',
|
||||
),
|
||||
);
|
||||
$data['tmgmt_local_task']['progress'] = array(
|
||||
'title' => t('Progress'),
|
||||
'help' => t('Displays the progress of a job.'),
|
||||
'real field' => 'tltid',
|
||||
'field' => array(
|
||||
'handler' => 'tmgmt_local_task_handler_field_progress',
|
||||
),
|
||||
);
|
||||
$data['tmgmt_local_task']['word_count'] = array(
|
||||
'title' => t('Word count'),
|
||||
'help' => t('Displays the word count of a job.'),
|
||||
'real field' => 'tltid',
|
||||
'field' => array(
|
||||
'handler' => 'tmgmt_local_task_handler_field_wordcount',
|
||||
),
|
||||
);
|
||||
$data['tmgmt_local_task']['item_count'] = array(
|
||||
'title' => t('Job item count'),
|
||||
'help' => t('Show the amount of job items per task (per job item status)'),
|
||||
'real field' => 'tltid',
|
||||
'field' => array(
|
||||
'handler' => 'tmgmt_local_task_handler_field_job_item_count',
|
||||
),
|
||||
);
|
||||
$data['tmgmt_job']['eligible'] = array(
|
||||
'title' => t('Eligible'),
|
||||
'help' => t('Limit translation tasks to those that the user can translate'),
|
||||
'real field' => 'tltid',
|
||||
'filter' => array(
|
||||
'handler' => 'tmgmt_local_task_handler_filter_eligible',
|
||||
),
|
||||
);
|
||||
// Manager handlers.
|
||||
$data['tmgmt_job']['task'] = array(
|
||||
'title' => t('Translation task'),
|
||||
'help' => t('Get the translation task of the job'),
|
||||
'relationship' => array(
|
||||
'base' => 'tmgmt_local_task',
|
||||
'base field' => 'tjid',
|
||||
'real field' => 'tjid',
|
||||
'label' => t('Job'),
|
||||
),
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Views controller class for the translation task item entity.
|
||||
*/
|
||||
class TMGMTLocalTaskItemViewsController extends EntityDefaultViewsController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function views_data() {
|
||||
$data = parent::views_data();
|
||||
$data['tmgmt_local_task_item']['operations'] = array(
|
||||
'title' => t('Operations'),
|
||||
'help' => t('Displays a list of operations which are available for a task item.'),
|
||||
'real field' => 'tltiid',
|
||||
'field' => array(
|
||||
'handler' => 'tmgmt_local_task_handler_field_item_operations',
|
||||
),
|
||||
);
|
||||
return $data;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
$view = new view();
|
||||
$view->name = 'tmgmt_local_task_items';
|
||||
$view->description = '';
|
||||
$view->tag = 'default';
|
||||
$view->base_table = 'tmgmt_local_task_item';
|
||||
$view->human_name = 'Translation Task Items';
|
||||
$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'] = 'Translation Task Items';
|
||||
$handler->display->display_options['use_more_always'] = FALSE;
|
||||
$handler->display->display_options['access']['type'] = 'none';
|
||||
$handler->display->display_options['cache']['type'] = 'none';
|
||||
$handler->display->display_options['query']['type'] = 'views_query';
|
||||
$handler->display->display_options['exposed_form']['type'] = 'basic';
|
||||
$handler->display->display_options['pager']['type'] = 'full';
|
||||
$handler->display->display_options['pager']['options']['items_per_page'] = '20';
|
||||
$handler->display->display_options['style_plugin'] = 'table';
|
||||
/* Relationship: Translation Task Item: Tltid */
|
||||
$handler->display->display_options['relationships']['tltid']['id'] = 'tltid';
|
||||
$handler->display->display_options['relationships']['tltid']['table'] = 'tmgmt_local_task_item';
|
||||
$handler->display->display_options['relationships']['tltid']['field'] = 'tltid';
|
||||
/* Relationship: Translation Task Item: Tjiid */
|
||||
$handler->display->display_options['relationships']['tjiid']['id'] = 'tjiid';
|
||||
$handler->display->display_options['relationships']['tjiid']['table'] = 'tmgmt_local_task_item';
|
||||
$handler->display->display_options['relationships']['tjiid']['field'] = 'tjiid';
|
||||
/* Field: Translation Management Job Item: Label */
|
||||
$handler->display->display_options['fields']['label']['id'] = 'label';
|
||||
$handler->display->display_options['fields']['label']['table'] = 'tmgmt_job_item';
|
||||
$handler->display->display_options['fields']['label']['field'] = 'label';
|
||||
$handler->display->display_options['fields']['label']['relationship'] = 'tjiid';
|
||||
/* Field: Translation Task Item: Status */
|
||||
$handler->display->display_options['fields']['status']['id'] = 'status';
|
||||
$handler->display->display_options['fields']['status']['table'] = 'tmgmt_local_task_item';
|
||||
$handler->display->display_options['fields']['status']['field'] = 'status';
|
||||
/* Field: Translation Task Item: Operations */
|
||||
$handler->display->display_options['fields']['operations']['id'] = 'operations';
|
||||
$handler->display->display_options['fields']['operations']['table'] = 'tmgmt_local_task_item';
|
||||
$handler->display->display_options['fields']['operations']['field'] = 'operations';
|
||||
/* Contextual filter: Translation Task Item: Tltid */
|
||||
$handler->display->display_options['arguments']['tltid']['id'] = 'tltid';
|
||||
$handler->display->display_options['arguments']['tltid']['table'] = 'tmgmt_local_task_item';
|
||||
$handler->display->display_options['arguments']['tltid']['field'] = 'tltid';
|
||||
$handler->display->display_options['arguments']['tltid']['default_action'] = 'empty';
|
||||
$handler->display->display_options['arguments']['tltid']['default_argument_type'] = 'fixed';
|
||||
$handler->display->display_options['arguments']['tltid']['summary']['number_of_records'] = '0';
|
||||
$handler->display->display_options['arguments']['tltid']['summary']['format'] = 'default_summary';
|
||||
$handler->display->display_options['arguments']['tltid']['summary_options']['items_per_page'] = '25';
|
||||
|
||||
/* Display: Block */
|
||||
$handler = $view->new_display('block', 'Block', 'block');
|
||||
$handler->display->display_options['defaults']['hide_admin_links'] = FALSE;
|
||||
$translatables['tmgmt_local_task_items'] = array(
|
||||
t('Master'),
|
||||
t('Translation Task Items'),
|
||||
t('more'),
|
||||
t('Apply'),
|
||||
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('Translation Task'),
|
||||
t('Translation Management Job Item'),
|
||||
t('Label'),
|
||||
t('Status'),
|
||||
t('Operations'),
|
||||
t('All'),
|
||||
t('Block'),
|
||||
);
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user