WIP: feature_paste #1

Draft
Miroslavsckaya wants to merge 24 commits from feature_paste into master
31 changed files with 5873 additions and 0 deletions

30
.env Normal file
View file

@ -0,0 +1,30 @@
# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
# * .env contains default values for the environment variables needed by the app
# * .env.local uncommitted file with local overrides
# * .env.$APP_ENV committed environment-specific defaults
# * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
# https://symfony.com/doc/current/configuration/secrets.html
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=your_secret_please_set_on_local_env
###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
#
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=15&charset=utf8"
###< doctrine/doctrine-bundle ###

10
.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
###> symfony/framework-bundle ###
/.env.local
/.env.local.php
/.env.*.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/
/vendor/
###< symfony/framework-bundle ###

17
bin/console Executable file
View file

@ -0,0 +1,17 @@
#!/usr/bin/env php
<?php
use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) {
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
}
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (array $context) {
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
return new Application($kernel);
};

75
composer.json Normal file
View file

@ -0,0 +1,75 @@
{
"type": "project",
"license": "proprietary",
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"doctrine/doctrine-bundle": "^2.10",
"doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.15",
"scrivo/highlight.php": "v9.18.1.10",
Review

Why so specific?

Why so specific?
"symfony/console": "6.3.*",
"symfony/dotenv": "6.3.*",
"symfony/flex": "^2",
"symfony/form": "6.3.*",
"symfony/framework-bundle": "6.3.*",
"symfony/runtime": "6.3.*",
"symfony/twig-bundle": "6.3.*",
"symfony/validator": "6.3.*",
"symfony/yaml": "6.3.*"
},
"config": {
"allow-plugins": {
"php-http/discovery": true,
"symfony/flex": true,
"symfony/runtime": true
},
"sort-packages": true
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
},
"replace": {
"symfony/polyfill-ctype": "*",
"symfony/polyfill-iconv": "*",
"symfony/polyfill-php72": "*",
"symfony/polyfill-php73": "*",
"symfony/polyfill-php74": "*",
"symfony/polyfill-php80": "*",
"symfony/polyfill-php81": "*"
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
]
},
"conflict": {
"symfony/symfony": "*"
},
"extra": {
"symfony": {
"allow-contrib": false,
"require": "6.3.*"
}
},
"require-dev": {
"symfony/maker-bundle": "^1.50"
}
}

5045
composer.lock generated Normal file

File diff suppressed because it is too large Load diff

9
config/bundles.php Normal file
View file

@ -0,0 +1,9 @@
<?php
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
];

View file

@ -0,0 +1,19 @@
framework:
cache:
# Unique name of your app: used to compute stable namespaces for cache keys.
#prefix_seed: your_vendor_name/app_name
# The "app" cache stores to the filesystem by default.
# The data in this cache should persist between deploys.
# Other options include:
# Redis
#app: cache.adapter.redis
#default_redis_provider: redis://localhost
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
#app: cache.adapter.apcu
# Namespaced pools use the above "app" backend by default
#pools:
#my.dedicated.cache: null

View file

@ -0,0 +1,46 @@
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '15'
orm:
auto_generate_proxy_classes: true
enable_lazy_ghost_objects: true
report_fields_where_declared: true
validate_xml_mapping: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
when@test:
doctrine:
dbal:
# "TEST_TOKEN" is typically set by ParaTest
dbname_suffix: '_test%env(default::TEST_TOKEN)%'
when@prod:
doctrine:
orm:
auto_generate_proxy_classes: false
proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool
framework:
cache:
pools:
doctrine.result_cache_pool:
adapter: cache.app
doctrine.system_cache_pool:
adapter: cache.system

View file

@ -0,0 +1,6 @@
doctrine_migrations:
migrations_paths:
# namespace is arbitrary but should be different from App\Migrations
# as migrations classes should NOT be autoloaded
'DoctrineMigrations': '%kernel.project_dir%/migrations'
enable_profiler: false

View file

