Compare commits
10 commits
7e8de026e1
...
6640112c54
Author | SHA1 | Date | |
---|---|---|---|
|
6640112c54 | ||
|
21d651f377 | ||
|
8bb77fbe6c | ||
|
1d08cfdc09 | ||
|
ffcfa29968 | ||
|
561b016e38 | ||
|
78ebba425b | ||
|
5b306cfd54 | ||
|
0d7c5487f4 | ||
|
9a98436eb8 |
2
.env
2
.env
|
@ -16,7 +16,7 @@
|
|||
|
||||
###> symfony/framework-bundle ###
|
||||
APP_ENV=dev
|
||||
APP_SECRET=90c78b17302e6ff2e14213f129e3f5f4
|
||||
APP_SECRET=your_secret_please_set_on_local_env
|
||||
###< symfony/framework-bundle ###
|
||||
|
||||
###> doctrine/doctrine-bundle ###
|
||||
|
|
0
migrations/.gitignore
vendored
0
migrations/.gitignore
vendored
|
@ -7,9 +7,7 @@ namespace DoctrineMigrations;
|
|||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
|
||||
final class Version20230720115905 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
|
@ -19,14 +17,12 @@ final class Version20230720115905 extends AbstractMigration
|
|||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SEQUENCE paste_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE paste (id INT NOT NULL, text TEXT NOT NULL, private BOOLEAN NOT NULL, language VARCHAR(25) NOT NULL, description TEXT NOT NULL, filename VARCHAR(128) NOT NULL, author VARCHAR(128) NOT NULL, publish_date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, expiration_date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, ip VARCHAR(15) NOT NULL, secret VARCHAR(40) NOT NULL, PRIMARY KEY(id))');
|
||||
$this->addSql('CREATE TABLE paste (id INT NOT NULL, text TEXT NOT NULL, language VARCHAR(25), description TEXT, filename VARCHAR(128), author VARCHAR(128) NOT NULL, publish_date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, expiration_date TIMESTAMP(0) WITHOUT TIME ZONE, ip VARCHAR(15) NOT NULL, secret VARCHAR(40), PRIMARY KEY(id))');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('DROP SEQUENCE paste_id_seq CASCADE');
|
||||
$this->addSql('DROP TABLE paste');
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20230720125632 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE paste ALTER language DROP NOT NULL');
|
||||
$this->addSql('ALTER TABLE paste ALTER description DROP NOT NULL');
|
||||
$this->addSql('ALTER TABLE paste ALTER filename DROP NOT NULL');
|
||||
$this->addSql('ALTER TABLE paste ALTER expiration_date DROP NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE paste ALTER language SET NOT NULL');
|
||||
$this->addSql('ALTER TABLE paste ALTER description SET NOT NULL');
|
||||
$this->addSql('ALTER TABLE paste ALTER filename SET NOT NULL');
|
||||
$this->addSql('ALTER TABLE paste ALTER expiration_date SET NOT NULL');
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20230720130259 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE paste ALTER secret DROP NOT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE paste ALTER secret SET NOT NULL');
|
||||
}
|
||||
}
|
0
src/Controller/.gitignore
vendored
0
src/Controller/.gitignore
vendored
|
@ -1,54 +1,65 @@
|
|||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\Controller;
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Form\Type\PasteForm;
|
||||
use App\Entity\Paste;
|
||||
use App\Repository\PasteRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use App\DTO\PasteFormData;
|
||||
use App\Entity\Paste;
|
||||
use App\Form\Type\PasteForm;
|
||||
use App\Repository\PasteRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
|
||||
class PasteController extends AbstractController
|
||||
{
|
||||
#[Route('/')]
|
||||
public function new(Request $request, EntityManagerInterface $entityManager): Response {
|
||||
$paste = new Paste();
|
||||
$form = $this->createForm(PasteForm::class, $paste);
|
||||
public function new(Request $request, PasteRepository $pasteRepository): Response
|
||||
{
|
||||
$pasteData = new PasteFormData();
|
||||
$form = $this->createForm(PasteForm::class, $pasteData);
|
||||
|
||||
$form->handleRequest($request);
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$paste = $form->getData();
|
||||
$paste->setIp($request->getClientIp());
|
||||
$paste->setPublishDate(new \DateTime());
|
||||
$pasteData = $form->getData();
|
||||
|
||||
if ($paste->isPrivate()) {
|
||||
$paste->setSecret(hash("sha1", random_bytes(25)));
|
||||
$secret = null;
|
||||
if ($pasteData->private) {
|
||||
$secret = hash('sha1', random_bytes(25));
|
||||
}
|
||||
|
||||
$entityManager->persist($paste);
|
||||
$entityManager->flush();
|
||||
$paste = new Paste(
|
||||
$pasteData->text,
|
||||
$pasteData->language,
|
||||
$pasteData->description,
|
||||
$pasteData->filename,
|
||||
$pasteData->author,
|
||||
new \DateTimeImmutable(),
|
||||
$pasteData->expirationDate,
|
||||
$request->getClientIp(),
|
||||
$secret
|
||||
);
|
||||
$pasteRepository->save($paste);
|
||||
|
||||
return $this->redirectToRoute($request->attributes->get('_route'));
|
||||
|
||||
return $this->redirectToRoute($request->attributes->get('_route'));
|
||||
}
|
||||
|
||||
return $this->render('paste.html.twig', [
|
||||
'form' => $form,
|
||||
]);
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/{id}/{secret}')]
|
||||
public function show_paste(PasteRepository $pasteRepository, Request $request, string $id, ?string $secret=NULL): Response {
|
||||
$paste = $pasteRepository->findOneBy(["id" => $id, "secret" => $secret]);
|
||||
$form = $this->createForm(PasteForm::class, $paste);
|
||||
public function showPaste(PasteRepository $pasteRepository, string $id, ?string $secret=NULL): Response
|
||||
{
|
||||
$paste = $pasteRepository->findOneBy(['id' => $id, 'secret' => $secret]);
|
||||
$pasteData = new PasteFormData($paste);
|
||||
$form = $this->createForm(PasteForm::class, $pasteData);
|
||||
|
||||
return $this->render('paste.html.twig', [
|
||||
'form' => $form,
|
||||
]);
|
||||
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
38
src/DTO/PasteFormData.php
Normal file
38
src/DTO/PasteFormData.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\DTO;
|
||||
|
||||
use App\Entity\Paste;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
|
||||
class PasteFormData
|
||||
{
|
||||
#[Assert\NotBlank]
|
||||
public string $text;
|
||||
#[Assert\Type(\boolean::class)]
|
||||
public bool $private;
|
||||
public ?string $language = null;
|
||||
public ?string $description = null;
|
||||
public ?string $filename = null;
|
||||
#[Assert\NotBlank]
|
||||
public string $author = 'anonymous';
|
||||
#[Assert\Type(\DateTimeImmutable::class)]
|
||||
public ?\DateTimeImmutable $expirationDate;
|
||||
|
||||
public function __construct(?Paste $paste=null)
|
||||
{
|
||||
if ($paste === null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
$this->text = $paste->text;
|
||||
$this->private = $paste->secret !== null;
|
||||
$this->language = $paste->language;
|
||||
$this->description = $paste->description;
|
||||
$this->filename = $paste->filename;
|
||||
$this->author = $paste->author;
|
||||
$this->expirationDate = $paste->expirationDate;
|
||||
}
|
||||
}
|
0
src/Entity/.gitignore
vendored
0
src/Entity/.gitignore
vendored
|
@ -1,139 +1,38 @@
|
|||
<?php
|
||||
namespace App\Entity;
|
||||
declare(strict_types = 1);
|
||||
|
||||
use App\Repository\PasteRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\PasteRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: PasteRepository::class)]
|
||||
class Paste
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(nullable: false)]
|
||||
private int $id;
|
||||
#[ORM\Column]
|
||||
public readonly int $id;
|
||||
|
||||
#[ORM\Column(type: "text", nullable: false)]
|
||||
#[Assert\NotBlank]
|
||||
private string $text;
|
||||
|
||||
#[ORM\Column(type: "boolean", nullable: false)]
|
||||
#[Assert\Type(\boolean::class)]
|
||||
private bool $private;
|
||||
|
||||
#[ORM\Column(length: 25, nullable: true)]
|
||||
private ?string $language;
|
||||
|
||||
#[ORM\Column(type: "text", nullable: true)]
|
||||
private ?string $description;
|
||||
|
||||
#[ORM\Column(length: 128, nullable: true)]
|
||||
private ?string $filename;
|
||||
|
||||
#[ORM\Column(length: 128, nullable: false)]
|
||||
#[Assert\NotBlank]
|
||||
private string $author = "anonymous";
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: false)]
|
||||
#[Assert\Type(\DateTime::class)]
|
||||
private \DateTime $publishDate;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||
#[Assert\Type(\DateTime::class)]
|
||||
private ?\DateTime $expirationDate;
|
||||
|
||||
#[ORM\Column(length: 15, nullable: false)]
|
||||
private string $ip;
|
||||
|
||||
#[ORM\Column(length: 40, nullable: true)]
|
||||
private ?string $secret;
|
||||
|
||||
public function getId(): int {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(int $id): void {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getText(): string {
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function setText(string $text): void {
|
||||
$this->text = $text;
|
||||
}
|
||||
|
||||
public function getLanguage(): ?string {
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
public function setLanguage(?string $language): void {
|
||||
$this->language = $language;
|
||||
}
|
||||
|
||||
public function getDescription(): ?string {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(?string $description): void {
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
public function getFilename(): ?string {
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
public function setFilename(?string $filename): void {
|
||||
$this->filename = $filename;
|
||||
}
|
||||
|
||||
public function getAuthor(): string {
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
public function setAuthor(string $author): void {
|
||||
$this->author = $author;
|
||||
}
|
||||
|
||||
public function getPublishDate(): \DateTime {
|
||||
return $this->publishDate;
|
||||
}
|
||||
|
||||
public function setPublishDate(\DateTime $date): void {
|
||||
$this->publishDate = $date;
|
||||
}
|
||||
|
||||
public function getExpirationDate(): ?\DateTime {
|
||||
return $this->expirationDate;
|
||||
}
|
||||
|
||||
public function setExpirationDate(?\DateTime $date): void {
|
||||
$this->expirationDate = $date;
|
||||
}
|
||||
|
||||
public function getIp(): string {
|
||||
return $this->ip;
|
||||
}
|
||||
|
||||
public function setIP(string $ip): void {
|
||||
$this->ip = $ip;
|
||||
}
|
||||
|
||||
public function getSecret(): ?string {
|
||||
return $this->secret;
|
||||
}
|
||||
|
||||
public function setSecret(?string $secret): void {
|
||||
$this->secret = $secret;
|
||||
}
|
||||
|
||||
public function isPrivate(): bool {
|
||||
return $this->private;
|
||||
}
|
||||
|
||||
public function setPrivate(bool $private): void {
|
||||
$this->private = $private;
|
||||
}
|
||||
public function __construct(
|
||||
#[ORM\Column(type: 'text', nullable: false)]
|
||||
public readonly string $text,
|
||||
#[ORM\Column(length: 25, nullable: true)]
|
||||
public readonly ?string $language,
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
public readonly ?string $description,
|
||||
#[ORM\Column(length: 128, nullable: true)]
|
||||
public readonly ?string $filename,
|
||||
#[ORM\Column(length: 128, nullable: false)]
|
||||
public readonly string $author,
|
||||
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: false)]
|
||||
public readonly \DateTimeImmutable $publishDate,
|
||||
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
||||
public readonly ?\DateTimeImmutable $expirationDate,
|
||||
#[ORM\Column(length: 15, nullable: false)]
|
||||
public readonly string $ip,
|
||||
#[ORM\Column(length: 40, nullable: true)]
|
||||
public readonly ?string $secret,
|
||||
) {}
|
||||
}
|
|
@ -1,28 +1,43 @@
|
|||
<?php
|
||||
namespace App\Form\Type;
|
||||
declare(strict_types = 1);
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
namespace App\Form\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
|
||||
|
||||
class PasteForm extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void {
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add("language", ChoiceType::class, ["choices" => ["Python" => "python", "PHP" => "php", "Plain text" => NULL]])
|
||||
->add("description", TextType::class, ["required" => false])
|
||||
->add("text", TextareaType::class)
|
||||
->add("author", TextType::class, ["attr" => ["maxlength" =>128]])
|
||||
->add("filename", TextType::class, ["required" => false, "attr" => ["maxlength" =>128]])
|
||||
->add("expirationDate", DateTimeType::class, ["required" => false, "date_widget" => "single_text", "input" => "datetime"])
|
||||
->add("private", CheckboxType::class, ["required" => false])
|
||||
->add("save", SubmitType::class)
|
||||
;
|
||||
->add('language', ChoiceType::class, [
|
||||
'choices' => [
|
||||
'Python' => 'python',
|
||||
'PHP' => 'php',
|
||||
'Plain text' => NULL,
|
||||
]
|
||||
]
|
||||
)
|
||||
->add('description', TextType::class, ['required' => false])
|
||||
->add('text', TextareaType::class)
|
||||
->add('author', TextType::class, ['attr' => ['maxlength' =>128]])
|
||||
->add('filename', TextType::class, ['required' => false, 'attr' => ['maxlength' =>128]])
|
||||
->add('expirationDate', DateTimeType::class, [
|
||||
'required' => false,
|
||||
'date_widget' => 'single_text',
|
||||
'input' => 'datetime_immutable',
|
||||
]
|
||||
)
|
||||
->add('private', CheckboxType::class, ['required' => false])
|
||||
->add('save', SubmitType::class)
|
||||
;
|
||||
}
|
||||
}
|
0
src/Repository/.gitignore
vendored
0
src/Repository/.gitignore
vendored
|
@ -1,14 +1,23 @@
|
|||
<?php
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace App\Repository;
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Paste;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use App\Entity\Paste;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
class PasteRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry) {
|
||||
parent::__construct($registry, Paste::class);
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Paste::class);
|
||||
}
|
||||
|
||||
public function save(Paste $paste): void
|
||||
{
|
||||
$entityManager = $this->getEntityManager();
|
||||
$entityManager->persist($paste);
|
||||
$entityManager->flush();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue