lionbridge_translation_provider-8.x-2.4/tmgmt_contentapi/src/Services/JobHelper.php
tmgmt_contentapi/src/Services/JobHelper.php
<?php
namespace Drupal\tmgmt_contentapi\Services;
use Drupal\Component\Utility\Xss;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\tmgmt\Entity\Job;
use Drupal\tmgmt\Entity\JobItem;
use Drupal\tmgmt\JobInterface;
use Drupal\tmgmt\TranslatorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Component\Transliteration\TransliterationInterface;
/**
* Provides general utility functions for the Job.
*/
class JobHelper {
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected FileSystemInterface $fileSystem;
/**
* The current user service.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected AccountProxyInterface $currentUser;
/**
* The MIME type guesser service.
*
* @var \Symfony\Component\Mime\MimeTypeGuesserInterface
*/
protected MimeTypeGuesserInterface $mimeTypeGuesser;
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* The transliteration service.
*
* @var \Drupal\Component\Transliteration\TransliterationInterface
*/
protected $transliteration;
/**
* Constructs a JobHelper object.
*
* @param \Drupal\Core\File\FileSystemInterface $fileSystem
* The file system service.
* @param \Drupal\Core\Session\AccountProxyInterface $currentUser
* The current user service.
* @param \Symfony\Component\Mime\MimeTypeGuesserInterface $mimeTypeGuesser
* The MIME type guesser service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager service.
* @param \Drupal\Component\Transliteration\TransliterationInterface $transliteration
* The transliteration service.
*/
public function __construct(
FileSystemInterface $fileSystem,
AccountProxyInterface $currentUser,
MimeTypeGuesserInterface $mimeTypeGuesser,
EntityTypeManagerInterface $entityTypeManager,
TransliterationInterface $transliteration
) {
$this->fileSystem = $fileSystem;
$this->currentUser = $currentUser;
$this->mimeTypeGuesser = $mimeTypeGuesser;
$this->entityTypeManager = $entityTypeManager;
$this->transliteration = $transliteration;
}
/**
* Creates an instance of the service using the container.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The service container.
*
* @return static
* An instance of GeneralHelperService.
*/
public static function create(ContainerInterface $container): self {
return new static(
$container->get('file_system'),
$container->get('current_user'),
$container->get('mime_type_guesser'),
$container->get('entity_type.manager'),
$container->get('transliteration'),
);
}
/**
* Retrieves the label of a job.
*
* @param \Drupal\tmgmt\JobInterface $job
* The job entity.
*
* @return string
* The label of the job.
*/
public function getJobLabel(JobInterface $job): string {
return $job->get("label")->value ?? $job->label()->getArguments()["@title"];
}
/**
* Retrieves a sanitized label of a job without special characters.
*
* @param \Drupal\tmgmt\JobInterface $job
* The job entity.
*
* @return string
* The sanitized label.
*/
public function getJobLabelNoSpeChars(JobInterface $job): string {
return $this->getStringNoSpeChars($this->getJobLabel($job));
}
/**
* Removes special characters from a string.
*
* @param string $arg
* The input string.
*
* @return string
* The sanitized string.
*/
public function getStringNoSpeChars(string $arg): string {
$toreturn = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $arg);
$toreturn = mb_ereg_replace("([\.]{2,})", '', $toreturn);
return Xss::filter($toreturn);
}
/**
* Creates a file entity from a URI.
*
* @param string $uri
* The file URI.
*
* @return \Drupal\file\FileInterface|object
* The file entity or an empty object if URI is invalid.
*/
public function createFileObject($uri) {
if (!$uri) {
return (object) [];
}
$fileMime = $this->mimeTypeGuesser->guessMimeType($uri);
$file = File::create([
'uid' => $this->currentUser->id(),
'filename' => $this->fileSystem->basename($uri),
'uri' => $uri,
'filemime' => $fileMime,
'filesize' => filesize($uri),
'status' => 1,
]);
$file->save();
return $file;
}
/**
* Adds CPA settings to a job.
*
* @param \Drupal\tmgmt\JobInterface $job
* The job entity.
* @param string $cpsettings
* The CPA settings to add.
*/
public function addCpaSettingsToJob(JobInterface $job, string $cpsettings): void {
$settings = $job->get('settings')->getValue();
$settings[0]['capi-remote'] = $cpsettings;
$job->set('settings', $settings);
}
/**
* Retrieves all active jobs for a translator.
*
* @param \Drupal\tmgmt\TranslatorInterface $translator
* The translator entity.
*
* @return array
* An array of job entities.
*/
public function getAllJobsByTranslator(TranslatorInterface $translator): array {
return $this->entityTypeManager->getStorage('tmgmt_job')->loadByProperties([
'translator' => $translator->id(),
'state' => Job::STATE_ACTIVE,
]);
}
/**
* Retrieves the CPA job ID from a local job.
*
* @param \Drupal\tmgmt\JobInterface $job
* The job entity.
*
* @return string
* The CPA job ID.
*
* @throws \Exception
* If the CPA job ID cannot be found.
*/
public function getCpJobIdfromLocJob(JobInterface $job): string {
// Check if the job is a CPA job.
if ($job->getSetting('capi-remote')) {
$jobSettings = unserialize($job->getSetting('capi-remote'));
$task = $job->getSetting('capi-settings')['task'];
if (isset($task)) {
$first_key = !empty($jobSettings) ? array_key_first($jobSettings) : null;
$firstRequest = $task === 'trans' ? $jobSettings[$first_key][0] : $jobSettings[$first_key];
}
}
if (!isset($firstRequest)) {
throw new \Exception('Job ID could not be found in local job!');
}
try {
// Check if $firstRequest is an object.
if (!is_object($firstRequest)) {
throw new \Exception('Job ID could not be found in local job!');
}
return $firstRequest->getJobId();
}
catch (\Exception $e) {
throw new \Exception('Job ID could not be found in local job!');
}
}
/**
* Resets a job and its items to the active state.
*
* @param \Drupal\tmgmt\JobInterface $job
* The job entity.
* @param \Drupal\file\FileInterface $file
* The file entity containing the XLIFF data.
*
* @return array
* An array of item IDs.
*/
public function resetJobandItemsToActive(JobInterface &$job, FileInterface $file) {
$items = $job->getItems();
$xmlPath = $this->fileSystem->realpath($file->getFileUri());
$xml = simplexml_load_file($xmlPath);
if ($xml === FALSE) {
return [];
}
$xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:1.2');
$itemid_array = [];
foreach ($items as $item) {
$itemId = $item->id();
$groups = $xml->xpath("//xliff:group[@id='{$itemId}']");
if (count($groups) === 1) {
$item->setState(Job::STATE_ACTIVE);
$job->setState(JobItem::STATE_ACTIVE);
$itemid_array[] = $itemId;
}
}
return $itemid_array;
}
/**
* Function to get jobs translator settings.
*
* @param int $tjid
* The job ID.
*
* @return array
* An array of settings.
*/
public function getJobTranslatorSettings($tjid = '') {
// If job not set then get default translators settings.
// Else get the job translator settings.
// Below if condition should only get used in case of cron.
if ($tjid == '') {
$translators = $this->entityTypeManager->getStorage('tmgmt_translator')->loadByProperties(['plugin' => 'contentapi']);
// Check if cron is enabled in any translator.
// If yes then return array with details.
$is_cron_enable_for_any_translator = FALSE;
foreach ($translators as $translator) {
if ($translator->getSetting('cron-settings')['status']) {
$is_cron_enable_for_any_translator = TRUE;
}
}
// Return array with cron set to 1 if any translator has cron enabled.
if ($is_cron_enable_for_any_translator) {
return ['iscronset' => 1];
}
// If no translator has cron enabled then return empty array.
return [];
}
else {
$job = $this->entityTypeManager->getStorage('tmgmt_job')->load($tjid);
$translator = $job->getTranslator();
$iscronset = $translator->getSetting('cron-settings')['status'];
// Check if quick import is enabled or fail safe.
$is_process_method = $translator->getSetting('process_method');
return ['iscronset' => $iscronset, 'process_method' => $is_process_method];
}
}
/**
* Cleans a string for safe directory/file creation and prevents DB errors.
*
* @param string $string
* The original label (job title or node title).
* @param string $basePath
* The base path (e.g. "public://.../job-title/..."). Empty when cleaning for directories.
*
* @return string
* A safe, truncated string for file or directory creation.
*/
public function getCleanStringForDirectoryAndFileCreation(string $string, string $basePath = ''): string {
// Step 1: Transliterate (accents → ASCII).
$title = $this->transliteration->transliterate($string, 'en');
// Step 2: Replace unsupported characters with underscore.
$title = preg_replace('/[^A-Za-z0-9._-]+/', '_', $title);
// Step 3: Collapse multiple underscores.
$title = preg_replace('/_+/', '_', $title);
// Step 4: Trim leading/trailing underscores, dots, and spaces.
$title = trim($title, ' ._');
// Step 5: Prevent reserved Windows names.
$reserved = [
'CON','PRN','AUX','NUL',
'COM1','COM2','COM3','COM4','COM5','COM6','COM7','COM8','COM9',
'LPT1','LPT2','LPT3','LPT4','LPT5','LPT6','LPT7','LPT8','LPT9'
];
if (in_array(strtoupper($title), $reserved, TRUE)) {
$title = '_' . $title;
}
// Step 6: Handle max length based on total URI.
// file_managed.uri column is VARCHAR(255).
// We reserve 100 chars for $basePath and extra suffix (_id_id_lang_lang.xlf).
$maxLength = 150;
if (!empty($basePath)) {
$available = 250 - strlen($basePath); // leave headroom
$maxLength = max(50, $available); // at least 50 chars
}
$title = substr($title, 0, $maxLength);
return $title ?: 'untitled';
}
}
