Compare commits
41 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
417ce35fc8 | |||
da5201dd9a | |||
2910509218 | |||
74f7b88bce | |||
6998424b78 | |||
05f566c115 | |||
c5fc106761 | |||
d7dc80cca4 | |||
6d3891979c |
@ -7,6 +7,7 @@
|
||||
**/storage/framework/sessions/*
|
||||
**/storage/framework/views/*
|
||||
**/storage/framework/testing/*
|
||||
**/storage/translation_service/*
|
||||
**/storage/logs/*
|
||||
**/vendor/
|
||||
**/node_modules/
|
||||
|
@ -1,17 +1,34 @@
|
||||
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
|
||||
#UNIT_SOURCE="\"172.16.0.0/12\""
|
||||
|
||||
APP_DEFAULT_LOCALE=ru
|
||||
APP_FAKER_LOCALE=ru_RU
|
||||
@ -59,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
|
||||
|
1
app/application/.gitignore
vendored
1
app/application/.gitignore
vendored
@ -8,6 +8,7 @@
|
||||
.env
|
||||
.env.backup
|
||||
.env.production
|
||||
.env.testing
|
||||
.phpunit.result.cache
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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\Rule;
|
||||
|
||||
final class RoleSyncPermissionsCommandHandlerException extends \Exception
|
||||
{
|
||||
|
||||
}
|
@ -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),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -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'],
|
||||
'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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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,10 @@
|
||||
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 +62,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),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -81,6 +84,9 @@ final class TranslationService extends Service
|
||||
$this->translationModelSyncCommand->execute($project, $language, $data->getTranslations());
|
||||
});
|
||||
$this->clearCacheCommandHandler->byTag(CacheTag::ProjectTranslation);
|
||||
if (\config('translation_service.enable', false)) {
|
||||
$this->translateContent($projectId, $languageId, $data);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
report($e);
|
||||
return $this->errService(__('Server Error'));
|
||||
@ -88,4 +94,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);
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ final class ProjectService extends Service
|
||||
|
||||
return $this->result([
|
||||
'projects' => $projects,
|
||||
'serviceTranslationEnable' => config('translation_service.enable', false),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use App\Enums\CacheTag;
|
||||
|
||||
final readonly class ClearCacheCommandHandler
|
||||
{
|
||||
public function byTag(CacheTag $tag)
|
||||
public function byTag(CacheTag $tag): void
|
||||
{
|
||||
$tag->getCache()->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,7 @@
|
||||
namespace App\Services\Role;
|
||||
|
||||
use App\Enums\Permission;
|
||||
use App\Exceptions\Rule\RoleSyncPermissionsCommandHandlerException;
|
||||
use App\Exceptions\Services\Rule\RoleSyncPermissionsCommandHandlerException;
|
||||
use App\Models\Role;
|
||||
use App\Models\RolePermission;
|
||||
|
||||
|
@ -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,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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -28,6 +28,15 @@ return [
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Сaptcha
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enables or disables captcha.
|
||||
*/
|
||||
'captcha' => (bool) env('APP_CAPTCHA', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|
9
app/application/config/feedback.php
Normal file
9
app/application/config/feedback.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/**
|
||||
* Enable new review alerts.
|
||||
*/
|
||||
'mail_notifications' => (bool) env('FEEDBACK_MAIL_NOTIFICATIONS', false),
|
||||
'mail_to' => env('FEEDBACK_MAIL_TO', null),
|
||||
];
|
11
app/application/config/translation_service.php
Normal file
11
app/application/config/translation_service.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Translate service
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enables or disables translate service.
|
||||
*/
|
||||
'enable' => (bool) env('TRANSLATION_SERVICE_ENABLE', false),
|
||||
];
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('storage', function (Blueprint $table) {
|
||||
$table->dropIndex(['morph_id']);
|
||||
$table->index(['morph_id', 'updated_at']);
|
||||
$table->index(['deleted_at']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('storage', function (Blueprint $table) {
|
||||
$table->dropIndex(['morph_id', 'updated_at']);
|
||||
$table->dropIndex(['deleted_at']);
|
||||
$table->index(['morph_id']);
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('project_translation_service', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('language_id')->unique();
|
||||
$table->foreign('language_id')->references('id')->on('project_languages');
|
||||
$table->unsignedBigInteger('source_language_id')->nullable()->index();
|
||||
$table->foreign('source_language_id')->references('id')->on('project_languages');
|
||||
$table->string('code', 50);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('project_translation_service');
|
||||
}
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('project_translation_service_hashes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('language_id')->index();
|
||||
$table->foreign('language_id')->references('id')->on('project_languages');
|
||||
$table->unsignedInteger('morph_type');
|
||||
$table->unsignedBigInteger('morph_id');
|
||||
$table->string('field', 255);
|
||||
$table->unsignedInteger('status')->index()->default(0);
|
||||
$table->char('hash', 64);
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['morph_type', 'morph_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('project_translation_service_hashes');
|
||||
}
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('project_translation_service_text_hashes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('code');
|
||||
$table->unsignedBigInteger('language_id')->index();
|
||||
$table->foreign('language_id')->references('id')->on('project_languages');
|
||||
$table->unsignedInteger('status')->index()->default(0);
|
||||
$table->char('hash', 64);
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['language_id', 'code'], 'unique_language_code');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('project_translation_service_text_hashes');
|
||||
}
|
||||
};
|
@ -256,6 +256,7 @@
|
||||
"The link has been deleted": "The link has been deleted",
|
||||
"Language not found": "Language not found",
|
||||
"Project not found": "Project not found",
|
||||
"Documentation not found": "Documentation not found",
|
||||
"Translations successfully updated": "Translations successfully updated",
|
||||
"allowed characters:": "allowed characters:",
|
||||
"Documentation version created successfully": "Documentation version created successfully",
|
||||
@ -267,5 +268,9 @@
|
||||
"Documentation successfully removed": "Documentation successfully removed",
|
||||
"Category successfully created": "Category successfully created",
|
||||
"Category updated successfully": "Category updated successfully",
|
||||
"Category successfully deleted": "Category successfully deleted"
|
||||
"Category successfully deleted": "Category successfully deleted",
|
||||
"Old Files deleted": "Old Files deleted",
|
||||
"Automatic translation": "Automatic translation",
|
||||
"The settings were saved successfully": "The settings were saved successfully",
|
||||
"Category not found": "Category not found"
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user