Compare commits
49 Commits
21026cdf52
...
0.4.0
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
|
|||
f986e0396d
|
|||
9f6924935d
|
|||
e74456ee2f
|
|||
42701a24db
|
|||
78e6a1e396
|
|||
b6e1c50486
|
|||
3bc508b8b0
|
|||
4b6cf902ff |
15
app/.dockerignore
Normal file
15
app/.dockerignore
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
**/.env
|
||||||
|
**/*.env
|
||||||
|
**/.env.example
|
||||||
|
**/storage/app/*
|
||||||
|
**/storage/debugbar
|
||||||
|
**/storage/framework/cache/*
|
||||||
|
**/storage/framework/sessions/*
|
||||||
|
**/storage/framework/views/*
|
||||||
|
**/storage/framework/testing/*
|
||||||
|
**/storage/translation_service/*
|
||||||
|
**/storage/logs/*
|
||||||
|
**/vendor/
|
||||||
|
**/node_modules/
|
||||||
|
**/public/build/
|
||||||
|
**/public/storage
|
@@ -1,17 +1,34 @@
|
|||||||
APP_NAME=Laravel
|
APP_NAME="My Projects Website"
|
||||||
APP_ENV=local
|
APP_ENV=local
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_TIMEZONE=UTC
|
APP_TIMEZONE=UTC
|
||||||
APP_URL=http://localhost
|
APP_URL=http://localhost
|
||||||
|
|
||||||
|
APP_CAPTCHA=false
|
||||||
CAPTCHA_API_DOMAIN=http://your-domain-captcha-or-IP:8081
|
CAPTCHA_API_DOMAIN=http://your-domain-captcha-or-IP:8081
|
||||||
CAPTCHA_PRIVATE_TOKEN=
|
CAPTCHA_PRIVATE_TOKEN=
|
||||||
CAPTCHA_STATIC_PATH=http://your-domain-captcha-or-IP:8081/captcha
|
CAPTCHA_STATIC_PATH=http://your-domain-captcha-or-IP:8081/captcha
|
||||||
CAPTCHA_PUBLIC_TOKEN=
|
CAPTCHA_PUBLIC_TOKEN=
|
||||||
|
|
||||||
|
# Don't forget to configure MAIL_MAILER to send mail.
|
||||||
|
FEEDBACK_MAIL_NOTIFICATIONS=false
|
||||||
|
FEEDBACK_MAIL_TO=
|
||||||
|
|
||||||
|
TRANSLATION_SERVICE_ENABLE=false
|
||||||
|
# yandex or log
|
||||||
|
TRANSLATE_SERVICE=log
|
||||||
|
TRANSLATE_YANDEX_FOLDER_ID=
|
||||||
|
TRANSLATE_YANDEX_AUTHORIZED_KEY_PATH=/storage/translation_service/authorized_key.json
|
||||||
|
TRANSLATE_YANDEX_LIMIT_MAX_REQUEST=20
|
||||||
|
TRANSLATE_YANDEX_LIMIT_RATE_SECONDS=1
|
||||||
|
TRANSLATE_YANDEX_LIMIT_MAX_SYMBOLS=9000
|
||||||
|
|
||||||
|
TRANSLATE_LOG_LIMIT_MAX_REQUEST=20
|
||||||
|
TRANSLATE_LOG_LIMIT_RATE_SECONDS=1
|
||||||
|
TRANSLATE_LOG_LIMIT_MAX_SYMBOLS=9000
|
||||||
|
|
||||||
APP_FORCE_HTTPS=false
|
APP_FORCE_HTTPS=false
|
||||||
#UNIT_SOURCE="\"172.16.0.0/12\""
|
|
||||||
|
|
||||||
APP_DEFAULT_LOCALE=ru
|
APP_DEFAULT_LOCALE=ru
|
||||||
APP_FAKER_LOCALE=ru_RU
|
APP_FAKER_LOCALE=ru_RU
|
||||||
@@ -59,7 +76,7 @@ REDIS_HOST=app-redis
|
|||||||
REDIS_PASSWORD=null
|
REDIS_PASSWORD=null
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
|
||||||
MAIL_MAILER=log
|
MAIL_MAILER=smtp
|
||||||
MAIL_HOST=127.0.0.1
|
MAIL_HOST=127.0.0.1
|
||||||
MAIL_PORT=2525
|
MAIL_PORT=2525
|
||||||
MAIL_USERNAME=null
|
MAIL_USERNAME=null
|
||||||
|
1
app/application/.gitignore
vendored
1
app/application/.gitignore
vendored
@@ -8,6 +8,7 @@
|
|||||||
.env
|
.env
|
||||||
.env.backup
|
.env.backup
|
||||||
.env.production
|
.env.production
|
||||||
|
.env.testing
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
Homestead.json
|
Homestead.json
|
||||||
Homestead.yaml
|
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());
|
||||||
|
}
|
||||||
|
}
|
@@ -2,14 +2,22 @@
|
|||||||
|
|
||||||
namespace App\Dto\Builder;
|
namespace App\Dto\Builder;
|
||||||
|
|
||||||
|
use App\Dto\Builder\DocumentationCategory\Category;
|
||||||
|
|
||||||
final readonly class Documentation
|
final readonly class Documentation
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ?bool $isPublic = null,
|
private ?bool $isPublic = null,
|
||||||
|
private ?Category $categoryId = null,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function isPublic(): ?bool
|
public function isPublic(): ?bool
|
||||||
{
|
{
|
||||||
return $this->isPublic;
|
return $this->isPublic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCategoryId(): ?Category
|
||||||
|
{
|
||||||
|
return $this->categoryId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,14 +2,22 @@
|
|||||||
|
|
||||||
namespace App\Dto\Builder;
|
namespace App\Dto\Builder;
|
||||||
|
|
||||||
|
use App\Dto\Builder\DocumentationCategory\Category;
|
||||||
|
|
||||||
final readonly class DocumentationCategory
|
final readonly class DocumentationCategory
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ?bool $isPublic = null,
|
private ?bool $isPublic = null,
|
||||||
|
private ?Category $parentId = null,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function isPublic(): ?bool
|
public function isPublic(): ?bool
|
||||||
{
|
{
|
||||||
return $this->isPublic;
|
return $this->isPublic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getParentId(): ?Category
|
||||||
|
{
|
||||||
|
return $this->parentId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,20 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Builder\DocumentationCategory;
|
||||||
|
|
||||||
|
final readonly class Category
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private ?int $categoryId,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getCategoryId(): ?int
|
||||||
|
{
|
||||||
|
return $this->categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isCategoryNull(): bool
|
||||||
|
{
|
||||||
|
return $this->getCategoryId() === null;
|
||||||
|
}
|
||||||
|
}
|
@@ -3,12 +3,15 @@
|
|||||||
namespace App\Dto\Service\Admin\Project\About;
|
namespace App\Dto\Service\Admin\Project\About;
|
||||||
|
|
||||||
use App\Dto\Service\Dto;
|
use App\Dto\Service\Dto;
|
||||||
|
use App\Dto\Service\Storage\Storages;
|
||||||
|
|
||||||
final readonly class StoreUpdate extends Dto
|
final readonly class StoreUpdate extends Dto
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private string $title,
|
private string $title,
|
||||||
private string $description,
|
private string $description,
|
||||||
|
private Storages $storages,
|
||||||
|
private bool $isTranslateAutomatically = false,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function getTitle(): string
|
public function getTitle(): string
|
||||||
@@ -20,4 +23,14 @@ final readonly class StoreUpdate extends Dto
|
|||||||
{
|
{
|
||||||
return $this->description;
|
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(
|
public function __construct(
|
||||||
private int $languageId,
|
private int $languageId,
|
||||||
private string $title,
|
private string $title,
|
||||||
|
private bool $isTranslateAutomatically = false,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function getLanguageId(): int
|
public function getLanguageId(): int
|
||||||
@@ -18,4 +19,9 @@ final readonly class Content
|
|||||||
{
|
{
|
||||||
return $this->title;
|
return $this->title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isTranslateAutomatically(): bool
|
||||||
|
{
|
||||||
|
return $this->isTranslateAutomatically;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,12 +2,16 @@
|
|||||||
|
|
||||||
namespace App\Dto\Service\Admin\Project\DocumentationContent;
|
namespace App\Dto\Service\Admin\Project\DocumentationContent;
|
||||||
|
|
||||||
|
use App\Dto\Service\Storage\Storages;
|
||||||
|
|
||||||
final readonly class Content
|
final readonly class Content
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private int $languageId,
|
private int $languageId,
|
||||||
private string $title,
|
private string $title,
|
||||||
private string $content,
|
private string $content,
|
||||||
|
private Storages $storages,
|
||||||
|
private bool $isTranslateAutomatically = false,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function getLanguageId(): int
|
public function getLanguageId(): int
|
||||||
@@ -24,4 +28,14 @@ final readonly class Content
|
|||||||
{
|
{
|
||||||
return $this->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;
|
||||||
|
}
|
||||||
|
}
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Dto\Service\Admin\Project\Translation;
|
namespace App\Dto\Service\Admin\Project\Translation;
|
||||||
|
|
||||||
|
use App\Enums\DocumentationVersionStatus;
|
||||||
use App\Exceptions\Dto\Admin\Project\Transaction\TranslationsException;
|
use App\Exceptions\Dto\Admin\Project\Transaction\TranslationsException;
|
||||||
|
|
||||||
final class Translations
|
final class Translations
|
||||||
@@ -23,7 +24,7 @@ final class Translations
|
|||||||
|
|
||||||
public static function getTranslationCodes(): array
|
public static function getTranslationCodes(): array
|
||||||
{
|
{
|
||||||
return [
|
$translations = [
|
||||||
'site.Menu',
|
'site.Menu',
|
||||||
'site.Powered by service',
|
'site.Powered by service',
|
||||||
'site.About project',
|
'site.About project',
|
||||||
@@ -38,6 +39,17 @@ final class Translations
|
|||||||
'site.attributes.message',
|
'site.attributes.message',
|
||||||
'site.Message sent successfully',
|
'site.Message sent successfully',
|
||||||
'Server Error',
|
'Server Error',
|
||||||
|
'site.Documentation',
|
||||||
|
'site.Documentation not created',
|
||||||
|
'site.Choose version',
|
||||||
|
'site.alert-status-not-supported',
|
||||||
|
'site.alert-status-future',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
foreach (DocumentationVersionStatus::cases() as $status) {
|
||||||
|
$translations[] = $status->getCodeForTranslation();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $translations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,10 +8,16 @@ final readonly class Update extends Dto
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private Translations $translations,
|
private Translations $translations,
|
||||||
|
private bool $isTranslateAutomatically = false,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function getTranslations(): Translations
|
public function getTranslations(): Translations
|
||||||
{
|
{
|
||||||
return $this->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] ?? [];
|
||||||
|
}
|
||||||
|
}
|
47
app/application/app/Dto/Service/Site/Documentation.php
Normal file
47
app/application/app/Dto/Service/Site/Documentation.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Site;
|
||||||
|
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\WebsiteTranslations;
|
||||||
|
|
||||||
|
final readonly class Documentation
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private Project $project,
|
||||||
|
private DocumentationVersion $version,
|
||||||
|
private WebsiteTranslations $websiteTranslations,
|
||||||
|
private ?User $user = null,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getProject(): Project
|
||||||
|
{
|
||||||
|
return $this->project;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVersion(): DocumentationVersion
|
||||||
|
{
|
||||||
|
return $this->version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWebsiteTranslations(): WebsiteTranslations
|
||||||
|
{
|
||||||
|
return $this->websiteTranslations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUser(): ?User
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'project' => $this->getProject(),
|
||||||
|
'version' => $this->getVersion(),
|
||||||
|
'websiteTranslations' => $this->getWebsiteTranslations(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,7 @@ enum CacheTag: string
|
|||||||
{
|
{
|
||||||
case Project = 'project';
|
case Project = 'project';
|
||||||
case ProjectTranslation = 'project_translation';
|
case ProjectTranslation = 'project_translation';
|
||||||
|
case DocumantationVersion = 'documantation_version';
|
||||||
|
|
||||||
public function getCache(): TaggedCache
|
public function getCache(): TaggedCache
|
||||||
{
|
{
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Enums;
|
namespace App\Enums;
|
||||||
|
|
||||||
|
use App\Services\WebsiteTranslations;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
enum DocumentationVersionStatus: int
|
enum DocumentationVersionStatus: int
|
||||||
@@ -11,9 +12,18 @@ enum DocumentationVersionStatus: int
|
|||||||
case CurrentVersion = 100;
|
case CurrentVersion = 100;
|
||||||
case FutureVersion = 150;
|
case FutureVersion = 150;
|
||||||
|
|
||||||
public function getTitle(): string
|
public function getTitle(?WebsiteTranslations $websiteTranslations = null): string
|
||||||
{
|
{
|
||||||
return __('version-status.' . $this->name);
|
if (\is_null($websiteTranslations)) {
|
||||||
|
return __($this->getCodeForTranslation());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $websiteTranslations->translate($this->getCodeForTranslation());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCodeForTranslation(): string
|
||||||
|
{
|
||||||
|
return 'version-status.' . $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function toArray(): array
|
public static function toArray(): array
|
||||||
|
@@ -2,16 +2,25 @@
|
|||||||
|
|
||||||
namespace App\Enums;
|
namespace App\Enums;
|
||||||
|
|
||||||
|
use App\Models\DocumentationCategoryContent;
|
||||||
|
use App\Models\DocumentationContent;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
|
use App\Models\ProjectContent;
|
||||||
|
|
||||||
enum Morph: int
|
enum Morph: int
|
||||||
{
|
{
|
||||||
case Project = 1;
|
case Project = 1;
|
||||||
|
case DocumentationContent = 2;
|
||||||
|
case ProjectContent = 3;
|
||||||
|
case DocumentationCategoryContent = 4;
|
||||||
|
|
||||||
public function getPathModel(): string
|
public function getPathModel(): string
|
||||||
{
|
{
|
||||||
return match ($this) {
|
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 => [
|
self::AdminPanel => [
|
||||||
'view' => __('permissions.Administrative panel allowed'),
|
'view' => __('permissions.Administrative panel allowed'),
|
||||||
],
|
],
|
||||||
|
self::Project => array_merge($this->getBasePermissions(), [
|
||||||
|
'Setting up automatic translation' => __('permissions.Setting up automatic translation'),
|
||||||
|
]),
|
||||||
self::ProjectContent => [
|
self::ProjectContent => [
|
||||||
'view' => __('permissions.Allowed to watch'),
|
'view' => __('permissions.Allowed to watch'),
|
||||||
'create' => __('permissions.Allowed to create'),
|
'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;
|
||||||
|
}
|
@@ -5,15 +5,18 @@ namespace App\Enums\Site;
|
|||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\ProjectLanguage;
|
use App\Models\ProjectLanguage;
|
||||||
|
|
||||||
enum ProjectSection
|
enum ProjectSection: string
|
||||||
{
|
{
|
||||||
case Home;
|
case Home = 'home';
|
||||||
case Feedback;
|
case Feedback = 'feedback';
|
||||||
case FeedbackSend;
|
case FeedbackSend = 'feedback.send';
|
||||||
|
case Documentation = 'documentation';
|
||||||
|
case DocumentationVersion = 'documentation.version';
|
||||||
|
case DocumentationCategory = 'documentation.category';
|
||||||
|
case DocumentationView = 'documentation.view';
|
||||||
|
|
||||||
public function url(Project $project, ?ProjectLanguage $language = null): string
|
public function url(Project $project, ?ProjectLanguage $language = null, array $parameters = []): string
|
||||||
{
|
{
|
||||||
$parameters = [];
|
|
||||||
$prefixProject = '';
|
$prefixProject = '';
|
||||||
if ($project->http_host === null) {
|
if ($project->http_host === null) {
|
||||||
$prefixProject = 'project.';
|
$prefixProject = 'project.';
|
||||||
@@ -26,12 +29,7 @@ enum ProjectSection
|
|||||||
$prefixLanguage = '-language';
|
$prefixLanguage = '-language';
|
||||||
}
|
}
|
||||||
|
|
||||||
$route = match ($this) {
|
$route = \route($prefixProject . $this->value . $prefixLanguage, $parameters, false);
|
||||||
self::Home => \route($prefixProject . 'home' . $prefixLanguage, $parameters, false),
|
|
||||||
|
|
||||||
self::Feedback => \route($prefixProject . 'feedback' . $prefixLanguage, $parameters, false),
|
|
||||||
self::FeedbackSend => \route($prefixProject . 'feedback.send' . $prefixLanguage, $parameters, false),
|
|
||||||
};
|
|
||||||
|
|
||||||
return $project->http_host . $route;
|
return $project->http_host . $route;
|
||||||
}
|
}
|
||||||
|
@@ -9,11 +9,13 @@ use App\Contracts\StorageType\Video;
|
|||||||
enum StorageType: int implements Image, Video, Audio
|
enum StorageType: int implements Image, Video, Audio
|
||||||
{
|
{
|
||||||
case Logo = 1;
|
case Logo = 1;
|
||||||
|
case ContentImages = 2;
|
||||||
|
|
||||||
public function getTitle(): string
|
public function getTitle(): string
|
||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
self::Logo => __('validation.attributes.logo'),
|
self::Logo => __('validation.attributes.logo'),
|
||||||
|
self::ContentImages => __('validation.attributes.content_images'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ enum StorageType: int implements Image, Video, Audio
|
|||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
self::Logo => ['jpeg', 'jpg', 'png'],
|
self::Logo => ['jpeg', 'jpg', 'png'],
|
||||||
|
self::ContentImages => ['jpeg', 'jpg', 'png'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,20 +31,21 @@ enum StorageType: int implements Image, Video, Audio
|
|||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
self::Logo => true,
|
self::Logo => true,
|
||||||
|
self::ContentImages => true,
|
||||||
default => false
|
default => false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isVideo(): bool
|
public function isVideo(): bool
|
||||||
{
|
{
|
||||||
return match ($this->name) {
|
return match ($this) {
|
||||||
default => false
|
default => false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isAudio(): bool
|
public function isAudio(): bool
|
||||||
{
|
{
|
||||||
return match ($this->name) {
|
return match ($this) {
|
||||||
default => false
|
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
|
public function login(): View
|
||||||
{
|
{
|
||||||
return view('login');
|
return view('login', [
|
||||||
|
'captcha' => config('app.captcha', false),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authorization(AuthorizationRequest $request): RedirectResponse
|
public function authorization(AuthorizationRequest $request): RedirectResponse
|
||||||
|
@@ -0,0 +1,96 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Site;
|
||||||
|
|
||||||
|
use App\Dto\Service\Site\Documentation;
|
||||||
|
use App\Services\Site\DocumentationService;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
final class DocumentationController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DocumentationService $documentationService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function defaultVersion(Request $request): RedirectResponse | View
|
||||||
|
{
|
||||||
|
$project = $request->get('project');
|
||||||
|
$websiteTranslations = $request->get('websiteTranslations');
|
||||||
|
|
||||||
|
$result = $this->documentationService->defaultVersion($project, $request->user());
|
||||||
|
if ($result->isError()) {
|
||||||
|
if ($result->getCode() === 404) {
|
||||||
|
return view('site.projects.documentation.no-default-version', [
|
||||||
|
'project' => $project,
|
||||||
|
'websiteTranslations' => $websiteTranslations,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = \App\Enums\Site\ProjectSection::DocumentationVersion->url($project, $websiteTranslations->getLanguage(), ['version' => $result->getVersion()->slug]);
|
||||||
|
/**
|
||||||
|
* 302 redirect because the documentation version can change at any time.
|
||||||
|
*/
|
||||||
|
return redirect($url, 302);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index(Request $request): View
|
||||||
|
{
|
||||||
|
$documentation = new Documentation(
|
||||||
|
project: $request->get('project'),
|
||||||
|
version: $request->get('version'),
|
||||||
|
websiteTranslations: $request->get('websiteTranslations'),
|
||||||
|
user: $request->user(),
|
||||||
|
);
|
||||||
|
$result = $this->documentationService->index($documentation);
|
||||||
|
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('site.projects.documentation.index', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function category(string $slug, Request $request): View
|
||||||
|
{
|
||||||
|
$documentation = new Documentation(
|
||||||
|
project: $request->get('project'),
|
||||||
|
version: $request->get('version'),
|
||||||
|
websiteTranslations: $request->get('websiteTranslations'),
|
||||||
|
user: $request->user(),
|
||||||
|
);
|
||||||
|
$result = $this->documentationService->category($slug, $documentation);
|
||||||
|
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
if ($result->isTranslation()) {
|
||||||
|
return $this->viewPageWithoutTranslation($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('site.projects.documentation.category', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view(string $slug, Request $request): View
|
||||||
|
{
|
||||||
|
$documentation = new Documentation(
|
||||||
|
project: $request->get('project'),
|
||||||
|
version: $request->get('version'),
|
||||||
|
websiteTranslations: $request->get('websiteTranslations'),
|
||||||
|
user: $request->user(),
|
||||||
|
);
|
||||||
|
$result = $this->documentationService->view($slug, $documentation);
|
||||||
|
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
if ($result->isTranslation()) {
|
||||||
|
return $this->viewPageWithoutTranslation($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('site.projects.documentation.view', $result->getData());
|
||||||
|
}
|
||||||
|
}
|
@@ -19,6 +19,7 @@ final class FeedbackController extends Controller
|
|||||||
return view('site.feedback.index', [
|
return view('site.feedback.index', [
|
||||||
'project' => $request->get('project'),
|
'project' => $request->get('project'),
|
||||||
'websiteTranslations' => $request->get('websiteTranslations'),
|
'websiteTranslations' => $request->get('websiteTranslations'),
|
||||||
|
'captcha' => config('app.captcha', false),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
40
app/application/app/Http/Middleware/DocumentationVersion.php
Normal file
40
app/application/app/Http/Middleware/DocumentationVersion.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Enums\CacheTag;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
final readonly class DocumentationVersion
|
||||||
|
{
|
||||||
|
public function handle(Request $request, \Closure $next): Response
|
||||||
|
{
|
||||||
|
$project = $request->get('project');
|
||||||
|
$versionSlug = $request->route()?->parameter('version');
|
||||||
|
if ($versionSlug === null || $project === null) {
|
||||||
|
\abort(Response::HTTP_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$seconds = 3600;
|
||||||
|
$version = CacheTag::DocumantationVersion->getCache()->remember(self::class . $project->id . '-' . $versionSlug, $seconds, function () use ($project, $versionSlug) {
|
||||||
|
return $project->documentationVersions()->where('slug', $versionSlug)->first() ?? false;
|
||||||
|
});
|
||||||
|
if ($version === false) {
|
||||||
|
\abort(Response::HTTP_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($request->route()->parameters['version']);
|
||||||
|
|
||||||
|
$request->attributes->set('version', $version);
|
||||||
|
|
||||||
|
if (
|
||||||
|
$version->is_public === false
|
||||||
|
&& ( $request->user() === null || $request->user()->cannot('view', $version) )
|
||||||
|
) {
|
||||||
|
\abort(Response::HTTP_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
@@ -20,6 +20,13 @@ final class IsProject
|
|||||||
\abort(Response::HTTP_NOT_FOUND);
|
\abort(Response::HTTP_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$project->is_public === false
|
||||||
|
&& ( $request->user() === null || $request->user()->cannot('view', $project) )
|
||||||
|
) {
|
||||||
|
\abort(Response::HTTP_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,10 +4,19 @@ namespace App\Http\Requests\Admin\Projects\About;
|
|||||||
|
|
||||||
use App\Contracts\FormRequestDto;
|
use App\Contracts\FormRequestDto;
|
||||||
use App\Dto\Service\Admin\Project\About\StoreUpdate;
|
use App\Dto\Service\Admin\Project\About\StoreUpdate;
|
||||||
|
use App\Dto\Service\Storage\Storages;
|
||||||
|
use App\Enums\StorageType;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
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.
|
* Get the validation rules that apply to the request.
|
||||||
*/
|
*/
|
||||||
@@ -16,6 +25,8 @@ final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
|||||||
return [
|
return [
|
||||||
'title' => ['required', 'string', 'max:255',],
|
'title' => ['required', 'string', 'max:255',],
|
||||||
'description' => ['nullable', 'string',],
|
'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(
|
return new StoreUpdate(
|
||||||
title: $this->input('title'),
|
title: $this->input('title'),
|
||||||
description: $this->input('description'),
|
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'],
|
'sort' => ['required', 'integer', 'min:-1000', 'max:1000'],
|
||||||
'parent_id' => ['nullable', 'integer', 'exists:documentation_categories,id'],
|
'parent_id' => ['nullable', 'integer', 'exists:documentation_categories,id'],
|
||||||
'content.*.title' => ['required', 'string', 'max:255'],
|
'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(
|
$contents->addContent(new Content(
|
||||||
languageId: (int) $languageId,
|
languageId: (int) $languageId,
|
||||||
title: $content['title'],
|
title: $content['title'],
|
||||||
|
isTranslateAutomatically: (bool) $this->input('translate-automatically.' . $languageId, false),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return $contents;
|
return $contents;
|
||||||
|
@@ -6,10 +6,19 @@ use App\Contracts\FormRequestDto;
|
|||||||
use App\Dto\Service\Admin\Project\Documentation\StoreUpdate;
|
use App\Dto\Service\Admin\Project\Documentation\StoreUpdate;
|
||||||
use App\Dto\Service\Admin\Project\DocumentationContent\Content;
|
use App\Dto\Service\Admin\Project\DocumentationContent\Content;
|
||||||
use App\Dto\Service\Admin\Project\DocumentationContent\Contents;
|
use App\Dto\Service\Admin\Project\DocumentationContent\Contents;
|
||||||
|
use App\Dto\Service\Storage\Storages;
|
||||||
|
use App\Enums\StorageType;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
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.
|
* 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'],
|
'category_id' => ['nullable', 'integer', 'exists:documentation_categories,id'],
|
||||||
'content.*.title' => ['required', 'string', 'max:255'],
|
'content.*.title' => ['required', 'string', 'max:255'],
|
||||||
'content.*.content' => ['nullable', 'string'],
|
'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,
|
languageId: (int) $languageId,
|
||||||
title: $content['title'],
|
title: $content['title'],
|
||||||
content: $content['content'] ?? '',
|
content: $content['content'] ?? '',
|
||||||
|
storages: $this->contentStorages($content),
|
||||||
|
isTranslateAutomatically: (bool) $this->input('translate-automatically.' . $languageId, false),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return $contents;
|
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' => ['nullable', 'array'],
|
||||||
'translations.*.code' => ['required', 'string', new In(Translations::getTranslationCodes())],
|
'translations.*.code' => ['required', 'string', new In(Translations::getTranslationCodes())],
|
||||||
'translations.*.text' => ['nullable', 'string', 'max:1000'],
|
'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
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
$rules = [
|
||||||
'email' => ['required', 'email', 'max:255'],
|
'email' => ['required', 'email', 'max:255'],
|
||||||
'password' => ['required', 'min:3'],
|
'password' => ['required', 'min:3'],
|
||||||
'captcha-verified' => ['captcha'],
|
|
||||||
'remember' => ['nullable', 'boolean'],
|
'remember' => ['nullable', 'boolean'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (config('app.captcha', false)) {
|
||||||
|
$rules['captcha-verified'] = ['captcha'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDto(): Authorization
|
public function getDto(): Authorization
|
||||||
|
@@ -32,12 +32,17 @@ final class SendRequest extends FormRequest implements FormRequestDto
|
|||||||
*/
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
$rules = [
|
||||||
'name' => ['nullable', 'string', 'max:255'],
|
'name' => ['nullable', 'string', 'max:255'],
|
||||||
'email' => ['nullable', 'string', 'max:255', 'email'],
|
'email' => ['nullable', 'string', 'max:255', 'email'],
|
||||||
'message' => ['required', 'string', 'max:5000'],
|
'message' => ['required', 'string', 'max:5000'],
|
||||||
'captcha-verified' => ['captcha'],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (config('app.captcha', false)) {
|
||||||
|
$rules['captcha-verified'] = ['captcha'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDto(): Send
|
public function getDto(): Send
|
||||||
|
@@ -6,7 +6,9 @@ use App\Contracts\FormRequestDto;
|
|||||||
use App\Dto\Service\Storage\Upload;
|
use App\Dto\Service\Storage\Upload;
|
||||||
use App\Enums\Morph;
|
use App\Enums\Morph;
|
||||||
use App\Enums\StorageType;
|
use App\Enums\StorageType;
|
||||||
|
use Illuminate\Contracts\Validation\Validator;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||||
use Illuminate\Validation\Rules\Enum;
|
use Illuminate\Validation\Rules\Enum;
|
||||||
|
|
||||||
final class ImageRequest extends FormRequest implements FormRequestDto
|
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')),
|
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,6 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Models\Scopes\SortScope;
|
||||||
|
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@@ -9,6 +11,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
#[ScopedBy([SortScope::class])]
|
||||||
final class Documentation extends Model
|
final class Documentation extends Model
|
||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, SoftDeletes;
|
||||||
|
@@ -2,13 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
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\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
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';
|
protected $table = 'documentation_content';
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Enums\DocumentationVersionStatus;
|
use App\Enums\DocumentationVersionStatus;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@@ -37,6 +38,16 @@ final class DocumentationVersion extends Model
|
|||||||
'status',
|
'status',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "booted" method of the model.
|
||||||
|
*/
|
||||||
|
protected static function booted(): void
|
||||||
|
{
|
||||||
|
static::addGlobalScope('status', function (Builder $builder) {
|
||||||
|
$builder->orderBy('status', 'desc');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the attributes that should be cast.
|
* Get the attributes that should be cast.
|
||||||
*
|
*
|
||||||
|
@@ -2,18 +2,21 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
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\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
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 $table = 'project_content';
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'title',
|
'title',
|
||||||
'description',
|
'description',
|
||||||
|
'language_id',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@ use App\Models\Scopes\SortScope;
|
|||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
|
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
|
||||||
|
|
||||||
@@ -70,4 +71,9 @@ final class ProjectLanguage extends Model
|
|||||||
},
|
},
|
||||||
)->shouldCache();
|
)->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');
|
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');
|
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
|
public function upload(User $user): bool
|
||||||
{
|
{
|
||||||
if ($user->hasPermission('project.create') || $user->hasPermission('project.update')) {
|
if ($user->hasPermission('project.create') || $user->hasPermission('project.update')) {
|
||||||
|
@@ -3,8 +3,10 @@
|
|||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use App\Enums\Morph;
|
use App\Enums\Morph;
|
||||||
|
use App\Services\ProjectFeedback\ProjectFeedbackCommandHandler;
|
||||||
use App\Services\Search\CreateSearchInstanceCommand;
|
use App\Services\Search\CreateSearchInstanceCommand;
|
||||||
use App\Services\Search\Search;
|
use App\Services\Search\Search;
|
||||||
|
use App\Services\Site\FeedbackService;
|
||||||
use App\Services\Storage\Image\ResizeCommandHandler;
|
use App\Services\Storage\Image\ResizeCommandHandler;
|
||||||
use App\Services\Storage\ImageService;
|
use App\Services\Storage\ImageService;
|
||||||
use App\Services\Storage\StorageCommandHandler;
|
use App\Services\Storage\StorageCommandHandler;
|
||||||
@@ -31,7 +33,15 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
});
|
});
|
||||||
|
|
||||||
$this->app->bind(StorageCommandHandler::class, function () {
|
$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) {
|
$this->app->bind(ImageService::class, function (Application $app) {
|
||||||
@@ -59,6 +69,8 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
Route::pattern('language', '[a-z_]+');
|
Route::pattern('language', '[a-z_]+');
|
||||||
Route::pattern('project', '[a-z0-9_-]+');
|
Route::pattern('project', '[a-z0-9_-]+');
|
||||||
|
Route::pattern('slug', '[a-z0-9._-]+');
|
||||||
|
Route::pattern('version', '[a-z0-9._-]+');
|
||||||
|
|
||||||
$this->configureRateLimiting();
|
$this->configureRateLimiting();
|
||||||
Gate::define('AdminPanel', [\App\Policies\AdminPanel::class, 'view']);
|
Gate::define('AdminPanel', [\App\Policies\AdminPanel::class, 'view']);
|
||||||
|
@@ -4,13 +4,13 @@ namespace App\Repositories;
|
|||||||
|
|
||||||
use App\Contracts\Search;
|
use App\Contracts\Search;
|
||||||
use App\Models\DocumentationCategory;
|
use App\Models\DocumentationCategory;
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
use App\Models\ProjectLanguage;
|
use App\Models\ProjectLanguage;
|
||||||
use App\Services\DocumentationCategory\BuilderCommand;
|
use App\Services\DocumentationCategory\BuilderCommand;
|
||||||
use App\Services\Search\CreateSearchInstanceCommand;
|
use App\Services\Search\CreateSearchInstanceCommand;
|
||||||
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
final readonly class DocumentationCategoryRepository
|
final readonly class DocumentationCategoryRepository
|
||||||
@@ -35,9 +35,19 @@ final readonly class DocumentationCategoryRepository
|
|||||||
return DocumentationCategory::query()->where('id', $id)->first();
|
return DocumentationCategory::query()->where('id', $id)->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCategoryByCode(string $code): ?DocumentationCategory
|
public function getCategoryBySlugWithContent(string $slug, int $versionId, ProjectLanguage $language): ?DocumentationCategory
|
||||||
{
|
{
|
||||||
return DocumentationCategory::query()->where('code', $code)->first();
|
$with = [
|
||||||
|
'content' => function (HasOne $hasOne) use ($language) {
|
||||||
|
$hasOne->where('language_id', $language->id);
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return DocumentationCategory::query()
|
||||||
|
->where('version_id', $versionId)
|
||||||
|
->where('slug', $slug)
|
||||||
|
->with($with)
|
||||||
|
->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isExistsSlug(int $versionId, string $slug, ?int $exceptId = null): bool
|
public function isExistsSlug(int $versionId, string $slug, ?int $exceptId = null): bool
|
||||||
@@ -52,7 +62,7 @@ final readonly class DocumentationCategoryRepository
|
|||||||
->exists();
|
->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 = [
|
$with = [
|
||||||
'content' => function (HasOne $hasOne) use ($defaultLanguage) {
|
'content' => function (HasOne $hasOne) use ($defaultLanguage) {
|
||||||
@@ -62,7 +72,7 @@ final readonly class DocumentationCategoryRepository
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
$categories = DocumentationCategory::query()
|
$categories = $version->categories()
|
||||||
->with($with)
|
->with($with)
|
||||||
->when($exceptCategory, function (Builder $query, DocumentationCategory $exceptCategory) {
|
->when($exceptCategory, function (Builder $query, DocumentationCategory $exceptCategory) {
|
||||||
$query->whereNotIn(
|
$query->whereNotIn(
|
||||||
|
@@ -5,9 +5,11 @@ namespace App\Repositories;
|
|||||||
use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
||||||
use App\Contracts\Search;
|
use App\Contracts\Search;
|
||||||
use App\Models\Documentation;
|
use App\Models\Documentation;
|
||||||
|
use App\Models\ProjectLanguage;
|
||||||
use App\Services\Documentation\BuilderCommand;
|
use App\Services\Documentation\BuilderCommand;
|
||||||
use App\Services\Search\CreateSearchInstanceCommand;
|
use App\Services\Search\CreateSearchInstanceCommand;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
final readonly class DocumentationRepository
|
final readonly class DocumentationRepository
|
||||||
@@ -32,9 +34,19 @@ final readonly class DocumentationRepository
|
|||||||
return Documentation::query()->where('id', $id)->first();
|
return Documentation::query()->where('id', $id)->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDocumentationByCode(string $code): ?Documentation
|
public function getDocumentationBySlugWithContent(string $slug, int $versionId, ProjectLanguage $language): ?Documentation
|
||||||
{
|
{
|
||||||
return Documentation::query()->where('code', $code)->first();
|
$with = [
|
||||||
|
'content' => function (HasOne $hasOne) use ($language) {
|
||||||
|
$hasOne->where('language_id', $language->id);
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return Documentation::query()
|
||||||
|
->where('version_id', $versionId)
|
||||||
|
->where('slug', $slug)
|
||||||
|
->with($with)
|
||||||
|
->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isExistsSlug(int $versionId, string $slug, ?int $exceptId = null): bool
|
public function isExistsSlug(int $versionId, string $slug, ?int $exceptId = null): bool
|
||||||
|
@@ -22,4 +22,13 @@ final readonly class ProjectTranslationRepository
|
|||||||
$query = ProjectTranslation::query()->withTrashed()->where('project_id', $projectId)->where('language_id', $languageId);
|
$query = ProjectTranslation::query()->withTrashed()->where('project_id', $projectId)->where('language_id', $languageId);
|
||||||
return $this->createSearchInstanceCommand->execute($query);
|
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);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,17 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\ServiceResults\Site\DocumentationService;
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
|
use App\ServiceResults\ServiceResult;
|
||||||
|
|
||||||
|
final class DefaultVersion extends ServiceResult
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DocumentationVersion $version,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getVersion(): DocumentationVersion
|
||||||
|
{
|
||||||
|
return $this->version;
|
||||||
|
}
|
||||||
|
}
|
@@ -4,6 +4,8 @@ namespace App\Services\Admin\Project;
|
|||||||
|
|
||||||
use App\Contracts\ServiceResultError;
|
use App\Contracts\ServiceResultError;
|
||||||
use App\Dto\Service\Admin\Project\About\StoreUpdate;
|
use App\Dto\Service\Admin\Project\About\StoreUpdate;
|
||||||
|
use App\Enums\Morph;
|
||||||
|
use App\Jobs\Translate\ProcessProjectContent;
|
||||||
use App\Models\ProjectContent;
|
use App\Models\ProjectContent;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Repositories\ProjectContentRepository;
|
use App\Repositories\ProjectContentRepository;
|
||||||
@@ -13,6 +15,7 @@ use App\ServiceResults\ServiceResultArray;
|
|||||||
use App\ServiceResults\StoreUpdateResult;
|
use App\ServiceResults\StoreUpdateResult;
|
||||||
use App\Services\ProjectContent\ProjectContentCommandHandler;
|
use App\Services\ProjectContent\ProjectContentCommandHandler;
|
||||||
use App\Services\Service;
|
use App\Services\Service;
|
||||||
|
use App\Services\Storage\StorageService;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
final class AboutService extends Service
|
final class AboutService extends Service
|
||||||
@@ -22,6 +25,7 @@ final class AboutService extends Service
|
|||||||
private readonly ProjectLanguageRepository $projectLanguageRepository,
|
private readonly ProjectLanguageRepository $projectLanguageRepository,
|
||||||
private readonly ProjectContentRepository $projectContentRepository,
|
private readonly ProjectContentRepository $projectContentRepository,
|
||||||
private readonly ProjectContentCommandHandler $projectContentCommandHandler,
|
private readonly ProjectContentCommandHandler $projectContentCommandHandler,
|
||||||
|
private readonly StorageService $storageService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function languages(int $projectId, User $user): ServiceResultError | ServiceResultArray
|
public function languages(int $projectId, User $user): ServiceResultError | ServiceResultArray
|
||||||
@@ -66,6 +70,7 @@ final class AboutService extends Service
|
|||||||
'project' => $project,
|
'project' => $project,
|
||||||
'language' => $language,
|
'language' => $language,
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
|
'serviceTranslationEnable' => config('translation_service.enable', false),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,12 +93,23 @@ final class AboutService extends Service
|
|||||||
if ($user->cannot('create', ProjectContent::class)) {
|
if ($user->cannot('create', ProjectContent::class)) {
|
||||||
return $this->errFobidden(__('Access is denied'));
|
return $this->errFobidden(__('Access is denied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$storages = $this->storageService->getStoragesAndValidate($data->getStorages(), Morph::ProjectContent);
|
||||||
|
if (!$storages->isSuccess()) {
|
||||||
|
return $storages;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$aboutProject = DB::transaction(function () use ($data, $projectId, $languageId) {
|
$aboutProject = DB::transaction(function () use ($data, $projectId, $languageId, $storages) {
|
||||||
$dataAboutProject = $this->getDataAboutProject($data);
|
$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) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
return $this->errService(__('Server Error'));
|
return $this->errService(__('Server Error'));
|
||||||
@@ -108,12 +124,23 @@ final class AboutService extends Service
|
|||||||
return $this->errFobidden(__('Access is denied'));
|
return $this->errFobidden(__('Access is denied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$storages = $this->storageService->getStoragesAndValidate($data->getStorages(), Morph::ProjectContent);
|
||||||
|
if (!$storages->isSuccess()) {
|
||||||
|
return $storages;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$aboutProject = DB::transaction(function () use ($data, $content) {
|
$aboutProject = DB::transaction(function () use ($data, $content, $storages) {
|
||||||
$dataAboutProject = $this->getDataAboutProject($data);
|
$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) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
return $this->errService(__('Server Error'));
|
return $this->errService(__('Server Error'));
|
||||||
@@ -129,4 +156,15 @@ final class AboutService extends Service
|
|||||||
'description' => $data->getDescription(),
|
'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\QuerySettingsDto;
|
||||||
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
||||||
use App\Dto\Service\Admin\Project\DocumentationCategory\StoreUpdate;
|
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\DocumentationCategory\ParentException;
|
||||||
use App\Exceptions\Services\ServiceException;
|
use App\Exceptions\Services\ServiceException;
|
||||||
|
use App\Jobs\Translate\ProcessProjectDocumentationCategoryContent;
|
||||||
use App\Models\DocumentationCategory;
|
use App\Models\DocumentationCategory;
|
||||||
use App\Models\DocumentationCategoryContent;
|
use App\Models\DocumentationCategoryContent;
|
||||||
use App\Models\ProjectLanguage;
|
use App\Models\ProjectLanguage;
|
||||||
@@ -89,7 +92,8 @@ final class DocumentationCategoryService extends Service
|
|||||||
'version' => $version,
|
'version' => $version,
|
||||||
'project' => $project,
|
'project' => $project,
|
||||||
'category' => new DocumentationCategory(),
|
'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,
|
'version' => $version,
|
||||||
'project' => $project,
|
'project' => $project,
|
||||||
'category' => $category,
|
'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;
|
return $category;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (\config('translation_service.enable', false)) {
|
||||||
|
$this->translateContent($category, $data->getContents());
|
||||||
|
}
|
||||||
} catch (ServiceException $e) {
|
} catch (ServiceException $e) {
|
||||||
return $e->getServiceResultError();
|
return $e->getServiceResultError();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -201,6 +210,10 @@ final class DocumentationCategoryService extends Service
|
|||||||
|
|
||||||
return $category;
|
return $category;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (\config('translation_service.enable', false)) {
|
||||||
|
$this->translateContent($category, $data->getContents());
|
||||||
|
}
|
||||||
} catch (ServiceException $e) {
|
} catch (ServiceException $e) {
|
||||||
return $e->getServiceResultError();
|
return $e->getServiceResultError();
|
||||||
} catch (ParentException $e) {
|
} catch (ParentException $e) {
|
||||||
@@ -254,4 +267,27 @@ final class DocumentationCategoryService extends Service
|
|||||||
'parent_id' => $data->getParentId(),
|
'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\Builder\Documentation as DocumentationBuilderDto;
|
||||||
use App\Dto\QuerySettingsDto;
|
use App\Dto\QuerySettingsDto;
|
||||||
use App\Dto\Service\Admin\Project\Documentation\StoreUpdate;
|
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\Documentation;
|
||||||
use App\Models\ProjectLanguage;
|
use App\Models\ProjectLanguage;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Repositories\DocumentationCategoryRepository;
|
use App\Repositories\DocumentationCategoryRepository;
|
||||||
use App\Repositories\DocumentationRepository;
|
use App\Repositories\DocumentationRepository;
|
||||||
use App\Repositories\DocumentationVersionRepository;
|
use App\Repositories\DocumentationVersionRepository;
|
||||||
use App\Repositories\ProjectRepository;
|
|
||||||
use App\ServiceResults\ServiceResultArray;
|
use App\ServiceResults\ServiceResultArray;
|
||||||
use App\ServiceResults\ServiceResultError;
|
use App\ServiceResults\ServiceResultError;
|
||||||
use App\ServiceResults\ServiceResultSuccess;
|
use App\ServiceResults\ServiceResultSuccess;
|
||||||
@@ -89,7 +92,8 @@ final class DocumentationService extends Service
|
|||||||
'version' => $version,
|
'version' => $version,
|
||||||
'project' => $project,
|
'project' => $project,
|
||||||
'documentation' => new Documentation(),
|
'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,
|
'version' => $version,
|
||||||
'project' => $project,
|
'project' => $project,
|
||||||
'documentation' => $documentation,
|
'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;
|
return $documentation;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (\config('translation_service.enable', false)) {
|
||||||
|
$this->translateContent($documentation, $data->getContents());
|
||||||
|
}
|
||||||
|
} catch (StorageCommandException $e) {
|
||||||
|
return $e->getResultError();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
return $this->errService(__('Server Error'));
|
return $this->errService(__('Server Error'));
|
||||||
@@ -190,6 +201,12 @@ final class DocumentationService extends Service
|
|||||||
|
|
||||||
return $documentation;
|
return $documentation;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (\config('translation_service.enable', false)) {
|
||||||
|
$this->translateContent($documentation, $data->getContents());
|
||||||
|
}
|
||||||
|
} catch (StorageCommandException $e) {
|
||||||
|
return $e->getResultError();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
return $this->errService(__('Server Error'));
|
return $this->errService(__('Server Error'));
|
||||||
@@ -236,4 +253,27 @@ final class DocumentationService extends Service
|
|||||||
'category_id' => $data->getCategoryId(),
|
'category_id' => $data->getCategoryId(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function translateContent(Documentation $documentation, Contents $contents): void
|
||||||
|
{
|
||||||
|
$translateExceptLanguages = [];
|
||||||
|
|
||||||
|
$translateLanguages = [];
|
||||||
|
foreach ($contents->getContents() as $content) {
|
||||||
|
/** @var Content $content */
|
||||||
|
$translateExceptLanguages[] = $content->getLanguageId();
|
||||||
|
if ($content->isTranslateAutomatically()) {
|
||||||
|
$translateLanguages[] = $content->getLanguageId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($translateLanguages)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$translateContentIds = $documentation->contents()->select('id')
|
||||||
|
->whereIn('language_id', $translateLanguages)
|
||||||
|
->get()->pluck('id')->toArray();
|
||||||
|
ProcessProjectDocumentationContent::dispatch($documentation->id, $translateContentIds, $translateExceptLanguages);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ namespace App\Services\Admin\Project;
|
|||||||
use App\Dto\QuerySettingsDto;
|
use App\Dto\QuerySettingsDto;
|
||||||
use App\Dto\Builder\DocumentationVersion as DocumentationVersionBuilderDto;
|
use App\Dto\Builder\DocumentationVersion as DocumentationVersionBuilderDto;
|
||||||
use App\Dto\Service\Admin\Project\DocumentationVersion\StoreUpdate;
|
use App\Dto\Service\Admin\Project\DocumentationVersion\StoreUpdate;
|
||||||
|
use App\Enums\CacheTag;
|
||||||
use App\Enums\DocumentationVersionStatus;
|
use App\Enums\DocumentationVersionStatus;
|
||||||
use App\Models\DocumentationVersion;
|
use App\Models\DocumentationVersion;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
@@ -14,6 +15,7 @@ use App\ServiceResults\ServiceResultArray;
|
|||||||
use App\ServiceResults\ServiceResultError;
|
use App\ServiceResults\ServiceResultError;
|
||||||
use App\ServiceResults\ServiceResultSuccess;
|
use App\ServiceResults\ServiceResultSuccess;
|
||||||
use App\ServiceResults\StoreUpdateResult;
|
use App\ServiceResults\StoreUpdateResult;
|
||||||
|
use App\Services\ClearCacheCommandHandler;
|
||||||
use App\Services\DocumentationVersion\DocumentationVersionCommandHandler;
|
use App\Services\DocumentationVersion\DocumentationVersionCommandHandler;
|
||||||
use App\Services\Service;
|
use App\Services\Service;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
@@ -24,6 +26,7 @@ final class DocumentationVersionService extends Service
|
|||||||
private readonly ProjectRepository $projectRepository,
|
private readonly ProjectRepository $projectRepository,
|
||||||
private readonly DocumentationVersionRepository $documentationVersionRepository,
|
private readonly DocumentationVersionRepository $documentationVersionRepository,
|
||||||
private readonly DocumentationVersionCommandHandler $documentationVersionCommandHandler,
|
private readonly DocumentationVersionCommandHandler $documentationVersionCommandHandler,
|
||||||
|
private readonly ClearCacheCommandHandler $clearCacheCommandHandler,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function index(int $projectId, DocumentationVersionBuilderDto $documentationVersionBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
|
public function index(int $projectId, DocumentationVersionBuilderDto $documentationVersionBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
|
||||||
@@ -138,6 +141,7 @@ final class DocumentationVersionService extends Service
|
|||||||
$dataVersion = $this->getDataVersion($data);
|
$dataVersion = $this->getDataVersion($data);
|
||||||
return $this->documentationVersionCommandHandler->handleStore($project, $dataVersion);
|
return $this->documentationVersionCommandHandler->handleStore($project, $dataVersion);
|
||||||
});
|
});
|
||||||
|
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
return $this->errService(__('Server Error'));
|
return $this->errService(__('Server Error'));
|
||||||
@@ -174,6 +178,7 @@ final class DocumentationVersionService extends Service
|
|||||||
$dataVersion = $this->getDataVersion($data);
|
$dataVersion = $this->getDataVersion($data);
|
||||||
return $this->documentationVersionCommandHandler->handleUpdate($version, $dataVersion);
|
return $this->documentationVersionCommandHandler->handleUpdate($version, $dataVersion);
|
||||||
});
|
});
|
||||||
|
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
return $this->errService(__('Server Error'));
|
return $this->errService(__('Server Error'));
|
||||||
@@ -202,6 +207,7 @@ final class DocumentationVersionService extends Service
|
|||||||
DB::transaction(function () use ($version) {
|
DB::transaction(function () use ($version) {
|
||||||
$this->documentationVersionCommandHandler->handleDestroy($version);
|
$this->documentationVersionCommandHandler->handleDestroy($version);
|
||||||
});
|
});
|
||||||
|
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
return $this->errService(__('Server Error'));
|
return $this->errService(__('Server Error'));
|
||||||
|
@@ -0,0 +1,67 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Admin\Project;
|
||||||
|
|
||||||
|
use App\Dto\Service\Admin\Project\ServiceTranslate\Update;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Repositories\ProjectRepository;
|
||||||
|
use App\ServiceResults\ServiceResultArray;
|
||||||
|
use App\ServiceResults\ServiceResultError;
|
||||||
|
use App\ServiceResults\ServiceResultSuccess;
|
||||||
|
use App\Services\ProjectTranslationService\ModelSyncCommand as TranslationServiceModelSyncCommand;
|
||||||
|
use App\Services\Service;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
final class ServiceTranslateService extends Service
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ProjectRepository $projectRepository,
|
||||||
|
private readonly TranslationServiceModelSyncCommand $translationServiceModelSyncCommand,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function view(int $projectId, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$project = $this->projectRepository->getProjectById($projectId);
|
||||||
|
if (\is_null($project)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
config('translation_service.enable', false) === false
|
||||||
|
|| $user->cannot('settingUpAutomaticTranslation', $project)
|
||||||
|
) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->result([
|
||||||
|
'project' => $project,
|
||||||
|
'languages' => $project->languages()->with(['serviceTranslate'])->get(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(int $projectId, Update $data, User $user): ServiceResultError | ServiceResultSuccess
|
||||||
|
{
|
||||||
|
$project = $this->projectRepository->getProjectById($projectId);
|
||||||
|
if (\is_null($project)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
config('translation_service.enable', false) === false
|
||||||
|
|| $user->cannot('settingUpAutomaticTranslation', $project)
|
||||||
|
) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::transaction(function () use ($data, $project) {
|
||||||
|
$this->translationServiceModelSyncCommand->execute($project, $data->getTranslations());
|
||||||
|
});
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->ok(__('The settings were saved successfully'));
|
||||||
|
}
|
||||||
|
}
|
@@ -3,8 +3,10 @@
|
|||||||
namespace App\Services\Admin\Project;
|
namespace App\Services\Admin\Project;
|
||||||
|
|
||||||
use App\Dto\Service\Admin\Project\Translation\Translations;
|
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\Dto\Service\Admin\Project\Translation\Update;
|
||||||
use App\Enums\CacheTag;
|
use App\Enums\CacheTag;
|
||||||
|
use App\Jobs\Translate\ProcessTranslationText;
|
||||||
use App\Models\ProjectTranslation;
|
use App\Models\ProjectTranslation;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Repositories\ProjectRepository;
|
use App\Repositories\ProjectRepository;
|
||||||
@@ -60,6 +62,7 @@ final class TranslationService extends Service
|
|||||||
'language' => $language,
|
'language' => $language,
|
||||||
'projectTranslations' => $this->projectTranslationRepository->getProjectTranslations($projectId, $languageId)->all()->pluck('text', 'code')->toArray(),
|
'projectTranslations' => $this->projectTranslationRepository->getProjectTranslations($projectId, $languageId)->all()->pluck('text', 'code')->toArray(),
|
||||||
'translations' => Translations::getTranslationCodes(),
|
'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->translationModelSyncCommand->execute($project, $language, $data->getTranslations());
|
||||||
});
|
});
|
||||||
$this->clearCacheCommandHandler->byTag(CacheTag::ProjectTranslation);
|
$this->clearCacheCommandHandler->byTag(CacheTag::ProjectTranslation);
|
||||||
|
if (\config('translation_service.enable', false)) {
|
||||||
|
$this->translateContent($projectId, $languageId, $data);
|
||||||
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
return $this->errService(__('Server Error'));
|
return $this->errService(__('Server Error'));
|
||||||
@@ -88,4 +94,19 @@ final class TranslationService extends Service
|
|||||||
|
|
||||||
return $this->ok(__('Translations successfully updated'));
|
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([
|
return $this->result([
|
||||||
'projects' => $projects,
|
'projects' => $projects,
|
||||||
|
'serviceTranslationEnable' => config('translation_service.enable', false),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@ use App\Enums\CacheTag;
|
|||||||
|
|
||||||
final readonly class ClearCacheCommandHandler
|
final readonly class ClearCacheCommandHandler
|
||||||
{
|
{
|
||||||
public function byTag(CacheTag $tag)
|
public function byTag(CacheTag $tag): void
|
||||||
{
|
{
|
||||||
$tag->getCache()->flush();
|
$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.'));
|
||||||
|
}
|
||||||
|
}
|
@@ -10,10 +10,12 @@ final readonly class BuilderCommand
|
|||||||
{
|
{
|
||||||
public function execute(Relation | Builder $query, DocumentationBuilderDto $documentationBuilderDto): Relation | Builder
|
public function execute(Relation | Builder $query, DocumentationBuilderDto $documentationBuilderDto): Relation | Builder
|
||||||
{
|
{
|
||||||
if ($documentationBuilderDto->isPublic() !== null) {
|
return $query
|
||||||
|
->when($documentationBuilderDto->isPublic(), function (Builder $query) use ($documentationBuilderDto) {
|
||||||
$query->where('is_public', $documentationBuilderDto->isPublic());
|
$query->where('is_public', $documentationBuilderDto->isPublic());
|
||||||
}
|
})
|
||||||
|
->when($documentationBuilderDto->getCategoryId(), function (Builder $query) use ($documentationBuilderDto) {
|
||||||
return $query;
|
$query->where('category_id', $documentationBuilderDto->getCategoryId()->getCategoryId());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,10 +10,12 @@ final readonly class BuilderCommand
|
|||||||
{
|
{
|
||||||
public function execute(Relation | Builder $query, DocumentationCategoryBuilderDto $documentationCategoryBuilderDto): Relation | Builder
|
public function execute(Relation | Builder $query, DocumentationCategoryBuilderDto $documentationCategoryBuilderDto): Relation | Builder
|
||||||
{
|
{
|
||||||
if ($documentationCategoryBuilderDto->isPublic() !== null) {
|
return $query
|
||||||
|
->when($documentationCategoryBuilderDto->isPublic(), function (Builder $query) use ($documentationCategoryBuilderDto) {
|
||||||
$query->where('is_public', $documentationCategoryBuilderDto->isPublic());
|
$query->where('is_public', $documentationCategoryBuilderDto->isPublic());
|
||||||
}
|
})
|
||||||
|
->when($documentationCategoryBuilderDto->getParentId(), function (Builder $query) use ($documentationCategoryBuilderDto) {
|
||||||
return $query;
|
$query->where('parent_id', $documentationCategoryBuilderDto->getParentId()->getCategoryId());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,18 +4,33 @@ namespace App\Services\DocumentationContent;
|
|||||||
|
|
||||||
use App\Dto\Service\Admin\Project\DocumentationContent\Content;
|
use App\Dto\Service\Admin\Project\DocumentationContent\Content;
|
||||||
use App\Dto\Service\Admin\Project\DocumentationContent\Contents;
|
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\ContentSaveException;
|
||||||
|
use App\Exceptions\Services\DocumentationContent\StorageCommandException;
|
||||||
use App\Models\Documentation;
|
use App\Models\Documentation;
|
||||||
|
use App\Models\DocumentationContent;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
|
|
||||||
final readonly class ModelSyncCommand
|
final readonly class ModelSyncCommand
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
private StorageCommand $storageCommand,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws StorageCommandException
|
||||||
|
* @throws ContentSaveException
|
||||||
|
*/
|
||||||
public function execute(Project $project, Documentation $documentation, Contents $contents): void
|
public function execute(Project $project, Documentation $documentation, Contents $contents): void
|
||||||
{
|
{
|
||||||
|
$storageDto = new StorageDto();
|
||||||
|
|
||||||
$languages = $project->languages;
|
$languages = $project->languages;
|
||||||
$documentationContents = $documentation->contents;
|
$documentationContents = $documentation->contents;
|
||||||
|
|
||||||
$newContents = [];
|
$newContents = [];
|
||||||
|
$contentsStorageCreated = [];
|
||||||
|
$contentLanguages = [];
|
||||||
foreach ($contents->getContents() as $content) {
|
foreach ($contents->getContents() as $content) {
|
||||||
/** @var Content $content */
|
/** @var Content $content */
|
||||||
$language = $languages->firstWhere('id', $content->getLanguageId());
|
$language = $languages->firstWhere('id', $content->getLanguageId());
|
||||||
@@ -26,15 +41,29 @@ final readonly class ModelSyncCommand
|
|||||||
$model = $documentationContents->firstWhere('language_id', $language->id);
|
$model = $documentationContents->firstWhere('language_id', $language->id);
|
||||||
$data = $this->getData($content);
|
$data = $this->getData($content);
|
||||||
if (\is_null($model)) {
|
if (\is_null($model)) {
|
||||||
|
$contentsStorageCreated[$content->getLanguageId()] = $content->getStorages();
|
||||||
$newContents[] = array_merge(['language_id' => $content->getLanguageId()], $data);
|
$newContents[] = array_merge(['language_id' => $content->getLanguageId()], $data);
|
||||||
|
$contentLanguages[] = $content->getLanguageId();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
$storageDto->add($model, $content->getStorages());
|
||||||
$model->update($data);
|
$model->update($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($newContents)) {
|
if (!empty($newContents)) {
|
||||||
$documentation->contents()->createMany($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
|
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;
|
namespace App\Services\Role;
|
||||||
|
|
||||||
use App\Enums\Permission;
|
use App\Enums\Permission;
|
||||||
use App\Exceptions\Rule\RoleSyncPermissionsCommandHandlerException;
|
use App\Exceptions\Services\Rule\RoleSyncPermissionsCommandHandlerException;
|
||||||
use App\Models\Role;
|
use App\Models\Role;
|
||||||
use App\Models\RolePermission;
|
use App\Models\RolePermission;
|
||||||
|
|
||||||
|
151
app/application/app/Services/Site/DocumentationService.php
Normal file
151
app/application/app/Services/Site/DocumentationService.php
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Site;
|
||||||
|
|
||||||
|
use App\Dto\Service\Site\Documentation;
|
||||||
|
use App\Enums\CacheTag;
|
||||||
|
use App\Enums\DocumentationVersionStatus;
|
||||||
|
use App\Models\DocumentationCategory;
|
||||||
|
use App\Models\Documentation as ModelDocumentation;
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Repositories\DocumentationCategoryRepository;
|
||||||
|
use App\Repositories\DocumentationRepository;
|
||||||
|
use App\ServiceResults\ServiceResultArray;
|
||||||
|
use App\ServiceResults\ServiceResultError;
|
||||||
|
use App\ServiceResults\Site\DocumentationService\DefaultVersion;
|
||||||
|
use app\ServiceResults\Site\PagePossibleWithoutTranslation;
|
||||||
|
use App\Services\Service;
|
||||||
|
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
||||||
|
use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
final class DocumentationService extends Service
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DocumentationCategoryRepository $documentationCategoryRepository,
|
||||||
|
private readonly DocumentationRepository $documentationRepository
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function defaultVersion(Project $project, ?User $user): ServiceResultError | DefaultVersion
|
||||||
|
{
|
||||||
|
$seconds = 3600;
|
||||||
|
$isPublic = null;
|
||||||
|
if ($user?->cannot('viewAny', DocumentationVersion::class)) {
|
||||||
|
$isPublic = 1;
|
||||||
|
}
|
||||||
|
$version = CacheTag::DocumantationVersion->getCache()
|
||||||
|
->remember(self::class . $project->id . '_' . $isPublic ?? 0, $seconds, function () use ($project, $isPublic) {
|
||||||
|
$versions = $project->documentationVersions()
|
||||||
|
->when($isPublic, function (Builder $query) {
|
||||||
|
$query->where('is_public', 1);
|
||||||
|
})
|
||||||
|
->limit(10)
|
||||||
|
->get();
|
||||||
|
return $versions->firstWhere('status', DocumentationVersionStatus::CurrentVersion)
|
||||||
|
??
|
||||||
|
$versions->first() ?? false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($version === false) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DefaultVersion($version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index(Documentation $documentation): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
return $this->result(array_merge($documentation->toArray(), [
|
||||||
|
'categories' => $this->getCategories($documentation, null),
|
||||||
|
'documentations' => $this->getDocumentations($documentation, null),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function category(string $slug, Documentation $documentation): ServiceResultError | PagePossibleWithoutTranslation
|
||||||
|
{
|
||||||
|
$category = $this->documentationCategoryRepository->getCategoryBySlugWithContent($slug, $documentation->getVersion()->id, $documentation->getWebsiteTranslations()->getLanguage());
|
||||||
|
if (!$category) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
$category->is_public === false &&
|
||||||
|
($documentation->getUser() === null || $documentation->getUser()->cannot('view', $category))
|
||||||
|
) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array_merge($documentation->toArray(), [
|
||||||
|
'category' => $category,
|
||||||
|
'categories' => $this->getCategories($documentation, $category->id),
|
||||||
|
'documentations' => $this->getDocumentations($documentation, $category->id),
|
||||||
|
]);
|
||||||
|
return $this->resultSitePage($documentation->getProject(), $documentation->getWebsiteTranslations(), $data, \is_null($category->content?->title));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view(string $slug, Documentation $documentation): ServiceResultError | PagePossibleWithoutTranslation
|
||||||
|
{
|
||||||
|
$document = $this->documentationRepository->getDocumentationBySlugWithContent($slug, $documentation->getVersion()->id, $documentation->getWebsiteTranslations()->getLanguage());
|
||||||
|
if (!$document) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
$document->is_public === false &&
|
||||||
|
($documentation->getUser() === null || $documentation->getUser()->cannot('view', $document))
|
||||||
|
) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array_merge($documentation->toArray(), [
|
||||||
|
'documentation' => $document,
|
||||||
|
]);
|
||||||
|
return $this->resultSitePage($documentation->getProject(), $documentation->getWebsiteTranslations(), $data, \is_null($document->content?->title));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCategories(Documentation $documentation, ?int $parentId): Collection
|
||||||
|
{
|
||||||
|
$isPublic = null;
|
||||||
|
if ($documentation->getUser() === null || $documentation->getUser()->cannot('viewAny', DocumentationCategory::class)) {
|
||||||
|
$isPublic = true;
|
||||||
|
}
|
||||||
|
$builderDto = new DocumentationCategoryBuilderDto(
|
||||||
|
isPublic: $isPublic,
|
||||||
|
parentId: new DocumentationCategoryBuilderDto\Category($parentId),
|
||||||
|
);
|
||||||
|
$with = [
|
||||||
|
'content' => function (HasOne $hasOne) use ($documentation) {
|
||||||
|
$hasOne->where('language_id', $documentation->getWebsiteTranslations()->getLanguage()->id);
|
||||||
|
}
|
||||||
|
];
|
||||||
|
return $this->documentationCategoryRepository->getCategories(
|
||||||
|
$documentation->getVersion()->id,
|
||||||
|
$builderDto,
|
||||||
|
$with
|
||||||
|
)->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDocumentations(Documentation $documentation, ?int $categoryId): Collection
|
||||||
|
{
|
||||||
|
$isPublic = null;
|
||||||
|
if ($documentation->getUser() === null || $documentation->getUser()->cannot('viewAny', ModelDocumentation::class)) {
|
||||||
|
$isPublic = true;
|
||||||
|
}
|
||||||
|
$builderDto = new DocumentationBuilderDto(
|
||||||
|
isPublic: $isPublic,
|
||||||
|
categoryId: new DocumentationCategoryBuilderDto\Category($categoryId),
|
||||||
|
);
|
||||||
|
$with = [
|
||||||
|
'content' => function (HasOne $hasOne) use ($documentation) {
|
||||||
|
$hasOne->where('language_id', $documentation->getWebsiteTranslations()->getLanguage()->id);
|
||||||
|
}
|
||||||
|
];
|
||||||
|
return $this->documentationRepository->getDocumentations(
|
||||||
|
$documentation->getVersion()->id,
|
||||||
|
$builderDto,
|
||||||
|
$with
|
||||||
|
)->all();
|
||||||
|
}
|
||||||
|
}
|
@@ -5,32 +5,44 @@ namespace App\Services\Site;
|
|||||||
use App\Dto\Service\Site\Feedback\Send;
|
use App\Dto\Service\Site\Feedback\Send;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Notifications\ReviewAdded;
|
||||||
use App\ServiceResults\ServiceResultError;
|
use App\ServiceResults\ServiceResultError;
|
||||||
use App\ServiceResults\ServiceResultSuccess;
|
use App\ServiceResults\ServiceResultSuccess;
|
||||||
use App\Services\ProjectFeedback\ProjectFeedbackCommandHandler;
|
use App\Services\ProjectFeedback\ProjectFeedbackCommandHandler;
|
||||||
use App\Services\Service;
|
use App\Services\Service;
|
||||||
use App\Services\WebsiteTranslations;
|
use App\Services\WebsiteTranslations;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
|
||||||
final class FeedbackService extends Service
|
final class FeedbackService extends Service
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ProjectFeedbackCommandHandler $feedbackCommandHandler,
|
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
|
public function send(Send $send, Project $project, WebsiteTranslations $websiteTranslations, ?User $user = null): ServiceResultError | ServiceResultSuccess
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
DB::transaction(function () use ($send, $project, $user) {
|
$feedback = DB::transaction(function () use ($send, $project, $user) {
|
||||||
$data = $this->getDataFeedback($send);
|
$data = $this->getDataFeedback($send);
|
||||||
$data['user_id'] = $user?->id;
|
$data['user_id'] = $user?->id;
|
||||||
$this->feedbackCommandHandler->handleStore($project, $data);
|
return $this->feedbackCommandHandler->handleStore($project, $data);
|
||||||
});
|
});
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
return $this->errService($websiteTranslations->translate('Server Error'));
|
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'));
|
return $this->ok($websiteTranslations->translate('site.Message sent successfully'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user