Compare commits
42 Commits
Author | SHA1 | Date | |
---|---|---|---|
53bae19d3e | |||
d4174cf7d2
|
|||
de496eaea1
|
|||
94db6aa51d
|
|||
c33fcd1d7c
|
|||
3d4bef0e56
|
|||
19b0e68b8b
|
|||
ba74aa9977
|
|||
224240708d
|
|||
4049f9df53
|
|||
caa6ecc2bf | |||
c112b225dc
|
|||
e3194cef48
|
|||
41a7343338
|
|||
90aa909b7f
|
|||
8b35a5691f
|
|||
844264fb62
|
|||
d8d17064d0
|
|||
4dc6060fb3
|
|||
58a256e1e4
|
|||
25faa3d62b
|
|||
c4817a675a
|
|||
cb2161356e
|
|||
b729d057a9
|
|||
c2266f2a67 | |||
d36507b180
|
|||
0e98d67c49 | |||
8159145e07
|
|||
17e84ae0a3 | |||
4e414a952e
|
|||
907a41e057 | |||
3902f5d36f
|
|||
46da639055
|
|||
c84ed9f12b
|
|||
dde792b97a
|
|||
618c925dfa
|
|||
8bc4c0d8d0
|
|||
a5e3c5ed25
|
|||
22bb840705
|
|||
707762d29b
|
|||
b33362a235
|
|||
4d36821ecc
|
@@ -7,6 +7,7 @@
|
||||
**/storage/framework/sessions/*
|
||||
**/storage/framework/views/*
|
||||
**/storage/framework/testing/*
|
||||
**/storage/translation_service/*
|
||||
**/storage/logs/*
|
||||
**/vendor/
|
||||
**/node_modules/
|
||||
|
@@ -1,15 +1,33 @@
|
||||
APP_NAME=Laravel
|
||||
APP_NAME="My Projects Website"
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_TIMEZONE=UTC
|
||||
APP_URL=http://localhost
|
||||
|
||||
APP_CAPTCHA=false
|
||||
CAPTCHA_API_DOMAIN=http://your-domain-captcha-or-IP:8081
|
||||
CAPTCHA_PRIVATE_TOKEN=
|
||||
CAPTCHA_STATIC_PATH=http://your-domain-captcha-or-IP:8081/captcha
|
||||
CAPTCHA_PUBLIC_TOKEN=
|
||||
|
||||
# Don't forget to configure MAIL_MAILER to send mail.
|
||||
FEEDBACK_MAIL_NOTIFICATIONS=false
|
||||
FEEDBACK_MAIL_TO=
|
||||
|
||||
TRANSLATION_SERVICE_ENABLE=false
|
||||
# yandex or log
|
||||
TRANSLATE_SERVICE=log
|
||||
TRANSLATE_YANDEX_FOLDER_ID=
|
||||
TRANSLATE_YANDEX_AUTHORIZED_KEY_PATH=/storage/translation_service/authorized_key.json
|
||||
TRANSLATE_YANDEX_LIMIT_MAX_REQUEST=20
|
||||
TRANSLATE_YANDEX_LIMIT_RATE_SECONDS=1
|
||||
TRANSLATE_YANDEX_LIMIT_MAX_SYMBOLS=9000
|
||||
|
||||
TRANSLATE_LOG_LIMIT_MAX_REQUEST=20
|
||||
TRANSLATE_LOG_LIMIT_RATE_SECONDS=1
|
||||
TRANSLATE_LOG_LIMIT_MAX_SYMBOLS=9000
|
||||
|
||||
APP_FORCE_HTTPS=false
|
||||
|
||||
APP_DEFAULT_LOCALE=ru
|
||||
@@ -58,7 +76,7 @@ REDIS_HOST=app-redis
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=log
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
|
@@ -0,0 +1,50 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Files;
|
||||
|
||||
use App\Services\Commands\DeleteOldFilesService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
final class DeleteOldFilesFromStorage extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'files:delete-old-files-from-storage';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove temporary files or files that have been deleted from the storage table';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(DeleteOldFilesService $deleteOldFilesService): void
|
||||
{
|
||||
$temporaryBeforeDate = Carbon::now()->subDays(3);
|
||||
$deletedBeforeDate = Carbon::now()->subDays(7);
|
||||
|
||||
$result = $deleteOldFilesService->fromStorage($temporaryBeforeDate, $deletedBeforeDate);
|
||||
if ($result->isError()) {
|
||||
$this->error($result->getMessage());
|
||||
return;
|
||||
}
|
||||
$this->info($result->getMessage());
|
||||
}
|
||||
}
|
@@ -3,12 +3,15 @@
|
||||
namespace App\Dto\Service\Admin\Project\About;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
use App\Dto\Service\Storage\Storages;
|
||||
|
||||
final readonly class StoreUpdate extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private string $title,
|
||||
private string $description,
|
||||
private Storages $storages,
|
||||
private bool $isTranslateAutomatically = false,
|
||||
) { }
|
||||
|
||||
public function getTitle(): string
|
||||
@@ -20,4 +23,14 @@ final readonly class StoreUpdate extends Dto
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function getStorages(): Storages
|
||||
{
|
||||
return $this->storages;
|
||||
}
|
||||
|
||||
public function isTranslateAutomatically(): bool
|
||||
{
|
||||
return $this->isTranslateAutomatically;
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ final readonly class Content
|
||||
public function __construct(
|
||||
private int $languageId,
|
||||
private string $title,
|
||||
private bool $isTranslateAutomatically = false,
|
||||
) { }
|
||||
|
||||
public function getLanguageId(): int
|
||||
@@ -18,4 +19,9 @@ final readonly class Content
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function isTranslateAutomatically(): bool
|
||||
{
|
||||
return $this->isTranslateAutomatically;
|
||||
}
|
||||
}
|
||||
|
@@ -2,12 +2,16 @@
|
||||
|
||||
namespace App\Dto\Service\Admin\Project\DocumentationContent;
|
||||
|
||||
use App\Dto\Service\Storage\Storages;
|
||||
|
||||
final readonly class Content
|
||||
{
|
||||
public function __construct(
|
||||
private int $languageId,
|
||||
private string $title,
|
||||
private string $content,
|
||||
private Storages $storages,
|
||||
private bool $isTranslateAutomatically = false,
|
||||
) { }
|
||||
|
||||
public function getLanguageId(): int
|
||||
@@ -24,4 +28,14 @@ final readonly class Content
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function getStorages(): Storages
|
||||
{
|
||||
return $this->storages;
|
||||
}
|
||||
|
||||
public function isTranslateAutomatically(): bool
|
||||
{
|
||||
return $this->isTranslateAutomatically;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Project\ServiceTranslate;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
final readonly class Translation extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private int $languageId,
|
||||
private ?int $sourceLanguageId,
|
||||
private ?string $code,
|
||||
) { }
|
||||
|
||||
public function getLanguageId(): int
|
||||
{
|
||||
return $this->languageId;
|
||||
}
|
||||
|
||||
public function getSourceLanguageId(): ?int
|
||||
{
|
||||
return $this->sourceLanguageId;
|
||||
}
|
||||
|
||||
public function getCode(): ?string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Project\ServiceTranslate;
|
||||
|
||||
final class Translations
|
||||
{
|
||||
private array $translations;
|
||||
|
||||
public function add(Translation $translation): void
|
||||
{
|
||||
$this->translations[] = $translation;
|
||||
}
|
||||
|
||||
public function getTranslations(): array
|
||||
{
|
||||
return $this->translations;
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\Admin\Project\ServiceTranslate;
|
||||
|
||||
use App\Dto\Service\Dto;
|
||||
|
||||
final readonly class Update extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private Translations $translations,
|
||||
) { }
|
||||
|
||||
public function getTranslations(): Translations
|
||||
{
|
||||
return $this->translations;
|
||||
}
|
||||
}
|
@@ -8,10 +8,16 @@ final readonly class Update extends Dto
|
||||
{
|
||||
public function __construct(
|
||||
private Translations $translations,
|
||||
private bool $isTranslateAutomatically = false,
|
||||
) { }
|
||||
|
||||
public function getTranslations(): Translations
|
||||
{
|
||||
return $this->translations;
|
||||
}
|
||||
|
||||
public function isTranslateAutomatically(): bool
|
||||
{
|
||||
return $this->isTranslateAutomatically;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\DocumentationContent;
|
||||
|
||||
use App\Dto\Service\Storage\Storages;
|
||||
use App\Models\DocumentationContent;
|
||||
|
||||
final class StorageDto
|
||||
{
|
||||
/**
|
||||
* @var array [][DocumentationContent documentationContent, Storages storages]
|
||||
*/
|
||||
private array $storages = [];
|
||||
|
||||
public function add(DocumentationContent $documentationContent, Storages $storages): void
|
||||
{
|
||||
$this->storages[] = [
|
||||
'documentationContent' => $documentationContent,
|
||||
'storages' => $storages
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array [][DocumentationContent documentationContent, Storages storages]
|
||||
*/
|
||||
public function getStorages(): array
|
||||
{
|
||||
return $this->storages;
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\ProjectTranslationServiceHash;
|
||||
|
||||
final class Fields
|
||||
{
|
||||
private array $fields = [];
|
||||
|
||||
public function add(string $name, string $value): self
|
||||
{
|
||||
$this->fields[$name] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFields(): array
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
public function getNames(): array
|
||||
{
|
||||
return \array_keys($this->fields);
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\ProjectTranslationServiceHash;
|
||||
|
||||
final class HashStatusWaiting
|
||||
{
|
||||
private array $hash = [];
|
||||
|
||||
public function add(string $fieldName, bool $isWaiting): void
|
||||
{
|
||||
$this->hash[$fieldName] = $isWaiting;
|
||||
}
|
||||
|
||||
public function isStatusWaiting(string $fieldName): bool
|
||||
{
|
||||
return $this->hash[$fieldName] ?? false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\ProjectTranslationServiceHash;
|
||||
|
||||
final class Hashes
|
||||
{
|
||||
private array $hashes = [];
|
||||
private array $ids = [];
|
||||
|
||||
public function add(int $hashId, string $fieldName, string $hash): void
|
||||
{
|
||||
$this->hashes[$hashId] = [
|
||||
'field' => $fieldName,
|
||||
'hash' => $hash,
|
||||
];
|
||||
$this->ids[] = $hashId;
|
||||
}
|
||||
|
||||
public function getHash(int $hashId): ?array
|
||||
{
|
||||
return $this->hashes[$hashId] ?? null;
|
||||
}
|
||||
|
||||
public function getIds(): array
|
||||
{
|
||||
return $this->ids;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\ProjectTranslationServiceHash;
|
||||
|
||||
final class TranslateFields
|
||||
{
|
||||
private array $fields = [];
|
||||
private array $hashes = [];
|
||||
|
||||
public function add(int $languageId, string $fieldName, string $hash, int $hashId): self
|
||||
{
|
||||
if (!isset($this->fields[$languageId])) {
|
||||
$this->fields[$languageId] = [];
|
||||
}
|
||||
$this->fields[$languageId][] = $fieldName;
|
||||
|
||||
if (!isset($this->hashes[$languageId])) {
|
||||
$this->hashes[$languageId] = [];
|
||||
}
|
||||
$this->hashes[$languageId][$fieldName] = [
|
||||
'hash' => $hash,
|
||||
'hashId' => $hashId,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFields(): array
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
public function getHashesByLanguage(int $languageId): array
|
||||
{
|
||||
return $this->hashes[$languageId] ?? [];
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\ProjectTranslationServiceTextHash;
|
||||
|
||||
final class Codes
|
||||
{
|
||||
private array $codes = [];
|
||||
|
||||
public function add(string $code, string $value): self
|
||||
{
|
||||
$this->codes[$code] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCodes(): array
|
||||
{
|
||||
return $this->codes;
|
||||
}
|
||||
|
||||
public function getCodeNames(): array
|
||||
{
|
||||
return \array_keys($this->codes);
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\ProjectTranslationServiceTextHash;
|
||||
|
||||
final class HashStatusWaiting
|
||||
{
|
||||
private array $hash = [];
|
||||
|
||||
public function add(string $code, bool $isWaiting): void
|
||||
{
|
||||
$this->hash[$code] = $isWaiting;
|
||||
}
|
||||
|
||||
public function isStatusWaiting(string $code): bool
|
||||
{
|
||||
return $this->hash[$code] ?? false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\ProjectTranslationServiceTextHash;
|
||||
|
||||
final class Hashes
|
||||
{
|
||||
private array $hashes = [];
|
||||
private array $ids = [];
|
||||
|
||||
public function add(int $hashId, string $code, string $hash): void
|
||||
{
|
||||
$this->hashes[$hashId] = [
|
||||
'code' => $code,
|
||||
'hash' => $hash,
|
||||
];
|
||||
$this->ids[] = $hashId;
|
||||
}
|
||||
|
||||
public function getHash(int $hashId): ?array
|
||||
{
|
||||
return $this->hashes[$hashId] ?? null;
|
||||
}
|
||||
|
||||
public function getIds(): array
|
||||
{
|
||||
return $this->ids;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\Service\ProjectTranslationServiceTextHash;
|
||||
|
||||
final class TranslateCodes
|
||||
{
|
||||
private array $codes = [];
|
||||
private array $hashes = [];
|
||||
|
||||
public function add(int $languageId, string $code, string $hash, int $hashId): self
|
||||
{
|
||||
if (!isset($this->codes[$languageId])) {
|
||||
$this->codes[$languageId] = [];
|
||||
}
|
||||
$this->codes[$languageId][] = $code;
|
||||
|
||||
if (!isset($this->hashes[$languageId])) {
|
||||
$this->hashes[$languageId] = [];
|
||||
}
|
||||
$this->hashes[$languageId][$code] = [
|
||||
'hash' => $hash,
|
||||
'hashId' => $hashId,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCodes(): array
|
||||
{
|
||||
return $this->codes;
|
||||
}
|
||||
|
||||
public function getHashesByLanguage(int $languageId): array
|
||||
{
|
||||
return $this->hashes[$languageId] ?? [];
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Dto\View\Volt\Form;
|
||||
|
||||
use App\Enums\Morph;
|
||||
use App\Helpers\Helpers;
|
||||
|
||||
final readonly class WysiwygStorageUpload
|
||||
{
|
||||
public function __construct(
|
||||
private string $inputName,
|
||||
private Morph $morph,
|
||||
) { }
|
||||
|
||||
public function getInputName(): string
|
||||
{
|
||||
return $this->inputName;
|
||||
}
|
||||
|
||||
public function getRequestInputName(): string
|
||||
{
|
||||
return Helpers::formatAttributeNameToRequestName($this->getInputName());
|
||||
}
|
||||
|
||||
public function getMorph(): Morph
|
||||
{
|
||||
return $this->morph;
|
||||
}
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Illuminate\Cache\TaggedCache;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
enum CacheTag: string
|
||||
{
|
||||
case Project = 'project';
|
||||
case ProjectTranslation = 'project_translation';
|
||||
case DocumantationVersion = 'documantation_version';
|
||||
|
||||
public function getCache(): TaggedCache
|
||||
{
|
||||
return Cache::tags($this->value);
|
||||
}
|
||||
}
|
@@ -2,16 +2,25 @@
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Models\DocumentationCategoryContent;
|
||||
use App\Models\DocumentationContent;
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectContent;
|
||||
|
||||
enum Morph: int
|
||||
{
|
||||
case Project = 1;
|
||||
case DocumentationContent = 2;
|
||||
case ProjectContent = 3;
|
||||
case DocumentationCategoryContent = 4;
|
||||
|
||||
public function getPathModel(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Project => Project::class,
|
||||
self::Project => Project::class,
|
||||
self::DocumentationContent => DocumentationContent::class,
|
||||
self::ProjectContent => ProjectContent::class,
|
||||
self::DocumentationCategoryContent => DocumentationCategoryContent::class,
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -21,6 +21,9 @@ enum Permission: string
|
||||
self::AdminPanel => [
|
||||
'view' => __('permissions.Administrative panel allowed'),
|
||||
],
|
||||
self::Project => array_merge($this->getBasePermissions(), [
|
||||
'Setting up automatic translation' => __('permissions.Setting up automatic translation'),
|
||||
]),
|
||||
self::ProjectContent => [
|
||||
'view' => __('permissions.Allowed to watch'),
|
||||
'create' => __('permissions.Allowed to create'),
|
||||
|
@@ -0,0 +1,9 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Enums\ProjectTranslationServiceHashes;
|
||||
|
||||
enum Status: int
|
||||
{
|
||||
case Waiting = 0;
|
||||
case Success = 10;
|
||||
}
|
@@ -9,39 +9,43 @@ use App\Contracts\StorageType\Video;
|
||||
enum StorageType: int implements Image, Video, Audio
|
||||
{
|
||||
case Logo = 1;
|
||||
case ContentImages = 2;
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Logo => __('validation.attributes.logo'),
|
||||
self::ContentImages => __('validation.attributes.content_images'),
|
||||
};
|
||||
}
|
||||
|
||||
public function getAcceptMimes(): array
|
||||
{
|
||||
return match ($this) {
|
||||
self::Logo => ['jpeg', 'jpg', 'png'],
|
||||
self::Logo => ['jpeg', 'jpg', 'png'],
|
||||
self::ContentImages => ['jpeg', 'jpg', 'png'],
|
||||
};
|
||||
}
|
||||
|
||||
public function isImage(): bool
|
||||
{
|
||||
return match ($this) {
|
||||
self::Logo => true,
|
||||
self::Logo => true,
|
||||
self::ContentImages => true,
|
||||
default => false
|
||||
};
|
||||
}
|
||||
|
||||
public function isVideo(): bool
|
||||
{
|
||||
return match ($this->name) {
|
||||
return match ($this) {
|
||||
default => false
|
||||
};
|
||||
}
|
||||
|
||||
public function isAudio(): bool
|
||||
{
|
||||
return match ($this->name) {
|
||||
return match ($this) {
|
||||
default => false
|
||||
};
|
||||
}
|
||||
|
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions\Services\DocumentationContent;
|
||||
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
|
||||
final class StorageCommandException extends \Exception
|
||||
{
|
||||
public function __construct(private readonly ServiceResultError $resultError, string $message = "", int $code = 0, ?\Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public function getResultError(): ServiceResultError
|
||||
{
|
||||
return $this->resultError;
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions\Services\Translate;
|
||||
|
||||
final class CompletedException extends \Exception
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Admin\Projects;
|
||||
|
||||
use App\Http\Controllers\Admin\Controller;
|
||||
use App\Http\Requests\Admin\Projects\ServiceTranslate\UpdateRequest;
|
||||
use App\Services\Admin\Project\ServiceTranslateService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class ServiceTranslateController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ServiceTranslateService $serviceTranslateService,
|
||||
) { }
|
||||
|
||||
public function view(int $projectId, Request $request): View
|
||||
{
|
||||
$user = $request->user();
|
||||
$result = $this->serviceTranslateService->view($projectId, $user);
|
||||
if ($result->isError()) {
|
||||
$this->errors($result);
|
||||
}
|
||||
|
||||
return view('admin.projects.service-translate.view', $result->getData());
|
||||
}
|
||||
|
||||
public function update(int $projectId, UpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$data = $request->getDto();
|
||||
$user = $request->user();
|
||||
$result = $this->serviceTranslateService->update($projectId, $data, $user);
|
||||
|
||||
if ($result->isError()) {
|
||||
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||
}
|
||||
|
||||
return redirect()->route('admin.projects.service-translate.view', ['project' => $projectId])->withSuccess($result->getMessage());
|
||||
}
|
||||
}
|
@@ -19,7 +19,9 @@ final class AuthController extends Controller
|
||||
|
||||
public function login(): View
|
||||
{
|
||||
return view('login');
|
||||
return view('login', [
|
||||
'captcha' => config('app.captcha', false),
|
||||
]);
|
||||
}
|
||||
|
||||
public function authorization(AuthorizationRequest $request): RedirectResponse
|
||||
|
@@ -19,6 +19,7 @@ final class FeedbackController extends Controller
|
||||
return view('site.feedback.index', [
|
||||
'project' => $request->get('project'),
|
||||
'websiteTranslations' => $request->get('websiteTranslations'),
|
||||
'captcha' => config('app.captcha', false),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Enums\CacheTag;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final readonly class DocumentationVersion
|
||||
@@ -17,7 +17,7 @@ final readonly class DocumentationVersion
|
||||
}
|
||||
|
||||
$seconds = 3600;
|
||||
$version = CacheTag::DocumantationVersion->getCache()->remember(self::class . $project->id . '-' . $versionSlug, $seconds, function () use ($project, $versionSlug) {
|
||||
$version = Cache::remember(self::class . $project->id . '-' . $versionSlug, $seconds, function () use ($project, $versionSlug) {
|
||||
return $project->documentationVersions()->where('slug', $versionSlug)->first() ?? false;
|
||||
});
|
||||
if ($version === false) {
|
||||
|
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Enums\CacheTag;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Closure;
|
||||
|
||||
@@ -17,7 +17,7 @@ final class ProjectAndLanguage extends ProjectLanguage
|
||||
}
|
||||
|
||||
$seconds = 3600;
|
||||
$project = CacheTag::Project->getCache()->remember(self::class . $projectCode, $seconds, function () use ($projectCode) {
|
||||
$project = Cache::remember(self::class . $projectCode, $seconds, function () use ($projectCode) {
|
||||
return $this->projectRepository->getProjectByCode($projectCode) ?? false;
|
||||
});
|
||||
if ($project === false) {
|
||||
|
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Enums\CacheTag;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
@@ -21,7 +21,7 @@ final class ProjectDomainAndLanguage extends ProjectLanguage
|
||||
}
|
||||
|
||||
$seconds = 3600;
|
||||
$project = CacheTag::Project->getCache()->remember(self::class . $httpHost, $seconds, function () use ($httpHost) {
|
||||
$project = Cache::remember(self::class . $httpHost, $seconds, function () use ($httpHost) {
|
||||
return $this->projectRepository->getProjectByHttpHost($httpHost) ?? false;
|
||||
});
|
||||
if ($project === false) {
|
||||
|
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Enums\CacheTag;
|
||||
use App\Models\Project;
|
||||
use App\Repositories\ProjectLanguageRepository;
|
||||
use App\Repositories\ProjectRepository;
|
||||
use App\Repositories\ProjectTranslationRepository;
|
||||
use App\Services\WebsiteTranslations;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
abstract class ProjectLanguage
|
||||
{
|
||||
@@ -21,7 +21,7 @@ abstract class ProjectLanguage
|
||||
protected function getWebsiteTranslations(Project $project, ?string $languageCode): ?WebsiteTranslations
|
||||
{
|
||||
$seconds = 3600 * 3;
|
||||
$language = CacheTag::Project->getCache()->remember(self::class . $project->id . '-' . $languageCode, $seconds, function () use ($project, $languageCode) {
|
||||
$language = Cache::remember(self::class . $project->id . '-' . $languageCode, $seconds, function () use ($project, $languageCode) {
|
||||
return $this->projectLanguageRepository->getProjectLanguageByCodeOrDefault($project, $languageCode) ?? false;
|
||||
});
|
||||
if ($language === false) {
|
||||
@@ -34,7 +34,7 @@ abstract class ProjectLanguage
|
||||
}
|
||||
|
||||
$seconds = 3600 * 24;
|
||||
$translations = CacheTag::ProjectTranslation->getCache()->remember(self::class . '-translations-' . $project->id . '-' . $language->id, $seconds, function () use ($project, $language) {
|
||||
$translations = Cache::remember(self::class . '-translations-' . $project->id . '-' . $language->id, $seconds, function () use ($project, $language) {
|
||||
return $this->projectTranslationRepository->getProjectTranslations($project->id, $language->id)->all()->pluck('text', 'code')->toArray();
|
||||
});
|
||||
|
||||
|
@@ -4,10 +4,19 @@ namespace App\Http\Requests\Admin\Projects\About;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Admin\Project\About\StoreUpdate;
|
||||
use App\Dto\Service\Storage\Storages;
|
||||
use App\Enums\StorageType;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'storage.content_images.*.file' => __('validation.attributes.content_images'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
@@ -16,6 +25,8 @@ final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
return [
|
||||
'title' => ['required', 'string', 'max:255',],
|
||||
'description' => ['nullable', 'string',],
|
||||
'storage.content_images.*.file' => ['numeric', 'min:1'],
|
||||
'translate-automatically' => ['nullable', 'boolean'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -24,6 +35,19 @@ final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
return new StoreUpdate(
|
||||
title: $this->input('title'),
|
||||
description: $this->input('description'),
|
||||
storages: $this->storages(),
|
||||
isTranslateAutomatically: (bool) $this->input('translate-automatically', false),
|
||||
);
|
||||
}
|
||||
|
||||
private function storages(): Storages
|
||||
{
|
||||
$storages = new Storages();
|
||||
|
||||
$content = $this->input('storage', []);
|
||||
$images = $content['content_images'] ?? [];
|
||||
$storages->addMany($images, StorageType::ContentImages);
|
||||
|
||||
return $storages;
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
'sort' => ['required', 'integer', 'min:-1000', 'max:1000'],
|
||||
'parent_id' => ['nullable', 'integer', 'exists:documentation_categories,id'],
|
||||
'content.*.title' => ['required', 'string', 'max:255'],
|
||||
'translate-automatically.*' => ['nullable', 'boolean'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -47,6 +48,7 @@ final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
$contents->addContent(new Content(
|
||||
languageId: (int) $languageId,
|
||||
title: $content['title'],
|
||||
isTranslateAutomatically: (bool) $this->input('translate-automatically.' . $languageId, false),
|
||||
));
|
||||
}
|
||||
return $contents;
|
||||
|
@@ -6,10 +6,19 @@ use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Admin\Project\Documentation\StoreUpdate;
|
||||
use App\Dto\Service\Admin\Project\DocumentationContent\Content;
|
||||
use App\Dto\Service\Admin\Project\DocumentationContent\Contents;
|
||||
use App\Dto\Service\Storage\Storages;
|
||||
use App\Enums\StorageType;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'content.*.content_images.*.file' => __('validation.attributes.content_images'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
@@ -22,6 +31,8 @@ final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
'category_id' => ['nullable', 'integer', 'exists:documentation_categories,id'],
|
||||
'content.*.title' => ['required', 'string', 'max:255'],
|
||||
'content.*.content' => ['nullable', 'string'],
|
||||
'content.*.content_images.*.file' => ['numeric', 'min:1'],
|
||||
'translate-automatically.*' => ['nullable', 'boolean'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -49,8 +60,20 @@ final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||
languageId: (int) $languageId,
|
||||
title: $content['title'],
|
||||
content: $content['content'] ?? '',
|
||||
storages: $this->contentStorages($content),
|
||||
isTranslateAutomatically: (bool) $this->input('translate-automatically.' . $languageId, false),
|
||||
));
|
||||
}
|
||||
return $contents;
|
||||
}
|
||||
|
||||
private function contentStorages(array $content): Storages
|
||||
{
|
||||
$storages = new Storages();
|
||||
|
||||
$images = $content['content_images'] ?? [];
|
||||
$storages->addMany($images, StorageType::ContentImages);
|
||||
|
||||
return $storages;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,56 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\Admin\Projects\ServiceTranslate;
|
||||
|
||||
use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Admin\Project\ServiceTranslate\Translation;
|
||||
use App\Dto\Service\Admin\Project\ServiceTranslate\Translations;
|
||||
use App\Dto\Service\Admin\Project\ServiceTranslate\Update;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
final class UpdateRequest extends FormRequest implements FormRequestDto
|
||||
{
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'language.*.id' => __('validation.attributes.language_id'),
|
||||
'language.*.code' => __('validation.attributes.code'),
|
||||
'language.*.source_language_id' => __('validation.attributes.source_language_id'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'language.*.id' => ['required', 'numeric', 'min:1'],
|
||||
'language.*.code' => ['nullable', 'string', 'min:2', 'max:50', 'required_with:language.*.source_language_id'],
|
||||
'language.*.source_language_id' => ['nullable', 'numeric', 'min:1', 'different:language.*.id'],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function getDto(): Update
|
||||
{
|
||||
$translations = new Translations();
|
||||
foreach ($this->input('language', []) as $language) {
|
||||
$sourceLanguageId = $language['source_language_id'] ?? null;
|
||||
if ($sourceLanguageId) {
|
||||
$sourceLanguageId = (int) $sourceLanguageId;
|
||||
}
|
||||
|
||||
$translation = new Translation(
|
||||
languageId: (int) $language['id'],
|
||||
sourceLanguageId: $sourceLanguageId,
|
||||
code: $language['code'] ?? null,
|
||||
);
|
||||
$translations->add($translation);
|
||||
}
|
||||
|
||||
return new Update(
|
||||
translations: $translations,
|
||||
);
|
||||
}
|
||||
}
|
@@ -20,6 +20,7 @@ final class UpdateRequest extends FormRequest implements FormRequestDto
|
||||
'translations' => ['nullable', 'array'],
|
||||
'translations.*.code' => ['required', 'string', new In(Translations::getTranslationCodes())],
|
||||
'translations.*.text' => ['nullable', 'string', 'max:1000'],
|
||||
'translate-automatically' => ['nullable', 'boolean'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -33,6 +34,9 @@ final class UpdateRequest extends FormRequest implements FormRequestDto
|
||||
);
|
||||
}
|
||||
|
||||
return new Update($translations);
|
||||
return new Update(
|
||||
translations: $translations,
|
||||
isTranslateAutomatically: (bool) $this->input('translate-automatically', false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -13,12 +13,17 @@ final class AuthorizationRequest extends FormRequest implements FormRequestDto
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
$rules = [
|
||||
'email' => ['required', 'email', 'max:255'],
|
||||
'password' => ['required', 'min:3'],
|
||||
'captcha-verified' => ['captcha'],
|
||||
'remember' => ['nullable', 'boolean'],
|
||||
];
|
||||
|
||||
if (config('app.captcha', false)) {
|
||||
$rules['captcha-verified'] = ['captcha'];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function getDto(): Authorization
|
||||
|
@@ -32,12 +32,17 @@ final class SendRequest extends FormRequest implements FormRequestDto
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
$rules = [
|
||||
'name' => ['nullable', 'string', 'max:255'],
|
||||
'email' => ['nullable', 'string', 'max:255', 'email'],
|
||||
'message' => ['required', 'string', 'max:5000'],
|
||||
'captcha-verified' => ['captcha'],
|
||||
];
|
||||
|
||||
if (config('app.captcha', false)) {
|
||||
$rules['captcha-verified'] = ['captcha'];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function getDto(): Send
|
||||
|
@@ -6,7 +6,9 @@ use App\Contracts\FormRequestDto;
|
||||
use App\Dto\Service\Storage\Upload;
|
||||
use App\Enums\Morph;
|
||||
use App\Enums\StorageType;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
final class ImageRequest extends FormRequest implements FormRequestDto
|
||||
@@ -37,4 +39,20 @@ final class ImageRequest extends FormRequest implements FormRequestDto
|
||||
morph: Morph::from((int) $this->input('morph')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error messages for the defined validation rules.*
|
||||
* @return array
|
||||
*/
|
||||
protected function failedValidation(Validator $validator): array
|
||||
{
|
||||
/**
|
||||
* To always return json
|
||||
*/
|
||||
throw new HttpResponseException(response()->json([
|
||||
'errors' => $validator->errors(),
|
||||
'status' => true
|
||||
], 422));
|
||||
}
|
||||
|
||||
}
|
||||
|
58
app/application/app/Jobs/Translate/ProcessProjectContent.php
Normal file
58
app/application/app/Jobs/Translate/ProcessProjectContent.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Jobs\Translate;
|
||||
|
||||
use App\Services\Translate\Project\ProjectContentService;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
|
||||
final class ProcessProjectContent implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, Queueable;
|
||||
|
||||
/**
|
||||
* Get the middleware the job should pass through.
|
||||
*
|
||||
* @return array<int, object>
|
||||
*/
|
||||
public function middleware(): array
|
||||
{
|
||||
return [
|
||||
(new WithoutOverlapping($this->uniqueId()))->expireAfter(180),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly int $projectId,
|
||||
private readonly array $contentIds = [],
|
||||
private readonly array $exceptLanguages = [],
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Get the unique ID for the job.
|
||||
*/
|
||||
public function uniqueId(): string
|
||||
{
|
||||
return 'project-' . $this->projectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function handle(ProjectContentService $projectContentService): void
|
||||
{
|
||||
$result = $projectContentService->translate($this->projectId, $this->contentIds, $this->exceptLanguages);
|
||||
if ($result->isError() && $result->getCode() !== 404) {
|
||||
cache()->lock($this->uniqueId())->forceRelease();
|
||||
throw new Exception($result->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Jobs\Translate;
|
||||
|
||||
use App\Services\Translate\Project\DocumentationCategoryContentService;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
|
||||
final class ProcessProjectDocumentationCategoryContent implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, Queueable;
|
||||
|
||||
/**
|
||||
* Get the middleware the job should pass through.
|
||||
*
|
||||
* @return array<int, object>
|
||||
*/
|
||||
public function middleware(): array
|
||||
{
|
||||
return [
|
||||
(new WithoutOverlapping($this->uniqueId()))->expireAfter(180),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly int $projectDocumentCategoryId,
|
||||
private readonly array $contentIds = [],
|
||||
private readonly array $exceptLanguages = [],
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Get the unique ID for the job.
|
||||
*/
|
||||
public function uniqueId(): string
|
||||
{
|
||||
return 'documentation-category-' . $this->projectDocumentCategoryId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function handle(DocumentationCategoryContentService $categoryContentService): void
|
||||
{
|
||||
$result = $categoryContentService->translate($this->projectDocumentCategoryId, $this->contentIds, $this->exceptLanguages);
|
||||
if ($result->isError() && $result->getCode() !== 404) {
|
||||
cache()->lock($this->uniqueId())->forceRelease();
|
||||
throw new Exception($result->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Jobs\Translate;
|
||||
|
||||
use App\Services\Translate\Project\DocumentationContentService;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
|
||||
final class ProcessProjectDocumentationContent implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, Queueable;
|
||||
|
||||
/**
|
||||
* Get the middleware the job should pass through.
|
||||
*
|
||||
* @return array<int, object>
|
||||
*/
|
||||
public function middleware(): array
|
||||
{
|
||||
return [
|
||||
(new WithoutOverlapping($this->uniqueId()))->expireAfter(180),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly int $projectDocumentId,
|
||||
private readonly array $contentIds = [],
|
||||
private readonly array $exceptLanguages = [],
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Get the unique ID for the job.
|
||||
*/
|
||||
public function uniqueId(): string
|
||||
{
|
||||
return 'documentation-' . $this->projectDocumentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function handle(DocumentationContentService $documentationContentService): void
|
||||
{
|
||||
$result = $documentationContentService->translate($this->projectDocumentId, $this->contentIds, $this->exceptLanguages);
|
||||
if ($result->isError() && $result->getCode() !== 404) {
|
||||
cache()->lock($this->uniqueId())->forceRelease();
|
||||
throw new Exception($result->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Jobs\Translate;
|
||||
|
||||
use App\Services\Translate\Project\TranslationTextService;
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
|
||||
final class ProcessTranslationText implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, Queueable;
|
||||
|
||||
/**
|
||||
* Get the middleware the job should pass through.
|
||||
*
|
||||
* @return array<int, object>
|
||||
*/
|
||||
public function middleware(): array
|
||||
{
|
||||
return [
|
||||
(new WithoutOverlapping($this->uniqueId()))->expireAfter(180),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly int $projectId,
|
||||
private readonly int $languageId,
|
||||
private readonly array $translateTextCode = [],
|
||||
private readonly array $exceptLanguages = [],
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Get the unique ID for the job.
|
||||
*/
|
||||
public function uniqueId(): string
|
||||
{
|
||||
return 'ProcessTranslationText-' . $this->projectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function handle(TranslationTextService $translationTextService): void
|
||||
{
|
||||
$result = $translationTextService->translate($this->projectId, $this->languageId, $this->translateTextCode, $this->exceptLanguages);
|
||||
if ($result->isError() && $result->getCode() !== 404) {
|
||||
cache()->lock($this->uniqueId())->forceRelease();
|
||||
throw new Exception($result->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -37,6 +37,7 @@ final class Documentation extends Model
|
||||
'slug',
|
||||
'is_public',
|
||||
'category_id',
|
||||
'sort',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@@ -2,13 +2,15 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Contracts\Models\Storage as StorageContract;
|
||||
use App\Models\Traits\StorageTrait;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
final class DocumentationContent extends Model
|
||||
final class DocumentationContent extends Model implements StorageContract
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
use HasFactory, SoftDeletes, StorageTrait;
|
||||
|
||||
protected $table = 'documentation_content';
|
||||
|
||||
|
@@ -2,18 +2,21 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Contracts\Models\Storage as StorageContract;
|
||||
use App\Models\Traits\StorageTrait;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
final class ProjectContent extends Model
|
||||
final class ProjectContent extends Model implements StorageContract
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
use HasFactory, SoftDeletes, StorageTrait;
|
||||
|
||||
protected $table = 'project_content';
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'description',
|
||||
'language_id',
|
||||
];
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ use App\Models\Scopes\SortScope;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
|
||||
|
||||
@@ -70,4 +71,9 @@ final class ProjectLanguage extends Model
|
||||
},
|
||||
)->shouldCache();
|
||||
}
|
||||
|
||||
public function serviceTranslate(): HasOne
|
||||
{
|
||||
return $this->hasOne(ProjectTranslationService::class, 'language_id', 'id');
|
||||
}
|
||||
}
|
||||
|
24
app/application/app/Models/ProjectTranslationService.php
Normal file
24
app/application/app/Models/ProjectTranslationService.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class ProjectTranslationService extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'project_translation_service';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'code',
|
||||
'language_id',
|
||||
'source_language_id',
|
||||
];
|
||||
}
|
51
app/application/app/Models/ProjectTranslationServiceHash.php
Normal file
51
app/application/app/Models/ProjectTranslationServiceHash.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Morph;
|
||||
use App\Enums\ProjectTranslationServiceHashes\Status;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class ProjectTranslationServiceHash extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'project_translation_service_hashes';
|
||||
|
||||
/**
|
||||
* The model's default values for attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [
|
||||
'status' => Status::Waiting,
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'language_id',
|
||||
'field',
|
||||
'status',
|
||||
'hash',
|
||||
'morph_type',
|
||||
'morph_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'status' => Status::class,
|
||||
'morph_type' => Morph::class,
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ProjectTranslationServiceHashes\Status;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class ProjectTranslationServiceTextHash extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'project_translation_service_text_hashes';
|
||||
|
||||
/**
|
||||
* The model's default values for attributes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [
|
||||
'status' => Status::Waiting,
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'language_id',
|
||||
'code',
|
||||
'status',
|
||||
'hash',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'status' => Status::class,
|
||||
];
|
||||
}
|
||||
}
|
60
app/application/app/Notifications/ReviewAdded.php
Normal file
60
app/application/app/Notifications/ReviewAdded.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Enums\Site\ProjectSection;
|
||||
use App\Models\ProjectFeedback;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
final class ReviewAdded extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly ProjectFeedback $feedback
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*/
|
||||
public function toMail(object $notifiable): MailMessage
|
||||
{
|
||||
$project = $this->feedback->project;
|
||||
|
||||
return (new MailMessage)
|
||||
->subject(__('notification.Review Added: :name', ['name' => $project->name]))
|
||||
->line(__('notification.Added a new review.'))
|
||||
->action(__('notification.Project :name', ['name' => $project->name]), \url(ProjectSection::Home->url($project)))
|
||||
->line( __('site.attributes.name') . ': ' . $this->feedback->name)
|
||||
->line( __('site.attributes.email') . ': ' . $this->feedback->email)
|
||||
->line(__('site.attributes.message') . ': ' . $this->feedback->message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(object $notifiable): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
17
app/application/app/Policies/DocumentationContentPolicy.php
Normal file
17
app/application/app/Policies/DocumentationContentPolicy.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
final readonly class DocumentationContentPolicy extends Policy
|
||||
{
|
||||
public function upload(User $user): bool
|
||||
{
|
||||
if ($user->hasPermission('documentation.create') || $user->hasPermission('documentation.update')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -26,4 +26,13 @@ final readonly class ProjectContentPolicy extends Policy
|
||||
{
|
||||
return $user->hasPermission('project-content.update');
|
||||
}
|
||||
|
||||
public function upload(User $user): bool
|
||||
{
|
||||
if ($user->hasPermission('project-content.create') || $user->hasPermission('project-content.update')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -32,6 +32,11 @@ final readonly class ProjectPolicy extends Policy
|
||||
return $user->hasPermission('project.delete');
|
||||
}
|
||||
|
||||
public function settingUpAutomaticTranslation(User $user, Project $project): bool
|
||||
{
|
||||
return $user->hasPermission('project.setting-up-automatic-translation');
|
||||
}
|
||||
|
||||
public function upload(User $user): bool
|
||||
{
|
||||
if ($user->hasPermission('project.create') || $user->hasPermission('project.update')) {
|
||||
|
@@ -3,8 +3,10 @@
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Enums\Morph;
|
||||
use App\Services\ProjectFeedback\ProjectFeedbackCommandHandler;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use App\Services\Search\Search;
|
||||
use App\Services\Site\FeedbackService;
|
||||
use App\Services\Storage\Image\ResizeCommandHandler;
|
||||
use App\Services\Storage\ImageService;
|
||||
use App\Services\Storage\StorageCommandHandler;
|
||||
@@ -31,7 +33,15 @@ class AppServiceProvider extends ServiceProvider
|
||||
});
|
||||
|
||||
$this->app->bind(StorageCommandHandler::class, function () {
|
||||
return new StorageCommandHandler(disc: (string) config('storage.disk'));
|
||||
return new StorageCommandHandler(disc: (string) \config('storage.disk'));
|
||||
});
|
||||
|
||||
$this->app->bind(FeedbackService::class, function (Application $app) {
|
||||
return new FeedbackService(
|
||||
$app->make(ProjectFeedbackCommandHandler::class),
|
||||
(bool) \config('feedback.mail_notifications', false),
|
||||
\config('feedback.mail_to', null),
|
||||
);
|
||||
});
|
||||
|
||||
$this->app->bind(ImageService::class, function (Application $app) {
|
||||
|
@@ -11,7 +11,6 @@ use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
final readonly class DocumentationCategoryRepository
|
||||
@@ -63,7 +62,7 @@ final readonly class DocumentationCategoryRepository
|
||||
->exists();
|
||||
}
|
||||
|
||||
public function getForSelect(?ProjectLanguage $defaultLanguage, ?DocumentationCategory $exceptCategory = null, array $withExcepts = []): array
|
||||
public function getForSelect(DocumentationVersion $version, ?ProjectLanguage $defaultLanguage, ?DocumentationCategory $exceptCategory = null, array $withExcepts = []): array
|
||||
{
|
||||
$with = [
|
||||
'content' => function (HasOne $hasOne) use ($defaultLanguage) {
|
||||
@@ -73,7 +72,7 @@ final readonly class DocumentationCategoryRepository
|
||||
}
|
||||
];
|
||||
|
||||
$categories = DocumentationCategory::query()
|
||||
$categories = $version->categories()
|
||||
->with($with)
|
||||
->when($exceptCategory, function (Builder $query, DocumentationCategory $exceptCategory) {
|
||||
$query->whereNotIn(
|
||||
|
@@ -22,4 +22,13 @@ final readonly class ProjectTranslationRepository
|
||||
$query = ProjectTranslation::query()->withTrashed()->where('project_id', $projectId)->where('language_id', $languageId);
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
|
||||
public function getProjectTranslationByCodes(int $projectId, int $languageId, array $codes): Search
|
||||
{
|
||||
$query = ProjectTranslation::query()
|
||||
->where('project_id', $projectId)
|
||||
->where('language_id', $languageId)
|
||||
->whereIn('code', $codes);
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Contracts\Search;
|
||||
use App\Enums\Morph;
|
||||
use App\Models\ProjectTranslationServiceHash;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
final readonly class ProjectTranslationServiceHashRepository
|
||||
{
|
||||
public function __construct(
|
||||
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||
) { }
|
||||
|
||||
public function getHashes(Morph $morph, int $morphId, ?array $languages = null, ?array $fields = null): Search
|
||||
{
|
||||
$query = ProjectTranslationServiceHash::query()
|
||||
->where('morph_type', $morph)
|
||||
->where('morph_id', $morphId)
|
||||
->when($languages, function (Builder $query) use ($languages) {
|
||||
$query->whereIn('language_id', $languages);
|
||||
})
|
||||
->when($fields, function (Builder $query) use ($fields) {
|
||||
$query->whereIn('field', $fields);
|
||||
});
|
||||
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
|
||||
public function getHashesByIds(array $ids): Search
|
||||
{
|
||||
$query = ProjectTranslationServiceHash::query()->whereIn('id', $ids);
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Models\ProjectTranslationService;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use App\Services\Search\Search;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
final readonly class ProjectTranslationServiceRepository
|
||||
{
|
||||
public function __construct(
|
||||
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||
) { }
|
||||
|
||||
public function getLanguagesBySourceLanguage(int $sourceLanguage, ?array $excludeLanguages = null): Search
|
||||
{
|
||||
$query = ProjectTranslationService::query()
|
||||
->select('language_id', 'code')
|
||||
->where('source_language_id', $sourceLanguage)
|
||||
->when($excludeLanguages, function (Builder $query) use ($excludeLanguages) {
|
||||
$query->whereNotIn('language_id', $excludeLanguages);
|
||||
});
|
||||
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
|
||||
public function getLanguageCodeByLanguageId(int $languageId): ?string
|
||||
{
|
||||
return ProjectTranslationService::query()
|
||||
->select('code')
|
||||
->where('language_id', $languageId)
|
||||
->first()?->code ?? null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Contracts\Search;
|
||||
use App\Models\ProjectTranslationServiceTextHash;
|
||||
use App\Services\Search\CreateSearchInstanceCommand;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
final readonly class ProjectTranslationServiceTextHashRepository
|
||||
{
|
||||
|
||||
public function __construct(
|
||||
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||
) { }
|
||||
|
||||
public function getHashes(array $codes, ?array $languages = null): Search
|
||||
{
|
||||
$query = ProjectTranslationServiceTextHash::query()
|
||||
->whereIn('code', $codes)
|
||||
->when($languages, function (Builder $query) use ($languages) {
|
||||
$query->whereIn('language_id', $languages);
|
||||
});
|
||||
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
|
||||
public function getHashesByIds(array $ids): Search
|
||||
{
|
||||
$query = ProjectTranslationServiceTextHash::query()->whereIn('id', $ids);
|
||||
return $this->createSearchInstanceCommand->execute($query);
|
||||
}
|
||||
}
|
@@ -4,6 +4,8 @@ namespace App\Services\Admin\Project;
|
||||
|
||||
use App\Contracts\ServiceResultError;
|
||||
use App\Dto\Service\Admin\Project\About\StoreUpdate;
|
||||
use App\Enums\Morph;
|
||||
use App\Jobs\Translate\ProcessProjectContent;
|
||||
use App\Models\ProjectContent;
|
||||
use App\Models\User;
|
||||
use App\Repositories\ProjectContentRepository;
|
||||
@@ -13,6 +15,7 @@ use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\StoreUpdateResult;
|
||||
use App\Services\ProjectContent\ProjectContentCommandHandler;
|
||||
use App\Services\Service;
|
||||
use App\Services\Storage\StorageService;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class AboutService extends Service
|
||||
@@ -22,6 +25,7 @@ final class AboutService extends Service
|
||||
private readonly ProjectLanguageRepository $projectLanguageRepository,
|
||||
private readonly ProjectContentRepository $projectContentRepository,
|
||||
private readonly ProjectContentCommandHandler $projectContentCommandHandler,
|
||||
private readonly StorageService $storageService,
|
||||
) { }
|
||||
|
||||
public function languages(int $projectId, User $user): ServiceResultError | ServiceResultArray
|
||||
@@ -66,6 +70,7 @@ final class AboutService extends Service
|
||||
'project' => $project,
|
||||
'language' => $language,
|
||||
'content' => $content,
|
||||
'serviceTranslationEnable' => config('translation_service.enable', false),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -88,12 +93,23 @@ final class AboutService extends Service
|
||||
if ($user->cannot('create', ProjectContent::class)) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$storages = $this->storageService->getStoragesAndValidate($data->getStorages(), Morph::ProjectContent);
|
||||
if (!$storages->isSuccess()) {
|
||||
return $storages;
|
||||
}
|
||||
|
||||
try {
|
||||
$aboutProject = DB::transaction(function () use ($data, $projectId, $languageId) {
|
||||
$aboutProject = DB::transaction(function () use ($data, $projectId, $languageId, $storages) {
|
||||
$dataAboutProject = $this->getDataAboutProject($data);
|
||||
|
||||
return $this->projectContentCommandHandler->handleStore($projectId, $languageId, $dataAboutProject);
|
||||
$aboutProject = $this->projectContentCommandHandler->handleStore($projectId, $languageId, $dataAboutProject);
|
||||
$this->storageService->saveAndDelete($aboutProject, $storages);
|
||||
return $aboutProject;
|
||||
});
|
||||
if (\config('translation_service.enable', false)) {
|
||||
$this->translateContent($aboutProject, $data);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@@ -108,12 +124,23 @@ final class AboutService extends Service
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
$storages = $this->storageService->getStoragesAndValidate($data->getStorages(), Morph::ProjectContent);
|
||||
if (!$storages->isSuccess()) {
|
||||
return $storages;
|
||||
}
|
||||
|
||||
try {
|
||||
$aboutProject = DB::transaction(function () use ($data, $content) {
|
||||
$aboutProject = DB::transaction(function () use ($data, $content, $storages) {
|
||||
$dataAboutProject = $this->getDataAboutProject($data);
|
||||
|
||||
return $this->projectContentCommandHandler->handleUpdate($content, $dataAboutProject);
|
||||
$aboutProject = $this->projectContentCommandHandler->handleUpdate($content, $dataAboutProject);
|
||||
$this->storageService->saveAndDelete($aboutProject, $storages);
|
||||
|
||||
return $aboutProject;
|
||||
});
|
||||
if (\config('translation_service.enable', false)) {
|
||||
$this->translateContent($aboutProject, $data);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@@ -129,4 +156,15 @@ final class AboutService extends Service
|
||||
'description' => $data->getDescription(),
|
||||
];
|
||||
}
|
||||
|
||||
private function translateContent(ProjectContent $projectContent, StoreUpdate $data): void
|
||||
{
|
||||
if (! $data->isTranslateAutomatically()) {
|
||||
return;
|
||||
}
|
||||
$translateExceptLanguages = [$projectContent->language_id];
|
||||
$translateContentIds = [$projectContent->id];
|
||||
|
||||
ProcessProjectContent::dispatch($projectContent->project_id, $translateContentIds, $translateExceptLanguages);
|
||||
}
|
||||
}
|
||||
|
@@ -5,8 +5,11 @@ namespace App\Services\Admin\Project;
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
||||
use App\Dto\Service\Admin\Project\DocumentationCategory\StoreUpdate;
|
||||
use App\Dto\Service\Admin\Project\DocumentationCategoryContent\Content;
|
||||
use App\Dto\Service\Admin\Project\DocumentationCategoryContent\Contents;
|
||||
use App\Exceptions\Services\DocumentationCategory\ParentException;
|
||||
use App\Exceptions\Services\ServiceException;
|
||||
use App\Jobs\Translate\ProcessProjectDocumentationCategoryContent;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Models\DocumentationCategoryContent;
|
||||
use App\Models\ProjectLanguage;
|
||||
@@ -89,7 +92,8 @@ final class DocumentationCategoryService extends Service
|
||||
'version' => $version,
|
||||
'project' => $project,
|
||||
'category' => new DocumentationCategory(),
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($defaultLanguage),
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage),
|
||||
'serviceTranslationEnable' => config('translation_service.enable', false),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -119,7 +123,8 @@ final class DocumentationCategoryService extends Service
|
||||
'version' => $version,
|
||||
'project' => $project,
|
||||
'category' => $category,
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($defaultLanguage, $category, $withCategories),
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage, $category, $withCategories),
|
||||
'serviceTranslationEnable' => config('translation_service.enable', false),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -151,6 +156,10 @@ final class DocumentationCategoryService extends Service
|
||||
|
||||
return $category;
|
||||
});
|
||||
|
||||
if (\config('translation_service.enable', false)) {
|
||||
$this->translateContent($category, $data->getContents());
|
||||
}
|
||||
} catch (ServiceException $e) {
|
||||
return $e->getServiceResultError();
|
||||
} catch (\Throwable $e) {
|
||||
@@ -201,6 +210,10 @@ final class DocumentationCategoryService extends Service
|
||||
|
||||
return $category;
|
||||
});
|
||||
|
||||
if (\config('translation_service.enable', false)) {
|
||||
$this->translateContent($category, $data->getContents());
|
||||
}
|
||||
} catch (ServiceException $e) {
|
||||
return $e->getServiceResultError();
|
||||
} catch (ParentException $e) {
|
||||
@@ -254,4 +267,27 @@ final class DocumentationCategoryService extends Service
|
||||
'parent_id' => $data->getParentId(),
|
||||
];
|
||||
}
|
||||
|
||||
private function translateContent(DocumentationCategory $category, Contents $contents): void
|
||||
{
|
||||
$translateExceptLanguages = [];
|
||||
|
||||
$translateLanguages = [];
|
||||
foreach ($contents->getContents() as $content) {
|
||||
/** @var Content $content */
|
||||
$translateExceptLanguages[] = $content->getLanguageId();
|
||||
if ($content->isTranslateAutomatically()) {
|
||||
$translateLanguages[] = $content->getLanguageId();
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($translateLanguages)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$translateContentIds = $category->contents()->select('id')
|
||||
->whereIn('language_id', $translateLanguages)
|
||||
->get()->pluck('id')->toArray();
|
||||
ProcessProjectDocumentationCategoryContent::dispatch($category->id, $translateContentIds, $translateExceptLanguages);
|
||||
}
|
||||
}
|
||||
|
@@ -5,13 +5,16 @@ namespace App\Services\Admin\Project;
|
||||
use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Dto\Service\Admin\Project\Documentation\StoreUpdate;
|
||||
use App\Dto\Service\Admin\Project\DocumentationContent\Content;
|
||||
use App\Dto\Service\Admin\Project\DocumentationContent\Contents;
|
||||
use App\Exceptions\Services\DocumentationContent\StorageCommandException;
|
||||
use App\Jobs\Translate\ProcessProjectDocumentationContent;
|
||||
use App\Models\Documentation;
|
||||
use App\Models\ProjectLanguage;
|
||||
use App\Models\User;
|
||||
use App\Repositories\DocumentationCategoryRepository;
|
||||
use App\Repositories\DocumentationRepository;
|
||||
use App\Repositories\DocumentationVersionRepository;
|
||||
use App\Repositories\ProjectRepository;
|
||||
use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
@@ -89,7 +92,8 @@ final class DocumentationService extends Service
|
||||
'version' => $version,
|
||||
'project' => $project,
|
||||
'documentation' => new Documentation(),
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($defaultLanguage),
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage),
|
||||
'serviceTranslationEnable' => config('translation_service.enable', false),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -119,7 +123,8 @@ final class DocumentationService extends Service
|
||||
'version' => $version,
|
||||
'project' => $project,
|
||||
'documentation' => $documentation,
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($defaultLanguage, null, $withCategories),
|
||||
'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage, null, $withCategories),
|
||||
'serviceTranslationEnable' => config('translation_service.enable', false),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -150,6 +155,12 @@ final class DocumentationService extends Service
|
||||
|
||||
return $documentation;
|
||||
});
|
||||
|
||||
if (\config('translation_service.enable', false)) {
|
||||
$this->translateContent($documentation, $data->getContents());
|
||||
}
|
||||
} catch (StorageCommandException $e) {
|
||||
return $e->getResultError();
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@@ -190,6 +201,12 @@ final class DocumentationService extends Service
|
||||
|
||||
return $documentation;
|
||||
});
|
||||
|
||||
if (\config('translation_service.enable', false)) {
|
||||
$this->translateContent($documentation, $data->getContents());
|
||||
}
|
||||
} catch (StorageCommandException $e) {
|
||||
return $e->getResultError();
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@@ -236,4 +253,27 @@ final class DocumentationService extends Service
|
||||
'category_id' => $data->getCategoryId(),
|
||||
];
|
||||
}
|
||||
|
||||
private function translateContent(Documentation $documentation, Contents $contents): void
|
||||
{
|
||||
$translateExceptLanguages = [];
|
||||
|
||||
$translateLanguages = [];
|
||||
foreach ($contents->getContents() as $content) {
|
||||
/** @var Content $content */
|
||||
$translateExceptLanguages[] = $content->getLanguageId();
|
||||
if ($content->isTranslateAutomatically()) {
|
||||
$translateLanguages[] = $content->getLanguageId();
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($translateLanguages)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$translateContentIds = $documentation->contents()->select('id')
|
||||
->whereIn('language_id', $translateLanguages)
|
||||
->get()->pluck('id')->toArray();
|
||||
ProcessProjectDocumentationContent::dispatch($documentation->id, $translateContentIds, $translateExceptLanguages);
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ namespace App\Services\Admin\Project;
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Dto\Builder\DocumentationVersion as DocumentationVersionBuilderDto;
|
||||
use App\Dto\Service\Admin\Project\DocumentationVersion\StoreUpdate;
|
||||
use App\Enums\CacheTag;
|
||||
use App\Enums\DocumentationVersionStatus;
|
||||
use App\Models\DocumentationVersion;
|
||||
use App\Models\User;
|
||||
@@ -141,7 +140,7 @@ final class DocumentationVersionService extends Service
|
||||
$dataVersion = $this->getDataVersion($data);
|
||||
return $this->documentationVersionCommandHandler->handleStore($project, $dataVersion);
|
||||
});
|
||||
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||
$this->clearCacheCommandHandler->all();
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@@ -178,7 +177,7 @@ final class DocumentationVersionService extends Service
|
||||
$dataVersion = $this->getDataVersion($data);
|
||||
return $this->documentationVersionCommandHandler->handleUpdate($version, $dataVersion);
|
||||
});
|
||||
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||
$this->clearCacheCommandHandler->all();
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@@ -207,7 +206,7 @@ final class DocumentationVersionService extends Service
|
||||
DB::transaction(function () use ($version) {
|
||||
$this->documentationVersionCommandHandler->handleDestroy($version);
|
||||
});
|
||||
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||
$this->clearCacheCommandHandler->all();
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
|
@@ -0,0 +1,67 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Admin\Project;
|
||||
|
||||
use App\Dto\Service\Admin\Project\ServiceTranslate\Update;
|
||||
use App\Models\User;
|
||||
use App\Repositories\ProjectRepository;
|
||||
use App\ServiceResults\ServiceResultArray;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\Services\ProjectTranslationService\ModelSyncCommand as TranslationServiceModelSyncCommand;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class ServiceTranslateService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ProjectRepository $projectRepository,
|
||||
private readonly TranslationServiceModelSyncCommand $translationServiceModelSyncCommand,
|
||||
) { }
|
||||
|
||||
public function view(int $projectId, User $user): ServiceResultError | ServiceResultArray
|
||||
{
|
||||
$project = $this->projectRepository->getProjectById($projectId);
|
||||
if (\is_null($project)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if (
|
||||
config('translation_service.enable', false) === false
|
||||
|| $user->cannot('settingUpAutomaticTranslation', $project)
|
||||
) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
return $this->result([
|
||||
'project' => $project,
|
||||
'languages' => $project->languages()->with(['serviceTranslate'])->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(int $projectId, Update $data, User $user): ServiceResultError | ServiceResultSuccess
|
||||
{
|
||||
$project = $this->projectRepository->getProjectById($projectId);
|
||||
if (\is_null($project)) {
|
||||
return $this->errNotFound(__('Not Found'));
|
||||
}
|
||||
|
||||
if (
|
||||
config('translation_service.enable', false) === false
|
||||
|| $user->cannot('settingUpAutomaticTranslation', $project)
|
||||
) {
|
||||
return $this->errFobidden(__('Access is denied'));
|
||||
}
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($data, $project) {
|
||||
$this->translationServiceModelSyncCommand->execute($project, $data->getTranslations());
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
}
|
||||
|
||||
return $this->ok(__('The settings were saved successfully'));
|
||||
}
|
||||
}
|
@@ -3,8 +3,9 @@
|
||||
namespace App\Services\Admin\Project;
|
||||
|
||||
use App\Dto\Service\Admin\Project\Translation\Translations;
|
||||
use App\Dto\Service\Admin\Project\Translation\Translation;
|
||||
use App\Dto\Service\Admin\Project\Translation\Update;
|
||||
use App\Enums\CacheTag;
|
||||
use App\Jobs\Translate\ProcessTranslationText;
|
||||
use App\Models\ProjectTranslation;
|
||||
use App\Models\User;
|
||||
use App\Repositories\ProjectRepository;
|
||||
@@ -60,6 +61,7 @@ final class TranslationService extends Service
|
||||
'language' => $language,
|
||||
'projectTranslations' => $this->projectTranslationRepository->getProjectTranslations($projectId, $languageId)->all()->pluck('text', 'code')->toArray(),
|
||||
'translations' => Translations::getTranslationCodes(),
|
||||
'serviceTranslationEnable' => config('translation_service.enable', false),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -80,7 +82,10 @@ final class TranslationService extends Service
|
||||
DB::transaction(function () use ($data, $project, $language) {
|
||||
$this->translationModelSyncCommand->execute($project, $language, $data->getTranslations());
|
||||
});
|
||||
$this->clearCacheCommandHandler->byTag(CacheTag::ProjectTranslation);
|
||||
$this->clearCacheCommandHandler->all();
|
||||
if (\config('translation_service.enable', false)) {
|
||||
$this->translateContent($projectId, $languageId, $data);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@@ -88,4 +93,19 @@ final class TranslationService extends Service
|
||||
|
||||
return $this->ok(__('Translations successfully updated'));
|
||||
}
|
||||
|
||||
private function translateContent(int $projectId, int $languageId, Update $data): void
|
||||
{
|
||||
if (! $data->isTranslateAutomatically()) {
|
||||
return;
|
||||
}
|
||||
$translateExceptLanguages = [$languageId];
|
||||
$translateTextCode = [];
|
||||
foreach ($data->getTranslations()->getTranslations() as $translation) {
|
||||
/** @var Translation $translation */
|
||||
$translateTextCode[] = $translation->getCode();
|
||||
}
|
||||
|
||||
ProcessTranslationText::dispatch($projectId, $languageId, $translateTextCode, $translateExceptLanguages);
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ namespace App\Services\Admin;
|
||||
use App\Dto\Builder\Project as ProjectBuilderDto;
|
||||
use App\Dto\QuerySettingsDto;
|
||||
use App\Dto\Service\Admin\Project\StoreUpdate;
|
||||
use App\Enums\CacheTag;
|
||||
use App\Enums\Morph;
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectLanguage;
|
||||
@@ -50,6 +49,7 @@ final class ProjectService extends Service
|
||||
|
||||
return $this->result([
|
||||
'projects' => $projects,
|
||||
'serviceTranslationEnable' => config('translation_service.enable', false),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ final class ProjectService extends Service
|
||||
|
||||
return $project;
|
||||
});
|
||||
$this->clearCacheCommandHandler->byTag(CacheTag::Project);
|
||||
$this->clearCacheCommandHandler->all();
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@@ -176,7 +176,7 @@ final class ProjectService extends Service
|
||||
|
||||
return $project;
|
||||
});
|
||||
$this->clearCacheCommandHandler->byTag(CacheTag::Project);
|
||||
$this->clearCacheCommandHandler->all();
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@@ -201,7 +201,7 @@ final class ProjectService extends Service
|
||||
DB::transaction(function () use ($project) {
|
||||
$this->projectCommandHandler->handleDestroy($project);
|
||||
});
|
||||
$this->clearCacheCommandHandler->byTag(CacheTag::Project);
|
||||
$this->clearCacheCommandHandler->all();
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
|
@@ -2,12 +2,12 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\CacheTag;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
final readonly class ClearCacheCommandHandler
|
||||
{
|
||||
public function byTag(CacheTag $tag)
|
||||
public function all(): void
|
||||
{
|
||||
$tag->getCache()->flush();
|
||||
Cache::flush();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,46 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Commands;
|
||||
|
||||
use App\Models\Storage as StorageModel;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Storage as StorageSupport;
|
||||
|
||||
final class DeleteOldFilesService extends Service
|
||||
{
|
||||
/**
|
||||
* $temporaryBeforeDate = date of deletion of temporary files
|
||||
* $deletedBeforeDate = date of deletion files that were marked as deleted
|
||||
*
|
||||
* @param Carbon $temporaryBeforeDate
|
||||
* @param Carbon $deletedBeforeDate
|
||||
* @return ServiceResultError|ServiceResultSuccess
|
||||
*/
|
||||
public function fromStorage(Carbon $temporaryBeforeDate, Carbon $deletedBeforeDate): ServiceResultError|ServiceResultSuccess
|
||||
{
|
||||
$disk = config('storage.disk');
|
||||
|
||||
StorageModel::withTrashed()
|
||||
->where(function (Builder $query) use($temporaryBeforeDate) {
|
||||
$query->whereNull('morph_id')->where('updated_at', '<', $temporaryBeforeDate);
|
||||
})->orWhere(function (Builder $query) use($deletedBeforeDate) {
|
||||
$query->whereNotNull('deleted_at')->where('deleted_at', '<', $deletedBeforeDate);
|
||||
})->chunkById(100, function ($items) use($disk) {
|
||||
$deleteIds = [];
|
||||
foreach ($items as $item) {
|
||||
$deleteIds[] = $item->id;
|
||||
|
||||
if (StorageSupport::disk($disk)->exists($item->file)) {
|
||||
StorageSupport::disk($disk)->delete($item->file);
|
||||
}
|
||||
}
|
||||
StorageModel::withTrashed()->whereIn('id', $deleteIds)->forceDelete();
|
||||
});
|
||||
|
||||
return $this->ok(__('Old Files deleted.'));
|
||||
}
|
||||
}
|
@@ -4,18 +4,33 @@ namespace App\Services\DocumentationContent;
|
||||
|
||||
use App\Dto\Service\Admin\Project\DocumentationContent\Content;
|
||||
use App\Dto\Service\Admin\Project\DocumentationContent\Contents;
|
||||
use App\Dto\Service\DocumentationContent\StorageDto;
|
||||
use App\Exceptions\Services\DocumentationContent\ContentSaveException;
|
||||
use App\Exceptions\Services\DocumentationContent\StorageCommandException;
|
||||
use App\Models\Documentation;
|
||||
use App\Models\DocumentationContent;
|
||||
use App\Models\Project;
|
||||
|
||||
final readonly class ModelSyncCommand
|
||||
{
|
||||
public function __construct(
|
||||
private StorageCommand $storageCommand,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* @throws StorageCommandException
|
||||
* @throws ContentSaveException
|
||||
*/
|
||||
public function execute(Project $project, Documentation $documentation, Contents $contents): void
|
||||
{
|
||||
$storageDto = new StorageDto();
|
||||
|
||||
$languages = $project->languages;
|
||||
$documentationContents = $documentation->contents;
|
||||
|
||||
$newContents = [];
|
||||
$contentsStorageCreated = [];
|
||||
$contentLanguages = [];
|
||||
foreach ($contents->getContents() as $content) {
|
||||
/** @var Content $content */
|
||||
$language = $languages->firstWhere('id', $content->getLanguageId());
|
||||
@@ -26,15 +41,29 @@ final readonly class ModelSyncCommand
|
||||
$model = $documentationContents->firstWhere('language_id', $language->id);
|
||||
$data = $this->getData($content);
|
||||
if (\is_null($model)) {
|
||||
$contentsStorageCreated[$content->getLanguageId()] = $content->getStorages();
|
||||
$newContents[] = array_merge(['language_id' => $content->getLanguageId()], $data);
|
||||
$contentLanguages[] = $content->getLanguageId();
|
||||
continue;
|
||||
}
|
||||
$storageDto->add($model, $content->getStorages());
|
||||
$model->update($data);
|
||||
}
|
||||
|
||||
if (!empty($newContents)) {
|
||||
$documentation->contents()->createMany($newContents);
|
||||
$contents = $documentation->contents()->whereIn('language_id', $contentLanguages)->get();
|
||||
foreach ($contents as $content) {
|
||||
/** @var DocumentationContent $content */
|
||||
if (!isset($contentsStorageCreated[$content->language_id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$storageDto->add($content, $contentsStorageCreated[$content->language_id]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->storageCommand->execute($storageDto);
|
||||
}
|
||||
|
||||
private function getData(Content $content): array
|
||||
|
@@ -0,0 +1,31 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\DocumentationContent;
|
||||
|
||||
use App\Dto\Service\DocumentationContent\StorageDto;
|
||||
use App\Enums\Morph;
|
||||
use App\Exceptions\Services\DocumentationContent\StorageCommandException;
|
||||
use App\Services\Storage\StorageService;
|
||||
|
||||
final readonly class StorageCommand
|
||||
{
|
||||
public function __construct(
|
||||
private StorageService $storageService,
|
||||
) { }
|
||||
|
||||
|
||||
/**
|
||||
* @throws StorageCommandException
|
||||
*/
|
||||
public function execute(StorageDto $storageDto): void
|
||||
{
|
||||
foreach ($storageDto->getStorages() as $storage) {
|
||||
$storages = $this->storageService->getStoragesAndValidate($storage['storages'], Morph::DocumentationContent, $storage['documentationContent']->id);
|
||||
if (!$storages->isSuccess()) {
|
||||
throw new StorageCommandException($storages, 'Error when adding a file to storage: ' . $storages->getMessage());
|
||||
}
|
||||
|
||||
$this->storageService->saveAndDelete($storage['documentationContent'], $storages);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\ProjectTranslation;
|
||||
|
||||
use App\Models\ProjectLanguage;
|
||||
|
||||
final readonly class TranslationText
|
||||
{
|
||||
public function __construct(
|
||||
private ProjectLanguage $language,
|
||||
private array $translations,
|
||||
) { }
|
||||
|
||||
public static function init(ProjectLanguage $language, array $translations): self
|
||||
{
|
||||
return new self($language, $translations);
|
||||
}
|
||||
|
||||
public function translate(string $code): string
|
||||
{
|
||||
return $this->translations[$code] ?? __($code, [], $this->language->system_lang?->getLocale());
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\ProjectTranslation;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectLanguage;
|
||||
use App\Repositories\ProjectTranslationRepository;
|
||||
|
||||
final readonly class TranslationTextCommand
|
||||
{
|
||||
public function __construct(
|
||||
private ProjectTranslationRepository $translationRepository,
|
||||
) { }
|
||||
|
||||
public function execute(Project $project, ProjectLanguage $language, array $codes): TranslationText
|
||||
{
|
||||
$translations = $this->translationRepository
|
||||
->getProjectTranslationByCodes($project->id, $language->id, $codes)
|
||||
->all()->pluck('text', 'code');
|
||||
|
||||
return TranslationText::init($language, $translations->toArray());
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\ProjectTranslationService;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Dto\Service\Admin\Project\ServiceTranslate\Translations;
|
||||
use App\Models\ProjectTranslationService;
|
||||
|
||||
final readonly class ModelSyncCommand
|
||||
{
|
||||
public function execute(Project $project, Translations $translations): void
|
||||
{
|
||||
$insert = [];
|
||||
$delete = [];
|
||||
|
||||
$languages = $project->languages()->with(['serviceTranslate'])->get();
|
||||
foreach ($translations->getTranslations() as $translation) {
|
||||
/* @var \App\Dto\Service\Admin\Project\ServiceTranslate\Translation $translation */
|
||||
$language = $languages->firstWhere('id', '=', $translation->getLanguageId());
|
||||
if ($language === null) {
|
||||
continue;
|
||||
}
|
||||
$serviceTranslate = $language->serviceTranslate;
|
||||
if ($serviceTranslate === null) {
|
||||
if ($translation->getCode() !== null) {
|
||||
$insert[] = [
|
||||
'language_id' => $translation->getLanguageId(),
|
||||
'code' => $translation->getCode(),
|
||||
'source_language_id' => $translation->getSourceLanguageId(),
|
||||
];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($translation->getCode() === null) {
|
||||
$delete[] = [$serviceTranslate->id];
|
||||
continue;
|
||||
}
|
||||
|
||||
$serviceTranslate->update([
|
||||
'code' => $translation->getCode(),
|
||||
'source_language_id' => $translation->getSourceLanguageId(),
|
||||
]);
|
||||
}
|
||||
|
||||
if (!empty($insert)) {
|
||||
ProjectTranslationService::query()->insert($insert);
|
||||
}
|
||||
|
||||
if (!empty($delete)) {
|
||||
ProjectTranslationService::query()->whereIn('id', $delete)->delete();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\ProjectTranslationService;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
final readonly class NoTranslateAttributeHandler
|
||||
{
|
||||
public function handleAddAttribute(string $text): string
|
||||
{
|
||||
return Str::replace('<code', '<code translate="no"', $text);
|
||||
}
|
||||
|
||||
public function handleRemoveAttribute(string $text): string
|
||||
{
|
||||
return Str::replace('<code translate="no"', '<code', $text);
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\ProjectTranslationServiceHash;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\Hashes;
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\HashStatusWaiting;
|
||||
use App\Enums\ProjectTranslationServiceHashes\Status;
|
||||
use App\Repositories\ProjectTranslationServiceHashRepository;
|
||||
|
||||
final readonly class CompletionChecker
|
||||
{
|
||||
public function __construct(
|
||||
private ProjectTranslationServiceHashRepository $hashRepository,
|
||||
private ProjectTranslationServiceHashCommandHandler $commandHandler,
|
||||
) { }
|
||||
|
||||
public function execute(Hashes $hashesDto): HashStatusWaiting
|
||||
{
|
||||
$hashes = $this->hashRepository->getHashesByIds($hashesDto->getIds())->all();
|
||||
$hashStatusWaiting = new HashStatusWaiting();
|
||||
$hashSuccessIds = [];
|
||||
foreach ($hashes as $hash) {
|
||||
$dataHash = $hashesDto->getHash($hash->id);
|
||||
if ($dataHash === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isWaiting = false;
|
||||
if ($hash->status === Status::Waiting && $hash->hash === $dataHash['hash'] && $hash->field === $dataHash['field']) {
|
||||
$isWaiting = true;
|
||||
$hashSuccessIds[] = $hash->id;
|
||||
}
|
||||
$hashStatusWaiting->add($hash->field, $isWaiting);
|
||||
}
|
||||
|
||||
$this->commandHandler->handleSetStatusById($hashSuccessIds, Status::Success);
|
||||
|
||||
return $hashStatusWaiting;
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\ProjectTranslationServiceHash;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\Fields;
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\TranslateFields;
|
||||
use App\Enums\Morph;
|
||||
use App\Enums\ProjectTranslationServiceHashes\Status;
|
||||
use App\Models\ProjectTranslationServiceHash;
|
||||
use App\Repositories\ProjectTranslationServiceHashRepository;
|
||||
|
||||
final readonly class HashTrackerCommand
|
||||
{
|
||||
public function __construct(
|
||||
private ProjectTranslationServiceHashRepository $hashRepository,
|
||||
private ProjectTranslationServiceHashCommandHandler $commandHandler,
|
||||
) { }
|
||||
|
||||
public function execute(Morph $morph, int $morphId, array $languages, Fields $fields): TranslateFields
|
||||
{
|
||||
$hashes = $this->hashRepository->getHashes($morph, $morphId, $languages, $fields->getNames())->all();
|
||||
|
||||
$translateFields = new TranslateFields();
|
||||
foreach ($fields->getFields() as $fieldName => $fieldValue) {
|
||||
$textHash = $this->generateHashFromText((string) $fieldValue);
|
||||
foreach ($languages as $language) {
|
||||
$modelHash = $hashes->where('language_id', $language)->firstWhere('field', $fieldName);
|
||||
if ($modelHash === null) {
|
||||
$modelHash = $this->commandHandler->handleStore([
|
||||
'language_id' => $language,
|
||||
'morph_type' => $morph,
|
||||
'morph_id' => $morphId,
|
||||
'field' => $fieldName,
|
||||
'hash' => $textHash,
|
||||
'status' => Status::Waiting,
|
||||
]);
|
||||
$translateFields->add($language, $fieldName, $textHash, $modelHash->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($modelHash->hash === $textHash && $this->isSuccess($modelHash)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translateFields->add($language, $fieldName, $textHash, $modelHash->id);
|
||||
$this->commandHandler->handleUpdate($modelHash, [
|
||||
'hash' => $textHash,
|
||||
'status' => Status::Waiting,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $translateFields;
|
||||
}
|
||||
|
||||
private function generateHashFromText(string $text): string
|
||||
{
|
||||
return \hash('sha256', $text);
|
||||
}
|
||||
|
||||
private function isSuccess(ProjectTranslationServiceHash $modelHash): bool
|
||||
{
|
||||
if ($modelHash->status === Status::Success) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ($modelHash->updated_at >= now()->subMinutes(10));
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\ProjectTranslationServiceHash;
|
||||
|
||||
use App\Enums\ProjectTranslationServiceHashes\Status;
|
||||
use App\Models\ProjectTranslationServiceHash;
|
||||
|
||||
final readonly class ProjectTranslationServiceHashCommandHandler
|
||||
{
|
||||
public function handleStore(array $data): ProjectTranslationServiceHash
|
||||
{
|
||||
return ProjectTranslationServiceHash::create($data);
|
||||
}
|
||||
|
||||
public function handleUpdate(ProjectTranslationServiceHash $hash, array $data): ProjectTranslationServiceHash
|
||||
{
|
||||
$hash->update($data);
|
||||
$hash->touch();
|
||||
return $hash;
|
||||
}
|
||||
|
||||
public function handleSetStatusById(array $ids, Status $status): void
|
||||
{
|
||||
ProjectTranslationServiceHash::query()->whereIn('id', $ids)->update(['status' => $status->value]);
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\ProjectTranslationServiceTextHash;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceTextHash\Hashes;
|
||||
use App\Dto\Service\ProjectTranslationServiceTextHash\HashStatusWaiting;
|
||||
use App\Enums\ProjectTranslationServiceHashes\Status;
|
||||
use App\Repositories\ProjectTranslationServiceTextHashRepository;
|
||||
|
||||
final readonly class CompletionChecker
|
||||
{
|
||||
public function __construct(
|
||||
private ProjectTranslationServiceTextHashRepository $hashRepository,
|
||||
private ProjectTranslationServiceTextHashCommandHandler $commandHandler,
|
||||
) { }
|
||||
|
||||
public function execute(Hashes $hashesDto): HashStatusWaiting
|
||||
{
|
||||
$hashes = $this->hashRepository->getHashesByIds($hashesDto->getIds())->all();
|
||||
$hashStatusWaiting = new HashStatusWaiting();
|
||||
$hashSuccessIds = [];
|
||||
foreach ($hashes as $hash) {
|
||||
$dataHash = $hashesDto->getHash($hash->id);
|
||||
if ($dataHash === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isWaiting = false;
|
||||
if ($hash->status === Status::Waiting && $hash->hash === $dataHash['hash'] && $hash->code === $dataHash['code']) {
|
||||
$isWaiting = true;
|
||||
$hashSuccessIds[] = $hash->id;
|
||||
}
|
||||
$hashStatusWaiting->add($hash->code, $isWaiting);
|
||||
}
|
||||
|
||||
$this->commandHandler->handleSetStatusById($hashSuccessIds, Status::Success);
|
||||
|
||||
return $hashStatusWaiting;
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\ProjectTranslationServiceTextHash;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceTextHash\Codes;
|
||||
use App\Dto\Service\ProjectTranslationServiceTextHash\TranslateCodes;
|
||||
use App\Enums\ProjectTranslationServiceHashes\Status;
|
||||
use App\Models\ProjectTranslationServiceTextHash;
|
||||
use App\Repositories\ProjectTranslationServiceTextHashRepository;
|
||||
|
||||
final readonly class HashTrackerCommand
|
||||
{
|
||||
public function __construct(
|
||||
private ProjectTranslationServiceTextHashRepository $hashRepository,
|
||||
private ProjectTranslationServiceTextHashCommandHandler $commandHandler,
|
||||
) { }
|
||||
|
||||
public function execute(array $languages, Codes $codes): TranslateCodes
|
||||
{
|
||||
$hashes = $this->hashRepository->getHashes($codes->getCodeNames(), $languages)->all();
|
||||
|
||||
$translateCodes = new TranslateCodes();
|
||||
foreach ($codes->getCodes() as $code => $value) {
|
||||
$textHash = $this->generateHashFromText((string) $value);
|
||||
foreach ($languages as $language) {
|
||||
$modelHash = $hashes->where('language_id', $language)->firstWhere('code', $code);
|
||||
if ($modelHash === null) {
|
||||
$modelHash = $this->commandHandler->handleStore([
|
||||
'language_id' => $language,
|
||||
'code' => $code,
|
||||
'hash' => $textHash,
|
||||
'status' => Status::Waiting,
|
||||
]);
|
||||
$translateCodes->add($language, $code, $textHash, $modelHash->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($modelHash->hash === $textHash && $this->isSuccess($modelHash)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translateCodes->add($language, $code, $textHash, $modelHash->id);
|
||||
$this->commandHandler->handleUpdate($modelHash, [
|
||||
'hash' => $textHash,
|
||||
'status' => Status::Waiting,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $translateCodes;
|
||||
}
|
||||
|
||||
private function generateHashFromText(string $text): string
|
||||
{
|
||||
return \hash('sha256', $text);
|
||||
}
|
||||
|
||||
private function isSuccess(ProjectTranslationServiceTextHash $modelHash): bool
|
||||
{
|
||||
if ($modelHash->status === Status::Success) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ($modelHash->updated_at >= now()->subMinutes(10));
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\ProjectTranslationServiceTextHash;
|
||||
|
||||
use App\Enums\ProjectTranslationServiceHashes\Status;
|
||||
use App\Models\ProjectTranslationServiceTextHash;
|
||||
|
||||
final readonly class ProjectTranslationServiceTextHashCommandHandler
|
||||
{
|
||||
public function handleStore(array $data): ProjectTranslationServiceTextHash
|
||||
{
|
||||
return ProjectTranslationServiceTextHash::create($data);
|
||||
}
|
||||
|
||||
public function handleUpdate(ProjectTranslationServiceTextHash $hash, array $data): ProjectTranslationServiceTextHash
|
||||
{
|
||||
$hash->update($data);
|
||||
$hash->touch();
|
||||
return $hash;
|
||||
}
|
||||
|
||||
public function handleSetStatusById(array $ids, Status $status): void
|
||||
{
|
||||
ProjectTranslationServiceTextHash::query()->whereIn('id', $ids)->update(['status' => $status->value]);
|
||||
}
|
||||
}
|
@@ -3,7 +3,6 @@
|
||||
namespace App\Services\Site;
|
||||
|
||||
use App\Dto\Service\Site\Documentation;
|
||||
use App\Enums\CacheTag;
|
||||
use App\Enums\DocumentationVersionStatus;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Models\Documentation as ModelDocumentation;
|
||||
@@ -22,6 +21,7 @@ use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
final class DocumentationService extends Service
|
||||
{
|
||||
@@ -37,8 +37,7 @@ final class DocumentationService extends Service
|
||||
if ($user?->cannot('viewAny', DocumentationVersion::class)) {
|
||||
$isPublic = 1;
|
||||
}
|
||||
$version = CacheTag::DocumantationVersion->getCache()
|
||||
->remember(self::class . $project->id . '_' . $isPublic ?? 0, $seconds, function () use ($project, $isPublic) {
|
||||
$version = Cache::remember(self::class . $project->id . '_' . $isPublic ?? 0, $seconds, function () use ($project, $isPublic) {
|
||||
$versions = $project->documentationVersions()
|
||||
->when($isPublic, function (Builder $query) {
|
||||
$query->where('is_public', 1);
|
||||
|
@@ -5,32 +5,44 @@ namespace App\Services\Site;
|
||||
use App\Dto\Service\Site\Feedback\Send;
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
use App\Notifications\ReviewAdded;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\Services\ProjectFeedback\ProjectFeedbackCommandHandler;
|
||||
use App\Services\Service;
|
||||
use App\Services\WebsiteTranslations;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
final class FeedbackService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ProjectFeedbackCommandHandler $feedbackCommandHandler,
|
||||
private readonly bool $isNotifications = false,
|
||||
private readonly ?string $mailNotifications = null,
|
||||
) { }
|
||||
|
||||
public function send(Send $send, Project $project, WebsiteTranslations $websiteTranslations, ?User $user = null): ServiceResultError | ServiceResultSuccess
|
||||
{
|
||||
try {
|
||||
DB::transaction(function () use ($send, $project, $user) {
|
||||
$feedback = DB::transaction(function () use ($send, $project, $user) {
|
||||
$data = $this->getDataFeedback($send);
|
||||
$data['user_id'] = $user?->id;
|
||||
$this->feedbackCommandHandler->handleStore($project, $data);
|
||||
return $this->feedbackCommandHandler->handleStore($project, $data);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService($websiteTranslations->translate('Server Error'));
|
||||
}
|
||||
|
||||
try {
|
||||
if ($this->isNotifications) {
|
||||
Notification::route('mail', $this->mailNotifications)->notify(new ReviewAdded($feedback));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
}
|
||||
|
||||
return $this->ok($websiteTranslations->translate('site.Message sent successfully'));
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,89 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Translate\Completed\Project;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\Hashes;
|
||||
use App\Exceptions\Services\Translate\CompletedException;
|
||||
use App\Jobs\Translate\ProcessProjectDocumentationCategoryContent;
|
||||
use App\Models\DocumentationCategory;
|
||||
use App\Models\DocumentationCategoryContent;
|
||||
use App\Repositories\DocumentationCategoryRepository;
|
||||
use App\Services\ProjectTranslationService\NoTranslateAttributeHandler;
|
||||
use App\Services\ProjectTranslationServiceHash\CompletionChecker;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use KorElf\TranslateLaravel\Contracts\TranslationCompletedListener;
|
||||
|
||||
final readonly class DocumentationCategoryContentService implements TranslationCompletedListener
|
||||
{
|
||||
public function __construct(
|
||||
private DocumentationCategoryRepository $categoryRepository,
|
||||
private NoTranslateAttributeHandler $noTranslateAttributeHandler,
|
||||
private CompletionChecker $completionChecker,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* @throws CompletedException
|
||||
*/
|
||||
public function onTranslationCompleted(array $translatedText, array $data = []): void
|
||||
{
|
||||
if (
|
||||
!isset($data['categoryId'])
|
||||
|| !isset($data['languageId'])
|
||||
|| !isset($data['hashes'])
|
||||
) {
|
||||
throw new CompletedException('Required data is missing: categoryId, languageId, or hashes.');
|
||||
}
|
||||
|
||||
$category = $this->categoryRepository->getCategoryById((int) $data['categoryId']);
|
||||
if ($category === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$categoryContent = DB::transaction(function () use ($data, $category, $translatedText) {
|
||||
$values = [];
|
||||
$hashes = $this->completionChecker->execute(
|
||||
$this->getHashes($data['hashes']),
|
||||
);
|
||||
|
||||
foreach ($translatedText as $translatedTextKey => $translatedTextValue) {
|
||||
if ($hashes->isStatusWaiting($translatedTextKey) !== true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translatedTextValue = $this->noTranslateAttributeHandler->handleRemoveAttribute($translatedTextValue);
|
||||
$values[$translatedTextKey] = $translatedTextValue;
|
||||
}
|
||||
if (\count($values) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$categoryContent = $category->contents()->where('language_id', $data['languageId'])->first();
|
||||
if ($categoryContent !== null) {
|
||||
$categoryContent->update($values);
|
||||
return $categoryContent;
|
||||
}
|
||||
|
||||
$values['language_id'] = $data['languageId'];
|
||||
return $category->contents()->create($values);
|
||||
});
|
||||
if (\config('translation_service.enable', false) && $categoryContent !== null) {
|
||||
$this->translateContent($category, $categoryContent, $data);
|
||||
}
|
||||
}
|
||||
|
||||
private function translateContent(DocumentationCategory $category, DocumentationCategoryContent $categoryContent, array $data): void
|
||||
{
|
||||
$translateExceptLanguages = $data['exceptLanguages'] ?? [];
|
||||
$translateExceptLanguages[] = $data['languageId'];
|
||||
ProcessProjectDocumentationCategoryContent::dispatch($category->id, [$categoryContent->id], $translateExceptLanguages);
|
||||
}
|
||||
|
||||
private function getHashes(array $hashes): Hashes
|
||||
{
|
||||
$hashesDto = new Hashes();
|
||||
foreach ($hashes as $field => $hash) {
|
||||
$hashesDto->add((int) $hash['hashId'], $field, $hash['hash']);
|
||||
}
|
||||
return $hashesDto;
|
||||
}
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Translate\Completed\Project;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\Hashes;
|
||||
use App\Exceptions\Services\Translate\CompletedException;
|
||||
use App\Jobs\Translate\ProcessProjectDocumentationContent;
|
||||
use App\Models\Documentation;
|
||||
use App\Models\DocumentationContent;
|
||||
use App\Repositories\DocumentationRepository;
|
||||
use App\Services\ProjectTranslationService\NoTranslateAttributeHandler;
|
||||
use App\Services\ProjectTranslationServiceHash\CompletionChecker;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use KorElf\TranslateLaravel\Contracts\TranslationCompletedListener;
|
||||
|
||||
final readonly class DocumentationContentService implements TranslationCompletedListener
|
||||
{
|
||||
public function __construct(
|
||||
private DocumentationRepository $documentationRepository,
|
||||
private NoTranslateAttributeHandler $noTranslateAttributeHandler,
|
||||
private CompletionChecker $completionChecker,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* @throws CompletedException
|
||||
*/
|
||||
public function onTranslationCompleted(array $translatedText, array $data = []): void
|
||||
{
|
||||
if (
|
||||
!isset($data['projectDocumentId'])
|
||||
|| !isset($data['languageId'])
|
||||
|| !isset($data['hashes'])
|
||||
) {
|
||||
throw new CompletedException('Required data is missing: projectDocumentId, languageId, or hashes.');
|
||||
}
|
||||
|
||||
$documentation = $this->documentationRepository->getDocumentationById((int) $data['projectDocumentId']);
|
||||
if ($documentation === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$documentationContent = DB::transaction(function () use ($data, $documentation, $translatedText) {
|
||||
$values = [];
|
||||
$hashes = $this->completionChecker->execute(
|
||||
$this->getHashes($data['hashes']),
|
||||
);
|
||||
|
||||
foreach ($translatedText as $translatedTextKey => $translatedTextValue) {
|
||||
if ($hashes->isStatusWaiting($translatedTextKey) !== true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translatedTextValue = $this->noTranslateAttributeHandler->handleRemoveAttribute($translatedTextValue);
|
||||
$values[$translatedTextKey] = $translatedTextValue;
|
||||
}
|
||||
if (\count($values) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$documentationContent = $documentation->contents()->where('language_id', $data['languageId'])->first();
|
||||
if ($documentationContent !== null) {
|
||||
$documentationContent->update($values);
|
||||
return $documentationContent;
|
||||
}
|
||||
|
||||
$values['language_id'] = $data['languageId'];
|
||||
return $documentation->contents()->create($values);
|
||||
});
|
||||
if (\config('translation_service.enable', false) && $documentationContent !== null) {
|
||||
$this->translateContent($documentation, $documentationContent, $data);
|
||||
}
|
||||
}
|
||||
|
||||
private function translateContent(Documentation $documentation, DocumentationContent $documentationContent, array $data): void
|
||||
{
|
||||
$translateExceptLanguages = $data['exceptLanguages'] ?? [];
|
||||
$translateExceptLanguages[] = $data['languageId'];
|
||||
ProcessProjectDocumentationContent::dispatch($documentation->id, [$documentationContent->id], $translateExceptLanguages);
|
||||
}
|
||||
|
||||
private function getHashes(array $hashes): Hashes
|
||||
{
|
||||
$hashesDto = new Hashes();
|
||||
foreach ($hashes as $field => $hash) {
|
||||
$hashesDto->add((int) $hash['hashId'], $field, $hash['hash']);
|
||||
}
|
||||
return $hashesDto;
|
||||
}
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Translate\Completed\Project;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\Hashes;
|
||||
use App\Exceptions\Services\Translate\CompletedException;
|
||||
use App\Jobs\Translate\ProcessProjectContent;
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectContent;
|
||||
use App\Repositories\ProjectRepository;
|
||||
use App\Services\ProjectTranslationService\NoTranslateAttributeHandler;
|
||||
use App\Services\ProjectTranslationServiceHash\CompletionChecker;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use KorElf\TranslateLaravel\Contracts\TranslationCompletedListener;
|
||||
|
||||
final readonly class ProjectContentService implements TranslationCompletedListener
|
||||
{
|
||||
public function __construct(
|
||||
private ProjectRepository $projectRepository,
|
||||
private NoTranslateAttributeHandler $noTranslateAttributeHandler,
|
||||
private CompletionChecker $completionChecker,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* @throws CompletedException
|
||||
*/
|
||||
public function onTranslationCompleted(array $translatedText, array $data = []): void
|
||||
{
|
||||
if (
|
||||
!isset($data['projectId'])
|
||||
|| !isset($data['languageId'])
|
||||
|| !isset($data['hashes'])
|
||||
) {
|
||||
throw new CompletedException('Required data is missing: projectId, languageId, or hashes.');
|
||||
}
|
||||
|
||||
$project = $this->projectRepository->getProjectById((int) $data['projectId']);
|
||||
if ($project === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$projectContent = DB::transaction(function () use ($data, $project, $translatedText) {
|
||||
$values = [];
|
||||
$hashes = $this->completionChecker->execute(
|
||||
$this->getHashes($data['hashes']),
|
||||
);
|
||||
|
||||
foreach ($translatedText as $translatedTextKey => $translatedTextValue) {
|
||||
if ($hashes->isStatusWaiting($translatedTextKey) !== true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translatedTextValue = $this->noTranslateAttributeHandler->handleRemoveAttribute($translatedTextValue);
|
||||
$values[$translatedTextKey] = $translatedTextValue;
|
||||
}
|
||||
if (\count($values) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$projectContent = $project->contents()->where('language_id', $data['languageId'])->first();
|
||||
if ($projectContent !== null) {
|
||||
$projectContent->update($values);
|
||||
return $projectContent;
|
||||
}
|
||||
|
||||
$values['language_id'] = $data['languageId'];
|
||||
return $project->contents()->create($values);
|
||||
});
|
||||
if (\config('translation_service.enable', false) && $projectContent !== null) {
|
||||
$this->translateContent($project, $projectContent, $data);
|
||||
}
|
||||
}
|
||||
|
||||
private function translateContent(Project $project, ProjectContent $projectContent, array $data): void
|
||||
{
|
||||
$translateExceptLanguages = $data['exceptLanguages'] ?? [];
|
||||
$translateExceptLanguages[] = $data['languageId'];
|
||||
ProcessProjectContent::dispatch($project->id, [$projectContent->id], $translateExceptLanguages);
|
||||
}
|
||||
|
||||
private function getHashes(array $hashes): Hashes
|
||||
{
|
||||
$hashesDto = new Hashes();
|
||||
foreach ($hashes as $field => $hash) {
|
||||
$hashesDto->add((int) $hash['hashId'], $field, $hash['hash']);
|
||||
}
|
||||
return $hashesDto;
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Translate\Completed\Project;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceTextHash\Hashes;
|
||||
use App\Exceptions\Services\Translate\CompletedException;
|
||||
use App\Jobs\Translate\ProcessTranslationText;
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectTranslation;
|
||||
use App\Repositories\ProjectRepository;
|
||||
use App\Repositories\ProjectTranslationRepository;
|
||||
use App\Services\ProjectTranslationServiceTextHash\CompletionChecker;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use KorElf\TranslateLaravel\Contracts\TranslationCompletedListener;
|
||||
|
||||
final readonly class TranslationTextService implements TranslationCompletedListener
|
||||
{
|
||||
public function __construct(
|
||||
private ProjectRepository $projectRepository,
|
||||
private CompletionChecker $completionChecker,
|
||||
private ProjectTranslationRepository $projectTranslationRepository,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* @throws CompletedException
|
||||
*/
|
||||
public function onTranslationCompleted(array $translatedText, array $data = []): void
|
||||
{
|
||||
if (
|
||||
!isset($data['projectId'])
|
||||
|| !isset($data['languageId'])
|
||||
|| !isset($data['hashes'])
|
||||
) {
|
||||
throw new CompletedException('Required data is missing: projectId, languageId, or hashes.');
|
||||
}
|
||||
|
||||
$project = $this->projectRepository->getProjectById((int) $data['projectId']);
|
||||
if ($project === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$translations = $this->projectTranslationRepository->getProjectTranslationByCodes($project->id, $data['languageId'], \array_keys($translatedText))->all();
|
||||
|
||||
$translateTextCode = DB::transaction(function () use ($data, $translatedText, $translations) {
|
||||
$translateTextCode = [];
|
||||
$hashes = $this->completionChecker->execute(
|
||||
$this->getHashes($data['hashes']),
|
||||
);
|
||||
|
||||
foreach ($translatedText as $translatedTextKey => $translatedTextValue) {
|
||||
if ($hashes->isStatusWaiting($translatedTextKey) !== true) {
|
||||
continue;
|
||||
}
|
||||
$translateTextCode[] = $translatedTextKey;
|
||||
$translation = $translations->firstWhere('code', $translatedTextKey);
|
||||
if ($translation !== null) {
|
||||
$translation->update([
|
||||
'text' => $translatedTextValue,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
ProjectTranslation::create([
|
||||
'project_id' => (int) $data['projectId'],
|
||||
'language_id' => (int) $data['languageId'],
|
||||
'code' => $translatedTextKey,
|
||||
'text' => $translatedTextValue,
|
||||
]);
|
||||
}
|
||||
|
||||
return $translateTextCode;
|
||||
});
|
||||
|
||||
if (\config('translation_service.enable', false) && !empty($translateTextCode)) {
|
||||
$this->translateContent($project, $translateTextCode, $data);
|
||||
}
|
||||
}
|
||||
|
||||
private function translateContent(Project $project, array $translateTextCode, array $data): void
|
||||
{
|
||||
$translateExceptLanguages = $data['exceptLanguages'] ?? [];
|
||||
$translateExceptLanguages[] = $data['languageId'];
|
||||
ProcessTranslationText::dispatch($project->id, $data['languageId'], $translateTextCode, $translateExceptLanguages);
|
||||
}
|
||||
|
||||
private function getHashes(array $hashes): Hashes
|
||||
{
|
||||
$hashesDto = new Hashes();
|
||||
foreach ($hashes as $code => $hash) {
|
||||
$hashesDto->add((int) $hash['hashId'], $code, $hash['hash']);
|
||||
}
|
||||
return $hashesDto;
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Translate\Project;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\Fields;
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\TranslateFields;
|
||||
use App\Enums\Morph;
|
||||
use App\Repositories\DocumentationCategoryRepository;
|
||||
use App\Repositories\ProjectTranslationServiceRepository;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\Services\ProjectTranslationService\NoTranslateAttributeHandler;
|
||||
use App\Services\ProjectTranslationServiceHash\HashTrackerCommand;
|
||||
use App\Services\Translate\Completed\Project\DocumentationCategoryContentService as DocumentationCategoryContentServiceCompleted;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use KorElf\TranslateLaravel\DTO\AfterTranslateDto;
|
||||
use KorElf\TranslateLaravel\Facades\Translate;
|
||||
|
||||
final class DocumentationCategoryContentService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DocumentationCategoryRepository $documentationCategoryRepository,
|
||||
private readonly ProjectTranslationServiceRepository $projectTranslationServiceRepository,
|
||||
private readonly HashTrackerCommand $hashTrackerCommand,
|
||||
private readonly NoTranslateAttributeHandler $noTranslateAttributeHandler,
|
||||
) {}
|
||||
|
||||
public function translate(int $categoryId, array $contentIds, array $exceptLanguages): ServiceResultSuccess | ServiceResultError
|
||||
{
|
||||
try {
|
||||
$category = $this->documentationCategoryRepository->getCategoryById($categoryId);
|
||||
if ($category === null) {
|
||||
return $this->errNotFound(__('Category not found'));
|
||||
}
|
||||
|
||||
$contents = $category->contents()
|
||||
->whereIn('id', $contentIds)
|
||||
->cursor();
|
||||
|
||||
$sourceLanguagesCode = [];
|
||||
foreach ($contents as $content) {
|
||||
if (!isset($sourceLanguagesCode[$content->language_id])) {
|
||||
$sourceLanguagesCode[$content->language_id] = $this->projectTranslationServiceRepository->getLanguageCodeByLanguageId($content->language_id) ?? 'null';
|
||||
}
|
||||
$translateIntoLanguage = $this->projectTranslationServiceRepository
|
||||
->getLanguagesBySourceLanguage($content->language_id, $exceptLanguages)
|
||||
->all()->pluck('code', 'language_id')->toArray();
|
||||
|
||||
if (empty($translateIntoLanguage)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fields = (new Fields())
|
||||
->add('title', $content->title ?? '');
|
||||
$translations = DB::transaction(function () use ($content, $translateIntoLanguage, $fields) {
|
||||
return $this->hashTrackerCommand->execute(Morph::DocumentationCategoryContent, $content->id, \array_keys($translateIntoLanguage), $fields);
|
||||
});
|
||||
/** @var TranslateFields $translations */
|
||||
unset($fields);
|
||||
|
||||
foreach ($translations->getFields() as $languageId => $fields) {
|
||||
$params = new \KorElf\TranslateLaravel\DTO\RunTranslateDto();
|
||||
$sourceLanguageCode = null;
|
||||
if (isset($sourceLanguagesCode[$content->language_id]) && $sourceLanguagesCode[$content->language_id] !== 'null') {
|
||||
$sourceLanguageCode = $sourceLanguagesCode[$content->language_id];
|
||||
}
|
||||
foreach ($fields as $field) {
|
||||
$text = $this->noTranslateAttributeHandler->handleAddAttribute((string) $content->{$field} ?? '');
|
||||
$params->addParamHtml($field, $text, $translateIntoLanguage[$languageId], $sourceLanguageCode);
|
||||
}
|
||||
|
||||
$afterTranslateDto = new AfterTranslateDto(DocumentationCategoryContentServiceCompleted::class, [
|
||||
'categoryId' => $categoryId,
|
||||
'languageId' => $languageId,
|
||||
'hashes' => $translations->getHashesByLanguage($languageId),
|
||||
'exceptLanguages' => $exceptLanguages,
|
||||
]);
|
||||
Translate::runJob($params, $afterTranslateDto);
|
||||
unset($params, $afterTranslateDto);
|
||||
}
|
||||
|
||||
unset($translations, $translateIntoLanguage);
|
||||
}
|
||||
unset($contents, $documentation);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
\report($e);
|
||||
return $this->errService($e->getMessage());
|
||||
}
|
||||
|
||||
return $this->ok();
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Translate\Project;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\Fields;
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\TranslateFields;
|
||||
use App\Enums\Morph;
|
||||
use App\Repositories\DocumentationRepository;
|
||||
use App\Repositories\ProjectTranslationServiceRepository;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\Services\ProjectTranslationService\NoTranslateAttributeHandler;
|
||||
use App\Services\ProjectTranslationServiceHash\HashTrackerCommand;
|
||||
use App\Services\Service;
|
||||
use App\Services\Translate\Completed\Project\DocumentationContentService as DocumentationContentServiceCompleted;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use KorElf\TranslateLaravel\DTO\AfterTranslateDto;
|
||||
use KorElf\TranslateLaravel\Facades\Translate;
|
||||
|
||||
final class DocumentationContentService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DocumentationRepository $documentationRepository,
|
||||
private readonly ProjectTranslationServiceRepository $projectTranslationServiceRepository,
|
||||
private readonly HashTrackerCommand $hashTrackerCommand,
|
||||
private readonly NoTranslateAttributeHandler $noTranslateAttributeHandler,
|
||||
) {}
|
||||
|
||||
public function translate(int $projectDocumentId, array $contentIds, array $exceptLanguages): ServiceResultSuccess | ServiceResultError
|
||||
{
|
||||
try {
|
||||
$documentation = $this->documentationRepository->getDocumentationById($projectDocumentId);
|
||||
if ($documentation === null) {
|
||||
return $this->errNotFound(__('Documentation not found'));
|
||||
}
|
||||
|
||||
$contents = $documentation->contents()
|
||||
->whereIn('id', $contentIds)
|
||||
->cursor();
|
||||
|
||||
$sourceLanguagesCode = [];
|
||||
foreach ($contents as $content) {
|
||||
if (!isset($sourceLanguagesCode[$content->language_id])) {
|
||||
$sourceLanguagesCode[$content->language_id] = $this->projectTranslationServiceRepository->getLanguageCodeByLanguageId($content->language_id) ?? 'null';
|
||||
}
|
||||
$translateIntoLanguage = $this->projectTranslationServiceRepository
|
||||
->getLanguagesBySourceLanguage($content->language_id, $exceptLanguages)
|
||||
->all()->pluck('code', 'language_id')->toArray();
|
||||
|
||||
if (empty($translateIntoLanguage)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fields = (new Fields())
|
||||
->add('title', $content->title ?? '')
|
||||
->add('content', $content->content ?? '');
|
||||
$translations = DB::transaction(function () use ($content, $translateIntoLanguage, $fields) {
|
||||
return $this->hashTrackerCommand->execute(Morph::DocumentationContent, $content->id, \array_keys($translateIntoLanguage), $fields);
|
||||
});
|
||||
/** @var TranslateFields $translations */
|
||||
unset($fields);
|
||||
|
||||
foreach ($translations->getFields() as $languageId => $fields) {
|
||||
$params = new \KorElf\TranslateLaravel\DTO\RunTranslateDto();
|
||||
$sourceLanguageCode = null;
|
||||
if (isset($sourceLanguagesCode[$content->language_id]) && $sourceLanguagesCode[$content->language_id] !== 'null') {
|
||||
$sourceLanguageCode = $sourceLanguagesCode[$content->language_id];
|
||||
}
|
||||
foreach ($fields as $field) {
|
||||
$text = $this->noTranslateAttributeHandler->handleAddAttribute((string) $content->{$field} ?? '');
|
||||
$params->addParamHtml($field, $text, $translateIntoLanguage[$languageId], $sourceLanguageCode);
|
||||
}
|
||||
|
||||
$afterTranslateDto = new AfterTranslateDto(DocumentationContentServiceCompleted::class, [
|
||||
'projectDocumentId' => $projectDocumentId,
|
||||
'languageId' => $languageId,
|
||||
'hashes' => $translations->getHashesByLanguage($languageId),
|
||||
'exceptLanguages' => $exceptLanguages,
|
||||
]);
|
||||
Translate::runJob($params, $afterTranslateDto);
|
||||
unset($params, $afterTranslateDto);
|
||||
}
|
||||
|
||||
unset($translations, $translateIntoLanguage);
|
||||
}
|
||||
unset($contents, $documentation);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
\report($e);
|
||||
return $this->errService($e->getMessage());
|
||||
}
|
||||
|
||||
return $this->ok();
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Translate\Project;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\Fields;
|
||||
use App\Dto\Service\ProjectTranslationServiceHash\TranslateFields;
|
||||
use App\Enums\Morph;
|
||||
use App\Repositories\ProjectRepository;
|
||||
use App\Repositories\ProjectTranslationServiceRepository;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\Services\ProjectTranslationService\NoTranslateAttributeHandler;
|
||||
use App\Services\ProjectTranslationServiceHash\HashTrackerCommand;
|
||||
use App\Services\Service;
|
||||
use App\Services\Translate\Completed\Project\ProjectContentService as ProjectContentServiceCompleted;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use KorElf\TranslateLaravel\DTO\AfterTranslateDto;
|
||||
use KorElf\TranslateLaravel\Facades\Translate;
|
||||
|
||||
final class ProjectContentService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ProjectRepository $projectRepository,
|
||||
private readonly ProjectTranslationServiceRepository $projectTranslationServiceRepository,
|
||||
private readonly HashTrackerCommand $hashTrackerCommand,
|
||||
private readonly NoTranslateAttributeHandler $noTranslateAttributeHandler,
|
||||
) {}
|
||||
|
||||
public function translate(int $projectId, array $contentIds, array $exceptLanguages): ServiceResultSuccess | ServiceResultError
|
||||
{
|
||||
try {
|
||||
$project = $this->projectRepository->getProjectById($projectId);
|
||||
if ($project === null) {
|
||||
return $this->errNotFound(__('Project not found'));
|
||||
}
|
||||
|
||||
$contents = $project->contents()
|
||||
->whereIn('id', $contentIds)
|
||||
->cursor();
|
||||
|
||||
$sourceLanguagesCode = [];
|
||||
foreach ($contents as $content) {
|
||||
if (!isset($sourceLanguagesCode[$content->language_id])) {
|
||||
$sourceLanguagesCode[$content->language_id] = $this->projectTranslationServiceRepository->getLanguageCodeByLanguageId($content->language_id) ?? 'null';
|
||||
}
|
||||
$translateIntoLanguage = $this->projectTranslationServiceRepository
|
||||
->getLanguagesBySourceLanguage($content->language_id, $exceptLanguages)
|
||||
->all()->pluck('code', 'language_id')->toArray();
|
||||
|
||||
if (empty($translateIntoLanguage)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fields = (new Fields())
|
||||
->add('title', $content->title ?? '')
|
||||
->add('description', $content->description ?? '');
|
||||
$translations = DB::transaction(function () use ($content, $translateIntoLanguage, $fields) {
|
||||
return $this->hashTrackerCommand->execute(Morph::ProjectContent, $content->id, \array_keys($translateIntoLanguage), $fields);
|
||||
});
|
||||
/** @var TranslateFields $translations */
|
||||
unset($fields);
|
||||
|
||||
foreach ($translations->getFields() as $languageId => $fields) {
|
||||
$params = new \KorElf\TranslateLaravel\DTO\RunTranslateDto();
|
||||
$sourceLanguageCode = null;
|
||||
if (isset($sourceLanguagesCode[$content->language_id]) && $sourceLanguagesCode[$content->language_id] !== 'null') {
|
||||
$sourceLanguageCode = $sourceLanguagesCode[$content->language_id];
|
||||
}
|
||||
foreach ($fields as $field) {
|
||||
$text = $this->noTranslateAttributeHandler->handleAddAttribute((string) $content->{$field} ?? '');
|
||||
$params->addParamHtml($field, $text, $translateIntoLanguage[$languageId], $sourceLanguageCode);
|
||||
}
|
||||
|
||||
$afterTranslateDto = new AfterTranslateDto(ProjectContentServiceCompleted::class, [
|
||||
'projectId' => $projectId,
|
||||
'languageId' => $languageId,
|
||||
'hashes' => $translations->getHashesByLanguage($languageId),
|
||||
'exceptLanguages' => $exceptLanguages,
|
||||
]);
|
||||
Translate::runJob($params, $afterTranslateDto);
|
||||
unset($params, $afterTranslateDto);
|
||||
}
|
||||
|
||||
unset($translations, $translateIntoLanguage);
|
||||
}
|
||||
unset($contents, $documentation);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
\report($e);
|
||||
return $this->errService($e->getMessage());
|
||||
}
|
||||
|
||||
return $this->ok();
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Translate\Project;
|
||||
|
||||
use App\Dto\Service\ProjectTranslationServiceTextHash\Codes;
|
||||
use App\Dto\Service\ProjectTranslationServiceTextHash\TranslateCodes;
|
||||
use App\Repositories\ProjectRepository;
|
||||
use App\Repositories\ProjectTranslationServiceRepository;
|
||||
use App\ServiceResults\ServiceResultError;
|
||||
use App\ServiceResults\ServiceResultSuccess;
|
||||
use App\Services\ProjectTranslation\TranslationText;
|
||||
use App\Services\ProjectTranslation\TranslationTextCommand;
|
||||
use App\Services\ProjectTranslationServiceTextHash\HashTrackerCommand;
|
||||
use App\Services\Translate\Completed\Project\TranslationTextService as TranslationTextServiceCompleted;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use KorElf\TranslateLaravel\DTO\AfterTranslateDto;
|
||||
use KorElf\TranslateLaravel\Facades\Translate;
|
||||
|
||||
final class TranslationTextService extends Service
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ProjectRepository $projectRepository,
|
||||
private readonly TranslationTextCommand $translationTextCommand,
|
||||
private readonly ProjectTranslationServiceRepository $projectTranslationServiceRepository,
|
||||
private readonly HashTrackerCommand $hashTrackerCommand,
|
||||
) { }
|
||||
|
||||
public function translate(int $projectId, int $languageId, array $translateTextCode, array $exceptLanguages): ServiceResultSuccess | ServiceResultError
|
||||
{
|
||||
try {
|
||||
$project = $this->projectRepository->getProjectById($projectId);
|
||||
if ($project === null) {
|
||||
return $this->errNotFound(__('Project not found'));
|
||||
}
|
||||
$language = $project->languages()->firstWhere('id', $languageId);
|
||||
if ($language === null) {
|
||||
return $this->errNotFound(__('Language not found'));
|
||||
}
|
||||
|
||||
$translateIntoLanguage = $this->projectTranslationServiceRepository
|
||||
->getLanguagesBySourceLanguage($languageId, $exceptLanguages)
|
||||
->all()->pluck('code', 'language_id')->toArray();
|
||||
|
||||
if (empty($translateIntoLanguage)) {
|
||||
return $this->ok();
|
||||
}
|
||||
|
||||
$translationText = $this->translationTextCommand->execute($project, $language, $translateTextCode);
|
||||
$codes = $this->getCodes($translationText, $translateTextCode);
|
||||
$translations = DB::transaction(function () use ($translateIntoLanguage, $codes) {
|
||||
return $this->hashTrackerCommand->execute(\array_keys($translateIntoLanguage), $codes);
|
||||
});
|
||||
/** @var TranslateCodes $translations */
|
||||
unset($codes);
|
||||
|
||||
$sourceLanguageCode = $this->projectTranslationServiceRepository->getLanguageCodeByLanguageId($language->id);
|
||||
foreach ($translations->getCodes() as $currentLanguageId => $codes) {
|
||||
$params = new \KorElf\TranslateLaravel\DTO\RunTranslateDto();
|
||||
foreach ($codes as $code) {
|
||||
$text = $translationText->translate($code);
|
||||
$params->addParamHtml($code, $text, $translateIntoLanguage[$currentLanguageId], $sourceLanguageCode);
|
||||
}
|
||||
|
||||
$afterTranslateDto = new AfterTranslateDto(TranslationTextServiceCompleted::class, [
|
||||
'projectId' => $projectId,
|
||||
'languageId' => $currentLanguageId,
|
||||
'hashes' => $translations->getHashesByLanguage($currentLanguageId),
|
||||
'exceptLanguages' => $exceptLanguages,
|
||||
]);
|
||||
Translate::runJob($params, $afterTranslateDto);
|
||||
unset($params, $afterTranslateDto);
|
||||
}
|
||||
|
||||
unset($translations, $translateIntoLanguage, $project, $language, $translationText, $sourceLanguageCode);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
\report($e);
|
||||
return $this->errService($e->getMessage());
|
||||
}
|
||||
|
||||
return $this->ok();
|
||||
}
|
||||
|
||||
private function getCodes(TranslationText $translationText, array $translateTextCode): Codes
|
||||
{
|
||||
$codes = new Codes();
|
||||
foreach ($translateTextCode as $code) {
|
||||
$codes->add($code, $translationText->translate($code));
|
||||
}
|
||||
|
||||
return $codes;
|
||||
}
|
||||
}
|
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace app\View\Components\Site;
|
||||
|
||||
use App\Enums\CacheTag;
|
||||
use App\Models\Project;
|
||||
use App\Services\WebsiteTranslations;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
@@ -24,7 +24,7 @@ final class ChooseLanguage extends Component
|
||||
}
|
||||
|
||||
$seconds = 3600 * 12;
|
||||
$languages = CacheTag::Project->getCache()->remember(self::class . $this->project->id, $seconds, function () {
|
||||
$languages = Cache::remember(self::class . $this->project->id, $seconds, function () {
|
||||
return $this->project->languages;
|
||||
});
|
||||
|
||||
|
@@ -2,13 +2,12 @@
|
||||
|
||||
namespace App\View\Components\Site;
|
||||
|
||||
use App\Enums\CacheTag;
|
||||
use App\Models\DocumentationVersion;
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
use App\Services\WebsiteTranslations;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
@@ -29,8 +28,7 @@ final class ChooseVersion extends Component
|
||||
}
|
||||
|
||||
$seconds = 3600 * 12;
|
||||
$versions = CacheTag::DocumantationVersion->getCache()
|
||||
->remember(self::class . $this->project->id . '-' . $isPublic ?? 0, $seconds, function () use ($isPublic) {
|
||||
$versions = Cache::remember(self::class . $this->project->id . '-' . $isPublic ?? 0, $seconds, function () use ($isPublic) {
|
||||
return $this->project->documentationVersions()
|
||||
->when($isPublic, function (Builder $query) {
|
||||
$query->where('is_public', 1);
|
||||
|
@@ -2,15 +2,17 @@
|
||||
|
||||
namespace App\View\Components\Volt\Forms;
|
||||
|
||||
use App\Dto\View\Volt\Form\WysiwygStorageUpload;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class TextareaWysiwyg extends Form
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $title,
|
||||
private readonly string $name,
|
||||
private readonly ?string $value = '',
|
||||
private readonly string $title,
|
||||
private readonly string $name,
|
||||
private readonly ?string $value = '',
|
||||
private readonly ?WysiwygStorageUpload $storageUpload = null,
|
||||
) { }
|
||||
|
||||
protected function getName(): string
|
||||
@@ -28,6 +30,11 @@ final class TextareaWysiwyg extends Form
|
||||
return (string) old($this->getRequestName(), $this->value);
|
||||
}
|
||||
|
||||
public function getStorageUpload(): ?WysiwygStorageUpload
|
||||
{
|
||||
return $this->storageUpload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -37,10 +44,11 @@ final class TextareaWysiwyg extends Form
|
||||
|
||||
return view('components.volt.forms.textarea-wysiwyg', [
|
||||
'tinymceLicenseKey' => $tinymceLicenseKey,
|
||||
'title' => $this->getTitle(),
|
||||
'name' => $this->getName(),
|
||||
'requestName' => $this->getRequestName(),
|
||||
'value' => $this->getValue(),
|
||||
'title' => $this->getTitle(),
|
||||
'name' => $this->getName(),
|
||||
'requestName' => $this->getRequestName(),
|
||||
'value' => $this->getValue(),
|
||||
'storageUpload' => $this->getStorageUpload(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
@@ -19,8 +20,15 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
},
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
//
|
||||
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions) {
|
||||
//
|
||||
$exceptions->respond(function (Response $response) {
|
||||
if ($response->getStatusCode() === 419) {
|
||||
return back()
|
||||
->withInput()
|
||||
->withErrors(['csrf' => __('http-statuses.csrfError')]);
|
||||
}
|
||||
return $response;
|
||||
});
|
||||
})->create();
|
||||
|
@@ -8,6 +8,7 @@
|
||||
"php": "^8.3",
|
||||
"intervention/image-laravel": "^1.2",
|
||||
"kor-elf/captcha-rule-for-laravel": "^1.0",
|
||||
"kor-elf/translate-laravel": "1.3.0",
|
||||
"laravel/framework": "^11.0",
|
||||
"laravel/tinker": "^2.9",
|
||||
"staudenmeir/laravel-adjacency-list": "^1.0"
|
||||
|
3088
app/application/composer.lock
generated
3088
app/application/composer.lock
generated
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user