JobCheckoutManager.php in Translation Management Tool 8
Namespace
Drupal\tmgmtFile
src/JobCheckoutManager.phpView source
<?php
namespace Drupal\tmgmt;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandler;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Provides functionality related to job checkout and submissions.
*
* @ingroup tmgmt_job
*/
class JobCheckoutManager {
use StringTranslationTrait;
use MessengerTrait;
/**
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* @var \Drupal\Core\Extension\ModuleHandler
*/
protected $moduleHandler;
/**
* @var \Drupal\tmgmt\JobQueue
*/
protected $jobQueue;
/**
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
public function __construct(RequestStack $request_stack, JobQueue $job_queue, ModuleHandler $module_handler, ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager) {
$this->requestStack = $request_stack;
$this->jobQueue = $job_queue;
$this->moduleHandler = $module_handler;
$this->configFactory = $config_factory;
$this->entityTypeManager = $entity_type_manager;
}
/**
* Attempts to checkout a number of jobs and prepare the necessary redirects.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state array, used to set the initial redirect.
* @param \Drupal\tmgmt\JobInterface[] $jobs
* Array of jobs to attempt checkout
*
* @ingroup tmgmt_job
*/
public function checkoutAndRedirect(FormStateInterface $form_state, array $jobs) {
$checkout_jobs = $this
->checkoutMultiple($jobs);
$jobs_ready_for_checkout = array_udiff($jobs, $checkout_jobs, function (JobInterface $a, JobInterface $b) {
if ($a
->id() < $b
->id()) {
return -1;
}
elseif ($a
->id() > $b
->id()) {
return 1;
}
else {
return 0;
}
});
// If necessary, do a redirect.
if ($checkout_jobs || $jobs_ready_for_checkout) {
$request = $this->requestStack
->getCurrentRequest();
if ($request->query
->has('destination')) {
// Remove existing destination, as that will prevent us from being
// redirect to the job checkout page. Set the destination as the final
// redirect instead.
$redirect = $request->query
->get('destination');
$request->query
->remove('destination');
}
else {
$redirect = Url::fromRoute('<current>')
->getInternalPath();
}
$this->jobQueue
->startQueue(array_merge($checkout_jobs, $jobs_ready_for_checkout), $redirect);
// Prepare a batch job for the jobs that can be submitted already.
if ($jobs_ready_for_checkout) {
$batch = array(
'title' => t('Submitting jobs'),
'operations' => [],
'finished' => [
JobCheckoutManager::class,
'batchSubmitFinished',
],
);
foreach ($jobs_ready_for_checkout as $job) {
$batch['operations'][] = [
[
JobCheckoutManager::class,
'batchSubmit',
],
[
$job
->id(),
NULL,
],
];
}
batch_set($batch);
}
else {
$form_state
->setRedirectUrl($this->jobQueue
->getNextUrl());
}
// Count of the job messages is one less due to the final redirect.
$this
->messenger()
->addStatus($this
->getStringTranslation()
->formatPlural(count($checkout_jobs), t('One job needs to be checked out.'), t('@count jobs need to be checked out.')));
}
}
/**
* Attempts to check out a number of jobs.
*
* Performs a number of checks on each job and also allows to alter the
* behavior through hooks.
*
* @param \Drupal\tmgmt\JobInterface[] $jobs
* The jobs to be checked out.
* @param bool $skip_request_translation
* (optional) If TRUE, the jobs that can be submitted immediately will be
* prepared but not submitted yet. They will not be returned, the caller
* is responsible for submitting them.
*
* @return \Drupal\tmgmt\JobInterface[]
* List of jobs that have not been submitted immediately and need to be
* processed.
*
* @ingroup tmgmt_job
*
* @see \Drupal\tmgmt\JobCheckoutManager::checkoutAndRedirect()
*/
public function checkoutMultiple(array $jobs, $skip_request_translation = FALSE) {
$remaining_jobs = array();
// Allow other modules to jump in and eg. auto-checkout with rules or use a
// customized checkout form.
$this->moduleHandler
->alter('tmgmt_job_checkout_before', $remaining_jobs, $jobs);
$denied = 0;
foreach ($jobs as $job) {
if (!$job
->isUnprocessed()) {
// Job is already checked out, just ignore that one. This could happen
// if jobs have already been submitted in the before hook.
continue;
}
if (!$this->configFactory
->get('tmgmt.settings')
->get('quick_checkout') || $this
->needsCheckoutForm($job)) {
if (!$job
->access('submit')) {
// Ignore jobs if the user is not allowed to submit, ignore.
$denied++;
// Make sure that the job is saved.
$job
->save();
continue;
}
$remaining_jobs[] = $job;
}
else {
// No manual checkout required. Request translations now, save the job
// in case someone excepts to be able to load the job and have the
// translator available.
$job
->save();
if (!$skip_request_translation) {
$this
->requestTranslation($job);
}
}
}
// Allow other modules to jump in and eg. auto-checkout with rules or use a
// customized checkout form.
$this->moduleHandler
->alter('tmgmt_job_checkout_after', $remaining_jobs, $jobs);
// Display message for created jobs that can not be checked out.
if ($denied) {
$this
->messenger()
->addStatus($this
->getStringTranslation()
->formatPlural($denied, 'One job has been created.', '@count jobs have been created.'));
}
return $remaining_jobs;
}
/**
* Check if a job needs a checkout form.
*
* The current checks include if there is more than one translator available,
* if he has settings and if the job has a fixed target language.
*
* @param \Drupal\tmgmt\JobInterface $job
* The job item.
*
* @return bool
* TRUE if the job needs a checkout form.
*/
public function needsCheckoutForm(JobInterface $job) {
// If the job has no target language (or source language, even though this
// should never be the case in our use case), checkout is mandatory.
if (!$job
->getTargetLangcode() || !$job
->getSourceLangcode()) {
return TRUE;
}
// If no translator is pre-selected, try to pick one automatically.
if (!$job
->hasTranslator()) {
// If there is more than a single translator available or if there are no
// translators available at all checkout is mandatory.
$translators = tmgmt_translator_load_available($job);
if (empty($translators) || count($translators) > 1) {
return TRUE;
}
$translator = reset($translators);
$job->translator = $translator
->id();
}
// If that translator has settings, the checkout is mandatory.
if ($job
->getTranslator()
->hasCheckoutSettings($job)) {
return TRUE;
}
return FALSE;
}
/**
* Batch dispatch callback for submitting a job.
*
* @param int $job_id
* The job ID to submit.
* @param int|null $template_job_id
* (optional) A template job to use for the translator and settings.
*/
public static function batchSubmit($job_id, $template_job_id = NULL, &$context) {
\Drupal::service('tmgmt.job_checkout_manager')
->doBatchSubmit($job_id, $template_job_id);
}
/**
* Batch callback for submitting a job.
*
* @param int $job_id
* The job ID to submit.
* @param int|null $template_job_id
* (optional) A template job to use for the translator and settings.
*/
public function doBatchSubmit($job_id, $template_job_id = NULL) {
/** @var \Drupal\tmgmt\JobInterface $job */
$job = $this->entityTypeManager
->getStorage('tmgmt_job')
->load($job_id);
if (!$job) {
return;
}
// Delete duplicates.
if ($existing_items_ids = $job
->getConflictingItemIds()) {
$item_storage = $this->entityTypeManager
->getStorage('tmgmt_job_item');
if (count($existing_items_ids) == $job
->getItems()) {
$this
->messenger()
->addStatus($this
->t('All job items for job @label are conflicting, the job can not be submitted.', [
'@label' => $job
->label(),
]));
return;
}
$item_storage
->delete($item_storage
->loadMultiple($existing_items_ids));
$num_of_items = count($existing_items_ids);
$this
->messenger()
->addWarning($this
->getStringTranslation()
->formatPlural($num_of_items, '1 conflicting item has been dropped for job @label.', '@count conflicting items have been dropped for job @label.', [
'@label' => $job
->label(),
]));
}
if ($template_job_id && $job_id != $template_job_id) {
/** @var \Drupal\tmgmt\JobInterface $template_job */
$template_job = $this->entityTypeManager
->getStorage('tmgmt_job')
->load($template_job_id);
if ($template_job) {
$job
->set('translator', $template_job
->getTranslatorId());
$job
->set('settings', $template_job
->get('settings')
->getValue());
// If there is a custom label on the template job, copy that as well.
if ($template_job
->get('label')->value) {
$job
->set('label', $template_job
->get('label')->value);
}
}
}
$translator = $job
->getTranslator();
// Check translator availability.
$translatable_status = $translator
->checkTranslatable($job);
if (!$translatable_status
->getSuccess()) {
$this
->messenger()
->addError($this
->t('Job @label is not translatable with the chosen settings: @reason', [
'@label' => $job
->label(),
'@reason' => $translatable_status
->getReason(),
]));
return;
}
if ($this
->requestTranslation($job)) {
$this->jobQueue
->markJobAsProcessed($job);
}
}
/**
* Batch dispatch submission finished callback.
*/
public static function batchSubmitFinished($success, $results, $operations) {
return \Drupal::service('tmgmt.job_checkout_manager')
->doBatchSubmitFinished($success, $results, $operations);
}
/**
* Batch submission finished callback.
*/
public function doBatchSubmitFinished($success, $results, $operations) {
if ($redirect = $this->jobQueue
->getNextUrl()) {
// Proceed to the next redirect queue item, if there is one.
return new RedirectResponse($redirect
->setAbsolute()
->toString());
}
elseif ($destination = $this->jobQueue
->getDestination()) {
// Proceed to the defined destination if there is one.
return new RedirectResponse(Url::fromUri('base:' . $destination)
->setAbsolute()
->toString());
}
else {
// Per default we want to redirect the user to the overview.
return new RedirectResponse(Url::fromRoute('view.tmgmt_job_overview.page_1')
->setAbsolute()
->toString());
}
}
/**
* Requests translations for a job and prints messages which have happened since
* then.
*
* @param \Drupal\tmgmt\JobInterface $job
* The job object for which translations should be requested.
*
* @return bool
* TRUE if it worked, FALSE if there were any errors of the type error which
* means that something did go wrong.
*/
function requestTranslation(JobInterface $job) {
// Process the translation request.
$job
->requestTranslation();
return tmgmt_write_request_messages($job);
}
}
Classes
Name | Description |
---|---|
JobCheckoutManager | Provides functionality related to job checkout and submissions. |