@ -0,0 +1,25 @@
# see https://symfony.com/doc/current/reference/configuration/framework.html
framework:
secret: '%env(APP_SECRET)%'
#csrf_protection: true
http_method_override: false
handle_all_throwables: true
# Enables session support. Note that the session will ONLY be started if you read or write from it.
# Remove or comment this section to explicitly disable session support.
session:
handler_id: null
cookie_secure: auto
cookie_samesite: lax
storage_factory_id: session.storage.factory.native
#esi: true
#fragments: true
php_errors:
log: true
when@test:
framework:
test: true
session:
storage_factory_id: session.storage.factory.mock_file

View file

@ -0,0 +1,12 @@
framework:
router:
utf8: true
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
#default_uri: http://localhost
when@prod:
framework:
router:
strict_requirements: null

View file

@ -0,0 +1,6 @@
twig:
default_path: '%kernel.project_dir%/templates'
when@test:
twig:
strict_variables: true

View file

@ -0,0 +1,13 @@
framework:
validation:
email_validation_mode: html5
# Enables validator auto-mapping support.
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
#auto_mapping:
# App\Entity\: []
when@test:
framework:
validation:
not_compromised_password: false

5
config/preload.php Normal file
View file

@ -0,0 +1,5 @@
<?php
if (file_exists(dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php')) {
require dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php';
}

5
config/routes.yaml Normal file
View file

@ -0,0 +1,5 @@
controllers:
Review

I'd suggest we move to route list in YAML here before merging it. But it's not a main priority for now. Just let this thread be until we ready with everything else.

I'd suggest we move to route list in YAML here before merging it. But it's not a main priority for now. Just let this thread be until we ready with everything else.
resource:
path: ../src/Controller/
namespace: App\Controller
type: attribute

View file

@ -0,0 +1,4 @@
when@dev:
_errors:
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
prefix: /_error

24
config/services.yaml Normal file
View file

@ -0,0 +1,24 @@
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# 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:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones

View file

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20230720115905 extends AbstractMigration
{
public function up(Schema $schema): void
skobkin marked this conversation as resolved
Review

Do you need it here if it's empty?

Do you need it here if it's empty?
{
$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, language VARCHAR(25), description TEXT, filename VARCHAR(128), author VARCHAR(128), publish_date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, expiration_date TIMESTAMP(0) WITHOUT TIME ZONE, ip VARCHAR(39) NOT NULL, secret VARCHAR(40), PRIMARY KEY(id))');
}
public function down(Schema $schema): void
{
$this->addSql('CREATE SCHEMA public');
Review

This shouldn't be here.

This shouldn't be here.
$this->addSql('DROP SEQUENCE paste_id_seq CASCADE');
$this->addSql('DROP TABLE paste');
}
}

74
public/css/ocean.css Normal file
View file

@ -0,0 +1,74 @@
/* Ocean Dark Theme */
/* https://github.com/gavsiu */
/* Original theme - https://github.com/chriskempson/base16 */
/* Ocean Comment */
.hljs-comment,
.hljs-quote {
color: #65737e;
}
/* Ocean Red */
.hljs-variable,
.hljs-template-variable,
.hljs-tag,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class,
.hljs-regexp,
.hljs-deletion {
color: #bf616a;
}
/* Ocean Orange */
.hljs-number,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params,
.hljs-meta,
.hljs-link {
color: #d08770;
}
/* Ocean Yellow */
.hljs-attribute {
color: #ebcb8b;
}
/* Ocean Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition {
color: #a3be8c;
}
/* Ocean Blue */
.hljs-title,
.hljs-section {
color: #8fa1b3;
}
/* Ocean Purple */
.hljs-keyword,
.hljs-selector-tag {
color: #b48ead;
}
.hljs {
display: block;
overflow-x: auto;
background: #2b303b;
color: #c0c5ce;
padding: 0.5em;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}

9
public/index.php Normal file
View file

@ -0,0 +1,9 @@
<?php
use App\Kernel;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (array $context) {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};

View file

@ -0,0 +1,52 @@
<?php
skobkin marked this conversation as resolved
Review

