<?php
namespace Drupal\tmgmt\Entity;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\tmgmt\JobInterface;
use Drupal\tmgmt\JobItemInterface;
use Drupal\tmgmt\TMGMTException;
use Drupal\tmgmt\Translator\TranslatableResult;
use Drupal\user\EntityOwnerInterface;
use Drupal\user\UserInterface;
class Job extends ContentEntityBase implements EntityOwnerInterface, JobInterface {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['tjid'] = BaseFieldDefinition::create('integer')
->setLabel(t('Job ID'))
->setDescription(t('The Job ID.'))
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
$fields['uuid'] = BaseFieldDefinition::create('uuid')
->setLabel(t('UUID'))
->setDescription(t('The node UUID.'))
->setReadOnly(TRUE);
$fields['source_language'] = BaseFieldDefinition::create('language')
->setLabel(t('Source language code'))
->setDescription(t('The source language.'));
$fields['target_language'] = BaseFieldDefinition::create('language')
->setLabel(t('Target language code'))
->setDescription(t('The target language.'));
$fields['label'] = BaseFieldDefinition::create('string')
->setLabel(t('Label'))
->setDescription(t('The label of this job.'))
->setDefaultValue('')
->setSettings(array(
'max_length' => 255,
))
->setDisplayOptions('form', array(
'type' => 'string',
'weight' => -5,
))
->setDisplayConfigurable('form', TRUE);
$fields['uid'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Owner'))
->setDescription(t('The user that is the job owner.'))
->setSettings(array(
'target_type' => 'user',
))
->setDefaultValue(0);
$fields['translator'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Provider'))
->setDescription(t('The selected provider'))
->setSettings(array(
'target_type' => 'tmgmt_translator',
));
$fields['settings'] = BaseFieldDefinition::create('map')
->setLabel(t('Settings'))
->setDescription(t('Provider specific configuration and context information for this job.'))
->setDefaultValue(array());
$fields['reference'] = BaseFieldDefinition::create('string')
->setLabel(t('Reference'))
->setDescription(t('Remote reference of this job'))
->setDefaultValue('')
->setSettings(array(
'max_length' => 255,
));
$fields['state'] = BaseFieldDefinition::create('list_integer')
->setLabel(t('Job state'))
->setDescription(t('The job state.'))
->setSetting('allowed_values', Job::getStates())
->setDefaultValue(Job::STATE_UNPROCESSED);
$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(t('Created'))
->setDescription(t('The time that the job was created.'));
$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
->setDescription(t('The time that the job was last edited.'));
$fields['job_type'] = BaseFieldDefinition::create('list_string')
->setLabel(t('Job type'))
->setDescription(t('Type of job entity, can be Normal or Continuous.'))
->setSetting('allowed_values', [
static::TYPE_NORMAL,
static::TYPE_CONTINUOUS,
])
->setDefaultValue(static::TYPE_NORMAL);
$fields['continuous_settings'] = BaseFieldDefinition::create('map')
->setLabel(t('Continuous settings'))
->setDescription(t('Continuous sources configuration.'))
->setDefaultValue(array());
return $fields;
}
public static function preCreate(EntityStorageInterface $storage, array &$values) {
parent::preCreate($storage, $values);
if (empty($values['source_language']) || empty($values['target_language'])) {
$languages = tmgmt_available_languages();
if (empty($values['source_language'])) {
$values['source_language'] = key($languages);
}
if (empty($values['target_language'])) {
$source_language = $values['source_language'];
while (is_array($source_language)) {
$source_language = reset($source_language);
}
unset($languages[$source_language]);
$values['target_language'] = key($languages);
}
}
}
protected $filterTranslatedItems = FALSE;
public function getTargetLanguage() {
return $this
->get('target_language')->language;
}
public function getTargetLangcode() {
return $this
->get('target_language')->value;
}
public function getSourceLanguage() {
return $this
->get('source_language')->language;
}
public function getSourceLangcode() {
return $this
->get('source_language')->value;
}
public function getChangedTime() {
return $this
->get('changed')->value;
}
public function getCreatedTime() {
return $this
->get('created')->value;
}
public function getReference() {
return $this
->get('reference')->value;
}
public function getJobType() {
return $this
->get('job_type')->value;
}
public function getContinuousSettings() {
return $this
->get('continuous_settings')[0] ? $this
->get('continuous_settings')[0]
->getValue() : array();
}
public function cloneAsUnprocessed() {
$clone = $this
->createDuplicate();
$clone->uid->value = 0;
$clone->reference->value = '';
$clone->created->value = \Drupal::time()
->getRequestTime();
$clone->state->value = Job::STATE_UNPROCESSED;
return $clone;
}
public static function postDelete(EntityStorageInterface $storage, array $entities) {
parent::postDelete($storage, $entities);
$entity_type_manager = \Drupal::entityTypeManager();
$tjiids = \Drupal::entityQuery('tmgmt_job_item')
->accessCheck(TRUE)
->condition('tjid', array_keys($entities), 'IN')
->execute();
if (!empty($tjiids)) {
$job_items = $entity_type_manager
->getStorage('tmgmt_job_item')
->loadMultiple($tjiids);
$entity_type_manager
->getStorage('tmgmt_job_item')
->delete($job_items);
}
$mids = \Drupal::entityQuery('tmgmt_message')
->accessCheck(TRUE)
->condition('tjid', array_keys($entities), 'IN')
->execute();
if (!empty($mids)) {
$messages = $entity_type_manager
->getStorage('tmgmt_message')
->loadMultiple($mids);
$entity_type_manager
->getStorage('tmgmt_message')
->delete($messages);
}
$trids = \Drupal::entityQuery('tmgmt_remote')
->accessCheck(TRUE)
->condition('tjid', array_keys($entities), 'IN')
->execute();
if (!empty($trids)) {
$remotes = $entity_type_manager
->getStorage('tmgmt_remote')
->loadMultiple($trids);
$entity_type_manager
->getStorage('tmgmt_remote')
->delete($remotes);
}
}
public function label($langcode = NULL) {
if (!empty($this
->get('label')->value)) {
return $this
->get('label')->value;
}
$items = $this
->getItems();
$count = count($items);
if ($count > 0) {
$source_label = reset($items)
->getSourceLabel();
$t_args = array(
'@title' => $source_label,
'@more' => $count - 1,
);
$label = \Drupal::translation()
->formatPlural($count, '@title', '@title and @more more', $t_args);
if (strlen($label) > Job::LABEL_MAX_LENGTH) {
$max_length = strlen($source_label) - (strlen($label) - Job::LABEL_MAX_LENGTH);
$source_label = Unicode::truncate($source_label, $max_length, TRUE);
$t_args['@title'] = $source_label;
$label = \Drupal::translation()
->formatPlural($count, '@title', '@title and @more more', $t_args);
}
}
else {
$source = $this
->getSourceLanguage() ? $this
->getSourceLanguage()
->getName() : '?';
$target = $this
->getTargetLanguage() ? $this
->getTargetLanguage()
->getName() : '?';
$label = t('From @source to @target', array(
'@source' => $source,
'@target' => $target,
));
}
return $label;
}
public function addItem($plugin, $item_type, $item_id) {
$transaction = \Drupal::database()
->startTransaction();
$is_new = FALSE;
if ($this
->isNew()) {
$this
->save();
$is_new = TRUE;
}
$item = tmgmt_job_item_create($plugin, $item_type, $item_id, array(
'tjid' => $this
->id(),
));
$item
->save();
if ($item
->getWordCount() == 0) {
$transaction
->rollback();
if ($is_new) {
$this->tjid = NULL;
}
throw new TMGMTException('Job item @label (@type) has no translatable content.', array(
'@label' => $item
->label(),
'@type' => $item
->getSourceType(),
));
}
return $item;
}
public function addExistingItem(JobItemInterface $item) {
$item->tjid = $this
->id();
$item
->save();
}
public function addMessage($message, $variables = array(), $type = 'status') {
if (!$this
->isNew() || $this
->save()) {
$message = tmgmt_message_create($message, $variables, array(
'tjid' => $this
->id(),
'type' => $type,
));
if ($message
->save()) {
return $message;
}
}
return FALSE;
}
public function getItems($conditions = array()) {
$items = [];
$query = \Drupal::entityQuery('tmgmt_job_item')
->accessCheck(TRUE)
->condition('tjid', $this
->id());
foreach ($conditions as $key => $condition) {
if (is_array($condition)) {
$operator = isset($condition['operator']) ? $condition['operator'] : '=';
$query
->condition($key, $condition['value'], $operator);
}
else {
$query
->condition($key, $condition);
}
}
$query
->sort('tjiid', 'ASC');
$results = $query
->execute();
if (!empty($results)) {
$items = \Drupal::entityTypeManager()
->getStorage('tmgmt_job_item')
->loadMultiple($results);
if ($this->filterTranslatedItems) {
$items = array_filter($items, function (JobItemInterface $item) {
return $item
->getCountPending() > 0;
});
}
}
return $items;
}
public function getMostRecentItem($plugin, $item_type, $item_id) {
$query = \Drupal::entityQuery('tmgmt_job_item')
->accessCheck(TRUE)
->condition('tjid', $this
->id())
->condition('plugin', $plugin)
->condition('item_type', $item_type)
->condition('item_id', $item_id)
->sort('tjiid', 'DESC')
->range(0, 1);
$result = $query
->execute();
if (!empty($result)) {
return JobItem::load(reset($result));
}
return NULL;
}
public function getMessages($conditions = array()) {
$query = \Drupal::entityQuery('tmgmt_message')
->accessCheck(TRUE)
->condition('tjid', $this
->id());
foreach ($conditions as $key => $condition) {
if (is_array($condition)) {
$operator = isset($condition['operator']) ? $condition['operator'] : '=';
$query
->condition($key, $condition['value'], $operator);
}
else {
$query
->condition($key, $condition);
}
}
$query
->sort('created', 'ASC');
$query
->sort('mid', 'ASC');
$results = $query
->execute();
if (!empty($results)) {
return Message::loadMultiple($results);
}
return array();
}
public function getMessagesSince($time = NULL) {
$time = isset($time) ? $time : \Drupal::time()
->getRequestTime();
$conditions = array(
'created' => array(
'value' => $time,
'operator' => '>=',
),
);
return $this
->getMessages($conditions);
}
public function getRemoteSourceLanguage() {
return $this
->getTranslator()
->mapToRemoteLanguage($this
->getSourceLangcode());
}
public function getRemoteTargetLanguage() {
return $this
->getTranslator()
->mapToRemoteLanguage($this
->getTargetLangcode());
}
public function getSetting($name) {
if (isset($this->settings->{$name})) {
return $this->settings->{$name};
}
if ($this
->hasTranslator()) {
if (($setting = $this
->getTranslator()
->getSetting($name)) !== NULL) {
return $setting;
}
$defaults = $this
->getTranslatorPlugin()
->defaultSettings();
if (isset($defaults[$name])) {
return $defaults[$name];
}
}
}
public function getTranslatorId() {
return $this
->get('translator')->target_id;
}
public function getTranslatorLabel() {
if ($this
->hasTranslator()) {
return $this
->getTranslator()
->label();
}
if ($this
->getTranslatorId() == NULL) {
return t('(Undefined)');
}
return t('(Missing)');
}
public function getTranslator() {
if ($this
->hasTranslator()) {
return $this->translator->entity;
}
else {
if (!$this->translator->entity) {
throw new TMGMTException('The job has no provider assigned.');
}
else {
if (!$this->translator->entity
->hasPlugin()) {
throw new TMGMTException('The translator assigned to this job is missing the plugin.');
}
}
}
}
public function hasTranslator() {
return $this->translator->entity && $this->translator->target_id && $this->translator->entity
->hasPlugin();
}
public function getState() {
return $this
->get('state')->value;
}
public function setState($state, $message = NULL, $variables = array(), $type = 'debug') {
if (array_key_exists($state, Job::getStates())) {
$this->state = $state;
$this
->save();
if (!empty($message)) {
$this
->addMessage($message, $variables, $type);
}
}
return $this
->getState();
}
public function isState($state) {
return $this
->getState() == $state;
}
public function isAuthor(AccountInterface $account = NULL) {
$account = isset($account) ? $account : \Drupal::currentUser();
return $this
->getOwnerId() == $account
->id();
}
public function isUnprocessed() {
return $this
->isState(Job::STATE_UNPROCESSED);
}
public function isAborted() {
return $this
->isState(static::STATE_ABORTED);
}
public function isActive() {
return $this
->isState(static::STATE_ACTIVE);
}
public function isRejected() {
return $this
->isState(static::STATE_REJECTED);
}
public function isFinished() {
return $this
->isState(static::STATE_FINISHED);
}
public function isContinuousActive() {
return $this
->isState(static::STATE_CONTINUOUS);
}
public function isContinuousInactive() {
return $this
->isState(static::STATE_CONTINUOUS_INACTIVE);
}
public function canRequestTranslation() {
if ($translator = $this
->getTranslator()) {
return $translator
->checkTranslatable($this);
}
return TranslatableResult::no(t('Translation cant be requested.'));
}
public function isAbortable() {
if ($this
->isContinuous()) {
return FALSE;
}
else {
return $this
->isActive();
}
}
public function isSubmittable() {
if ($this
->isContinuous()) {
return FALSE;
}
else {
return $this
->isUnprocessed() || $this
->isRejected();
}
}
public function isDeletable() {
return !$this
->isActive();
}
public function isContinuous() {
return $this
->getJobType() == static::TYPE_CONTINUOUS;
}
public function submitted($message = NULL, $variables = array(), $type = 'status') {
if (!isset($message)) {
$message = 'The translation job has been submitted.';
}
$this
->setState(static::STATE_ACTIVE, $message, $variables, $type);
}
public function finished($message = NULL, $variables = array(), $type = 'status') {
if (!isset($message)) {
$message = 'The translation job has been finished.';
}
return $this
->setState(static::STATE_FINISHED, $message, $variables, $type);
}
public function aborted($message = NULL, $variables = array(), $type = 'status') {
if (!isset($message)) {
$message = 'The translation job has been aborted.';
}
foreach ($this
->getItems() as $item) {
$item
->setState(JobItem::STATE_ABORTED);
}
return $this
->setState(static::STATE_ABORTED, $message, $variables, $type);
}
public function rejected($message = NULL, $variables = array(), $type = 'error') {
if (!isset($message)) {
$message = 'The translation job has been rejected by the translation provider.';
}
return $this
->setState(static::STATE_REJECTED, $message, $variables, $type);
}
public function requestTranslation() {
if (!$this
->canRequestTranslation()
->getSuccess()) {
return FALSE;
}
if (!$this
->isContinuous()) {
$this
->setOwnerId(\Drupal::currentUser()
->id());
}
\Drupal::moduleHandler()
->invokeAll('tmgmt_job_before_request_translation', [
$this
->getItems(),
]);
$this->filterTranslatedItems = TRUE;
if (!empty($this
->getItems())) {
$this
->getTranslatorPlugin()
->requestTranslation($this);
}
else {
$this
->submitted();
}
$this->filterTranslatedItems = FALSE;
\Drupal::moduleHandler()
->invokeAll('tmgmt_job_after_request_translation', [
$this
->getItems(),
]);
}
public function preSave(EntityStorageInterface $storage) {
parent::preSave($storage);
if ($this
->isContinuous() && !$this
->isContinuousInactive() && !$this
->isAborted()) {
$this->state = Job::STATE_CONTINUOUS;
}
if ($this
->isActive() && (!isset($this->original) || !$this->original
->isActive())) {
foreach ($this
->getItems() as $item) {
if ($item
->isInactive()) {
$item
->setState(JobItemInterface::STATE_ACTIVE);
}
}
}
}
public function abortTranslation() {
if (!$this
->isAbortable() || !($plugin = $this
->getTranslatorPlugin())) {
return FALSE;
}
return $plugin
->abortTranslation($this);
}
public function getTranslatorPlugin() {
return $this
->getTranslator()
->getPlugin();
}
public function getData($key = array(), $index = NULL) {
$data = array();
if (!empty($key)) {
$tjiid = array_shift($key);
$item = JobItem::load($tjiid);
if ($item) {
$data[$tjiid] = $item
->getData($key, $index);
if (!isset($data[$tjiid]['#label'])) {
$data[$tjiid]['#label'] = $item
->getSourceLabel();
}
}
}
else {
foreach ($this
->getItems() as $tjiid => $item) {
$data[$tjiid] = $item
->getData();
if (!isset($data[$tjiid]['#label'])) {
$data[$tjiid]['#label'] = $item
->getSourceLabel();
}
}
}
return $data;
}
public function getCountPending() {
return tmgmt_job_statistic($this, 'count_pending');
}
public function getCountTranslated() {
return tmgmt_job_statistic($this, 'count_translated');
}
public function getCountAccepted() {
return tmgmt_job_statistic($this, 'count_accepted');
}
public function getCountReviewed() {
return tmgmt_job_statistic($this, 'count_reviewed');
}
public function getWordCount() {
return tmgmt_job_statistic($this, 'word_count');
}
public function getTagsCount() {
return tmgmt_job_statistic($this, 'tags_count');
}
public function getFileCount() {
return tmgmt_job_statistic($this, 'file_count');
}
public function addTranslatedData(array $data, $key = NULL, $status = NULL) {
$key = \Drupal::service('tmgmt.data')
->ensureArrayKey($key);
$items = $this
->getItems();
if (!empty($key)) {
$item_id = array_shift($key);
if (isset($items[$item_id])) {
$items[$item_id]
->addTranslatedData($data, $key, $status);
}
}
else {
foreach ($data as $key => $value) {
if (isset($items[$key])) {
$items[$key]
->addTranslatedData($value, [], $status);
}
}
}
}
public function acceptTranslation() {
foreach ($this
->getItems() as $item) {
$item
->acceptTranslation();
}
}
public function getRemoteMappings() {
$trids = \Drupal::entityQuery('tmgmt_remote')
->accessCheck(TRUE)
->condition('tjid', $this
->id())
->execute();
if (!empty($trids)) {
return RemoteMapping::loadMultiple($trids);
}
return array();
}
public function getSuggestions(array $conditions = array()) {
$suggestions = \Drupal::moduleHandler()
->invokeAll('tmgmt_source_suggestions', array(
$this
->getItems($conditions),
$this,
));
foreach ($suggestions as &$suggestion) {
$jobItem = $suggestion['job_item'];
$jobItem->tjid = $this
->id();
$jobItem
->recalculateStatistics();
}
return $suggestions;
}
public function cleanSuggestionsList(array &$suggestions) {
foreach ($suggestions as $k => $suggestion) {
if (is_array($suggestion) && isset($suggestion['job_item']) && $suggestion['job_item'] instanceof JobItem) {
$jobItem = $suggestion['job_item'];
if ($jobItem
->getWordCount() <= 0) {
unset($suggestions[$k]);
continue;
}
$items = tmgmt_job_item_load_all_latest($jobItem
->getPlugin(), $jobItem
->getItemType(), $jobItem
->getItemId(), $this
->getSourceLangcode());
if (isset($items[$this
->getTargetLangcode()])) {
unset($suggestions[$k]);
continue;
}
foreach ($items as $item) {
if ($item
->getJobId() == $this
->id()) {
unset($suggestions[$k]);
continue;
}
}
}
else {
unset($suggestions[$k]);
continue;
}
}
}
public function isTranslatable() {
return FALSE;
}
public function language() {
return new Language(array(
'id' => Language::LANGCODE_NOT_SPECIFIED,
));
}
public function getOwner() {
return $this
->get('uid')->entity;
}
public function getOwnerId() {
return $this
->get('uid')->target_id;
}
public function setOwnerId($uid) {
$this
->set('uid', $uid);
return $this;
}
public function setOwner(UserInterface $account) {
$this
->set('uid', $account
->id());
return $this;
}
public static function getStates() {
return array(
static::STATE_UNPROCESSED => t('Unprocessed'),
static::STATE_ACTIVE => t('Active'),
static::STATE_REJECTED => t('Rejected'),
static::STATE_ABORTED => t('Aborted'),
static::STATE_FINISHED => t('Finished'),
static::STATE_CONTINUOUS => t('Continuous'),
static::STATE_CONTINUOUS_INACTIVE => t('Continuous Inactive'),
);
}
public function getConflictingItemIds() {
return array_keys($this
->getConflictingItems());
}
public function getConflictingItems() {
$conflicting_items = [];
foreach ($this
->getItems() as $item) {
$existing_items = \Drupal::entityQuery('tmgmt_job_item')
->accessCheck(TRUE)
->condition('state', [
JobItemInterface::STATE_ACTIVE,
JobItemInterface::STATE_REVIEW,
], 'IN')
->condition('plugin', $item
->getPlugin())
->condition('item_type', $item
->getItemType())
->condition('item_id', $item
->getItemId())
->condition('tjiid', $item
->id(), '<>')
->condition('tjid.entity.source_language', $this
->getSourceLangcode())
->condition('tjid.entity.target_language', $this
->getTargetLangcode())
->execute();
if ($existing_items) {
$conflicting_items[$item
->id()] = $existing_items;
}
}
return $conflicting_items;
}
public function setFilterTranslatedItems(bool $filter_translated) {
$this->filterTranslatedItems = $filter_translated;
return $this;
}
}