bumped symfony to 5.3
4
.env
|
@ -26,3 +26,7 @@ APP_SECRET=cd0ae68f915f2a06b82007f2906e54e8
|
||||||
# DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7"
|
# DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7"
|
||||||
DATABASE_URL="mysql://24unix:24.unix@127.0.0.1:3306/24unix"
|
DATABASE_URL="mysql://24unix:24.unix@127.0.0.1:3306/24unix"
|
||||||
###< doctrine/doctrine-bundle ###
|
###< doctrine/doctrine-bundle ###
|
||||||
|
|
||||||
|
###> symfony/mailer ###
|
||||||
|
MAILER_DSN=smtp://localhost
|
||||||
|
###< symfony/mailer ###
|
||||||
|
|
|
@ -8,3 +8,10 @@
|
||||||
/var/
|
/var/
|
||||||
/vendor/
|
/vendor/
|
||||||
###< symfony/framework-bundle ###
|
###< symfony/framework-bundle ###
|
||||||
|
|
||||||
|
###> symfony/webpack-encore-bundle ###
|
||||||
|
/node_modules/
|
||||||
|
/public/build/
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
###< symfony/webpack-encore-bundle ###
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* Welcome to your app's main JavaScript file!
|
||||||
|
*
|
||||||
|
* We recommend including the built version of this JavaScript file
|
||||||
|
* (and its CSS file) in your base layout (base.html.twig).
|
||||||
|
*/
|
||||||
|
|
||||||
|
// any CSS you import will output into a single css file (app.css in this case)
|
||||||
|
import './styles/app.css';
|
||||||
|
|
||||||
|
// start the Stimulus application
|
||||||
|
import './bootstrap';
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { startStimulusApp } from '@symfony/stimulus-bridge';
|
||||||
|
|
||||||
|
// Registers Stimulus controllers from controllers.json and in the controllers/ directory
|
||||||
|
export const app = startStimulusApp(require.context(
|
||||||
|
'@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
|
||||||
|
true,
|
||||||
|
/\.(j|t)sx?$/
|
||||||
|
));
|
||||||
|
|
||||||
|
// register any custom, 3rd party controllers here
|
||||||
|
// app.register('some_controller_name', SomeImportedController);
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"controllers": [],
|
||||||
|
"entrypoints": []
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Controller } from 'stimulus';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an example Stimulus controller!
|
||||||
|
*
|
||||||
|
* Any element with a data-controller="hello" attribute will cause
|
||||||
|
* this controller to be executed. The name "hello" comes from the filename:
|
||||||
|
* hello_controller.js -> "hello"
|
||||||
|
*
|
||||||
|
* Delete this file or adapt it for your use!
|
||||||
|
*/
|
||||||
|
export default class extends Controller {
|
||||||
|
connect() {
|
||||||
|
this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 153 KiB |
After Width: | Height: | Size: 154 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 453 KiB |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 431 KiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 309 KiB |
After Width: | Height: | Size: 5.9 MiB |
After Width: | Height: | Size: 540 B |
After Width: | Height: | Size: 960 B |
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg version="1.0" width="1024.000000pt" height="1024.000000pt" viewBox="0 0 1024.000000 1024.000000" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com">
|
||||||
|
<g transform="matrix(0.1, 0, 0, -0.1, -6.266793, 1030.994141)" fill="#000000" stroke="none">
|
||||||
|
<path d="M 1568.308 10309.078 C 564.548 10309.078 62.668 9803.508 62.668 8792.368 C 62.668 8339.535 62.668 1727.029 62.668 1714.388 C 62.668 703.248 564.548 197.678 1568.308 197.678 C 1591.463 197.678 7590.868 197.678 8594.628 197.678 C 9598.388 197.678 10100.268 703.248 10100.268 1714.388 C 10100.268 2725.528 10100.268 6486.982 10100.268 8792.368 C 10100.268 9803.508 9598.388 10309.078 8594.628 10309.078 C 5583.348 10309.078 1568.308 10309.078 1568.308 10309.078 Z M 3672.54 7275.658 C 4244.339 8047.825 5331.794 8472.407 6085.228 8392.819 C 7558.724 8237.167 7711.517 7222.701 7669.179 6891 C 7601.342 6359.52 7261.927 6120.025 7215.766 6046.83 C 7054.424 5790.996 7112.956 5590 7129.487 5573 C 7140.184 5562 7284.674 5550.452 7381.343 5616 C 7983.187 6016.375 8092.748 6264.518 8434.473 6410 C 9054.145 6673.811 9255.358 5797.308 9262.001 5720 C 9313.839 5116.734 9046.297 5272.999 8804.116 5495.788 C 8923.874 4704.112 8630.922 4160.925 8514.503 4156.928 C 8067.033 4141.565 7890.303 4557.157 7878.388 4495.295 C 7651.349 3316.524 6684.687 4203.602 6680.894 4195 C 6408.836 3577.95 6794.683 3431.998 6849.431 3394 C 7072.287 3239.325 7829.534 3522.262 7901.588 3527 C 8353.345 3556.705 8404.386 3093.747 8337.202 2842.622 C 8300.167 2704.19 8116.008 3110.391 8098.694 3056.379 C 7905.831 2454.74 7286.79 1799.512 5252.72 1970 C 2634.673 2189.435 3169.554 3982.907 3145.643 3965.989 C 2508.377 3515.099 2335 4324.288 2233.361 4750 C 2228.393 4770.808 2005.604 4213.617 1673.29 4210.931 C 1340.976 4208.245 1180.756 5173.068 1418.474 5904 C 1278.78 5813.12 1110.84 5657.594 908.927 5784 C 768.062 5872.188 787.568 6952.627 1704.288 7206 C 1828.193 7240.246 2045.681 7168.896 2123.478 7041 C 2259.345 6817.638 2477.148 6490.068 2526.059 6431 C 2665.475 6262.634 3023.704 6040.791 3220.366 6417 C 3444.851 6846.434 3309.293 6770.088 3672.54 7275.658 Z" style="stroke: rgb(218, 85, 85); fill: #ff8040;"/>
|
||||||
|
<path d="M 4218 7190 C 3843.862 7161.67 3684.337 6798.411 3725.003 6362.026 C 3760.674 5979.24 4109.523 6050.046 4179.282 6064.14 C 4462.744 6121.409 4624.226 6383.444 4672.836 6857.022 C 4686.564 6990.762 4563.698 7216.176 4218 7190 Z" style="stroke: rgb(218, 85, 85); fill: #ff8040;"/>
|
||||||
|
<path d="M 5340 6904 C 5231.393 6881.963 5107.694 6745.696 4939 6400 C 4867.75 6253.991 4906.668 6046.567 4987 5945 C 5126.502 5768.621 5353.992 5737.6 5564.869 5909.852 C 5636.594 5968.439 5788.19 6124.729 5832.272 6408.204 C 5894.908 6810.991 5662.601 6969.457 5340 6904 Z" style="stroke: rgb(218, 85, 85); fill: #ff8040;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 1012 B |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 528 B |
After Width: | Height: | Size: 987 B |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 24 KiB |
|
@ -0,0 +1,12 @@
|
||||||
|
// assets/js/app.js
|
||||||
|
|
||||||
|
|
||||||
|
import '../styles/app.scss';
|
||||||
|
|
||||||
|
// import $ from 'jquery';
|
||||||
|
// global.$ = $;
|
||||||
|
|
||||||
|
import 'bootstrap';
|
||||||
|
|
||||||
|
require('@fortawesome/fontawesome-free/css/all.min.css');
|
||||||
|
require('@fortawesome/fontawesome-free/js/all.js');
|
|
@ -0,0 +1,3 @@
|
||||||
|
body {
|
||||||
|
background-color: lightgray;
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
/*
|
||||||
|
jet black (tiefschwarz) - RAL 9005: #0e0e10, rgba(14, 14, 16, 1.0);
|
||||||
|
mango: #ff8040, rgba(255, 130, 67, 1.0);
|
||||||
|
gray: #a1a1a1, rgba(161, 161, 161, 1.0)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
@media (prefers-color-scheme: light) {}
|
||||||
|
@media (prefers-color-scheme: dark) {}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* debug */
|
||||||
|
* {
|
||||||
|
border: 0 solid gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// customize some Bootstrap variables
|
||||||
|
|
||||||
|
|
||||||
|
$primary: #FF8040;
|
||||||
|
$body-bg: #0E0E10;
|
||||||
|
$body-color: darken(white, 20);
|
||||||
|
|
||||||
|
// the ~ allows you to reference things in node_modules
|
||||||
|
@import "~bootstrap/scss/bootstrap";
|
||||||
|
|
||||||
|
body {
|
||||||
|
padding-top: 95px;
|
||||||
|
padding-bottom: 155px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.content {
|
||||||
|
height: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-center {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenav-left {
|
||||||
|
padding-top: 20px;
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
height: 100%;
|
||||||
|
margin-left: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenav-right {
|
||||||
|
padding-top: 20px;
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.sidenav {
|
||||||
|
height: auto;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
.row.content {height:auto;}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.navbar-top {
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-color: #FF8040;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-bottom {
|
||||||
|
border-top-width: 1px;
|
||||||
|
border-top-color: #FF8040;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
height: 125px;
|
||||||
|
opacity: 0.9;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-nav > li > .dropdown-menu {
|
||||||
|
background-color: #A1A1A1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
.dropdown-toggle:after {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
#dropdown-menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
.navbar {
|
||||||
|
padding-left: 100px;
|
||||||
|
padding-right: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.button-login {
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #ff8040;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
background-image: url('../images/bg.jpeg');
|
||||||
|
background-position: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BlogPosts */
|
||||||
|
|
||||||
|
.main-article {
|
||||||
|
border: 2px solid #efefee;
|
||||||
|
Background: #fff;
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
border-top-right-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-article img {
|
||||||
|
width: 100%;
|
||||||
|
height: 250px;
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-top: 5px solid lightblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-container {
|
||||||
|
border: 1px solid #FF8040;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: #0E0E10;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
background-image: url('../images/bg.jpeg');
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-article-link, .article-container a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-article-link:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-title {
|
||||||
|
min-width: 300px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 440px) {
|
||||||
|
.article-title {
|
||||||
|
min-width: 100px;
|
||||||
|
max-width: 245px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-img {
|
||||||
|
height: 100px;
|
||||||
|
width: 100px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-author-img {
|
||||||
|
height: 25px;
|
||||||
|
border: 1px solid darkgray;
|
||||||
|
margin-left: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-details {
|
||||||
|
font-size: .8em;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-teaser {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
|
@ -13,22 +13,27 @@
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.1",
|
"doctrine/doctrine-migrations-bundle": "^3.1",
|
||||||
"doctrine/orm": "^2.9",
|
"doctrine/orm": "^2.9",
|
||||||
"easycorp/easyadmin-bundle": "^3",
|
"easycorp/easyadmin-bundle": "^3",
|
||||||
"symfony/console": "5.2.*",
|
"knplabs/knp-time-bundle": "^1.16",
|
||||||
"symfony/dotenv": "5.2.*",
|
"symfony/console": "5.3.*",
|
||||||
|
"symfony/dotenv": "5.3.*",
|
||||||
"symfony/flex": "^1.3.1",
|
"symfony/flex": "^1.3.1",
|
||||||
"symfony/framework-bundle": "5.2.*",
|
"symfony/framework-bundle": "5.3.*",
|
||||||
"symfony/proxy-manager-bridge": "5.2.*",
|
"symfony/mailer": "5.3.*",
|
||||||
"symfony/security-bundle": "5.2.*",
|
"symfony/proxy-manager-bridge": "5.3.*",
|
||||||
|
"symfony/security-bundle": "5.3.*",
|
||||||
"symfony/twig-bundle": "^5.2",
|
"symfony/twig-bundle": "^5.2",
|
||||||
"symfony/validator": "5.2.*",
|
"symfony/validator": "5.3.*",
|
||||||
"symfony/yaml": "5.2.*",
|
"symfony/webpack-encore-bundle": "^1.11",
|
||||||
|
"symfony/yaml": "5.3.*",
|
||||||
|
"symfonycasts/verify-email-bundle": "^1.5",
|
||||||
"twig/extra-bundle": "^2.12|^3.0",
|
"twig/extra-bundle": "^2.12|^3.0",
|
||||||
|
"twig/intl-extra": "^3.3",
|
||||||
"twig/twig": "^2.12|^3.0"
|
"twig/twig": "^2.12|^3.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/maker-bundle": "^1.31",
|
"symfony/maker-bundle": "^1.31",
|
||||||
"symfony/stopwatch": "^5.2",
|
"symfony/stopwatch": "^5.3",
|
||||||
"symfony/web-profiler-bundle": "^5.2"
|
"symfony/web-profiler-bundle": "^5.3"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"optimize-autoloader": true,
|
"optimize-autoloader": true,
|
||||||
|
@ -70,7 +75,7 @@
|
||||||
"extra": {
|
"extra": {
|
||||||
"symfony": {
|
"symfony": {
|
||||||
"allow-contrib": false,
|
"allow-contrib": false,
|
||||||
"require": "5.2.*"
|
"require": "5.3.*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,4 +10,7 @@ return [
|
||||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||||
EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true],
|
EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true],
|
||||||
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
||||||
|
SymfonyCasts\Bundle\VerifyEmail\SymfonyCastsVerifyEmailBundle::class => ['all' => true],
|
||||||
|
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
|
||||||
|
Knp\Bundle\TimeBundle\KnpTimeBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
framework:
|
||||||
|
assets:
|
||||||
|
json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'
|
|
@ -0,0 +1,3 @@
|
||||||
|
framework:
|
||||||
|
mailer:
|
||||||
|
dsn: '%env(MAILER_DSN)%'
|
|
@ -0,0 +1,4 @@
|
||||||
|
#webpack_encore:
|
||||||
|
# Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
|
||||||
|
# Available in version 1.2
|
||||||
|
#cache: true
|
|
@ -0,0 +1,2 @@
|
||||||
|
#webpack_encore:
|
||||||
|
# strict_mode: false
|
|
@ -0,0 +1,30 @@
|
||||||
|
webpack_encore:
|
||||||
|
# The path where Encore is building the assets - i.e. Encore.setOutputPath()
|
||||||
|
output_path: '%kernel.project_dir%/public/build'
|
||||||
|
# If multiple builds are defined (as shown below), you can disable the default build:
|
||||||
|
# output_path: false
|
||||||
|
|
||||||
|
# Set attributes that will be rendered on all script and link tags
|
||||||
|
script_attributes:
|
||||||
|
defer: true
|
||||||
|
# link_attributes:
|
||||||
|
|
||||||
|
# If using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials')
|
||||||
|
# crossorigin: 'anonymous'
|
||||||
|
|
||||||
|
# Preload all rendered script and link tags automatically via the HTTP/2 Link header
|
||||||
|
# preload: true
|
||||||
|
|
||||||
|
# Throw an exception if the entrypoints.json file is missing or an entry is missing from the data
|
||||||
|
# strict_mode: false
|
||||||
|
|
||||||
|
# If you have multiple builds:
|
||||||
|
# builds:
|
||||||
|
# pass "frontend" as the 3rg arg to the Twig functions
|
||||||
|
# {{ encore_entry_script_tags('entry1', null, 'frontend') }}
|
||||||
|
|
||||||
|
# frontend: '%kernel.project_dir%/public/frontend/build'
|
||||||
|
|
||||||
|
# Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
|
||||||
|
# Put in config/packages/prod/webpack_encore.yaml
|
||||||
|
# cache: true
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?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 Version20210530183330 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 user ADD is_verified TINYINT(1) NOT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE user DROP is_verified');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?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 Version20210601114523 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 blog ADD slug VARCHAR(255) NOT NULL, CHANGE title title VARCHAR(255) DEFAULT NULL, CHANGE content content LONGTEXT DEFAULT NULL, CHANGE created_at created_at DATETIME DEFAULT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('ALTER TABLE blog DROP slug, CHANGE title title VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, CHANGE content content LONGTEXT CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, CHANGE created_at created_at DATETIME NOT NULL');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"@symfony/stimulus-bridge": "^2.0.0",
|
||||||
|
"@symfony/webpack-encore": "^1.0.0",
|
||||||
|
"core-js": "^3.0.0",
|
||||||
|
"file-loader": "^6.0.0",
|
||||||
|
"regenerator-runtime": "^0.13.2",
|
||||||
|
"sass": "^1.34.0",
|
||||||
|
"sass-loader": "^11.0.0",
|
||||||
|
"stimulus": "^2.0.0",
|
||||||
|
"webpack-notifier": "^1.6.0"
|
||||||
|
},
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev-server": "encore dev-server",
|
||||||
|
"dev": "encore dev",
|
||||||
|
"watch": "encore dev --watch",
|
||||||
|
"build": "encore production --progress"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-free": "^5.15.3",
|
||||||
|
"@popperjs/core": "^2.9.2",
|
||||||
|
"bootstrap": "^5.0.1",
|
||||||
|
"copy-webpack-plugin": "^9.0.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ class BlogCrudController extends AbstractCrudController
|
||||||
AssociationField::new('author')
|
AssociationField::new('author')
|
||||||
->autocomplete(),
|
->autocomplete(),
|
||||||
TextField::new('title'),
|
TextField::new('title'),
|
||||||
|
TextField::new('slug'),
|
||||||
TextEditorField::new('teaser'),
|
TextEditorField::new('teaser'),
|
||||||
TextEditorField::new('content'),
|
TextEditorField::new('content'),
|
||||||
DateTimeField::new('createdAt'),
|
DateTimeField::new('createdAt'),
|
||||||
|
|
|
@ -29,11 +29,11 @@ class BlogController extends AbstractController
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
#[Route('/blog/{id}', name: 'blog')]
|
#[Route('/blog/{slug}', name: 'blog')]
|
||||||
public function show($id, BlogRepository $blogRepository): Response
|
public function show($slug, BlogRepository $blogRepository): Response
|
||||||
{
|
{
|
||||||
return $this->render('blog/show.html.twig', [
|
return $this->render('blog/show.html.twig', [
|
||||||
'blog' => $blogRepository->findOneBy(['id' => $id])
|
'blog' => $blogRepository->findOneBy(['slug' => $slug])
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Form\RegistrationFormType;
|
||||||
|
use App\Security\EmailVerifier;
|
||||||
|
use App\Repository\UserRepository;
|
||||||
|
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Mime\Address;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
||||||
|
use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface;
|
||||||
|
|
||||||
|
class RegistrationController extends AbstractController
|
||||||
|
{
|
||||||
|
private $emailVerifier;
|
||||||
|
|
||||||
|
public function __construct(EmailVerifier $emailVerifier)
|
||||||
|
{
|
||||||
|
$this->emailVerifier = $emailVerifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/register', name: 'app_register')]
|
||||||
|
public function register(Request $request, UserPasswordEncoderInterface $passwordEncoder): Response
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$form = $this->createForm(RegistrationFormType::class, $user);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
// encode the plain password
|
||||||
|
$user->setPassword(
|
||||||
|
$passwordEncoder->encodePassword(
|
||||||
|
$user,
|
||||||
|
$form->get('plainPassword')->getData()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$entityManager = $this->getDoctrine()->getManager();
|
||||||
|
$entityManager->persist($user);
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
// generate a signed url and email it to the user
|
||||||
|
$this->emailVerifier->sendEmailConfirmation('app_verify_email', $user,
|
||||||
|
(new TemplatedEmail())
|
||||||
|
->from(new Address('tracer@24unix.net', '24unix'))
|
||||||
|
->to($user->getEmail())
|
||||||
|
->subject('Please Confirm your Email')
|
||||||
|
->htmlTemplate('registration/confirmation_email.html.twig')
|
||||||
|
);
|
||||||
|
// do anything else you need here, like send an email
|
||||||
|
|
||||||
|
return $this->redirectToRoute('blogs');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('registration/register.html.twig', [
|
||||||
|
'registrationForm' => $form->createView(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/verify/email', name: 'app_verify_email')]
|
||||||
|
public function verifyUserEmail(Request $request, UserRepository $userRepository): Response
|
||||||
|
{
|
||||||
|
$id = $request->get('id');
|
||||||
|
|
||||||
|
if (null === $id) {
|
||||||
|
return $this->redirectToRoute('app_register');
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $userRepository->find($id);
|
||||||
|
|
||||||
|
if (null === $user) {
|
||||||
|
return $this->redirectToRoute('app_register');
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate email confirmation link, sets User::isVerified=true and persists
|
||||||
|
try {
|
||||||
|
$this->emailVerifier->handleEmailConfirmation($request, $user);
|
||||||
|
} catch (VerifyEmailExceptionInterface $exception) {
|
||||||
|
$this->addFlash('verify_email_error', $exception->getReason());
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_register');
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TODO Change the redirect on success and handle or remove the flash message in your templates
|
||||||
|
$this->addFlash('success', 'Your email address has been verified.');
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_register');
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,9 +6,12 @@ use App\Repository\BlogRepository;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use JetBrains\PhpStorm\Pure;
|
||||||
|
use App\Repository\SectionRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity(repositoryClass=BlogRepository::class)
|
* @ORM\Entity(repositoryClass=BlogRepository::class)
|
||||||
|
* @ORM\HasLifecycleCallbacks()
|
||||||
*/
|
*/
|
||||||
class Blog
|
class Blog
|
||||||
{
|
{
|
||||||
|
@ -22,28 +25,28 @@ class Blog
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="string", length=255)
|
* @ORM\Column(type="string", length=255)
|
||||||
*/
|
*/
|
||||||
private $title;
|
private ?string $title;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="text", nullable=true)
|
* @ORM\Column(type="text", nullable=true)
|
||||||
*/
|
*/
|
||||||
private $teaser;
|
private ?string $teaser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="string", length=255, nullable=true)
|
* @ORM\Column(type="string", length=255, nullable=true)
|
||||||
*/
|
*/
|
||||||
private $teaserImage;
|
private ?string $teaserImage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="text")
|
* @ORM\Column(type="text")
|
||||||
*/
|
*/
|
||||||
private $content;
|
private ?string $content;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToOne(targetEntity=User::class, inversedBy="blogs")
|
* @ORM\ManyToOne(targetEntity=User::class, inversedBy="blogs")
|
||||||
* @ORM\JoinColumn(nullable=false)
|
* @ORM\JoinColumn(nullable=false)
|
||||||
*/
|
*/
|
||||||
private $author;
|
private ?User $author;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToMany(targetEntity=Section::class, inversedBy="blogs")
|
* @ORM\ManyToMany(targetEntity=Section::class, inversedBy="blogs")
|
||||||
|
@ -53,17 +56,17 @@ class Blog
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="datetime")
|
* @ORM\Column(type="datetime")
|
||||||
*/
|
*/
|
||||||
private $createdAt;
|
private ?\DateTimeInterface $createdAt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="datetime", nullable=true)
|
* @ORM\Column(type="datetime", nullable=true)
|
||||||
*/
|
*/
|
||||||
private $editedAt;
|
private ?\DateTimeInterface $editedAt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\ManyToOne(targetEntity=User::class)
|
* @ORM\ManyToOne(targetEntity=User::class)
|
||||||
*/
|
*/
|
||||||
private $editedBy;
|
private ?User $editedBy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Column(type="string", length=255, nullable=true)
|
* @ORM\Column(type="string", length=255, nullable=true)
|
||||||
|
@ -75,16 +78,25 @@ class Blog
|
||||||
*/
|
*/
|
||||||
private $comments;
|
private $comments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", length=255)
|
||||||
|
*/
|
||||||
|
private $slug;
|
||||||
|
|
||||||
|
#[Pure]
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->section = new ArrayCollection();
|
$this->section = new ArrayCollection();
|
||||||
$this->comments = new ArrayCollection();
|
$this->comments = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __toString()
|
/**
|
||||||
{
|
* @return null|string
|
||||||
return $this->title;
|
*/
|
||||||
}
|
public function __toString()
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
|
@ -253,4 +265,16 @@ class Blog
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSlug(): ?string
|
||||||
|
{
|
||||||
|
return $this->slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSlug(string $slug): self
|
||||||
|
{
|
||||||
|
$this->slug = $slug;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,12 @@ use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use JetBrains\PhpStorm\Pure;
|
use JetBrains\PhpStorm\Pure;
|
||||||
|
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Entity(repositoryClass=UserRepository::class)
|
* @ORM\Entity(repositoryClass=UserRepository::class)
|
||||||
|
* @UniqueEntity(fields={"username"}, message="There is already an account with this username")
|
||||||
*/
|
*/
|
||||||
class User implements UserInterface
|
class User implements UserInterface
|
||||||
{
|
{
|
||||||
|
@ -71,22 +73,27 @@ class User implements UserInterface
|
||||||
* @ORM\OneToMany(targetEntity=Comment::class, mappedBy="author")
|
* @ORM\OneToMany(targetEntity=Comment::class, mappedBy="author")
|
||||||
*/
|
*/
|
||||||
private $comments;
|
private $comments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="boolean")
|
||||||
|
*/
|
||||||
|
private $isVerified = false;
|
||||||
|
|
||||||
#[Pure] public function __construct()
|
#[Pure] public function __construct()
|
||||||
{
|
{
|
||||||
$this->blogs = new ArrayCollection();
|
$this->blogs = new ArrayCollection();
|
||||||
$this->comments = new ArrayCollection();
|
$this->comments = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __toString()
|
public function __toString()
|
||||||
{
|
{
|
||||||
return $this->username;
|
return $this->username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A visual identifier that represents this user.
|
* A visual identifier that represents this user.
|
||||||
|
@ -94,50 +101,50 @@ class User implements UserInterface
|
||||||
* @see UserInterface
|
* @see UserInterface
|
||||||
*/
|
*/
|
||||||
public function getUsername(): string
|
public function getUsername(): string
|
||||||
{
|
{
|
||||||
return (string)$this->username;
|
return (string)$this->username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUsername(string $username): self
|
public function setUsername(string $username): self
|
||||||
{
|
{
|
||||||
$this->username = $username;
|
$this->username = $username;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see UserInterface
|
* @see UserInterface
|
||||||
*/
|
*/
|
||||||
public function getRoles(): array
|
public function getRoles(): array
|
||||||
{
|
{
|
||||||
$roles = $this->roles;
|
$roles = $this->roles;
|
||||||
// guarantee every user at least has ROLE_USER
|
// guarantee every user at least has ROLE_USER
|
||||||
$roles[] = 'ROLE_USER';
|
$roles[] = 'ROLE_USER';
|
||||||
|
|
||||||
return array_unique($roles);
|
return array_unique($roles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRoles(array $roles): self
|
public function setRoles(array $roles): self
|
||||||
{
|
{
|
||||||
$this->roles = $roles;
|
$this->roles = $roles;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see UserInterface
|
* @see UserInterface
|
||||||
*/
|
*/
|
||||||
public function getPassword(): string
|
public function getPassword(): string
|
||||||
{
|
{
|
||||||
return $this->password;
|
return $this->password;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPassword(string $password): self
|
public function setPassword(string $password): self
|
||||||
{
|
{
|
||||||
$this->password = $password;
|
$this->password = $password;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returning a salt is only needed, if you are not using a modern
|
* Returning a salt is only needed, if you are not using a modern
|
||||||
|
@ -146,136 +153,148 @@ class User implements UserInterface
|
||||||
* @see UserInterface
|
* @see UserInterface
|
||||||
*/
|
*/
|
||||||
public function getSalt(): ?string
|
public function getSalt(): ?string
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see UserInterface
|
* @see UserInterface
|
||||||
*/
|
*/
|
||||||
public function eraseCredentials()
|
public function eraseCredentials()
|
||||||
{
|
{
|
||||||
// If you store any temporary, sensitive data on the user, clear it here
|
// If you store any temporary, sensitive data on the user, clear it here
|
||||||
// $this->plainPassword = null;
|
// $this->plainPassword = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFirstName(): ?string
|
public function getFirstName(): ?string
|
||||||
{
|
{
|
||||||
return $this->firstName;
|
return $this->firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setFirstName(?string $firstName): self
|
public function setFirstName(?string $firstName): self
|
||||||
{
|
{
|
||||||
$this->firstName = $firstName;
|
$this->firstName = $firstName;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastName(): ?string
|
public function getLastName(): ?string
|
||||||
{
|
{
|
||||||
return $this->lastName;
|
return $this->lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setLastName(?string $lastName): self
|
public function setLastName(?string $lastName): self
|
||||||
{
|
{
|
||||||
$this->lastName = $lastName;
|
$this->lastName = $lastName;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getEmail(): ?string
|
public function getEmail(): ?string
|
||||||
{
|
{
|
||||||
return $this->email;
|
return $this->email;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setEmail(string $email): self
|
public function setEmail(string $email): self
|
||||||
{
|
{
|
||||||
$this->email = $email;
|
$this->email = $email;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreatedAt(): ?\DateTimeInterface
|
public function getCreatedAt(): ?\DateTimeInterface
|
||||||
{
|
{
|
||||||
return $this->createdAt;
|
return $this->createdAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setCreatedAt(\DateTimeInterface $createdAt): self
|
public function setCreatedAt(\DateTimeInterface $createdAt): self
|
||||||
{
|
{
|
||||||
$this->createdAt = $createdAt;
|
$this->createdAt = $createdAt;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastLoginAt(): ?\DateTimeInterface
|
public function getLastLoginAt(): ?\DateTimeInterface
|
||||||
{
|
{
|
||||||
return $this->lastLoginAt;
|
return $this->lastLoginAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setLastLoginAt(?\DateTimeInterface $lastLoginAt): self
|
public function setLastLoginAt(?\DateTimeInterface $lastLoginAt): self
|
||||||
{
|
{
|
||||||
$this->lastLoginAt = $lastLoginAt;
|
$this->lastLoginAt = $lastLoginAt;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection|Blog[]
|
* @return Collection|Blog[]
|
||||||
*/
|
*/
|
||||||
public function getBlogs(): Collection
|
public function getBlogs(): Collection
|
||||||
{
|
{
|
||||||
return $this->blogs;
|
return $this->blogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addBlog(Blog $blog): self
|
public function addBlog(Blog $blog): self
|
||||||
{
|
{
|
||||||
if (!$this->blogs->contains($blog)) {
|
if (!$this->blogs->contains($blog)) {
|
||||||
$this->blogs[] = $blog;
|
$this->blogs[] = $blog;
|
||||||
$blog->setAuthor($this);
|
$blog->setAuthor($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeBlog(Blog $blog): self
|
public function removeBlog(Blog $blog): self
|
||||||
{
|
{
|
||||||
if ($this->blogs->removeElement($blog)) {
|
if ($this->blogs->removeElement($blog)) {
|
||||||
// set the owning side to null (unless already changed)
|
// set the owning side to null (unless already changed)
|
||||||
if ($blog->getAuthor() === $this) {
|
if ($blog->getAuthor() === $this) {
|
||||||
$blog->setAuthor(null);
|
$blog->setAuthor(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection|Comment[]
|
* @return Collection|Comment[]
|
||||||
*/
|
*/
|
||||||
public function getComments(): Collection
|
public function getComments(): Collection
|
||||||
{
|
{
|
||||||
return $this->comments;
|
return $this->comments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addComment(Comment $comment): self
|
public function addComment(Comment $comment): self
|
||||||
{
|
{
|
||||||
if (!$this->comments->contains($comment)) {
|
if (!$this->comments->contains($comment)) {
|
||||||
$this->comments[] = $comment;
|
$this->comments[] = $comment;
|
||||||
$comment->setAuthor($this);
|
$comment->setAuthor($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeComment(Comment $comment): self
|
public function removeComment(Comment $comment): self
|
||||||
{
|
{
|
||||||
if ($this->comments->removeElement($comment)) {
|
if ($this->comments->removeElement($comment)) {
|
||||||
// set the owning side to null (unless already changed)
|
// set the owning side to null (unless already changed)
|
||||||
if ($comment->getAuthor() === $this) {
|
if ($comment->getAuthor() === $this) {
|
||||||
$comment->setAuthor(null);
|
$comment->setAuthor(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isVerified(): bool
|
||||||
|
{
|
||||||
|
return $this->isVerified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsVerified(bool $isVerified): self
|
||||||
|
{
|
||||||
|
$this->isVerified = $isVerified;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use Symfony\Component\Validator\Constraints\IsTrue;
|
||||||
|
use Symfony\Component\Validator\Constraints\Length;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
|
||||||
|
class RegistrationFormType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('username')
|
||||||
|
->add('agreeTerms', CheckboxType::class, [
|
||||||
|
'mapped' => false,
|
||||||
|
'constraints' => [
|
||||||
|
new IsTrue([
|
||||||
|
'message' => 'You should agree to our terms.',
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->add('plainPassword', PasswordType::class, [
|
||||||
|
// instead of being set onto the object directly,
|
||||||
|
// this is read and encoded in the controller
|
||||||
|
'mapped' => false,
|
||||||
|
'attr' => ['autocomplete' => 'new-password'],
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank([
|
||||||
|
'message' => 'Please enter a password',
|
||||||
|
]),
|
||||||
|
new Length([
|
||||||
|
'min' => 6,
|
||||||
|
'minMessage' => 'Your password should be at least {{ limit }} characters',
|
||||||
|
// max length allowed by Symfony for security reasons
|
||||||
|
'max' => 4096,
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => User::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Security;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface;
|
||||||
|
use SymfonyCasts\Bundle\VerifyEmail\VerifyEmailHelperInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class EmailVerifier
|
||||||
|
* @package App\Security
|
||||||
|
*/
|
||||||
|
class EmailVerifier
|
||||||
|
{
|
||||||
|
private VerifyEmailHelperInterface $verifyEmailHelper;
|
||||||
|
private MailerInterface $mailer;
|
||||||
|
private EntityManagerInterface $entityManager;
|
||||||
|
|
||||||
|
public function __construct(VerifyEmailHelperInterface $helper, MailerInterface $mailer, EntityManagerInterface $manager)
|
||||||
|
{
|
||||||
|
$this->verifyEmailHelper = $helper;
|
||||||
|
$this->mailer = $mailer;
|
||||||
|
$this->entityManager = $manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendEmailConfirmation(string $verifyEmailRouteName, UserInterface $user, TemplatedEmail $email): void
|
||||||
|
{
|
||||||
|
$signatureComponents = $this->verifyEmailHelper->generateSignature(
|
||||||
|
$verifyEmailRouteName,
|
||||||
|
$user->getId(),
|
||||||
|
$user->getEmail(),
|
||||||
|
['id' => $user->getId()]
|
||||||
|
);
|
||||||
|
|
||||||
|
$context = $email->getContext();
|
||||||
|
$context['signedUrl'] = $signatureComponents->getSignedUrl();
|
||||||
|
$context['expiresAtMessageKey'] = $signatureComponents->getExpirationMessageKey();
|
||||||
|
$context['expiresAtMessageData'] = $signatureComponents->getExpirationMessageData();
|
||||||
|
|
||||||
|
$email->context($context);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->mailer->send($email);
|
||||||
|
} catch (TransportExceptionInterface $e) {
|
||||||
|
die("Error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws VerifyEmailExceptionInterface
|
||||||
|
*/
|
||||||
|
public function handleEmailConfirmation(Request $request, UserInterface $user): void
|
||||||
|
{
|
||||||
|
$this->verifyEmailHelper->validateEmailConfirmation($request->getUri(), $user->getId(), $user->getEmail());
|
||||||
|
|
||||||
|
$user->setIsVerified(true);
|
||||||
|
|
||||||
|
$this->entityManager->persist($user);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
}
|
||||||
|
}
|
58
symfony.lock
|
@ -91,9 +91,15 @@
|
||||||
"ref": "b131e6cbfe1b898a508987851963fff485986285"
|
"ref": "b131e6cbfe1b898a508987851963fff485986285"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"egulias/email-validator": {
|
||||||
|
"version": "3.1.1"
|
||||||
|
},
|
||||||
"friendsofphp/proxy-manager-lts": {
|
"friendsofphp/proxy-manager-lts": {
|
||||||
"version": "v1.0.5"
|
"version": "v1.0.5"
|
||||||
},
|
},
|
||||||
|
"knplabs/knp-time-bundle": {
|
||||||
|
"version": "v1.16.0"
|
||||||
|
},
|
||||||
"laminas/laminas-code": {
|
"laminas/laminas-code": {
|
||||||
"version": "4.3.0"
|
"version": "4.3.0"
|
||||||
},
|
},
|
||||||
|
@ -216,6 +222,18 @@
|
||||||
"symfony/intl": {
|
"symfony/intl": {
|
||||||
"version": "v5.2.7"
|
"version": "v5.2.7"
|
||||||
},
|
},
|
||||||
|
"symfony/mailer": {
|
||||||
|
"version": "4.3",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "4.3",
|
||||||
|
"ref": "15658c2a0176cda2e7dba66276a2030b52bd81b2"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/mailer.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
"symfony/maker-bundle": {
|
"symfony/maker-bundle": {
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
@ -225,18 +243,27 @@
|
||||||
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
|
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"symfony/mime": {
|
||||||
|
"version": "v5.2.9"
|
||||||
|
},
|
||||||
"symfony/options-resolver": {
|
"symfony/options-resolver": {
|
||||||
"version": "v5.2.4"
|
"version": "v5.2.4"
|
||||||
},
|
},
|
||||||
"symfony/orm-pack": {
|
"symfony/orm-pack": {
|
||||||
"version": "v2.1.0"
|
"version": "v2.1.0"
|
||||||
},
|
},
|
||||||
|
"symfony/password-hasher": {
|
||||||
|
"version": "v5.3.0"
|
||||||
|
},
|
||||||
"symfony/polyfill-intl-grapheme": {
|
"symfony/polyfill-intl-grapheme": {
|
||||||
"version": "v1.23.0"
|
"version": "v1.23.0"
|
||||||
},
|
},
|
||||||
"symfony/polyfill-intl-icu": {
|
"symfony/polyfill-intl-icu": {
|
||||||
"version": "v1.23.0"
|
"version": "v1.23.0"
|
||||||
},
|
},
|
||||||
|
"symfony/polyfill-intl-idn": {
|
||||||
|
"version": "v1.23.0"
|
||||||
|
},
|
||||||
"symfony/polyfill-intl-normalizer": {
|
"symfony/polyfill-intl-normalizer": {
|
||||||
"version": "v1.23.0"
|
"version": "v1.23.0"
|
||||||
},
|
},
|
||||||
|
@ -249,6 +276,9 @@
|
||||||
"symfony/polyfill-php80": {
|
"symfony/polyfill-php80": {
|
||||||
"version": "v1.23.0"
|
"version": "v1.23.0"
|
||||||
},
|
},
|
||||||
|
"symfony/polyfill-php81": {
|
||||||
|
"version": "v1.23.0"
|
||||||
|
},
|
||||||
"symfony/polyfill-uuid": {
|
"symfony/polyfill-uuid": {
|
||||||
"version": "v1.23.0"
|
"version": "v1.23.0"
|
||||||
},
|
},
|
||||||
|
@ -383,12 +413,40 @@
|
||||||
"config/routes/dev/web_profiler.yaml"
|
"config/routes/dev/web_profiler.yaml"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"symfony/webpack-encore-bundle": {
|
||||||
|
"version": "1.9",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "master",
|
||||||
|
"version": "1.9",
|
||||||
|
"ref": "9399a0bfc6ee7a0c019f952bca46d6a6045dd451"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"assets/app.js",
|
||||||
|
"assets/bootstrap.js",
|
||||||
|
"assets/controllers.json",
|
||||||
|
"assets/controllers/hello_controller.js",
|
||||||
|
"assets/styles/app.css",
|
||||||
|
"config/packages/assets.yaml",
|
||||||
|
"config/packages/prod/webpack_encore.yaml",
|
||||||
|
"config/packages/test/webpack_encore.yaml",
|
||||||
|
"config/packages/webpack_encore.yaml",
|
||||||
|
"package.json",
|
||||||
|
"webpack.config.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
"symfony/yaml": {
|
"symfony/yaml": {
|
||||||
"version": "v5.2.9"
|
"version": "v5.2.9"
|
||||||
},
|
},
|
||||||
|
"symfonycasts/verify-email-bundle": {
|
||||||
|
"version": "v1.5.0"
|
||||||
|
},
|
||||||
"twig/extra-bundle": {
|
"twig/extra-bundle": {
|
||||||
"version": "v3.3.1"
|
"version": "v3.3.1"
|
||||||
},
|
},
|
||||||
|
"twig/intl-extra": {
|
||||||
|
"version": "v3.3.0"
|
||||||
|
},
|
||||||
"twig/twig": {
|
"twig/twig": {
|
||||||
"version": "v3.3.2"
|
"version": "v3.3.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<footer class="footer">
|
||||||
|
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-bottom navbar-bottom">
|
||||||
|
<div id="legal">
|
||||||
|
</div>
|
||||||
|
<div class="powered">
|
||||||
|
powered by <a href="#"><img src="{{ asset('build/images/Spookie/spookie_64x64.png') }}" alt="Spookie"></a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</footer>
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top navbar-top">
|
||||||
|
|
||||||
|
<a class="navbar-brand" href="{{ path('blogs') }}">
|
||||||
|
<img src="{{ asset('build/images/24unix/24_logo_bg_64x64.png') }}" alt="24unix.net" id="site-logo">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button class="navbar-toggler border-0" type="button" data-toggle="collapse" data-target="#CollapsingNavbar">
|
||||||
|
☰
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="CollapsingNavbar">
|
||||||
|
<ul class="navbar-nav ml-auto">
|
||||||
|
{% if is_granted('ROLE_USER') %}
|
||||||
|
<li class="nav-item dropdown my-2 my-lg-0">
|
||||||
|
<button type="button" id="navbar-dropdown" data-toggle="dropdown"
|
||||||
|
class="btn btn-primary dropdown-toggle ml-auto button-login">
|
||||||
|
{{ app.user.username }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="dropdown-menu dropdown-menu-right" id="dropdown-menu" aria-labelledby="navbar-dropdown">
|
||||||
|
<a class="dropdown-item" href="{{ path('blogs') }}">
|
||||||
|
<span class="fas fa-user" aria-hidden="true"></span>
|
||||||
|
Profile</a>
|
||||||
|
<a class="dropdown-item" href="#">
|
||||||
|
<span class="fas fa-cog" aria-hidden="true"></span>
|
||||||
|
Settings</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
{% if is_granted('ROLE_ADMIN') %}
|
||||||
|
<a class="dropdown-item" href="{{ path('admin') }}">
|
||||||
|
<span class="fas fa-cog" aria-hidden="true"></span>
|
||||||
|
Administration
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<a class="dropdown-item" href="{{ path('app_logout') }}">
|
||||||
|
<span class="fas fa-sign-out-alt" aria-hidden="true"></span>
|
||||||
|
Logout
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn btn-primary button-login" href="{{ path('app_login') }}" role="button" id="buttonLogin">
|
||||||
|
Login
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</nav>
|
|
@ -1,19 +1,42 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>{% block title %}Welcome!{% endblock %}</title>
|
<title>{% block title %}Spookie{% endblock %}</title>
|
||||||
{# Run `composer require symfony/webpack-encore-bundle`
|
|
||||||
and uncomment the following Encore helpers to start using Symfony UX #}
|
|
||||||
{% block stylesheets %}
|
{% block stylesheets %}
|
||||||
{#{{ encore_entry_link_tags('app') }}#}
|
{{ encore_entry_link_tags('app') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascripts %}
|
|
||||||
{#{{ encore_entry_script_tags('app') }}#}
|
|
||||||
{% endblock %}
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% block body %}{% endblock %}
|
|
||||||
|
{% include '_header.html.twig' %}
|
||||||
|
|
||||||
|
<div class="container-fluid text-center">
|
||||||
|
<div class="row content d-flex justify-content-sm-center">
|
||||||
|
<div class="col-sm-2 sidenav-left box" id="main-menu">
|
||||||
|
<p>
|
||||||
|
<a href="{{ path('blogs') }}">Blogs</a><br>
|
||||||
|
<a href="//git.24unix.net">git.24unix.net</a>
|
||||||
|
<a href="//pastebin.24unix.net">pastebin.24unix.net</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-9 text-left center-block" id="main-content">
|
||||||
|
{% block body %} {% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-1 sidenav" id="spacer">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include '_footer.html.twig' %}
|
||||||
|
|
||||||
|
{% block javascripts %}
|
||||||
|
{{ encore_entry_script_tags('app') }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,12 +1,38 @@
|
||||||
|
{# templates/blog/blog_show.html.twig #}
|
||||||
{% extends 'base.html.twig' %}
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
{% block title %}Hello BlogController!{% endblock %}
|
{% block title %} Blogpost {% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% for blog in blogs %}
|
<div class="container">
|
||||||
<h4>{{ blog }}</h4>
|
<div class="row">
|
||||||
<p>{{ blog.teaser }}</p>
|
<!-- blog List -->
|
||||||
|
<div class="col-sm-12">
|
||||||
|
{% for blogpost in blogs %}
|
||||||
|
<div class="blog-container my-4">
|
||||||
|
<a href="{{ path('blog', { slug: blogpost.slug }) }} ">
|
||||||
|
{% if blogpost.teaserImage %}
|
||||||
|
<img class="blog-img" src="{{ asset('build/images/asteroid.jpeg') }}" alt="asteroid">
|
||||||
|
{% endif %}
|
||||||
|
<div class="article-title d-inline-block pl-3 align-middle">
|
||||||
|
<span>{{ blogpost.title }}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<br>
|
||||||
|
<span class="align-left blog-details">
|
||||||
|
<img class="article-author-img rounded-circle"
|
||||||
|
src="{{ asset('build/images/alien-profile.png') }}" alt="profile">
|
||||||
|
{{ blogpost.author }}
|
||||||
|
</span>
|
||||||
|
<span class="pl-5 blog-details float-right">{{ blogpost.createdAt|ago }}</span>
|
||||||
|
<br>
|
||||||
|
<span class="blog-teaser">
|
||||||
|
{{ blogpost.teaser }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
<a href="{{ path('blog', { id: blog.id }) }}">View more …</a>
|
</div>
|
||||||
{% endfor %}
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<h1>Hi! Please confirm your email!</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Please confirm your email address by clicking the following link: <br><br>
|
||||||
|
<a href="{{ signedUrl }}">Confirm my Email</a>.
|
||||||
|
This link will expire in {{ expiresAtMessageKey|trans(expiresAtMessageData, 'VerifyEmailBundle') }}.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Cheers!
|
||||||
|
</p>
|
|
@ -0,0 +1,21 @@
|
||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}Register{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
{% for flashError in app.flashes('verify_email_error') %}
|
||||||
|
<div class="alert alert-danger" role="alert">{{ flashError }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<h1>Register</h1>
|
||||||
|
|
||||||
|
{{ form_start(registrationForm) }}
|
||||||
|
{{ form_row(registrationForm.username) }}
|
||||||
|
{{ form_row(registrationForm.plainPassword, {
|
||||||
|
label: 'Password'
|
||||||
|
}) }}
|
||||||
|
{{ form_row(registrationForm.agreeTerms) }}
|
||||||
|
|
||||||
|
<button type="submit" class="btn">Register</button>
|
||||||
|
{{ form_end(registrationForm) }}
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,84 @@
|
||||||
|
const Encore = require("@symfony/webpack-encore");
|
||||||
|
|
||||||
|
// Manually configure the runtime environment if not already configured yet by the "encore" command.
|
||||||
|
// It's useful when you use tools that rely on webpack.config.js file.
|
||||||
|
if (!Encore.isRuntimeEnvironmentConfigured()) {
|
||||||
|
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || "dev");
|
||||||
|
}
|
||||||
|
|
||||||
|
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||||
|
|
||||||
|
Encore
|
||||||
|
// directory where compiled assets will be stored
|
||||||
|
.setOutputPath("public/build/")
|
||||||
|
.copyFiles({
|
||||||
|
from: "./assets/images",
|
||||||
|
to: "images/[path][name].[ext]"
|
||||||
|
})
|
||||||
|
|
||||||
|
// public path used by the web server to access the output path
|
||||||
|
.setPublicPath("/build")
|
||||||
|
// only needed for CDN's or sub-directory deploy
|
||||||
|
//.setManifestKeyPrefix('build/')
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ENTRY CONFIG
|
||||||
|
*
|
||||||
|
* Add 1 entry for each "page" of your app
|
||||||
|
* (including one that's included on every page - e.g. "app")
|
||||||
|
*
|
||||||
|
* Each entry will result in one JavaScript file (e.g. app.js)
|
||||||
|
* and one CSS file (e.g. app.scss) if your JavaScript imports CSS.
|
||||||
|
*/
|
||||||
|
.addEntry("app", "./assets/js/app.js")
|
||||||
|
//.addEntry('page1', './assets/js/page1.js')
|
||||||
|
//.addEntry('page2', './assets/js/page2.js')
|
||||||
|
|
||||||
|
// When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
|
||||||
|
.splitEntryChunks()
|
||||||
|
|
||||||
|
// will require an extra script tag for runtime.js
|
||||||
|
// but, you probably want this, unless you're building a single-page app
|
||||||
|
//.enableSingleRuntimeChunk()
|
||||||
|
.disableSingleRuntimeChunk()
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FEATURE CONFIG
|
||||||
|
*
|
||||||
|
* Enable & configure other features below. For a full
|
||||||
|
* list of features, see:
|
||||||
|
* https://symfony.com/doc/current/frontend.html#adding-more-features
|
||||||
|
*/
|
||||||
|
.cleanupOutputBeforeBuild()
|
||||||
|
.enableBuildNotifications()
|
||||||
|
.enableSourceMaps(!Encore.isProduction())
|
||||||
|
// enables hashed filenames (e.g. app.abc123.css)
|
||||||
|
.enableVersioning(Encore.isProduction())
|
||||||
|
|
||||||
|
// enables @babel/preset-env polyfills
|
||||||
|
.configureBabel(() => {
|
||||||
|
}, {
|
||||||
|
useBuiltIns: "usage",
|
||||||
|
corejs: 3
|
||||||
|
})
|
||||||
|
|
||||||
|
// enables Sass/SCSS support
|
||||||
|
.enableSassLoader()
|
||||||
|
|
||||||
|
// uncomment if you use TypeScript
|
||||||
|
//.enableTypeScriptLoader()
|
||||||
|
|
||||||
|
// uncomment to get integrity="..." attributes on your script & link tags
|
||||||
|
// requires WebpackEncoreBundle 1.4 or higher
|
||||||
|
//.enableIntegrityHashes(Encore.isProduction())
|
||||||
|
|
||||||
|
// uncomment if you're having problems with a jQuery plugin
|
||||||
|
//.autoProvidejQuery()
|
||||||
|
|
||||||
|
// uncomment if you use API Platform Admin (composer req api-admin)
|
||||||
|
//.enableReactPreset()
|
||||||
|
//.addEntry('admin', './assets/js/admin.js')
|
||||||
|
;
|
||||||
|
|
||||||
|
module.exports = Encore.getWebpackConfig();
|