I can't leave you a comment on src/Controller/.gitignore, so I'll ask here.

Why do you need it?

I can't leave you a comment on `src/Controller/.gitignore`, so I'll ask here. Why do you need it?
declare(strict_types = 1);
namespace App\Controller;
use App\DTO\PasteFormData;
use App\Entity\CodeHighlighter;
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
Review

I'd suggest explicitly defining which request methods we're processing here.

P.S. We'll move this to YAML in the end.

I'd suggest explicitly defining which request methods we're processing here. P.S. We'll move this to YAML in the end.
{
#[Route('/', name: 'homepage')]
public function new(Request $request, PasteRepository $pasteRepository): Response
{
$pasteData = new PasteFormData();
$form = $this->createForm(PasteForm::class, $pasteData);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$pasteData = $form->getData();
$paste = Paste::fromFormDataAndIp($pasteData, $request->getClientIp());
$pasteRepository->save($paste, true);
return $this->redirectToRoute('showpaste', ['id' => $paste->id, 'secret' => $paste->secret]);
}
return $this->render('paste.html.twig', [
'form' => $form,
]);
}
#[Route('/{id}/{secret}', name: 'showpaste')]
Review

show_paste at least.

`show_paste` at least.
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('show_paste.html.twig', [
'form' => $form,
'highlighted_text' => CodeHighlighter::highlight($pasteData->language, $pasteData->text)
Review

I'd suggest you implementing Twig extension wih filter and function to use highlighter in the template.

I'd suggest you implementing Twig extension wih filter and function to use highlighter in the template.
]);
}
}

40
src/DTO/PasteFormData.php Normal file
View file

@ -0,0 +1,40 @@
<?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;
public bool $private;
public ?string $language = null;
public ?string $description = null;
public ?string $filename = null;
public ?string $author = null;
public ?\DateTimeImmutable $expirationDate;
skobkin marked this conversation as resolved
Review

Why string and Assert\NotBlank?

Why `string` and `Assert\NotBlank`?
public function __construct(?Paste $paste = null)
{
skobkin marked this conversation as resolved
Review

Is this validation being processed BEFORE storing the data in the DTO?
If not, it's pointless as with bool field earlier.

Is this validation being processed BEFORE storing the data in the DTO? If not, it's pointless as with `bool` field earlier.
if ($paste === null) {
return;
skobkin marked this conversation as resolved
Review

Or you can make constructor private, use property promotion and add fromPaste() method to create it from the entity.

Or you can make constructor `private`, use property promotion and add `fromPaste()` method to create it from the entity.
}
$this->fromPaste($paste);
}
private function fromPaste(Paste $paste)
{
$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 !== null ? $paste->author : 'anonymous';
$this->expirationDate = $paste->expirationDate;
}
}

View file

@ -0,0 +1,26 @@
<?php
declare(strict_types = 1);
namespace App\Entity;
Review

Why Entity?
Are you storing it in the database? Does it have an identity?

Why `Entity`? Are you storing it in the database? Does it have an identity?
use \Highlight\Highlighter;
class CodeHighlighter
{
public static function highlight(?string $language, string $code): string
{
$hl = new Highlighter();
Review

Why not inject into constructor?

Why not inject into constructor?
try {
// Highlight some code.
$highlighted = $hl->highlight($language, $code);
$highlighted_text = "<pre><code class=\"hljs {$highlighted->language}\">".$highlighted->value.'</code></pre>';
Review

Why do you use HTML here and not in the template? Check the comment I left in PasterController::showPaste().

Why do you use HTML here and not in the template? Check the comment I left in `PasterController::showPaste()`.
Review

Also naming here is not good. You have $highlighted and $highlighted_text.

  • $highlighted_text is not in $camelCase
  • Both variables contain text, both contain code. If it shouldn't be removed, I'd say to think a bit more on naming.
Also naming here is not good. You have `$highlighted` and `$highlighted_text`. - `$highlighted_text` is not in `$camelCase` - Both variables contain text, both contain code. If it shouldn't be removed, I'd say to think a bit more on naming.
}
catch (\DomainException $e) {
// This is thrown if the specified language does not exist
$highlighted_text = '<pre><code>'.htmlentities($code).'</code></pre>';
Review

Again why HTML here?

Again why HTML here?
}
return $highlighted_text;
}
}

