diff --git a/composer.json b/composer.json index f7fbeb6..006d61a 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,6 @@ "php": ">=8.3", "ext-ctype": "*", "ext-iconv": "*", - "czproject/git-php": "^4.1", "doctrine/doctrine-bundle": "^2.9", "doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/orm": "^2.14", diff --git a/composer.lock b/composer.lock index d15a48c..757823e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,60 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0d9cbf5f6f95b13006484d60ccb4d67c", + "content-hash": "59e27d83488f4238ab779a64962a039a", "packages": [ - { - "name": "czproject/git-php", - "version": "v4.2.0", - "source": { - "type": "git", - "url": "https://github.com/czproject/git-php.git", - "reference": "e257f2c3b43fe8fef19ddb5727b604416b423107" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/czproject/git-php/zipball/e257f2c3b43fe8fef19ddb5727b604416b423107", - "reference": "e257f2c3b43fe8fef19ddb5727b604416b423107", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "require-dev": { - "nette/tester": "^2.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jan Pecha", - "email": "janpecha@email.cz" - } - ], - "description": "Library for work with Git repository in PHP.", - "keywords": [ - "git" - ], - "support": { - "issues": "https://github.com/czproject/git-php/issues", - "source": "https://github.com/czproject/git-php/tree/v4.2.0" - }, - "funding": [ - { - "url": "https://www.janpecha.cz/donate/git-php/", - "type": "other" - } - ], - "time": "2023-07-12T09:14:30+00:00" - }, { "name": "doctrine/cache", "version": "2.2.0", diff --git a/config/services.yaml b/config/services.yaml index bf61fd9..5268342 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -4,8 +4,6 @@ # Put parameters here that don't need to change on each machine where the app is deployed # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration parameters: - snipStorageType: 'db' # 'db' or 'git - gitStoragePath: '%kernel.project_dir%/var/snips' services: # default configuration for services in *this* file @@ -25,8 +23,3 @@ services: App\Service\LastRelease: arguments: - '%kernel.project_dir%/release.json' - - App\Service\SnipServiceFactory: - arguments: - $gitStoragePath: '%gitStoragePath%' - $storageType: '%snipStorageType%' \ No newline at end of file diff --git a/deploy.php b/deploy.php index 9860a13..14ac384 100644 --- a/deploy.php +++ b/deploy.php @@ -67,7 +67,7 @@ task('deployment:log', function () { //https://stackoverflow.com/questions/59686 $commitDate = $commit[1]; // $line = sprintf('%s %s branch="%s" hash="%s"', $date, $commitHashShort, $branch, $commitHash); - $projectUrlBase = 'https://git.loken.nl/ardent/AnimeRSS4'; + $projectUrlBase = 'https://git.loken.nl/ardent/Snips'; $array = [ 'branch' => $branch, 'branchUrl' => sprintf('%s/src/branch/%s', $projectUrlBase, $branch), diff --git a/migrations/Version20231220204107.php b/migrations/Version20231220204107.php new file mode 100644 index 0000000..cb91e17 --- /dev/null +++ b/migrations/Version20231220204107.php @@ -0,0 +1,35 @@ +addSql('ALTER TABLE snip ADD active_version_id BINARY(16) DEFAULT NULL COMMENT \'(DC2Type:ulid)\', DROP active_commit'); + $this->addSql('ALTER TABLE snip ADD CONSTRAINT FK_FEBD97966A1E45F3 FOREIGN KEY (active_version_id) REFERENCES snip_content (id)'); + $this->addSql('CREATE INDEX IDX_FEBD97966A1E45F3 ON snip (active_version_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE snip DROP FOREIGN KEY FK_FEBD97966A1E45F3'); + $this->addSql('DROP INDEX IDX_FEBD97966A1E45F3 ON snip'); + $this->addSql('ALTER TABLE snip ADD active_commit VARCHAR(255) DEFAULT NULL, DROP active_version_id'); + } +} diff --git a/src/Controller/SnipController.php b/src/Controller/SnipController.php index 4c2ad59..0bf9aab 100644 --- a/src/Controller/SnipController.php +++ b/src/Controller/SnipController.php @@ -49,11 +49,10 @@ class SnipController extends AbstractController $this->denyAccessUnlessGranted(SnipVoter::VIEW, $snip); $snipService = $this->snipServiceFactory->create($snip); - dump($snipService); + return $this->render('snip/single.html.twig', [ 'snip' => $snip, - 'content' => $pl->parse($snipService->get()), - 'branch' => $snipService->getCommit(), + 'content' => $pl->parse($snipService->getActiveText()), ]); } @@ -63,7 +62,7 @@ class SnipController extends AbstractController $this->denyAccessUnlessGranted(SnipVoter::VIEW, $snip); $response = new Response( - $pl->clean($this->snipServiceFactory->create($snip)->get()), + $pl->clean($this->snipServiceFactory->create($snip)->getActiveText()), Response::HTTP_OK, ['Content-Type' => 'text/html'] ); @@ -88,7 +87,7 @@ class SnipController extends AbstractController $form = $this->createForm(SnipType::class, $snip); $form->add('Save', SubmitType::class); if ($snip->getId()) { - $form->get('content')->setData($this->snipServiceFactory->create($snip)->get()); + $form->get('content')->setData($this->snipServiceFactory->create($snip)->getActiveText()); } $form->handleRequest($request); @@ -113,7 +112,7 @@ class SnipController extends AbstractController public function new(Request $request): Response { $snip = new Snip(); - $snip->setCreatedAtTodayNoSeconds() + $snip->setCreatedAtNow() ->setCreatedBy($this->getUser()); return $this->edit($snip, $request); diff --git a/src/Controller/HistoryController.php b/src/Controller/VersionController.php similarity index 69% rename from src/Controller/HistoryController.php rename to src/Controller/VersionController.php index 1a08bd3..046be9a 100644 --- a/src/Controller/HistoryController.php +++ b/src/Controller/VersionController.php @@ -3,14 +3,15 @@ namespace App\Controller; use App\Entity\Snip; +use App\Entity\SnipContent; use App\Security\Voter\SnipVoter; use App\Service\SnipServiceFactory; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; -#[Route('/history/{snip}', name: 'history')] -class HistoryController extends AbstractController +#[Route('/version/{snip}', name: 'version')] +class VersionController extends AbstractController { public function __construct( private readonly SnipServiceFactory $snipServiceFactory, @@ -20,22 +21,19 @@ class HistoryController extends AbstractController public function index(Snip $snip): Response { $this->denyAccessUnlessGranted(SnipVoter::EDIT, $snip); - - $snipService = $this->snipServiceFactory->create($snip); - return $this->render('history/index.html.twig', [ + + return $this->render('version/index.html.twig', [ 'snip' => $snip, - 'versions' => $snipService->getVersions(), - 'latestVersion' => $snipService->getLatestVersion(), ]); } #[Route('/set/{version}', name: '_set')] - public function set(Snip $snip, string $version): Response + public function set(Snip $snip, SnipContent $version): Response { $this->denyAccessUnlessGranted(SnipVoter::EDIT, $snip); $this->snipServiceFactory->create($snip)->setVersion($version); - $this->addFlash('success', 'Snip version set to ' . $version); + $this->addFlash('success', 'Snip version set to ' . $version->getId()); return $this->redirectToRoute('snip_single', ['snip' => $snip->getId()]); } } \ No newline at end of file diff --git a/src/Entity/Helpers/TrackedTrait.php b/src/Entity/Helpers/TrackedTrait.php index 1fdd5f6..6cdf453 100644 --- a/src/Entity/Helpers/TrackedTrait.php +++ b/src/Entity/Helpers/TrackedTrait.php @@ -40,10 +40,17 @@ trait TrackedTrait return $this; } - public function setCreatedAtTodayNoSeconds(): self + public function setCreatedAtNowNoSeconds(): self { $this->setCreatedAt(DateTime::createFromFormat('Y-m-d H:i', date('Y-m-d H:i'))); return $this; } + + public function setCreatedAtNow(): self + { + $this->setCreatedAt(new DateTime()); + + return $this; + } } \ No newline at end of file diff --git a/src/Entity/Snip.php b/src/Entity/Snip.php index 19824aa..bd4e99f 100644 --- a/src/Entity/Snip.php +++ b/src/Entity/Snip.php @@ -27,8 +27,8 @@ class Snip #[ORM\OneToMany(mappedBy: 'snip', targetEntity: SnipContent::class, orphanRemoval: true)] private Collection $snipContents; - #[ORM\Column(length: 255, nullable: true)] - private ?string $activeCommit = null; + #[ORM\ManyToOne] + private ?SnipContent $activeVersion = null; public function __construct() { @@ -99,14 +99,19 @@ class Snip return $this; } - public function getActiveCommit(): ?string + public function getLatestVersion(): ?SnipContent { - return $this->activeCommit; + return $this->snipContents->last(); } - public function setActiveCommit(?string $activeCommit): static + public function getActiveVersion(): ?SnipContent { - $this->activeCommit = $activeCommit; + return $this->activeVersion; + } + + public function setActiveVersion(?SnipContent $activeVersion): static + { + $this->activeVersion = $activeVersion; return $this; } diff --git a/src/Git/CustomGit.php b/src/Git/CustomGit.php deleted file mode 100644 index e7350e3..0000000 --- a/src/Git/CustomGit.php +++ /dev/null @@ -1,13 +0,0 @@ -runner); - } -} diff --git a/src/Git/CustomGitRepository.php b/src/Git/CustomGitRepository.php deleted file mode 100644 index 6b5585c..0000000 --- a/src/Git/CustomGitRepository.php +++ /dev/null @@ -1,33 +0,0 @@ - - * @throws \CzProject\GitPhp\GitException - */ - public function getAllCommits(): array - { - $result = $this->run('log', '--pretty=%H,%cI'); - - if (empty($result->getOutput())) { - return []; - } - - $commits = []; - foreach ($result->getOutput() as $line) { - $parts = explode(',', $line); - $commits[] = new SimpleCommit( - $parts[0], - new DateTime($parts[1]) - ); - } - - return $commits; - } -} \ No newline at end of file diff --git a/src/Git/SimpleCommit.php b/src/Git/SimpleCommit.php deleted file mode 100644 index 254c846..0000000 --- a/src/Git/SimpleCommit.php +++ /dev/null @@ -1,26 +0,0 @@ -hash; - } - - public function getDate(): DateTime - { - return $this->date; - } -} \ No newline at end of file diff --git a/src/Repository/.gitignore b/src/Repository/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/src/Service/SnipContent/SnipContentGit.php b/src/Service/SnipContent/SnipContentGit.php deleted file mode 100644 index 005ff3f..0000000 --- a/src/Service/SnipContent/SnipContentGit.php +++ /dev/null @@ -1,80 +0,0 @@ -getSnipPath()); - } - - private function getSnipPath(): string - { - return sprintf('%s/snip.txt', $this->repo->getRepositoryPath()); - } - - public function update(string $snipContents): void - { - if (!$this->user instanceof UserInterface) { - return; - } - if ($this->repo->getCurrentBranchName() !== self::MASTER_BRANCH_NAME) { - $this->repo->checkout(self::MASTER_BRANCH_NAME); - } - file_put_contents($this->getSnipPath(), $snipContents); - $this->repo->addFile(self::SNIP_FILE_NAME); - if ($this->repo->hasChanges()) { - $this->repo->commit(sprintf('Updated snip at %s by %s', date('Y-m-d H:i:s'), $this->user)); - } - } - - public function get(): string - { - if (!$this->snipExists()) { - return ''; - } - return file_get_contents($this->getSnipPath()); - } - - public function getVersions(): array - { - return array_map(fn(SimpleCommit $c) => [ - 'id' => $c->getHash(), - 'name' => $c->getDate()->format('Y-m-d H:i:s'), - ], $this->repo->getAllCommits()); - } - - public function setVersion(string $version): void - { - $this->repo->checkout($version); - } - - public function getCommit(): string - { - return $this->repo->getCurrentBranchName(); - } - - public function delete(): void - { - system("rm -rf " . escapeshellarg($this->repo->getRepositoryPath())); - } - - public function getLatestVersion(): string - { - return self::MASTER_BRANCH_NAME; - } -} \ No newline at end of file diff --git a/src/Service/SnipContent/SnipContentInterface.php b/src/Service/SnipContent/SnipContentInterface.php deleted file mode 100644 index f687eb5..0000000 --- a/src/Service/SnipContent/SnipContentInterface.php +++ /dev/null @@ -1,21 +0,0 @@ -em->persist($content); $this->em->flush(); - $this->snip->setActiveCommit($content->getId()); + $this->snip->setActiveVersion($content->getId()); $this->em->persist($this->snip); $this->em->flush(); } - public function get(): string + // Shortcut to get the active text + public function getActiveText(): string { $contentRepo = $this->em->getRepository(SnipContent::class); - return $contentRepo->find($this->snip->getActiveCommit())->getText(); + return $contentRepo->find($this->snip->getActiveVersion())->getText(); } - public function getVersions(): array + public function setVersion(SnipContent $version): void { - // Return all snipContent entities (by parent) - return array_map(fn(SnipContent $content) => [ - 'id' => (string)$content->getId(), - 'name' => $content->getId()->getDateTime()->format('Y-m-d H:i:s'), - ], $this->snip->getSnipContents()->toArray()); - } - - public function setVersion(string $version): void - { - $this->snip->setActiveCommit($version); + $this->snip->setActiveVersion($version); $this->em->persist($this->snip); $this->em->flush(); } - public function getCommit(): string - { - return $this->snip->getActiveCommit(); - } - public function delete(): void { - // Cleanup the history - } - - public function getLatestVersion(): string - { - return $this->snip->getSnipContents()->last()->getId(); + // Cleanup the versions } } \ No newline at end of file diff --git a/src/Service/SnipParser/Stages/IncludeReferenceStage.php b/src/Service/SnipParser/Stages/IncludeReferenceStage.php index 03c1336..db887f7 100644 --- a/src/Service/SnipParser/Stages/IncludeReferenceStage.php +++ b/src/Service/SnipParser/Stages/IncludeReferenceStage.php @@ -2,10 +2,10 @@ namespace App\Service\SnipParser\Stages; +use App\Repository\SnipContentRepository; use App\Repository\SnipRepository; use App\Security\Voter\SnipVoter; use App\Service\SnipParser\Pipeline; -use App\Service\SnipServiceFactory; use League\Pipeline\StageInterface; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\DependencyInjection\Attribute\Autowire; @@ -13,10 +13,10 @@ use Symfony\Component\DependencyInjection\Attribute\Autowire; class IncludeReferenceStage implements StageInterface { public function __construct( - #[Autowire(lazy: true)] private readonly Security $security, - #[Autowire(lazy: true)] private readonly SnipRepository $snipRepository, - #[Autowire(lazy: true)] private readonly SnipServiceFactory $snipServiceFactory, - #[Autowire(lazy: true)] private readonly Pipeline $pipeline, + #[Autowire(lazy: true)] private readonly Security $security, + #[Autowire(lazy: true)] private readonly SnipRepository $snipRepository, + #[Autowire(lazy: true)] private readonly SnipContentRepository $snipContentRepository, + #[Autowire(lazy: true)] private readonly Pipeline $pipeline, ) {} public function __invoke(mixed $payload): string @@ -26,19 +26,32 @@ class IncludeReferenceStage implements StageInterface private function replaceReferences(mixed $payload): string { - // replaces all references (#n) to other snips with links - $pattern = '/\{\{(\d+)\}\}/'; + // replaces all references ({{ID}}) with the content of the snip + $pattern = '/\{\{([A-Z0-9]+)\}\}/'; return preg_replace_callback($pattern, function ($matches) { - $snip = $this->snipRepository->find($matches[1]); - if ($snip === null) { - return sprintf('%s', $matches[0]); + $id = $matches[1]; + try { + $content = $this->snipContentRepository->find($id); + } catch (\Exception) { + $content = null; + } + if ($content) { + $snip = $content->getSnip(); + } else { + $snip = $this->snipRepository->find($id); + if ($snip) { + $content = $this->snipContentRepository->find($snip->getActiveVersion()); + } + } + if ($content === null) { + return sprintf('%s', $matches[0]); } if (!$this->security->isGranted(SnipVoter::VIEW, $snip)) { return sprintf('%s', $matches[0]); } - return $this->pipeline->parse($this->snipServiceFactory->create($snip)->get()); + return $this->pipeline->parse($content->getText()); }, $payload); } } \ No newline at end of file diff --git a/src/Service/SnipServiceFactory.php b/src/Service/SnipServiceFactory.php index 1c85c18..1359beb 100644 --- a/src/Service/SnipServiceFactory.php +++ b/src/Service/SnipServiceFactory.php @@ -3,48 +3,17 @@ namespace App\Service; use App\Entity\Snip; -use App\Git\CustomGit; -use App\Service\SnipContent\SnipContentDB; -use App\Service\SnipContent\SnipContentGit; -use App\Service\SnipContent\SnipContentInterface; +use App\Service\SnipContent\SnipContentService; use Doctrine\ORM\EntityManagerInterface; -use Symfony\Bundle\SecurityBundle\Security; class SnipServiceFactory { public function __construct( - private readonly string $gitStoragePath, - private readonly string $storageType, - private readonly Security $security, private readonly EntityManagerInterface $em, ) {} - public function create(Snip $snip): SnipContentInterface + public function create(Snip $snip): SnipContentService { - return match ($this->storageType) { - 'git' => $this->createGit($snip), - 'db' => $this->createDB($snip), - default => throw new \Exception('Unknown storage type'), - }; - } - - private function createGit(Snip $snip): SnipContentGit - { - $git = new CustomGit(); - $repoPath = sprintf('%s/%s', $this->gitStoragePath, $snip->getId()); - if (!is_dir($repoPath)) { - $repo = $git->init($repoPath); - touch(sprintf('%s/.gitignore', $repoPath)); - $repo->addFile('.gitignore'); - $repo->commit('Initial commit'); - } else { - $repo = $git->open($repoPath); - } - return new SnipContentGit($repo, $this->security->getUser()); - } - - private function createDB(Snip $snip): SnipContentDB - { - return new SnipContentDB($snip, $this->security->getUser(), $this->em); + return new SnipContentService($snip, $this->em); } } \ No newline at end of file diff --git a/templates/snip/single.html.twig b/templates/snip/single.html.twig index b1d0c7a..e86d88d 100644 --- a/templates/snip/single.html.twig +++ b/templates/snip/single.html.twig @@ -10,8 +10,8 @@ Edit - - History + + Versions Delete @@ -26,12 +26,15 @@ {{ include('snip/badge.html.twig', {snip: snip}) }} {{ snip }} #{{ snip.id }} -
-

Current version: {{ branch }}

-
{{ content|raw }}
+ {% endblock %} diff --git a/templates/history/index.html.twig b/templates/version/index.html.twig similarity index 57% rename from templates/history/index.html.twig rename to templates/version/index.html.twig index 79714ec..aeeaced 100644 --- a/templates/history/index.html.twig +++ b/templates/version/index.html.twig @@ -6,14 +6,14 @@
Back - + Latest

- {% for version in versions %} - - {{ version.name }} - {{ version.id }} + {% for version in snip.snipContents %} + + {{ version.id.dateTime|date('Y-m-d H:i:s') }} - {{ version.id }} {% endfor %}