Compare commits
41 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
21026cdf52 | |||
45504791c0 | |||
156d8a9f68 | |||
478cbe4762 | |||
35c2368ae2 | |||
303af6f203 | |||
1485180e20 |
@ -1,5 +1,6 @@
|
|||||||
DOCKER_APP_PORT=8080
|
DOCKER_APP_PORT=8080
|
||||||
DOCKER_CAPTCHA_PORT=8081
|
DOCKER_CAPTCHA_PORT=8081
|
||||||
|
DOCKER_CAPTCHA_WEBSOCKET_PORT=8082
|
||||||
DOCKER_DB_PORT=3306
|
DOCKER_DB_PORT=3306
|
||||||
MYSQL_ROOT_PASSWORD=root_pass
|
MYSQL_ROOT_PASSWORD=root_pass
|
||||||
DB_DATABASE=my-projetcs
|
DB_DATABASE=my-projetcs
|
||||||
|
14
app/.dockerignore
Normal file
14
app/.dockerignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
**/.env
|
||||||
|
**/*.env
|
||||||
|
**/.env.example
|
||||||
|
**/storage/app/*
|
||||||
|
**/storage/debugbar
|
||||||
|
**/storage/framework/cache/*
|
||||||
|
**/storage/framework/sessions/*
|
||||||
|
**/storage/framework/views/*
|
||||||
|
**/storage/framework/testing/*
|
||||||
|
**/storage/logs/*
|
||||||
|
**/vendor/
|
||||||
|
**/node_modules/
|
||||||
|
**/public/build/
|
||||||
|
**/public/storage
|
@ -1,17 +1,21 @@
|
|||||||
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=
|
||||||
|
|
||||||
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 +63,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());
|
||||||
|
}
|
||||||
|
}
|
23
app/application/app/Dto/Builder/Documentation.php
Normal file
23
app/application/app/Dto/Builder/Documentation.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Builder;
|
||||||
|
|
||||||
|
use App\Dto\Builder\DocumentationCategory\Category;
|
||||||
|
|
||||||
|
final readonly class Documentation
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private ?bool $isPublic = null,
|
||||||
|
private ?Category $categoryId = null,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function isPublic(): ?bool
|
||||||
|
{
|
||||||
|
return $this->isPublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCategoryId(): ?Category
|
||||||
|
{
|
||||||
|
return $this->categoryId;
|
||||||
|
}
|
||||||
|
}
|
23
app/application/app/Dto/Builder/DocumentationCategory.php
Normal file
23
app/application/app/Dto/Builder/DocumentationCategory.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Builder;
|
||||||
|
|
||||||
|
use App\Dto\Builder\DocumentationCategory\Category;
|
||||||
|
|
||||||
|
final readonly class DocumentationCategory
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private ?bool $isPublic = null,
|
||||||
|
private ?Category $parentId = null,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function isPublic(): ?bool
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
15
app/application/app/Dto/Builder/DocumentationVersion.php
Normal file
15
app/application/app/Dto/Builder/DocumentationVersion.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Builder;
|
||||||
|
|
||||||
|
final readonly class DocumentationVersion
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private ?bool $isPublic = null,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function isPublic(): ?bool
|
||||||
|
{
|
||||||
|
return $this->isPublic;
|
||||||
|
}
|
||||||
|
}
|
@ -3,12 +3,14 @@
|
|||||||
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,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function getTitle(): string
|
public function getTitle(): string
|
||||||
@ -20,4 +22,9 @@ public function getDescription(): string
|
|||||||
{
|
{
|
||||||
return $this->description;
|
return $this->description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getStorages(): Storages
|
||||||
|
{
|
||||||
|
return $this->storages;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Admin\Project\Documentation;
|
||||||
|
|
||||||
|
use App\Dto\Builder\Documentation;
|
||||||
|
use App\Dto\Service\Pages;
|
||||||
|
|
||||||
|
final readonly class Index extends Pages
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private Documentation $documentation,
|
||||||
|
int $page
|
||||||
|
) {
|
||||||
|
parent::__construct($page);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDocumentation(): Documentation
|
||||||
|
{
|
||||||
|
return $this->documentation;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Admin\Project\Documentation;
|
||||||
|
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationContent\Contents;
|
||||||
|
use App\Dto\Service\Dto;
|
||||||
|
|
||||||
|
final readonly class StoreUpdate extends Dto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private string $slug,
|
||||||
|
private bool $isPublic,
|
||||||
|
private int $sort,
|
||||||
|
private Contents $contents,
|
||||||
|
private ?int $categoryId,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getSlug(): string
|
||||||
|
{
|
||||||
|
return $this->slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isPublic(): bool
|
||||||
|
{
|
||||||
|
return $this->isPublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCategoryId(): ?int
|
||||||
|
{
|
||||||
|
return $this->categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSort(): int
|
||||||
|
{
|
||||||
|
return $this->sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContents(): Contents
|
||||||
|
{
|
||||||
|
return $this->contents;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Admin\Project\DocumentationCategory;
|
||||||
|
|
||||||
|
use App\Dto\Builder\DocumentationCategory;
|
||||||
|
use App\Dto\Service\Pages;
|
||||||
|
|
||||||
|
final readonly class Index extends Pages
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private DocumentationCategory $documentationCategory,
|
||||||
|
int $page
|
||||||
|
) {
|
||||||
|
parent::__construct($page);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDocumentationCategory(): DocumentationCategory
|
||||||
|
{
|
||||||
|
return $this->documentationCategory;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Admin\Project\DocumentationCategory;
|
||||||
|
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationCategoryContent\Contents;
|
||||||
|
use App\Dto\Service\Dto;
|
||||||
|
|
||||||
|
final readonly class StoreUpdate extends Dto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private string $slug,
|
||||||
|
private bool $isPublic,
|
||||||
|
private int $sort,
|
||||||
|
private Contents $contents,
|
||||||
|
private ?int $parentId,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getSlug(): string
|
||||||
|
{
|
||||||
|
return $this->slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isPublic(): bool
|
||||||
|
{
|
||||||
|
return $this->isPublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSort(): int
|
||||||
|
{
|
||||||
|
return $this->sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParentId(): ?int
|
||||||
|
{
|
||||||
|
return $this->parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContents(): Contents
|
||||||
|
{
|
||||||
|
return $this->contents;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Admin\Project\DocumentationCategoryContent;
|
||||||
|
|
||||||
|
final readonly class Content
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private int $languageId,
|
||||||
|
private string $title,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getLanguageId(): int
|
||||||
|
{
|
||||||
|
return $this->languageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Admin\Project\DocumentationCategoryContent;
|
||||||
|
|
||||||
|
final class Contents
|
||||||
|
{
|
||||||
|
private array $contents = [];
|
||||||
|
|
||||||
|
public function addContent(Content $content): void
|
||||||
|
{
|
||||||
|
$this->contents[$content->getLanguageId()] = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent(int $languageId): ?Content
|
||||||
|
{
|
||||||
|
return $this->contents[$languageId] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContents(): array
|
||||||
|
{
|
||||||
|
return $this->contents;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Admin\Project\DocumentationContent;
|
||||||
|
|
||||||
|
use App\Dto\Service\Storage\Storages;
|
||||||
|
|
||||||
|
final readonly class Content
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private int $languageId,
|
||||||
|
private string $title,
|
||||||
|
private string $content,
|
||||||
|
private Storages $storages,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getLanguageId(): int
|
||||||
|
{
|
||||||
|
return $this->languageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent(): string
|
||||||
|
{
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStorages(): Storages
|
||||||
|
{
|
||||||
|
return $this->storages;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Admin\Project\DocumentationContent;
|
||||||
|
|
||||||
|
final class Contents
|
||||||
|
{
|
||||||
|
private array $contents = [];
|
||||||
|
|
||||||
|
public function addContent(Content $content): void
|
||||||
|
{
|
||||||
|
$this->contents[$content->getLanguageId()] = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent(int $languageId): ?Content
|
||||||
|
{
|
||||||
|
return $this->contents[$languageId] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContents(): array
|
||||||
|
{
|
||||||
|
return $this->contents;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Admin\Project\DocumentationVersion;
|
||||||
|
|
||||||
|
use App\Dto\Builder\DocumentationVersion;
|
||||||
|
use App\Dto\Service\Pages;
|
||||||
|
|
||||||
|
final readonly class Index extends Pages
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private DocumentationVersion $documentationVersionBuildDto,
|
||||||
|
int $page
|
||||||
|
) {
|
||||||
|
parent::__construct($page);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDocumentationVersionBuildDto(): DocumentationVersion
|
||||||
|
{
|
||||||
|
return $this->documentationVersionBuildDto;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Admin\Project\DocumentationVersion;
|
||||||
|
|
||||||
|
use App\Dto\Service\Dto;
|
||||||
|
use App\Enums\DocumentationVersionStatus;
|
||||||
|
|
||||||
|
final readonly class StoreUpdate extends Dto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private string $title,
|
||||||
|
private string $slug,
|
||||||
|
private bool $isPublic,
|
||||||
|
private DocumentationVersionStatus $status,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSlug(): string
|
||||||
|
{
|
||||||
|
return $this->slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isPublic(): bool
|
||||||
|
{
|
||||||
|
return $this->isPublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatus(): DocumentationVersionStatus
|
||||||
|
{
|
||||||
|
return $this->status;
|
||||||
|
}
|
||||||
|
}
|
@ -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 @@ public function getTranslations(): array
|
|||||||
|
|
||||||
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 @@ public static function getTranslationCodes(): array
|
|||||||
'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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
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
|
||||||
{
|
{
|
||||||
|
46
app/application/app/Enums/DocumentationVersionStatus.php
Normal file
46
app/application/app/Enums/DocumentationVersionStatus.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
use App\Services\WebsiteTranslations;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
enum DocumentationVersionStatus: int
|
||||||
|
{
|
||||||
|
case NotSupported = 0;
|
||||||
|
case Supported = 50;
|
||||||
|
case CurrentVersion = 100;
|
||||||
|
case FutureVersion = 150;
|
||||||
|
|
||||||
|
public function getTitle(?WebsiteTranslations $websiteTranslations = null): string
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
$items = [];
|
||||||
|
foreach (self::cases() as $item) {
|
||||||
|
$items[] = [
|
||||||
|
'name' => $item->name,
|
||||||
|
'value' => $item->value,
|
||||||
|
'title' => $item->getTitle(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function toCollection(): Collection
|
||||||
|
{
|
||||||
|
return collect(self::toArray());
|
||||||
|
}
|
||||||
|
}
|
@ -2,16 +2,22 @@
|
|||||||
|
|
||||||
namespace App\Enums;
|
namespace App\Enums;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@ enum Permission: string
|
|||||||
case ProjectLink = 'project-link';
|
case ProjectLink = 'project-link';
|
||||||
case ProjectTranslation = 'project-translation';
|
case ProjectTranslation = 'project-translation';
|
||||||
case ProjectFeedback = 'project-feedback';
|
case ProjectFeedback = 'project-feedback';
|
||||||
|
case Documentation = 'documentation';
|
||||||
|
case DocumentationCategory = 'documentation-category';
|
||||||
|
|
||||||
public function getPermissions(): array
|
public function getPermissions(): array
|
||||||
{
|
{
|
||||||
|
@ -5,15 +5,18 @@
|
|||||||
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 @@ public function url(Project $project, ?ProjectLanguage $language = null): string
|
|||||||
$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,39 +9,43 @@
|
|||||||
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'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAcceptMimes(): array
|
public function getAcceptMimes(): array
|
||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
self::Logo => ['jpeg', 'jpg', 'png'],
|
self::Logo => ['jpeg', 'jpg', 'png'],
|
||||||
|
self::ContentImages => ['jpeg', 'jpg', 'png'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isImage(): bool
|
public function isImage(): bool
|
||||||
{
|
{
|
||||||
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,8 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exceptions\Services\DocumentationCategory;
|
||||||
|
|
||||||
|
final class ParentException extends \Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exceptions\Services\DocumentationCategoryContent;
|
||||||
|
|
||||||
|
final class ContentSaveException extends \Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exceptions\Services\DocumentationContent;
|
||||||
|
|
||||||
|
final class ContentSaveException extends \Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
19
app/application/app/Exceptions/Services/ServiceException.php
Normal file
19
app/application/app/Exceptions/Services/ServiceException.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exceptions\Services;
|
||||||
|
|
||||||
|
use App\Contracts\ServiceResultError as ServiceResultErrorContract;
|
||||||
|
|
||||||
|
final class ServiceException extends \Exception
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ServiceResultErrorContract $serviceResultError
|
||||||
|
) {
|
||||||
|
parent::__construct($this->serviceResultError->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getServiceResultError(): ServiceResultErrorContract
|
||||||
|
{
|
||||||
|
return $this->serviceResultError;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin\Projects;
|
||||||
|
|
||||||
|
use App\Dto\QuerySettingsDto;
|
||||||
|
use App\Http\Controllers\Admin\Controller;
|
||||||
|
use App\Http\Requests\Admin\Projects\DocumentationCategories\IndexRequest;
|
||||||
|
use App\Http\Requests\Admin\Projects\DocumentationCategories\StoreUpdateRequest;
|
||||||
|
use App\Services\Admin\Project\DocumentationCategoryService;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
final class DocumentationCategoriesController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DocumentationCategoryService $documentationCategoryService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function index(int $projectId, int $versionId, IndexRequest $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$data = $request->getDto();
|
||||||
|
$querySettingsDto = new QuerySettingsDto(
|
||||||
|
limit: 20,
|
||||||
|
page: $data->getPage(),
|
||||||
|
queryWith: []
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = $this->documentationCategoryService->index($projectId, $versionId, $data->getDocumentationCategory(), $querySettingsDto, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/documentation-categories/index', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(int $projectId, int $versionId, Request $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationCategoryService->create($projectId, $versionId, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/documentation-categories/create', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(int $projectId, int $versionId, int $id, Request $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationCategoryService->edit($projectId, $versionId, $id, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/documentation-categories/edit', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(int $projectId, int $versionId, StoreUpdateRequest $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$data = $request->getDto();
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationCategoryService->store($projectId, $versionId, $data, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('admin.projects.documentation-versions.categories.edit', [
|
||||||
|
'project' => $projectId,
|
||||||
|
'version' => $versionId,
|
||||||
|
'category' => $result->getModel()->id,
|
||||||
|
])->withSuccess($result->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(int $projectId, int $versionId, int $id, StoreUpdateRequest $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$data = $request->getDto();
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationCategoryService->update($projectId, $versionId, $id, $data, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('admin.projects.documentation-versions.categories.edit', [
|
||||||
|
'project' => $projectId,
|
||||||
|
'version' => $versionId,
|
||||||
|
'category' => $result->getModel()->id,
|
||||||
|
])->withSuccess($result->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(int $projectId, int $versionId, int $id, Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationCategoryService->destroy($projectId, $versionId, $id, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('admin.projects.documentation-versions.categories.index', [
|
||||||
|
'project' => $projectId,
|
||||||
|
'version' => $versionId,
|
||||||
|
])->withSuccess($result->getMessage());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin\Projects;
|
||||||
|
|
||||||
|
use App\Dto\QuerySettingsDto;
|
||||||
|
use App\Http\Controllers\Admin\Controller;
|
||||||
|
use App\Http\Requests\Admin\Projects\DocumentVersions\IndexRequest;
|
||||||
|
use App\Http\Requests\Admin\Projects\DocumentVersions\StoreUpdateRequest;
|
||||||
|
use App\Services\Admin\Project\DocumentationVersionService;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
final class DocumentationVersionController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DocumentationVersionService $documentationVersionService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function index(int $projectId, IndexRequest $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$data = $request->getDto();
|
||||||
|
$querySettingsDto = new QuerySettingsDto(
|
||||||
|
limit: 20,
|
||||||
|
page: $data->getPage(),
|
||||||
|
queryWith: []
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = $this->documentationVersionService->index($projectId, $data->getDocumentationVersionBuildDto(), $querySettingsDto, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/documentation-versions/index', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show(int $projectId, int $id, Request $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationVersionService->show($projectId, $id, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/documentation-versions/show', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(int $projectId, Request $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationVersionService->create($projectId, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/documentation-versions/create', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(int $projectId, int $id, Request $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationVersionService->edit($projectId, $id, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/documentation-versions/edit', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(int $projectId, StoreUpdateRequest $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$data = $request->getDto();
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationVersionService->store($projectId, $data, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('admin.projects.documentation-versions.edit', [
|
||||||
|
'project' => $projectId,
|
||||||
|
'documentation_version' => $result->getModel()->id,
|
||||||
|
])->withSuccess($result->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(int $projectId, int $id, StoreUpdateRequest $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$data = $request->getDto();
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationVersionService->update($projectId, $id, $data, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('admin.projects.documentation-versions.edit', [
|
||||||
|
'project' => $projectId,
|
||||||
|
'documentation_version' => $result->getModel()->id,
|
||||||
|
])->withSuccess($result->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(int $projectId, int $id, Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationVersionService->destroy($projectId, $id, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('admin.projects.documentation-versions.index', ['project' => $projectId])->withSuccess($result->getMessage());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin\Projects;
|
||||||
|
|
||||||
|
use App\Dto\QuerySettingsDto;
|
||||||
|
use App\Http\Controllers\Admin\Controller;
|
||||||
|
use App\Http\Requests\Admin\Projects\Documentations\IndexRequest;
|
||||||
|
use App\Http\Requests\Admin\Projects\Documentations\StoreUpdateRequest;
|
||||||
|
use App\Services\Admin\Project\DocumentationService;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
final class DocumentationsController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DocumentationService $documentationService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function index(int $projectId, int $versionId, IndexRequest $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$data = $request->getDto();
|
||||||
|
$querySettingsDto = new QuerySettingsDto(
|
||||||
|
limit: 20,
|
||||||
|
page: $data->getPage(),
|
||||||
|
queryWith: []
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = $this->documentationService->index($projectId, $versionId, $data->getDocumentation(), $querySettingsDto, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/documentations/index', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(int $projectId, int $versionId, Request $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationService->create($projectId, $versionId, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/documentations/create', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(int $projectId, int $versionId, int $id, Request $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationService->edit($projectId, $versionId, $id, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/documentations/edit', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(int $projectId, int $versionId, StoreUpdateRequest $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$data = $request->getDto();
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationService->store($projectId, $versionId, $data, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('admin.projects.documentation-versions.documentations.edit', [
|
||||||
|
'project' => $projectId,
|
||||||
|
'version' => $versionId,
|
||||||
|
'documentation' => $result->getModel()->id,
|
||||||
|
])->withSuccess($result->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(int $projectId, int $versionId, int $id, StoreUpdateRequest $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$data = $request->getDto();
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationService->update($projectId, $versionId, $id, $data, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('admin.projects.documentation-versions.documentations.edit', [
|
||||||
|
'project' => $projectId,
|
||||||
|
'version' => $versionId,
|
||||||
|
'documentation' => $result->getModel()->id,
|
||||||
|
])->withSuccess($result->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(int $projectId, int $versionId, int $id, Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->documentationService->destroy($projectId, $versionId, $id, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('admin.projects.documentation-versions.documentations.index', [
|
||||||
|
'project' => $projectId,
|
||||||
|
'version' => $versionId,
|
||||||
|
])->withSuccess($result->getMessage());
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,9 @@ public function __construct(
|
|||||||
|
|
||||||
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());
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,6 @@
|
|||||||
namespace App\Http\Controllers\Site;
|
namespace App\Http\Controllers\Site;
|
||||||
|
|
||||||
use App\Http\Requests\Site\Feedback\SendRequest;
|
use App\Http\Requests\Site\Feedback\SendRequest;
|
||||||
use App\Models\ProjectFeedback;
|
|
||||||
use App\Services\Site\FeedbackService;
|
use App\Services\Site\FeedbackService;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -20,6 +19,7 @@ public function index(Request $request): View
|
|||||||
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),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ public function index(Request $request): View
|
|||||||
{
|
{
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
$project = $request->get('project');
|
$project = $request->get('project');
|
||||||
$websiteTranslations = $request->get('websiteTranslations');
|
|
||||||
|
|
||||||
if (\is_null($project)) {
|
if (\is_null($project)) {
|
||||||
$with = ['storage'];
|
$with = ['storage'];
|
||||||
@ -28,7 +27,8 @@ public function index(Request $request): View
|
|||||||
return \view('site.projects.index', $result->getData());
|
return \view('site.projects.index', $result->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $this->projectService->getAboutByProject($project, $websiteTranslations, $request->user());
|
$websiteTranslations = $request->get('websiteTranslations');
|
||||||
|
$result = $this->projectService->getAboutByProject($project, $websiteTranslations, $user);
|
||||||
if ($result->isError()) {
|
if ($result->isError()) {
|
||||||
$this->errors($result);
|
$this->errors($result);
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
32
app/application/app/Http/Middleware/IsProject.php
Normal file
32
app/application/app/Http/Middleware/IsProject.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Models\Project;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
final class IsProject
|
||||||
|
{
|
||||||
|
public function handle(Request $request, \Closure $next): Response
|
||||||
|
{
|
||||||
|
$project = $request->get('project');
|
||||||
|
if (\is_null($project)) {
|
||||||
|
\abort(Response::HTTP_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($project instanceof Project === false) {
|
||||||
|
\report("$project must be an instance of Project");
|
||||||
|
\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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Services\WebsiteTranslations;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
final class IsWebsiteTranslations
|
||||||
|
{
|
||||||
|
public function handle(Request $request, \Closure $next): Response
|
||||||
|
{
|
||||||
|
$websiteTranslations = $request->get('websiteTranslations');
|
||||||
|
if (\is_null($websiteTranslations)) {
|
||||||
|
\abort(Response::HTTP_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($websiteTranslations instanceof WebsiteTranslations === false) {
|
||||||
|
\report("$websiteTranslations must be an instance of WebsiteTranslations");
|
||||||
|
\abort(Response::HTTP_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Closure;
|
use Closure;
|
||||||
|
|
||||||
class ProjectAndLanguage extends ProjectLanguage
|
final class ProjectAndLanguage extends ProjectLanguage
|
||||||
{
|
{
|
||||||
public function handle(Request $request, Closure $next): Response
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
|
@ -4,10 +4,19 @@
|
|||||||
|
|
||||||
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,7 @@ public function rules(): array
|
|||||||
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'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +34,18 @@ public function getDto(): StoreUpdate
|
|||||||
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(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function storages(): Storages
|
||||||
|
{
|
||||||
|
$storages = new Storages();
|
||||||
|
|
||||||
|
$content = $this->input('storage', []);
|
||||||
|
$images = $content['content_images'] ?? [];
|
||||||
|
$storages->addMany($images, StorageType::ContentImages);
|
||||||
|
|
||||||
|
return $storages;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Admin\Projects\DocumentVersions;
|
||||||
|
|
||||||
|
use App\Contracts\FormRequestDto;
|
||||||
|
use App\Dto\Builder\DocumentationVersion;
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationVersion\Index;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
final class IndexRequest extends FormRequest implements FormRequestDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'page' => ['nullable', 'numeric', 'min:1']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDto(): Index
|
||||||
|
{
|
||||||
|
return new Index(
|
||||||
|
documentationVersionBuildDto: new DocumentationVersion(),
|
||||||
|
page: (int) $this->input('page', 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Admin\Projects\DocumentVersions;
|
||||||
|
|
||||||
|
use App\Contracts\FormRequestDto;
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationVersion\StoreUpdate;
|
||||||
|
use App\Enums\DocumentationVersionStatus;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rules\Enum;
|
||||||
|
|
||||||
|
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'title' => ['required', 'string', 'max:255'],
|
||||||
|
'slug' => ['required', 'string', 'max:70', 'regex:/^[a-z0-9._-]+$/'],
|
||||||
|
'is_public' => ['required', 'boolean'],
|
||||||
|
'status' => ['required', new Enum(DocumentationVersionStatus::class)],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDto(): StoreUpdate
|
||||||
|
{
|
||||||
|
return new StoreUpdate(
|
||||||
|
title: $this->input('title'),
|
||||||
|
slug: $this->input('slug'),
|
||||||
|
isPublic: (bool) $this->input('is_public', false),
|
||||||
|
status: DocumentationVersionStatus::from((int) $this->input('status')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Admin\Projects\DocumentationCategories;
|
||||||
|
|
||||||
|
use App\Contracts\FormRequestDto;
|
||||||
|
use App\Dto\Builder\DocumentationCategory;
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationCategory\Index;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
final class IndexRequest extends FormRequest implements FormRequestDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'page' => ['nullable', 'numeric', 'min:1']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDto(): Index
|
||||||
|
{
|
||||||
|
return new Index(
|
||||||
|
documentationCategory: new DocumentationCategory(),
|
||||||
|
page: (int) $this->input('page', 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Admin\Projects\DocumentationCategories;
|
||||||
|
|
||||||
|
use App\Contracts\FormRequestDto;
|
||||||
|
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 Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'slug' => ['required', 'string', 'max:200', 'regex:/^[a-z0-9._-]+$/'],
|
||||||
|
'is_public' => ['required', 'boolean'],
|
||||||
|
'sort' => ['required', 'integer', 'min:-1000', 'max:1000'],
|
||||||
|
'parent_id' => ['nullable', 'integer', 'exists:documentation_categories,id'],
|
||||||
|
'content.*.title' => ['required', 'string', 'max:255'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDto(): StoreUpdate
|
||||||
|
{
|
||||||
|
$parentId = $this->input('parent_id', null);
|
||||||
|
if (!\is_null($parentId)) {
|
||||||
|
$parentId = (int) $parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StoreUpdate(
|
||||||
|
slug: $this->input('slug'),
|
||||||
|
isPublic: (bool) $this->input('is_public', false),
|
||||||
|
sort: (int) $this->input('sort'),
|
||||||
|
contents: $this->getContents(),
|
||||||
|
parentId: $parentId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getContents(): Contents
|
||||||
|
{
|
||||||
|
$contents = new Contents();
|
||||||
|
foreach ($this->input('content', []) as $languageId => $content) {
|
||||||
|
$contents->addContent(new Content(
|
||||||
|
languageId: (int) $languageId,
|
||||||
|
title: $content['title'],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return $contents;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Admin\Projects\Documentations;
|
||||||
|
|
||||||
|
use App\Contracts\FormRequestDto;
|
||||||
|
use App\Dto\Builder\Documentation;
|
||||||
|
use App\Dto\Service\Admin\Project\Documentation\Index;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
final class IndexRequest extends FormRequest implements FormRequestDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'page' => ['nullable', 'numeric', 'min:1']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDto(): Index
|
||||||
|
{
|
||||||
|
return new Index(
|
||||||
|
documentation: new Documentation(),
|
||||||
|
page: (int) $this->input('page', 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Admin\Projects\Documentations;
|
||||||
|
|
||||||
|
use App\Contracts\FormRequestDto;
|
||||||
|
use App\Dto\Service\Admin\Project\Documentation\StoreUpdate;
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationContent\Content;
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationContent\Contents;
|
||||||
|
use App\Dto\Service\Storage\Storages;
|
||||||
|
use App\Enums\StorageType;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||||
|
{
|
||||||
|
public function attributes(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'content.*.content_images.*.file' => __('validation.attributes.content_images'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'slug' => ['required', 'string', 'max:200', 'regex:/^[a-z0-9._-]+$/'],
|
||||||
|
'is_public' => ['required', 'boolean'],
|
||||||
|
'sort' => ['required', 'integer', 'min:-1000', 'max:1000'],
|
||||||
|
'category_id' => ['nullable', 'integer', 'exists:documentation_categories,id'],
|
||||||
|
'content.*.title' => ['required', 'string', 'max:255'],
|
||||||
|
'content.*.content' => ['nullable', 'string'],
|
||||||
|
'content.*.content_images.*.file' => ['numeric', 'min:1'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDto(): StoreUpdate
|
||||||
|
{
|
||||||
|
$categoryId = $this->input('category_id', null);
|
||||||
|
if (!\is_null($categoryId)) {
|
||||||
|
$categoryId = (int) $categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StoreUpdate(
|
||||||
|
slug: $this->input('slug'),
|
||||||
|
isPublic: (bool) $this->input('is_public', false),
|
||||||
|
sort: (int) $this->input('sort'),
|
||||||
|
contents: $this->getContents(),
|
||||||
|
categoryId: $categoryId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getContents(): Contents
|
||||||
|
{
|
||||||
|
$contents = new Contents();
|
||||||
|
foreach ($this->input('content', []) as $languageId => $content) {
|
||||||
|
$contents->addContent(new Content(
|
||||||
|
languageId: (int) $languageId,
|
||||||
|
title: $content['title'],
|
||||||
|
content: $content['content'] ?? '',
|
||||||
|
storages: $this->contentStorages($content),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return $contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function contentStorages(array $content): Storages
|
||||||
|
{
|
||||||
|
$storages = new Storages();
|
||||||
|
|
||||||
|
$images = $content['content_images'] ?? [];
|
||||||
|
$storages->addMany($images, StorageType::ContentImages);
|
||||||
|
|
||||||
|
return $storages;
|
||||||
|
}
|
||||||
|
}
|
@ -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 @@ public function attributes(): array
|
|||||||
*/
|
*/
|
||||||
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\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 @@ public function getDto(): Upload
|
|||||||
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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
68
app/application/app/Models/Documentation.php
Normal file
68
app/application/app/Models/Documentation.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Models\Scopes\SortScope;
|
||||||
|
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
#[ScopedBy([SortScope::class])]
|
||||||
|
final class Documentation extends Model
|
||||||
|
{
|
||||||
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'documentation';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model's default values for attributes.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $attributes = [
|
||||||
|
'sort' => 100,
|
||||||
|
'is_public' => true,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'slug',
|
||||||
|
'is_public',
|
||||||
|
'category_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the attributes that should be cast.
|
||||||
|
*
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'is_public' => 'boolean',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function category(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(DocumentationCategory::class, 'category_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contents(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(DocumentationContent::class, 'documentation_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function content(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(DocumentationContent::class, 'documentation_id', 'id');
|
||||||
|
}
|
||||||
|
}
|
75
app/application/app/Models/DocumentationCategory.php
Normal file
75
app/application/app/Models/DocumentationCategory.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Models\Scopes\SortScope;
|
||||||
|
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;
|
||||||
|
|
||||||
|
#[ScopedBy([SortScope::class])]
|
||||||
|
final class DocumentationCategory extends Model
|
||||||
|
{
|
||||||
|
use HasFactory, SoftDeletes, HasRecursiveRelationships;
|
||||||
|
|
||||||
|
protected $table = 'documentation_categories';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model's default values for attributes.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $attributes = [
|
||||||
|
'sort' => 100,
|
||||||
|
'is_public' => true,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'slug',
|
||||||
|
'sort',
|
||||||
|
'parent_id',
|
||||||
|
'is_public',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the attributes that should be cast.
|
||||||
|
*
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'is_public' => 'boolean',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parent(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(DocumentationCategory::class, 'parent_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function contents(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(DocumentationCategoryContent::class, 'category_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function content(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(DocumentationCategoryContent::class, 'category_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function version(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(DocumentationVersion::class, 'version_id');
|
||||||
|
}
|
||||||
|
}
|
24
app/application/app/Models/DocumentationCategoryContent.php
Normal file
24
app/application/app/Models/DocumentationCategoryContent.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;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
final class DocumentationCategoryContent extends Model
|
||||||
|
{
|
||||||
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'documentation_category_content';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'title',
|
||||||
|
'language_id',
|
||||||
|
];
|
||||||
|
}
|
27
app/application/app/Models/DocumentationContent.php
Normal file
27
app/application/app/Models/DocumentationContent.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Contracts\Models\Storage as StorageContract;
|
||||||
|
use App\Models\Traits\StorageTrait;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
final class DocumentationContent extends Model implements StorageContract
|
||||||
|
{
|
||||||
|
use HasFactory, SoftDeletes, StorageTrait;
|
||||||
|
|
||||||
|
protected $table = 'documentation_content';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'title',
|
||||||
|
'content',
|
||||||
|
'language_id',
|
||||||
|
];
|
||||||
|
}
|
78
app/application/app/Models/DocumentationVersion.php
Normal file
78
app/application/app/Models/DocumentationVersion.php
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Enums\DocumentationVersionStatus;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
final class DocumentationVersion extends Model
|
||||||
|
{
|
||||||
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'documentation_versions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model's default values for attributes.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $attributes = [
|
||||||
|
'is_public' => true,
|
||||||
|
'status' => DocumentationVersionStatus::CurrentVersion,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'title',
|
||||||
|
'slug',
|
||||||
|
'is_public',
|
||||||
|
'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.
|
||||||
|
*
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'is_public' => 'boolean',
|
||||||
|
'status' => DocumentationVersionStatus::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function project(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Project::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function documentations(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Documentation::class, 'version_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function categories(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(DocumentationCategory::class, 'version_id');
|
||||||
|
}
|
||||||
|
}
|
@ -62,4 +62,9 @@ public function feedbacks(): HasMany
|
|||||||
{
|
{
|
||||||
return $this->hasMany(ProjectFeedback::class);
|
return $this->hasMany(ProjectFeedback::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function documentationVersions(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(DocumentationVersion::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 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';
|
||||||
|
|
||||||
|
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 [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
34
app/application/app/Policies/DocumentationCategoryPolicy.php
Normal file
34
app/application/app/Policies/DocumentationCategoryPolicy.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\DocumentationCategory;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
final readonly class DocumentationCategoryPolicy extends Policy
|
||||||
|
{
|
||||||
|
public function viewAny(User $user): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('documentation-category.view');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view(User $user, DocumentationCategory $category): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('documentation-category.view');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(User $user): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('documentation-category.create');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(User $user, DocumentationCategory $category): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('documentation.update');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(User $user, DocumentationCategory $category): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('documentation-category.delete');
|
||||||
|
}
|
||||||
|
}
|
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;
|
||||||
|
}
|
||||||
|
}
|
34
app/application/app/Policies/DocumentationPolicy.php
Normal file
34
app/application/app/Policies/DocumentationPolicy.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\Documentation;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
final readonly class DocumentationPolicy extends Policy
|
||||||
|
{
|
||||||
|
public function viewAny(User $user): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('documentation.view');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view(User $user, Documentation $documentation): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('documentation.view');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(User $user): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('documentation.create');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(User $user, Documentation $documentation): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('documentation.update');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(User $user, Documentation $documentation): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('documentation.delete');
|
||||||
|
}
|
||||||
|
}
|
39
app/application/app/Policies/DocumentationVersionPolicy.php
Normal file
39
app/application/app/Policies/DocumentationVersionPolicy.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
final readonly class DocumentationVersionPolicy extends Policy
|
||||||
|
{
|
||||||
|
public function viewAny(User $user): bool
|
||||||
|
{
|
||||||
|
// Not a mistake or typo. Shared rights with Documentation.
|
||||||
|
return $user->hasPermission('documentation.view');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view(User $user, DocumentationVersion $documentationVersion): bool
|
||||||
|
{
|
||||||
|
// Not a mistake or typo. Shared rights with Documentation.
|
||||||
|
return $user->hasPermission('documentation.view');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(User $user): bool
|
||||||
|
{
|
||||||
|
// Not a mistake or typo. Shared rights with Documentation.
|
||||||
|
return $user->hasPermission('documentation.create');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(User $user, DocumentationVersion $documentationVersion): bool
|
||||||
|
{
|
||||||
|
// Not a mistake or typo. Shared rights with Documentation.
|
||||||
|
return $user->hasPermission('documentation.update');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(User $user, DocumentationVersion $documentationVersion): bool
|
||||||
|
{
|
||||||
|
// Not a mistake or typo. Shared rights with Documentation.
|
||||||
|
return $user->hasPermission('documentation.delete');
|
||||||
|
}
|
||||||
|
}
|
@ -26,4 +26,13 @@ public function update(User $user, ProjectContent $projectContent): bool
|
|||||||
{
|
{
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 @@ public function register(): void
|
|||||||
});
|
});
|
||||||
|
|
||||||
$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 @@ public function boot(): void
|
|||||||
|
|
||||||
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']);
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Repositories;
|
||||||
|
|
||||||
|
use App\Contracts\Search;
|
||||||
|
use App\Models\DocumentationCategory;
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
|
use App\Models\ProjectLanguage;
|
||||||
|
use App\Services\DocumentationCategory\BuilderCommand;
|
||||||
|
use App\Services\Search\CreateSearchInstanceCommand;
|
||||||
|
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
final readonly class DocumentationCategoryRepository
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||||
|
private BuilderCommand $builderCommand
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getCategories(int $versionId, DocumentationCategoryBuilderDto $documentationCategoryBuilderDto, array $with = []): Search
|
||||||
|
{
|
||||||
|
$query = $this->builderCommand->execute(
|
||||||
|
query: DocumentationCategory::query()->where('version_id', $versionId)->with($with),
|
||||||
|
documentationCategoryBuilderDto: $documentationCategoryBuilderDto
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->createSearchInstanceCommand->execute($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCategoryById(int $id): ?DocumentationCategory
|
||||||
|
{
|
||||||
|
return DocumentationCategory::query()->where('id', $id)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCategoryBySlugWithContent(string $slug, int $versionId, ProjectLanguage $language): ?DocumentationCategory
|
||||||
|
{
|
||||||
|
$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
|
||||||
|
{
|
||||||
|
return DocumentationCategory::query()
|
||||||
|
->where('version_id', $versionId)
|
||||||
|
->where('slug', Str::lower($slug))
|
||||||
|
->when($exceptId, function (Builder $query, int $exceptId) {
|
||||||
|
$query->where('id', '!=', $exceptId);
|
||||||
|
})
|
||||||
|
->withTrashed()
|
||||||
|
->exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getForSelect(DocumentationVersion $version, ?ProjectLanguage $defaultLanguage, ?DocumentationCategory $exceptCategory = null, array $withExcepts = []): array
|
||||||
|
{
|
||||||
|
$with = [
|
||||||
|
'content' => function (HasOne $hasOne) use ($defaultLanguage) {
|
||||||
|
$hasOne->when($defaultLanguage, function (Builder $query, ProjectLanguage $defaultLanguage) {
|
||||||
|
$query->where('language_id', $defaultLanguage->id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
$categories = $version->categories()
|
||||||
|
->with($with)
|
||||||
|
->when($exceptCategory, function (Builder $query, DocumentationCategory $exceptCategory) {
|
||||||
|
$query->whereNotIn(
|
||||||
|
'id',
|
||||||
|
$exceptCategory->descendantsAndSelf()->pluck('id')->toArray()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
->when($withExcepts, function (Builder $query) use ($withExcepts) {
|
||||||
|
$query->withTrashed()->whereNull('deleted_at')->orWhereIn('id', $withExcepts);
|
||||||
|
})->get();
|
||||||
|
|
||||||
|
return $categories->map(function (DocumentationCategory $documentationCategory) use ($defaultLanguage) {
|
||||||
|
return [
|
||||||
|
'id' => $documentationCategory->id,
|
||||||
|
'title' => $documentationCategory->content?->title ?? $documentationCategory->slug,
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->pluck('title', 'id')
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
}
|
63
app/application/app/Repositories/DocumentationRepository.php
Normal file
63
app/application/app/Repositories/DocumentationRepository.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Repositories;
|
||||||
|
|
||||||
|
use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
||||||
|
use App\Contracts\Search;
|
||||||
|
use App\Models\Documentation;
|
||||||
|
use App\Models\ProjectLanguage;
|
||||||
|
use App\Services\Documentation\BuilderCommand;
|
||||||
|
use App\Services\Search\CreateSearchInstanceCommand;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
final readonly class DocumentationRepository
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||||
|
private BuilderCommand $builderCommand
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getDocumentations(int $versionId, DocumentationBuilderDto $documentationBuilderDto, array $with = []): Search
|
||||||
|
{
|
||||||
|
$query = $this->builderCommand->execute(
|
||||||
|
query: Documentation::query()->where('version_id', $versionId)->with($with),
|
||||||
|
documentationBuilderDto: $documentationBuilderDto
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->createSearchInstanceCommand->execute($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDocumentationById(int $id): ?Documentation
|
||||||
|
{
|
||||||
|
return Documentation::query()->where('id', $id)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDocumentationBySlugWithContent(string $slug, int $versionId, ProjectLanguage $language): ?Documentation
|
||||||
|
{
|
||||||
|
$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
|
||||||
|
{
|
||||||
|
return Documentation::query()
|
||||||
|
->where('version_id', $versionId)
|
||||||
|
->where('slug', Str::lower($slug))
|
||||||
|
->when($exceptId, function (Builder $query, int $exceptId) {
|
||||||
|
$query->where('id', '!=', $exceptId);
|
||||||
|
})
|
||||||
|
->withTrashed()
|
||||||
|
->exists();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Repositories;
|
||||||
|
|
||||||
|
use App\Contracts\Search;
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
|
use App\Services\DocumentationVersion\BuilderCommand;
|
||||||
|
use App\Services\Search\CreateSearchInstanceCommand;
|
||||||
|
use App\Dto\Builder\DocumentationVersion as DocumentationVersionBuilderDto;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
final readonly class DocumentationVersionRepository
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private CreateSearchInstanceCommand $createSearchInstanceCommand,
|
||||||
|
private BuilderCommand $builderCommand
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getVersions(int $projectId, DocumentationVersionBuilderDto $documentationVersionBuilderDto, array $with = []): Search
|
||||||
|
{
|
||||||
|
$query = $this->builderCommand->execute(
|
||||||
|
query: DocumentationVersion::query()->where('project_id', $projectId)->with($with),
|
||||||
|
documentationVersionBuilderDto: $documentationVersionBuilderDto
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->createSearchInstanceCommand->execute($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVersionById(int $id): ?DocumentationVersion
|
||||||
|
{
|
||||||
|
return DocumentationVersion::query()->where('id', $id)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVersionByCode(string $code): ?DocumentationVersion
|
||||||
|
{
|
||||||
|
return DocumentationVersion::query()->where('code', $code)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isExistsSlug(int $projectId, string $slug, ?int $exceptId = null): bool
|
||||||
|
{
|
||||||
|
return DocumentationVersion::query()
|
||||||
|
->where('project_id', $projectId)
|
||||||
|
->where('slug', Str::lower($slug))
|
||||||
|
->when($exceptId, function (Builder $query, int $exceptId) {
|
||||||
|
$query->where('id', '!=', $exceptId);
|
||||||
|
})
|
||||||
|
->withTrashed()
|
||||||
|
->exists();
|
||||||
|
}
|
||||||
|
}
|
@ -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,7 @@
|
|||||||
|
|
||||||
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\Models\ProjectContent;
|
use App\Models\ProjectContent;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Repositories\ProjectContentRepository;
|
use App\Repositories\ProjectContentRepository;
|
||||||
@ -13,6 +14,7 @@
|
|||||||
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 +24,7 @@ public function __construct(
|
|||||||
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
|
||||||
@ -88,11 +91,19 @@ private function store(int $projectId, int $languageId, StoreUpdate $data, User
|
|||||||
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;
|
||||||
});
|
});
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
@ -108,11 +119,19 @@ private function update(ProjectContent $content, StoreUpdate $data, User $user):
|
|||||||
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;
|
||||||
});
|
});
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
|
@ -0,0 +1,257 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Admin\Project;
|
||||||
|
|
||||||
|
use App\Dto\QuerySettingsDto;
|
||||||
|
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationCategory\StoreUpdate;
|
||||||
|
use App\Exceptions\Services\DocumentationCategory\ParentException;
|
||||||
|
use App\Exceptions\Services\ServiceException;
|
||||||
|
use App\Models\DocumentationCategory;
|
||||||
|
use App\Models\DocumentationCategoryContent;
|
||||||
|
use App\Models\ProjectLanguage;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Repositories\DocumentationCategoryRepository;
|
||||||
|
use App\Repositories\DocumentationVersionRepository;
|
||||||
|
use App\ServiceResults\ServiceResultArray;
|
||||||
|
use App\ServiceResults\ServiceResultError;
|
||||||
|
use App\ServiceResults\ServiceResultSuccess;
|
||||||
|
use App\ServiceResults\StoreUpdateResult;
|
||||||
|
use App\Services\DocumentationCategory\DocumentationCategoryCommandHandler;
|
||||||
|
use App\Services\DocumentationCategoryContent\ModelSyncCommand;
|
||||||
|
use App\Services\Service;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
final class DocumentationCategoryService extends Service
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DocumentationVersionRepository $documentationVersionRepository,
|
||||||
|
private readonly DocumentationCategoryRepository $documentationCategoryRepository,
|
||||||
|
private readonly DocumentationCategoryCommandHandler $categoryCommandHandler,
|
||||||
|
private readonly ModelSyncCommand $contentSaveCommand,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function index(int $projectId, int $versionId, DocumentationCategoryBuilderDto $documentationCategoryBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('viewAny', DocumentationCategory::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$defaultLanguage = $project->languages()->where('is_default', 1)->first();
|
||||||
|
$with = [
|
||||||
|
'content' => function (HasOne $hasOne) use ($defaultLanguage) {
|
||||||
|
/** @var ?ProjectLanguage $defaultLanguage */
|
||||||
|
$hasOne->when($defaultLanguage, function (Builder $query, ProjectLanguage $defaultLanguage) {
|
||||||
|
$query->where('language_id', $defaultLanguage->id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
||||||
|
$with = array_merge($with, $querySettingsDto->getQueryWith());
|
||||||
|
|
||||||
|
$categories = $this->documentationCategoryRepository->getCategories(
|
||||||
|
$version->id,
|
||||||
|
$documentationCategoryBuilderDto,
|
||||||
|
$with
|
||||||
|
)->pagination(
|
||||||
|
$querySettingsDto->getLimit(),
|
||||||
|
$querySettingsDto->getPage()
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->result([
|
||||||
|
'version' => $version,
|
||||||
|
'project' => $project,
|
||||||
|
'categories' => $categories,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(int $projectId, int $versionId, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('create', DocumentationCategory::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$defaultLanguage = $project->languages->where('is_default', 1)->first();
|
||||||
|
return $this->result([
|
||||||
|
'version' => $version,
|
||||||
|
'project' => $project,
|
||||||
|
'category' => new DocumentationCategory(),
|
||||||
|
'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(int $projectId, int $versionId, int $categoryId, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$category = $this->documentationCategoryRepository->getCategoryById($categoryId);
|
||||||
|
if (\is_null($category)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('view', $category)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$withCategories = [];
|
||||||
|
if ($category->parent_id) {
|
||||||
|
$withCategories[] = $category->parent_id;
|
||||||
|
}
|
||||||
|
$defaultLanguage = $project->languages->where('is_default', 1)->first();
|
||||||
|
return $this->result([
|
||||||
|
'version' => $version,
|
||||||
|
'project' => $project,
|
||||||
|
'category' => $category,
|
||||||
|
'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage, $category, $withCategories),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(int $projectId, int $versionId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||||
|
{
|
||||||
|
if ($user->cannot('create', DocumentationCategory::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$category = DB::transaction(function () use ($data, $version, $user, $versionId, $project) {
|
||||||
|
if ($this->documentationCategoryRepository->isExistsSlug($versionId, $data->getSlug()) !== false) {
|
||||||
|
$error = $this->errValidate(
|
||||||
|
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||||
|
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||||
|
);
|
||||||
|
throw new ServiceException($error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dataCategory = $this->getDataCategory($data);
|
||||||
|
$category = $this->categoryCommandHandler->handleStore($version, $dataCategory);
|
||||||
|
$this->contentSaveCommand->execute($project, $category, $data->getContents());
|
||||||
|
|
||||||
|
return $category;
|
||||||
|
});
|
||||||
|
} catch (ServiceException $e) {
|
||||||
|
return $e->getServiceResultError();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resultStoreUpdateModel($category, __('Category successfully created'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(int $projectId, int $versionId, int $categoryId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||||
|
{
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$category = $this->documentationCategoryRepository->getCategoryById($categoryId);
|
||||||
|
if (\is_null($category)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('view', $category)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($data->getParentId() === $category->id) {
|
||||||
|
return $this->errValidate(
|
||||||
|
__('validation.parent', ['attribute' => __('validation.attributes.parent_id')]),
|
||||||
|
['parent_id' => __('validation.parent', ['attribute' => __('validation.attributes.parent_id')])]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$category = DB::transaction(function () use ($data, $category, $versionId, $project) {
|
||||||
|
if ($this->documentationCategoryRepository->isExistsSlug($versionId, $data->getSlug(), $category->id) !== false) {
|
||||||
|
$error = $this->errValidate(
|
||||||
|
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||||
|
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||||
|
);
|
||||||
|
throw new ServiceException($error);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dataCategory = $this->getDataCategory($data);
|
||||||
|
$category = $this->categoryCommandHandler->handleUpdate($category, $dataCategory);
|
||||||
|
$this->contentSaveCommand->execute($project, $category, $data->getContents());
|
||||||
|
|
||||||
|
return $category;
|
||||||
|
});
|
||||||
|
} catch (ServiceException $e) {
|
||||||
|
return $e->getServiceResultError();
|
||||||
|
} catch (ParentException $e) {
|
||||||
|
return $this->errValidate(
|
||||||
|
__('validation.parent_cycle_detected', ['attribute' => __('validation.attributes.parent_id')]),
|
||||||
|
['parent_id' => __('validation.parent_cycle_detected', ['attribute' => __('validation.attributes.parent_id')])]
|
||||||
|
);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resultStoreUpdateModel($category, __('Category updated successfully'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(int $projectId, int $versionId, int $categoryId, User $user): ServiceResultError|ServiceResultSuccess
|
||||||
|
{
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$category = $this->documentationCategoryRepository->getCategoryById($categoryId);
|
||||||
|
if (\is_null($category)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('delete', $category)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::transaction(function () use ($category) {
|
||||||
|
$this->categoryCommandHandler->handleDestroy($category);
|
||||||
|
});
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->ok(__('Category successfully deleted'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDataCategory(StoreUpdate $data): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'slug' => $data->getSlug(),
|
||||||
|
'is_public' => $data->isPublic(),
|
||||||
|
'sort' => $data->getSort(),
|
||||||
|
'parent_id' => $data->getParentId(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,243 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Admin\Project;
|
||||||
|
|
||||||
|
use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
||||||
|
use App\Dto\QuerySettingsDto;
|
||||||
|
use App\Dto\Service\Admin\Project\Documentation\StoreUpdate;
|
||||||
|
use App\Exceptions\Services\DocumentationContent\StorageCommandException;
|
||||||
|
use App\Models\Documentation;
|
||||||
|
use App\Models\ProjectLanguage;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Repositories\DocumentationCategoryRepository;
|
||||||
|
use App\Repositories\DocumentationRepository;
|
||||||
|
use App\Repositories\DocumentationVersionRepository;
|
||||||
|
use App\ServiceResults\ServiceResultArray;
|
||||||
|
use App\ServiceResults\ServiceResultError;
|
||||||
|
use App\ServiceResults\ServiceResultSuccess;
|
||||||
|
use App\ServiceResults\StoreUpdateResult;
|
||||||
|
use App\Services\Documentation\DocumentationCommandHandler;
|
||||||
|
use App\Services\DocumentationContent\ModelSyncCommand;
|
||||||
|
use App\Services\Service;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
final class DocumentationService extends Service
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DocumentationVersionRepository $documentationVersionRepository,
|
||||||
|
private readonly DocumentationCategoryRepository $documentationCategoryRepository,
|
||||||
|
private readonly DocumentationRepository $documentationRepository,
|
||||||
|
private readonly DocumentationCommandHandler $documentationCommandHandler,
|
||||||
|
private readonly ModelSyncCommand $contentSaveCommand,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function index(int $projectId, int $versionId, DocumentationBuilderDto $documentationBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('viewAny', Documentation::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$defaultLanguage = $project->languages()->where('is_default', 1)->first();
|
||||||
|
$with = [
|
||||||
|
'content' => function (HasOne $hasOne) use ($defaultLanguage) {
|
||||||
|
/** @var ?ProjectLanguage $defaultLanguage */
|
||||||
|
$hasOne->when($defaultLanguage, function (Builder $query, ProjectLanguage $defaultLanguage) {
|
||||||
|
$query->where('language_id', $defaultLanguage->id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
||||||
|
$with = array_merge($with, $querySettingsDto->getQueryWith());
|
||||||
|
|
||||||
|
$documentations = $this->documentationRepository->getDocumentations(
|
||||||
|
$version->id,
|
||||||
|
$documentationBuilderDto,
|
||||||
|
$with
|
||||||
|
)->pagination(
|
||||||
|
$querySettingsDto->getLimit(),
|
||||||
|
$querySettingsDto->getPage()
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->result([
|
||||||
|
'version' => $version,
|
||||||
|
'project' => $project,
|
||||||
|
'documentations' => $documentations,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(int $projectId, int $versionId, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('create', Documentation::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$defaultLanguage = $project->languages->where('is_default', 1)->first();
|
||||||
|
return $this->result([
|
||||||
|
'version' => $version,
|
||||||
|
'project' => $project,
|
||||||
|
'documentation' => new Documentation(),
|
||||||
|
'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(int $projectId, int $versionId, int $documentationId, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$documentation = $this->documentationRepository->getDocumentationById($documentationId);
|
||||||
|
if (\is_null($documentation)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('view', $documentation)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$withCategories = [];
|
||||||
|
if ($documentation->category_id) {
|
||||||
|
$withCategories[] = $documentation->category_id;
|
||||||
|
}
|
||||||
|
$defaultLanguage = $project->languages->where('is_default', 1)->first();
|
||||||
|
return $this->result([
|
||||||
|
'version' => $version,
|
||||||
|
'project' => $project,
|
||||||
|
'documentation' => $documentation,
|
||||||
|
'categories' => $this->documentationCategoryRepository->getForSelect($version, $defaultLanguage, null, $withCategories),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(int $projectId, int $versionId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||||
|
{
|
||||||
|
if ($user->cannot('create', Documentation::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->documentationRepository->isExistsSlug($versionId, $data->getSlug()) !== false) {
|
||||||
|
return $this->errValidate(
|
||||||
|
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||||
|
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$documentation = DB::transaction(function () use ($data, $version, $user, $project) {
|
||||||
|
$dataDocumentation = $this->getDataDocumentation($data);
|
||||||
|
$documentation = $this->documentationCommandHandler->handleStore($version, $dataDocumentation);
|
||||||
|
$this->contentSaveCommand->execute($project, $documentation, $data->getContents());
|
||||||
|
|
||||||
|
return $documentation;
|
||||||
|
});
|
||||||
|
} catch (StorageCommandException $e) {
|
||||||
|
return $e->getResultError();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resultStoreUpdateModel($documentation, __('Documentation created successfully'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(int $projectId, int $versionId, int $documentationId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||||
|
{
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$documentation = $this->documentationRepository->getDocumentationById($documentationId);
|
||||||
|
if (\is_null($documentation)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('view', $documentation)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->documentationRepository->isExistsSlug($versionId, $data->getSlug(), $documentation->id) !== false) {
|
||||||
|
return $this->errValidate(
|
||||||
|
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||||
|
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$documentation = DB::transaction(function () use ($data, $documentation, $project) {
|
||||||
|
$dataDocumentation = $this->getDataDocumentation($data);
|
||||||
|
$documentation = $this->documentationCommandHandler->handleUpdate($documentation, $dataDocumentation);
|
||||||
|
$this->contentSaveCommand->execute($project, $documentation, $data->getContents());
|
||||||
|
|
||||||
|
return $documentation;
|
||||||
|
});
|
||||||
|
} catch (StorageCommandException $e) {
|
||||||
|
return $e->getResultError();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resultStoreUpdateModel($documentation, __('Documentation updated successfully'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(int $projectId, int $versionId, int $documentationId, User $user): ServiceResultError|ServiceResultSuccess
|
||||||
|
{
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
$project = $version?->project;
|
||||||
|
if (\is_null($version) || $project?->id !== $projectId) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$documentation = $this->documentationRepository->getDocumentationById($documentationId);
|
||||||
|
if (\is_null($documentation)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('delete', $documentation)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::transaction(function () use ($documentation) {
|
||||||
|
$this->documentationCommandHandler->handleDestroy($documentation);
|
||||||
|
});
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->ok(__('Documentation successfully removed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDataDocumentation(StoreUpdate $data): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'slug' => $data->getSlug(),
|
||||||
|
'is_public' => $data->isPublic(),
|
||||||
|
'sort' => $data->getSort(),
|
||||||
|
'category_id' => $data->getCategoryId(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,228 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Admin\Project;
|
||||||
|
|
||||||
|
use App\Dto\QuerySettingsDto;
|
||||||
|
use App\Dto\Builder\DocumentationVersion as DocumentationVersionBuilderDto;
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationVersion\StoreUpdate;
|
||||||
|
use App\Enums\CacheTag;
|
||||||
|
use App\Enums\DocumentationVersionStatus;
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Repositories\DocumentationVersionRepository;
|
||||||
|
use App\Repositories\ProjectRepository;
|
||||||
|
use App\ServiceResults\ServiceResultArray;
|
||||||
|
use App\ServiceResults\ServiceResultError;
|
||||||
|
use App\ServiceResults\ServiceResultSuccess;
|
||||||
|
use App\ServiceResults\StoreUpdateResult;
|
||||||
|
use App\Services\ClearCacheCommandHandler;
|
||||||
|
use App\Services\DocumentationVersion\DocumentationVersionCommandHandler;
|
||||||
|
use App\Services\Service;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
final class DocumentationVersionService extends Service
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ProjectRepository $projectRepository,
|
||||||
|
private readonly DocumentationVersionRepository $documentationVersionRepository,
|
||||||
|
private readonly DocumentationVersionCommandHandler $documentationVersionCommandHandler,
|
||||||
|
private readonly ClearCacheCommandHandler $clearCacheCommandHandler,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function index(int $projectId, DocumentationVersionBuilderDto $documentationVersionBuilderDto, QuerySettingsDto $querySettingsDto, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$project = $this->projectRepository->getProjectById($projectId);
|
||||||
|
if (\is_null($project)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('viewAny', DocumentationVersion::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$versions = $this->documentationVersionRepository->getVersions(
|
||||||
|
$project->id,
|
||||||
|
$documentationVersionBuilderDto,
|
||||||
|
$querySettingsDto->getQueryWith()
|
||||||
|
)->pagination(
|
||||||
|
$querySettingsDto->getLimit(),
|
||||||
|
$querySettingsDto->getPage()
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->result([
|
||||||
|
'versions' => $versions,
|
||||||
|
'project' => $project,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show(int $projectId, int $versionId, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$project = $this->projectRepository->getProjectById($projectId);
|
||||||
|
if (\is_null($project)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
if (\is_null($version)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('view', $version)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->result([
|
||||||
|
'version' => $version,
|
||||||
|
'project' => $project,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(int $projectId, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$project = $this->projectRepository->getProjectById($projectId);
|
||||||
|
if (\is_null($project)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('create', DocumentationVersion::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->result([
|
||||||
|
'version' => new DocumentationVersion(),
|
||||||
|
'statuses' => DocumentationVersionStatus::toCollection()->pluck( 'title', 'value')->toArray(),
|
||||||
|
'project' => $project,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(int $projectId, int $versionId, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$project = $this->projectRepository->getProjectById($projectId);
|
||||||
|
if (\is_null($project)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
if (\is_null($version)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('view', $version)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->result([
|
||||||
|
'version' => $version,
|
||||||
|
'statuses' => DocumentationVersionStatus::toCollection()->pluck( 'title', 'value')->toArray(),
|
||||||
|
'project' => $project,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(int $projectId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||||
|
{
|
||||||
|
if ($user->cannot('create', DocumentationVersion::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$project = $this->projectRepository->getProjectById($projectId);
|
||||||
|
if (\is_null($project)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->documentationVersionRepository->isExistsSlug($projectId, $data->getSlug()) !== false) {
|
||||||
|
return $this->errValidate(
|
||||||
|
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||||
|
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$version = DB::transaction(function () use ($data, $project, $user) {
|
||||||
|
$dataVersion = $this->getDataVersion($data);
|
||||||
|
return $this->documentationVersionCommandHandler->handleStore($project, $dataVersion);
|
||||||
|
});
|
||||||
|
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resultStoreUpdateModel($version, __('Documentation version created successfully'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(int $projectId, int $versionId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||||
|
{
|
||||||
|
$project = $this->projectRepository->getProjectById($projectId);
|
||||||
|
if (\is_null($project)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
if (\is_null($version)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('update', $version)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->documentationVersionRepository->isExistsSlug($projectId, $data->getSlug(), $version->id) !== false) {
|
||||||
|
return $this->errValidate(
|
||||||
|
__('validation.unique', ['attribute' => __('validation.attributes.slug')]),
|
||||||
|
['slug' => __('validation.unique', ['attribute' => __('validation.attributes.slug')])]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$version = DB::transaction(function () use ($data, $version) {
|
||||||
|
$dataVersion = $this->getDataVersion($data);
|
||||||
|
return $this->documentationVersionCommandHandler->handleUpdate($version, $dataVersion);
|
||||||
|
});
|
||||||
|
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resultStoreUpdateModel($version, __('Documentation version updated successfully'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(int $projectId, int $versionId, User $user): ServiceResultError|ServiceResultSuccess
|
||||||
|
{
|
||||||
|
$project = $this->projectRepository->getProjectById($projectId);
|
||||||
|
if (\is_null($project)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = $this->documentationVersionRepository->getVersionById($versionId);
|
||||||
|
if (\is_null($version)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('delete', $version)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::transaction(function () use ($version) {
|
||||||
|
$this->documentationVersionCommandHandler->handleDestroy($version);
|
||||||
|
});
|
||||||
|
$this->clearCacheCommandHandler->byTag(CacheTag::DocumantationVersion);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->ok(__('Documentation version successfully removed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDataVersion(StoreUpdate $data): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'title' => $data->getTitle(),
|
||||||
|
'slug' => $data->getSlug(),
|
||||||
|
'is_public' => $data->isPublic(),
|
||||||
|
'status' => $data->getStatus(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -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.'));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Documentation;
|
||||||
|
|
||||||
|
use App\Dto\Builder\Documentation as DocumentationBuilderDto;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||||
|
|
||||||
|
final readonly class BuilderCommand
|
||||||
|
{
|
||||||
|
public function execute(Relation | Builder $query, DocumentationBuilderDto $documentationBuilderDto): Relation | Builder
|
||||||
|
{
|
||||||
|
return $query
|
||||||
|
->when($documentationBuilderDto->isPublic(), function (Builder $query) use ($documentationBuilderDto) {
|
||||||
|
$query->where('is_public', $documentationBuilderDto->isPublic());
|
||||||
|
})
|
||||||
|
->when($documentationBuilderDto->getCategoryId(), function (Builder $query) use ($documentationBuilderDto) {
|
||||||
|
$query->where('category_id', $documentationBuilderDto->getCategoryId()->getCategoryId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Documentation;
|
||||||
|
|
||||||
|
use App\Models\Documentation;
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
final readonly class DocumentationCommandHandler
|
||||||
|
{
|
||||||
|
public function handleStore(DocumentationVersion $version, array $data): Documentation
|
||||||
|
{
|
||||||
|
$data['slug'] = Str::lower($data['slug']);
|
||||||
|
return $version->documentations()->create($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleUpdate(Documentation $documentation, array $data): Documentation
|
||||||
|
{
|
||||||
|
if (isset($data['slug'])) {
|
||||||
|
$data['slug'] = Str::lower($data['slug']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$documentation->update($data);
|
||||||
|
$documentation->touch();
|
||||||
|
|
||||||
|
return $documentation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleDestroy(Documentation $documentation): void
|
||||||
|
{
|
||||||
|
$documentation->update([
|
||||||
|
'slug' => $documentation->slug . '#delete:' . $documentation->id,
|
||||||
|
]);
|
||||||
|
$documentation->delete();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\DocumentationCategory;
|
||||||
|
|
||||||
|
use App\Dto\Builder\DocumentationCategory as DocumentationCategoryBuilderDto;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||||
|
|
||||||
|
final readonly class BuilderCommand
|
||||||
|
{
|
||||||
|
public function execute(Relation | Builder $query, DocumentationCategoryBuilderDto $documentationCategoryBuilderDto): Relation | Builder
|
||||||
|
{
|
||||||
|
return $query
|
||||||
|
->when($documentationCategoryBuilderDto->isPublic(), function (Builder $query) use ($documentationCategoryBuilderDto) {
|
||||||
|
$query->where('is_public', $documentationCategoryBuilderDto->isPublic());
|
||||||
|
})
|
||||||
|
->when($documentationCategoryBuilderDto->getParentId(), function (Builder $query) use ($documentationCategoryBuilderDto) {
|
||||||
|
$query->where('parent_id', $documentationCategoryBuilderDto->getParentId()->getCategoryId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\DocumentationCategory;
|
||||||
|
|
||||||
|
use App\Exceptions\Services\DocumentationCategory\ParentException;
|
||||||
|
use App\Models\DocumentationCategory;
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
final readonly class DocumentationCategoryCommandHandler
|
||||||
|
{
|
||||||
|
public function handleStore(DocumentationVersion $version, array $data): DocumentationCategory
|
||||||
|
{
|
||||||
|
$data['slug'] = Str::lower($data['slug']);
|
||||||
|
return $version->categories()->create($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleUpdate(DocumentationCategory $category, array $data): DocumentationCategory
|
||||||
|
{
|
||||||
|
if (!empty($data['parent_id'])) {
|
||||||
|
$category->parent_id = $data['parent_id'];
|
||||||
|
if ($category->ancestors()->withTrashed()->where('id', $category->id)->exists()) {
|
||||||
|
throw new ParentException('Category ID occurs in the parent chain (category: ' . $category->id . ', parent_id: ' . $category->parent_id . ').');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data['slug'])) {
|
||||||
|
$data['slug'] = Str::lower($data['slug']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$category->update($data);
|
||||||
|
$category->touch();
|
||||||
|
|
||||||
|
return $category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleDestroy(DocumentationCategory $category): void
|
||||||
|
{
|
||||||
|
$category->update([
|
||||||
|
'slug' => $category->slug . '#delete:' . $category->id,
|
||||||
|
]);
|
||||||
|
$category->delete();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\DocumentationCategoryContent;
|
||||||
|
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationCategoryContent\Content;
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationCategoryContent\Contents;
|
||||||
|
use App\Exceptions\Services\DocumentationCategoryContent\ContentSaveException;
|
||||||
|
use App\Models\DocumentationCategory;
|
||||||
|
use App\Models\Project;
|
||||||
|
|
||||||
|
final readonly class ModelSyncCommand
|
||||||
|
{
|
||||||
|
public function execute(Project $project, DocumentationCategory $category, Contents $contents): void
|
||||||
|
{
|
||||||
|
$languages = $project->languages;
|
||||||
|
$categoryContents = $category->contents;
|
||||||
|
|
||||||
|
$newContents = [];
|
||||||
|
foreach ($contents->getContents() as $content) {
|
||||||
|
/** @var Content $content */
|
||||||
|
$language = $languages->firstWhere('id', $content->getLanguageId());
|
||||||
|
if (!$language) {
|
||||||
|
throw new ContentSaveException('Language not found: ' . $content->getLanguageId());
|
||||||
|
}
|
||||||
|
|
||||||
|
$model = $categoryContents->firstWhere('language_id', $language->id);
|
||||||
|
$data = $this->getData($content);
|
||||||
|
if (\is_null($model)) {
|
||||||
|
$newContents[] = array_merge(['language_id' => $content->getLanguageId()], $data);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$model->update($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($newContents)) {
|
||||||
|
$category->contents()->createMany($newContents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getData(Content $content): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'title' => $content->getTitle(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\DocumentationContent;
|
||||||
|
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationContent\Content;
|
||||||
|
use App\Dto\Service\Admin\Project\DocumentationContent\Contents;
|
||||||
|
use App\Dto\Service\DocumentationContent\StorageDto;
|
||||||
|
use App\Exceptions\Services\DocumentationContent\ContentSaveException;
|
||||||
|
use App\Exceptions\Services\DocumentationContent\StorageCommandException;
|
||||||
|
use App\Models\Documentation;
|
||||||
|
use App\Models\DocumentationContent;
|
||||||
|
use App\Models\Project;
|
||||||
|
|
||||||
|
final readonly class ModelSyncCommand
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private StorageCommand $storageCommand,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws StorageCommandException
|
||||||
|
* @throws ContentSaveException
|
||||||
|
*/
|
||||||
|
public function execute(Project $project, Documentation $documentation, Contents $contents): void
|
||||||
|
{
|
||||||
|
$storageDto = new StorageDto();
|
||||||
|
|
||||||
|
$languages = $project->languages;
|
||||||
|
$documentationContents = $documentation->contents;
|
||||||
|
|
||||||
|
$newContents = [];
|
||||||
|
$contentsStorageCreated = [];
|
||||||
|
$contentLanguages = [];
|
||||||
|
foreach ($contents->getContents() as $content) {
|
||||||
|
/** @var Content $content */
|
||||||
|
$language = $languages->firstWhere('id', $content->getLanguageId());
|
||||||
|
if (!$language) {
|
||||||
|
throw new ContentSaveException('Language not found: ' . $content->getLanguageId());
|
||||||
|
}
|
||||||
|
|
||||||
|
$model = $documentationContents->firstWhere('language_id', $language->id);
|
||||||
|
$data = $this->getData($content);
|
||||||
|
if (\is_null($model)) {
|
||||||
|
$contentsStorageCreated[$content->getLanguageId()] = $content->getStorages();
|
||||||
|
$newContents[] = array_merge(['language_id' => $content->getLanguageId()], $data);
|
||||||
|
$contentLanguages[] = $content->getLanguageId();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$storageDto->add($model, $content->getStorages());
|
||||||
|
$model->update($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($newContents)) {
|
||||||
|
$documentation->contents()->createMany($newContents);
|
||||||
|
$contents = $documentation->contents()->whereIn('language_id', $contentLanguages)->get();
|
||||||
|
foreach ($contents as $content) {
|
||||||
|
/** @var DocumentationContent $content */
|
||||||
|
if (!isset($contentsStorageCreated[$content->language_id])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$storageDto->add($content, $contentsStorageCreated[$content->language_id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->storageCommand->execute($storageDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getData(Content $content): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'title' => $content->getTitle(),
|
||||||
|
'content' => $content->getContent(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -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,19 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\DocumentationVersion;
|
||||||
|
|
||||||
|
use App\Dto\Builder\DocumentationVersion as DocumentationVersionBuilderDto;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||||
|
|
||||||
|
final readonly class BuilderCommand
|
||||||
|
{
|
||||||
|
public function execute(Relation | Builder $query, DocumentationVersionBuilderDto $documentationVersionBuilderDto): Relation | Builder
|
||||||
|
{
|
||||||
|
if ($documentationVersionBuilderDto->isPublic() !== null) {
|
||||||
|
$query->where('is_public', $documentationVersionBuilderDto->isPublic());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\DocumentationVersion;
|
||||||
|
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
|
use App\Models\Project;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
final readonly class DocumentationVersionCommandHandler
|
||||||
|
{
|
||||||
|
public function handleStore(Project $project, array $data): DocumentationVersion
|
||||||
|
{
|
||||||
|
$data['slug'] = Str::lower($data['slug']);
|
||||||
|
|
||||||
|
return $project->documentationVersions()->create($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleUpdate(DocumentationVersion $version, array $data): DocumentationVersion
|
||||||
|
{
|
||||||
|
if (isset($data['slug'])) {
|
||||||
|
$data['slug'] = Str::lower($data['slug']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$version->update($data);
|
||||||
|
$version->touch();
|
||||||
|
|
||||||
|
return $version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleDestroy(DocumentationVersion $version): void
|
||||||
|
{
|
||||||
|
$version->update([
|
||||||
|
'slug' => $version->slug . '#delete:' . $version->id,
|
||||||
|
]);
|
||||||
|
$version->delete();
|
||||||
|
}
|
||||||
|
}
|
@ -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 @@
|
|||||||
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'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
48
app/application/app/View/Components/Site/ChooseVersion.php
Normal file
48
app/application/app/View/Components/Site/ChooseVersion.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\View\Components\Site;
|
||||||
|
|
||||||
|
use App\Enums\CacheTag;
|
||||||
|
use App\Models\DocumentationVersion;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\WebsiteTranslations;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
final class ChooseVersion extends Component
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly DocumentationVersion $version,
|
||||||
|
private readonly WebsiteTranslations $websiteTranslations,
|
||||||
|
private readonly Project $project,
|
||||||
|
private readonly ?User $user,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
$isPublic = null;
|
||||||
|
if (\is_null($this->user) || $this->user->cannot('viewAny', DocumentationVersion::class)) {
|
||||||
|
$isPublic = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$seconds = 3600 * 12;
|
||||||
|
$versions = CacheTag::DocumantationVersion->getCache()
|
||||||
|
->remember(self::class . $this->project->id . '-' . $isPublic ?? 0, $seconds, function () use ($isPublic) {
|
||||||
|
return $this->project->documentationVersions()
|
||||||
|
->when($isPublic, function (Builder $query) {
|
||||||
|
$query->where('is_public', 1);
|
||||||
|
})
|
||||||
|
->get();
|
||||||
|
});
|
||||||
|
|
||||||
|
return view('components.site.choose-version', [
|
||||||
|
'websiteTranslations' => $this->websiteTranslations,
|
||||||
|
'versions' => $versions,
|
||||||
|
'version' => $this->version,
|
||||||
|
'project' => $this->project,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\View\Components\Site;
|
||||||
|
|
||||||
|
use App\Services\WebsiteTranslations;
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
final class DocumentationVersion extends Component
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly \App\Models\DocumentationVersion $version,
|
||||||
|
private readonly WebsiteTranslations $websiteTranslations,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('components.site.documentation-version', [
|
||||||
|
'websiteTranslations' => $this->websiteTranslations,
|
||||||
|
'version' => $this->version,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -27,12 +27,12 @@ private function getTitle(): string
|
|||||||
|
|
||||||
private function getCheckboxValue(): string
|
private function getCheckboxValue(): string
|
||||||
{
|
{
|
||||||
return (string) old($this->getRequestName(), $this->checkboxValue);
|
return (string) $this->checkboxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getUserValue(): string
|
private function getUserValue(): string
|
||||||
{
|
{
|
||||||
return (string) $this->userValue;
|
return (string) old($this->getRequestName(), $this->userValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getNotCheckedValue(): ?string
|
private function getNotCheckedValue(): ?string
|
||||||
|
@ -13,6 +13,7 @@ public function __construct(
|
|||||||
private readonly string $type = 'text',
|
private readonly string $type = 'text',
|
||||||
private readonly ?string $value = '',
|
private readonly ?string $value = '',
|
||||||
private readonly ?string $example = null,
|
private readonly ?string $example = null,
|
||||||
|
private readonly ?string $allowedCharacters = null,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
protected function getName(): string
|
protected function getName(): string
|
||||||
@ -40,6 +41,11 @@ private function getExample(): ?string
|
|||||||
return $this->example;
|
return $this->example;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAllowedCharacters(): ?string
|
||||||
|
{
|
||||||
|
return $this->allowedCharacters;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@ -52,6 +58,7 @@ public function render(): View
|
|||||||
'type' => $this->getType(),
|
'type' => $this->getType(),
|
||||||
'value' => $this->getValue(),
|
'value' => $this->getValue(),
|
||||||
'example' => $this->getExample(),
|
'example' => $this->getExample(),
|
||||||
|
'allowedCharacters' => $this->getAllowedCharacters(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ protected function getName(): string
|
|||||||
private function getValue(): array
|
private function getValue(): array
|
||||||
{
|
{
|
||||||
$value = old($this->getRequestName(), null);
|
$value = old($this->getRequestName(), null);
|
||||||
if (\is_null($value)) {
|
if (\is_null($value) || !isset($value['items']) || !\is_array($value['items'])) {
|
||||||
return $this->value;
|
return $this->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,15 +2,17 @@
|
|||||||
|
|
||||||
namespace App\View\Components\Volt\Forms;
|
namespace App\View\Components\Volt\Forms;
|
||||||
|
|
||||||
|
use App\Dto\View\Volt\Form\WysiwygStorageUpload;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
final class TextareaWysiwyg extends Form
|
final class TextareaWysiwyg extends Form
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly string $title,
|
private readonly string $title,
|
||||||
private readonly string $name,
|
private readonly string $name,
|
||||||
private readonly ?string $value = '',
|
private readonly ?string $value = '',
|
||||||
|
private readonly ?WysiwygStorageUpload $storageUpload = null,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
protected function getName(): string
|
protected function getName(): string
|
||||||
@ -28,6 +30,11 @@ private function getValue(): string
|
|||||||
return (string) old($this->getRequestName(), $this->value);
|
return (string) old($this->getRequestName(), $this->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getStorageUpload(): ?WysiwygStorageUpload
|
||||||
|
{
|
||||||
|
return $this->storageUpload;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@ -37,10 +44,11 @@ public function render(): View
|
|||||||
|
|
||||||
return view('components.volt.forms.textarea-wysiwyg', [
|
return view('components.volt.forms.textarea-wysiwyg', [
|
||||||
'tinymceLicenseKey' => $tinymceLicenseKey,
|
'tinymceLicenseKey' => $tinymceLicenseKey,
|
||||||
'title' => $this->getTitle(),
|
'title' => $this->getTitle(),
|
||||||
'name' => $this->getName(),
|
'name' => $this->getName(),
|
||||||
'requestName' => $this->getRequestName(),
|
'requestName' => $this->getRequestName(),
|
||||||
'value' => $this->getValue(),
|
'value' => $this->getValue(),
|
||||||
|
'storageUpload' => $this->getStorageUpload(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
"intervention/image-laravel": "^1.2",
|
"intervention/image-laravel": "^1.2",
|
||||||
"kor-elf/captcha-rule-for-laravel": "^1.0",
|
"kor-elf/captcha-rule-for-laravel": "^1.0",
|
||||||
"laravel/framework": "^11.0",
|
"laravel/framework": "^11.0",
|
||||||
"laravel/tinker": "^2.9"
|
"laravel/tinker": "^2.9",
|
||||||
|
"staudenmeir/laravel-adjacency-list": "^1.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"barryvdh/laravel-debugbar": "^3.13",
|
"barryvdh/laravel-debugbar": "^3.13",
|
||||||
|
170
app/application/composer.lock
generated
170
app/application/composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "5e3226c3090c81571c830e2c383433ab",
|
"content-hash": "f7873c3a33599ee1ce0bbce99af9db22",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@ -3381,6 +3381,174 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-11-08T05:53:05+00:00"
|
"time": "2023-11-08T05:53:05+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "staudenmeir/eloquent-has-many-deep-contracts",
|
||||||
|
"version": "v1.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts.git",
|
||||||
|
"reference": "bcbe1a921caad7201b324e297eddb696d4bd8647"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/staudenmeir/eloquent-has-many-deep-contracts/zipball/bcbe1a921caad7201b324e297eddb696d4bd8647",
|
||||||
|
"reference": "bcbe1a921caad7201b324e297eddb696d4bd8647",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/database": "^11.0",
|
||||||
|
"php": "^8.2"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Staudenmeir\\EloquentHasManyDeepContracts\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jonas Staudenmeir",
|
||||||
|
"email": "mail@jonas-staudenmeir.de"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Contracts for staudenmeir/eloquent-has-many-deep",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/issues",
|
||||||
|
"source": "https://github.com/staudenmeir/eloquent-has-many-deep-contracts/tree/v1.2"
|
||||||
|
},
|
||||||
|
"time": "2024-01-18T01:20:44+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "staudenmeir/laravel-adjacency-list",
|
||||||
|
"version": "v1.22",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/staudenmeir/laravel-adjacency-list.git",
|
||||||
|
"reference": "0ec695e5d4094434f4a7adb956ebd23e228970ea"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/staudenmeir/laravel-adjacency-list/zipball/0ec695e5d4094434f4a7adb956ebd23e228970ea",
|
||||||
|
"reference": "0ec695e5d4094434f4a7adb956ebd23e228970ea",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/database": "^11.0",
|
||||||
|
"php": "^8.2",
|
||||||
|
"staudenmeir/eloquent-has-many-deep-contracts": "^1.2",
|
||||||
|
"staudenmeir/laravel-cte": "^1.11"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"barryvdh/laravel-ide-helper": "^3.0",
|
||||||
|
"larastan/larastan": "^2.0",
|
||||||
|
"mockery/mockery": "^1.5.1",
|
||||||
|
"orchestra/testbench": "^9.0",
|
||||||
|
"phpunit/phpunit": "^10.5",
|
||||||
|
"singlestoredb/singlestoredb-laravel": "^1.5.4",
|
||||||
|
"staudenmeir/eloquent-has-many-deep": "^1.20"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"barryvdh/laravel-ide-helper": "Provide type hints for attributes and relations."
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Staudenmeir\\LaravelAdjacencyList\\IdeHelperServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Staudenmeir\\LaravelAdjacencyList\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jonas Staudenmeir",
|
||||||
|
"email": "mail@jonas-staudenmeir.de"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Recursive Laravel Eloquent relationships with CTEs",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/staudenmeir/laravel-adjacency-list/issues",
|
||||||
|
"source": "https://github.com/staudenmeir/laravel-adjacency-list/tree/v1.22"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://paypal.me/JonasStaudenmeir",
|
||||||
|
"type": "custom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-04-19T12:11:45+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "staudenmeir/laravel-cte",
|
||||||
|
"version": "v1.11",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/staudenmeir/laravel-cte.git",
|
||||||
|
"reference": "1e7063021febf9db7e47d8ea602f582b00d55da6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/staudenmeir/laravel-cte/zipball/1e7063021febf9db7e47d8ea602f582b00d55da6",
|
||||||
|
"reference": "1e7063021febf9db7e47d8ea602f582b00d55da6",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/database": "^11.0",
|
||||||
|
"php": "^8.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"orchestra/testbench": "^9.0",
|
||||||
|
"phpstan/phpstan": "^1.10",
|
||||||
|
"phpunit/phpunit": "^10.5"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Staudenmeir\\LaravelCte\\DatabaseServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Staudenmeir\\LaravelCte\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jonas Staudenmeir",
|
||||||
|
"email": "mail@jonas-staudenmeir.de"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Laravel queries with common table expressions",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/staudenmeir/laravel-cte/issues",
|
||||||
|
"source": "https://github.com/staudenmeir/laravel-cte/tree/v1.11"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://paypal.me/JonasStaudenmeir",
|
||||||
|
"type": "custom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-03-09T10:12:45+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/clock",
|
"name": "symfony/clock",
|
||||||
"version": "v7.0.5",
|
"version": "v7.0.5",
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user