52
src/Entity/Paste.php Normal file
View file

@ -0,0 +1,52 @@
<?php
declare(strict_types = 1);
namespace App\Entity;
use App\DTO\PasteFormData;
use App\Repository\PasteRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: PasteRepository::class)]
Review

I'd suggest explicitly defining Table attribute here.

Especially since you're defining Column attributes below ⬇️

I'd suggest explicitly defining `Table` attribute here. Especially since you're defining `Column` attributes below ⬇️
Review

I'd also suggest to make this entity readonly. It'll give some performance benefits.

You can read about that here.

I'd also suggest to make this entity readonly. It'll give some performance benefits. You can read about that [here](https://www.doctrine-project.org/projects/doctrine-orm/en/2.15/reference/attributes-reference.html#attrref_entity).
class Paste
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
public readonly int $id;
Review

Why not also constructor promotion though?

Why not also constructor promotion though?
private function __construct(
#[ORM\Column(type: 'text', nullable: false)]
public readonly string $text,
#[ORM\Column(type: 'string', length: 25, nullable: true)]
public readonly ?string $language,
#[ORM\Column(type: 'text', nullable: true)]
public readonly ?string $description,
#[ORM\Column(type: 'string', length: 128, nullable: true)]
public readonly ?string $filename,
Review

Why 128?

Why 128?
#[ORM\Column(type: 'string', length: 128, nullable: true)]
public readonly ?string $author,
#[ORM\Column(type: 'datetime_immutable', nullable: false)]
public readonly \DateTimeImmutable $publishDate,
#[ORM\Column(type: 'datetime_immutable', nullable: true)]
public readonly ?\DateTimeImmutable $expirationDate,
#[ORM\Column(type: 'string', length: 39, nullable: false)]
public readonly string $ip,
skobkin marked this conversation as resolved
Review

IPv6?

IPv6?
#[ORM\Column(type: 'string', length: 40, nullable: true)]
public readonly ?string $secret,
) {}
public static function fromFormDataAndIp(PasteFormData $pasteFormData, $ip): Paste
Review

You can use self as return type hint too.

You can use `self` as return type hint too.
{
return new self(
$pasteFormData->text,
$pasteFormData->language,
$pasteFormData->description,
$pasteFormData->filename,
$pasteFormData->author,
new \DateTimeImmutable(),
$pasteFormData->expirationDate,
$ip,
$pasteFormData->private ? \hash('sha1', \random_bytes(25)) : null,
);
}
}

View file

@ -0,0 +1,43 @@
<?php
declare(strict_types = 1);
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
{
$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], 'required' => false])
->add('filename', TextType::class, ['required' => false, 'attr' => ['maxlength' =>128]])
Review

Why 128?

Why 128?
->add('expirationDate', DateTimeType::class, [
'required' => false,
'date_widget' => 'single_text',
'input' => 'datetime_immutable',
]
)
->add('private', CheckboxType::class, ['required' => false])
->add('save', SubmitType::class)
;
}
}

11
src/Kernel.php Normal file
View file

@ -0,0 +1,11 @@
<?php
namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
}

View file

@ -0,0 +1,26 @@
<?php
skobkin marked this conversation as resolved
Review

Can't leave a comment for src/Repository/.gitignore, so I'll ask here.

Do you need it?

Can't leave a comment for `src/Repository/.gitignore`, so I'll ask here. Do you need it?
declare(strict_types = 1);
namespace App\Repository;
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 save(Paste $paste, bool $flush = false): void
{
$entityManager = $this->getEntityManager();
$entityManager->persist($paste);
if ($flush) {
$entityManager->flush();
}
}
}

119
symfony.lock Normal file
View file

@ -0,0 +1,119 @@
{
"doctrine/doctrine-bundle": {
"version": "2.10",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.10",
"ref": "f0d8c9a4da17815830aac0d63e153a940ae176bb"
},
"files": [
"config/packages/doctrine.yaml",
"src/Entity/.gitignore",
"src/Repository/.gitignore"
]
},
"doctrine/doctrine-migrations-bundle": {
"version": "3.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "3.1",
"ref": "1d01ec03c6ecbd67c3375c5478c9a423ae5d6a33"
},
"files": [
"config/packages/doctrine_migrations.yaml",
"migrations/.gitignore"
]
},
"symfony/console": {
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.3",
"ref": "da0c8be8157600ad34f10ff0c9cc91232522e047"
},
"files": [
"bin/console"
]
},
"symfony/flex": {
"version": "2.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "146251ae39e06a95be0fe3d13c807bcf3938b172"
},
"files": [
".env"
]
},
"symfony/framework-bundle": {
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.2",
"ref": "af47254c5e4cd543e6af3e4508298ffebbdaddd3"
},
"files": [
"config/packages/cache.yaml",
"config/packages/framework.yaml",
"config/preload.php",
"config/routes/framework.yaml",
"config/services.yaml",
"public/index.php",
"src/Controller/.gitignore",
"src/Kernel.php"
]
},
"symfony/maker-bundle": {
"version": "1.50",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
}
},
"symfony/routing": {
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.2",
"ref": "e0a11b4ccb8c9e70b574ff5ad3dfdcd41dec5aa6"
},
"files": [
"config/packages/routing.yaml",
"config/routes.yaml"
]
},
"symfony/twig-bundle": {
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.3",
"ref": "b7772eb20e92f3fb4d4fe756e7505b4ba2ca1a2c"
},
"files": [
"config/packages/twig.yaml",
"templates/base.html.twig"
]
},
"symfony/validator": {
"version": "6.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.3",
"ref": "c32cfd98f714894c4f128bb99aa2530c1227603c"
},
"files": [
"config/packages/validator.yaml"
]
}
}

15
templates/base.html.twig Normal file
View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Copypaste{% endblock %}</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
Review

It's better not to rely on external CDN's when you can.
You can just save needed resources in the repository since we're not making our front-end too complex to make dynamic build.

It's better not to rely on external CDN's when you can. You can just save needed resources in the repository since we're not making our front-end too complex to make dynamic build.
{% block stylesheets %}
{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
Review

Same here.

Same here.
</body>
</html>

View file

@ -0,0 +1,6 @@
{% extends "base.html.twig" %}
Review

Why double quotes again?

Why double quotes again?
Review

BTW, if you're including it in the show_paste.html.twig, then you don't need to extend base.html.twig here.

BTW, if you're including it in the `show_paste.html.twig`, then you don't need to extend `base.html.twig` here.
{% block content %}
{{ form(form) }}
{% endblock %}

View file

@ -0,0 +1,24 @@
{% extends "base.html.twig" %}
{% block stylesheets %}
<link rel="stylesheet" href="/css/ocean.css"></link>
{% endblock %}
{% block content %}
<ul class="nav nav-tabs" id="myTab" role="tablist">
Review

Do not forget to name classes and ID's properly.

Do not forget to name classes and ID's properly.
<li class="nav-item" role="presentation">
<button class="nav-link active" id="show-tab" data-bs-toggle="tab" data-bs-target="#show" type="button" role="tab" aria-controls="show" aria-selected="true">Show</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#edit" type="button" role="tab" aria-controls="edit" aria-selected="false">Edit</button>
Review

profile-tab?

`profile-tab`?
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="show" role="tabpanel" aria-labelledby="show-tab">
{{ highlighted_text|raw }}
Review

As I said above, better implement Twig extension and use it here.

As I said above, better implement Twig extension and use it here.
</div>
<div class="tab-pane fade" id="edit" role="tabpanel" aria-labelledby="edit-tab">
{% include 'paste.html.twig' %}
</div>
</div>
{% endblock %}